Merge branch 'master' into dm-notifications-enhancements

This commit is contained in:
Austin Huang 2021-03-05 14:21:24 -05:00 committed by GitHub
commit d6539bddc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1000 additions and 792 deletions

View File

@ -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",

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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);

View File

@ -366,7 +366,7 @@ public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
}
}
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);
}
}

View File

@ -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),

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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,36 +476,32 @@ 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) {
sticking = false;
try {
poll.setMyChoice(w);
Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
}
catch (Exception ignored) {}
sticking = true;
storiesService.respondToPoll(
currentStory.getStoryMediaId().split("_")[0],
poll.getId(),
w,
new ServiceCallback<StoryStickerResponse>() {
@Override
public void onSuccess(final StoryStickerResponse result) {
sticking = false;
try {
poll.setMyChoice(w);
Toast.makeText(context, R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
}
catch (Exception ignored) {}
}
@Override
public void onFailure(final Throwable t) {
sticking = false;
Log.e(TAG, "Error responding", t);
try {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
catch (Exception ignored) {}
@Override
public void onFailure(final Throwable t) {
sticking = false;
Log.e(TAG, "Error responding", t);
try {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
});
}
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);
}

View File

@ -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);
}

View File

@ -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,11 +364,31 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
inflater.inflate(R.menu.profile_menu, menu);
blockMenuItem = menu.findItem(R.id.block);
if (blockMenuItem != null) {
blockMenuItem.setVisible(false);
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) {
restrictMenuItem.setVisible(false);
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);
}
}
}
@ -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,15 +910,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
blockMenuItem.setTitle(R.string.block);
}
}
return;
}
if (!isReallyPrivate() && restrictMenuItem != null) {
restrictMenuItem.setVisible(true);
if (profileModel.getFriendshipStatus().isRestricted()) {
restrictMenuItem.setTitle(R.string.unrestrict);
} else {
restrictMenuItem.setTitle(R.string.restrict);
if (chainingMenuItem != null && !Objects.equals(profileId, myId)) {
chainingMenuItem.setVisible(true);
}
return;
}
}

View File

@ -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;
}));

View File

@ -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; }
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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 + '\'' +
'}';
}
}

View File

@ -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;
}
}

View File

@ -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)

View File

@ -781,7 +781,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(

View File

@ -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<>();
}

View File

@ -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);

View File

@ -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);
}
callback.onSuccess(result);
} catch (JSONException e) {
callback.onFailure(e);
}
final List<Notification> result = new ArrayList<>();
result.addAll(body.getNewStories());
result.addAll(body.getOldStories());
callback.onSuccess(result);
}
@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"),
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));
result.add(new Notification(
new NotificationArgs(
data.optString("text"),
null,
user.getLong(Constants.EXTRAS_ID),
user.getString("profile_pic_url"),
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,
data.getLong(Constants.EXTRAS_ID),
data.getString("username"),
data.getString("profile_pic_url"),
0,
null, NotificationType.REQUEST));
result.add(new Notification(
new NotificationArgs(
null,
null,
data.getLong(Constants.EXTRAS_ID),
data.getString("profile_pic_url"),
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"),
0L,
data.getLong("pk"),
data.getString("username"),
data.getString("profile_pic_url"),
0,
data.getString("full_name"), // just borrowing this field
NotificationType.AYML);
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,
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);
}
});
}
}

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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" />

View File

@ -89,6 +89,9 @@
android:name="type"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
app:argType="long" />
</action>
<fragment

View File

@ -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" />

View File

@ -54,6 +54,9 @@
android:name="type"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
app:argType="long" />
</action>
<fragment

View File

@ -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" />

View File

@ -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" />

View File

@ -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"

View File

@ -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>