mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-26 08:37:29 +00:00
viewing saved collections, so half of #545
This commit is contained in:
parent
6aacf1945f
commit
117b1c1629
@ -40,7 +40,7 @@ public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicCluste
|
||||
public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||
final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false);
|
||||
return new TopicClusterViewHolder(binding, onTopicClickListener);
|
||||
return new TopicClusterViewHolder(binding, onTopicClickListener, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,57 @@
|
||||
package awais.instagrabber.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
|
||||
import awais.instagrabber.adapters.viewholder.TopicClusterViewHolder;
|
||||
import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
|
||||
import awais.instagrabber.repositories.responses.saved.SavedCollection;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
|
||||
public class SavedCollectionsAdapter extends ListAdapter<SavedCollection, TopicClusterViewHolder> {
|
||||
private static final DiffUtil.ItemCallback<SavedCollection> DIFF_CALLBACK = new DiffUtil.ItemCallback<SavedCollection>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final SavedCollection oldItem, @NonNull final SavedCollection newItem) {
|
||||
return oldItem.getId().equals(newItem.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final SavedCollection oldItem, @NonNull final SavedCollection newItem) {
|
||||
if (oldItem.getCoverMedias().size() == newItem.getCoverMedias().size()) {
|
||||
if (oldItem.getCoverMedias().size() == 0) return true;
|
||||
return oldItem.getCoverMedias().get(0).getId().equals(newItem.getCoverMedias().get(0).getId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private final OnCollectionClickListener onCollectionClickListener;
|
||||
|
||||
public SavedCollectionsAdapter(final OnCollectionClickListener onCollectionClickListener) {
|
||||
super(DIFF_CALLBACK);
|
||||
this.onCollectionClickListener = onCollectionClickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public TopicClusterViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||
final ItemDiscoverTopicBinding binding = ItemDiscoverTopicBinding.inflate(layoutInflater, parent, false);
|
||||
return new TopicClusterViewHolder(binding, null, onCollectionClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final TopicClusterViewHolder holder, final int position) {
|
||||
final SavedCollection topicCluster = getItem(position);
|
||||
holder.bind(topicCluster);
|
||||
}
|
||||
|
||||
public interface OnCollectionClickListener {
|
||||
void onCollectionClick(SavedCollection savedCollection, View root, View cover, View title, int titleColor, int backgroundColor);
|
||||
}
|
||||
}
|
@ -25,19 +25,24 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.DiscoverTopicsAdapter;
|
||||
import awais.instagrabber.adapters.SavedCollectionsAdapter;
|
||||
import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
|
||||
import awais.instagrabber.repositories.responses.discover.TopicCluster;
|
||||
import awais.instagrabber.repositories.responses.saved.SavedCollection;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
|
||||
public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemDiscoverTopicBinding binding;
|
||||
private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener;
|
||||
private final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener;
|
||||
|
||||
public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding,
|
||||
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener) {
|
||||
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener,
|
||||
final SavedCollectionsAdapter.OnCollectionClickListener onCollectionClickListener) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.onTopicClickListener = onTopicClickListener;
|
||||
this.onCollectionClickListener = onCollectionClickListener;
|
||||
}
|
||||
|
||||
public void bind(final TopicCluster topicCluster) {
|
||||
@ -102,4 +107,69 @@ public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
|
||||
}
|
||||
binding.title.setText(topicCluster.getTitle());
|
||||
}
|
||||
|
||||
public void bind(final SavedCollection topicCluster) {
|
||||
if (topicCluster == null) {
|
||||
return;
|
||||
}
|
||||
final AtomicInteger titleColor = new AtomicInteger(-1);
|
||||
final AtomicInteger backgroundColor = new AtomicInteger(-1);
|
||||
if (onCollectionClickListener != null) {
|
||||
itemView.setOnClickListener(v -> onCollectionClickListener.onCollectionClick(
|
||||
topicCluster,
|
||||
binding.getRoot(),
|
||||
binding.cover,
|
||||
binding.title,
|
||||
titleColor.get(),
|
||||
backgroundColor.get()
|
||||
));
|
||||
}
|
||||
// binding.title.setTransitionName("title-" + topicCluster.getId());
|
||||
binding.cover.setTransitionName("cover-" + topicCluster.getId());
|
||||
final String thumbUrl = ResponseBodyUtils.getThumbUrl(topicCluster.getCoverMedias() == null
|
||||
? null
|
||||
: topicCluster.getCoverMedias().get(0));
|
||||
if (thumbUrl == null) {
|
||||
binding.cover.setImageURI((String) null);
|
||||
} else {
|
||||
final ImageRequest imageRequest = ImageRequestBuilder
|
||||
.newBuilderWithSource(Uri.parse(thumbUrl))
|
||||
.build();
|
||||
final ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||
final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline
|
||||
.fetchDecodedImage(imageRequest, CallerThreadExecutor.getInstance());
|
||||
dataSource.subscribe(new BaseBitmapDataSubscriber() {
|
||||
@Override
|
||||
public void onNewResultImpl(@Nullable Bitmap bitmap) {
|
||||
if (dataSource.isFinished()) {
|
||||
dataSource.close();
|
||||
}
|
||||
if (bitmap != null) {
|
||||
Palette.from(bitmap).generate(p -> {
|
||||
final Palette.Swatch swatch = p.getDominantSwatch();
|
||||
final Resources resources = itemView.getResources();
|
||||
int titleTextColor = resources.getColor(R.color.white);
|
||||
if (swatch != null) {
|
||||
backgroundColor.set(swatch.getRgb());
|
||||
GradientDrawable gd = new GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
new int[]{Color.TRANSPARENT, backgroundColor.get()});
|
||||
titleTextColor = swatch.getTitleTextColor();
|
||||
binding.background.setBackground(gd);
|
||||
}
|
||||
titleColor.set(titleTextColor);
|
||||
binding.title.setTextColor(titleTextColor);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailureImpl(@NonNull DataSource dataSource) {
|
||||
dataSource.close();
|
||||
}
|
||||
}, CallerThreadExecutor.getInstance());
|
||||
binding.cover.setImageRequest(imageRequest);
|
||||
}
|
||||
binding.title.setText(topicCluster.getTitle());
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,14 @@ public class SavedPostFetchService implements PostFetcher.PostFetchService {
|
||||
private final boolean isLoggedIn;
|
||||
|
||||
private String nextMaxId;
|
||||
private final String collectionId;
|
||||
private boolean moreAvailable;
|
||||
|
||||
public SavedPostFetchService(final long profileId, final PostItemType type, final boolean isLoggedIn) {
|
||||
public SavedPostFetchService(final long profileId, final PostItemType type, final boolean isLoggedIn, final String collectionId) {
|
||||
this.profileId = profileId;
|
||||
this.type = type;
|
||||
this.isLoggedIn = isLoggedIn;
|
||||
this.collectionId = collectionId;
|
||||
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
|
||||
profileService = isLoggedIn ? ProfileService.getInstance() : null;
|
||||
}
|
||||
@ -58,10 +60,12 @@ public class SavedPostFetchService implements PostFetcher.PostFetchService {
|
||||
if (isLoggedIn) profileService.fetchTagged(profileId, nextMaxId, callback);
|
||||
else graphQLService.fetchTaggedPosts(profileId, 30, nextMaxId, callback);
|
||||
break;
|
||||
case COLLECTION:
|
||||
case SAVED:
|
||||
default:
|
||||
profileService.fetchSaved(nextMaxId, callback);
|
||||
profileService.fetchSaved(nextMaxId, collectionId, callback);
|
||||
break;
|
||||
default:
|
||||
callback.onFailure(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,432 @@
|
||||
package awais.instagrabber.fragments;
|
||||
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
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.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.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.content.PermissionChecker;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.NavController;
|
||||
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.fragments.CollectionPostsFragmentDirections;
|
||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||
import awais.instagrabber.models.enums.PostItemType;
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
import awais.instagrabber.repositories.responses.saved.SavedCollection;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||
|
||||
public class CollectionPostsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||
private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030;
|
||||
|
||||
private MainActivity fragmentActivity;
|
||||
private FragmentCollectionPostsBinding binding;
|
||||
private CoordinatorLayout root;
|
||||
private boolean shouldRefresh = true;
|
||||
private SavedCollection savedCollection;
|
||||
private ActionMode actionMode;
|
||||
private Set<Media> selectedFeedModels;
|
||||
private Media downloadFeedModel;
|
||||
private int downloadChildPosition = -1;
|
||||
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.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 (CollectionPostsFragment.this.selectedFeedModels == null) return false;
|
||||
final Context context = getContext();
|
||||
if (context == null) return false;
|
||||
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||
DownloadUtils.download(context, ImmutableList.copyOf(CollectionPostsFragment.this.selectedFeedModels));
|
||||
binding.posts.endSelection();
|
||||
return true;
|
||||
}
|
||||
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE_FOR_SELECTION);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||
@Override
|
||||
public void onPostClick(final Media feedModel, final View profilePicView, final View mainPostImage) {
|
||||
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSliderClick(final Media feedModel, final int position) {
|
||||
openPostDialog(feedModel, null, null, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommentsClick(final Media feedModel) {
|
||||
final NavDirections commentsAction = CollectionPostsFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||
feedModel.getCode(),
|
||||
feedModel.getPk(),
|
||||
feedModel.getUser().getPk()
|
||||
);
|
||||
NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(commentsAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadClick(final Media feedModel, final int childPosition) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||
DownloadUtils.showDownloadDialog(context, feedModel, childPosition);
|
||||
return;
|
||||
}
|
||||
downloadFeedModel = feedModel;
|
||||
downloadChildPosition = -1;
|
||||
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHashtagClick(final String hashtag) {
|
||||
final NavDirections action = CollectionPostsFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||
NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationClick(final Media feedModel) {
|
||||
final NavDirections action = CollectionPostsFragmentDirections.actionGlobalLocationFragment(feedModel.getLocation().getPk());
|
||||
NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMentionClick(final String mention) {
|
||||
navigateToProfile(mention.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNameClick(final Media feedModel, final View profilePicView) {
|
||||
navigateToProfile("@" + feedModel.getUser().getUsername());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProfilePicClick(final Media feedModel, final View profilePicView) {
|
||||
navigateToProfile("@" + feedModel.getUser().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 View profilePicView,
|
||||
final View mainPostImage,
|
||||
final int position) {
|
||||
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||
.builder(feedModel);
|
||||
if (position >= 0) {
|
||||
builder.setPosition(position);
|
||||
}
|
||||
if (!layoutPreferences.isAnimationDisabled()) {
|
||||
builder.setSharedProfilePicElement(profilePicView)
|
||||
.setSharedMainPostElement(mainPostImage);
|
||||
}
|
||||
builder.build().show(getChildFragmentManager(), "post_view");
|
||||
}
|
||||
};
|
||||
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();
|
||||
transitionSet.addTransition(new ChangeBounds())
|
||||
.addTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move))
|
||||
.setDuration(200);
|
||||
setSharedElementEnterTransition(transitionSet);
|
||||
postponeEnterTransition();
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@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.topic_posts_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 onResume() {
|
||||
super.onResume();
|
||||
fragmentActivity.setToolbar(binding.toolbar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
binding.posts.refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
resetToolbar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
resetToolbar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
final boolean granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
if (requestCode == STORAGE_PERM_REQUEST_CODE && granted) {
|
||||
if (downloadFeedModel == null) return;
|
||||
DownloadUtils.showDownloadDialog(context, downloadFeedModel, downloadChildPosition);
|
||||
downloadFeedModel = null;
|
||||
downloadChildPosition = -1;
|
||||
return;
|
||||
}
|
||||
if (requestCode == STORAGE_PERM_REQUEST_CODE_FOR_SELECTION && granted) {
|
||||
DownloadUtils.download(context, ImmutableList.copyOf(selectedFeedModels));
|
||||
binding.posts.endSelection();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetToolbar() {
|
||||
fragmentActivity.resetToolbar();
|
||||
}
|
||||
|
||||
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.getId());
|
||||
fragmentActivity.setToolbar(binding.toolbar);
|
||||
binding.collapsingToolbarLayout.setTitle(savedCollection.getTitle());
|
||||
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 String coverUrl = ResponseBodyUtils.getImageUrl(savedCollection.getCoverMedias() == null
|
||||
? null
|
||||
: savedCollection.getCoverMedias().get(0));
|
||||
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.getId()))
|
||||
.setLayoutPreferences(layoutPreferences)
|
||||
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||
.setFeedItemCallback(feedItemCallback)
|
||||
.setSelectionModeCallback(selectionModeCallback)
|
||||
.init();
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
}
|
||||
|
||||
private void updateSwipeRefreshState() {
|
||||
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching());
|
||||
}
|
||||
|
||||
private void navigateToProfile(final String username) {
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("username", username);
|
||||
navController.navigate(R.id.action_global_profileFragment, bundle);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
package awais.instagrabber.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
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.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.fragment.FragmentNavigator;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.activities.MainActivity;
|
||||
import awais.instagrabber.adapters.SavedCollectionsAdapter;
|
||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
||||
import awais.instagrabber.databinding.FragmentSavedCollectionsBinding;
|
||||
import awais.instagrabber.repositories.responses.StoryStickerResponse;
|
||||
import awais.instagrabber.repositories.responses.saved.CollectionsListResponse;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.SavedCollectionsViewModel;
|
||||
import awais.instagrabber.webservices.ProfileService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class SavedCollectionsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "SavedCollectionsFragment";
|
||||
|
||||
private MainActivity fragmentActivity;
|
||||
private CoordinatorLayout root;
|
||||
private FragmentSavedCollectionsBinding binding;
|
||||
private SavedCollectionsViewModel savedCollectionsViewModel;
|
||||
private boolean shouldRefresh = true;
|
||||
private ProfileService profileService;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
profileService = ProfileService.getInstance();
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
if (root != null) {
|
||||
shouldRefresh = false;
|
||||
return root;
|
||||
}
|
||||
binding = FragmentSavedCollectionsBinding.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.saved_collection_menu, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
if (item.getItemId() == R.id.add) {
|
||||
final Context context = getContext();
|
||||
final EditText input = new EditText(context);
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.saved_create_collection)
|
||||
.setView(input)
|
||||
.setPositiveButton(R.string.confirm, (d, w) -> {
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
profileService.createCollection(
|
||||
input.getText().toString(),
|
||||
settingsHelper.getString(Constants.DEVICE_UUID),
|
||||
CookieUtils.getUserIdFromCookie(cookie),
|
||||
CookieUtils.getCsrfTokenFromCookie(cookie),
|
||||
new ServiceCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(final String result) {
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "Error creating collection", t);
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setupTopics();
|
||||
fetchTopics(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
fetchTopics(null);
|
||||
}
|
||||
|
||||
public void setupTopics() {
|
||||
savedCollectionsViewModel = new ViewModelProvider(fragmentActivity).get(SavedCollectionsViewModel.class);
|
||||
binding.topicsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(2)));
|
||||
final SavedCollectionsAdapter adapter = new SavedCollectionsAdapter((topicCluster, root, cover, title, titleColor, backgroundColor) -> {
|
||||
final FragmentNavigator.Extras.Builder builder = new FragmentNavigator.Extras.Builder()
|
||||
.addSharedElement(cover, "collection-" + topicCluster.getId());
|
||||
final SavedCollectionsFragmentDirections.ActionSavedCollectionsFragmentToCollectionPostsFragment action = SavedCollectionsFragmentDirections
|
||||
.actionSavedCollectionsFragmentToCollectionPostsFragment(topicCluster, titleColor, backgroundColor);
|
||||
NavHostFragment.findNavController(this).navigate(action, builder.build());
|
||||
});
|
||||
binding.topicsRecyclerView.setAdapter(adapter);
|
||||
savedCollectionsViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
|
||||
}
|
||||
|
||||
private void fetchTopics(final String maxId) {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
profileService.fetchCollections(maxId, new ServiceCallback<CollectionsListResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final CollectionsListResponse result) {
|
||||
if (result == null) return;
|
||||
savedCollectionsViewModel.getList().postValue(result.getItems());
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "onFailure", t);
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -286,7 +286,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
|
||||
private void setupPosts() {
|
||||
binding.posts.setViewModelStoreOwner(this)
|
||||
.setLifeCycleOwner(this)
|
||||
.setPostFetchService(new SavedPostFetchService(profileId, type, isLoggedIn))
|
||||
.setPostFetchService(new SavedPostFetchService(profileId, type, isLoggedIn, null))
|
||||
.setLayoutPreferences(layoutPreferences)
|
||||
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||
.setFeedItemCallback(feedItemCallback)
|
||||
|
@ -946,9 +946,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}
|
||||
});
|
||||
profileDetailsBinding.btnSaved.setOnClickListener(v -> {
|
||||
final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment(profileModel.getUsername(),
|
||||
profileModel.getPk(),
|
||||
PostItemType.SAVED);
|
||||
final NavDirections action = ProfileFragmentDirections.actionGlobalSavedCollectionsFragment();
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
});
|
||||
profileDetailsBinding.btnLiked.setOnClickListener(v -> {
|
||||
|
@ -7,6 +7,7 @@ public enum PostItemType implements Serializable {
|
||||
DISCOVER,
|
||||
FEED,
|
||||
SAVED,
|
||||
COLLECTION,
|
||||
LIKED,
|
||||
TAGGED,
|
||||
HASHTAG,
|
||||
|
@ -2,10 +2,14 @@ package awais.instagrabber.repositories;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import awais.instagrabber.repositories.responses.saved.CollectionsListResponse;
|
||||
import awais.instagrabber.repositories.responses.UserFeedResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.FieldMap;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.QueryMap;
|
||||
|
||||
public interface ProfileRepository {
|
||||
@ -16,9 +20,20 @@ public interface ProfileRepository {
|
||||
@GET("/api/v1/feed/saved/")
|
||||
Call<UserFeedResponse> fetchSaved(@QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET("/api/v1/feed/collection/{collectionId}/")
|
||||
Call<UserFeedResponse> fetchSavedCollection(@Path("collectionId") final String collectionId,
|
||||
@QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET("/api/v1/feed/liked/")
|
||||
Call<UserFeedResponse> fetchLiked(@QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET("/api/v1/usertags/{profileId}/feed/")
|
||||
Call<UserFeedResponse> fetchTagged(@Path("profileId") final long profileId, @QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET("/api/v1/collections/list/")
|
||||
Call<CollectionsListResponse> fetchCollections(@QueryMap Map<String, String> queryParams);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/api/v1/collections/create/")
|
||||
Call<String> createCollection(@FieldMap Map<String, String> signedForm);
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package awais.instagrabber.repositories.responses.saved;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CollectionsListResponse {
|
||||
private final boolean moreAvailable;
|
||||
private final String nextMaxId;
|
||||
private final String maxId;
|
||||
private final String status;
|
||||
// private final int numResults;
|
||||
private final List<SavedCollection> items;
|
||||
|
||||
public CollectionsListResponse(final boolean moreAvailable,
|
||||
final String nextMaxId,
|
||||
final String maxId,
|
||||
final String status,
|
||||
// final int numResults,
|
||||
final List<SavedCollection> items) {
|
||||
this.moreAvailable = moreAvailable;
|
||||
this.nextMaxId = nextMaxId;
|
||||
this.maxId = maxId;
|
||||
this.status = status;
|
||||
// this.numResults = numResults;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public boolean isMoreAvailable() {
|
||||
return moreAvailable;
|
||||
}
|
||||
|
||||
public String getNextMaxId() {
|
||||
return nextMaxId;
|
||||
}
|
||||
|
||||
public String getMaxId() {
|
||||
return maxId;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
// public int getNumResults() {
|
||||
// return numResults;
|
||||
// }
|
||||
|
||||
public List<SavedCollection> getItems() {
|
||||
return items;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package awais.instagrabber.repositories.responses.saved;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
|
||||
public class SavedCollection implements Serializable {
|
||||
private final String collectionId;
|
||||
private final String collectionName;
|
||||
private final String collectionType;
|
||||
private final int collectionMediacount;
|
||||
private final List<Media> coverMediaList;
|
||||
|
||||
public SavedCollection(final String collectionId,
|
||||
final String collectionName,
|
||||
final String collectionType,
|
||||
final int collectionMediacount,
|
||||
final List<Media> coverMediaList) {
|
||||
this.collectionId = collectionId;
|
||||
this.collectionName = collectionName;
|
||||
this.collectionType = collectionType;
|
||||
this.collectionMediacount = collectionMediacount;
|
||||
this.coverMediaList = coverMediaList;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return collectionId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return collectionName;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return collectionType;
|
||||
}
|
||||
|
||||
public int getMediaCount() {
|
||||
return collectionMediacount;
|
||||
}
|
||||
|
||||
public List<Media> getCoverMedias() {
|
||||
return coverMediaList;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package awais.instagrabber.viewmodels;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.repositories.responses.saved.SavedCollection;
|
||||
|
||||
public class SavedCollectionsViewModel extends ViewModel {
|
||||
private MutableLiveData<List<SavedCollection>> list;
|
||||
|
||||
public MutableLiveData<List<SavedCollection>> getList() {
|
||||
if (list == null) {
|
||||
list = new MutableLiveData<>();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
@ -4,10 +4,15 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import awais.instagrabber.repositories.ProfileRepository;
|
||||
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||
import awais.instagrabber.repositories.responses.UserFeedResponse;
|
||||
import awais.instagrabber.repositories.responses.saved.CollectionsListResponse;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
@ -68,12 +73,15 @@ public class ProfileService extends BaseService {
|
||||
}
|
||||
|
||||
public void fetchSaved(final String maxId,
|
||||
final String collectionId,
|
||||
final ServiceCallback<PostsFetchResponse> callback) {
|
||||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
Call<UserFeedResponse> request = null;
|
||||
if (!TextUtils.isEmpty(maxId)) {
|
||||
builder.put("max_id", maxId);
|
||||
}
|
||||
final Call<UserFeedResponse> request = repository.fetchSaved(builder.build());
|
||||
if (TextUtils.isEmpty(collectionId) || collectionId.equals("ALL_MEDIA_AUTO_COLLECTION")) request = repository.fetchSaved(builder.build());
|
||||
else request = repository.fetchSavedCollection(collectionId, builder.build());
|
||||
request.enqueue(new Callback<UserFeedResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<UserFeedResponse> call, @NonNull final Response<UserFeedResponse> response) {
|
||||
@ -99,6 +107,71 @@ public class ProfileService extends BaseService {
|
||||
});
|
||||
}
|
||||
|
||||
public void fetchCollections(final String maxId,
|
||||
final ServiceCallback<CollectionsListResponse> callback) {
|
||||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
if (!TextUtils.isEmpty(maxId)) {
|
||||
builder.put("max_id", maxId);
|
||||
}
|
||||
builder.put("collection_types", "[\"ALL_MEDIA_AUTO_COLLECTION\",\"MEDIA\",\"PRODUCT_AUTO_COLLECTION\"]");
|
||||
final Call<CollectionsListResponse> request = repository.fetchCollections(builder.build());
|
||||
request.enqueue(new Callback<CollectionsListResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<CollectionsListResponse> call, @NonNull final Response<CollectionsListResponse> response) {
|
||||
if (callback == null) return;
|
||||
final CollectionsListResponse collectionsListResponse = response.body();
|
||||
if (collectionsListResponse == null) {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
callback.onSuccess(collectionsListResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<CollectionsListResponse> call, @NonNull final Throwable t) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void createCollection(final String name,
|
||||
final String deviceUuid,
|
||||
final long userId,
|
||||
final String csrfToken,
|
||||
final ServiceCallback<String> callback) {
|
||||
final Map<String, Object> form = new HashMap<>(6);
|
||||
form.put("_csrftoken", csrfToken);
|
||||
form.put("_uuid", deviceUuid);
|
||||
form.put("_uid", userId);
|
||||
form.put("collection_visibility", "0"); // 1 for public, planned for future but currently inexistant
|
||||
form.put("module_name", "collection_create");
|
||||
form.put("name", name);
|
||||
final Map<String, String> signedForm = Utils.sign(form);
|
||||
final Call<String> request = repository.createCollection(signedForm);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
if (callback == null) return;
|
||||
final String collectionsListResponse = response.body();
|
||||
if (collectionsListResponse == null) {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
callback.onSuccess(collectionsListResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
if (callback != null) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void fetchLiked(final String maxId,
|
||||
final ServiceCallback<PostsFetchResponse> callback) {
|
||||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
|
58
app/src/main/res/layout/fragment_collection_posts.xml
Normal file
58
app/src/main/res/layout/fragment_collection_posts.xml
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:id="@+id/collapsing_toolbar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_collapseMode="parallax">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/cover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
app:actualImageScaleType="centerCrop"
|
||||
tools:background="@mipmap/ic_launcher" />
|
||||
|
||||
<View
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_collapseMode="pin" />
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipe_refresh_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<awais.instagrabber.customviews.PostsRecyclerView
|
||||
android:id="@+id/posts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
24
app/src/main/res/layout/fragment_saved_collections.xml
Normal file
24
app/src/main/res/layout/fragment_saved_collections.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipe_refresh_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/topics_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2"
|
||||
tools:itemCount="10"
|
||||
tools:listitem="@layout/item_discover_topic" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
10
app/src/main/res/menu/saved_collection_menu.xml
Normal file
10
app/src/main/res/menu/saved_collection_menu.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/add"
|
||||
android:icon="@drawable/ic_add"
|
||||
android:title="@string/saved_create_collection"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
@ -82,6 +82,12 @@
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/saved_nav_graph" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_savedCollectionsFragment"
|
||||
app:destination="@id/saved_nav_graph" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/profileFragment"
|
||||
android:name="awais.instagrabber.fragments.main.ProfileFragment"
|
||||
|
101
app/src/main/res/navigation/saved_nav_graph.xml
Normal file
101
app/src/main/res/navigation/saved_nav_graph.xml
Normal file
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/saved_nav_graph"
|
||||
app:startDestination="@id/savedCollectionsFragment">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_hashTagFragment"
|
||||
app:destination="@id/hashtag_nav_graph">
|
||||
<argument
|
||||
android:name="hashtag"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_profileFragment"
|
||||
app:destination="@id/profile_nav_graph">
|
||||
<argument
|
||||
android:name="username"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
</action>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_locationFragment"
|
||||
app:destination="@id/location_nav_graph">
|
||||
<argument
|
||||
android:name="locationId"
|
||||
app:argType="long" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/comments_nav_graph" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_commentsViewerFragment"
|
||||
app:destination="@id/comments_nav_graph">
|
||||
<argument
|
||||
android:name="shortCode"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="postId"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="postUserId"
|
||||
app:argType="long" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/likes_nav_graph" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_likesViewerFragment"
|
||||
app:destination="@id/likes_nav_graph">
|
||||
<argument
|
||||
android:name="postId"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="isComment"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_notificationsViewerFragment"
|
||||
app:destination="@id/notification_viewer_nav_graph">
|
||||
<argument
|
||||
android:name="type"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/savedCollectionsFragment"
|
||||
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
|
||||
android:label="@string/saved"
|
||||
tools:layout="@layout/fragment_saved_collections" >
|
||||
<action
|
||||
android:id="@+id/action_savedCollectionsFragment_to_collectionPostsFragment"
|
||||
app:destination="@id/collectionPostsFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/collectionPostsFragment"
|
||||
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
|
||||
tools:layout="@layout/fragment_collection_posts">
|
||||
<argument
|
||||
android:name="savedCollection"
|
||||
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
|
||||
|
||||
<argument
|
||||
android:name="titleColor"
|
||||
app:argType="integer" />
|
||||
|
||||
<argument
|
||||
android:name="backgroundColor"
|
||||
app:argType="integer" />
|
||||
</fragment>
|
||||
</navigation>
|
@ -97,6 +97,7 @@
|
||||
<string name="remove_all_acc">Remove all accounts</string>
|
||||
<string name="remove_all_acc_warning">This will remove all added accounts from the app!\nTo remove just one account, long tap the account from the account switcher dialog.\nDo you want to continue?</string>
|
||||
<string name="time_settings">Date format</string>
|
||||
<string name="saved_create_collection">Create new collection</string>
|
||||
<string name="liked">Liked</string>
|
||||
<string name="saved">Saved</string>
|
||||
<string name="tagged">Tagged</string>
|
||||
|
Loading…
Reference in New Issue
Block a user