From 7acd5faefd5ff39674f1af67c87da67b27becd92 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Thu, 4 Mar 2021 15:27:46 -0500 Subject: [PATCH] notification backend revamp --- .../adapters/NotificationsAdapter.java | 35 +-- .../viewholder/NotificationViewHolder.java | 31 +-- .../asyncs/GetActivityAsyncTask.java | 139 ------------ .../asyncs/NotificationsFetcher.java | 18 +- .../NotificationsViewerFragment.java | 37 ++-- .../models/NotificationModel.java | 79 ------- .../repositories/NewsRepository.java | 6 +- .../repositories/responses/AymlResponse.java | 22 ++ .../repositories/responses/AymlUser.java | 34 +++ .../repositories/responses/AymlUserList.java | 15 ++ .../responses/NewsInboxResponse.java | 29 +++ .../repositories/responses/Notification.java | 29 +++ .../responses/NotificationArgs.java | 86 ++++++++ .../responses/NotificationCounts.java | 57 +++++ .../responses/NotificationImage.java | 19 ++ .../repositories/responses/UserInfo.java | 49 ----- .../services/ActivityCheckerService.java | 17 +- .../viewmodels/NotificationViewModel.java | 6 +- .../instagrabber/webservices/NewsService.java | 205 +++++++----------- 19 files changed, 452 insertions(+), 461 deletions(-) delete mode 100644 app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java delete mode 100755 app/src/main/java/awais/instagrabber/models/NotificationModel.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/AymlResponse.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/AymlUser.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/AymlUserList.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/Notification.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java create mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java delete mode 100644 app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java diff --git a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java index 394c2374..8a5c3a4b 100644 --- a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java @@ -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 { +public final class NotificationsAdapter extends ListAdapter { private final OnNotificationClickListener notificationClickListener; - private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() { + private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() { @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 list, @Nullable final Runnable commitCallback) { + public void submitList(@Nullable final List list, @Nullable final Runnable commitCallback) { if (list == null) { super.submitList(null, commitCallback); return; @@ -61,7 +62,7 @@ public final class NotificationsAdapter extends ListAdapter list) { + public void submitList(@Nullable final List list) { if (list == null) { super.submitList(null); return; @@ -69,8 +70,10 @@ public final class NotificationsAdapter extends ListAdapter sort(final List list) { - final List listCopy = new ArrayList<>(list); + private List sort(final List list) { + final List 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 { 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); diff --git a/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java b/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java deleted file mode 100644 index 13a88ee4..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java +++ /dev/null @@ -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 { - 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); - } -} diff --git a/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java index 714a2793..89729909 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java @@ -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> { +public final class NotificationsFetcher extends AsyncTask> { private static final String TAG = "NotificationsFetcher"; - private final FetchListener> fetchListener; + private final FetchListener> fetchListener; private final NewsService newsService; private final boolean markAsSeen; private boolean fetchedWeb = false; public NotificationsFetcher(final boolean markAsSeen, - final FetchListener> fetchListener) { + final FetchListener> fetchListener) { this.markAsSeen = markAsSeen; this.fetchListener = fetchListener; newsService = NewsService.getInstance(); } @Override - protected List doInBackground(final Void... voids) { - List notificationModels = new ArrayList<>(); + protected List doInBackground(final Void... voids) { + List notificationModels = new ArrayList<>(); - newsService.fetchAppInbox(markAsSeen, new ServiceCallback>() { + newsService.fetchAppInbox(markAsSeen, new ServiceCallback>() { @Override - public void onSuccess(final List result) { + public void onSuccess(final List result) { if (result == null) return; notificationModels.addAll(result); if (fetchedWeb) { @@ -44,7 +44,7 @@ public final class NotificationsFetcher extends AsyncTask() { + mediaService.fetch(mediaId, new ServiceCallback() { @Override public void onSuccess(final Media feedModel) { final PostViewV2Fragment fragment = PostViewV2Fragment @@ -95,13 +101,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 +117,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 +138,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe break; case 1: if (model.getType() == NotificationType.REQUEST) { - friendshipService.approve(model.getUserId(), new ServiceCallback() { + friendshipService.approve(args.getUserId(), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { onRefresh(); @@ -148,7 +155,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe clickListener.onPreviewClick(model); break; case 2: - friendshipService.ignore(model.getUserId(), new ServiceCallback() { + friendshipService.ignore(args.getUserId(), new ServiceCallback() { @Override public void onSuccess(final FriendshipChangeResponse result) { onRefresh(); @@ -226,9 +233,9 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe binding.swipeRefreshLayout.setRefreshing(true); switch (type) { case "notif": - new NotificationsFetcher(true, new FetchListener>() { + new NotificationsFetcher(true, new FetchListener>() { @Override - public void onResult(final List notificationModels) { + public void onResult(final List notificationModels) { binding.swipeRefreshLayout.setRefreshing(false); notificationViewModel.getList().postValue(notificationModels); } @@ -245,9 +252,9 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe break; case "ayml": final NewsService newsService = NewsService.getInstance(); - newsService.fetchSuggestions(csrfToken, new ServiceCallback>() { + newsService.fetchSuggestions(csrfToken, new ServiceCallback>() { @Override - public void onSuccess(final List notificationModels) { + public void onSuccess(final List notificationModels) { binding.swipeRefreshLayout.setRefreshing(false); notificationViewModel.getList().postValue(notificationModels); } diff --git a/app/src/main/java/awais/instagrabber/models/NotificationModel.java b/app/src/main/java/awais/instagrabber/models/NotificationModel.java deleted file mode 100755 index 4ca34b2d..00000000 --- a/app/src/main/java/awais/instagrabber/models/NotificationModel.java +++ /dev/null @@ -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; } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java index edc1e149..cd9ec396 100644 --- a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java @@ -2,6 +2,8 @@ package awais.instagrabber.repositories; import java.util.Map; +import awais.instagrabber.repositories.responses.AymlResponse; +import awais.instagrabber.repositories.responses.NewsInboxResponse; import retrofit2.Call; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; @@ -16,9 +18,9 @@ public interface NewsRepository { Call webInbox(@Header("User-Agent") String userAgent); @GET("/api/v1/news/inbox/") - Call appInbox(@Header("User-Agent") String userAgent, @Query(value = "mark_as_seen", encoded = true) boolean markAsSeen); + Call appInbox(@Header("User-Agent") String userAgent, @Query(value = "mark_as_seen", encoded = true) boolean markAsSeen); @FormUrlEncoded @POST("/api/v1/discover/ayml/") - Call getAyml(@Header("User-Agent") String userAgent, @FieldMap final Map form); + Call getAyml(@Header("User-Agent") String userAgent, @FieldMap final Map form); } diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/AymlResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/AymlResponse.java new file mode 100644 index 00000000..5762e170 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/AymlResponse.java @@ -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; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/AymlUser.java b/app/src/main/java/awais/instagrabber/repositories/responses/AymlUser.java new file mode 100644 index 00000000..7f5b2632 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/AymlUser.java @@ -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; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/AymlUserList.java b/app/src/main/java/awais/instagrabber/repositories/responses/AymlUserList.java new file mode 100644 index 00000000..47b2c112 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/AymlUserList.java @@ -0,0 +1,15 @@ +package awais.instagrabber.repositories.responses; + +import java.util.List; + +public class AymlUserList { + private final List suggestions; + + public AymlUserList(final List suggestions) { + this.suggestions = suggestions; + } + + public List getSuggestions() { + return suggestions; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java new file mode 100644 index 00000000..5dcdb487 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java @@ -0,0 +1,29 @@ +package awais.instagrabber.repositories.responses; + +import java.util.List; + +public class NewsInboxResponse { + private final NotificationCounts counts; + private final List newStories; + private final List oldStories; + + public NewsInboxResponse(final NotificationCounts counts, + final List newStories, + final List oldStories) { + this.counts = counts; + this.newStories = newStories; + this.oldStories = oldStories; + } + + public NotificationCounts getCounts() { + return counts; + } + + public List getNewStories() { + return newStories; + } + + public List getOldStories() { + return oldStories; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Notification.java b/app/src/main/java/awais/instagrabber/repositories/responses/Notification.java new file mode 100644 index 00000000..d81bf357 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Notification.java @@ -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; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java b/app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java new file mode 100644 index 00000000..f66361fa --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java @@ -0,0 +1,86 @@ +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 media; + private final double timestamp; + private final String profileName; + private final String fullName; // for AYML, not naturally generated + + public NotificationArgs(final String text, + final String richText, // for AYML, this is the algorithm + final long profileId, + final String profileImage, + final List media, + final double timestamp, + final String profileName, + final String fullName) { + this.text = text; + this.richText = richText; + this.profileId = profileId; + this.profileImage = profileImage; + this.media = media; + this.timestamp = timestamp; + this.profileName = profileName; + this.fullName = fullName; + } + + 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 getMedia() { + return media; + } + + public double getTimestamp() { + return timestamp; + } + + @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; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java b/app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java new file mode 100644 index 00000000..38fb70f9 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java @@ -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; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java b/app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java new file mode 100644 index 00000000..3ba92ce2 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java @@ -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; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java b/app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java deleted file mode 100644 index d85f5161..00000000 --- a/app/src/main/java/awais/instagrabber/repositories/responses/UserInfo.java +++ /dev/null @@ -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 + '\'' + - '}'; - } -} diff --git a/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java b/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java index f0ccea70..bdadac87 100644 --- a/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java +++ b/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java @@ -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 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) diff --git a/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java index aa8fc194..59a8bb64 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java @@ -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; + private MutableLiveData> list; - public MutableLiveData> getList() { + public MutableLiveData> getList() { if (list == null) { list = new MutableLiveData<>(); } diff --git a/app/src/main/java/awais/instagrabber/webservices/NewsService.java b/app/src/main/java/awais/instagrabber/webservices/NewsService.java index 2eca1769..b450312c 100644 --- a/app/src/main/java/awais/instagrabber/webservices/NewsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/NewsService.java @@ -9,17 +9,23 @@ 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.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 +58,31 @@ public class NewsService extends BaseService { } public void fetchAppInbox(final boolean markAsSeen, - final ServiceCallback> callback) { - final List result = new ArrayList<>(); - final Call request = repository.appInbox(appUa, markAsSeen); - request.enqueue(new Callback() { + final ServiceCallback> callback) { + final Call request = repository.appInbox(appUa, markAsSeen); + request.enqueue(new Callback() { @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String body = response.body(); + public void onResponse(@NonNull final Call call, @NonNull final Response 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 result = new ArrayList<>(); + result.addAll(body.getNewStories()); + result.addAll(body.getOldStories()); + callback.onSuccess(result); } @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { callback.onFailure(t); // Log.e(TAG, "onFailure: ", t); } }); } - public void fetchWebInbox(final boolean markAsSeen, - final ServiceCallback> callback) { + public void fetchWebInbox(final ServiceCallback> callback) { final Call request = repository.webInbox(browserUa); request.enqueue(new Callback() { @Override @@ -104,7 +93,7 @@ public class NewsService extends BaseService { return; } try { - final List result = new ArrayList<>(); + final List result = new ArrayList<>(); final JSONObject page = new JSONObject(body) .getJSONObject("graphql") .getJSONObject("user"); @@ -124,16 +113,24 @@ 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 + ), + type, + data.getString(Constants.EXTRAS_ID) + )); } } @@ -144,15 +141,20 @@ 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") + ), + "REQUEST", + data.getString(Constants.EXTRAS_ID) + )); } } callback.onSuccess(result); @@ -169,40 +171,8 @@ 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> callback) { + final ServiceCallback> callback) { final Map form = new HashMap<>(); form.put("_uuid", UUID.randomUUID().toString()); form.put("_csrftoken", csrfToken); @@ -210,57 +180,46 @@ public class NewsService extends BaseService { form.put("device_id", UUID.randomUUID().toString()); form.put("module", "discover_people"); form.put("paginate", "false"); - final Call request = repository.getAyml(appUa, form); - request.enqueue(new Callback() { + final Call request = repository.getAyml(appUa, form); + request.enqueue(new Callback() { @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String body = response.body(); + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final AymlResponse body = response.body(); if (body == null) { callback.onSuccess(null); return; } - try { - final List 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 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 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() + ), + "AYML", + i.getUuid() + ); + }) + .collect(Collectors.toList()); + callback.onSuccess(newsItems); } @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + public void onFailure(@NonNull final Call 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); - } }