mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-30 19:15:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			481 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			481 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package awais.instagrabber.fragments;
 | |
| 
 | |
| import android.animation.ArgbEvaluator;
 | |
| import android.content.Context;
 | |
| import android.graphics.Color;
 | |
| import android.graphics.PorterDuff;
 | |
| import android.graphics.drawable.Animatable;
 | |
| import android.graphics.drawable.Drawable;
 | |
| import android.graphics.drawable.GradientDrawable;
 | |
| 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 android.widget.EditText;
 | |
| import android.widget.Toast;
 | |
| 
 | |
| import androidx.activity.OnBackPressedCallback;
 | |
| import androidx.activity.OnBackPressedDispatcher;
 | |
| import androidx.annotation.NonNull;
 | |
| import androidx.annotation.Nullable;
 | |
| import androidx.appcompat.app.AlertDialog;
 | |
| import androidx.coordinatorlayout.widget.CoordinatorLayout;
 | |
| import androidx.core.graphics.ColorUtils;
 | |
| import androidx.fragment.app.Fragment;
 | |
| import androidx.navigation.NavDirections;
 | |
| import androidx.navigation.fragment.NavHostFragment;
 | |
| import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 | |
| import androidx.transition.ChangeBounds;
 | |
| import androidx.transition.TransitionInflater;
 | |
| import androidx.transition.TransitionSet;
 | |
| 
 | |
| import com.facebook.drawee.backends.pipeline.Fresco;
 | |
| import com.facebook.drawee.controller.BaseControllerListener;
 | |
| import com.facebook.drawee.interfaces.DraweeController;
 | |
| import com.facebook.imagepipeline.image.ImageInfo;
 | |
| 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.SavedPostFetchService;
 | |
| import awais.instagrabber.customviews.PrimaryActionModeCallback;
 | |
| import awais.instagrabber.databinding.FragmentCollectionPostsBinding;
 | |
| import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
 | |
| import awais.instagrabber.models.PostsLayoutPreferences;
 | |
| import awais.instagrabber.models.enums.PostItemType;
 | |
| import awais.instagrabber.repositories.responses.Location;
 | |
| import awais.instagrabber.repositories.responses.Media;
 | |
| import awais.instagrabber.repositories.responses.User;
 | |
| import awais.instagrabber.repositories.responses.saved.SavedCollection;
 | |
| import awais.instagrabber.utils.AppExecutors;
 | |
| import awais.instagrabber.utils.Constants;
 | |
| import awais.instagrabber.utils.CookieUtils;
 | |
| import awais.instagrabber.utils.DownloadUtils;
 | |
| import awais.instagrabber.utils.ResponseBodyUtils;
 | |
| import awais.instagrabber.utils.Utils;
 | |
| import awais.instagrabber.webservices.CollectionService;
 | |
| import awais.instagrabber.webservices.ServiceCallback;
 | |
| 
 | |
| public class CollectionPostsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
 | |
|     private static final String TAG = "CollectionPostsFragment";
 | |
| 
 | |
|     private MainActivity fragmentActivity;
 | |
|     private FragmentCollectionPostsBinding binding;
 | |
|     private CoordinatorLayout root;
 | |
|     private boolean shouldRefresh = true;
 | |
|     private SavedCollection savedCollection;
 | |
|     private ActionMode actionMode;
 | |
|     private Set<Media> selectedFeedModels;
 | |
|     private CollectionService collectionService;
 | |
|     private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_SAVED_POSTS_LAYOUT);
 | |
| 
 | |
|     private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
 | |
|         @Override
 | |
|         public void handleOnBackPressed() {
 | |
|             binding.posts.endSelection();
 | |
|         }
 | |
|     };
 | |
|     private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
 | |
|             R.menu.saved_collection_select_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 (CollectionPostsFragment.this.selectedFeedModels == null) return false;
 | |
|                 final Context context = getContext();
 | |
|                 if (context == null) return false;
 | |
|                 DownloadUtils.download(context, ImmutableList.copyOf(CollectionPostsFragment.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 = CollectionPostsFragmentDirections.actionToComments(
 | |
|                         feedModel.getCode(),
 | |
|                         feedModel.getPk(),
 | |
|                         user.getPk()
 | |
|                 );
 | |
|                 NavHostFragment.findNavController(CollectionPostsFragment.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 = CollectionPostsFragmentDirections.actionToHashtag(hashtag);
 | |
|                 NavHostFragment.findNavController(CollectionPostsFragment.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 = CollectionPostsFragmentDirections.actionToLocation(location.getPk());
 | |
|                 NavHostFragment.findNavController(CollectionPostsFragment.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) {
 | |
|             final User user = feedModel.getUser();
 | |
|             if (user == null) return;
 | |
|             navigateToProfile("@" + user.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 = CollectionPostsFragmentDirections.actionToPost(feedModel, position);
 | |
|                 NavHostFragment.findNavController(CollectionPostsFragment.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<Media> selectedFeedModels) {
 | |
|             final String title = getString(R.string.number_selected, selectedFeedModels.size());
 | |
|             if (actionMode != null) {
 | |
|                 actionMode.setTitle(title);
 | |
|             }
 | |
|             CollectionPostsFragment.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) requireActivity();
 | |
|         final TransitionSet transitionSet = new TransitionSet();
 | |
|         final Context context = getContext();
 | |
|         if (context == null) return;
 | |
|         transitionSet.addTransition(new ChangeBounds())
 | |
|                      .addTransition(TransitionInflater.from(context).inflateTransition(android.R.transition.move))
 | |
|                      .setDuration(200);
 | |
|         setSharedElementEnterTransition(transitionSet);
 | |
|         postponeEnterTransition();
 | |
|         setHasOptionsMenu(true);
 | |
|         final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
 | |
|         final long userId = CookieUtils.getUserIdFromCookie(cookie);
 | |
|         final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID);
 | |
|         final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
 | |
|         collectionService = CollectionService.getInstance(deviceUuid, csrfToken, userId);
 | |
|     }
 | |
| 
 | |
|     @Nullable
 | |
|     @Override
 | |
|     public View onCreateView(@NonNull final LayoutInflater inflater,
 | |
|                              @Nullable final ViewGroup container,
 | |
|                              @Nullable final Bundle savedInstanceState) {
 | |
|         if (root != null) {
 | |
|             shouldRefresh = false;
 | |
|             return root;
 | |
|         }
 | |
|         binding = FragmentCollectionPostsBinding.inflate(inflater, 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.collection_posts_menu, menu);
 | |
|         final MenuItem deleteMenu = menu.findItem(R.id.delete);
 | |
|         if (deleteMenu != null)
 | |
|             deleteMenu.setVisible(savedCollection.getCollectionType().equals("MEDIA"));
 | |
|         final MenuItem editMenu = menu.findItem(R.id.edit);
 | |
|         if (editMenu != null)
 | |
|             editMenu.setVisible(savedCollection.getCollectionType().equals("MEDIA"));
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
 | |
|         if (item.getItemId() == R.id.layout) {
 | |
|             showPostsLayoutPreferences();
 | |
|             return true;
 | |
|         } else if (item.getItemId() == R.id.delete) {
 | |
|             final Context context = getContext();
 | |
|             if (context == null) return false;
 | |
|             new AlertDialog.Builder(context)
 | |
|                     .setTitle(R.string.are_you_sure)
 | |
|                     .setMessage(R.string.delete_collection_note)
 | |
|                     .setPositiveButton(R.string.confirm, (d, w) -> collectionService.deleteCollection(
 | |
|                             savedCollection.getCollectionId(),
 | |
|                             new ServiceCallback<String>() {
 | |
|                                 @Override
 | |
|                                 public void onSuccess(final String result) {
 | |
|                                     SavedCollectionsFragment.pleaseRefresh = true;
 | |
|                                     NavHostFragment.findNavController(CollectionPostsFragment.this).navigateUp();
 | |
|                                 }
 | |
| 
 | |
|                                 @Override
 | |
|                                 public void onFailure(final Throwable t) {
 | |
|                                     Log.e(TAG, "Error deleting collection", t);
 | |
|                                     try {
 | |
|                                         final Context context = getContext();
 | |
|                                         if (context == null) return;
 | |
|                                         Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
 | |
|                                     } catch (final Throwable ignored) {}
 | |
|                                 }
 | |
|                             }))
 | |
|                     .setNegativeButton(R.string.cancel, null)
 | |
|                     .show();
 | |
|         } else if (item.getItemId() == R.id.edit) {
 | |
|             final Context context = getContext();
 | |
|             if (context == null) return false;
 | |
|             final EditText input = new EditText(context);
 | |
|             new AlertDialog.Builder(context)
 | |
|                     .setTitle(R.string.edit_collection)
 | |
|                     .setView(input)
 | |
|                     .setPositiveButton(R.string.confirm, (d, w) -> collectionService.editCollectionName(
 | |
|                             savedCollection.getCollectionId(),
 | |
|                             input.getText().toString(),
 | |
|                             new ServiceCallback<String>() {
 | |
|                                 @Override
 | |
|                                 public void onSuccess(final String result) {
 | |
|                                     binding.collapsingToolbarLayout.setTitle(input.getText().toString());
 | |
|                                     SavedCollectionsFragment.pleaseRefresh = true;
 | |
|                                 }
 | |
| 
 | |
|                                 @Override
 | |
|                                 public void onFailure(final Throwable t) {
 | |
|                                     Log.e(TAG, "Error editing collection", t);
 | |
|                                     try {
 | |
|                                         final Context context = getContext();
 | |
|                                         if (context == null) return;
 | |
|                                         Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
 | |
|                                     } catch (final Throwable ignored) {}
 | |
|                                 }
 | |
|                             }))
 | |
|                     .setNegativeButton(R.string.cancel, null)
 | |
|                     .show();
 | |
|         }
 | |
|         return super.onOptionsItemSelected(item);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public void onResume() {
 | |
|         super.onResume();
 | |
|         fragmentActivity.setToolbar(binding.toolbar, this);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public void onRefresh() {
 | |
|         binding.posts.refresh();
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public void onStop() {
 | |
|         super.onStop();
 | |
|         fragmentActivity.resetToolbar(this);
 | |
|     }
 | |
| 
 | |
|     private void init() {
 | |
|         if (getArguments() == null) return;
 | |
|         final CollectionPostsFragmentArgs fragmentArgs = CollectionPostsFragmentArgs.fromBundle(getArguments());
 | |
|         savedCollection = fragmentArgs.getSavedCollection();
 | |
|         setupToolbar(fragmentArgs.getTitleColor(), fragmentArgs.getBackgroundColor());
 | |
|         setupPosts();
 | |
|     }
 | |
| 
 | |
|     private void setupToolbar(final int titleColor, final int backgroundColor) {
 | |
|         if (savedCollection == null) {
 | |
|             return;
 | |
|         }
 | |
|         binding.cover.setTransitionName("collection-" + savedCollection.getCollectionId());
 | |
|         fragmentActivity.setToolbar(binding.toolbar, this);
 | |
|         binding.collapsingToolbarLayout.setTitle(savedCollection.getCollectionName());
 | |
|         final int collapsedTitleTextColor = ColorUtils.setAlphaComponent(titleColor, 0xFF);
 | |
|         final int expandedTitleTextColor = ColorUtils.setAlphaComponent(titleColor, 0x99);
 | |
|         binding.collapsingToolbarLayout.setExpandedTitleColor(expandedTitleTextColor);
 | |
|         binding.collapsingToolbarLayout.setCollapsedTitleTextColor(collapsedTitleTextColor);
 | |
|         binding.collapsingToolbarLayout.setContentScrimColor(backgroundColor);
 | |
|         final Drawable navigationIcon = binding.toolbar.getNavigationIcon();
 | |
|         final Drawable overflowIcon = binding.toolbar.getOverflowIcon();
 | |
|         if (navigationIcon != null && overflowIcon != null) {
 | |
|             final Drawable navDrawable = navigationIcon.mutate();
 | |
|             final Drawable overflowDrawable = overflowIcon.mutate();
 | |
|             navDrawable.setAlpha(0xFF);
 | |
|             overflowDrawable.setAlpha(0xFF);
 | |
|             final ArgbEvaluator argbEvaluator = new ArgbEvaluator();
 | |
|             binding.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
 | |
|                 final int totalScrollRange = appBarLayout.getTotalScrollRange();
 | |
|                 final float current = totalScrollRange + verticalOffset;
 | |
|                 final float fraction = current / totalScrollRange;
 | |
|                 final int tempColor = (int) argbEvaluator.evaluate(fraction, collapsedTitleTextColor, expandedTitleTextColor);
 | |
|                 navDrawable.setColorFilter(tempColor, PorterDuff.Mode.SRC_ATOP);
 | |
|                 overflowDrawable.setColorFilter(tempColor, PorterDuff.Mode.SRC_ATOP);
 | |
| 
 | |
|             });
 | |
|         }
 | |
|         final GradientDrawable gd = new GradientDrawable(
 | |
|                 GradientDrawable.Orientation.TOP_BOTTOM,
 | |
|                 new int[]{Color.TRANSPARENT, backgroundColor});
 | |
|         binding.background.setBackground(gd);
 | |
|         setupCover();
 | |
|     }
 | |
| 
 | |
|     private void setupCover() {
 | |
|         final Media coverMedia = savedCollection.getCoverMediaList() == null
 | |
|                 ? savedCollection.getCoverMedia()
 | |
|                 : savedCollection.getCoverMediaList().get(0);
 | |
|         final String coverUrl = ResponseBodyUtils.getImageUrl(coverMedia);
 | |
|         final DraweeController controller = Fresco
 | |
|                 .newDraweeControllerBuilder()
 | |
|                 .setOldController(binding.cover.getController())
 | |
|                 .setUri(coverUrl)
 | |
|                 .setControllerListener(new BaseControllerListener<ImageInfo>() {
 | |
| 
 | |
|                     @Override
 | |
|                     public void onFailure(final String id, final Throwable throwable) {
 | |
|                         super.onFailure(id, throwable);
 | |
|                         startPostponedEnterTransition();
 | |
|                     }
 | |
| 
 | |
|                     @Override
 | |
|                     public void onFinalImageSet(final String id,
 | |
|                                                 @Nullable final ImageInfo imageInfo,
 | |
|                                                 @Nullable final Animatable animatable) {
 | |
|                         startPostponedEnterTransition();
 | |
|                     }
 | |
|                 })
 | |
|                 .build();
 | |
|         binding.cover.setController(controller);
 | |
|     }
 | |
| 
 | |
|     private void setupPosts() {
 | |
|         binding.posts.setViewModelStoreOwner(this)
 | |
|                      .setLifeCycleOwner(this)
 | |
|                      .setPostFetchService(new SavedPostFetchService(0, PostItemType.COLLECTION, true, savedCollection.getCollectionId()))
 | |
|                      .setLayoutPreferences(layoutPreferences)
 | |
|                      .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
 | |
|                      .setFeedItemCallback(feedItemCallback)
 | |
|                      .setSelectionModeCallback(selectionModeCallback)
 | |
|                      .init();
 | |
|     }
 | |
| 
 | |
|     private void updateSwipeRefreshState() {
 | |
|         AppExecutors.INSTANCE.getMainThread().execute(() ->
 | |
|             binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching())
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     private void navigateToProfile(final String username) {
 | |
|         try {
 | |
|             final NavDirections action = CollectionPostsFragmentDirections.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_SAVED_POSTS_LAYOUT,
 | |
|                 preferences -> {
 | |
|                     layoutPreferences = preferences;
 | |
|                     new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200);
 | |
|                 });
 | |
|         fragment.show(getChildFragmentManager(), "posts_layout_preferences");
 | |
|     }
 | |
| }
 |