diff --git a/app/build.gradle b/app/build.gradle index d945f017..493366c3 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -132,6 +132,6 @@ dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' githubImplementation 'io.sentry:sentry-android:4.3.0' - + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' } diff --git a/app/src/main/java/awais/instagrabber/adapters/KeywordsFilterAdapter.java b/app/src/main/java/awais/instagrabber/adapters/KeywordsFilterAdapter.java new file mode 100644 index 00000000..d3b0cc70 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/adapters/KeywordsFilterAdapter.java @@ -0,0 +1,42 @@ +package awais.instagrabber.adapters; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; + +import awais.instagrabber.R; +import awais.instagrabber.adapters.viewholder.dialogs.KeywordsFilterDialogViewHolder; + +public class KeywordsFilterAdapter extends RecyclerView.Adapter { + + private final Context context; + private final ArrayList items; + + public KeywordsFilterAdapter(Context context, ArrayList items){ + this.context = context; + this.items = items; + } + + @NonNull + @Override + public KeywordsFilterDialogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + final View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_keyword, parent, false); + return new KeywordsFilterDialogViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull KeywordsFilterDialogViewHolder holder, int position) { + holder.bind(items, position, context, this); + } + + @Override + public int getItemCount() { + return items.size(); + } +} diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/dialogs/KeywordsFilterDialogViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/dialogs/KeywordsFilterDialogViewHolder.java new file mode 100644 index 00000000..65fe7b77 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/dialogs/KeywordsFilterDialogViewHolder.java @@ -0,0 +1,51 @@ +package awais.instagrabber.adapters.viewholder.dialogs; + +import android.content.Context; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.HashSet; + +import awais.instagrabber.R; +import awais.instagrabber.adapters.KeywordsFilterAdapter; +import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.SettingsHelper; + +public class KeywordsFilterDialogViewHolder extends RecyclerView.ViewHolder { + + private final Button deleteButton; + private final TextView item; + + public KeywordsFilterDialogViewHolder(@NonNull View itemView) { + super(itemView); + deleteButton = itemView.findViewById(R.id.keyword_delete); + item = itemView.findViewById(R.id.keyword_text); + } + + public void bind(ArrayList items, int position, Context context, KeywordsFilterAdapter adapter){ + item.setText(items.get(position)); + deleteButton.setOnClickListener(view -> { + final String s = items.get(position); + SettingsHelper settingsHelper = new SettingsHelper(context); + items.remove(position); + settingsHelper.putStringSet(Constants.KEYWORD_FILTERS, new HashSet<>(items)); + adapter.notifyDataSetChanged(); + final String message = context.getString(R.string.removed_keywords, s); + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + }); + } + + public Button getDeleteButton(){ + return deleteButton; + } + + public TextView getTextView(){ + return item; + } +} diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java index b07b7b45..4fe91772 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java @@ -40,7 +40,10 @@ public class FeedPostFetchService implements PostFetcher.PostFetchService { } else if (result == null) return; nextCursor = result.getNextCursor(); hasNextPage = result.hasNextPage(); - feedModels.addAll(result.getFeedModels()); + + final List mediaResults = result.getFeedModels(); + feedModels.addAll(mediaResults); + if (fetchListener != null) { // if (feedModels.size() < 15 && hasNextPage) { // feedService.fetch(csrfToken, nextCursor, this); diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java index eef69f41..ddd670f1 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java @@ -133,7 +133,7 @@ public final class PostFetcher extends AsyncTask { // feedModelBuilder.setSliderItems(postModels); // } // return feedModelBuilder.build(); - return ResponseBodyUtils.parseGraphQLItem(media); + return ResponseBodyUtils.parseGraphQLItem(media, null); } } catch (Exception e) { // if (logCollector != null) { diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java deleted file mode 100755 index 1f1ba2cc..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java +++ /dev/null @@ -1,101 +0,0 @@ -package awais.instagrabber.asyncs; - -import android.os.AsyncTask; -import android.util.Log; - -import androidx.annotation.Nullable; - -import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.repositories.responses.FriendshipStatus; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.webservices.GraphQLService; -import awais.instagrabber.webservices.ServiceCallback; -import awais.instagrabber.webservices.UserService; - -public final class ProfileFetcher extends AsyncTask { - private static final String TAG = ProfileFetcher.class.getSimpleName(); - private final UserService userService; - private final GraphQLService graphQLService; - - private final FetchListener fetchListener; - private final long myId; - private final boolean isLoggedIn; - private final String userName; - - public ProfileFetcher(final String userName, - final long myId, - final boolean isLoggedIn, - final FetchListener fetchListener) { - this.userName = userName; - this.myId = myId; - this.isLoggedIn = isLoggedIn; - this.fetchListener = fetchListener; - userService = isLoggedIn ? UserService.getInstance() : null; - graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); - } - - @Nullable - @Override - protected Void doInBackground(final Void... voids) { - if (isLoggedIn && userName != null) { - userService.getUsernameInfo(userName, new ServiceCallback() { - @Override - public void onSuccess(final User user) { - userService.getUserFriendship(user.getPk(), new ServiceCallback() { - @Override - public void onSuccess(final FriendshipStatus status) { - user.setFriendshipStatus(status); - fetchListener.onResult(user); - } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error", t); - fetchListener.onFailure(t); - } - }); - } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error", t); - fetchListener.onFailure(t); - } - }); - } - else if (isLoggedIn) { - userService.getUserInfo(myId, new ServiceCallback() { - @Override - public void onSuccess(final User user) { - fetchListener.onResult(user); - } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error", t); - fetchListener.onFailure(t); - } - }); - } - else { - graphQLService.fetchUser(userName, new ServiceCallback() { - @Override - public void onSuccess(final User user) { - fetchListener.onResult(user); - } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error", t); - fetchListener.onFailure(t); - } - }); - } - return null; - } - - @Override - protected void onPreExecute() { - if (fetchListener != null) fetchListener.doBefore(); - } -} diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java index e2cfa10c..02e0e27a 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java @@ -49,7 +49,7 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService { } }; if (isLoggedIn) profileService.fetchPosts(profileModel.getPk(), nextMaxId, cb); - else graphQLService.fetchProfilePosts(profileModel.getPk(), 30, nextMaxId, cb); + else graphQLService.fetchProfilePosts(profileModel.getPk(), 30, nextMaxId, profileModel, cb); } @Override diff --git a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java index f7aac54e..cdab91c9 100644 --- a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java +++ b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java @@ -32,11 +32,15 @@ import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.repositories.responses.Media; +import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.KeywordsFilterUtils; import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.Utils; import awais.instagrabber.viewmodels.MediaViewModel; import awais.instagrabber.workers.DownloadWorker; +import static awais.instagrabber.utils.Utils.settingsHelper; + public class PostsRecyclerView extends RecyclerView { private static final String TAG = "PostsRecyclerView"; @@ -70,7 +74,13 @@ public class PostsRecyclerView extends RecyclerView { } final List models = mediaViewModel.getList().getValue(); final List modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models); - modelsCopy.addAll(result); + if (settingsHelper.getBoolean(Constants.TOGGLE_KEYWORD_FILTER)){ + final ArrayList items = new ArrayList<>(settingsHelper.getStringSet(Constants.KEYWORD_FILTERS)); + modelsCopy.addAll(new KeywordsFilterUtils(items).filter(result)); + } + else { + modelsCopy.addAll(result); + } mediaViewModel.getList().postValue(modelsCopy); dispatchFetchStatus(); } diff --git a/app/src/main/java/awais/instagrabber/customviews/emoji/Emoji.java b/app/src/main/java/awais/instagrabber/customviews/emoji/Emoji.java index d90c4d8e..2be01c47 100644 --- a/app/src/main/java/awais/instagrabber/customviews/emoji/Emoji.java +++ b/app/src/main/java/awais/instagrabber/customviews/emoji/Emoji.java @@ -2,7 +2,6 @@ package awais.instagrabber.customviews.emoji; import androidx.annotation.NonNull; -import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -12,10 +11,12 @@ public class Emoji { private final List variants; private GoogleCompatEmojiDrawable drawable; - public Emoji(final String unicode, final String name) { + public Emoji(final String unicode, + final String name, + final List variants) { this.unicode = unicode; this.name = name; - this.variants = new LinkedList<>(); + this.variants = variants; } public String getUnicode() { @@ -35,7 +36,7 @@ public class Emoji { } public GoogleCompatEmojiDrawable getDrawable() { - if (drawable == null) { + if (drawable == null && unicode != null) { drawable = new GoogleCompatEmojiDrawable(unicode); } return drawable; @@ -60,6 +61,7 @@ public class Emoji { return "Emoji{" + "unicode='" + unicode + '\'' + ", name='" + name + '\'' + + ", variants=" + variants + '}'; } } diff --git a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategory.java b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategory.java index 4a621db0..89a2c866 100644 --- a/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategory.java +++ b/app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategory.java @@ -1,8 +1,8 @@ package awais.instagrabber.customviews.emoji; import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -10,12 +10,13 @@ import awais.instagrabber.R; public class EmojiCategory { private final EmojiCategoryType type; - private final Map emojis = new LinkedHashMap<>(); + private final Map emojis; @DrawableRes private int drawableRes; - public EmojiCategory(final EmojiCategoryType type) { + public EmojiCategory(final EmojiCategoryType type, final Map emojis) { this.type = type; + this.emojis = emojis; } public EmojiCategoryType getType() { @@ -73,4 +74,13 @@ public class EmojiCategory { public int hashCode() { return Objects.hash(type); } + + @NonNull + @Override + public String toString() { + return "EmojiCategory{" + + "type=" + type + + ", emojis=" + emojis + + '}'; + } } diff --git a/app/src/main/java/awais/instagrabber/dialogs/KeywordsFilterDialog.java b/app/src/main/java/awais/instagrabber/dialogs/KeywordsFilterDialog.java new file mode 100644 index 00000000..2a4d8a04 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/dialogs/KeywordsFilterDialog.java @@ -0,0 +1,78 @@ +package awais.instagrabber.dialogs; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.HashSet; + +import awais.instagrabber.R; +import awais.instagrabber.adapters.KeywordsFilterAdapter; +import awais.instagrabber.databinding.DialogKeywordsFilterBinding; +import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.SettingsHelper; +import awais.instagrabber.utils.Utils; + +public final class KeywordsFilterDialog extends DialogFragment { + + @Override + public void onStart() { + super.onStart(); + final Dialog dialog = getDialog(); + if (dialog == null) return; + final Window window = dialog.getWindow(); + if (window == null) return; + final int height = ViewGroup.LayoutParams.WRAP_CONTENT; + final int width = (int) (Utils.displayMetrics.widthPixels * 0.8); + window.setLayout(width, height); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + final DialogKeywordsFilterBinding dialogKeywordsFilterBinding = DialogKeywordsFilterBinding.inflate(inflater, container, false); + init(dialogKeywordsFilterBinding, getContext()); + dialogKeywordsFilterBinding.btnOK.setOnClickListener(view -> this.dismiss()); + return dialogKeywordsFilterBinding.getRoot(); + } + + private void init(DialogKeywordsFilterBinding dialogKeywordsFilterBinding, Context context){ + final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context); + final RecyclerView recyclerView = dialogKeywordsFilterBinding.recyclerKeyword; + recyclerView.setLayoutManager(linearLayoutManager); + + final SettingsHelper settingsHelper = new SettingsHelper(context); + final ArrayList items = new ArrayList<>(settingsHelper.getStringSet(Constants.KEYWORD_FILTERS)); + final KeywordsFilterAdapter adapter = new KeywordsFilterAdapter(context, items); + recyclerView.setAdapter(adapter); + + final EditText editText = dialogKeywordsFilterBinding.editText; + + dialogKeywordsFilterBinding.btnAdd.setOnClickListener(view ->{ + final String s = editText.getText().toString(); + if(s.isEmpty()) return; + if(items.contains(s)) { + editText.setText(""); + return; + } + items.add(s.toLowerCase()); + settingsHelper.putStringSet(Constants.KEYWORD_FILTERS, new HashSet<>(items)); + adapter.notifyItemInserted(items.size()); + final String message = context.getString(R.string.added_keywords, s); + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + editText.setText(""); + }); + } +} diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index d822587d..75e72949 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -378,6 +378,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe if (getArguments() == null) return; final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments()); hashtag = fragmentArgs.getHashtag(); + if (hashtag.charAt(0) == '#') hashtag = hashtag.substring(1); fetchHashtagModel(); } diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java index 0daf6561..0d0c7865 100644 --- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java @@ -925,7 +925,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im } private void setupLocation(final Location location) { - if (location == null) { + if (location == null || !detailsVisible) { binding.location.setVisibility(View.GONE); return; } diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index 161c8aa9..a9b9f722 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -4,7 +4,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.graphics.Typeface; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -57,7 +56,6 @@ import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.HighlightsAdapter; import awais.instagrabber.asyncs.CreateThreadAction; -import awais.instagrabber.asyncs.ProfileFetcher; import awais.instagrabber.asyncs.ProfilePostFetchService; import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper; @@ -73,7 +71,6 @@ import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.ProfilePicDialogFragment; import awais.instagrabber.fragments.PostViewV2Fragment; -import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.managers.DirectMessagesManager; import awais.instagrabber.managers.InboxManager; import awais.instagrabber.models.HighlightModel; @@ -93,11 +90,14 @@ import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; +import awais.instagrabber.viewmodels.AppStateViewModel; import awais.instagrabber.viewmodels.HighlightsViewModel; import awais.instagrabber.webservices.FriendshipService; +import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.MediaService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; +import awais.instagrabber.webservices.UserService; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG; @@ -120,6 +120,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private FriendshipService friendshipService; private StoriesService storiesService; private MediaService mediaService; + private UserService userService; + private GraphQLService graphQLService; private boolean shouldRefresh = true; private boolean hasStories = false; private HighlightsAdapter highlightsAdapter; @@ -304,6 +306,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private LayoutProfileDetailsBinding profileDetailsBinding; private AccountRepository accountRepository; private FavoriteRepository favoriteRepository; + private AppStateViewModel appStateViewModel; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { @@ -317,8 +320,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe friendshipService = isLoggedIn ? FriendshipService.getInstance(deviceUuid, csrfToken, myId) : null; storiesService = isLoggedIn ? StoriesService.getInstance(null, 0L, null) : null; mediaService = isLoggedIn ? MediaService.getInstance(null, null, 0) : null; + userService = isLoggedIn ? UserService.getInstance() : null; + graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext())); favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext())); + appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class); setHasOptionsMenu(true); } @@ -601,31 +607,66 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe if (usernameTemp.startsWith("@")) { usernameTemp = usernameTemp.substring(1); } - new ProfileFetcher(TextUtils.isEmpty(username) ? null : usernameTemp, myId, isLoggedIn, new FetchListener() { - @Override - public void onResult(final User user) { - if (getContext() == null) return; - if (TextUtils.isEmpty(username)) { - username = user.getUsername(); - setUsernameDelayed(); + if (TextUtils.isEmpty(usernameTemp)) { + profileModel = appStateViewModel.getCurrentUser(); + username = profileModel.getUsername(); + setUsernameDelayed(); + setProfileDetails(); + } + else if (isLoggedIn) { + userService.getUsernameInfo(usernameTemp, new ServiceCallback() { + @Override + public void onSuccess(final User user) { + userService.getUserFriendship(user.getPk(), new ServiceCallback() { + @Override + public void onSuccess(final FriendshipStatus status) { + user.setFriendshipStatus(status); + profileModel = user; + setProfileDetails(); + } + + @Override + public void onFailure(final Throwable t) { + Log.e(TAG, "Error fetching profile relationship", t); + final Context context = getContext(); + try { + if (t == null) Toast.makeText(context, R.string.error_loading_profile_loggedin, Toast.LENGTH_LONG).show(); + else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); + } catch (final Throwable ignored) {} + } + }); } - profileModel = user; - setProfileDetails(); - } - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error fetching profile", t); - final Context context = getContext(); - try { - if (t == null) Toast.makeText(context, - isLoggedIn ? R.string.error_loading_profile_loggedin : R.string.error_loading_profile, - Toast.LENGTH_LONG).show(); - else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); - } catch (final Throwable ignored) {} - } + @Override + public void onFailure(final Throwable t) { + Log.e(TAG, "Error fetching profile", t); + final Context context = getContext(); + try { + if (t == null) Toast.makeText(context, R.string.error_loading_profile_loggedin, Toast.LENGTH_LONG).show(); + else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); + } catch (final Throwable ignored) {} + } + }); + } + else { + graphQLService.fetchUser(usernameTemp, new ServiceCallback() { + @Override + public void onSuccess(final User user) { + profileModel = user; + setProfileDetails(); + } - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + @Override + public void onFailure(final Throwable t) { + Log.e(TAG, "Error fetching profile", t); + final Context context = getContext(); + try { + if (t == null) Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_LONG).show(); + else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show(); + } catch (final Throwable ignored) {} + } + }); + } } private void setProfileDetails() { diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/PostPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/PostPreferencesFragment.java index 07012fe9..14daec7f 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/PostPreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/PostPreferencesFragment.java @@ -8,10 +8,9 @@ import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreferenceCompat; import awais.instagrabber.R; +import awais.instagrabber.dialogs.KeywordsFilterDialog; import awais.instagrabber.utils.Constants; -import static awais.instagrabber.utils.Utils.settingsHelper; - public class PostPreferencesFragment extends BasePreferencesFragment { @Override void setupPreferenceScreen(final PreferenceScreen screen) { @@ -20,6 +19,8 @@ public class PostPreferencesFragment extends BasePreferencesFragment { // generalCategory.addPreference(getAutoPlayVideosPreference(context)); screen.addPreference(getAlwaysMuteVideosPreference(context)); screen.addPreference(getShowCaptionPreference(context)); + screen.addPreference(getToggleKeywordFilterPreference(context)); + screen.addPreference(getEditKeywordFilterPreference(context)); } private Preference getAutoPlayVideosPreference(@NonNull final Context context) { @@ -46,4 +47,24 @@ public class PostPreferencesFragment extends BasePreferencesFragment { preference.setIconSpaceReserved(false); return preference; } + + private Preference getToggleKeywordFilterPreference(@NonNull final Context context) { + final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); + preference.setKey(Constants.TOGGLE_KEYWORD_FILTER); + preference.setDefaultValue(false); + preference.setTitle(R.string.toggle_keyword_filter); + preference.setIconSpaceReserved(false); + return preference; + } + + private Preference getEditKeywordFilterPreference(@NonNull final Context context){ + final Preference preference = new Preference(context); + preference.setTitle(R.string.edit_keyword_filter); + preference.setIconSpaceReserved(false); + preference.setOnPreferenceClickListener(view ->{ + new KeywordsFilterDialog().show(getParentFragmentManager(), null); + return true; + }); + return preference; + } } diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index 64666798..969f5d6a 100644 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -11,12 +11,15 @@ public final class Constants { public static final String APP_THEME = "app_theme_v19"; public static final String APP_LANGUAGE = "app_language_v19"; public static final String STORY_SORT = "story_sort"; + // set string prefs + public static final String KEYWORD_FILTERS = "keyword_filters"; // int prefs, do not export public static final String PREV_INSTALL_VERSION = "prevVersion"; public static final String BROWSER_UA_CODE = "browser_ua_code"; public static final String APP_UA_CODE = "app_ua_code"; // boolean prefs public static final String DOWNLOAD_USER_FOLDER = "download_user_folder"; + public static final String TOGGLE_KEYWORD_FILTER = "toggle_keyword_filter"; // deprecated: public static final String BOTTOM_TOOLBAR = "bottom_toolbar"; public static final String FOLDER_SAVE_TO = "saved_to"; public static final String AUTOPLAY_VIDEOS = "autoplay_videos"; diff --git a/app/src/main/java/awais/instagrabber/utils/KeywordsFilterUtils.java b/app/src/main/java/awais/instagrabber/utils/KeywordsFilterUtils.java new file mode 100644 index 00000000..f4d42ac1 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/KeywordsFilterUtils.java @@ -0,0 +1,49 @@ +package awais.instagrabber.utils; + +import java.util.ArrayList; +import java.util.List; + +import awais.instagrabber.repositories.responses.Caption; +import awais.instagrabber.repositories.responses.Media; + +public final class KeywordsFilterUtils { + + private final ArrayList keywords; + + public KeywordsFilterUtils(final ArrayList keywords){ + this.keywords = keywords; + } + + public boolean filter(final String caption){ + if(caption == null) return false; + if(keywords.isEmpty()) return false; + final String temp = caption.toLowerCase(); + for(final String s:keywords){ + if(temp.contains(s)) return true; + } + return false; + } + + public boolean filter(final Media media){ + if(media == null) return false; + final Caption c = media.getCaption(); + if(c == null) return false; + if(keywords.isEmpty()) return false; + final String temp = c.getText().toLowerCase(); + for(final String s:keywords){ + if(temp.contains(s)) return true; + } + return false; + } + + public List filter(final List media){ + if(keywords.isEmpty()) return media; + if(media == null) return new ArrayList<>(); + + final List result= new ArrayList<>(); + for(final Media m:media){ + if(!filter(m)) result.add(m); + } + return result; + } +} diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index 7a5b8361..21047942 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -675,7 +675,8 @@ public final class ResponseBodyUtils { // return feedModelBuilder.build(); // } - public static Media parseGraphQLItem(final JSONObject itemJson) throws JSONException { + // the "user" argument can be null, it's used because instagram redacts user details from responses + public static Media parseGraphQLItem(final JSONObject itemJson, final User backup) throws JSONException { if (itemJson == null) { return null; } @@ -728,41 +729,28 @@ public final class ResponseBodyUtils { width = dimensions.optInt("width"); } String thumbnailUrl = null; - try { - thumbnailUrl = feedItem.getJSONArray("display_resources") - .getJSONObject(0) - .getString("src"); - } catch (JSONException ignored) {} - // final FeedModel.Builder feedModelBuilder = new FeedModel.Builder() - // .setProfileModel(profileModel) - // .setItemType(isVideo ? MediaItemType.MEDIA_TYPE_VIDEO - // : MediaItemType.MEDIA_TYPE_IMAGE) - // .setViewCount(videoViews) - // .setPostId(feedItem.getString(Constants.EXTRAS_ID)) - // .setDisplayUrl(resourceUrl) - // .setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl : displayUrl) - // .setShortCode(feedItem.getString(Constants.EXTRAS_SHORTCODE)) - // .setPostCaption(captionText) - // .setCommentsCount(commentsCount) - // .setTimestamp(feedItem.optLong("taken_at_timestamp", -1)) - // .setLiked(feedItem.optBoolean("viewer_has_liked")) - // .setBookmarked(feedItem.optBoolean("viewer_has_saved")) - // .setLikesCount(likesCount) - // .setLocationName(locationName) - // .setLocationId(String.valueOf(locationId)) - // .setImageHeight(height) - // .setImageWidth(width); + final JSONArray displayResources = feedItem.getJSONArray("display_resources"); + final List candidates = new ArrayList(); + for (int i = 0; i < displayResources.length(); i++) { + final JSONObject displayResource = displayResources.getJSONObject(i); + candidates.add(new MediaCandidate( + displayResource.getInt("config_width"), + displayResource.getInt("config_height"), + displayResource.getString("src") + )); + } + final ImageVersions2 imageVersions2 = new ImageVersions2(candidates); - User user = null; + User user = backup; long userId = -1; - if (feedItem.has("owner")) { + if (feedItem.has("owner") && user == null) { final JSONObject owner = feedItem.getJSONObject("owner"); final FriendshipStatus friendshipStatus = new FriendshipStatus( false, false, false, false, - owner.optBoolean("is_private"), + false, false, false, false, @@ -774,7 +762,7 @@ public final class ResponseBodyUtils { userId, owner.optString(Constants.EXTRAS_USERNAME), owner.optString("full_name"), - owner.optBoolean("is_private"), + false, owner.optString("profile_pic_url"), null, friendshipStatus, @@ -783,13 +771,6 @@ public final class ResponseBodyUtils { null, null, null, null); } final String id = feedItem.getString(Constants.EXTRAS_ID); - final ImageVersions2 imageVersions2 = new ImageVersions2( - Collections.singletonList(new MediaCandidate( - width, - height, - isVideo ? thumbnailUrl : resourceUrl - )) - ); VideoVersion videoVersion = null; if (isVideo) { videoVersion = new VideoVersion( @@ -821,7 +802,7 @@ public final class ResponseBodyUtils { for (int i = 0; i < children.length(); i++) { final JSONObject child = children.optJSONObject(i); if (child == null) continue; - final Media media = parseGraphQLItem(child); + final Media media = parseGraphQLItem(child, null); media.setIsSidecarChild(true); childItems.add(media); } diff --git a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java index d51acd4a..1ff49767 100755 --- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java +++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java @@ -8,6 +8,9 @@ import androidx.annotation.NonNull; import androidx.annotation.StringDef; import androidx.appcompat.app.AppCompatDelegate; +import java.util.HashSet; +import java.util.Set; + import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT; @@ -39,6 +42,7 @@ import static awais.instagrabber.utils.Constants.MUTED_VIDEOS; import static awais.instagrabber.utils.Constants.PREF_DARK_THEME; import static awais.instagrabber.utils.Constants.PREF_EMOJI_VARIANTS; import static awais.instagrabber.utils.Constants.PREF_HASHTAG_POSTS_LAYOUT; +import static awais.instagrabber.utils.Constants.KEYWORD_FILTERS; import static awais.instagrabber.utils.Constants.PREF_LIGHT_THEME; import static awais.instagrabber.utils.Constants.PREF_LIKED_POSTS_LAYOUT; import static awais.instagrabber.utils.Constants.PREF_LOCATION_POSTS_LAYOUT; @@ -54,6 +58,7 @@ import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG; import static awais.instagrabber.utils.Constants.SKIPPED_VERSION; import static awais.instagrabber.utils.Constants.STORY_SORT; import static awais.instagrabber.utils.Constants.SWAP_DATE_TIME_FORMAT_ENABLED; +import static awais.instagrabber.utils.Constants.TOGGLE_KEYWORD_FILTER; public final class SettingsHelper { private final SharedPreferences sharedPreferences; @@ -69,6 +74,12 @@ public final class SettingsHelper { return stringDefault; } + public Set getStringSet(@StringSetSettings final String key) { + final Set stringSetDefault = new HashSet<>(); + if (sharedPreferences != null) return sharedPreferences.getStringSet(key, stringSetDefault); + return stringSetDefault; + } + public int getInteger(@IntegerSettings final String key) { final int integerDefault = getIntegerDefault(key); if (sharedPreferences != null) return sharedPreferences.getInt(key, integerDefault); @@ -123,6 +134,10 @@ public final class SettingsHelper { if (sharedPreferences != null) sharedPreferences.edit().putString(key, val).apply(); } + public void putStringSet(@StringSetSettings final String key, final Set val) { + if (sharedPreferences != null) sharedPreferences.edit().putStringSet(key, val).apply(); + } + public void putInteger(@IntegerSettings final String key, final int val) { if (sharedPreferences != null) sharedPreferences.edit().putInt(key, val).apply(); } @@ -146,9 +161,12 @@ public final class SettingsHelper { @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY, CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH, - FLAG_SECURE, PREF_ENABLE_SENTRY}) + FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY}) public @interface BooleanSettings {} @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER}) public @interface IntegerSettings {} + + @StringDef({KEYWORD_FILTERS}) + public @interface StringSetSettings {} } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.java b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.java new file mode 100644 index 00000000..1fc510e2 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiCategoryDeserializer.java @@ -0,0 +1,52 @@ +package awais.instagrabber.utils.emoji; + +import android.util.Log; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; + +import awais.instagrabber.customviews.emoji.Emoji; +import awais.instagrabber.customviews.emoji.EmojiCategory; +import awais.instagrabber.customviews.emoji.EmojiCategoryType; + +public class EmojiCategoryDeserializer implements JsonDeserializer { + private static final String TAG = EmojiCategoryDeserializer.class.getSimpleName(); + + @Override + public EmojiCategory deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + final JsonObject jsonObject = json.getAsJsonObject(); + final JsonElement typeElement = jsonObject.get("type"); + final JsonObject emojisObject = jsonObject.getAsJsonObject("emojis"); + if (typeElement == null || emojisObject == null) { + throw new JsonParseException("Invalid json for EmojiCategory"); + } + final String typeString = typeElement.getAsString(); + EmojiCategoryType type; + try { + type = EmojiCategoryType.valueOf(typeString); + } catch (IllegalArgumentException e) { + Log.e(TAG, "deserialize: ", e); + type = EmojiCategoryType.OTHERS; + } + final Map emojis = new LinkedHashMap<>(); + for (final Map.Entry emojiObjectEntry : emojisObject.entrySet()) { + final String unicode = emojiObjectEntry.getKey(); + final JsonElement value = emojiObjectEntry.getValue(); + if (unicode == null || value == null) { + throw new JsonParseException("Invalid json for EmojiCategory"); + } + final Emoji emoji = context.deserialize(value, Emoji.class); + emojis.put(unicode, emoji); + } + return new EmojiCategory(type, emojis); + } +} diff --git a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.java b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.java new file mode 100644 index 00000000..cc774d60 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiDeserializer.java @@ -0,0 +1,44 @@ +package awais.instagrabber.utils.emoji; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.util.LinkedList; +import java.util.List; + +import awais.instagrabber.customviews.emoji.Emoji; + +public class EmojiDeserializer implements JsonDeserializer { + @Override + public Emoji deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + final JsonObject jsonObject = json.getAsJsonObject(); + final JsonElement unicodeElement = jsonObject.get("unicode"); + final JsonElement nameElement = jsonObject.get("name"); + if (unicodeElement == null || nameElement == null) { + throw new JsonParseException("Invalid json for Emoji class"); + } + final JsonElement variantsElement = jsonObject.get("variants"); + final List variants = new LinkedList<>(); + if (variantsElement != null) { + final JsonArray variantsArray = variantsElement.getAsJsonArray(); + for (final JsonElement variantElement : variantsArray) { + final Emoji variant = context.deserialize(variantElement, Emoji.class); + if (variant != null) { + variants.add(variant); + } + } + } + return new Emoji( + unicodeElement.getAsString(), + nameElement.getAsString(), + variants + ); + } +} diff --git a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java index 09a67225..17c8327c 100644 --- a/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java +++ b/app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java @@ -3,10 +3,11 @@ package awais.instagrabber.utils.emoji; import android.util.Log; import com.google.common.collect.ImmutableList; +import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; -import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.util.Collection; @@ -52,7 +53,12 @@ public final class EmojiParser { } try (final InputStream in = classLoader.getResourceAsStream(file)) { final String json = NetworkUtils.readFromInputStream(in); - final Gson gson = new Gson(); + final Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .registerTypeAdapter(EmojiCategory.class, new EmojiCategoryDeserializer()) + .registerTypeAdapter(Emoji.class, new EmojiDeserializer()) + .setLenient() + .create(); final Type type = new TypeToken>() {}.getType(); categoryMap = gson.fromJson(json, type); // Log.d(TAG, "EmojiParser: " + categoryMap); @@ -68,7 +74,7 @@ public final class EmojiParser { .build() .stream()) .collect(Collectors.toMap(Emoji::getUnicode, Function.identity())); - } catch (IOException e) { + } catch (Exception e) { Log.e(TAG, "EmojiParser: ", e); } } diff --git a/app/src/main/java/awais/instagrabber/viewmodels/AppStateViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/AppStateViewModel.java index c09e8e74..dcea12ed 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/AppStateViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/AppStateViewModel.java @@ -1,21 +1,18 @@ package awais.instagrabber.viewmodels; import android.app.Application; -import android.os.AsyncTask; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; -import awais.instagrabber.asyncs.ProfileFetcher; import awais.instagrabber.db.datasources.AccountDataSource; -import awais.instagrabber.db.entities.Account; import awais.instagrabber.db.repositories.AccountRepository; -import awais.instagrabber.db.repositories.RepositoryCallback; -import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.repositories.responses.User; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.TextUtils; +import awais.instagrabber.webservices.ServiceCallback; +import awais.instagrabber.webservices.UserService; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -27,6 +24,7 @@ public class AppStateViewModel extends AndroidViewModel { private User currentUser; private AccountRepository accountRepository; + private UserService userService; public AppStateViewModel(@NonNull final Application application) { super(application); @@ -34,6 +32,7 @@ public class AppStateViewModel extends AndroidViewModel { cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; if (!isLoggedIn) return; + userService = UserService.getInstance(); accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(application)); fetchProfileDetails(); } @@ -44,6 +43,14 @@ public class AppStateViewModel extends AndroidViewModel { private void fetchProfileDetails() { final long uid = CookieUtils.getUserIdFromCookie(cookie); - new ProfileFetcher(null, uid, true, user -> this.currentUser = user).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + userService.getUserInfo(uid, new ServiceCallback() { + @Override + public void onSuccess(final User user) { + currentUser = user; + } + + @Override + public void onFailure(final Throwable t) {} + }); } } diff --git a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java index 52e91af1..4cd1c1b6 100644 --- a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java +++ b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java @@ -56,6 +56,7 @@ public class GraphQLService extends BaseService { final String variables, final String arg1, final String arg2, + final User backup, final ServiceCallback callback) { final Map queryMap = new HashMap<>(); queryMap.put("query_hash", queryHash); @@ -66,7 +67,7 @@ public class GraphQLService extends BaseService { public void onResponse(@NonNull final Call call, @NonNull final Response response) { try { // Log.d(TAG, "onResponse: body: " + response.body()); - final PostsFetchResponse postsFetchResponse = parsePostResponse(response, arg1, arg2); + final PostsFetchResponse postsFetchResponse = parsePostResponse(response, arg1, arg2, backup); if (callback != null) { callback.onSuccess(postsFetchResponse); } @@ -96,6 +97,7 @@ public class GraphQLService extends BaseService { "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", Constants.EXTRAS_LOCATION, "edge_location_to_media", + null, callback); } @@ -108,12 +110,14 @@ public class GraphQLService extends BaseService { "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", Constants.EXTRAS_HASHTAG, "edge_hashtag_to_media", + null, callback); } public void fetchProfilePosts(final long profileId, final int postsPerPage, final String maxId, + final User backup, final ServiceCallback callback) { fetch("18a7b935ab438c4514b1f742d8fa07a7", "{\"id\":\"" + profileId + "\"," + @@ -121,6 +125,7 @@ public class GraphQLService extends BaseService { "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", Constants.EXTRAS_USER, "edge_owner_to_timeline_media", + backup, callback); } @@ -134,21 +139,28 @@ public class GraphQLService extends BaseService { "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", Constants.EXTRAS_USER, "edge_user_to_photos_of_you", + null, callback); } @NonNull - private PostsFetchResponse parsePostResponse(@NonNull final Response response, @NonNull final String arg1, @NonNull final String arg2) + private PostsFetchResponse parsePostResponse(@NonNull final Response response, + @NonNull final String arg1, + @NonNull final String arg2, + final User backup) throws JSONException { if (TextUtils.isEmpty(response.body())) { Log.e(TAG, "parseResponse: feed response body is empty with status code: " + response.code()); return new PostsFetchResponse(Collections.emptyList(), false, null); } - return parseResponseBody(response.body(), arg1, arg2); + return parseResponseBody(response.body(), arg1, arg2, backup); } @NonNull - private PostsFetchResponse parseResponseBody(@NonNull final String body, @NonNull final String arg1, @NonNull final String arg2) + private PostsFetchResponse parseResponseBody(@NonNull final String body, + @NonNull final String arg1, + @NonNull final String arg2, + final User backup) throws JSONException { final List items = new ArrayList<>(); final JSONObject timelineFeed = new JSONObject(body) @@ -174,7 +186,7 @@ public class GraphQLService extends BaseService { if (itemJson == null) { continue; } - final Media media = ResponseBodyUtils.parseGraphQLItem(itemJson); + final Media media = ResponseBodyUtils.parseGraphQLItem(itemJson, backup); if (media != null) { items.add(media); } diff --git a/app/src/main/java/awais/instagrabber/webservices/NewsService.java b/app/src/main/java/awais/instagrabber/webservices/NewsService.java index 6a51d8f1..fe54c13d 100644 --- a/app/src/main/java/awais/instagrabber/webservices/NewsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/NewsService.java @@ -121,9 +121,15 @@ public class NewsService extends BaseService { callback.onSuccess(null); return; } - final List aymlUsers = new ArrayList<>(); - aymlUsers.addAll(body.getNewSuggestedUsers().getSuggestions()); - aymlUsers.addAll(body.getSuggestedUsers().getSuggestions()); + final List aymlUsers = new ArrayList(); + final List newSuggestions = body.getNewSuggestedUsers().getSuggestions(); + if (newSuggestions != null) { + aymlUsers.addAll(newSuggestions); + } + final List oldSuggestions = body.getSuggestedUsers().getSuggestions(); + if (oldSuggestions != null) { + aymlUsers.addAll(oldSuggestions); + } final List newsItems = aymlUsers.stream() .map(i -> { diff --git a/app/src/main/res/layout/dialog_keywords_filter.xml b/app/src/main/res/layout/dialog_keywords_filter.xml new file mode 100644 index 00000000..507d3b54 --- /dev/null +++ b/app/src/main/res/layout/dialog_keywords_filter.xml @@ -0,0 +1,57 @@ + + + + + + + +