package awais.instagrabber.fragments.main; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import androidx.activity.OnBackPressedCallback; import androidx.activity.OnBackPressedDispatcher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.navigation.NavDirections; import androidx.navigation.fragment.NavHostFragment; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.google.common.collect.ImmutableList; import java.util.Set; import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.asyncs.DiscoverPostFetchService; import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.databinding.FragmentDiscoverBinding; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.User; import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; import static awais.instagrabber.utils.Utils.settingsHelper; public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "DiscoverFragment"; private MainActivity fragmentActivity; private SwipeRefreshLayout root; private FragmentDiscoverBinding binding; private ActionMode actionMode; private boolean isLoggedIn, shouldRefresh = true; private String keyword; private Set selectedFeedModels; private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_TOPIC_POSTS_LAYOUT); private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) { @Override public void handleOnBackPressed() { binding.posts.endSelection(); } }; private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback( R.menu.multi_select_download_menu, new PrimaryActionModeCallback.CallbacksHelper() { @Override public void onDestroy(final ActionMode mode) { binding.posts.endSelection(); } @Override public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { if (item.getItemId() == R.id.action_download) { if (selectedFeedModels == null) return false; final Context context = getContext(); if (context == null) return false; DownloadUtils.download(context, ImmutableList.copyOf(DiscoverFragment.this.selectedFeedModels)); binding.posts.endSelection(); } return false; } }); private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() { @Override public void onPostClick(final Media feedModel) { openPostDialog(feedModel, -1); } @Override public void onSliderClick(final Media feedModel, final int position) { openPostDialog(feedModel, position); } @Override public void onCommentsClick(final Media feedModel) { final User user = feedModel.getUser(); if (user == null) return; try { final NavDirections commentsAction = ProfileFragmentDirections.actionToComments( feedModel.getCode(), feedModel.getPk(), user.getPk() ); NavHostFragment.findNavController(DiscoverFragment.this).navigate(commentsAction); } catch (Exception e) { Log.e(TAG, "onCommentsClick: ", e); } } @Override public void onDownloadClick(final Media feedModel, final int childPosition, final View popupLocation) { final Context context = getContext(); if (context == null) return; DownloadUtils.showDownloadDialog(context, feedModel, childPosition, popupLocation); } @Override public void onHashtagClick(final String hashtag) { try { final NavDirections action = ProfileFragmentDirections.actionToHashtag(hashtag); NavHostFragment.findNavController(DiscoverFragment.this).navigate(action); } catch (Exception e) { Log.e(TAG, "onHashtagClick: ", e); } } @Override public void onLocationClick(final Media feedModel) { final Location location = feedModel.getLocation(); if (location == null) return; try { final NavDirections action = ProfileFragmentDirections.actionToLocation(location.getPk()); NavHostFragment.findNavController(DiscoverFragment.this).navigate(action); } catch (Exception e) { Log.e(TAG, "onLocationClick: ", e); } } @Override public void onMentionClick(final String mention) { navigateToProfile(mention.trim()); } @Override public void onNameClick(final Media feedModel) { navigateToProfile("@" + feedModel.getUser().getUsername()); } @Override public void onProfilePicClick(final Media feedModel) { final User user = feedModel.getUser(); if (user == null) return; navigateToProfile("@" + user.getUsername()); } @Override public void onURLClick(final String url) { Utils.openURL(getContext(), url); } @Override public void onEmailClick(final String emailId) { Utils.openEmailAddress(getContext(), emailId); } private void openPostDialog(final Media feedModel, final int position) { try { final NavDirections action = DiscoverFragmentDirections.actionToPost(feedModel, position); NavHostFragment.findNavController(DiscoverFragment.this).navigate(action); } catch (Exception e) { Log.e(TAG, "openPostDialog: ", e); } } }; private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() { @Override public void onSelectionStart() { if (!onBackPressedCallback.isEnabled()) { final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher(); onBackPressedCallback.setEnabled(true); onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback); } if (actionMode == null) { actionMode = fragmentActivity.startActionMode(multiSelectAction); } } @Override public void onSelectionChange(final Set selectedFeedModels) { final String title = getString(R.string.number_selected, selectedFeedModels.size()); if (actionMode != null) { actionMode.setTitle(title); } DiscoverFragment.this.selectedFeedModels = selectedFeedModels; } @Override public void onSelectionEnd() { if (onBackPressedCallback.isEnabled()) { onBackPressedCallback.setEnabled(false); onBackPressedCallback.remove(); } if (actionMode != null) { actionMode.finish(); actionMode = null; } } }; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentActivity = (MainActivity) getActivity(); setHasOptionsMenu(true); } @Override public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { final String cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; if (root != null) { shouldRefresh = false; return root; } binding = FragmentDiscoverBinding.inflate(getLayoutInflater(), container, false); root = binding.getRoot(); return root; } @Override public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { if (!shouldRefresh) return; binding.swipeRefreshLayout.setOnRefreshListener(this); init(); shouldRefresh = false; } @Override public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { inflater.inflate(R.menu.saved_viewer_menu, menu); } @Override public boolean onOptionsItemSelected(@NonNull final MenuItem item) { if (item.getItemId() == R.id.layout) { showPostsLayoutPreferences(); return true; } return super.onOptionsItemSelected(item); } @Override public void onRefresh() { binding.posts.refresh(); } private void init() { final Bundle arguments = getArguments(); if (arguments == null) return; final DiscoverFragmentArgs fragmentArgs = DiscoverFragmentArgs.fromBundle(arguments); keyword = fragmentArgs.getKeyword(); setupPosts(); } private void setupPosts() { binding.posts.setViewModelStoreOwner(this) .setLifeCycleOwner(this) .setPostFetchService(new DiscoverPostFetchService()) .setLayoutPreferences(layoutPreferences) .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState()) .setFeedItemCallback(feedItemCallback) .setSelectionModeCallback(selectionModeCallback) .init(); binding.swipeRefreshLayout.setRefreshing(true); } private void updateSwipeRefreshState() { AppExecutors.INSTANCE.getMainThread().execute(() -> binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching()) ); } private void navigateToProfile(final String username) { try { final NavDirections action = DiscoverFragmentDirections.actionToProfile().setUsername(username); NavHostFragment.findNavController(this).navigate(action); } catch (Exception e) { Log.e(TAG, "navigateToProfile: ", e); } } private void showPostsLayoutPreferences() { final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment( Constants.PREF_TOPIC_POSTS_LAYOUT, preferences -> { layoutPreferences = preferences; new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200); }); fragment.show(getChildFragmentManager(), "posts_layout_preferences"); } }