From 975654961fc66166a0c603341b4933e6e02f9088 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Wed, 30 Dec 2020 13:26:16 -0500 Subject: [PATCH] add suggested users feature --- .../viewholder/NotificationViewHolder.java | 10 ++- .../NotificationsViewerFragment.java | 39 +++++++++-- .../settings/MorePreferencesFragment.java | 8 ++- .../models/enums/NotificationType.java | 6 +- .../repositories/NewsRepository.java | 5 ++ .../instagrabber/webservices/NewsService.java | 70 ++++++++++++++++++- .../main/res/drawable/ic_suggested_users.xml | 10 +++ .../navigation/direct_messages_nav_graph.xml | 7 +- .../res/navigation/discover_nav_graph.xml | 7 +- .../main/res/navigation/feed_nav_graph.xml | 7 +- .../main/res/navigation/more_nav_graph.xml | 7 +- .../notification_viewer_nav_graph.xml | 14 +++- .../main/res/navigation/profile_nav_graph.xml | 7 +- app/src/main/res/values/strings.xml | 3 +- 14 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 app/src/main/res/drawable/ic_suggested_users.xml diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java index e695d07e..8915ec60 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java @@ -53,18 +53,22 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder { case RESPONDED_STORY: subtext = model.getText(); break; + case AYML: + subtext = model.getPostId(); + break; } + binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? model.getText() : subtext); if (text == -1 && subtext != null) { binding.tvComment.setText(subtext); - binding.tvSubComment.setVisibility(View.GONE); + binding.tvComment.setVisibility(TextUtils.isEmpty(subtext) ? View.GONE : View.VISIBLE); + binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE); } else if (text != -1) { binding.tvComment.setText(text); - binding.tvSubComment.setText(subtext); binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE); } - if (model.getType() != NotificationType.REQUEST) { + if (model.getType() != NotificationType.REQUEST && model.getType() != NotificationType.AYML) { binding.tvDate.setText(model.getDateTime()); } diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java index cd48cd99..2eb9406d 100644 --- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java @@ -26,6 +26,8 @@ import androidx.navigation.fragment.NavHostFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import java.util.List; + import awais.instagrabber.R; import awais.instagrabber.adapters.NotificationsAdapter; import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener; @@ -46,6 +48,7 @@ import awais.instagrabber.utils.Utils; import awais.instagrabber.viewmodels.NotificationViewModel; import awais.instagrabber.webservices.FriendshipService; import awais.instagrabber.webservices.MediaService; +import awais.instagrabber.webservices.NewsService; import awais.instagrabber.webservices.ServiceCallback; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -59,8 +62,8 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe private NotificationViewModel notificationViewModel; private FriendshipService friendshipService; private MediaService mediaService; - private String userId; - private String csrfToken; + private NewsService newsService; + private String userId, csrfToken, type; private Context context; private final OnNotificationClickListener clickListener = new OnNotificationClickListener() { @@ -96,7 +99,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe public void onNotificationClick(final NotificationModel model) { if (model == null) return; final String username = model.getUsername(); - if (model.getType() == NotificationType.FOLLOW) { + if (model.getType() == NotificationType.FOLLOW || model.getType() == NotificationType.AYML) { openProfile(username); } else { @@ -242,6 +245,8 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe } private void init() { + final NotificationsViewerFragmentArgs fragmentArgs = NotificationsViewerFragmentArgs.fromBundle(getArguments()); + type = fragmentArgs.getType(); final Context context = getContext(); CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE)); binding.swipeRefreshLayout.setOnRefreshListener(this); @@ -256,10 +261,30 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe @Override public void onRefresh() { binding.swipeRefreshLayout.setRefreshing(true); - new NotificationsFetcher(true, notificationModels -> { - binding.swipeRefreshLayout.setRefreshing(false); - notificationViewModel.getList().postValue(notificationModels); - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + switch (type) { + case "notif": + new NotificationsFetcher(true, notificationModels -> { + binding.swipeRefreshLayout.setRefreshing(false); + notificationViewModel.getList().postValue(notificationModels); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + break; + case "ayml": + newsService = NewsService.getInstance(); + newsService.fetchSuggestions(csrfToken, new ServiceCallback>() { + @Override + public void onSuccess(final List notificationModels) { + binding.swipeRefreshLayout.setRefreshing(false); + notificationViewModel.getList().postValue(notificationModels); + } + + @Override + public void onFailure(final Throwable t) { + binding.swipeRefreshLayout.setRefreshing(false); + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + } + }); + break; + } } private void openProfile(final String username) { diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java index 12b94137..6e2b88b8 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java @@ -134,7 +134,13 @@ public class MorePreferencesFragment extends BasePreferencesFragment { screen.addPreference(getDivider(context)); if (isLoggedIn) { screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> { - NavHostFragment.findNavController(this).navigate(R.id.action_global_notificationsViewerFragment); + final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif"); + 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"); + NavHostFragment.findNavController(this).navigate(navDirections); return true; })); screen.addPreference(getPreference(R.string.action_archive, R.drawable.ic_archive, preference -> { diff --git a/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java b/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java index 79c3b805..7e39443b 100755 --- a/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java +++ b/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java @@ -15,8 +15,10 @@ public enum NotificationType implements Serializable { COMMENT_LIKE("13"), TAGGED_COMMENT("14"), RESPONDED_STORY("213"), - // efr - REQUEST("REQUEST"); + // efr - random value + REQUEST("REQUEST"), + // ayml - random value + AYML("AYML"); private final String itemType; private static final Map map = new HashMap<>(); diff --git a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java index 6271cd81..20b50934 100644 --- a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java @@ -21,4 +21,9 @@ public interface NewsRepository { @Headers("User-Agent: " + Constants.I_USER_AGENT) @GET("/api/v1/news/inbox/") Call appInbox(@Query(value = "mark_as_seen", encoded = true) boolean markAsSeen); + + @FormUrlEncoded + @Headers("User-Agent: " + Constants.I_USER_AGENT) + @POST("/api/v1/discover/ayml/") + Call getAyml(@FieldMap final Map form); } diff --git a/app/src/main/java/awais/instagrabber/webservices/NewsService.java b/app/src/main/java/awais/instagrabber/webservices/NewsService.java index 76407003..3e2a37fa 100644 --- a/app/src/main/java/awais/instagrabber/webservices/NewsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/NewsService.java @@ -12,14 +12,15 @@ import java.util.ArrayList; 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 awais.instagrabber.BuildConfig; import awais.instagrabber.models.NotificationModel; import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.repositories.NewsRepository; import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.NetworkUtils; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -89,7 +90,6 @@ public class NewsService extends BaseService { public void fetchWebInbox(final boolean markAsSeen, final ServiceCallback> callback) { - final List result = new ArrayList<>(); final Call request = repository.webInbox(); request.enqueue(new Callback() { @Override @@ -100,6 +100,7 @@ public class NewsService extends BaseService { return; } try { + final List result = new ArrayList<>(); final JSONObject page = new JSONObject(body) .getJSONObject("graphql") .getJSONObject("user"); @@ -168,7 +169,7 @@ public class NewsService extends BaseService { final String type = itemJson.getString("story_type"); final NotificationType notificationType = NotificationType.valueOfType(type); if (notificationType == null) { - Log.d("austin_debug", "unhandled news type: "+itemJson); + if (BuildConfig.DEBUG) Log.d("austin_debug", "unhandled news type: "+itemJson); return null; } final JSONObject data = itemJson.getJSONObject("args"); @@ -194,4 +195,67 @@ public class NewsService extends BaseService { } return result; } + + public void fetchSuggestions(final String csrfToken, + final ServiceCallback> callback) { + final Map form = new HashMap<>(); + form.put("_uuid", UUID.randomUUID().toString()); + form.put("_csrftoken", csrfToken); + form.put("phone_id", UUID.randomUUID().toString()); + form.put("device_id", UUID.randomUUID().toString()); + form.put("module", "discover_people"); + form.put("paginate", "false"); + final Call request = repository.getAyml(form); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String 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"); + + 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); + } + } + + @Override + 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.getString("pk"), + data.getString("username"), + data.getString("profile_pic_url"), + data.getString("full_name"), // just borrowing this field + null, + NotificationType.AYML); + } } diff --git a/app/src/main/res/drawable/ic_suggested_users.xml b/app/src/main/res/drawable/ic_suggested_users.xml new file mode 100644 index 00000000..709fe2d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_suggested_users.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/navigation/direct_messages_nav_graph.xml b/app/src/main/res/navigation/direct_messages_nav_graph.xml index a6846a39..5ac50a32 100644 --- a/app/src/main/res/navigation/direct_messages_nav_graph.xml +++ b/app/src/main/res/navigation/direct_messages_nav_graph.xml @@ -42,7 +42,12 @@ + app:destination="@id/notification_viewer_nav_graph"> + + diff --git a/app/src/main/res/navigation/discover_nav_graph.xml b/app/src/main/res/navigation/discover_nav_graph.xml index 06b346ae..d6067837 100644 --- a/app/src/main/res/navigation/discover_nav_graph.xml +++ b/app/src/main/res/navigation/discover_nav_graph.xml @@ -76,7 +76,12 @@ + app:destination="@id/notification_viewer_nav_graph"> + + + app:destination="@id/notification_viewer_nav_graph"> + + diff --git a/app/src/main/res/navigation/more_nav_graph.xml b/app/src/main/res/navigation/more_nav_graph.xml index 43bb07b1..8980e73a 100644 --- a/app/src/main/res/navigation/more_nav_graph.xml +++ b/app/src/main/res/navigation/more_nav_graph.xml @@ -50,7 +50,12 @@ + app:destination="@id/notification_viewer_nav_graph"> + + + tools:layout="@layout/fragment_notifications_viewer"> + + + app:destination="@id/notificationsViewer"> + + diff --git a/app/src/main/res/navigation/profile_nav_graph.xml b/app/src/main/res/navigation/profile_nav_graph.xml index 808b9396..108fd442 100644 --- a/app/src/main/res/navigation/profile_nav_graph.xml +++ b/app/src/main/res/navigation/profile_nav_graph.xml @@ -77,7 +77,12 @@ + app:destination="@id/notification_viewer_nav_graph"> + + Oops.. the app crashed, but don\'t worry you can send error report to the developer to help him fix the issue. (: Activity Story archive - Copyright (C) 2019 AWAiS\nCopyright (C) 2020 Austin Huang, Ammar Githam\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses/. + Suggested users + Copyright (C) 2019-2020 AWAiS\nCopyright (C) 2020-2021 Austin Huang & Ammar Githam\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses/. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Select Picture Uploading…