mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-26 08:37:29 +00:00
Merge remote-tracking branch 'origin/dm-notifications-enhancements' into dm-notifications-enhancements
This commit is contained in:
commit
a6d8de87ea
@ -42,6 +42,15 @@
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Zopieux",
|
||||
"name": "Alexandre Macabies",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/81353?v=4",
|
||||
"profile": "https://github.com/Zopieux",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MeLlamoPablo",
|
||||
"name": "Pablo Rodríguez",
|
||||
|
17
README.md
17
README.md
@ -1,4 +1,4 @@
|
||||
## THERE ARE CURRENTLY NO OFFICIAL GOOGLE PLAY RELEASES. PLEASE REPORT ANY OCCURRENCES TO US.
|
||||
### THERE ARE CURRENTLY NO OFFICIAL GOOGLE PLAY RELEASES. PLEASE REPORT ANY OCCURRENCES TO US.
|
||||
|
||||
<img src="./app/src/main/ic_launcher-round.png" alt="Barinsta logo" align="right" width="20%"/>
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
|
||||
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)
|
||||
[![GitHub stars](https://img.shields.io/github/stars/austinhuang0131/instagrabber.svg?style=social&label=Star)](https://GitHub.com/austinhuang0131/barinsta/stargazers/)<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-38-orange.svg)](#contributors)
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-39-orange.svg)](#contributors)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
Instagram client; previously known as InstaGrabber.
|
||||
@ -57,51 +57,52 @@ Prominent contributors are listed here in the [all-contributors](https://allcont
|
||||
<td align="center"><a href="https://austinhuang.me"><img src="https://avatars1.githubusercontent.com/u/16656689?s=100" width="100px;" alt=""/><br /><sub><b>Austin Huang</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/commits?author=austinhuang0131" title="Documentation">📖</a> <a href="#question-austinhuang0131" title="Answering Questions">💬</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a> <a href="#ideas-austinhuang0131" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/ammargitham"><img src="https://avatars0.githubusercontent.com/u/8017365?s=100" width="100px;" alt=""/><br /><sub><b>Ammar Githam</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=ammargitham" title="Code">💻</a> <a href="#design-ammargitham" title="Design">🎨</a> <a href="#ideas-ammargitham" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-ammargitham" title="Maintenance">🚧</a> <a href="#question-ammargitham" title="Answering Questions">💬</a></td>
|
||||
<td align="center"><a href="https://github.com/andersonvom"><img src="https://avatars3.githubusercontent.com/u/69922?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anderson Mesquita</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=andersonvom" title="Code">💻</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3Aandersonvom" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/Zopieux"><img src="https://avatars.githubusercontent.com/u/81353?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexandre Macabies</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=Zopieux" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/MeLlamoPablo"><img src="https://avatars.githubusercontent.com/u/11708035?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pablo Rodríguez</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=MeLlamoPablo" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://rerolledgeek.blogspot.com/"><img src="https://avatars3.githubusercontent.com/u/5278488?s=100" width="100px;" alt=""/><br /><sub><b>AWAiS</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/commits?author=AwaisKing" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://snajdovski.github.io"><img src="https://avatars2.githubusercontent.com/u/42580385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Najdovski</b></sub></a><br /><a href="#design-snajdovski" title="Design">🎨</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://snajdovski.github.io"><img src="https://avatars2.githubusercontent.com/u/42580385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Najdovski</b></sub></a><br /><a href="#design-snajdovski" title="Design">🎨</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/CrazyMarvin"><img src="https://avatars3.githubusercontent.com/u/15004217?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CrazyMarvin</b></sub></a><br /><a href="#financial-CrazyMarvin" title="Financial">💵</a></td>
|
||||
<td align="center"><a href="http://kevinthomas.dev"><img src="https://avatars2.githubusercontent.com/u/15370181?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Thomas</b></sub></a><br /><a href="#financial-KevinNThomas" title="Financial">💵</a></td>
|
||||
<td align="center"><a href="https://github.com/Shadowspear123"><img src="https://avatars1.githubusercontent.com/u/50462281?s=100" width="100px;" alt=""/><br /><sub><b>Shadowspear123</b></sub></a><br /><a href="#blog-Shadowspear123" title="Blogposts">📝</a> <a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3AShadowspear123" title="Bug reports">🐛</a> <a href="#ideas-Shadowspear123" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-Shadowspear123" title="Answering Questions">💬</a></td>
|
||||
<td align="center"><a href="https://github.com/RickyM7"><img src="https://avatars3.githubusercontent.com/u/24703825?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo</b></sub></a><br /><a href="https://github.com/austinhuang0131/barinsta/issues?q=author%3ARickyM7" title="Bug reports">🐛</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://airikr.me/"><img src="https://avatars0.githubusercontent.com/u/53869451?s=100" width="100px;" alt=""/><br /><sub><b>Airikr</b></sub></a><br /><a href="#ideas-e-edgren" title="Ideas, Planning, & Feedback">🤔</a> <a href="#question-e-edgren" title="Answering Questions">💬</a></td>
|
||||
<td align="center"><a href="https://github.com/Akrai"><img src="https://avatars1.githubusercontent.com/u/5624597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akrai</b></sub></a><br /><a href="#ideas-Akrai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/Akrai"><img src="https://avatars1.githubusercontent.com/u/5624597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akrai</b></sub></a><br /><a href="#ideas-Akrai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/avtkal"><img src="https://avatars.githubusercontent.com/u/63205014?v=4?s=100" width="100px;" alt=""/><br /><sub><b>avtkal</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/cizordj"><img src="https://avatars2.githubusercontent.com/u/32869222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cézar Augusto</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/dimitrist19"><img src="https://avatars.githubusercontent.com/u/56406468?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dimitris T</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/farzadx"><img src="https://avatars2.githubusercontent.com/u/70059397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>farzadx</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/faydin"><img src="https://avatars2.githubusercontent.com/u/22706676?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fatih Aydın</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/fouze555"><img src="https://avatars3.githubusercontent.com/u/71935341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fouze555</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/fouze555"><img src="https://avatars3.githubusercontent.com/u/71935341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>fouze555</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/Galang23"><img src="https://avatars3.githubusercontent.com/u/13700948?s=100" width="100px;" alt=""/><br /><sub><b>Galang23</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/initdebugs"><img src="https://avatars0.githubusercontent.com/u/75781464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Initdebugs</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://janek.xyz/"><img src="https://avatars3.githubusercontent.com/u/8365659?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jakub Janek</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/GenosseFlosse"><img src="https://avatars.githubusercontent.com/u/59205524?v=4?s=100" width="100px;" alt=""/><br /><sub><b>GenosseFlosse</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://becauseofprog.fr/"><img src="https://avatars3.githubusercontent.com/u/24623168?s=100" width="100px;" alt=""/><br /><sub><b>kernoeb</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/MoaufmKlo"><img src="https://avatars1.githubusercontent.com/u/45636897?s=100" width="100px;" alt=""/><br /><sub><b>MoaufmKlo</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/nalinalini"><img src="https://avatars0.githubusercontent.com/u/65640431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nalinalini</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/peterge1998"><img src="https://avatars2.githubusercontent.com/u/47355238?s=100" width="100px;" alt=""/><br /><sub><b>peterge1998</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/PierreM0"><img src="https://avatars3.githubusercontent.com/u/71077853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PierreM0</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/RAMAR-RAR"><img src="https://avatars3.githubusercontent.com/u/47423745?s=100" width="100px;" alt=""/><br /><sub><b>RAMAR-RAR</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/rohang02"><img src="https://avatars3.githubusercontent.com/u/47921164?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rohang02</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/retiolus"><img src="https://avatars1.githubusercontent.com/u/65604466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>retiolus</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/retiolus"><img src="https://avatars1.githubusercontent.com/u/65604466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>retiolus</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/rikishi0071"><img src="https://avatars3.githubusercontent.com/u/18183855?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rikishi0071</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://gitlab.com/sandboiii"><img src="https://avatars.githubusercontent.com/u/17468894?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexey Peschany</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://stillu.cc/"><img src="https://avatars2.githubusercontent.com/u/5843208?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Still Hsu</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/Lego8486"><img src="https://avatars1.githubusercontent.com/u/47414485?s=100" width="100px;" alt=""/><br /><sub><b>Ten_Lego</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/wagnim"><img src="https://avatars0.githubusercontent.com/u/30241419?s=100" width="100px;" alt=""/><br /><sub><b>wagnim</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/wokija"><img src="https://avatars.githubusercontent.com/u/14982166?v=4?s=100" width="100px;" alt=""/><br /><sub><b>wokija</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/wokija"><img src="https://avatars.githubusercontent.com/u/14982166?v=4?s=100" width="100px;" alt=""/><br /><sub><b>wokija</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/ysakamoto"><img src="https://avatars3.githubusercontent.com/u/1331642?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ysakamoto</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
<td align="center"><a href="https://github.com/ZDVokoun"><img src="https://avatars.githubusercontent.com/u/76393152?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ZDVokoun</b></sub></a><br /><a href="https://crowdin.com/project/instagrabber" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
|
@ -11,24 +11,25 @@ import androidx.recyclerview.widget.ListAdapter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
|
||||
import awais.instagrabber.databinding.ItemNotificationBinding;
|
||||
import awais.instagrabber.models.NotificationModel;
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
import awais.instagrabber.repositories.responses.Notification;
|
||||
|
||||
public final class NotificationsAdapter extends ListAdapter<NotificationModel, NotificationViewHolder> {
|
||||
public final class NotificationsAdapter extends ListAdapter<Notification, NotificationViewHolder> {
|
||||
private final OnNotificationClickListener notificationClickListener;
|
||||
|
||||
private static final DiffUtil.ItemCallback<NotificationModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<NotificationModel>() {
|
||||
private static final DiffUtil.ItemCallback<Notification> DIFF_CALLBACK = new DiffUtil.ItemCallback<Notification>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) {
|
||||
return oldItem.getId().equals(newItem.getId());
|
||||
public boolean areItemsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
|
||||
return oldItem.getPk().equals(newItem.getPk());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) {
|
||||
return oldItem.getId().equals(newItem.getId());
|
||||
public boolean areContentsTheSame(@NonNull final Notification oldItem, @NonNull final Notification newItem) {
|
||||
return oldItem.getPk().equals(newItem.getPk());
|
||||
}
|
||||
};
|
||||
|
||||
@ -47,12 +48,12 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final NotificationViewHolder holder, final int position) {
|
||||
final NotificationModel notificationModel = getItem(position);
|
||||
holder.bind(notificationModel, notificationClickListener);
|
||||
final Notification Notification = getItem(position);
|
||||
holder.bind(Notification, notificationClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitList(@Nullable final List<NotificationModel> list, @Nullable final Runnable commitCallback) {
|
||||
public void submitList(@Nullable final List<Notification> list, @Nullable final Runnable commitCallback) {
|
||||
if (list == null) {
|
||||
super.submitList(null, commitCallback);
|
||||
return;
|
||||
@ -61,7 +62,7 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitList(@Nullable final List<NotificationModel> list) {
|
||||
public void submitList(@Nullable final List<Notification> list) {
|
||||
if (list == null) {
|
||||
super.submitList(null);
|
||||
return;
|
||||
@ -69,8 +70,10 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
|
||||
super.submitList(sort(list));
|
||||
}
|
||||
|
||||
private List<NotificationModel> sort(final List<NotificationModel> list) {
|
||||
final List<NotificationModel> listCopy = new ArrayList<>(list);
|
||||
private List<Notification> sort(final List<Notification> list) {
|
||||
final List<Notification> listCopy = new ArrayList<>(list).stream()
|
||||
.filter(i -> i.getType() != null)
|
||||
.collect(Collectors.toList());
|
||||
Collections.sort(listCopy, (o1, o2) -> {
|
||||
// keep requests at top
|
||||
if (o1.getType() == o2.getType()
|
||||
@ -79,16 +82,16 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
|
||||
else if (o1.getType() == NotificationType.REQUEST) return -1;
|
||||
else if (o2.getType() == NotificationType.REQUEST) return 1;
|
||||
// timestamp
|
||||
return Long.compare(o2.getTimestamp(), o1.getTimestamp());
|
||||
return Double.compare(o2.getArgs().getTimestamp(), o1.getArgs().getTimestamp());
|
||||
});
|
||||
return listCopy;
|
||||
}
|
||||
|
||||
public interface OnNotificationClickListener {
|
||||
void onNotificationClick(final NotificationModel model);
|
||||
void onNotificationClick(final Notification model);
|
||||
|
||||
void onProfileClick(final String username);
|
||||
|
||||
void onPreviewClick(final NotificationModel model);
|
||||
void onPreviewClick(final Notification model);
|
||||
}
|
||||
}
|
@ -8,8 +8,9 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
|
||||
import awais.instagrabber.databinding.ItemNotificationBinding;
|
||||
import awais.instagrabber.models.NotificationModel;
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
import awais.instagrabber.repositories.responses.Notification;
|
||||
import awais.instagrabber.repositories.responses.NotificationArgs;
|
||||
|
||||
public final class NotificationViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemNotificationBinding binding;
|
||||
@ -19,22 +20,23 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
public void bind(final NotificationModel model,
|
||||
public void bind(final Notification model,
|
||||
final OnNotificationClickListener notificationClickListener) {
|
||||
if (model == null) return;
|
||||
int text = -1;
|
||||
CharSequence subtext = null;
|
||||
final NotificationArgs args = model.getArgs();
|
||||
switch (model.getType()) {
|
||||
case LIKE:
|
||||
text = R.string.liked_notif;
|
||||
break;
|
||||
case COMMENT:
|
||||
text = R.string.comment_notif;
|
||||
subtext = model.getText();
|
||||
subtext = args.getText();
|
||||
break;
|
||||
case COMMENT_MENTION:
|
||||
text = R.string.mention_notif;
|
||||
subtext = model.getText();
|
||||
subtext = args.getText();
|
||||
break;
|
||||
case TAGGED:
|
||||
text = R.string.tagged_notif;
|
||||
@ -44,45 +46,50 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder {
|
||||
break;
|
||||
case REQUEST:
|
||||
text = R.string.request_notif;
|
||||
subtext = model.getText();
|
||||
subtext = args.getText();
|
||||
break;
|
||||
case COMMENT_LIKE:
|
||||
case TAGGED_COMMENT:
|
||||
case RESPONDED_STORY:
|
||||
subtext = model.getText();
|
||||
subtext = args.getText();
|
||||
break;
|
||||
case AYML:
|
||||
subtext = model.getPreviewPic();
|
||||
subtext = args.getFullName();
|
||||
break;
|
||||
}
|
||||
binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? model.getText() : subtext);
|
||||
binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? args.getText() : subtext);
|
||||
if (text == -1 && subtext != null) {
|
||||
binding.tvComment.setText(subtext);
|
||||
binding.tvComment.setVisibility(TextUtils.isEmpty(subtext) ? View.GONE : View.VISIBLE);
|
||||
binding.tvComment.setText(args.getText());
|
||||
binding.tvComment.setVisibility(TextUtils.isEmpty(args.getText()) || args.getText().equals(args.getFullName())
|
||||
? View.GONE : View.VISIBLE);
|
||||
binding.tvSubComment.setText(subtext);
|
||||
binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE);
|
||||
} else if (text != -1) {
|
||||
binding.tvComment.setText(text);
|
||||
binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
binding.tvDate.setVisibility(model.getType() == NotificationType.AYML ? View.GONE : View.VISIBLE);
|
||||
if (model.getType() != NotificationType.REQUEST && model.getType() != NotificationType.AYML) {
|
||||
binding.tvDate.setText(model.getDateTime());
|
||||
binding.tvDate.setText(args.getDateTime());
|
||||
}
|
||||
|
||||
binding.tvUsername.setText(model.getUsername());
|
||||
binding.ivProfilePic.setImageURI(model.getProfilePic());
|
||||
binding.isVerified.setVisibility(args.isVerified() ? View.VISIBLE : View.GONE);
|
||||
|
||||
binding.tvUsername.setText(args.getUsername());
|
||||
binding.ivProfilePic.setImageURI(args.getProfilePic());
|
||||
binding.ivProfilePic.setOnClickListener(v -> {
|
||||
if (notificationClickListener == null) return;
|
||||
notificationClickListener.onProfileClick(model.getUsername());
|
||||
notificationClickListener.onProfileClick(args.getUsername());
|
||||
});
|
||||
|
||||
if (model.getType() == NotificationType.AYML) {
|
||||
binding.ivPreviewPic.setVisibility(View.GONE);
|
||||
} else if (TextUtils.isEmpty(model.getPreviewPic())) {
|
||||
} else if (args.getMedia() == null) {
|
||||
binding.ivPreviewPic.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
binding.ivPreviewPic.setVisibility(View.VISIBLE);
|
||||
binding.ivPreviewPic.setImageURI(model.getPreviewPic());
|
||||
binding.ivPreviewPic.setImageURI(args.getMedia().get(0).getImage());
|
||||
binding.ivPreviewPic.setOnClickListener(v -> {
|
||||
if (notificationClickListener == null) return;
|
||||
notificationClickListener.onPreviewClick(model);
|
||||
|
@ -144,7 +144,7 @@ public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
|
||||
private void setReadState(@NonNull final DirectThread thread) {
|
||||
final boolean read = DMUtils.isRead(thread);
|
||||
binding.unread.setVisibility(read ? View.GONE : View.VISIBLE);
|
||||
binding.threadTitle.setTypeface(binding.threadTitle.getTypeface(), read ? Typeface.NORMAL : Typeface.BOLD);
|
||||
binding.subtitle.setTypeface(binding.subtitle.getTypeface(), read ? Typeface.NORMAL : Typeface.BOLD);
|
||||
binding.threadTitle.setTypeface(null, read ? Typeface.NORMAL : Typeface.BOLD);
|
||||
binding.subtitle.setTypeface(null, read ? Typeface.NORMAL : Typeface.BOLD);
|
||||
}
|
||||
}
|
@ -115,7 +115,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
owner.getString("profile_pic_url"),
|
||||
null,
|
||||
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
|
||||
false, false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null);
|
||||
false, false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null,
|
||||
null, null);
|
||||
final JSONObject likedBy = childComment.optJSONObject("edge_liked_by");
|
||||
commentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
|
||||
childComment.getString("text"),
|
||||
@ -193,7 +194,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
null,
|
||||
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
|
||||
owner.optBoolean("is_verified"),
|
||||
false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null);
|
||||
false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null, null, null,
|
||||
null);
|
||||
final JSONObject likedBy = comment.optJSONObject("edge_liked_by");
|
||||
final String commentId = comment.getString(Constants.EXTRAS_ID);
|
||||
final CommentModel commentModel = new CommentModel(commentId,
|
||||
@ -235,7 +237,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
|
||||
null,
|
||||
new FriendshipStatus(false, false, false, false, false, false, false, false, false, false),
|
||||
tempJsonObject.optBoolean("is_verified"), false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0,
|
||||
null, null);
|
||||
null, null, null, null, null);
|
||||
|
||||
tempJsonObject = childComment.optJSONObject("edge_liked_by");
|
||||
childCommentModels.add(new CommentModel(childComment.getString(Constants.EXTRAS_ID),
|
||||
|
@ -1,139 +0,0 @@
|
||||
package awais.instagrabber.asyncs;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.NetworkUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public class GetActivityAsyncTask extends AsyncTask<String, Void, GetActivityAsyncTask.NotificationCounts> {
|
||||
private static final String TAG = "GetActivityAsyncTask";
|
||||
|
||||
private final OnTaskCompleteListener onTaskCompleteListener;
|
||||
|
||||
public GetActivityAsyncTask(final OnTaskCompleteListener onTaskCompleteListener) {
|
||||
this.onTaskCompleteListener = onTaskCompleteListener;
|
||||
}
|
||||
|
||||
/*
|
||||
This needs to be redone to fetch i inbox instead
|
||||
Within inbox, data is (body JSON => counts)
|
||||
Then we have these counts:
|
||||
new_posts, activity_feed_dot_badge, relationships, campaign_notification
|
||||
usertags, likes, comment_likes, shopping_notification, comments
|
||||
photos_of_you (not sure about difference to usertags), requests
|
||||
*/
|
||||
|
||||
protected NotificationCounts doInBackground(final String... cookiesArray) {
|
||||
if (cookiesArray == null) return null;
|
||||
final String cookie = cookiesArray[0];
|
||||
if (TextUtils.isEmpty(cookie)) return null;
|
||||
final long uid = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final String url = "https://www.instagram.com/graphql/query/?query_hash=0f318e8cfff9cc9ef09f88479ff571fb"
|
||||
+ "&variables={\"id\":\"" + uid + "\"}";
|
||||
HttpURLConnection urlConnection = null;
|
||||
try {
|
||||
urlConnection = (HttpURLConnection) new URL(url).openConnection();
|
||||
urlConnection.setUseCaches(false);
|
||||
urlConnection.setRequestProperty("User-Agent", Utils.settingsHelper.getString(Constants.BROWSER_UA));
|
||||
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
|
||||
urlConnection.connect();
|
||||
if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||
return null;
|
||||
}
|
||||
final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(urlConnection))
|
||||
.getJSONObject("data")
|
||||
.getJSONObject("user")
|
||||
.getJSONObject("edge_activity_count")
|
||||
.getJSONArray("edges")
|
||||
.getJSONObject(0)
|
||||
.getJSONObject("node");
|
||||
return new NotificationCounts(
|
||||
data.getInt("relationships"),
|
||||
data.getInt("usertags"),
|
||||
data.getInt("comments"),
|
||||
data.getInt("comment_likes"),
|
||||
data.getInt("likes")
|
||||
);
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, "Error", ex);
|
||||
} finally {
|
||||
if (urlConnection != null) {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final NotificationCounts result) {
|
||||
if (onTaskCompleteListener == null) return;
|
||||
onTaskCompleteListener.onTaskComplete(result);
|
||||
}
|
||||
|
||||
public static class NotificationCounts {
|
||||
private final int relationshipsCount;
|
||||
private final int userTagsCount;
|
||||
private final int commentsCount;
|
||||
private final int commentLikesCount;
|
||||
private final int likesCount;
|
||||
|
||||
public NotificationCounts(final int relationshipsCount,
|
||||
final int userTagsCount,
|
||||
final int commentsCount,
|
||||
final int commentLikesCount,
|
||||
final int likesCount) {
|
||||
this.relationshipsCount = relationshipsCount;
|
||||
this.userTagsCount = userTagsCount;
|
||||
this.commentsCount = commentsCount;
|
||||
this.commentLikesCount = commentLikesCount;
|
||||
this.likesCount = likesCount;
|
||||
}
|
||||
|
||||
public int getRelationshipsCount() {
|
||||
return relationshipsCount;
|
||||
}
|
||||
|
||||
public int getUserTagsCount() {
|
||||
return userTagsCount;
|
||||
}
|
||||
|
||||
public int getCommentsCount() {
|
||||
return commentsCount;
|
||||
}
|
||||
|
||||
public int getCommentLikesCount() {
|
||||
return commentLikesCount;
|
||||
}
|
||||
|
||||
public int getLikesCount() {
|
||||
return likesCount;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NotificationCounts{" +
|
||||
"relationshipsCount=" + relationshipsCount +
|
||||
", userTagsCount=" + userTagsCount +
|
||||
", commentsCount=" + commentsCount +
|
||||
", commentLikesCount=" + commentLikesCount +
|
||||
", likesCount=" + likesCount +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnTaskCompleteListener {
|
||||
void onTaskComplete(final NotificationCounts result);
|
||||
}
|
||||
}
|
@ -8,35 +8,35 @@ import java.util.List;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.NotificationModel;
|
||||
import awais.instagrabber.repositories.responses.Notification;
|
||||
import awais.instagrabber.webservices.NewsService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
import awaisomereport.LogCollector;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.logCollector;
|
||||
|
||||
public final class NotificationsFetcher extends AsyncTask<Void, Void, List<NotificationModel>> {
|
||||
public final class NotificationsFetcher extends AsyncTask<Void, Void, List<Notification>> {
|
||||
private static final String TAG = "NotificationsFetcher";
|
||||
|
||||
private final FetchListener<List<NotificationModel>> fetchListener;
|
||||
private final FetchListener<List<Notification>> fetchListener;
|
||||
private final NewsService newsService;
|
||||
private final boolean markAsSeen;
|
||||
private boolean fetchedWeb = false;
|
||||
|
||||
public NotificationsFetcher(final boolean markAsSeen,
|
||||
final FetchListener<List<NotificationModel>> fetchListener) {
|
||||
final FetchListener<List<Notification>> fetchListener) {
|
||||
this.markAsSeen = markAsSeen;
|
||||
this.fetchListener = fetchListener;
|
||||
newsService = NewsService.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<NotificationModel> doInBackground(final Void... voids) {
|
||||
List<NotificationModel> notificationModels = new ArrayList<>();
|
||||
protected List<Notification> doInBackground(final Void... voids) {
|
||||
List<Notification> notificationModels = new ArrayList<>();
|
||||
|
||||
newsService.fetchAppInbox(markAsSeen, new ServiceCallback<List<NotificationModel>>() {
|
||||
newsService.fetchAppInbox(markAsSeen, new ServiceCallback<List<Notification>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<NotificationModel> result) {
|
||||
public void onSuccess(final List<Notification> result) {
|
||||
if (result == null) return;
|
||||
notificationModels.addAll(result);
|
||||
if (fetchedWeb) {
|
||||
@ -44,7 +44,7 @@ public final class NotificationsFetcher extends AsyncTask<Void, Void, List<Notif
|
||||
}
|
||||
else {
|
||||
fetchedWeb = true;
|
||||
newsService.fetchWebInbox(markAsSeen, this);
|
||||
newsService.fetchWebInbox(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,51 +0,0 @@
|
||||
package awais.instagrabber.asyncs;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
import awais.instagrabber.utils.NetworkUtils;
|
||||
|
||||
public class SeenAction extends AsyncTask<Void, Void, Void> {
|
||||
private static final String TAG = "SeenAction";
|
||||
|
||||
private final String cookie;
|
||||
private final StoryModel storyModel;
|
||||
|
||||
public SeenAction(final String cookie, final StoryModel storyModel) {
|
||||
this.cookie = cookie;
|
||||
this.storyModel = storyModel;
|
||||
}
|
||||
|
||||
protected Void doInBackground(Void... voids) {
|
||||
final String url = "https://www.instagram.com/stories/reel/seen";
|
||||
try {
|
||||
final String urlParameters = "reelMediaId=" + storyModel.getStoryMediaId().split("_")[0]
|
||||
+ "&reelMediaOwnerId=" + storyModel.getUserId()
|
||||
+ "&reelId=" + storyModel.getUserId()
|
||||
+ "&reelMediaTakenAt=" + storyModel.getTimestamp()
|
||||
+ "&viewSeenAt=" + storyModel.getTimestamp();
|
||||
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
|
||||
urlConnection.setRequestMethod("POST");
|
||||
urlConnection.setUseCaches(false);
|
||||
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
|
||||
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
|
||||
urlConnection.setDoOutput(true);
|
||||
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
|
||||
wr.writeBytes(urlParameters);
|
||||
wr.flush();
|
||||
wr.close();
|
||||
urlConnection.connect();
|
||||
Log.d(TAG, urlConnection.getResponseCode() + " " + NetworkUtils.readFromConnection(urlConnection));
|
||||
urlConnection.disconnect();
|
||||
} catch (Throwable ex) {
|
||||
Log.e(TAG, "Error", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -276,7 +276,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
tagsService = TagsService.getInstance();
|
||||
storiesService = StoriesService.getInstance();
|
||||
storiesService = StoriesService.getInstance(null, 0L, null);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
storiesService = StoriesService.getInstance();
|
||||
storiesService = StoriesService.getInstance(null, 0L, null);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,9 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
@ -33,11 +35,13 @@ import awais.instagrabber.asyncs.NotificationsFetcher;
|
||||
import awais.instagrabber.databinding.FragmentNotificationsViewerBinding;
|
||||
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.NotificationModel;
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
import awais.instagrabber.repositories.requests.StoryViewerOptions;
|
||||
import awais.instagrabber.repositories.responses.FriendshipChangeResponse;
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
import awais.instagrabber.repositories.responses.Notification;
|
||||
import awais.instagrabber.repositories.responses.NotificationArgs;
|
||||
import awais.instagrabber.repositories.responses.NotificationImage;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
@ -53,16 +57,36 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
public final class NotificationsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "NotificationsViewer";
|
||||
|
||||
private AppCompatActivity fragmentActivity;
|
||||
private FragmentNotificationsViewerBinding binding;
|
||||
private SwipeRefreshLayout root;
|
||||
private boolean shouldRefresh = true;
|
||||
private NotificationViewModel notificationViewModel;
|
||||
private FriendshipService friendshipService;
|
||||
private MediaService mediaService;
|
||||
private String csrfToken;
|
||||
private NewsService newsService;
|
||||
private String csrfToken, deviceUuid;
|
||||
private String type;
|
||||
private long targetId;
|
||||
private Context context;
|
||||
|
||||
private final ServiceCallback<List<Notification>> cb = new ServiceCallback<List<Notification>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<Notification> notificationModels) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
notificationViewModel.getList().postValue(notificationModels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
try {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
catch(Throwable e) {}
|
||||
}
|
||||
};
|
||||
|
||||
private final OnNotificationClickListener clickListener = new OnNotificationClickListener() {
|
||||
@Override
|
||||
public void onProfileClick(final String username) {
|
||||
@ -70,14 +94,18 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviewClick(final NotificationModel model) {
|
||||
public void onPreviewClick(final Notification model) {
|
||||
final NotificationImage notificationImage = model.getArgs().getMedia().get(0);
|
||||
final long mediaId = Long.valueOf(notificationImage.getId().split("_")[0]);
|
||||
if (model.getType() == NotificationType.RESPONDED_STORY) {
|
||||
final NavDirections action = NotificationsViewerFragmentDirections
|
||||
.actionNotificationsViewerFragmentToStoryViewerFragment(StoryViewerOptions.forStory(model.getPostId(),
|
||||
model.getUsername()));
|
||||
.actionNotificationsViewerFragmentToStoryViewerFragment(
|
||||
StoryViewerOptions.forStory(
|
||||
mediaId,
|
||||
model.getArgs().getUsername()));
|
||||
NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);
|
||||
} else {
|
||||
mediaService.fetch(model.getPostId(), new ServiceCallback<Media>() {
|
||||
mediaService.fetch(mediaId, new ServiceCallback<Media>() {
|
||||
@Override
|
||||
public void onSuccess(final Media feedModel) {
|
||||
final PostViewV2Fragment fragment = PostViewV2Fragment
|
||||
@ -95,13 +123,14 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationClick(final NotificationModel model) {
|
||||
public void onNotificationClick(final Notification model) {
|
||||
if (model == null) return;
|
||||
final String username = model.getUsername();
|
||||
final NotificationArgs args = model.getArgs();
|
||||
final String username = args.getUsername();
|
||||
if (model.getType() == NotificationType.FOLLOW || model.getType() == NotificationType.AYML) {
|
||||
openProfile(username);
|
||||
} else {
|
||||
final SpannableString title = new SpannableString(username + (TextUtils.isEmpty(model.getText()) ? "" : (":\n" + model.getText())));
|
||||
final SpannableString title = new SpannableString(username + (TextUtils.isEmpty(args.getText()) ? "" : (":\n" + args.getText())));
|
||||
title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
String[] commentDialogList;
|
||||
@ -110,7 +139,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
getString(R.string.open_profile),
|
||||
getString(R.string.view_story)
|
||||
};
|
||||
} else if (model.getPostId() > 0) {
|
||||
} else if (args.getMedia() != null) {
|
||||
commentDialogList = new String[]{
|
||||
getString(R.string.open_profile),
|
||||
getString(R.string.view_post)
|
||||
@ -131,7 +160,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
break;
|
||||
case 1:
|
||||
if (model.getType() == NotificationType.REQUEST) {
|
||||
friendshipService.approve(model.getUserId(), new ServiceCallback<FriendshipChangeResponse>() {
|
||||
friendshipService.approve(args.getUserId(), new ServiceCallback<FriendshipChangeResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipChangeResponse result) {
|
||||
onRefresh();
|
||||
@ -148,7 +177,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
clickListener.onPreviewClick(model);
|
||||
break;
|
||||
case 2:
|
||||
friendshipService.ignore(model.getUserId(), new ServiceCallback<FriendshipChangeResponse>() {
|
||||
friendshipService.ignore(args.getUserId(), new ServiceCallback<FriendshipChangeResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final FriendshipChangeResponse result) {
|
||||
onRefresh();
|
||||
@ -174,6 +203,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (AppCompatActivity) requireActivity();
|
||||
context = getContext();
|
||||
if (context == null) return;
|
||||
NotificationManagerCompat.from(context.getApplicationContext()).cancel(Constants.ACTIVITY_NOTIFICATION_ID);
|
||||
@ -183,9 +213,10 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
}
|
||||
mediaService = MediaService.getInstance(null, null, 0);
|
||||
final long userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
friendshipService = FriendshipService.getInstance(deviceUuid, csrfToken, userId);
|
||||
newsService = NewsService.getInstance();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -210,6 +241,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
private void init() {
|
||||
final NotificationsViewerFragmentArgs fragmentArgs = NotificationsViewerFragmentArgs.fromBundle(getArguments());
|
||||
type = fragmentArgs.getType();
|
||||
targetId = fragmentArgs.getTargetId();
|
||||
final Context context = getContext();
|
||||
CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE));
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
@ -224,11 +256,13 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
switch (type) {
|
||||
case "notif":
|
||||
new NotificationsFetcher(true, new FetchListener<List<NotificationModel>>() {
|
||||
if (actionBar != null) actionBar.setTitle(R.string.action_notif);
|
||||
new NotificationsFetcher(true, new FetchListener<List<Notification>>() {
|
||||
@Override
|
||||
public void onResult(final List<NotificationModel> notificationModels) {
|
||||
public void onResult(final List<Notification> notificationModels) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
notificationViewModel.getList().postValue(notificationModels);
|
||||
}
|
||||
@ -244,23 +278,12 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
break;
|
||||
case "ayml":
|
||||
final NewsService newsService = NewsService.getInstance();
|
||||
newsService.fetchSuggestions(csrfToken, new ServiceCallback<List<NotificationModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<NotificationModel> notificationModels) {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
notificationViewModel.getList().postValue(notificationModels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
try {
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
catch(Throwable e) {}
|
||||
}
|
||||
});
|
||||
if (actionBar != null) actionBar.setTitle(R.string.action_ayml);
|
||||
newsService.fetchSuggestions(csrfToken, deviceUuid, cb);
|
||||
break;
|
||||
case "chaining":
|
||||
if (actionBar != null) actionBar.setTitle(R.string.action_ayml);
|
||||
newsService.fetchChaining(targetId, cb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr
|
||||
fragmentActivity = (AppCompatActivity) requireActivity();
|
||||
context = getContext();
|
||||
if (context == null) return;
|
||||
storiesService = StoriesService.getInstance();
|
||||
storiesService = StoriesService.getInstance(null, 0L, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -71,7 +71,6 @@ import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.StoriesAdapter;
|
||||
import awais.instagrabber.asyncs.CreateThreadAction;
|
||||
import awais.instagrabber.asyncs.PostFetcher;
|
||||
import awais.instagrabber.asyncs.SeenAction;
|
||||
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
|
||||
import awais.instagrabber.databinding.FragmentStoryViewerBinding;
|
||||
import awais.instagrabber.fragments.main.ProfileFragmentDirections;
|
||||
@ -151,17 +150,16 @@ public class StoryViewerFragment extends Fragment {
|
||||
private DirectMessagesService directMessagesService;
|
||||
|
||||
private final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
private final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
private final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
private final String deviceId = settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
private StoryViewerOptions options;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final String deviceId = settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
fragmentActivity = (AppCompatActivity) requireActivity();
|
||||
storiesService = StoriesService.getInstance();
|
||||
if (csrfToken == null) return;
|
||||
storiesService = StoriesService.getInstance(csrfToken, userIdFromCookie, deviceId);
|
||||
directMessagesService = DirectMessagesService.getInstance(csrfToken, userIdFromCookie, deviceId);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
@ -478,14 +476,11 @@ public class StoryViewerFragment extends Fragment {
|
||||
poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
|
||||
poll.getRightChoice() + " (" + poll.getRightCount() + ")"
|
||||
}), (d, w) -> {
|
||||
if (!TextUtils.isEmpty(cookie)) {
|
||||
sticking = true;
|
||||
storiesService.respondToPoll(
|
||||
currentStory.getStoryMediaId().split("_")[0],
|
||||
poll.getId(),
|
||||
w,
|
||||
userIdFromCookie,
|
||||
csrfToken,
|
||||
new ServiceCallback<StoryStickerResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryStickerResponse result) {
|
||||
@ -507,7 +502,6 @@ public class StoryViewerFragment extends Fragment {
|
||||
catch (Exception ignored) {}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.cancel, null)
|
||||
.show();
|
||||
@ -525,8 +519,6 @@ public class StoryViewerFragment extends Fragment {
|
||||
currentStory.getStoryMediaId().split("_")[0],
|
||||
question.getId(),
|
||||
input.getText().toString(),
|
||||
userIdFromCookie,
|
||||
csrfToken,
|
||||
new ServiceCallback<StoryStickerResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryStickerResponse result) {
|
||||
@ -565,14 +557,12 @@ public class StoryViewerFragment extends Fragment {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion())
|
||||
.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, choices), (d, w) -> {
|
||||
if (quiz.getMyChoice() == -1 && !TextUtils.isEmpty(cookie)) {
|
||||
if (quiz.getMyChoice() == -1) {
|
||||
sticking = true;
|
||||
storiesService.respondToQuiz(
|
||||
currentStory.getStoryMediaId().split("_")[0],
|
||||
quiz.getId(),
|
||||
w,
|
||||
userIdFromCookie,
|
||||
csrfToken,
|
||||
new ServiceCallback<StoryStickerResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryStickerResponse result) {
|
||||
@ -643,8 +633,6 @@ public class StoryViewerFragment extends Fragment {
|
||||
currentStory.getStoryMediaId().split("_")[0],
|
||||
slider.getId(),
|
||||
sliderValue,
|
||||
userIdFromCookie,
|
||||
csrfToken,
|
||||
new ServiceCallback<StoryStickerResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final StoryStickerResponse result) {
|
||||
@ -868,7 +856,7 @@ public class StoryViewerFragment extends Fragment {
|
||||
binding.poll.setTag(poll);
|
||||
|
||||
question = currentStory.getQuestion();
|
||||
binding.answer.setVisibility((question != null && !TextUtils.isEmpty(cookie)) ? View.VISIBLE : View.GONE);
|
||||
binding.answer.setVisibility((question != null) ? View.VISIBLE : View.GONE);
|
||||
binding.answer.setTag(question);
|
||||
|
||||
mentions = currentStory.getMentions();
|
||||
@ -909,7 +897,11 @@ public class StoryViewerFragment extends Fragment {
|
||||
actionBar.setSubtitle(Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L)));
|
||||
}
|
||||
|
||||
if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction(cookie, currentStory).execute();
|
||||
if (settingsHelper.getBoolean(MARK_AS_SEEN))
|
||||
storiesService.seen(currentStory.getStoryMediaId(),
|
||||
currentStory.getTimestamp(),
|
||||
System.currentTimeMillis() / 1000,
|
||||
null);
|
||||
}
|
||||
|
||||
private void downloadStory() {
|
||||
@ -947,7 +939,7 @@ public class StoryViewerFragment extends Fragment {
|
||||
if (menuDownload != null) {
|
||||
menuDownload.setVisible(true);
|
||||
}
|
||||
if (currentStory.canReply() && menuDm != null && !TextUtils.isEmpty(cookie)) {
|
||||
if (currentStory.canReply() && menuDm != null) {
|
||||
menuDm.setVisible(true);
|
||||
}
|
||||
binding.progressView.setVisibility(View.GONE);
|
||||
@ -980,7 +972,7 @@ public class StoryViewerFragment extends Fragment {
|
||||
@NonNull final LoadEventInfo loadEventInfo,
|
||||
@NonNull final MediaLoadData mediaLoadData) {
|
||||
if (menuDownload != null) menuDownload.setVisible(true);
|
||||
if (currentStory.canReply() && menuDm != null && !TextUtils.isEmpty(cookie))
|
||||
if (currentStory.canReply() && menuDm != null)
|
||||
menuDm.setVisible(true);
|
||||
binding.progressView.setVisibility(View.GONE);
|
||||
}
|
||||
@ -991,7 +983,7 @@ public class StoryViewerFragment extends Fragment {
|
||||
@NonNull final LoadEventInfo loadEventInfo,
|
||||
@NonNull final MediaLoadData mediaLoadData) {
|
||||
if (menuDownload != null) menuDownload.setVisible(true);
|
||||
if (currentStory.canReply() && menuDm != null && !TextUtils.isEmpty(cookie))
|
||||
if (currentStory.canReply() && menuDm != null)
|
||||
menuDm.setVisible(true);
|
||||
binding.progressView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
@ -259,7 +259,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
storiesService = StoriesService.getInstance();
|
||||
storiesService = StoriesService.getInstance(null, 0L, null);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ import awais.instagrabber.repositories.responses.FriendshipRestrictResponse;
|
||||
import awais.instagrabber.repositories.responses.FriendshipStatus;
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.UserProfileContextLink;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
@ -123,6 +124,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
private HighlightsViewModel highlightsViewModel;
|
||||
private MenuItem blockMenuItem;
|
||||
private MenuItem restrictMenuItem;
|
||||
private MenuItem chainingMenuItem;
|
||||
private boolean highlightsFetching;
|
||||
private boolean postsSetupDone = false;
|
||||
private Set<Media> selectedFeedModels;
|
||||
@ -306,7 +308,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
friendshipService = isLoggedIn ? FriendshipService.getInstance(deviceUuid, csrfToken, userId) : null;
|
||||
storiesService = isLoggedIn ? StoriesService.getInstance() : null;
|
||||
storiesService = isLoggedIn ? StoriesService.getInstance(null, 0L, null) : null;
|
||||
mediaService = isLoggedIn ? MediaService.getInstance(null, null, 0) : null;
|
||||
accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
|
||||
favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
|
||||
@ -362,13 +364,33 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
inflater.inflate(R.menu.profile_menu, menu);
|
||||
blockMenuItem = menu.findItem(R.id.block);
|
||||
if (blockMenuItem != null) {
|
||||
if (profileModel != null) {
|
||||
blockMenuItem.setVisible(!Objects.equals(profileModel.getPk(), CookieUtils.getUserIdFromCookie(cookie)));
|
||||
blockMenuItem.setTitle(profileModel.getFriendshipStatus().isBlocking() ? R.string.unblock : R.string.block);
|
||||
} else {
|
||||
blockMenuItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
restrictMenuItem = menu.findItem(R.id.restrict);
|
||||
if (restrictMenuItem != null) {
|
||||
if (profileModel != null) {
|
||||
restrictMenuItem.setVisible(!Objects.equals(profileModel.getPk(), CookieUtils.getUserIdFromCookie(cookie)));
|
||||
restrictMenuItem.setTitle(profileModel.getFriendshipStatus().isRestricted() ? R.string.unrestrict : R.string.restrict);
|
||||
}
|
||||
else {
|
||||
restrictMenuItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
chainingMenuItem = menu.findItem(R.id.chaining);
|
||||
if (chainingMenuItem != null) {
|
||||
if (profileModel != null) {
|
||||
chainingMenuItem.setVisible(!Objects.equals(profileModel.getPk(), CookieUtils.getUserIdFromCookie(cookie)));
|
||||
}
|
||||
else {
|
||||
chainingMenuItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
@ -431,11 +453,18 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (item.getItemId() == R.id.chaining) {
|
||||
if (!isLoggedIn) return false;
|
||||
final NavDirections navDirections = ProfileFragmentDirections.actionGlobalNotificationsViewerFragment("chaining", profileModel.getPk());
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
profileDetailsBinding.countsBarrier.setVisibility(View.GONE);
|
||||
profileDetailsBinding.mainProfileImage.setVisibility(View.INVISIBLE);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
@ -653,6 +682,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
profileDetailsBinding.mainProfileImage.setImageURI(profileModel.getProfilePicUrl());
|
||||
profileDetailsBinding.mainProfileImage.setVisibility(View.VISIBLE);
|
||||
|
||||
profileDetailsBinding.countsBarrier.setVisibility(View.VISIBLE);
|
||||
|
||||
final long followersCount = profileModel.getFollowerCount();
|
||||
final long followingCount = profileModel.getFollowingCount();
|
||||
|
||||
@ -691,7 +722,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
: profileModel.getFullName());
|
||||
|
||||
final String biography = profileModel.getBiography();
|
||||
if (!TextUtils.isEmpty(biography)) {
|
||||
if (TextUtils.isEmpty(biography)) {
|
||||
profileDetailsBinding.mainBiography.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
profileDetailsBinding.mainBiography.setVisibility(View.VISIBLE);
|
||||
profileDetailsBinding.mainBiography.setText(biography);
|
||||
profileDetailsBinding.mainBiography.addOnHashtagListener(autoLinkItem -> {
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
@ -756,6 +791,27 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
String profileContext = profileModel.getProfileContext();
|
||||
if (TextUtils.isEmpty(profileContext)) {
|
||||
profileDetailsBinding.profileContext.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
profileDetailsBinding.profileContext.setVisibility(View.VISIBLE);
|
||||
final List<UserProfileContextLink> userProfileContextLinks = profileModel.getProfileContextLinks();
|
||||
for (int i = 0; i < userProfileContextLinks.size(); i++) {
|
||||
final UserProfileContextLink link = userProfileContextLinks.get(i);
|
||||
if (link.getUsername() != null)
|
||||
profileContext = profileContext.substring(0, link.getStart() + i)
|
||||
+ "@" + profileContext.substring(link.getStart() + i);
|
||||
}
|
||||
profileDetailsBinding.profileContext.setText(profileContext);
|
||||
profileDetailsBinding.profileContext.addOnMentionClickListener(autoLinkItem -> {
|
||||
final String originalText = autoLinkItem.getOriginalText().trim();
|
||||
navigateToProfile(originalText);
|
||||
});
|
||||
}
|
||||
|
||||
final String url = profileModel.getExternalUrl();
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
profileDetailsBinding.mainUrl.setVisibility(View.GONE);
|
||||
@ -830,13 +886,13 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
if (profileModel.getFriendshipStatus().isFollowing()) {
|
||||
profileDetailsBinding.btnFollow.setText(R.string.unfollow);
|
||||
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
profileDetailsBinding.btnFollow.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
} else if (profileModel.getFriendshipStatus().isOutgoingRequest()) {
|
||||
profileDetailsBinding.btnFollow.setText(R.string.cancel);
|
||||
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
profileDetailsBinding.btnFollow.setChipIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||
} else {
|
||||
profileDetailsBinding.btnFollow.setText(R.string.follow);
|
||||
profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_24);
|
||||
profileDetailsBinding.btnFollow.setChipIconResource(R.drawable.ic_outline_person_add_24);
|
||||
}
|
||||
if (restrictMenuItem != null) {
|
||||
restrictMenuItem.setVisible(true);
|
||||
@ -854,16 +910,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
blockMenuItem.setTitle(R.string.block);
|
||||
}
|
||||
}
|
||||
if (chainingMenuItem != null && !Objects.equals(profileId, myId)) {
|
||||
chainingMenuItem.setVisible(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!isReallyPrivate() && restrictMenuItem != null) {
|
||||
restrictMenuItem.setVisible(true);
|
||||
if (profileModel.getFriendshipStatus().isRestricted()) {
|
||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||
} else {
|
||||
restrictMenuItem.setTitle(R.string.restrict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchStoryAndHighlights(final long profileId) {
|
||||
|
@ -134,12 +134,12 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
|
||||
screen.addPreference(getDivider(context));
|
||||
if (isLoggedIn) {
|
||||
screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif");
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif", 0l);
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml");
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml", 0l);
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
return true;
|
||||
}));
|
||||
|
@ -1,79 +0,0 @@
|
||||
package awais.instagrabber.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public final class NotificationModel {
|
||||
private final String id;
|
||||
private final long userId;
|
||||
private final String username;
|
||||
private final String profilePicUrl;
|
||||
private final long postId;
|
||||
private final String previewUrl;
|
||||
private final NotificationType type;
|
||||
private final CharSequence text;
|
||||
private final long timestamp;
|
||||
|
||||
public NotificationModel(final String id,
|
||||
final String text,
|
||||
final long timestamp,
|
||||
final long userId,
|
||||
final String username,
|
||||
final String profilePicUrl,
|
||||
final long postId,
|
||||
final String previewUrl,
|
||||
final NotificationType type) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
this.timestamp = timestamp;
|
||||
this.userId = userId;
|
||||
this.username = username;
|
||||
this.profilePicUrl = profilePicUrl;
|
||||
this.postId = postId;
|
||||
this.previewUrl = previewUrl;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public CharSequence getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDateTime() {
|
||||
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
|
||||
}
|
||||
|
||||
public long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getProfilePic() {
|
||||
return profilePicUrl;
|
||||
}
|
||||
|
||||
public long getPostId() {
|
||||
return postId;
|
||||
}
|
||||
|
||||
public String getPreviewPic() {
|
||||
return previewUrl;
|
||||
}
|
||||
|
||||
public NotificationType getType() { return type; }
|
||||
}
|
@ -2,6 +2,9 @@ package awais.instagrabber.repositories;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import awais.instagrabber.repositories.responses.AymlResponse;
|
||||
import awais.instagrabber.repositories.responses.NewsInboxResponse;
|
||||
import awais.instagrabber.repositories.responses.UserSearchResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.FieldMap;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
@ -16,9 +19,12 @@ public interface NewsRepository {
|
||||
Call<String> webInbox(@Header("User-Agent") String userAgent);
|
||||
|
||||
@GET("/api/v1/news/inbox/")
|
||||
Call<String> appInbox(@Header("User-Agent") String userAgent, @Query(value = "mark_as_seen", encoded = true) boolean markAsSeen);
|
||||
Call<NewsInboxResponse> appInbox(@Header("User-Agent") String userAgent, @Query(value = "mark_as_seen", encoded = true) boolean markAsSeen);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/discover/ayml/")
|
||||
Call<String> getAyml(@Header("User-Agent") String userAgent, @FieldMap final Map<String, String> form);
|
||||
Call<AymlResponse> getAyml(@Header("User-Agent") String userAgent, @FieldMap final Map<String, String> form);
|
||||
|
||||
@GET("/api/v1/discover/chaining/")
|
||||
Call<UserSearchResponse> getChaining(@Header("User-Agent") String userAgent, @Query(value = "target_id") long targetId);
|
||||
}
|
||||
|
@ -36,4 +36,8 @@ public interface StoriesRepository {
|
||||
@Path("action") String action,
|
||||
// story_poll_vote, story_question_response, story_slider_vote, story_quiz_answer
|
||||
@FieldMap Map<String, String> form);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v2/media/seen/")
|
||||
Call<String> seen(@QueryMap Map<String, String> queryParams, @FieldMap Map<String, String> form);
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AymlResponse {
|
||||
private final AymlUserList newSuggestedUsers;
|
||||
private final AymlUserList suggestedUsers;
|
||||
|
||||
public AymlResponse(final AymlUserList newSuggestedUsers,
|
||||
final AymlUserList suggestedUsers) {
|
||||
this.newSuggestedUsers = newSuggestedUsers;
|
||||
this.suggestedUsers = suggestedUsers;
|
||||
}
|
||||
|
||||
public AymlUserList getNewSuggestedUsers() {
|
||||
return newSuggestedUsers;
|
||||
}
|
||||
|
||||
public AymlUserList getSuggestedUsers() {
|
||||
return suggestedUsers;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
public class AymlUser {
|
||||
private final User user;
|
||||
private final String algorithm;
|
||||
private final String socialContext;
|
||||
private final String uuid;
|
||||
|
||||
public AymlUser(final User user,
|
||||
final String algorithm,
|
||||
final String socialContext,
|
||||
final String uuid) {
|
||||
this.user = user;
|
||||
this.algorithm = algorithm;
|
||||
this.socialContext = socialContext;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public String getSocialContext() {
|
||||
return socialContext;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AymlUserList {
|
||||
private final List<AymlUser> suggestions;
|
||||
|
||||
public AymlUserList(final List<AymlUser> suggestions) {
|
||||
this.suggestions = suggestions;
|
||||
}
|
||||
|
||||
public List<AymlUser> getSuggestions() {
|
||||
return suggestions;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class NewsInboxResponse {
|
||||
private final NotificationCounts counts;
|
||||
private final List<Notification> newStories;
|
||||
private final List<Notification> oldStories;
|
||||
|
||||
public NewsInboxResponse(final NotificationCounts counts,
|
||||
final List<Notification> newStories,
|
||||
final List<Notification> oldStories) {
|
||||
this.counts = counts;
|
||||
this.newStories = newStories;
|
||||
this.oldStories = oldStories;
|
||||
}
|
||||
|
||||
public NotificationCounts getCounts() {
|
||||
return counts;
|
||||
}
|
||||
|
||||
public List<Notification> getNewStories() {
|
||||
return newStories;
|
||||
}
|
||||
|
||||
public List<Notification> getOldStories() {
|
||||
return oldStories;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
|
||||
public class Notification {
|
||||
private final NotificationArgs args;
|
||||
private final String storyType;
|
||||
private final String pk;
|
||||
|
||||
public Notification(final NotificationArgs args,
|
||||
final String storyType,
|
||||
final String pk) {
|
||||
this.args = args;
|
||||
this.storyType = storyType;
|
||||
this.pk = pk;
|
||||
}
|
||||
|
||||
public NotificationArgs getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public NotificationType getType() {
|
||||
return NotificationType.valueOfType(storyType);
|
||||
}
|
||||
|
||||
public String getPk() {
|
||||
return pk;
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public class NotificationArgs {
|
||||
private final String text;
|
||||
private final String richText;
|
||||
private final long profileId;
|
||||
private final String profileImage;
|
||||
private final List<NotificationImage> media;
|
||||
private final double timestamp;
|
||||
private final String profileName;
|
||||
private final String fullName; // for AYML, not naturally generated
|
||||
private final boolean isVerified; // mostly for AYML, not sure about notif
|
||||
|
||||
public NotificationArgs(final String text,
|
||||
final String richText, // for AYML, this is the algorithm
|
||||
final long profileId,
|
||||
final String profileImage,
|
||||
final List<NotificationImage> media,
|
||||
final double timestamp,
|
||||
final String profileName,
|
||||
final String fullName,
|
||||
final boolean isVerified) {
|
||||
this.text = text;
|
||||
this.richText = richText;
|
||||
this.profileId = profileId;
|
||||
this.profileImage = profileImage;
|
||||
this.media = media;
|
||||
this.timestamp = timestamp;
|
||||
this.profileName = profileName;
|
||||
this.fullName = fullName;
|
||||
this.isVerified = isVerified;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text == null ? cleanRichText(richText) : text;
|
||||
}
|
||||
|
||||
public long getUserId() {
|
||||
return profileId;
|
||||
}
|
||||
|
||||
public String getProfilePic() {
|
||||
return profileImage;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return profileName;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public List<NotificationImage> getMedia() {
|
||||
return media;
|
||||
}
|
||||
|
||||
public double getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return isVerified;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDateTime() {
|
||||
return Utils.datetimeParser.format(new Date(Math.round(timestamp * 1000)));
|
||||
}
|
||||
|
||||
private String cleanRichText(final String raw) {
|
||||
if (raw == null) return null;
|
||||
final Matcher matcher = Pattern.compile("\\{[\\p{L}\\d._]+\\|000000\\|1\\|user\\?id=\\d+\\}").matcher(raw);
|
||||
String result = raw;
|
||||
while (matcher.find()) {
|
||||
final String richObject = raw.substring(matcher.start(), matcher.end());
|
||||
final String username = richObject.split("\\|")[0].substring(1);
|
||||
result = result.replace(richObject, username);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class NotificationCounts {
|
||||
private final int commentLikes;
|
||||
private final int usertags;
|
||||
private final int likes;
|
||||
private final int comments;
|
||||
private final int relationships;
|
||||
private final int photosOfYou;
|
||||
private final int requests;
|
||||
|
||||
public NotificationCounts(final int commentLikes,
|
||||
final int usertags,
|
||||
final int likes,
|
||||
final int comments,
|
||||
final int relationships,
|
||||
final int photosOfYou,
|
||||
final int requests) {
|
||||
this.commentLikes = commentLikes;
|
||||
this.usertags = usertags;
|
||||
this.likes = likes;
|
||||
this.comments = comments;
|
||||
this.relationships = relationships;
|
||||
this.photosOfYou = photosOfYou;
|
||||
this.requests = requests;
|
||||
}
|
||||
|
||||
public int getRelationshipsCount() {
|
||||
return relationships;
|
||||
}
|
||||
|
||||
public int getUserTagsCount() {
|
||||
return usertags;
|
||||
}
|
||||
|
||||
public int getCommentsCount() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public int getCommentLikesCount() {
|
||||
return commentLikes;
|
||||
}
|
||||
|
||||
public int getLikesCount() {
|
||||
return likes;
|
||||
}
|
||||
|
||||
public int getPOYCount() {
|
||||
return photosOfYou;
|
||||
}
|
||||
|
||||
public int getRequestsCount() {
|
||||
return requests;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
public class NotificationImage {
|
||||
private final String id;
|
||||
private final String image;
|
||||
|
||||
public NotificationImage(final String id, final String image) {
|
||||
this.id = id;
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class User implements Serializable {
|
||||
@ -27,7 +28,9 @@ public class User implements Serializable {
|
||||
private final long usertagsCount;
|
||||
private final String publicEmail;
|
||||
private final HdProfilePicUrlInfo hdProfilePicUrlInfo;
|
||||
|
||||
private final String profileContext;
|
||||
private final List<UserProfileContextLink> profileContextLinksWithUserIds;
|
||||
private final String socialContext;
|
||||
|
||||
public User(final long pk,
|
||||
final String username,
|
||||
@ -51,7 +54,10 @@ public class User implements Serializable {
|
||||
final String externalUrl,
|
||||
final long usertagsCount,
|
||||
final String publicEmail,
|
||||
final HdProfilePicUrlInfo hdProfilePicUrlInfo) {
|
||||
final HdProfilePicUrlInfo hdProfilePicUrlInfo,
|
||||
final String profileContext,
|
||||
final List<UserProfileContextLink> profileContextLinksWithUserIds,
|
||||
final String socialContext) {
|
||||
this.pk = pk;
|
||||
this.username = username;
|
||||
this.fullName = fullName;
|
||||
@ -75,6 +81,9 @@ public class User implements Serializable {
|
||||
this.usertagsCount = usertagsCount;
|
||||
this.publicEmail = publicEmail;
|
||||
this.hdProfilePicUrlInfo = hdProfilePicUrlInfo;
|
||||
this.profileContext = profileContext;
|
||||
this.profileContextLinksWithUserIds = profileContextLinksWithUserIds;
|
||||
this.socialContext = socialContext;
|
||||
}
|
||||
|
||||
public long getPk() {
|
||||
@ -173,46 +182,17 @@ public class User implements Serializable {
|
||||
return publicEmail;
|
||||
}
|
||||
|
||||
// public boolean isReallyPrivate() {
|
||||
// final FriendshipStatus friendshipStatus = getFriendshipStatus();
|
||||
// !user.optBoolean("followed_by_viewer") && (id != uid && isPrivate)
|
||||
// }
|
||||
public String getProfileContext() {
|
||||
return profileContext;
|
||||
}
|
||||
|
||||
// public static User fromProfileModel(final ProfileModel profileModel) {
|
||||
// return new User(
|
||||
// Long.parseLong(profileModel.getId()),
|
||||
// profileModel.getUsername(),
|
||||
// profileModel.getName(),
|
||||
// profileModel.isPrivate(),
|
||||
// profileModel.getSdProfilePic(),
|
||||
// null,
|
||||
// new FriendshipStatus(
|
||||
// profileModel.isFollowing(),
|
||||
// false,
|
||||
// profileModel.isBlocked(),
|
||||
// false,
|
||||
// profileModel.isPrivate(),
|
||||
// false,
|
||||
// profileModel.isRequested(),
|
||||
// false,
|
||||
// profileModel.isRestricted(),
|
||||
// false),
|
||||
// profileModel.isVerified(),
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// null,
|
||||
// null,
|
||||
// profileModel.getPostCount(),
|
||||
// profileModel.getFollowersCount(),
|
||||
// profileModel.getFollowingCount(),
|
||||
// 0,
|
||||
// profileModel.getBiography(),
|
||||
// profileModel.getUrl(),
|
||||
// 0,
|
||||
// null);
|
||||
// }
|
||||
public String getSocialContext() {
|
||||
return socialContext;
|
||||
}
|
||||
|
||||
public List<UserProfileContextLink> getProfileContextLinks() {
|
||||
return profileContextLinksWithUserIds;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -1,49 +0,0 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
public class UserInfo {
|
||||
private final long pk;
|
||||
private final String username, fullName, profilePicUrl, hdProfilePicUrl;
|
||||
|
||||
public UserInfo(final long pk,
|
||||
final String username,
|
||||
final String fullName,
|
||||
final String profilePicUrl,
|
||||
final String hdProfilePicUrl) {
|
||||
this.pk = pk;
|
||||
this.username = username;
|
||||
this.fullName = fullName;
|
||||
this.profilePicUrl = profilePicUrl;
|
||||
this.hdProfilePicUrl = hdProfilePicUrl;
|
||||
}
|
||||
|
||||
public long getPk() {
|
||||
return pk;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getProfilePicUrl() {
|
||||
return profilePicUrl;
|
||||
}
|
||||
|
||||
public String getHDProfilePicUrl() {
|
||||
return hdProfilePicUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserInfo{" +
|
||||
"uid='" + pk + '\'' +
|
||||
", username='" + username + '\'' +
|
||||
", fullName='" + fullName + '\'' +
|
||||
", profilePicUrl='" + profilePicUrl + '\'' +
|
||||
", hdProfilePicUrl='" + hdProfilePicUrl + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
public class UserProfileContextLink {
|
||||
private final String username;
|
||||
private final int start;
|
||||
private final int end;
|
||||
|
||||
public UserProfileContextLink(final String username, final int start, final int end) {
|
||||
this.username = username;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
}
|
@ -18,8 +18,6 @@ import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.activities.MainActivity;
|
||||
import awais.instagrabber.asyncs.GetActivityAsyncTask.NotificationCounts;
|
||||
import awais.instagrabber.asyncs.GetActivityAsyncTask.OnTaskCompleteListener;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
@ -30,7 +28,7 @@ public class ActivityCheckerService extends Service {
|
||||
private static final int DELAY_MILLIS = 60000;
|
||||
|
||||
private Handler handler;
|
||||
private OnTaskCompleteListener onTaskCompleteListener;
|
||||
// private OnTaskCompleteListener onTaskCompleteListener;
|
||||
private NotificationManagerCompat notificationManager;
|
||||
|
||||
private final IBinder binder = new LocalBinder();
|
||||
@ -50,6 +48,7 @@ public class ActivityCheckerService extends Service {
|
||||
public void onCreate() {
|
||||
notificationManager = NotificationManagerCompat.from(getApplicationContext());
|
||||
handler = new Handler();
|
||||
/*
|
||||
onTaskCompleteListener = result -> {
|
||||
// Log.d(TAG, "onTaskCompleteListener: result: " + result);
|
||||
try {
|
||||
@ -62,20 +61,12 @@ public class ActivityCheckerService extends Service {
|
||||
handler.postDelayed(runnable, DELAY_MILLIS);
|
||||
}
|
||||
};
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
startChecking();
|
||||
// Uncomment to test notifications
|
||||
// final String notificationString = getNotificationString(new NotificationCounts(
|
||||
// 1,
|
||||
// 2,
|
||||
// 3,
|
||||
// 4,
|
||||
// 5
|
||||
// ));
|
||||
// showNotification(notificationString);
|
||||
return binder;
|
||||
}
|
||||
|
||||
@ -93,6 +84,7 @@ public class ActivityCheckerService extends Service {
|
||||
handler.removeCallbacks(runnable);
|
||||
}
|
||||
|
||||
/*
|
||||
private String getNotificationString(final NotificationCounts result) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
if (result.getRelationshipsCount() != 0) {
|
||||
@ -113,6 +105,7 @@ public class ActivityCheckerService extends Service {
|
||||
if (list.isEmpty()) return null;
|
||||
return TextUtils.join(", ", list);
|
||||
}
|
||||
*/
|
||||
|
||||
private void showNotification(final String notificationString) {
|
||||
final Notification notification = new NotificationCompat.Builder(this, Constants.ACTIVITY_CHANNEL_ID)
|
||||
|
@ -778,7 +778,8 @@ public final class ResponseBodyUtils {
|
||||
null,
|
||||
friendshipStatus,
|
||||
owner.optBoolean("is_verified"),
|
||||
false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null);
|
||||
false, false, false, false, null, null, 0, 0, 0, 0, null, null, 0, null, null,
|
||||
null, null, null);
|
||||
}
|
||||
final String id = feedItem.getString(Constants.EXTRAS_ID);
|
||||
final ImageVersions2 imageVersions2 = new ImageVersions2(
|
||||
|
@ -5,12 +5,12 @@ import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.models.NotificationModel;
|
||||
import awais.instagrabber.repositories.responses.Notification;
|
||||
|
||||
public class NotificationViewModel extends ViewModel {
|
||||
private MutableLiveData<List<NotificationModel>> list;
|
||||
private MutableLiveData<List<Notification>> list;
|
||||
|
||||
public MutableLiveData<List<NotificationModel>> getList() {
|
||||
public MutableLiveData<List<Notification>> getList() {
|
||||
if (list == null) {
|
||||
list = new MutableLiveData<>();
|
||||
}
|
||||
|
@ -243,6 +243,9 @@ public class GraphQLService extends BaseService {
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
// userModels.add(new ProfileModel(userObject.optBoolean("is_private"),
|
||||
@ -334,6 +337,9 @@ public class GraphQLService extends BaseService {
|
||||
url,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "onResponse", e);
|
||||
|
@ -9,17 +9,24 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.models.NotificationModel;
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
import awais.instagrabber.repositories.NewsRepository;
|
||||
import awais.instagrabber.repositories.responses.AymlResponse;
|
||||
import awais.instagrabber.repositories.responses.AymlUser;
|
||||
import awais.instagrabber.repositories.responses.UserSearchResponse;
|
||||
import awais.instagrabber.repositories.responses.NewsInboxResponse;
|
||||
import awais.instagrabber.repositories.responses.Notification;
|
||||
import awais.instagrabber.repositories.responses.NotificationArgs;
|
||||
import awais.instagrabber.repositories.responses.NotificationImage;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import retrofit2.Call;
|
||||
@ -52,48 +59,31 @@ public class NewsService extends BaseService {
|
||||
}
|
||||
|
||||
public void fetchAppInbox(final boolean markAsSeen,
|
||||
final ServiceCallback<List<NotificationModel>> callback) {
|
||||
final List<NotificationModel> result = new ArrayList<>();
|
||||
final Call<String> request = repository.appInbox(appUa, markAsSeen);
|
||||
request.enqueue(new Callback<String>() {
|
||||
final ServiceCallback<List<Notification>> callback) {
|
||||
final Call<NewsInboxResponse> request = repository.appInbox(appUa, markAsSeen);
|
||||
request.enqueue(new Callback<NewsInboxResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
final String body = response.body();
|
||||
public void onResponse(@NonNull final Call<NewsInboxResponse> call, @NonNull final Response<NewsInboxResponse> response) {
|
||||
final NewsInboxResponse body = response.body();
|
||||
if (body == null) {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JSONObject jsonObject = new JSONObject(body);
|
||||
final JSONArray oldStories = jsonObject.getJSONArray("old_stories"),
|
||||
newStories = jsonObject.getJSONArray("new_stories");
|
||||
|
||||
for (int j = 0; j < newStories.length(); ++j) {
|
||||
final NotificationModel newsItem = parseNewsItem(newStories.getJSONObject(j));
|
||||
if (newsItem != null) result.add(newsItem);
|
||||
}
|
||||
|
||||
for (int i = 0; i < oldStories.length(); ++i) {
|
||||
final NotificationModel newsItem = parseNewsItem(oldStories.getJSONObject(i));
|
||||
if (newsItem != null) result.add(newsItem);
|
||||
}
|
||||
|
||||
final List<Notification> result = new ArrayList<>();
|
||||
result.addAll(body.getNewStories());
|
||||
result.addAll(body.getOldStories());
|
||||
callback.onSuccess(result);
|
||||
} catch (JSONException e) {
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
public void onFailure(@NonNull final Call<NewsInboxResponse> call, @NonNull final Throwable t) {
|
||||
callback.onFailure(t);
|
||||
// Log.e(TAG, "onFailure: ", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void fetchWebInbox(final boolean markAsSeen,
|
||||
final ServiceCallback<List<NotificationModel>> callback) {
|
||||
public void fetchWebInbox(final ServiceCallback<List<Notification>> callback) {
|
||||
final Call<String> request = repository.webInbox(browserUa);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
@ -104,7 +94,7 @@ public class NewsService extends BaseService {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final List<NotificationModel> result = new ArrayList<>();
|
||||
final List<Notification> result = new ArrayList<>();
|
||||
final JSONObject page = new JSONObject(body)
|
||||
.getJSONObject("graphql")
|
||||
.getJSONObject("user");
|
||||
@ -124,16 +114,25 @@ public class NewsService extends BaseService {
|
||||
final NotificationType notificationType = NotificationType.valueOfType(type);
|
||||
if (notificationType == null) continue;
|
||||
final JSONObject user = data.getJSONObject("user");
|
||||
result.add(new NotificationModel(
|
||||
data.getString(Constants.EXTRAS_ID),
|
||||
data.optString("text"), // comments or mentions
|
||||
data.getLong("timestamp"),
|
||||
user.getLong("id"),
|
||||
user.getString("username"),
|
||||
|
||||
result.add(new Notification(
|
||||
new NotificationArgs(
|
||||
data.optString("text"),
|
||||
null,
|
||||
user.getLong(Constants.EXTRAS_ID),
|
||||
user.getString("profile_pic_url"),
|
||||
!data.isNull("media") ? Long.valueOf(data.getJSONObject("media").getString("id").split("_")[0]) : 0,
|
||||
data.has("media") ? data.getJSONObject("media").getString("thumbnail_src") : null,
|
||||
notificationType));
|
||||
data.isNull("media") ? null : Collections.singletonList(new NotificationImage(
|
||||
data.getJSONObject("media").getString("id"),
|
||||
data.getJSONObject("media").getString("thumbnail_src")
|
||||
)),
|
||||
data.getLong("timestamp"),
|
||||
user.getString("username"),
|
||||
null,
|
||||
false
|
||||
),
|
||||
type,
|
||||
data.getString(Constants.EXTRAS_ID)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,15 +143,21 @@ public class NewsService extends BaseService {
|
||||
for (int i = 0; i < media.length(); ++i) {
|
||||
data = media.optJSONObject(i).optJSONObject("node");
|
||||
if (data == null) continue;
|
||||
result.add(new NotificationModel(
|
||||
data.getString(Constants.EXTRAS_ID),
|
||||
data.optString("full_name"),
|
||||
0L,
|
||||
result.add(new Notification(
|
||||
new NotificationArgs(
|
||||
null,
|
||||
null,
|
||||
data.getLong(Constants.EXTRAS_ID),
|
||||
data.getString("username"),
|
||||
data.getString("profile_pic_url"),
|
||||
0,
|
||||
null, NotificationType.REQUEST));
|
||||
null,
|
||||
0L,
|
||||
data.getString("username"),
|
||||
data.optString("full_name"),
|
||||
data.optBoolean("is_verified")
|
||||
),
|
||||
"REQUEST",
|
||||
data.getString(Constants.EXTRAS_ID)
|
||||
));
|
||||
}
|
||||
}
|
||||
callback.onSuccess(result);
|
||||
@ -169,40 +174,9 @@ public class NewsService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
private NotificationModel parseNewsItem(final JSONObject itemJson) throws JSONException {
|
||||
if (itemJson == null) return null;
|
||||
final String type = itemJson.getString("story_type");
|
||||
final NotificationType notificationType = NotificationType.valueOfType(type);
|
||||
if (notificationType == null) {
|
||||
if (BuildConfig.DEBUG) Log.d("austin_debug", "unhandled news type: " + itemJson);
|
||||
return null;
|
||||
}
|
||||
final JSONObject data = itemJson.getJSONObject("args");
|
||||
return new NotificationModel(
|
||||
data.getString("tuuid"),
|
||||
data.has("text") ? data.getString("text") : cleanRichText(data.optString("rich_text", "")),
|
||||
data.getLong("timestamp"),
|
||||
data.getLong("profile_id"),
|
||||
data.getString("profile_name"),
|
||||
data.getString("profile_image"),
|
||||
!data.isNull("media") ? Long.valueOf(data.getJSONArray("media").getJSONObject(0).getString("id").split("_")[0]) : 0,
|
||||
!data.isNull("media") ? data.getJSONArray("media").getJSONObject(0).getString("image") : null,
|
||||
notificationType);
|
||||
}
|
||||
|
||||
private String cleanRichText(final String raw) {
|
||||
final Matcher matcher = Pattern.compile("\\{[\\p{L}\\d._]+\\|000000\\|1\\|user\\?id=\\d+\\}").matcher(raw);
|
||||
String result = raw;
|
||||
while (matcher.find()) {
|
||||
final String richObject = raw.substring(matcher.start(), matcher.end());
|
||||
final String username = richObject.split("\\|")[0].substring(1);
|
||||
result = result.replace(richObject, username);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void fetchSuggestions(final String csrfToken,
|
||||
final ServiceCallback<List<NotificationModel>> callback) {
|
||||
final String deviceUuid,
|
||||
final ServiceCallback<List<Notification>> callback) {
|
||||
final Map<String, String> form = new HashMap<>();
|
||||
form.put("_uuid", UUID.randomUUID().toString());
|
||||
form.put("_csrftoken", csrfToken);
|
||||
@ -210,57 +184,88 @@ public class NewsService extends BaseService {
|
||||
form.put("device_id", UUID.randomUUID().toString());
|
||||
form.put("module", "discover_people");
|
||||
form.put("paginate", "false");
|
||||
final Call<String> request = repository.getAyml(appUa, form);
|
||||
request.enqueue(new Callback<String>() {
|
||||
final Call<AymlResponse> request = repository.getAyml(appUa, form);
|
||||
request.enqueue(new Callback<AymlResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
final String body = response.body();
|
||||
public void onResponse(@NonNull final Call<AymlResponse> call, @NonNull final Response<AymlResponse> response) {
|
||||
final AymlResponse body = response.body();
|
||||
if (body == null) {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final List<NotificationModel> result = new ArrayList<>();
|
||||
final JSONObject jsonObject = new JSONObject(body);
|
||||
final JSONArray oldStories = jsonObject.getJSONObject("suggested_users").getJSONArray("suggestions"),
|
||||
newStories = jsonObject.getJSONObject("new_suggested_users").getJSONArray("suggestions");
|
||||
final List<AymlUser> aymlUsers = new ArrayList<>();
|
||||
aymlUsers.addAll(body.getNewSuggestedUsers().getSuggestions());
|
||||
aymlUsers.addAll(body.getSuggestedUsers().getSuggestions());
|
||||
|
||||
for (int j = 0; j < newStories.length(); ++j) {
|
||||
final NotificationModel newsItem = parseAymlItem(newStories.getJSONObject(j));
|
||||
if (newsItem != null) result.add(newsItem);
|
||||
}
|
||||
|
||||
for (int i = 0; i < oldStories.length(); ++i) {
|
||||
final NotificationModel newsItem = parseAymlItem(oldStories.getJSONObject(i));
|
||||
if (newsItem != null) result.add(newsItem);
|
||||
}
|
||||
|
||||
callback.onSuccess(result);
|
||||
} catch (JSONException e) {
|
||||
callback.onFailure(e);
|
||||
}
|
||||
final List<Notification> newsItems = aymlUsers.stream()
|
||||
.map(i -> {
|
||||
final User u = i.getUser();
|
||||
return new Notification(
|
||||
new NotificationArgs(
|
||||
i.getSocialContext(),
|
||||
i.getAlgorithm(),
|
||||
u.getPk(),
|
||||
u.getProfilePicUrl(),
|
||||
null,
|
||||
0L,
|
||||
u.getUsername(),
|
||||
u.getFullName(),
|
||||
u.isVerified()
|
||||
),
|
||||
"AYML",
|
||||
i.getUuid()
|
||||
);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
callback.onSuccess(newsItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
public void onFailure(@NonNull final Call<AymlResponse> call, @NonNull final Throwable t) {
|
||||
callback.onFailure(t);
|
||||
// Log.e(TAG, "onFailure: ", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private NotificationModel parseAymlItem(final JSONObject itemJson) throws JSONException {
|
||||
if (itemJson == null) return null;
|
||||
final JSONObject data = itemJson.getJSONObject("user");
|
||||
return new NotificationModel(
|
||||
itemJson.getString("uuid"),
|
||||
itemJson.getString("social_context"),
|
||||
public void fetchChaining(final long targetId, final ServiceCallback<List<Notification>> callback) {
|
||||
final Call<UserSearchResponse> request = repository.getChaining(appUa, targetId);
|
||||
request.enqueue(new Callback<UserSearchResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<UserSearchResponse> call, @NonNull final Response<UserSearchResponse> response) {
|
||||
final UserSearchResponse body = response.body();
|
||||
if (body == null) {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<Notification> newsItems = body.getUsers().stream()
|
||||
.map(u -> {
|
||||
return new Notification(
|
||||
new NotificationArgs(
|
||||
u.getSocialContext(),
|
||||
null,
|
||||
u.getPk(),
|
||||
u.getProfilePicUrl(),
|
||||
null,
|
||||
0L,
|
||||
data.getLong("pk"),
|
||||
data.getString("username"),
|
||||
data.getString("profile_pic_url"),
|
||||
0,
|
||||
data.getString("full_name"), // just borrowing this field
|
||||
NotificationType.AYML);
|
||||
u.getUsername(),
|
||||
u.getFullName(),
|
||||
u.isVerified()
|
||||
),
|
||||
"AYML",
|
||||
u.getProfilePicId() // placeholder
|
||||
);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
callback.onSuccess(newsItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<UserSearchResponse> call, @NonNull final Throwable t) {
|
||||
callback.onFailure(t);
|
||||
// Log.e(TAG, "onFailure: ", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import awais.instagrabber.models.FeedStoryModel;
|
||||
@ -36,20 +37,45 @@ import retrofit2.Retrofit;
|
||||
public class StoriesService extends BaseService {
|
||||
private static final String TAG = "StoriesService";
|
||||
|
||||
private final StoriesRepository repository;
|
||||
|
||||
private static StoriesService instance;
|
||||
|
||||
private StoriesService() {
|
||||
private final StoriesRepository repository;
|
||||
private final String csrfToken;
|
||||
private final long userId;
|
||||
private final String deviceUuid;
|
||||
|
||||
private StoriesService(@NonNull final String csrfToken,
|
||||
final long userId,
|
||||
@NonNull final String deviceUuid) {
|
||||
this.csrfToken = csrfToken;
|
||||
this.userId = userId;
|
||||
this.deviceUuid = deviceUuid;
|
||||
final Retrofit retrofit = getRetrofitBuilder()
|
||||
.baseUrl("https://i.instagram.com")
|
||||
.build();
|
||||
repository = retrofit.create(StoriesRepository.class);
|
||||
}
|
||||
|
||||
public static StoriesService getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new StoriesService();
|
||||
public String getCsrfToken() {
|
||||
return csrfToken;
|
||||
}
|
||||
|
||||
public long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getDeviceUuid() {
|
||||
return deviceUuid;
|
||||
}
|
||||
|
||||
public static StoriesService getInstance(final String csrfToken,
|
||||
final long userId,
|
||||
final String deviceUuid) {
|
||||
if (instance == null
|
||||
|| !Objects.equals(instance.getCsrfToken(), csrfToken)
|
||||
|| !Objects.equals(instance.getUserId(), userId)
|
||||
|| !Objects.equals(instance.getDeviceUuid(), deviceUuid)) {
|
||||
instance = new StoriesService(csrfToken, userId, deviceUuid);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
@ -146,6 +172,9 @@ public class StoriesService extends BaseService {
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
final String id = node.getString("id");
|
||||
@ -205,6 +234,9 @@ public class StoriesService extends BaseService {
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
final String id = node.getString("id");
|
||||
@ -390,13 +422,11 @@ public class StoriesService extends BaseService {
|
||||
final String action,
|
||||
final String arg1,
|
||||
final String arg2,
|
||||
final long userId,
|
||||
final String csrfToken,
|
||||
final ServiceCallback<StoryStickerResponse> callback) {
|
||||
final Map<String, Object> form = new HashMap<>();
|
||||
form.put("_csrftoken", csrfToken);
|
||||
form.put("_uid", userId);
|
||||
form.put("_uuid", UUID.randomUUID().toString());
|
||||
form.put("_uuid", deviceUuid);
|
||||
form.put("mutation_token", UUID.randomUUID().toString());
|
||||
form.put("client_context", UUID.randomUUID().toString());
|
||||
form.put("radio_type", "wifi-none");
|
||||
@ -427,39 +457,67 @@ public class StoriesService extends BaseService {
|
||||
public void respondToQuestion(final String storyId,
|
||||
final String stickerId,
|
||||
final String answer,
|
||||
final long userId,
|
||||
final String csrfToken,
|
||||
final ServiceCallback<StoryStickerResponse> callback) {
|
||||
respondToSticker(storyId, stickerId, "story_question_response", "response", answer, userId, csrfToken, callback);
|
||||
respondToSticker(storyId, stickerId, "story_question_response", "response", answer, callback);
|
||||
}
|
||||
|
||||
// QuizAction.java
|
||||
public void respondToQuiz(final String storyId,
|
||||
final String stickerId,
|
||||
final int answer,
|
||||
final long userId,
|
||||
final String csrfToken,
|
||||
final ServiceCallback<StoryStickerResponse> callback) {
|
||||
respondToSticker(storyId, stickerId, "story_quiz_answer", "answer", String.valueOf(answer), userId, csrfToken, callback);
|
||||
respondToSticker(storyId, stickerId, "story_quiz_answer", "answer", String.valueOf(answer), callback);
|
||||
}
|
||||
|
||||
// VoteAction.java
|
||||
public void respondToPoll(final String storyId,
|
||||
final String stickerId,
|
||||
final int answer,
|
||||
final long userId,
|
||||
final String csrfToken,
|
||||
final ServiceCallback<StoryStickerResponse> callback) {
|
||||
respondToSticker(storyId, stickerId, "story_poll_vote", "vote", String.valueOf(answer), userId, csrfToken, callback);
|
||||
respondToSticker(storyId, stickerId, "story_poll_vote", "vote", String.valueOf(answer), callback);
|
||||
}
|
||||
|
||||
public void respondToSlider(final String storyId,
|
||||
final String stickerId,
|
||||
final double answer,
|
||||
final long userId,
|
||||
final String csrfToken,
|
||||
final ServiceCallback<StoryStickerResponse> callback) {
|
||||
respondToSticker(storyId, stickerId, "story_slider_vote", "vote", String.valueOf(answer), userId, csrfToken, callback);
|
||||
respondToSticker(storyId, stickerId, "story_slider_vote", "vote", String.valueOf(answer), callback);
|
||||
}
|
||||
|
||||
public void seen(final String storyMediaId,
|
||||
final long takenAt,
|
||||
final long seenAt,
|
||||
final ServiceCallback<String> callback) {
|
||||
final Map<String, Object> form = new HashMap<>();
|
||||
form.put("_csrftoken", csrfToken);
|
||||
form.put("_uid", userId);
|
||||
form.put("_uuid", deviceUuid);
|
||||
form.put("container_module", "feed_timeline");
|
||||
final Map<String, Object> reelsForm = new HashMap<>();
|
||||
reelsForm.put(storyMediaId, Collections.singletonList(takenAt + "_" + seenAt));
|
||||
form.put("reels", reelsForm);
|
||||
final Map<String, String> signedForm = Utils.sign(form);
|
||||
final Map<String, String> queryMap = new HashMap<>();
|
||||
queryMap.put("reel", "1");
|
||||
queryMap.put("live_vod", "0");
|
||||
final Call<String> request = repository.seen(queryMap, signedForm);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call,
|
||||
@NonNull final Response<String> response) {
|
||||
if (callback != null) {
|
||||
callback.onSuccess(response.body());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call,
|
||||
@NonNull final Throwable t) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -45,6 +45,21 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="username" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/isVerified"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitStart"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/tvComment"
|
||||
app:layout_constraintEnd_toStartOf="@id/ivPreviewPic"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/verified"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tvComment"
|
||||
android:layout_width="0dp"
|
||||
|
@ -16,53 +16,27 @@
|
||||
android:transitionName="profile_pic"
|
||||
android:visibility="invisible"
|
||||
app:actualImageScaleType="centerCrop"
|
||||
app:layout_constraintEnd_toStartOf="@id/mainPostCount"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnFollow"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@id/fav_chip"
|
||||
tools:background="@mipmap/ic_launcher" />
|
||||
app:layout_constraintBottom_toBottomOf="@id/btnTagged"
|
||||
tools:visibility="visible"
|
||||
tools:foreground="@mipmap/ic_launcher" />
|
||||
|
||||
<!-- for other people -->
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/mainPostCount"
|
||||
android:id="@+id/btnFollow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/profile_chip_size"
|
||||
android:layout_marginStart="4dp"
|
||||
android:clickable="false"
|
||||
android:gravity="center"
|
||||
android:text="@string/follow"
|
||||
android:visibility="gone"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_outline_person_add_24"
|
||||
app:chipIconTint="@color/deep_purple_200"
|
||||
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
|
||||
tools:text="35 Posts"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/mainFollowing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/profile_chip_size"
|
||||
android:layout_marginStart="4dp"
|
||||
android:clickable="false"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:layout_constraintStart_toEndOf="@id/mainPostCount"
|
||||
app:layout_constraintTop_toTopOf="@id/mainPostCount"
|
||||
app:rippleColor="@color/grey_400"
|
||||
tools:text="10 Following"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/mainFollowers"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/profile_chip_size"
|
||||
android:layout_marginStart="4dp"
|
||||
android:clickable="false"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainPostCount"
|
||||
app:rippleColor="@color/grey_400"
|
||||
tools:text="10 Followers"
|
||||
app:layout_constraintBottom_toTopOf="@id/fav_chip"
|
||||
app:rippleColor="@color/purple_200"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
@ -74,11 +48,42 @@
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:layout_constraintStart_toEndOf="@id/mainFollowers"
|
||||
app:layout_constraintTop_toTopOf="@id/mainFollowers"
|
||||
app:layout_constraintStart_toEndOf="@id/btnFollow"
|
||||
app:layout_constraintBottom_toTopOf="@id/fav_chip"
|
||||
tools:text="omg what do u expect"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<!-- for user themself -->
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/btnSaved"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/profile_chip_size"
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="@string/saved"
|
||||
android:visibility="gone"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_outline_class_24"
|
||||
app:chipIconTint="@color/blue_700"
|
||||
app:layout_constraintStart_toEndOf="@id/mainStatus"
|
||||
app:layout_constraintBottom_toTopOf="@id/fav_chip"
|
||||
app:rippleColor="@color/blue_A400"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/btnLiked"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/profile_chip_size"
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="@string/liked"
|
||||
android:visibility="gone"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_like"
|
||||
app:chipIconTint="@color/red_600"
|
||||
app:layout_constraintStart_toEndOf="@id/btnSaved"
|
||||
app:layout_constraintBottom_toTopOf="@id/fav_chip"
|
||||
app:rippleColor="@color/red_300"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/fav_chip"
|
||||
android:layout_width="wrap_content"
|
||||
@ -90,7 +95,7 @@
|
||||
app:chipIcon="@drawable/ic_outline_star_plus_24"
|
||||
app:chipIconTint="@color/yellow_800"
|
||||
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainFollowers"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnFollow"
|
||||
app:rippleColor="@color/yellow_400"
|
||||
tools:visibility="visible" />
|
||||
|
||||
@ -104,24 +109,40 @@
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_outline_person_pin_24"
|
||||
app:chipIconTint="@color/deep_orange_800"
|
||||
app:layout_constraintStart_toEndOf="@id/fav_chip"
|
||||
app:layout_constraintTop_toTopOf="@id/fav_chip"
|
||||
app:layout_constraintStart_toEndOf="@id/mainProfileImage"
|
||||
app:layout_constraintTop_toBottomOf="@id/fav_chip"
|
||||
app:rippleColor="@color/deep_orange_400"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/btnDM"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/profile_chip_size"
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="@string/dm_person"
|
||||
android:visibility="gone"
|
||||
app:chipBackgroundColor="@null"
|
||||
app:chipIcon="@drawable/ic_round_send_24"
|
||||
app:chipIconTint="@color/green"
|
||||
app:layout_constraintStart_toEndOf="@id/btnTagged"
|
||||
app:layout_constraintTop_toBottomOf="@id/fav_chip"
|
||||
app:rippleColor="@color/green"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/mainFullName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/fav_chip"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnTagged"
|
||||
tools:text="Austin Huang" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
@ -130,12 +151,13 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:adjustViewBounds="true"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/mainFullName"
|
||||
app:layout_constraintStart_toEndOf="@id/mainFullName"
|
||||
app:layout_constraintTop_toBottomOf="@id/fav_chip"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnTagged"
|
||||
app:srcCompat="@drawable/verified"
|
||||
tools:visibility="visible" />
|
||||
|
||||
@ -165,14 +187,17 @@
|
||||
android:id="@+id/mainBiography"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
app:layout_constraintBottom_toTopOf="@id/mainUrl"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainFullName"
|
||||
tools:text="THE GLORIOUS (step)OWNER OF THIS APP. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec id justo lorem. In malesuada feugiat ornare. Suspendisse et mauris imperdiet, luctus augue eget, tempus eros. Cras vitae molestie ipsum. " />
|
||||
tools:text="One of THE GLORIOUS OWNERS OF THIS APP. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec id justo lorem. In malesuada feugiat ornare. Suspendisse et mauris imperdiet, luctus augue eget, tempus eros. Cras vitae molestie ipsum. " />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextViewV2
|
||||
android:id="@+id/mainUrl"
|
||||
@ -180,7 +205,10 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/mainBiography"
|
||||
android:ellipsize="marquee"
|
||||
android:padding="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@ -190,84 +218,92 @@
|
||||
tools:textColor="@android:color/holo_blue_dark"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnFollow"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="0dp"
|
||||
<awais.instagrabber.customviews.RamboTextViewV2
|
||||
android:id="@+id/profileContext"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/follow"
|
||||
android:textColor="@color/deep_purple_200"
|
||||
android:layout_below="@id/mainUrl"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_outline_person_add_24"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/deep_purple_200"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnSaved"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
app:rippleColor="@color/purple_200"
|
||||
app:layout_constraintBottom_toTopOf="@id/counts_barrier"
|
||||
tools:text="Followed by @instagram, @facebook + 69 more"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnSaved"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/saved"
|
||||
android:textColor="@color/blue_700"
|
||||
<View
|
||||
android:id="@+id/counts_barrier"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="?attr/colorOnPrimary"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_outline_class_24"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/blue_700"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnLiked"
|
||||
app:layout_constraintStart_toEndOf="@id/btnFollow"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
app:rippleColor="@color/blue_A400"
|
||||
app:layout_constraintTop_toBottomOf="@id/profileContext"
|
||||
tools:layout_editor_absoluteX="8dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnLiked"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/mainPostCount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/liked"
|
||||
android:textColor="@color/red_600"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_like"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/red_600"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/btnDM"
|
||||
app:layout_constraintStart_toEndOf="@id/btnSaved"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
app:rippleColor="@color/red_300"
|
||||
tools:visibility="visible" />
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
app:layout_constraintBottom_toBottomOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/mainFollowers"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/counts_barrier"
|
||||
tools:text="35\nPosts" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDM"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/mainFollowers"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dm_person"
|
||||
android:textColor="@color/green"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_round_send_24"
|
||||
app:iconGravity="top"
|
||||
app:iconTint="@color/green"
|
||||
app:layout_constraintBottom_toTopOf="@id/highlights_barrier"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
app:layout_constraintBottom_toBottomOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toStartOf="@id/mainFollowing"
|
||||
app:layout_constraintStart_toEndOf="@id/mainPostCount"
|
||||
app:layout_constraintTop_toBottomOf="@id/counts_barrier"
|
||||
tools:text="68\nFollowers" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/mainFollowing"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat"
|
||||
app:layout_constraintBottom_toBottomOf="@id/highlights_barrier"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/btnLiked"
|
||||
app:layout_constraintTop_toBottomOf="@id/mainUrl"
|
||||
app:rippleColor="@color/green"
|
||||
tools:visibility="visible" />
|
||||
app:layout_constraintStart_toEndOf="@id/mainFollowers"
|
||||
app:layout_constraintTop_toBottomOf="@id/counts_barrier"
|
||||
tools:text="64\nFollowing" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/highlights_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="btnSaved, btnLiked, btnFollow, btnDM" />
|
||||
app:barrierDirection="bottom" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/highlightsList"
|
||||
|
@ -20,4 +20,10 @@
|
||||
android:title="@string/restrict"
|
||||
android:visible="false"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/chaining"
|
||||
android:title="@string/action_ayml"
|
||||
android:visible="false"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
@ -41,6 +41,9 @@
|
||||
android:name="type"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="targetId"
|
||||
app:argType="long" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/comments_nav_graph" />
|
||||
|
@ -89,6 +89,9 @@
|
||||
android:name="type"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="targetId"
|
||||
app:argType="long" />
|
||||
</action>
|
||||
|
||||
<fragment
|
||||
|
@ -89,6 +89,9 @@
|
||||
android:name="type"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="targetId"
|
||||
app:argType="long" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/story_list_nav_graph" />
|
||||
|
@ -54,6 +54,9 @@
|
||||
android:name="type"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="targetId"
|
||||
app:argType="long" />
|
||||
</action>
|
||||
|
||||
<fragment
|
||||
|
@ -14,6 +14,9 @@
|
||||
android:name="type"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="targetId"
|
||||
app:argType="long" />
|
||||
<action
|
||||
android:id="@+id/action_notificationsViewerFragment_to_storyViewerFragment"
|
||||
app:destination="@id/storyViewerFragment" />
|
||||
|
@ -80,6 +80,9 @@
|
||||
android:name="type"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="targetId"
|
||||
app:argType="long" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/saved_nav_graph" />
|
||||
|
@ -64,15 +64,6 @@
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_notificationsViewerFragment"
|
||||
app:destination="@id/notification_viewer_nav_graph">
|
||||
<argument
|
||||
android:name="type"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/savedCollectionsFragment"
|
||||
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
|
||||
|
@ -49,14 +49,14 @@
|
||||
<string name="import_export">Import/Export</string>
|
||||
<string name="select_language">Language</string>
|
||||
<plurals name="main_posts_count_inline">
|
||||
<item quantity="one">%s Post</item>
|
||||
<item quantity="other">%s Posts</item>
|
||||
<item quantity="one">%s\nPost</item>
|
||||
<item quantity="other">%s\nPosts</item>
|
||||
</plurals>
|
||||
<plurals name="main_posts_followers">
|
||||
<item quantity="one">%s Follower</item>
|
||||
<item quantity="other">%s Followers</item>
|
||||
<item quantity="one">%s\nFollower</item>
|
||||
<item quantity="other">%s\nFollowers</item>
|
||||
</plurals>
|
||||
<string name="main_posts_following">%s Following </string>
|
||||
<string name="main_posts_following">%s\nFollowing</string>
|
||||
<string name="post_viewer_autoplay_video">Autoplay videos</string>
|
||||
<string name="post_viewer_muted_autoplay">Always mute videos</string>
|
||||
<string name="post_viewer_show_captions">Always show post captions</string>
|
||||
|
Loading…
Reference in New Issue
Block a user