mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 14:47:29 +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'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
|
||||||
|
|
||||||
def appcompat_version = "1.2.0"
|
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.material:material:1.3.0-alpha03'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer:2.12.0'
|
implementation 'com.google.android.exoplayer:exoplayer:2.12.0'
|
||||||
@ -61,9 +61,10 @@ dependencies {
|
|||||||
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
||||||
implementation "androidx.navigation:navigation-fragment:$nav_version"
|
implementation "androidx.navigation:navigation-fragment:$nav_version"
|
||||||
implementation "androidx.navigation:navigation-ui:$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.preference:preference:1.1.1"
|
||||||
implementation "androidx.work:work-runtime:2.4.0"
|
implementation "androidx.work:work-runtime:2.4.0"
|
||||||
|
implementation 'androidx.palette:palette:1.0.0'
|
||||||
|
|
||||||
implementation 'com.google.guava:guava:27.0.1-android'
|
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,
|
R.id.main_nav_host,
|
||||||
getIntent(),
|
getIntent(),
|
||||||
firstFragmentGraphIndex);
|
firstFragmentGraphIndex);
|
||||||
navControllerLiveData.observe(this, this::setupNavigation);
|
navControllerLiveData.observe(this, navController -> setupNavigation(binding.toolbar, navController));
|
||||||
currentNavControllerLiveData = navControllerLiveData;
|
currentNavControllerLiveData = navControllerLiveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,8 +446,11 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
return mainNavList;
|
return mainNavList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupNavigation(final NavController navController) {
|
private void setupNavigation(final Toolbar toolbar, final NavController navController) {
|
||||||
NavigationUI.setupWithNavController(binding.toolbar, navController);
|
if (navController == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NavigationUI.setupWithNavController(toolbar, navController);
|
||||||
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
|
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
|
// 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);
|
binding.appBarLayout.setExpanded(true, true);
|
||||||
@ -640,4 +643,16 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
public int getNavHostContainerId() {
|
public int getNavHostContainerId() {
|
||||||
return binding.mainNavHost.getId();
|
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
|
@Override
|
||||||
public void fetch(final String cursor, final FetchListener<List<FeedModel>> fetchListener) {
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
feedService.fetch(25, cursor, new ServiceCallback<PostsFetchResponse>() {
|
feedService.fetch(25, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final PostsFetchResponse result) {
|
public void onSuccess(final PostsFetchResponse result) {
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
@ -43,8 +43,8 @@ public class FeedPostFetchService implements PostFetcher.PostFetchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNextCursor() {
|
public void reset() {
|
||||||
return nextCursor;
|
nextCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,8 +23,8 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fetch(final String cursor, final FetchListener<List<FeedModel>> fetchListener) {
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
profileService.fetchPosts(profileModel, 30, cursor, new ServiceCallback<PostsFetchResponse>() {
|
profileService.fetchPosts(profileModel, 30, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final PostsFetchResponse result) {
|
public void onSuccess(final PostsFetchResponse result) {
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
@ -46,8 +46,8 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNextCursor() {
|
public void reset() {
|
||||||
return nextCursor;
|
nextCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -167,7 +167,7 @@ public class PostsRecyclerView extends RecyclerView {
|
|||||||
setNestedScrollingEnabled(true);
|
setNestedScrollingEnabled(true);
|
||||||
lazyLoader = new RecyclerLazyLoaderAtBottom(layoutManager, (page) -> {
|
lazyLoader = new RecyclerLazyLoaderAtBottom(layoutManager, (page) -> {
|
||||||
if (postFetcher.hasMore()) {
|
if (postFetcher.hasMore()) {
|
||||||
postFetcher.fetchNextPage();
|
postFetcher.fetch();
|
||||||
dispatchFetchStatus();
|
dispatchFetchStatus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -204,6 +204,7 @@ public class PostsRecyclerView extends RecyclerView {
|
|||||||
|
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
lazyLoader.resetState();
|
lazyLoader.resetState();
|
||||||
|
postFetcher.reset();
|
||||||
postFetcher.fetch();
|
postFetcher.fetch();
|
||||||
dispatchFetchStatus();
|
dispatchFetchStatus();
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,17 @@ public class PostFetcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void fetch() {
|
public void fetch() {
|
||||||
fetch(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void fetchNextPage() {
|
|
||||||
fetch(postFetchService.getNextCursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void fetch(final String cursor) {
|
|
||||||
fetching = true;
|
fetching = true;
|
||||||
postFetchService.fetch(cursor, result -> {
|
postFetchService.fetch(result -> {
|
||||||
fetching = false;
|
fetching = false;
|
||||||
fetchListener.onResult(result);
|
fetchListener.onResult(result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
postFetchService.reset();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isFetching() {
|
public boolean isFetching() {
|
||||||
return fetching;
|
return fetching;
|
||||||
}
|
}
|
||||||
@ -41,9 +37,9 @@ public class PostFetcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface PostFetchService {
|
public interface PostFetchService {
|
||||||
void fetch(String cursor, FetchListener<List<FeedModel>> fetchListener);
|
void fetch(FetchListener<List<FeedModel>> fetchListener);
|
||||||
|
|
||||||
String getNextCursor();
|
void reset();
|
||||||
|
|
||||||
boolean hasNextPage();
|
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 (!recyclerView.canScrollVertically(RecyclerView.SCROLL_AXIS_HORIZONTAL) && newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||||
if (!loading && lazyLoadListener != null) {
|
if (!loading && lazyLoadListener != null) {
|
||||||
loading = true;
|
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.graphics.Typeface;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.style.RelativeSizeSpan;
|
import android.text.style.RelativeSizeSpan;
|
||||||
import android.text.style.StyleSpan;
|
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;
|
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null;
|
||||||
final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments());
|
final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments());
|
||||||
hashtag = fragmentArgs.getHashtag();
|
hashtag = fragmentArgs.getHashtag();
|
||||||
setTitle();
|
// setTitle();
|
||||||
setupPosts();
|
setupPosts();
|
||||||
fetchHashtagModel();
|
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();
|
Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setTitle();
|
||||||
fetchPosts();
|
fetchPosts();
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
@ -324,9 +324,35 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
binding.btnFollowTag.setOnClickListener(v -> {
|
binding.btnFollowTag.setOnClickListener(v -> {
|
||||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||||
binding.btnFollowTag.setClickable(false);
|
if (csrfToken != null) {
|
||||||
if (!hashtagModel.getFollowing()) {
|
binding.btnFollowTag.setClickable(false);
|
||||||
tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
if (!hashtagModel.getFollowing()) {
|
||||||
|
tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final Boolean result) {
|
||||||
|
binding.btnFollowTag.setClickable(true);
|
||||||
|
if (!result) {
|
||||||
|
Log.e(TAG, "onSuccess: result is false");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull final Throwable t) {
|
||||||
|
binding.btnFollowTag.setClickable(true);
|
||||||
|
Log.e(TAG, "onFailure: ", t);
|
||||||
|
final String message = t.getMessage();
|
||||||
|
Snackbar.make(root,
|
||||||
|
message != null ? message
|
||||||
|
: getString(R.string.downloader_unknown_error),
|
||||||
|
BaseTransientBottomBar.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final Boolean result) {
|
public void onSuccess(final Boolean result) {
|
||||||
binding.btnFollowTag.setClickable(true);
|
binding.btnFollowTag.setClickable(true);
|
||||||
@ -349,31 +375,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final Boolean result) {
|
|
||||||
binding.btnFollowTag.setClickable(true);
|
|
||||||
if (!result) {
|
|
||||||
Log.e(TAG, "onSuccess: result is false");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NonNull final Throwable t) {
|
|
||||||
binding.btnFollowTag.setClickable(true);
|
|
||||||
Log.e(TAG, "onFailure: ", t);
|
|
||||||
final String message = t.getMessage();
|
|
||||||
Snackbar.make(root,
|
|
||||||
message != null ? message
|
|
||||||
: getString(R.string.downloader_unknown_error),
|
|
||||||
BaseTransientBottomBar.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
binding.btnFollowTag.setVisibility(View.GONE);
|
binding.btnFollowTag.setVisibility(View.GONE);
|
||||||
@ -425,7 +427,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
final NavDirections action = HashTagFragmentDirections
|
final NavDirections action = HashTagFragmentDirections
|
||||||
.actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
|
.actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName());
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -445,9 +446,10 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
private void setTitle() {
|
private void setTitle() {
|
||||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
Log.d(TAG, "setting title: " + hashtag);
|
// Log.d(TAG, "setting title: " + hashtag);
|
||||||
final Handler handler = new Handler();
|
actionBar.setTitle(hashtag);
|
||||||
handler.postDelayed(() -> actionBar.setTitle(hashtag), 200);
|
// final Handler handler = new Handler();
|
||||||
|
// handler.postDelayed(() -> , 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,7 +920,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
|||||||
binding.playerControls.getRoot().setVisibility(View.GONE);
|
binding.playerControls.getRoot().setVisibility(View.GONE);
|
||||||
binding.sliderParent.setVisibility(View.VISIBLE);
|
binding.sliderParent.setVisibility(View.VISIBLE);
|
||||||
binding.mediaCounter.setVisibility(View.VISIBLE);
|
binding.mediaCounter.setVisibility(View.VISIBLE);
|
||||||
if (sharedMainPostElement != null) {
|
if (!wasPaused && sharedMainPostElement != null) {
|
||||||
addSharedElement(sharedMainPostElement, binding.sliderParent);
|
addSharedElement(sharedMainPostElement, binding.sliderParent);
|
||||||
}
|
}
|
||||||
sliderItemsAdapter = new SliderItemsAdapter(onVerticalDragListener, binding.playerControls, true, new SliderCallbackAdapter() {
|
sliderItemsAdapter = new SliderItemsAdapter(onVerticalDragListener, binding.playerControls, true, new SliderCallbackAdapter() {
|
||||||
@ -1014,7 +1014,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
|||||||
binding.sliderParent.setVisibility(View.GONE);
|
binding.sliderParent.setVisibility(View.GONE);
|
||||||
binding.mediaCounter.setVisibility(View.GONE);
|
binding.mediaCounter.setVisibility(View.GONE);
|
||||||
// binding.playerControls.getRoot().setVisibility(View.VISIBLE);
|
// binding.playerControls.getRoot().setVisibility(View.VISIBLE);
|
||||||
if (sharedMainPostElement != null) {
|
if (!wasPaused && sharedMainPostElement != null) {
|
||||||
final GenericDraweeHierarchy hierarchy = binding.videoPost.thumbnail.getHierarchy();
|
final GenericDraweeHierarchy hierarchy = binding.videoPost.thumbnail.getHierarchy();
|
||||||
hierarchy.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP);
|
hierarchy.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP);
|
||||||
addSharedElement(sharedMainPostElement, binding.videoPost.thumbnailParent);
|
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;
|
package awais.instagrabber.fragments.main;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.activity.OnBackPressedDispatcher;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.fragment.FragmentNavigator;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.activities.MainActivity;
|
import awais.instagrabber.activities.MainActivity;
|
||||||
import awais.instagrabber.adapters.DiscoverAdapter;
|
import awais.instagrabber.adapters.DiscoverTopicsAdapter;
|
||||||
import awais.instagrabber.asyncs.DiscoverFetcher;
|
|
||||||
import awais.instagrabber.asyncs.i.iTopicFetcher;
|
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.databinding.FragmentDiscoverBinding;
|
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.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 {
|
public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
|
private static final String TAG = "DiscoverFragment";
|
||||||
|
|
||||||
private MainActivity fragmentActivity;
|
private MainActivity fragmentActivity;
|
||||||
private CoordinatorLayout root;
|
private CoordinatorLayout root;
|
||||||
private FragmentDiscoverBinding binding;
|
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 ActionMode actionMode;
|
||||||
private DiscoverItemViewModel discoverItemViewModel;
|
private TopicClusterViewModel topicClusterViewModel;
|
||||||
private boolean shouldRefresh = true;
|
private boolean shouldRefresh = true;
|
||||||
private boolean isPullToRefresh;
|
private DiscoverService discoverService;
|
||||||
|
|
||||||
private final FetchListener<DiscoverTopicModel> topicFetchListener = new FetchListener<DiscoverTopicModel>() {
|
// private final FetchListener<DiscoverItemModel[]> postsFetchListener = new FetchListener<DiscoverItemModel[]>() {
|
||||||
@Override
|
// @Override
|
||||||
public void doBefore() {}
|
// public void doBefore() {}
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public void onResult(final DiscoverTopicModel result) {
|
// public void onResult(final DiscoverItemModel[] result) {
|
||||||
if (result != null) {
|
// if (result == null || result.length <= 0) {
|
||||||
topicIds = result.getIds();
|
// binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
rankToken = result.getToken();
|
// final Context context = getContext();
|
||||||
final Context context = getContext();
|
// if (context == null) return;
|
||||||
if (context == null) return;
|
// Toast.makeText(context, R.string.discover_empty, Toast.LENGTH_SHORT).show();
|
||||||
final ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(
|
// return;
|
||||||
context,
|
// }
|
||||||
android.R.layout.simple_spinner_dropdown_item,
|
// List<DiscoverItemModel> current = discoverItemViewModel.getList().getValue();
|
||||||
result.getNames()
|
// final List<DiscoverItemModel> resultList = Arrays.asList(result);
|
||||||
);
|
// current = current == null ? new ArrayList<>() : new ArrayList<>(current); // copy to modifiable list
|
||||||
binding.discoverType.setAdapter(spinnerArrayAdapter);
|
// if (isPullToRefresh) {
|
||||||
}
|
// current = resultList;
|
||||||
}
|
// isPullToRefresh = false;
|
||||||
};
|
// } else {
|
||||||
private final FetchListener<DiscoverItemModel[]> postsFetchListener = new FetchListener<DiscoverItemModel[]>() {
|
// current.addAll(resultList);
|
||||||
@Override
|
// }
|
||||||
public void doBefore() {}
|
// discoverItemViewModel.getList().postValue(current);
|
||||||
|
// binding.swipeRefreshLayout.setRefreshing(false);
|
||||||
@Override
|
// final DiscoverItemModel discoverItemModel = result[result.length - 1];
|
||||||
public void onResult(final DiscoverItemModel[] result) {
|
// if (discoverItemModel != null) {
|
||||||
if (result == null || result.length <= 0) {
|
// discoverEndMaxId = discoverItemModel.getNextMaxId();
|
||||||
binding.discoverSwipeRefreshLayout.setRefreshing(false);
|
// discoverHasMore = discoverItemModel.hasMore();
|
||||||
final Context context = getContext();
|
// discoverItemModel.setMore(false, null);
|
||||||
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 OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
|
||||||
@Override
|
@Override
|
||||||
public void handleOnBackPressed() {
|
public void handleOnBackPressed() {
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
remove();
|
remove();
|
||||||
if (discoverAdapter == null) return;
|
// if (discoverAdapter == null) return;
|
||||||
discoverAdapter.clearSelection();
|
// discoverAdapter.clearSelection();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
|
||||||
@ -134,14 +92,14 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_download) {
|
if (item.getItemId() == R.id.action_download) {
|
||||||
if (discoverAdapter == null) return false;
|
// if (discoverAdapter == null) return false;
|
||||||
final Context context = getContext();
|
// final Context context = getContext();
|
||||||
if (context == null) return false;
|
// if (context == null) return false;
|
||||||
DownloadUtils.batchDownload(context,
|
// DownloadUtils.batchDownload(context,
|
||||||
null,
|
// null,
|
||||||
DownloadMethod.DOWNLOAD_DISCOVER,
|
// DownloadMethod.DOWNLOAD_DISCOVER,
|
||||||
discoverAdapter.getSelectedModels());
|
// discoverAdapter.getSelectedModels());
|
||||||
checkAndResetAction();
|
// checkAndResetAction();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -152,6 +110,7 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
fragmentActivity = (MainActivity) requireActivity();
|
fragmentActivity = (MainActivity) requireActivity();
|
||||||
|
discoverService = DiscoverService.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -170,96 +129,159 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||||
if (!shouldRefresh) return;
|
if (!shouldRefresh) return;
|
||||||
binding.discoverSwipeRefreshLayout.setOnRefreshListener(this);
|
binding.swipeRefreshLayout.setOnRefreshListener(this);
|
||||||
setupExplore();
|
init();
|
||||||
shouldRefresh = false;
|
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
|
@Override
|
||||||
public void onRefresh() {
|
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();
|
fetchTopics();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchTopics() {
|
public void setupTopics() {
|
||||||
new iTopicFetcher(topicFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
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() {
|
private void setupExplore() {
|
||||||
binding.discoverSwipeRefreshLayout.setRefreshing(true);
|
// discoverItemViewModel = new ViewModelProvider(fragmentActivity).get(DiscoverItemViewModel.class);
|
||||||
new DiscoverFetcher(currentTopic, discoverEndMaxId, rankToken, postsFetchListener, false)
|
// final Context context = getContext();
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
// 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() {
|
private boolean checkAndResetAction() {
|
||||||
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
if (!onBackPressedCallback.isEnabled() && actionMode == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -58,17 +58,10 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
private CoordinatorLayout root;
|
private CoordinatorLayout root;
|
||||||
private FragmentFeedBinding binding;
|
private FragmentFeedBinding binding;
|
||||||
private StoriesService storiesService;
|
private StoriesService storiesService;
|
||||||
// private boolean feedHasNextPage = false;
|
|
||||||
// private String feedEndCursor = null;
|
|
||||||
// private FeedViewModel feedViewModel;
|
|
||||||
// private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
|
|
||||||
private boolean shouldRefresh = true;
|
private boolean shouldRefresh = true;
|
||||||
// private boolean isPullToRefresh;
|
|
||||||
private FeedStoriesViewModel feedStoriesViewModel;
|
private FeedStoriesViewModel feedStoriesViewModel;
|
||||||
// private StaggeredGridLayoutManager gridLayoutManager;
|
|
||||||
private boolean storiesFetching;
|
private boolean storiesFetching;
|
||||||
|
|
||||||
// private final boolean shouldAutoPlay = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
|
|
||||||
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPostClick(final FeedModel feedModel, final View profilePicView, final View mainPostImage) {
|
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
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
// isPullToRefresh = true;
|
|
||||||
// feedEndCursor = null;
|
|
||||||
binding.feedRecyclerView.refresh();
|
binding.feedRecyclerView.refresh();
|
||||||
fetchStories();
|
fetchStories();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupFeed() {
|
private void setupFeed() {
|
||||||
final Context context = getContext();
|
|
||||||
if (context == null) return;
|
|
||||||
binding.feedRecyclerView.setViewModelStoreOwner(this)
|
binding.feedRecyclerView.setViewModelStoreOwner(this)
|
||||||
.setLifeCycleOwner(this)
|
.setLifeCycleOwner(this)
|
||||||
.setPostFetchService(new FeedPostFetchService())
|
.setPostFetchService(new FeedPostFetchService())
|
||||||
@ -331,8 +320,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showPostsLayoutPreferences() {
|
private void showPostsLayoutPreferences() {
|
||||||
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(Constants.PREF_POSTS_LAYOUT, preferences -> new Handler()
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||||
.postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));
|
Constants.PREF_POSTS_LAYOUT,
|
||||||
|
preferences -> new Handler().postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));
|
||||||
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
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 SHARED_PREFERENCES_NAME = "settings";
|
||||||
public static final String PREF_POSTS_LAYOUT = "posts_layout";
|
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_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)
|
if (Utils.logCollector != null)
|
||||||
Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "getLowQualityImage",
|
Utils.logCollector.appendException(e, LogCollector.LogFile.UTILS, "getLowQualityImage",
|
||||||
new Pair<>("resourcesNull", resources == null));
|
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;
|
return src;
|
||||||
}
|
}
|
||||||
@ -152,12 +152,24 @@ public final class ResponseBodyUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getVideoUrl(@NonNull final JSONObject mediaObj) {
|
public static String getVideoUrl(@NonNull final JSONObject mediaObj) {
|
||||||
String thumbnail = null;
|
String url = null;
|
||||||
final JSONArray imageVersions = mediaObj.optJSONArray("video_versions");
|
final JSONArray videoVersions = mediaObj.optJSONArray("video_versions");
|
||||||
if (imageVersions != null) {
|
if (videoVersions == null) {
|
||||||
thumbnail = getItemThumbnail(imageVersions).url;
|
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
|
@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_LIGHT_THEME;
|
||||||
import static awais.instagrabber.utils.Constants.PREF_POSTS_LAYOUT;
|
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_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.PREV_INSTALL_VERSION;
|
||||||
import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG;
|
import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG;
|
||||||
import static awais.instagrabber.utils.Constants.SKIPPED_VERSION;
|
import static awais.instagrabber.utils.Constants.SKIPPED_VERSION;
|
||||||
@ -115,7 +116,8 @@ public final class SettingsHelper {
|
|||||||
|
|
||||||
@StringDef(
|
@StringDef(
|
||||||
{APP_LANGUAGE, APP_THEME, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT,
|
{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 {}
|
public @interface StringSettings {}
|
||||||
|
|
||||||
@StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
|
@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:animateLayoutChanges="true"
|
||||||
android:background="?attr/colorSurface">
|
android:background="?attr/colorSurface">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<!--<com.google.android.material.appbar.AppBarLayout-->
|
||||||
android:layout_width="match_parent"
|
<!-- android:layout_width="match_parent"-->
|
||||||
android:layout_height="wrap_content"
|
<!-- android:layout_height="wrap_content"-->
|
||||||
android:background="?attr/colorSurface"
|
<!-- android:background="?attr/colorSurface"-->
|
||||||
app:elevation="0dp">
|
<!-- app:elevation="0dp">-->
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<!-- <com.google.android.material.appbar.CollapsingToolbarLayout-->
|
||||||
android:layout_width="match_parent"
|
<!-- android:layout_width="match_parent"-->
|
||||||
android:layout_height="wrap_content"
|
<!-- android:layout_height="wrap_content"-->
|
||||||
app:layout_scrollFlags="scroll|snap|enterAlways">
|
<!-- app:layout_scrollFlags="scroll|snap|enterAlways">-->
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatSpinner
|
<!-- <androidx.appcompat.widget.AppCompatSpinner-->
|
||||||
android:id="@+id/discoverType"
|
<!-- android:id="@+id/discoverType"-->
|
||||||
android:layout_width="match_parent"
|
<!-- android:layout_width="match_parent"-->
|
||||||
android:layout_height="wrap_content"
|
<!-- android:layout_height="wrap_content"-->
|
||||||
android:entries="@array/discover_placeholder"
|
<!-- android:entries="@array/discover_placeholder"-->
|
||||||
android:spinnerMode="dialog" />
|
<!-- android:spinnerMode="dialog" />-->
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
<!-- </com.google.android.material.appbar.CollapsingToolbarLayout>-->
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
<!--</com.google.android.material.appbar.AppBarLayout>-->
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/discoverSwipeRefreshLayout"
|
android:id="@+id/swipe_refresh_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/discoverPosts"
|
android:id="@+id/topics_recycler_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
tools:listitem="@layout/item_post" />
|
app:spanCount="2"
|
||||||
|
tools:itemCount="10"
|
||||||
|
tools:listitem="@layout/item_discover_topic" />
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>
|
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>
|
@ -285,20 +285,6 @@
|
|||||||
tools:listitem="@layout/item_feed_photo" />
|
tools:listitem="@layout/item_feed_photo" />
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</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
|
<LinearLayout
|
||||||
android:id="@+id/privatePage"
|
android:id="@+id/privatePage"
|
||||||
android:layout_width="match_parent"
|
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"
|
android:id="@+id/discover_nav_graph"
|
||||||
app:startDestination="@id/discoverFragment">
|
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/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
|
<action
|
||||||
android:id="@+id/action_global_postViewFragment"
|
android:id="@+id/action_global_postViewFragment"
|
||||||
@ -23,9 +54,48 @@
|
|||||||
app:argType="boolean" />
|
app:argType="boolean" />
|
||||||
</action>
|
</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
|
<fragment
|
||||||
android:id="@+id/discoverFragment"
|
android:id="@+id/discoverFragment"
|
||||||
android:name="awais.instagrabber.fragments.main.DiscoverFragment"
|
android:name="awais.instagrabber.fragments.main.DiscoverFragment"
|
||||||
android:label="@string/title_discover"
|
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>
|
</navigation>
|
Loading…
Reference in New Issue
Block a user