mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-26 00:27:30 +00:00
Update Discover tab
This commit is contained in:
parent
0a67e859e0
commit
efd9a9c29d
@ -49,7 +49,7 @@ dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
|
||||
|
||||
def appcompat_version = "1.2.0"
|
||||
def nav_version = "2.3.0"
|
||||
def nav_version = '2.3.1'
|
||||
|
||||
implementation 'com.google.android.material:material:1.3.0-alpha03'
|
||||
implementation 'com.google.android.exoplayer:exoplayer:2.12.0'
|
||||
@ -61,9 +61,10 @@ dependencies {
|
||||
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
||||
implementation "androidx.navigation:navigation-fragment:$nav_version"
|
||||
implementation "androidx.navigation:navigation-ui:$nav_version"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.2"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
implementation "androidx.work:work-runtime:2.4.0"
|
||||
implementation 'androidx.palette:palette:1.0.0'
|
||||
|
||||
implementation 'com.google.guava:guava:27.0.1-android'
|
||||
|
||||
|
@ -421,7 +421,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
R.id.main_nav_host,
|
||||
getIntent(),
|
||||
firstFragmentGraphIndex);
|
||||
navControllerLiveData.observe(this, this::setupNavigation);
|
||||
navControllerLiveData.observe(this, navController -> setupNavigation(binding.toolbar, navController));
|
||||
currentNavControllerLiveData = navControllerLiveData;
|
||||
}
|
||||
|
||||
@ -446,8 +446,11 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
return mainNavList;
|
||||
}
|
||||
|
||||
private void setupNavigation(final NavController navController) {
|
||||
NavigationUI.setupWithNavController(binding.toolbar, navController);
|
||||
private void setupNavigation(final Toolbar toolbar, final NavController navController) {
|
||||
if (navController == null) {
|
||||
return;
|
||||
}
|
||||
NavigationUI.setupWithNavController(toolbar, navController);
|
||||
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
|
||||
// below is a hack to check if we are at the end of the current stack, to setup the search view
|
||||
binding.appBarLayout.setExpanded(true, true);
|
||||
@ -640,4 +643,16 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
public int getNavHostContainerId() {
|
||||
return binding.mainNavHost.getId();
|
||||
}
|
||||
|
||||
public void setToolbar(final Toolbar toolbar) {
|
||||
binding.appBarLayout.setVisibility(View.GONE);
|
||||
setSupportActionBar(toolbar);
|
||||
setupNavigation(toolbar, currentNavControllerLiveData.getValue());
|
||||
}
|
||||
|
||||
public void resetToolbar() {
|
||||
binding.appBarLayout.setVisibility(View.VISIBLE);
|
||||
setSupportActionBar(binding.toolbar);
|
||||
setupNavigation(binding.toolbar, currentNavControllerLiveData.getValue());
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
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.models.TopicCluster;
|
||||
|
||||
public class DiscoverTopicsAdapter extends ListAdapter<TopicCluster, TopicClusterViewHolder> {
|
||||
private static final DiffUtil.ItemCallback<TopicCluster> DIFF_CALLBACK = new DiffUtil.ItemCallback<TopicCluster>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {
|
||||
return oldItem.getId().equals(newItem.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final TopicCluster oldItem, @NonNull final TopicCluster newItem) {
|
||||
return oldItem.getCoverMedia().getDisplayUrl().equals(newItem.getCoverMedia().getDisplayUrl())
|
||||
&& oldItem.getTitle().equals(newItem.getTitle());
|
||||
}
|
||||
};
|
||||
|
||||
private final OnTopicClickListener onTopicClickListener;
|
||||
|
||||
public DiscoverTopicsAdapter(final OnTopicClickListener onTopicClickListener) {
|
||||
super(DIFF_CALLBACK);
|
||||
this.onTopicClickListener = onTopicClickListener;
|
||||
}
|
||||
|
||||
@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, onTopicClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final TopicClusterViewHolder holder, final int position) {
|
||||
final TopicCluster topicCluster = getItem(position);
|
||||
holder.bind(topicCluster);
|
||||
}
|
||||
|
||||
public interface OnTopicClickListener {
|
||||
void onTopicClick(TopicCluster topicCluster, View root, View cover, View title, int titleColor, int backgroundColor);
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package awais.instagrabber.adapters.viewholder;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.palette.graphics.Palette;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.facebook.common.executors.CallerThreadExecutor;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.datasource.DataSource;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
||||
import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.DiscoverTopicsAdapter;
|
||||
import awais.instagrabber.databinding.ItemDiscoverTopicBinding;
|
||||
import awais.instagrabber.models.TopicCluster;
|
||||
|
||||
public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemDiscoverTopicBinding binding;
|
||||
private final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener;
|
||||
|
||||
public TopicClusterViewHolder(@NonNull final ItemDiscoverTopicBinding binding,
|
||||
final DiscoverTopicsAdapter.OnTopicClickListener onTopicClickListener) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.onTopicClickListener = onTopicClickListener;
|
||||
}
|
||||
|
||||
public void bind(final TopicCluster topicCluster) {
|
||||
if (topicCluster == null) {
|
||||
return;
|
||||
}
|
||||
final AtomicInteger titleColor = new AtomicInteger(-1);
|
||||
final AtomicInteger backgroundColor = new AtomicInteger(-1);
|
||||
if (onTopicClickListener != null) {
|
||||
itemView.setOnClickListener(v -> onTopicClickListener.onTopicClick(
|
||||
topicCluster,
|
||||
binding.getRoot(),
|
||||
binding.cover,
|
||||
binding.title,
|
||||
titleColor.get(),
|
||||
backgroundColor.get()
|
||||
));
|
||||
}
|
||||
// binding.title.setTransitionName("title-" + topicCluster.getId());
|
||||
binding.cover.setTransitionName("cover-" + topicCluster.getId());
|
||||
final ImageRequest imageRequest = ImageRequestBuilder
|
||||
.newBuilderWithSource(Uri.parse(topicCluster.getCoverMedia().getDisplayUrl()))
|
||||
.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());
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package awais.instagrabber.asyncs;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.webservices.DiscoverService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
public class DiscoverPostFetchService implements PostFetcher.PostFetchService {
|
||||
private static final String TAG = "DiscoverPostFetchService";
|
||||
private final DiscoverService discoverService;
|
||||
private final DiscoverService.TopicalExploreRequest topicalExploreRequest;
|
||||
private boolean moreAvailable = false;
|
||||
|
||||
public DiscoverPostFetchService(final DiscoverService.TopicalExploreRequest topicalExploreRequest) {
|
||||
this.topicalExploreRequest = topicalExploreRequest;
|
||||
discoverService = DiscoverService.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
discoverService.topicalExplore(topicalExploreRequest, new ServiceCallback<DiscoverService.TopicalExploreResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final DiscoverService.TopicalExploreResponse result) {
|
||||
if (result == null) {
|
||||
onFailure(new RuntimeException("result is null"));
|
||||
return;
|
||||
}
|
||||
moreAvailable = result.isMoreAvailable();
|
||||
topicalExploreRequest.setMaxId(result.getNextMaxId());
|
||||
if (fetchListener != null) {
|
||||
fetchListener.onResult(result.getItems());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
if (fetchListener != null) {
|
||||
fetchListener.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
topicalExploreRequest.setMaxId(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextPage() {
|
||||
return moreAvailable;
|
||||
}
|
||||
}
|
@ -20,8 +20,8 @@ public class FeedPostFetchService implements PostFetcher.PostFetchService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final String cursor, final FetchListener<List<FeedModel>> fetchListener) {
|
||||
feedService.fetch(25, cursor, new ServiceCallback<PostsFetchResponse>() {
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
feedService.fetch(25, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final PostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
@ -43,8 +43,8 @@ public class FeedPostFetchService implements PostFetcher.PostFetchService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNextCursor() {
|
||||
return nextCursor;
|
||||
public void reset() {
|
||||
nextCursor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,8 +23,8 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final String cursor, final FetchListener<List<FeedModel>> fetchListener) {
|
||||
profileService.fetchPosts(profileModel, 30, cursor, new ServiceCallback<PostsFetchResponse>() {
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
profileService.fetchPosts(profileModel, 30, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final PostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
@ -46,8 +46,8 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNextCursor() {
|
||||
return nextCursor;
|
||||
public void reset() {
|
||||
nextCursor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -167,7 +167,7 @@ public class PostsRecyclerView extends RecyclerView {
|
||||
setNestedScrollingEnabled(true);
|
||||
lazyLoader = new RecyclerLazyLoaderAtBottom(layoutManager, (page) -> {
|
||||
if (postFetcher.hasMore()) {
|
||||
postFetcher.fetchNextPage();
|
||||
postFetcher.fetch();
|
||||
dispatchFetchStatus();
|
||||
}
|
||||
});
|
||||
@ -204,6 +204,7 @@ public class PostsRecyclerView extends RecyclerView {
|
||||
|
||||
public void refresh() {
|
||||
lazyLoader.resetState();
|
||||
postFetcher.reset();
|
||||
postFetcher.fetch();
|
||||
dispatchFetchStatus();
|
||||
}
|
||||
|
@ -17,21 +17,17 @@ public class PostFetcher {
|
||||
}
|
||||
|
||||
public void fetch() {
|
||||
fetch(null);
|
||||
}
|
||||
|
||||
public void fetchNextPage() {
|
||||
fetch(postFetchService.getNextCursor());
|
||||
}
|
||||
|
||||
public void fetch(final String cursor) {
|
||||
fetching = true;
|
||||
postFetchService.fetch(cursor, result -> {
|
||||
postFetchService.fetch(result -> {
|
||||
fetching = false;
|
||||
fetchListener.onResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
postFetchService.reset();
|
||||
}
|
||||
|
||||
public boolean isFetching() {
|
||||
return fetching;
|
||||
}
|
||||
@ -41,9 +37,9 @@ public class PostFetcher {
|
||||
}
|
||||
|
||||
public interface PostFetchService {
|
||||
void fetch(String cursor, FetchListener<List<FeedModel>> fetchListener);
|
||||
void fetch(FetchListener<List<FeedModel>> fetchListener);
|
||||
|
||||
String getNextCursor();
|
||||
void reset();
|
||||
|
||||
boolean hasNextPage();
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public final class RecyclerLazyLoaderAtBottom extends RecyclerView.OnScrollListe
|
||||
if (!recyclerView.canScrollVertically(RecyclerView.SCROLL_AXIS_HORIZONTAL) && newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
if (!loading && lazyLoadListener != null) {
|
||||
loading = true;
|
||||
new Handler().postDelayed(() -> lazyLoadListener.onLoadMore(++currentPage), 1000);
|
||||
new Handler().postDelayed(() -> lazyLoadListener.onLoadMore(++currentPage), 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
@ -209,7 +208,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
||||
final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments());
|
||||
hashtag = fragmentArgs.getHashtag();
|
||||
setTitle();
|
||||
// setTitle();
|
||||
setupPosts();
|
||||
fetchHashtagModel();
|
||||
}
|
||||
@ -284,6 +283,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
setTitle();
|
||||
fetchPosts();
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
@ -324,6 +324,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
binding.btnFollowTag.setOnClickListener(v -> {
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
if (csrfToken != null) {
|
||||
binding.btnFollowTag.setClickable(false);
|
||||
if (!hashtagModel.getFollowing()) {
|
||||
tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
||||
@ -374,6 +375,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
binding.btnFollowTag.setVisibility(View.GONE);
|
||||
@ -425,7 +427,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
final NavDirections action = HashTagFragmentDirections
|
||||
.actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -445,9 +446,10 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
private void setTitle() {
|
||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
Log.d(TAG, "setting title: " + hashtag);
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(() -> actionBar.setTitle(hashtag), 200);
|
||||
// Log.d(TAG, "setting title: " + hashtag);
|
||||
actionBar.setTitle(hashtag);
|
||||
// final Handler handler = new Handler();
|
||||
// handler.postDelayed(() -> , 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -920,7 +920,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
binding.playerControls.getRoot().setVisibility(View.GONE);
|
||||
binding.sliderParent.setVisibility(View.VISIBLE);
|
||||
binding.mediaCounter.setVisibility(View.VISIBLE);
|
||||
if (sharedMainPostElement != null) {
|
||||
if (!wasPaused && sharedMainPostElement != null) {
|
||||
addSharedElement(sharedMainPostElement, binding.sliderParent);
|
||||
}
|
||||
sliderItemsAdapter = new SliderItemsAdapter(onVerticalDragListener, binding.playerControls, true, new SliderCallbackAdapter() {
|
||||
@ -1014,7 +1014,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
||||
binding.sliderParent.setVisibility(View.GONE);
|
||||
binding.mediaCounter.setVisibility(View.GONE);
|
||||
// binding.playerControls.getRoot().setVisibility(View.VISIBLE);
|
||||
if (sharedMainPostElement != null) {
|
||||
if (!wasPaused && sharedMainPostElement != null) {
|
||||
final GenericDraweeHierarchy hierarchy = binding.videoPost.thumbnail.getHierarchy();
|
||||
hierarchy.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP);
|
||||
addSharedElement(sharedMainPostElement, binding.videoPost.thumbnailParent);
|
||||
|
@ -0,0 +1,363 @@
|
||||
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.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
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 awais.instagrabber.R;
|
||||
import awais.instagrabber.activities.MainActivity;
|
||||
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||
import awais.instagrabber.asyncs.DiscoverPostFetchService;
|
||||
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
||||
import awais.instagrabber.databinding.FragmentTopicPostsBinding;
|
||||
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||
import awais.instagrabber.fragments.main.DiscoverFragmentDirections;
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||
import awais.instagrabber.models.TopicCluster;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.webservices.DiscoverService;
|
||||
|
||||
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||
private MainActivity fragmentActivity;
|
||||
private FragmentTopicPostsBinding binding;
|
||||
private NestedCoordinatorLayout root;
|
||||
private boolean shouldRefresh = true;
|
||||
private TopicCluster topicCluster;
|
||||
|
||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||
@Override
|
||||
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
||||
openPostDialog(feedModel, profilePicView, mainPostImage, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSliderClick(final FeedModel feedModel, final int position) {
|
||||
openPostDialog(feedModel, null, null, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommentsClick(final FeedModel feedModel) {
|
||||
final NavDirections commentsAction = DiscoverFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||
feedModel.getShortCode(),
|
||||
feedModel.getPostId(),
|
||||
feedModel.getProfileModel().getId()
|
||||
);
|
||||
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(commentsAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadClick(final FeedModel feedModel) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
if (checkSelfPermission(context, WRITE_PERMISSION) == PermissionChecker.PERMISSION_GRANTED) {
|
||||
showDownloadDialog(feedModel);
|
||||
return;
|
||||
}
|
||||
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHashtagClick(final String hashtag) {
|
||||
final NavDirections action = DiscoverFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationClick(final FeedModel feedModel) {
|
||||
final NavDirections action = DiscoverFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
|
||||
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMentionClick(final String mention) {
|
||||
navigateToProfile(mention.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNameClick(final FeedModel feedModel, final View profilePicView) {
|
||||
navigateToProfile("@" + feedModel.getProfileModel().getUsername());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProfilePicClick(final FeedModel feedModel, final View profilePicView) {
|
||||
navigateToProfile("@" + feedModel.getProfileModel().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 FeedModel feedModel,
|
||||
final View profilePicView,
|
||||
final View mainPostImage,
|
||||
final int position) {
|
||||
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||
.builder(feedModel);
|
||||
if (position >= 0) {
|
||||
builder.setPosition(position);
|
||||
}
|
||||
final PostViewV2Fragment fragment = builder
|
||||
.setSharedProfilePicElement(profilePicView)
|
||||
.setSharedMainPostElement(mainPostImage)
|
||||
.build();
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
}
|
||||
};
|
||||
|
||||
@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 = FragmentTopicPostsBinding.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 onRefresh() {
|
||||
binding.posts.refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
resetToolbar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
resetToolbar();
|
||||
}
|
||||
|
||||
private void resetToolbar() {
|
||||
fragmentActivity.resetToolbar();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (getArguments() == null) return;
|
||||
final TopicPostsFragmentArgs fragmentArgs = TopicPostsFragmentArgs.fromBundle(getArguments());
|
||||
topicCluster = fragmentArgs.getTopicCluster();
|
||||
setupToolbar(fragmentArgs.getTitleColor(), fragmentArgs.getBackgroundColor());
|
||||
setupPosts();
|
||||
}
|
||||
|
||||
private void setupToolbar(final int titleColor, final int backgroundColor) {
|
||||
if (topicCluster == null) {
|
||||
return;
|
||||
}
|
||||
binding.cover.setTransitionName("cover-" + topicCluster.getId());
|
||||
fragmentActivity.setToolbar(binding.toolbar);
|
||||
binding.collapsingToolbarLayout.setTitle(topicCluster.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 = topicCluster.getCoverMedia().getDisplayUrl();
|
||||
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() {
|
||||
final DiscoverService.TopicalExploreRequest topicalExploreRequest = new DiscoverService.TopicalExploreRequest();
|
||||
topicalExploreRequest.setClusterId(topicCluster.getId());
|
||||
binding.posts.setViewModelStoreOwner(this)
|
||||
.setLifeCycleOwner(this)
|
||||
.setPostFetchService(new DiscoverPostFetchService(topicalExploreRequest))
|
||||
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_TOPIC_POSTS_LAYOUT)))
|
||||
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||
.setFeedItemCallback(feedItemCallback)
|
||||
.init();
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
}
|
||||
|
||||
private void updateSwipeRefreshState() {
|
||||
binding.swipeRefreshLayout.setRefreshing(binding.posts.isFetching());
|
||||
}
|
||||
|
||||
private void showDownloadDialog(final FeedModel feedModel) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
DownloadUtils.download(context, feedModel);
|
||||
// switch (feedModel.getItemType()) {
|
||||
// case MEDIA_TYPE_IMAGE:
|
||||
// case MEDIA_TYPE_VIDEO:
|
||||
// break;
|
||||
// case MEDIA_TYPE_SLIDER:
|
||||
// break;
|
||||
// }
|
||||
// final List<ViewerPostModel> postModelsToDownload = new ArrayList<>();
|
||||
// // if (!session) {
|
||||
// final DialogInterface.OnClickListener clickListener = (dialog, which) -> {
|
||||
// if (which == DialogInterface.BUTTON_NEGATIVE) {
|
||||
// postModelsToDownload.addAll(postModels);
|
||||
// } else if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
// postModelsToDownload.add(postModels.get(childPosition));
|
||||
// } else {
|
||||
// session = true;
|
||||
// postModelsToDownload.add(postModels.get(childPosition));
|
||||
// }
|
||||
// if (postModelsToDownload.size() > 0) {
|
||||
// DownloadUtils.batchDownload(context,
|
||||
// username,
|
||||
// DownloadMethod.DOWNLOAD_POST_VIEWER,
|
||||
// postModelsToDownload);
|
||||
// }
|
||||
// };
|
||||
// new AlertDialog.Builder(context)
|
||||
// .setTitle(R.string.post_viewer_download_dialog_title)
|
||||
// .setMessage(R.string.post_viewer_download_message)
|
||||
// .setNeutralButton(R.string.post_viewer_download_session, clickListener)
|
||||
// .setPositiveButton(R.string.post_viewer_download_current, clickListener)
|
||||
// .setNegativeButton(R.string.post_viewer_download_album, clickListener).show();
|
||||
// } else {
|
||||
// DownloadUtils.batchDownload(context,
|
||||
// username,
|
||||
// DownloadMethod.DOWNLOAD_POST_VIEWER,
|
||||
// Collections.singletonList(postModels.get(childPosition)));
|
||||
}
|
||||
|
||||
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 -> new Handler().postDelayed(() -> binding.posts.setLayoutPreferences(preferences), 200));
|
||||
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||
}
|
||||
}
|
@ -1,126 +1,84 @@
|
||||
package awais.instagrabber.fragments.main;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ActionMode;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.activity.OnBackPressedDispatcher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.FragmentNavigator;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.activities.MainActivity;
|
||||
import awais.instagrabber.adapters.DiscoverAdapter;
|
||||
import awais.instagrabber.asyncs.DiscoverFetcher;
|
||||
import awais.instagrabber.asyncs.i.iTopicFetcher;
|
||||
import awais.instagrabber.adapters.DiscoverTopicsAdapter;
|
||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
||||
import awais.instagrabber.databinding.FragmentDiscoverBinding;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.DiscoverItemModel;
|
||||
import awais.instagrabber.models.DiscoverTopicModel;
|
||||
import awais.instagrabber.models.enums.DownloadMethod;
|
||||
import awais.instagrabber.utils.DownloadUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.DiscoverItemViewModel;
|
||||
import awais.instagrabber.viewmodels.TopicClusterViewModel;
|
||||
import awais.instagrabber.webservices.DiscoverService;
|
||||
import awais.instagrabber.webservices.ServiceCallback;
|
||||
|
||||
public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||
private static final String TAG = "DiscoverFragment";
|
||||
|
||||
private MainActivity fragmentActivity;
|
||||
private CoordinatorLayout root;
|
||||
private FragmentDiscoverBinding binding;
|
||||
private DiscoverAdapter discoverAdapter;
|
||||
private RecyclerLazyLoader lazyLoader;
|
||||
private boolean discoverHasMore = false;
|
||||
private String[] topicIds;
|
||||
private String rankToken;
|
||||
private String currentTopic;
|
||||
private String discoverEndMaxId;
|
||||
private ActionMode actionMode;
|
||||
private DiscoverItemViewModel discoverItemViewModel;
|
||||
private TopicClusterViewModel topicClusterViewModel;
|
||||
private boolean shouldRefresh = true;
|
||||
private boolean isPullToRefresh;
|
||||
private DiscoverService discoverService;
|
||||
|
||||
private final FetchListener<DiscoverTopicModel> topicFetchListener = new FetchListener<DiscoverTopicModel>() {
|
||||
@Override
|
||||
public void doBefore() {}
|
||||
|
||||
@Override
|
||||
public void onResult(final DiscoverTopicModel result) {
|
||||
if (result != null) {
|
||||
topicIds = result.getIds();
|
||||
rankToken = result.getToken();
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(
|
||||
context,
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
result.getNames()
|
||||
);
|
||||
binding.discoverType.setAdapter(spinnerArrayAdapter);
|
||||
}
|
||||
}
|
||||
};
|
||||
private final FetchListener<DiscoverItemModel[]> postsFetchListener = new FetchListener<DiscoverItemModel[]>() {
|
||||
@Override
|
||||
public void doBefore() {}
|
||||
|
||||
@Override
|
||||
public void onResult(final DiscoverItemModel[] result) {
|
||||
if (result == null || result.length <= 0) {
|
||||
binding.discoverSwipeRefreshLayout.setRefreshing(false);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
Toast.makeText(context, R.string.discover_empty, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
List<DiscoverItemModel> current = discoverItemViewModel.getList().getValue();
|
||||
final List<DiscoverItemModel> resultList = Arrays.asList(result);
|
||||
current = current == null ? new ArrayList<>() : new ArrayList<>(current); // copy to modifiable list
|
||||
if (isPullToRefresh) {
|
||||
current = resultList;
|
||||
isPullToRefresh = false;
|
||||
} else {
|
||||
current.addAll(resultList);
|
||||
}
|
||||
discoverItemViewModel.getList().postValue(current);
|
||||
binding.discoverSwipeRefreshLayout.setRefreshing(false);
|
||||
final DiscoverItemModel discoverItemModel = result[result.length - 1];
|
||||
if (discoverItemModel != null) {
|
||||
discoverEndMaxId = discoverItemModel.getNextMaxId();
|
||||
discoverHasMore = discoverItemModel.hasMore();
|
||||
discoverItemModel.setMore(false, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
// private final FetchListener<DiscoverItemModel[]> postsFetchListener = new FetchListener<DiscoverItemModel[]>() {
|
||||
// @Override
|
||||
// public void doBefore() {}
|
||||
//
|
||||
// @Override
|
||||
// public void onResult(final DiscoverItemModel[] result) {
|
||||
// if (result == null || result.length <= 0) {
|
||||
// binding.swipeRefreshLayout.setRefreshing(false);
|
||||
// final Context context = getContext();
|
||||
// if (context == null) return;
|
||||
// Toast.makeText(context, R.string.discover_empty, Toast.LENGTH_SHORT).show();
|
||||
// return;
|
||||
// }
|
||||
// List<DiscoverItemModel> current = discoverItemViewModel.getList().getValue();
|
||||
// final List<DiscoverItemModel> resultList = Arrays.asList(result);
|
||||
// current = current == null ? new ArrayList<>() : new ArrayList<>(current); // copy to modifiable list
|
||||
// if (isPullToRefresh) {
|
||||
// current = resultList;
|
||||
// isPullToRefresh = false;
|
||||
// } else {
|
||||
// current.addAll(resultList);
|
||||
// }
|
||||
// discoverItemViewModel.getList().postValue(current);
|
||||
// binding.swipeRefreshLayout.setRefreshing(false);
|
||||
// final DiscoverItemModel discoverItemModel = result[result.length - 1];
|
||||
// if (discoverItemModel != null) {
|
||||
// discoverEndMaxId = discoverItemModel.getNextMaxId();
|
||||
// discoverHasMore = discoverItemModel.hasMore();
|
||||
// discoverItemModel.setMore(false, null);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
setEnabled(false);
|
||||
remove();
|
||||
if (discoverAdapter == null) return;
|
||||
discoverAdapter.clearSelection();
|
||||
// if (discoverAdapter == null) return;
|
||||
// discoverAdapter.clearSelection();
|
||||
}
|
||||
};
|
||||
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||
@ -134,14 +92,14 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
@Override
|
||||
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_download) {
|
||||
if (discoverAdapter == null) return false;
|
||||
final Context context = getContext();
|
||||
if (context == null) return false;
|
||||
DownloadUtils.batchDownload(context,
|
||||
null,
|
||||
DownloadMethod.DOWNLOAD_DISCOVER,
|
||||
discoverAdapter.getSelectedModels());
|
||||
checkAndResetAction();
|
||||
// if (discoverAdapter == null) return false;
|
||||
// final Context context = getContext();
|
||||
// if (context == null) return false;
|
||||
// DownloadUtils.batchDownload(context,
|
||||
// null,
|
||||
// DownloadMethod.DOWNLOAD_DISCOVER,
|
||||
// discoverAdapter.getSelectedModels());
|
||||
// checkAndResetAction();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -152,6 +110,7 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
discoverService = DiscoverService.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -170,96 +129,159 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
if (!shouldRefresh) return;
|
||||
binding.discoverSwipeRefreshLayout.setOnRefreshListener(this);
|
||||
setupExplore();
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||
init();
|
||||
shouldRefresh = false;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// setExitSharedElementCallback(new SharedElementCallback() {
|
||||
// @Override
|
||||
// public void onSharedElementsArrived(final List<String> sharedElementNames,
|
||||
// final List<View> sharedElements,
|
||||
// final OnSharedElementsReadyListener listener) {
|
||||
// super.onSharedElementsArrived(sharedElementNames, sharedElements, listener);
|
||||
// Log.d(TAG, "onSharedElementsArrived: sharedElementNames: " + sharedElementNames);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onSharedElementEnd(final List<String> sharedElementNames,
|
||||
// final List<View> sharedElements,
|
||||
// final List<View> sharedElementSnapshots) {
|
||||
// super.onSharedElementEnd(sharedElementNames, sharedElements, sharedElementSnapshots);
|
||||
// Log.d(TAG, "onSharedElementEnd: sharedElementNames: " + sharedElementNames);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onSharedElementStart(final List<String> sharedElementNames,
|
||||
// final List<View> sharedElements,
|
||||
// final List<View> sharedElementSnapshots) {
|
||||
// super.onSharedElementStart(sharedElementNames, sharedElements, sharedElementSnapshots);
|
||||
// Log.d(TAG, "onSharedElementStart: sharedElementNames: " + sharedElementNames);
|
||||
// }
|
||||
// });
|
||||
setupTopics();
|
||||
fetchTopics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
isPullToRefresh = true;
|
||||
discoverEndMaxId = null;
|
||||
lazyLoader.resetState();
|
||||
fetchPosts();
|
||||
}
|
||||
|
||||
private void setupExplore() {
|
||||
discoverItemViewModel = new ViewModelProvider(fragmentActivity).get(DiscoverItemViewModel.class);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
||||
binding.discoverPosts.setLayoutManager(layoutManager);
|
||||
binding.discoverPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
||||
binding.discoverType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
if (topicIds == null || topicIds.length <= 0) return;
|
||||
currentTopic = topicIds[pos];
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
discoverAdapter = new DiscoverAdapter((model, position) -> {
|
||||
if (discoverAdapter.isSelecting()) {
|
||||
if (actionMode == null) return;
|
||||
final String title = getString(R.string.number_selected, discoverAdapter.getSelectedModels().size());
|
||||
actionMode.setTitle(title);
|
||||
return;
|
||||
}
|
||||
if (checkAndResetAction()) return;
|
||||
final List<DiscoverItemModel> discoverItemModels = discoverItemViewModel.getList().getValue();
|
||||
if (discoverItemModels == null || discoverItemModels.size() == 0) return;
|
||||
if (discoverItemModels.get(0) == null) return;
|
||||
final String postId = discoverItemModels.get(0).getPostId();
|
||||
final boolean isId = postId != null;
|
||||
final String[] idsOrShortCodes = new String[discoverItemModels.size()];
|
||||
for (int i = 0; i < discoverItemModels.size(); i++) {
|
||||
idsOrShortCodes[i] = isId ? discoverItemModels.get(i).getPostId()
|
||||
: discoverItemModels.get(i).getShortCode();
|
||||
}
|
||||
final NavDirections action = DiscoverFragmentDirections.actionGlobalPostViewFragment(
|
||||
position,
|
||||
idsOrShortCodes,
|
||||
isId);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
}, (model, position) -> {
|
||||
if (!discoverAdapter.isSelecting()) {
|
||||
checkAndResetAction();
|
||||
return true;
|
||||
}
|
||||
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||
if (onBackPressedCallback.isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||
final String title = getString(R.string.number_selected, 1);
|
||||
actionMode.setTitle(title);
|
||||
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||
return true;
|
||||
});
|
||||
binding.discoverPosts.setAdapter(discoverAdapter);
|
||||
discoverItemViewModel.getList().observe(fragmentActivity, discoverAdapter::submitList);
|
||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
||||
if (discoverHasMore) {
|
||||
fetchPosts();
|
||||
}
|
||||
}, 3);
|
||||
binding.discoverPosts.addOnScrollListener(lazyLoader);
|
||||
fetchTopics();
|
||||
}
|
||||
|
||||
private void fetchTopics() {
|
||||
new iTopicFetcher(topicFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
public void setupTopics() {
|
||||
topicClusterViewModel = new ViewModelProvider(fragmentActivity).get(TopicClusterViewModel.class);
|
||||
binding.topicsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(2)));
|
||||
final DiscoverTopicsAdapter adapter = new DiscoverTopicsAdapter((topicCluster, root, cover, title, titleColor, backgroundColor) -> {
|
||||
final FragmentNavigator.Extras.Builder builder = new FragmentNavigator.Extras.Builder()
|
||||
.addSharedElement(cover, "cover-" + topicCluster.getId());
|
||||
// .addSharedElement(title, "title-" + topicCluster.getId());
|
||||
final DiscoverFragmentDirections.ActionDiscoverFragmentToTopicPostsFragment action = DiscoverFragmentDirections
|
||||
.actionDiscoverFragmentToTopicPostsFragment(topicCluster, titleColor, backgroundColor);
|
||||
NavHostFragment.findNavController(this).navigate(action, builder.build());
|
||||
});
|
||||
binding.topicsRecyclerView.setAdapter(adapter);
|
||||
topicClusterViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
|
||||
}
|
||||
|
||||
private void fetchPosts() {
|
||||
binding.discoverSwipeRefreshLayout.setRefreshing(true);
|
||||
new DiscoverFetcher(currentTopic, discoverEndMaxId, rankToken, postsFetchListener, false)
|
||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
private void setupExplore() {
|
||||
// discoverItemViewModel = new ViewModelProvider(fragmentActivity).get(DiscoverItemViewModel.class);
|
||||
// final Context context = getContext();
|
||||
// if (context == null) return;
|
||||
// final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
||||
// binding.postsRecyclerView.setLayoutManager(layoutManager);
|
||||
// binding.postsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
||||
// binding.discoverType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
// @Override
|
||||
// public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
// if (topicIds == null || topicIds.length <= 0) return;
|
||||
// currentTopic = topicIds[pos];
|
||||
// onRefresh();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onNothingSelected(AdapterView<?> parent) {}
|
||||
// });
|
||||
// discoverAdapter = new DiscoverAdapter((model, position) -> {
|
||||
// if (discoverAdapter.isSelecting()) {
|
||||
// if (actionMode == null) return;
|
||||
// final String title = getString(R.string.number_selected, discoverAdapter.getSelectedModels().size());
|
||||
// actionMode.setTitle(title);
|
||||
// return;
|
||||
// }
|
||||
// if (checkAndResetAction()) return;
|
||||
// final List<DiscoverItemModel> discoverItemModels = discoverItemViewModel.getList().getValue();
|
||||
// if (discoverItemModels == null || discoverItemModels.size() == 0) return;
|
||||
// if (discoverItemModels.get(0) == null) return;
|
||||
// final String postId = discoverItemModels.get(0).getPostId();
|
||||
// final boolean isId = postId != null;
|
||||
// final String[] idsOrShortCodes = new String[discoverItemModels.size()];
|
||||
// for (int i = 0; i < discoverItemModels.size(); i++) {
|
||||
// idsOrShortCodes[i] = isId ? discoverItemModels.get(i).getPostId()
|
||||
// : discoverItemModels.get(i).getShortCode();
|
||||
// }
|
||||
// final NavDirections action = DiscoverFragmentDirections.actionGlobalPostViewFragment(
|
||||
// position,
|
||||
// idsOrShortCodes,
|
||||
// isId);
|
||||
// NavHostFragment.findNavController(this).navigate(action);
|
||||
// }, (model, position) -> {
|
||||
// if (!discoverAdapter.isSelecting()) {
|
||||
// checkAndResetAction();
|
||||
// return true;
|
||||
// }
|
||||
// final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||
// if (onBackPressedCallback.isEnabled()) {
|
||||
// return true;
|
||||
// }
|
||||
// actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||
// final String title = getString(R.string.number_selected, 1);
|
||||
// actionMode.setTitle(title);
|
||||
// onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||
// return true;
|
||||
// });
|
||||
// binding.postsRecyclerView.setAdapter(discoverAdapter);
|
||||
// discoverItemViewModel.getList().observe(fragmentActivity, discoverAdapter::submitList);
|
||||
// lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
||||
// if (discoverHasMore) {
|
||||
// fetchPosts();
|
||||
// }
|
||||
// }, 3);
|
||||
// binding.postsRecyclerView.addOnScrollListener(lazyLoader);
|
||||
// binding.postsRecyclerView.setViewModelStoreOwner(this)
|
||||
// .setLifeCycleOwner(this)
|
||||
// .setPostFetchService(new DiscoverPostFetchService())
|
||||
// .setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_PROFILE_POSTS_LAYOUT)))
|
||||
// .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||
// .setFeedItemCallback(feedItemCallback)
|
||||
// .init();
|
||||
// binding.swipeRefreshLayout.setRefreshing(true);
|
||||
}
|
||||
|
||||
private void fetchTopics() {
|
||||
// new iTopicFetcher(topicFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
discoverService.topicalExplore(new DiscoverService.TopicalExploreRequest(), new ServiceCallback<DiscoverService.TopicalExploreResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final DiscoverService.TopicalExploreResponse result) {
|
||||
topicClusterViewModel.getList().postValue(result.getClusters());
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "onFailure", t);
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// private void fetchPosts() {
|
||||
// binding.swipeRefreshLayout.setRefreshing(true);
|
||||
// new DiscoverFetcher(currentTopic, discoverEndMaxId, rankToken, postsFetchListener, false)
|
||||
// .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
// }
|
||||
|
||||
private boolean checkAndResetAction() {
|
||||
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
||||
return false;
|
||||
|
@ -58,17 +58,10 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
private CoordinatorLayout root;
|
||||
private FragmentFeedBinding binding;
|
||||
private StoriesService storiesService;
|
||||
// private boolean feedHasNextPage = false;
|
||||
// private String feedEndCursor = null;
|
||||
// private FeedViewModel feedViewModel;
|
||||
// private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
|
||||
private boolean shouldRefresh = true;
|
||||
// private boolean isPullToRefresh;
|
||||
private FeedStoriesViewModel feedStoriesViewModel;
|
||||
// private StaggeredGridLayoutManager gridLayoutManager;
|
||||
private boolean storiesFetching;
|
||||
|
||||
// private final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
|
||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||
@Override
|
||||
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
||||
@ -226,15 +219,11 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
// isPullToRefresh = true;
|
||||
// feedEndCursor = null;
|
||||
binding.feedRecyclerView.refresh();
|
||||
fetchStories();
|
||||
}
|
||||
|
||||
private void setupFeed() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
binding.feedRecyclerView.setViewModelStoreOwner(this)
|
||||
.setLifeCycleOwner(this)
|
||||
.setPostFetchService(new FeedPostFetchService())
|
||||
@ -331,8 +320,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
||||
}
|
||||
|
||||
private void showPostsLayoutPreferences() {
|
||||
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(Constants.PREF_POSTS_LAYOUT, preferences -> new Handler()
|
||||
.postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));
|
||||
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||
Constants.PREF_POSTS_LAYOUT,
|
||||
preferences -> new Handler().postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));
|
||||
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
package awais.instagrabber.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class TopicCluster implements Serializable {
|
||||
private String id;
|
||||
private String title;
|
||||
private String type;
|
||||
private boolean canMute;
|
||||
private boolean isMuted;
|
||||
private int rankedPosition;
|
||||
private FeedModel coverMedia;
|
||||
|
||||
public TopicCluster(final String id,
|
||||
final String title,
|
||||
final String type,
|
||||
final boolean canMute,
|
||||
final boolean isMuted,
|
||||
final int rankedPosition,
|
||||
final FeedModel coverMedia) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.type = type;
|
||||
this.canMute = canMute;
|
||||
this.isMuted = isMuted;
|
||||
this.rankedPosition = rankedPosition;
|
||||
this.coverMedia = coverMedia;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isCanMute() {
|
||||
return canMute;
|
||||
}
|
||||
|
||||
public boolean isMuted() {
|
||||
return isMuted;
|
||||
}
|
||||
|
||||
public int getRankedPosition() {
|
||||
return rankedPosition;
|
||||
}
|
||||
|
||||
public FeedModel getCoverMedia() {
|
||||
return coverMedia;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package awais.instagrabber.repositories;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.QueryMap;
|
||||
|
||||
public interface DiscoverRepository {
|
||||
@GET("/api/v1/discover/topical_explore/")
|
||||
Call<String> topicalExplore(@QueryMap Map<String, String> queryParams);
|
||||
}
|
@ -89,4 +89,5 @@ public final class Constants {
|
||||
public static final String SHARED_PREFERENCES_NAME = "settings";
|
||||
public static final String PREF_POSTS_LAYOUT = "posts_layout";
|
||||
public static final String PREF_PROFILE_POSTS_LAYOUT = "profile_posts_layout";
|
||||
public static final String PREF_TOPIC_POSTS_LAYOUT = "topic_posts_layout";
|
||||
}
|
@ -99,7 +99,7 @@ public final class ResponseBodyUtils {
|
||||
if (Utils.logCollector != null)
|
||||
Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "getLowQualityImage",
|
||||
new Pair<>("resourcesNull", resources == null));
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "Error in getLowQualityImage", e);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
@ -152,12 +152,24 @@ public final class ResponseBodyUtils {
|
||||
}
|
||||
|
||||
public static String getVideoUrl(@NonNull final JSONObject mediaObj) {
|
||||
String thumbnail = null;
|
||||
final JSONArray imageVersions = mediaObj.optJSONArray("video_versions");
|
||||
if (imageVersions != null) {
|
||||
thumbnail = getItemThumbnail(imageVersions).url;
|
||||
String url = null;
|
||||
final JSONArray videoVersions = mediaObj.optJSONArray("video_versions");
|
||||
if (videoVersions == null) {
|
||||
return null;
|
||||
}
|
||||
return thumbnail;
|
||||
int largestWidth = 0;
|
||||
for (int i = 0; i < videoVersions.length(); i++) {
|
||||
final JSONObject videoVersionJson = videoVersions.optJSONObject(i);
|
||||
if (videoVersionJson == null) {
|
||||
continue;
|
||||
}
|
||||
final int width = videoVersionJson.optInt("width");
|
||||
if (largestWidth == 0 || width > largestWidth) {
|
||||
largestWidth = width;
|
||||
url = videoVersionJson.optString("url");
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -32,6 +32,7 @@ import static awais.instagrabber.utils.Constants.PREF_DARK_THEME;
|
||||
import static awais.instagrabber.utils.Constants.PREF_LIGHT_THEME;
|
||||
import static awais.instagrabber.utils.Constants.PREF_POSTS_LAYOUT;
|
||||
import static awais.instagrabber.utils.Constants.PREF_PROFILE_POSTS_LAYOUT;
|
||||
import static awais.instagrabber.utils.Constants.PREF_TOPIC_POSTS_LAYOUT;
|
||||
import static awais.instagrabber.utils.Constants.PREV_INSTALL_VERSION;
|
||||
import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG;
|
||||
import static awais.instagrabber.utils.Constants.SKIPPED_VERSION;
|
||||
@ -115,7 +116,8 @@ public final class SettingsHelper {
|
||||
|
||||
@StringDef(
|
||||
{APP_LANGUAGE, APP_THEME, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT,
|
||||
DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, PREF_POSTS_LAYOUT, PREF_PROFILE_POSTS_LAYOUT})
|
||||
DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, PREF_POSTS_LAYOUT, PREF_PROFILE_POSTS_LAYOUT,
|
||||
PREF_TOPIC_POSTS_LAYOUT})
|
||||
public @interface StringSettings {}
|
||||
|
||||
@StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
|
||||
|
@ -0,0 +1,19 @@
|
||||
package awais.instagrabber.viewmodels;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.models.TopicCluster;
|
||||
|
||||
public class TopicClusterViewModel extends ViewModel {
|
||||
private MutableLiveData<List<TopicCluster>> list;
|
||||
|
||||
public MutableLiveData<List<TopicCluster>> getList() {
|
||||
if (list == null) {
|
||||
list = new MutableLiveData<>();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
@ -0,0 +1,501 @@
|
||||
package awais.instagrabber.webservices;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.models.FeedModel;
|
||||
import awais.instagrabber.models.PostChild;
|
||||
import awais.instagrabber.models.ProfileModel;
|
||||
import awais.instagrabber.models.TopicCluster;
|
||||
import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.repositories.DiscoverRepository;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
public class DiscoverService extends BaseService {
|
||||
|
||||
private static final String TAG = "DiscoverService";
|
||||
|
||||
private final DiscoverRepository repository;
|
||||
|
||||
private static DiscoverService instance;
|
||||
|
||||
private DiscoverService() {
|
||||
final Retrofit retrofit = getRetrofitBuilder()
|
||||
.baseUrl("https://i.instagram.com")
|
||||
.build();
|
||||
repository = retrofit.create(DiscoverRepository.class);
|
||||
}
|
||||
|
||||
public static DiscoverService getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new DiscoverService();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void topicalExplore(@NonNull final TopicalExploreRequest request,
|
||||
final ServiceCallback<TopicalExploreResponse> callback) {
|
||||
final ImmutableBiMap.Builder<String, String> builder = ImmutableBiMap.<String, String>builder()
|
||||
.put("module", "explore_popular");
|
||||
if (!TextUtils.isEmpty(request.getModule())) {
|
||||
builder.put("module", request.getModule());
|
||||
}
|
||||
if (!TextUtils.isEmpty(request.getClusterId())) {
|
||||
builder.put("cluster_id", request.getClusterId());
|
||||
}
|
||||
if (request.getMaxId() >= 0) {
|
||||
builder.put("max_id", String.valueOf(request.getMaxId()));
|
||||
}
|
||||
final Call<String> req = repository.topicalExplore(builder.build());
|
||||
req.enqueue(new Callback<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
final String body = response.body();
|
||||
if (TextUtils.isEmpty(body)) {
|
||||
callback.onSuccess(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final TopicalExploreResponse topicalExploreResponse = parseTopicalExploreResponse(body);
|
||||
callback.onSuccess(topicalExploreResponse);
|
||||
} catch (JSONException e) {
|
||||
callback.onFailure(e);
|
||||
// Log.e(TAG, "Error parsing topicalExplore response", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private TopicalExploreResponse parseTopicalExploreResponse(@NonNull final String body) throws JSONException {
|
||||
final JSONObject root = new JSONObject(body);
|
||||
final boolean moreAvailable = root.optBoolean("more_available");
|
||||
final int nextMaxId = root.optInt("next_max_id", -1);
|
||||
final int numResults = root.optInt("num_results");
|
||||
final String status = root.optString("status");
|
||||
final JSONArray clustersJson = root.optJSONArray("clusters");
|
||||
final List<TopicCluster> clusters = parseClusters(clustersJson);
|
||||
final JSONArray itemsJson = root.optJSONArray("items");
|
||||
final List<FeedModel> items = parseItems(itemsJson);
|
||||
return new TopicalExploreResponse(
|
||||
moreAvailable,
|
||||
nextMaxId,
|
||||
numResults,
|
||||
status,
|
||||
clusters,
|
||||
items
|
||||
);
|
||||
}
|
||||
|
||||
private List<TopicCluster> parseClusters(final JSONArray clustersJson) throws JSONException {
|
||||
if (clustersJson == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<TopicCluster> clusters = new ArrayList<>();
|
||||
for (int i = 0; i < clustersJson.length(); i++) {
|
||||
final JSONObject clusterJson = clustersJson.getJSONObject(i);
|
||||
final String id = clusterJson.optString("id");
|
||||
final String title = clusterJson.optString("title");
|
||||
if (id == null || title == null) {
|
||||
continue;
|
||||
}
|
||||
final String type = clusterJson.optString("type");
|
||||
final boolean canMute = clusterJson.optBoolean("can_mute");
|
||||
final boolean isMuted = clusterJson.optBoolean("is_muted");
|
||||
final JSONObject coverMediaJson = clusterJson.optJSONObject("cover_media");
|
||||
final int rankedPosition = clusterJson.optInt("ranked_position");
|
||||
final FeedModel feedModel = parseClusterCover(coverMediaJson);
|
||||
final TopicCluster topicCluster = new TopicCluster(
|
||||
id,
|
||||
title,
|
||||
type,
|
||||
canMute,
|
||||
isMuted,
|
||||
rankedPosition,
|
||||
feedModel
|
||||
);
|
||||
clusters.add(topicCluster);
|
||||
}
|
||||
return clusters;
|
||||
}
|
||||
|
||||
private FeedModel parseClusterCover(final JSONObject coverMediaJson) throws JSONException {
|
||||
if (coverMediaJson == null) {
|
||||
return null;
|
||||
}
|
||||
ProfileModel profileModel = null;
|
||||
if (coverMediaJson.has("user")) {
|
||||
final JSONObject user = coverMediaJson.getJSONObject("user");
|
||||
profileModel = new ProfileModel(
|
||||
user.optBoolean("is_private"),
|
||||
false,
|
||||
user.optBoolean("is_verified"),
|
||||
user.getString("pk"),
|
||||
user.getString(Constants.EXTRAS_USERNAME),
|
||||
user.optString("full_name"),
|
||||
null,
|
||||
null,
|
||||
user.getString("profile_pic_url"),
|
||||
null,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
final String resourceUrl = ResponseBodyUtils.getHighQualityImage(coverMediaJson);
|
||||
final String thumbnailUrl = ResponseBodyUtils.getLowQualityImage(coverMediaJson);
|
||||
final int width = coverMediaJson.optInt("original_width");
|
||||
final int height = coverMediaJson.optInt("original_height");
|
||||
return new FeedModel.Builder()
|
||||
.setProfileModel(profileModel)
|
||||
.setItemType(MediaItemType.MEDIA_TYPE_IMAGE)
|
||||
.setViewCount(0)
|
||||
.setPostId(coverMediaJson.getString(Constants.EXTRAS_ID))
|
||||
.setDisplayUrl(resourceUrl)
|
||||
.setThumbnailUrl(thumbnailUrl)
|
||||
.setShortCode(coverMediaJson.getString("code"))
|
||||
.setPostCaption(null)
|
||||
.setCommentsCount(0)
|
||||
.setTimestamp(coverMediaJson.optLong("taken_at", -1))
|
||||
.setLiked(false)
|
||||
.setBookmarked(false)
|
||||
.setLikesCount(0)
|
||||
.setLocationName(null)
|
||||
.setLocationId(null)
|
||||
.setImageHeight(height)
|
||||
.setImageWidth(width)
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<FeedModel> parseItems(final JSONArray items) throws JSONException {
|
||||
if (items == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<FeedModel> feedModels = new ArrayList<>();
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
final JSONObject itemJson = items.optJSONObject(i);
|
||||
if (itemJson == null) {
|
||||
continue;
|
||||
}
|
||||
final JSONObject mediaJson = itemJson.optJSONObject("media");
|
||||
final FeedModel feedModel = parseClusterItemMedia(mediaJson);
|
||||
if (feedModel != null) {
|
||||
feedModels.add(feedModel);
|
||||
}
|
||||
}
|
||||
return feedModels;
|
||||
}
|
||||
|
||||
private FeedModel parseClusterItemMedia(final JSONObject mediaJson) throws JSONException {
|
||||
if (mediaJson == null) {
|
||||
return null;
|
||||
}
|
||||
ProfileModel profileModel = null;
|
||||
if (mediaJson.has("user")) {
|
||||
final JSONObject user = mediaJson.getJSONObject("user");
|
||||
final JSONObject friendshipStatus = user.optJSONObject("friendship_status");
|
||||
boolean following = false;
|
||||
boolean restricted = false;
|
||||
boolean requested = false;
|
||||
if (friendshipStatus != null) {
|
||||
following = friendshipStatus.optBoolean("following");
|
||||
requested = friendshipStatus.optBoolean("outgoing_request");
|
||||
restricted = friendshipStatus.optBoolean("is_restricted");
|
||||
}
|
||||
profileModel = new ProfileModel(
|
||||
user.optBoolean("is_private"),
|
||||
false, // if you can see it then you def follow
|
||||
user.optBoolean("is_verified"),
|
||||
user.getString("pk"),
|
||||
user.getString(Constants.EXTRAS_USERNAME),
|
||||
user.optString("full_name"),
|
||||
null,
|
||||
null,
|
||||
user.getString("profile_pic_url"),
|
||||
null,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
following,
|
||||
restricted,
|
||||
false,
|
||||
requested);
|
||||
}
|
||||
final JSONObject captionJson = mediaJson.optJSONObject("caption");
|
||||
final JSONObject locationJson = mediaJson.optJSONObject("location");
|
||||
final MediaItemType mediaType = ResponseBodyUtils.getMediaItemType(mediaJson.optInt("media_type"));
|
||||
final FeedModel.Builder feedModelBuilder = new FeedModel.Builder()
|
||||
.setItemType(mediaType)
|
||||
.setProfileModel(profileModel)
|
||||
.setPostId(mediaJson.getString(Constants.EXTRAS_ID))
|
||||
.setThumbnailUrl(mediaType != MediaItemType.MEDIA_TYPE_SLIDER ? ResponseBodyUtils.getLowQualityImage(mediaJson) : null)
|
||||
.setShortCode(mediaJson.getString("code"))
|
||||
.setPostCaption(captionJson != null ? captionJson.optString("text") : null)
|
||||
.setCommentsCount(mediaJson.optInt("comment_count"))
|
||||
.setTimestamp(mediaJson.optLong("taken_at", -1))
|
||||
.setLiked(mediaJson.optBoolean("has_liked"))
|
||||
// .setBookmarked()
|
||||
.setLikesCount(mediaJson.optInt("like_count"))
|
||||
.setLocationName(locationJson != null ? locationJson.optString("name") : null)
|
||||
.setLocationId(locationJson != null ? String.valueOf(locationJson.optInt("pk")) : null)
|
||||
.setImageHeight(mediaJson.optInt("original_height"))
|
||||
.setImageWidth(mediaJson.optInt("original_width"));
|
||||
switch (mediaType) {
|
||||
case MEDIA_TYPE_VIDEO:
|
||||
final long videoViews = mediaJson.optLong("view_count", 0);
|
||||
feedModelBuilder.setViewCount(videoViews)
|
||||
.setDisplayUrl(ResponseBodyUtils.getVideoUrl(mediaJson));
|
||||
break;
|
||||
case MEDIA_TYPE_IMAGE:
|
||||
feedModelBuilder.setDisplayUrl(ResponseBodyUtils.getHighQualityImage(mediaJson));
|
||||
break;
|
||||
case MEDIA_TYPE_SLIDER:
|
||||
final List<PostChild> childPosts = getChildPosts(mediaJson);
|
||||
feedModelBuilder.setSliderItems(childPosts);
|
||||
break;
|
||||
}
|
||||
return feedModelBuilder.build();
|
||||
}
|
||||
|
||||
private List<PostChild> getChildPosts(final JSONObject mediaJson) throws JSONException {
|
||||
if (mediaJson == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final JSONArray carouselMedia = mediaJson.optJSONArray("carousel_media");
|
||||
if (carouselMedia == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<PostChild> children = new ArrayList<>();
|
||||
for (int i = 0; i < carouselMedia.length(); i++) {
|
||||
final JSONObject childJson = carouselMedia.optJSONObject(i);
|
||||
final PostChild childPost = getChildPost(childJson);
|
||||
if (childPost != null) {
|
||||
children.add(childPost);
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
private PostChild getChildPost(final JSONObject childJson) throws JSONException {
|
||||
if (childJson == null) {
|
||||
return null;
|
||||
}
|
||||
final MediaItemType mediaType = ResponseBodyUtils.getMediaItemType(childJson.optInt("media_type"));
|
||||
final PostChild.Builder builder = new PostChild.Builder();
|
||||
switch (mediaType) {
|
||||
case MEDIA_TYPE_VIDEO:
|
||||
builder.setDisplayUrl(ResponseBodyUtils.getVideoUrl(childJson));
|
||||
break;
|
||||
case MEDIA_TYPE_IMAGE:
|
||||
builder.setDisplayUrl(ResponseBodyUtils.getHighQualityImage(childJson));
|
||||
break;
|
||||
}
|
||||
return builder.setItemType(mediaType)
|
||||
.setPostId(childJson.getString("id"))
|
||||
.setThumbnailUrl(ResponseBodyUtils.getLowQualityImage(childJson))
|
||||
.setHeight(childJson.optInt("original_height"))
|
||||
.setWidth(childJson.optInt("original_width"))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static class TopicalExploreRequest {
|
||||
|
||||
private String module;
|
||||
private String clusterId;
|
||||
private int maxId = -1;
|
||||
|
||||
public TopicalExploreRequest() {}
|
||||
|
||||
public TopicalExploreRequest(final String module, final String clusterId, final int maxId) {
|
||||
this.module = module;
|
||||
this.clusterId = clusterId;
|
||||
this.maxId = maxId;
|
||||
}
|
||||
|
||||
public String getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public TopicalExploreRequest setModule(final String module) {
|
||||
this.module = module;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getClusterId() {
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
public TopicalExploreRequest setClusterId(final String clusterId) {
|
||||
this.clusterId = clusterId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMaxId() {
|
||||
return maxId;
|
||||
}
|
||||
|
||||
public TopicalExploreRequest setMaxId(final int maxId) {
|
||||
this.maxId = maxId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final TopicalExploreRequest that = (TopicalExploreRequest) o;
|
||||
return maxId == that.maxId &&
|
||||
Objects.equals(module, that.module) &&
|
||||
Objects.equals(clusterId, that.clusterId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(module, clusterId, maxId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TopicalExploreRequest{" +
|
||||
"module='" + module + '\'' +
|
||||
", clusterId='" + clusterId + '\'' +
|
||||
", maxId=" + maxId +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class TopicalExploreResponse {
|
||||
|
||||
private boolean moreAvailable;
|
||||
private int nextMaxId;
|
||||
private int numResults;
|
||||
private String status;
|
||||
private List<TopicCluster> clusters;
|
||||
private List<FeedModel> items;
|
||||
|
||||
public TopicalExploreResponse() {}
|
||||
|
||||
public TopicalExploreResponse(final boolean moreAvailable,
|
||||
final int nextMaxId,
|
||||
final int numResults,
|
||||
final String status,
|
||||
final List<TopicCluster> clusters, final List<FeedModel> items) {
|
||||
this.moreAvailable = moreAvailable;
|
||||
this.nextMaxId = nextMaxId;
|
||||
this.numResults = numResults;
|
||||
this.status = status;
|
||||
this.clusters = clusters;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public boolean isMoreAvailable() {
|
||||
return moreAvailable;
|
||||
}
|
||||
|
||||
public TopicalExploreResponse setMoreAvailable(final boolean moreAvailable) {
|
||||
this.moreAvailable = moreAvailable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getNextMaxId() {
|
||||
return nextMaxId;
|
||||
}
|
||||
|
||||
public TopicalExploreResponse setNextMaxId(final int nextMaxId) {
|
||||
this.nextMaxId = nextMaxId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getNumResults() {
|
||||
return numResults;
|
||||
}
|
||||
|
||||
public TopicalExploreResponse setNumResults(final int numResults) {
|
||||
this.numResults = numResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public TopicalExploreResponse setStatus(final String status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<TopicCluster> getClusters() {
|
||||
return clusters;
|
||||
}
|
||||
|
||||
public TopicalExploreResponse setClusters(final List<TopicCluster> clusters) {
|
||||
this.clusters = clusters;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<FeedModel> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public TopicalExploreResponse setItems(final List<FeedModel> items) {
|
||||
this.items = items;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final TopicalExploreResponse that = (TopicalExploreResponse) o;
|
||||
return moreAvailable == that.moreAvailable &&
|
||||
nextMaxId == that.nextMaxId &&
|
||||
numResults == that.numResults &&
|
||||
Objects.equals(status, that.status) &&
|
||||
Objects.equals(clusters, that.clusters) &&
|
||||
Objects.equals(items, that.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(moreAvailable, nextMaxId, numResults, status, clusters, items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TopicalExploreResponse{" +
|
||||
"moreAvailable=" + moreAvailable +
|
||||
", nextMaxId=" + nextMaxId +
|
||||
", numResults=" + numResults +
|
||||
", status='" + status + '\'' +
|
||||
", clusters=" + clusters +
|
||||
", items=" + items +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
@ -7,37 +7,39 @@
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="?attr/colorSurface">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface"
|
||||
app:elevation="0dp">
|
||||
<!--<com.google.android.material.appbar.AppBarLayout-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:background="?attr/colorSurface"-->
|
||||
<!-- app:elevation="0dp">-->
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|snap|enterAlways">
|
||||
<!-- <com.google.android.material.appbar.CollapsingToolbarLayout-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- app:layout_scrollFlags="scroll|snap|enterAlways">-->
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
android:id="@+id/discoverType"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/discover_placeholder"
|
||||
android:spinnerMode="dialog" />
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
<!-- <androidx.appcompat.widget.AppCompatSpinner-->
|
||||
<!-- android:id="@+id/discoverType"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:entries="@array/discover_placeholder"-->
|
||||
<!-- android:spinnerMode="dialog" />-->
|
||||
<!-- </com.google.android.material.appbar.CollapsingToolbarLayout>-->
|
||||
<!--</com.google.android.material.appbar.AppBarLayout>-->
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/discoverSwipeRefreshLayout"
|
||||
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/discoverPosts"
|
||||
android:id="@+id/topics_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/item_post" />
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2"
|
||||
tools:itemCount="10"
|
||||
tools:listitem="@layout/item_discover_topic" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>
|
@ -285,20 +285,6 @@
|
||||
tools:listitem="@layout/item_feed_photo" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<!--<androidx.swiperefreshlayout.widget.SwipeRefreshLayout-->
|
||||
<!-- android:id="@+id/swipeRefreshLayout"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="match_parent"-->
|
||||
<!-- app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">-->
|
||||
|
||||
<!-- <androidx.recyclerview.widget.RecyclerView-->
|
||||
<!-- android:id="@+id/mainPosts"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="match_parent"-->
|
||||
<!-- android:clipToPadding="false"-->
|
||||
<!-- tools:listitem="@layout/item_post" />-->
|
||||
<!--</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>-->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/privatePage"
|
||||
android:layout_width="match_parent"
|
||||
|
58
app/src/main/res/layout/fragment_topic_posts.xml
Normal file
58
app/src/main/res/layout/fragment_topic_posts.xml
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<awais.instagrabber.customviews.helpers.NestedCoordinatorLayout 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>
|
||||
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>
|
45
app/src/main/res/layout/item_discover_topic.xml
Normal file
45
app/src/main/res/layout/item_discover_topic.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/cover"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:actualImageScaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:roundedCornerRadius="5dp"
|
||||
app:viewAspectRatio="1"
|
||||
tools:background="@mipmap/ic_launcher" />
|
||||
|
||||
<View
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_percent="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="bottom"
|
||||
android:padding="8dp"
|
||||
android:textStyle="bold"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_percent="0.3"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Title" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
9
app/src/main/res/menu/topic_posts_menu.xml
Normal file
9
app/src/main/res/menu/topic_posts_menu.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?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/layout"
|
||||
android:title="@string/layout"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
@ -5,9 +5,40 @@
|
||||
android:id="@+id/discover_nav_graph"
|
||||
app:startDestination="@id/discoverFragment">
|
||||
|
||||
<include app:graph="@navigation/post_view_nav_graph" />
|
||||
<include app:graph="@navigation/hashtag_nav_graph" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_hashTagFragment"
|
||||
app:destination="@id/hashtag_nav_graph">
|
||||
<argument
|
||||
android:name="hashtag"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/profile_nav_graph" />
|
||||
<include app:graph="@navigation/comments_nav_graph" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_profileFragment"
|
||||
app:destination="@id/profile_nav_graph">
|
||||
<argument
|
||||
android:name="username"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/location_nav_graph" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_locationFragment"
|
||||
app:destination="@id/location_nav_graph">
|
||||
<argument
|
||||
android:name="locationId"
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<include app:graph="@navigation/post_view_nav_graph" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_global_postViewFragment"
|
||||
@ -23,9 +54,48 @@
|
||||
app:argType="boolean" />
|
||||
</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="string"
|
||||
app:nullable="false" />
|
||||
</action>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/discoverFragment"
|
||||
android:name="awais.instagrabber.fragments.main.DiscoverFragment"
|
||||
android:label="@string/title_discover"
|
||||
tools:layout="@layout/fragment_discover" />
|
||||
tools:layout="@layout/fragment_discover">
|
||||
<action
|
||||
android:id="@+id/action_discoverFragment_to_topicPostsFragment"
|
||||
app:destination="@id/topicPostsFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/topicPostsFragment"
|
||||
android:name="awais.instagrabber.fragments.TopicPostsFragment"
|
||||
tools:layout="@layout/fragment_topic_posts">
|
||||
<argument
|
||||
android:name="topicCluster"
|
||||
app:argType="awais.instagrabber.models.TopicCluster" />
|
||||
|
||||
<argument
|
||||
android:name="titleColor"
|
||||
app:argType="integer" />
|
||||
|
||||
<argument
|
||||
android:name="backgroundColor"
|
||||
app:argType="integer" />
|
||||
</fragment>
|
||||
</navigation>
|
Loading…
Reference in New Issue
Block a user