mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-26 08:37:29 +00:00
Add PostsRecyclerView to ProfileFragment
This commit is contained in:
parent
9b83c5e832
commit
0a67e859e0
@ -5,7 +5,7 @@ import java.util.List;
|
|||||||
import awais.instagrabber.customviews.helpers.PostFetcher;
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.repositories.responses.FeedFetchResponse;
|
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||||
import awais.instagrabber.webservices.FeedService;
|
import awais.instagrabber.webservices.FeedService;
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
@ -21,9 +21,9 @@ public class FeedPostFetchService implements PostFetcher.PostFetchService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fetch(final String cursor, final FetchListener<List<FeedModel>> fetchListener) {
|
public void fetch(final String cursor, final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
feedService.fetch(25, cursor, new ServiceCallback<FeedFetchResponse>() {
|
feedService.fetch(25, cursor, new ServiceCallback<PostsFetchResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final FeedFetchResponse result) {
|
public void onSuccess(final PostsFetchResponse result) {
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
nextCursor = result.getNextCursor();
|
nextCursor = result.getNextCursor();
|
||||||
hasNextPage = result.hasNextPage();
|
hasNextPage = result.hasNextPage();
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
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.models.ProfileModel;
|
||||||
|
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||||
|
import awais.instagrabber.webservices.ProfileService;
|
||||||
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
|
public class ProfilePostFetchService implements PostFetcher.PostFetchService {
|
||||||
|
private static final String TAG = "ProfilePostFetchService";
|
||||||
|
private final ProfileService profileService;
|
||||||
|
private final ProfileModel profileModel;
|
||||||
|
private String nextCursor;
|
||||||
|
private boolean hasNextPage;
|
||||||
|
|
||||||
|
public ProfilePostFetchService(final ProfileModel profileModel) {
|
||||||
|
this.profileModel = profileModel;
|
||||||
|
profileService = ProfileService.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fetch(final String cursor, final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
|
profileService.fetchPosts(profileModel, 30, cursor, new ServiceCallback<PostsFetchResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final PostsFetchResponse result) {
|
||||||
|
if (result == null) return;
|
||||||
|
nextCursor = result.getNextCursor();
|
||||||
|
hasNextPage = result.hasNextPage();
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onResult(result.getFeedModels());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
// Log.e(TAG, "onFailure: ", t);
|
||||||
|
if (fetchListener != null) {
|
||||||
|
fetchListener.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNextCursor() {
|
||||||
|
return nextCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNextPage() {
|
||||||
|
return hasNextPage;
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,6 @@ import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtBottom;
|
|||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.FeedViewModel;
|
import awais.instagrabber.viewmodels.FeedViewModel;
|
||||||
|
|
||||||
@ -130,16 +129,8 @@ public class PostsRecyclerView extends RecyclerView {
|
|||||||
throw new IllegalArgumentException("PostFetchService cannot be null");
|
throw new IllegalArgumentException("PostFetchService cannot be null");
|
||||||
}
|
}
|
||||||
if (layoutPreferences == null) {
|
if (layoutPreferences == null) {
|
||||||
layoutPreferences = PostsLayoutPreferences.builder()
|
layoutPreferences = PostsLayoutPreferences.builder().build();
|
||||||
.setType(PostsLayoutPreferences.PostsLayoutType.GRID)
|
// Utils.settingsHelper.putString(Constants.PREF_POSTS_LAYOUT, layoutPreferences.getJson());
|
||||||
.setColCount(3)
|
|
||||||
.setAvatarVisible(true)
|
|
||||||
.setNameVisible(false)
|
|
||||||
.setProfilePicSize(PostsLayoutPreferences.ProfilePicSize.TINY)
|
|
||||||
.setHasGap(true)
|
|
||||||
.setHasRoundedCorners(true)
|
|
||||||
.build();
|
|
||||||
Utils.settingsHelper.putString(Constants.PREF_POSTS_LAYOUT, layoutPreferences.getJson());
|
|
||||||
}
|
}
|
||||||
gridSpacingItemDecoration = new GridSpacingItemDecoration(Utils.convertDpToPx(2));
|
gridSpacingItemDecoration = new GridSpacingItemDecoration(Utils.convertDpToPx(2));
|
||||||
initTransition();
|
initTransition();
|
||||||
@ -156,9 +147,6 @@ public class PostsRecyclerView extends RecyclerView {
|
|||||||
|
|
||||||
private void initLayoutManager() {
|
private void initLayoutManager() {
|
||||||
layoutManager = new StaggeredGridLayoutManager(layoutPreferences.getColCount(), StaggeredGridLayoutManager.VERTICAL);
|
layoutManager = new StaggeredGridLayoutManager(layoutPreferences.getColCount(), StaggeredGridLayoutManager.VERTICAL);
|
||||||
if (layoutPreferences.getHasGap()) {
|
|
||||||
addItemDecoration(gridSpacingItemDecoration);
|
|
||||||
}
|
|
||||||
setLayoutManager(layoutManager);
|
setLayoutManager(layoutManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +160,9 @@ public class PostsRecyclerView extends RecyclerView {
|
|||||||
feedViewModel = new ViewModelProvider(viewModelStoreOwner).get(FeedViewModel.class);
|
feedViewModel = new ViewModelProvider(viewModelStoreOwner).get(FeedViewModel.class);
|
||||||
feedViewModel.getList().observe(lifeCycleOwner, feedAdapter::submitList);
|
feedViewModel.getList().observe(lifeCycleOwner, feedAdapter::submitList);
|
||||||
postFetcher = new PostFetcher(postFetchService, fetchListener);
|
postFetcher = new PostFetcher(postFetchService, fetchListener);
|
||||||
addItemDecoration(gridSpacingItemDecoration);
|
if (layoutPreferences.getHasGap()) {
|
||||||
|
addItemDecoration(gridSpacingItemDecoration);
|
||||||
|
}
|
||||||
setHasFixedSize(true);
|
setHasFixedSize(true);
|
||||||
setNestedScrollingEnabled(true);
|
setNestedScrollingEnabled(true);
|
||||||
lazyLoader = new RecyclerLazyLoaderAtBottom(layoutManager, (page) -> {
|
lazyLoader = new RecyclerLazyLoaderAtBottom(layoutManager, (page) -> {
|
||||||
|
@ -16,20 +16,21 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.databinding.DialogPostLayoutPreferencesBinding;
|
import awais.instagrabber.databinding.DialogPostLayoutPreferencesBinding;
|
||||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
|
public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private final PostsLayoutPreferences.Builder preferencesBuilder;
|
|
||||||
@NonNull
|
|
||||||
private final OnApplyListener onApplyListener;
|
private final OnApplyListener onApplyListener;
|
||||||
|
private final PostsLayoutPreferences.Builder preferencesBuilder;
|
||||||
|
private final String layoutPreferenceKey;
|
||||||
private DialogPostLayoutPreferencesBinding binding;
|
private DialogPostLayoutPreferencesBinding binding;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
public PostsLayoutPreferencesDialogFragment(@NonNull final OnApplyListener onApplyListener) {
|
public PostsLayoutPreferencesDialogFragment(final String layoutPreferenceKey,
|
||||||
final PostsLayoutPreferences preferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_POSTS_LAYOUT));
|
@NonNull final OnApplyListener onApplyListener) {
|
||||||
|
this.layoutPreferenceKey = layoutPreferenceKey;
|
||||||
|
final PostsLayoutPreferences preferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(layoutPreferenceKey));
|
||||||
this.preferencesBuilder = PostsLayoutPreferences.builder().mergeFrom(preferences);
|
this.preferencesBuilder = PostsLayoutPreferences.builder().mergeFrom(preferences);
|
||||||
this.onApplyListener = onApplyListener;
|
this.onApplyListener = onApplyListener;
|
||||||
}
|
}
|
||||||
@ -50,7 +51,7 @@ public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
|
|||||||
.setPositiveButton(R.string.apply, (dialog, which) -> {
|
.setPositiveButton(R.string.apply, (dialog, which) -> {
|
||||||
final PostsLayoutPreferences preferences = preferencesBuilder.build();
|
final PostsLayoutPreferences preferences = preferencesBuilder.build();
|
||||||
final String json = preferences.getJson();
|
final String json = preferences.getJson();
|
||||||
settingsHelper.putString(Constants.PREF_POSTS_LAYOUT, json);
|
settingsHelper.putString(layoutPreferenceKey, json);
|
||||||
onApplyListener.onApply(preferences);
|
onApplyListener.onApply(preferences);
|
||||||
})
|
})
|
||||||
.create();
|
.create();
|
||||||
|
@ -417,7 +417,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
wasPaused = true;
|
wasPaused = true;
|
||||||
captionState = bottomSheetBehavior.getState();
|
if (bottomSheetBehavior != null) {
|
||||||
|
captionState = bottomSheetBehavior.getState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -494,7 +496,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
|||||||
binding.postImage.setLayoutParams(new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
binding.postImage.setLayoutParams(new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
binding.postImage.requestLayout();
|
binding.postImage.requestLayout();
|
||||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
if (bottomSheetBehavior != null) {
|
||||||
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (destView == binding.sliderParent) {
|
if (destView == binding.sliderParent) {
|
||||||
@ -778,6 +782,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment {
|
|||||||
|
|
||||||
private void setupCaption() {
|
private void setupCaption() {
|
||||||
final CharSequence postCaption = feedModel.getPostCaption();
|
final CharSequence postCaption = feedModel.getPostCaption();
|
||||||
|
if (TextUtils.isEmpty(postCaption)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
binding.caption.addOnHashtagListener(autoLinkItem -> {
|
binding.caption.addOnHashtagListener(autoLinkItem -> {
|
||||||
final NavController navController = NavHostFragment.findNavController(this);
|
final NavController navController = NavHostFragment.findNavController(this);
|
||||||
final Bundle bundle = new Bundle();
|
final Bundle bundle = new Bundle();
|
||||||
|
@ -331,7 +331,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showPostsLayoutPreferences() {
|
private void showPostsLayoutPreferences() {
|
||||||
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(preferences -> new Handler()
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(Constants.PREF_POSTS_LAYOUT, preferences -> new Handler()
|
||||||
.postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));
|
.postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200));
|
||||||
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||||
}
|
}
|
||||||
|
@ -24,16 +24,17 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
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.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
|
import androidx.core.content.PermissionChecker;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
@ -46,7 +47,6 @@ import com.facebook.imagepipeline.image.ImageInfo;
|
|||||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -54,24 +54,25 @@ import java.util.List;
|
|||||||
import awais.instagrabber.ProfileNavGraphDirections;
|
import awais.instagrabber.ProfileNavGraphDirections;
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.activities.MainActivity;
|
import awais.instagrabber.activities.MainActivity;
|
||||||
|
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||||
import awais.instagrabber.adapters.HighlightsAdapter;
|
import awais.instagrabber.adapters.HighlightsAdapter;
|
||||||
import awais.instagrabber.adapters.PostsAdapter;
|
import awais.instagrabber.adapters.PostsAdapter;
|
||||||
import awais.instagrabber.asyncs.HighlightsFetcher;
|
import awais.instagrabber.asyncs.HighlightsFetcher;
|
||||||
import awais.instagrabber.asyncs.PostsFetcher;
|
|
||||||
import awais.instagrabber.asyncs.ProfileFetcher;
|
import awais.instagrabber.asyncs.ProfileFetcher;
|
||||||
|
import awais.instagrabber.asyncs.ProfilePostFetchService;
|
||||||
import awais.instagrabber.asyncs.UsernameFetcher;
|
import awais.instagrabber.asyncs.UsernameFetcher;
|
||||||
import awais.instagrabber.asyncs.direct_messages.CreateThreadAction;
|
import awais.instagrabber.asyncs.direct_messages.CreateThreadAction;
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||||
import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper;
|
import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper;
|
||||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
|
|
||||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
|
||||||
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
|
|
||||||
import awais.instagrabber.databinding.FragmentProfileBinding;
|
import awais.instagrabber.databinding.FragmentProfileBinding;
|
||||||
|
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
|
||||||
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
|
||||||
|
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
import awais.instagrabber.interfaces.MentionClickListener;
|
import awais.instagrabber.interfaces.MentionClickListener;
|
||||||
import awais.instagrabber.models.PostModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||||
import awais.instagrabber.models.ProfileModel;
|
import awais.instagrabber.models.ProfileModel;
|
||||||
import awais.instagrabber.models.StoryModel;
|
import awais.instagrabber.models.StoryModel;
|
||||||
import awais.instagrabber.models.enums.DownloadMethod;
|
import awais.instagrabber.models.enums.DownloadMethod;
|
||||||
@ -86,17 +87,17 @@ import awais.instagrabber.utils.DownloadUtils;
|
|||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.HighlightsViewModel;
|
import awais.instagrabber.viewmodels.HighlightsViewModel;
|
||||||
import awais.instagrabber.viewmodels.PostsViewModel;
|
|
||||||
import awais.instagrabber.webservices.FriendshipService;
|
import awais.instagrabber.webservices.FriendshipService;
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
import awais.instagrabber.webservices.StoriesService;
|
import awais.instagrabber.webservices.StoriesService;
|
||||||
import awaisomereport.LogCollector;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.Utils.logCollector;
|
import static androidx.core.content.PermissionChecker.checkSelfPermission;
|
||||||
|
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
|
||||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
|
||||||
private static final String TAG = "ProfileFragment";
|
private static final String TAG = "ProfileFragment";
|
||||||
|
private static final int STORAGE_PERM_REQUEST_CODE = 8020;
|
||||||
|
|
||||||
private MainActivity fragmentActivity;
|
private MainActivity fragmentActivity;
|
||||||
private CoordinatorLayout root;
|
private CoordinatorLayout root;
|
||||||
@ -105,21 +106,18 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
private String cookie;
|
private String cookie;
|
||||||
private String username;
|
private String username;
|
||||||
private ProfileModel profileModel;
|
private ProfileModel profileModel;
|
||||||
private PostsViewModel postsViewModel;
|
|
||||||
private PostsAdapter postsAdapter;
|
private PostsAdapter postsAdapter;
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
private Handler usernameSettingHandler;
|
private Handler usernameSettingHandler;
|
||||||
private FriendshipService friendshipService;
|
private FriendshipService friendshipService;
|
||||||
private StoriesService storiesService;
|
private StoriesService storiesService;
|
||||||
private boolean shouldRefresh = true, hasStories = false;
|
private boolean shouldRefresh = true;
|
||||||
private boolean hasNextPage;
|
private boolean hasStories = false;
|
||||||
private String endCursor;
|
|
||||||
private AsyncTask<Void, Void, List<PostModel>> currentlyExecuting;
|
|
||||||
private boolean isPullToRefresh;
|
|
||||||
private HighlightsAdapter highlightsAdapter;
|
private HighlightsAdapter highlightsAdapter;
|
||||||
private HighlightsViewModel highlightsViewModel;
|
private HighlightsViewModel highlightsViewModel;
|
||||||
private MenuItem blockMenuItem;
|
private MenuItem blockMenuItem;
|
||||||
private MenuItem restrictMenuItem;
|
private MenuItem restrictMenuItem;
|
||||||
|
private boolean highlightsFetching;
|
||||||
|
|
||||||
private final Runnable usernameSettingRunnable = () -> {
|
private final Runnable usernameSettingRunnable = () -> {
|
||||||
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
|
||||||
@ -165,36 +163,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
private final FetchListener<List<PostModel>> postsFetchListener = new FetchListener<List<PostModel>>() {
|
|
||||||
@Override
|
|
||||||
public void onResult(final List<PostModel> result) {
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(false);
|
|
||||||
if (result == null || result.isEmpty()) {
|
|
||||||
binding.privatePage1.setImageResource(R.drawable.ic_cancel);
|
|
||||||
binding.privatePage2.setText(R.string.empty_acc);
|
|
||||||
binding.privatePage.setVisibility(View.VISIBLE);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
binding.privatePage.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
|
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
|
||||||
List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>()
|
|
||||||
: new ArrayList<>(postModels);
|
|
||||||
if (isPullToRefresh) {
|
|
||||||
finalList = result;
|
|
||||||
isPullToRefresh = false;
|
|
||||||
} else {
|
|
||||||
finalList.addAll(result);
|
|
||||||
}
|
|
||||||
postsViewModel.getList().postValue(finalList);
|
|
||||||
final PostModel lastPostModel = result.get(result.size() - 1);
|
|
||||||
if (lastPostModel == null) return;
|
|
||||||
endCursor = lastPostModel.getEndCursor();
|
|
||||||
hasNextPage = lastPostModel.hasNextPage();
|
|
||||||
lastPostModel.setPageCursor(false, null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
|
private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
|
||||||
Log.d(TAG, "action...");
|
Log.d(TAG, "action...");
|
||||||
if (isHashtag) {
|
if (isHashtag) {
|
||||||
@ -213,6 +181,92 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
action.setUsername("@" + text);
|
action.setUsername("@" + text);
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
NavHostFragment.findNavController(this).navigate(action);
|
||||||
};
|
};
|
||||||
|
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 = FeedFragmentDirections.actionGlobalCommentsViewerFragment(
|
||||||
|
feedModel.getShortCode(),
|
||||||
|
feedModel.getPostId(),
|
||||||
|
feedModel.getProfileModel().getId()
|
||||||
|
);
|
||||||
|
NavHostFragment.findNavController(ProfileFragment.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 = FeedFragmentDirections.actionGlobalHashTagFragment(hashtag);
|
||||||
|
NavHostFragment.findNavController(ProfileFragment.this).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationClick(final FeedModel feedModel) {
|
||||||
|
final NavDirections action = FeedFragmentDirections.actionGlobalLocationFragment(feedModel.getLocationId());
|
||||||
|
NavHostFragment.findNavController(ProfileFragment.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");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private boolean postsSetupDone = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
@ -267,7 +321,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.profile_menu, menu);
|
inflater.inflate(R.menu.profile_menu, menu);
|
||||||
// favMenuItem = menu.findItem(R.id.favourites);
|
|
||||||
blockMenuItem = menu.findItem(R.id.block);
|
blockMenuItem = menu.findItem(R.id.block);
|
||||||
if (blockMenuItem != null) {
|
if (blockMenuItem != null) {
|
||||||
blockMenuItem.setVisible(false);
|
blockMenuItem.setVisible(false);
|
||||||
@ -280,6 +333,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.layout) {
|
||||||
|
showPostsLayoutPreferences();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (item.getItemId() == R.id.restrict) {
|
if (item.getItemId() == R.id.restrict) {
|
||||||
if (!isLoggedIn) return false;
|
if (!isLoggedIn) return false;
|
||||||
final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
|
final String action = profileModel.getRestricted() ? "Unrestrict" : "Restrict";
|
||||||
@ -346,10 +403,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
isPullToRefresh = true;
|
|
||||||
endCursor = null;
|
|
||||||
fetchProfileDetails();
|
fetchProfileDetails();
|
||||||
fetchPosts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -359,9 +413,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
if (usernameSettingHandler != null) {
|
if (usernameSettingHandler != null) {
|
||||||
usernameSettingHandler.removeCallbacks(usernameSettingRunnable);
|
usernameSettingHandler.removeCallbacks(usernameSettingRunnable);
|
||||||
}
|
}
|
||||||
if (postsViewModel != null) {
|
// if (postsViewModel != null) {
|
||||||
postsViewModel.getList().postValue(Collections.emptyList());
|
// postsViewModel.getList().postValue(Collections.emptyList());
|
||||||
}
|
// }
|
||||||
if (highlightsViewModel != null) {
|
if (highlightsViewModel != null) {
|
||||||
highlightsViewModel.getList().postValue(Collections.emptyList());
|
highlightsViewModel.getList().postValue(Collections.emptyList());
|
||||||
}
|
}
|
||||||
@ -385,7 +439,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
binding.swipeRefreshLayout.setEnabled(true);
|
binding.swipeRefreshLayout.setEnabled(true);
|
||||||
setupPosts();
|
|
||||||
setupHighlights();
|
setupHighlights();
|
||||||
setupCommonListeners();
|
setupCommonListeners();
|
||||||
fetchUsername();
|
fetchUsername();
|
||||||
@ -444,87 +497,18 @@ public class ProfileFragment 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;
|
||||||
}
|
}
|
||||||
|
if (!postsSetupDone) {
|
||||||
|
setupPosts();
|
||||||
|
} else {
|
||||||
|
binding.postsRecyclerView.refresh();
|
||||||
|
}
|
||||||
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
|
||||||
final String profileId = profileModel.getId();
|
final String profileId = profileModel.getId();
|
||||||
|
|
||||||
final String myId = CookieUtils.getUserIdFromCookie(cookie);
|
final String myId = CookieUtils.getUserIdFromCookie(cookie);
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
storiesService.getUserStory(profileId,
|
fetchStoryAndHighlights(profileId);
|
||||||
profileModel.getUsername(),
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
new ServiceCallback<List<StoryModel>>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final List<StoryModel> storyModels) {
|
|
||||||
if (storyModels != null && !storyModels.isEmpty()) {
|
|
||||||
binding.mainProfileImage.setStoriesBorder();
|
|
||||||
hasStories = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
Log.e(TAG, "Error", t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
new HighlightsFetcher(profileId,
|
|
||||||
result -> {
|
|
||||||
if (result != null) {
|
|
||||||
binding.highlightsList.setVisibility(View.VISIBLE);
|
|
||||||
highlightsViewModel.getList().postValue(result);
|
|
||||||
} else binding.highlightsList.setVisibility(View.GONE);
|
|
||||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
||||||
if (profileId.equals(myId)) {
|
|
||||||
binding.btnTagged.setVisibility(View.VISIBLE);
|
|
||||||
binding.btnSaved.setVisibility(View.VISIBLE);
|
|
||||||
binding.btnLiked.setVisibility(View.VISIBLE);
|
|
||||||
binding.btnDM.setVisibility(View.GONE);
|
|
||||||
binding.btnSaved.setText(R.string.saved);
|
|
||||||
} else {
|
|
||||||
binding.btnTagged.setVisibility(View.GONE);
|
|
||||||
binding.btnSaved.setVisibility(View.GONE);
|
|
||||||
binding.btnLiked.setVisibility(View.GONE);
|
|
||||||
binding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism?
|
|
||||||
binding.btnFollow.setVisibility(View.VISIBLE);
|
|
||||||
if (profileModel.getFollowing()) {
|
|
||||||
binding.btnFollow.setText(R.string.unfollow);
|
|
||||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
|
||||||
} else if (profileModel.getRequested()) {
|
|
||||||
binding.btnFollow.setText(R.string.cancel);
|
|
||||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
|
||||||
} else {
|
|
||||||
binding.btnFollow.setText(R.string.follow);
|
|
||||||
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_24);
|
|
||||||
}
|
|
||||||
if (restrictMenuItem != null) {
|
|
||||||
restrictMenuItem.setVisible(true);
|
|
||||||
if (profileModel.getRestricted()) {
|
|
||||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
|
||||||
} else {
|
|
||||||
restrictMenuItem.setTitle(R.string.restrict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
|
|
||||||
if (blockMenuItem != null) {
|
|
||||||
blockMenuItem.setVisible(true);
|
|
||||||
if (profileModel.getBlocked()) {
|
|
||||||
blockMenuItem.setTitle(R.string.unblock);
|
|
||||||
} else {
|
|
||||||
blockMenuItem.setTitle(R.string.block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!profileModel.isReallyPrivate() && restrictMenuItem != null) {
|
|
||||||
restrictMenuItem.setVisible(true);
|
|
||||||
if (profileModel.getRestricted()) {
|
|
||||||
restrictMenuItem.setTitle(R.string.unrestrict);
|
|
||||||
} else {
|
|
||||||
restrictMenuItem.setTitle(R.string.restrict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
setupButtons(profileId, myId);
|
||||||
if (!profileId.equals(myId)) {
|
if (!profileId.equals(myId)) {
|
||||||
binding.favCb.setVisibility(View.VISIBLE);
|
binding.favCb.setVisibility(View.VISIBLE);
|
||||||
final boolean isFav = Utils.dataBox.getFavorite(username.substring(1), FavoriteType.USER) != null;
|
final boolean isFav = Utils.dataBox.getFavorite(username.substring(1), FavoriteType.USER) != null;
|
||||||
@ -572,8 +556,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
: profileModel.getName());
|
: profileModel.getName());
|
||||||
|
|
||||||
CharSequence biography = profileModel.getBiography();
|
CharSequence biography = profileModel.getBiography();
|
||||||
// binding.mainBiography.setCaptionIsExpandable(true);
|
|
||||||
// binding.mainBiography.setCaptionIsExpanded(true);
|
|
||||||
if (TextUtils.hasMentions(biography)) {
|
if (TextUtils.hasMentions(biography)) {
|
||||||
biography = TextUtils.getMentionText(biography);
|
biography = TextUtils.getMentionText(biography);
|
||||||
binding.mainBiography.setText(biography, TextView.BufferType.SPANNABLE);
|
binding.mainBiography.setText(biography, TextView.BufferType.SPANNABLE);
|
||||||
@ -618,7 +600,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
binding.mainPosts.setVisibility(View.VISIBLE);
|
binding.postsRecyclerView.setVisibility(View.VISIBLE);
|
||||||
fetchPosts();
|
fetchPosts();
|
||||||
} else {
|
} else {
|
||||||
binding.mainFollowers.setClickable(false);
|
binding.mainFollowers.setClickable(false);
|
||||||
@ -628,10 +610,94 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
binding.privatePage1.setImageResource(R.drawable.lock);
|
binding.privatePage1.setImageResource(R.drawable.lock);
|
||||||
binding.privatePage2.setText(R.string.priv_acc);
|
binding.privatePage2.setText(R.string.priv_acc);
|
||||||
binding.privatePage.setVisibility(View.VISIBLE);
|
binding.privatePage.setVisibility(View.VISIBLE);
|
||||||
binding.mainPosts.setVisibility(View.GONE);
|
binding.postsRecyclerView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupButtons(final String profileId, final String myId) {
|
||||||
|
if (isLoggedIn) {
|
||||||
|
if (profileId.equals(myId)) {
|
||||||
|
binding.btnTagged.setVisibility(View.VISIBLE);
|
||||||
|
binding.btnSaved.setVisibility(View.VISIBLE);
|
||||||
|
binding.btnLiked.setVisibility(View.VISIBLE);
|
||||||
|
binding.btnDM.setVisibility(View.GONE);
|
||||||
|
binding.btnSaved.setText(R.string.saved);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.btnTagged.setVisibility(View.GONE);
|
||||||
|
binding.btnSaved.setVisibility(View.GONE);
|
||||||
|
binding.btnLiked.setVisibility(View.GONE);
|
||||||
|
binding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism?
|
||||||
|
binding.btnFollow.setVisibility(View.VISIBLE);
|
||||||
|
if (profileModel.getFollowing()) {
|
||||||
|
binding.btnFollow.setText(R.string.unfollow);
|
||||||
|
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||||
|
} else if (profileModel.getRequested()) {
|
||||||
|
binding.btnFollow.setText(R.string.cancel);
|
||||||
|
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24);
|
||||||
|
} else {
|
||||||
|
binding.btnFollow.setText(R.string.follow);
|
||||||
|
binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_24);
|
||||||
|
}
|
||||||
|
if (restrictMenuItem != null) {
|
||||||
|
restrictMenuItem.setVisible(true);
|
||||||
|
if (profileModel.getRestricted()) {
|
||||||
|
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||||
|
} else {
|
||||||
|
restrictMenuItem.setTitle(R.string.restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE);
|
||||||
|
if (blockMenuItem != null) {
|
||||||
|
blockMenuItem.setVisible(true);
|
||||||
|
if (profileModel.getBlocked()) {
|
||||||
|
blockMenuItem.setTitle(R.string.unblock);
|
||||||
|
} else {
|
||||||
|
blockMenuItem.setTitle(R.string.block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!profileModel.isReallyPrivate() && restrictMenuItem != null) {
|
||||||
|
restrictMenuItem.setVisible(true);
|
||||||
|
if (profileModel.getRestricted()) {
|
||||||
|
restrictMenuItem.setTitle(R.string.unrestrict);
|
||||||
|
} else {
|
||||||
|
restrictMenuItem.setTitle(R.string.restrict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchStoryAndHighlights(final String profileId) {
|
||||||
|
storiesService.getUserStory(profileId,
|
||||||
|
profileModel.getUsername(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
new ServiceCallback<List<StoryModel>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final List<StoryModel> storyModels) {
|
||||||
|
if (storyModels != null && !storyModels.isEmpty()) {
|
||||||
|
binding.mainProfileImage.setStoriesBorder();
|
||||||
|
hasStories = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Throwable t) {
|
||||||
|
Log.e(TAG, "Error", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
new HighlightsFetcher(profileId,
|
||||||
|
result -> {
|
||||||
|
highlightsFetching = false;
|
||||||
|
if (result != null) {
|
||||||
|
binding.highlightsList.setVisibility(View.VISIBLE);
|
||||||
|
highlightsViewModel.getList().postValue(result);
|
||||||
|
} else binding.highlightsList.setVisibility(View.GONE);
|
||||||
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
private void setupCommonListeners() {
|
private void setupCommonListeners() {
|
||||||
|
|
||||||
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie);
|
||||||
@ -781,62 +847,60 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupPosts() {
|
private void setupPosts() {
|
||||||
postsViewModel = new ViewModelProvider(this).get(PostsViewModel.class);
|
binding.postsRecyclerView.setViewModelStoreOwner(this)
|
||||||
final Context context = getContext();
|
.setLifeCycleOwner(this)
|
||||||
if (context == null) return;
|
.setPostFetchService(new ProfilePostFetchService(profileModel))
|
||||||
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110));
|
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_PROFILE_POSTS_LAYOUT)))
|
||||||
binding.mainPosts.setLayoutManager(layoutManager);
|
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||||
binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
|
.setFeedItemCallback(feedItemCallback)
|
||||||
postsAdapter = new PostsAdapter((postModel, position) -> {
|
.init();
|
||||||
if (postsAdapter.isSelecting()) {
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
if (actionMode == null) return;
|
postsSetupDone = true;
|
||||||
final String title = getString(R.string.number_selected,
|
// postsAdapter = new PostsAdapter((postModel, position) -> {
|
||||||
postsAdapter.getSelectedModels().size());
|
// if (postsAdapter.isSelecting()) {
|
||||||
actionMode.setTitle(title);
|
// if (actionMode == null) return;
|
||||||
return;
|
// final String title = getString(R.string.number_selected,
|
||||||
}
|
// postsAdapter.getSelectedModels().size());
|
||||||
if (checkAndResetAction()) return;
|
// actionMode.setTitle(title);
|
||||||
final List<PostModel> postModels = postsViewModel.getList().getValue();
|
// return;
|
||||||
if (postModels == null || postModels.size() == 0) return;
|
// }
|
||||||
if (postModels.get(0) == null) return;
|
// if (checkAndResetAction()) return;
|
||||||
final String postId = isLoggedIn ? postModels.get(0).getPostId() : postModels.get(0).getShortCode();
|
// final List<PostModel> postModels = postsViewModel.getList().getValue();
|
||||||
final boolean isId = isLoggedIn && postId != null;
|
// if (postModels == null || postModels.size() == 0) return;
|
||||||
final String[] idsOrShortCodes = new String[postModels.size()];
|
// if (postModels.get(0) == null) return;
|
||||||
for (int i = 0; i < postModels.size(); i++) {
|
// final String postId = isLoggedIn ? postModels.get(0).getPostId() : postModels.get(0).getShortCode();
|
||||||
idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
|
// final boolean isId = isLoggedIn && postId != null;
|
||||||
: postModels.get(i).getShortCode();
|
// final String[] idsOrShortCodes = new String[postModels.size()];
|
||||||
}
|
// for (int i = 0; i < postModels.size(); i++) {
|
||||||
final NavDirections action = ProfileFragmentDirections.actionGlobalPostViewFragment(
|
// idsOrShortCodes[i] = isId ? postModels.get(i).getPostId()
|
||||||
position,
|
// : postModels.get(i).getShortCode();
|
||||||
idsOrShortCodes,
|
// }
|
||||||
isId);
|
// final NavDirections action = ProfileFragmentDirections.actionGlobalPostViewFragment(
|
||||||
NavHostFragment.findNavController(this).navigate(action);
|
// position,
|
||||||
|
// idsOrShortCodes,
|
||||||
|
// isId);
|
||||||
|
// NavHostFragment.findNavController(this).navigate(action);
|
||||||
|
//
|
||||||
|
// }, (model, position) -> {
|
||||||
|
// if (!postsAdapter.isSelecting()) {
|
||||||
|
// checkAndResetAction();
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// if (onBackPressedCallback.isEnabled()) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
||||||
|
// onBackPressedCallback.setEnabled(true);
|
||||||
|
// actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
||||||
|
// final String title = getString(R.string.number_selected, 1);
|
||||||
|
// actionMode.setTitle(title);
|
||||||
|
// onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
||||||
|
// return true;
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
}, (model, position) -> {
|
private void updateSwipeRefreshState() {
|
||||||
if (!postsAdapter.isSelecting()) {
|
binding.swipeRefreshLayout.setRefreshing(binding.postsRecyclerView.isFetching() || highlightsFetching);
|
||||||
checkAndResetAction();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (onBackPressedCallback.isEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
|
|
||||||
onBackPressedCallback.setEnabled(true);
|
|
||||||
actionMode = fragmentActivity.startActionMode(multiSelectAction);
|
|
||||||
final String title = getString(R.string.number_selected, 1);
|
|
||||||
actionMode.setTitle(title);
|
|
||||||
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
|
|
||||||
binding.mainPosts.setAdapter(postsAdapter);
|
|
||||||
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
|
|
||||||
if (!hasNextPage) return;
|
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
|
||||||
fetchPosts();
|
|
||||||
endCursor = null;
|
|
||||||
});
|
|
||||||
binding.mainPosts.addOnScrollListener(lazyLoader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupHighlights() {
|
private void setupHighlights() {
|
||||||
@ -855,22 +919,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void fetchPosts() {
|
private void fetchPosts() {
|
||||||
stopCurrentExecutor();
|
// stopCurrentExecutor();
|
||||||
binding.swipeRefreshLayout.setRefreshing(true);
|
binding.swipeRefreshLayout.setRefreshing(true);
|
||||||
currentlyExecuting = new PostsFetcher(profileModel.getId(), PostItemType.MAIN, endCursor, postsFetchListener)
|
// currentlyExecuting = new PostsFetcher(profileModel.getId(), PostItemType.MAIN, endCursor, postsFetchListener)
|
||||||
.setUsername(profileModel.getUsername())
|
// .setUsername(profileModel.getUsername())
|
||||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
// .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
|
||||||
|
|
||||||
public void stopCurrentExecutor() {
|
|
||||||
if (currentlyExecuting != null) {
|
|
||||||
try {
|
|
||||||
currentlyExecuting.cancel(true);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
if (logCollector != null) logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor");
|
|
||||||
Log.e(TAG, "", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAndResetAction() {
|
private boolean checkAndResetAction() {
|
||||||
@ -887,4 +940,60 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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 showPostsLayoutPreferences() {
|
||||||
|
final PostsLayoutPreferencesDialogFragment fragment = new PostsLayoutPreferencesDialogFragment(
|
||||||
|
Constants.PREF_PROFILE_POSTS_LAYOUT,
|
||||||
|
preferences -> new Handler().postDelayed(() -> binding.postsRecyclerView.setLayoutPreferences(preferences), 200));
|
||||||
|
fragment.show(getChildFragmentManager(), "posts_layout_preferences");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@ public final class PostsLayoutPreferences {
|
|||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private PostsLayoutType type = PostsLayoutType.GRID;
|
private PostsLayoutType type = PostsLayoutType.GRID;
|
||||||
private int colCount = 2;
|
private int colCount = 3;
|
||||||
private boolean isAvatarVisible = false;
|
private boolean isAvatarVisible = true;
|
||||||
private boolean isNameVisible = false;
|
private boolean isNameVisible = false;
|
||||||
private ProfilePicSize profilePicSize = ProfilePicSize.REGULAR;
|
private ProfilePicSize profilePicSize = ProfilePicSize.SMALL;
|
||||||
private boolean hasRoundedCorners = true;
|
private boolean hasRoundedCorners = true;
|
||||||
private boolean hasGap = true;
|
private boolean hasGap = true;
|
||||||
|
|
||||||
@ -87,6 +87,9 @@ public final class PostsLayoutPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Builder mergeFrom(final PostsLayoutPreferences preferences) {
|
public Builder mergeFrom(final PostsLayoutPreferences preferences) {
|
||||||
|
if (preferences == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
setColCount(preferences.getColCount());
|
setColCount(preferences.getColCount());
|
||||||
setAvatarVisible(preferences.isAvatarVisible());
|
setAvatarVisible(preferences.isAvatarVisible());
|
||||||
setNameVisible(preferences.isNameVisible());
|
setNameVisible(preferences.isNameVisible());
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
package awais.instagrabber.repositories;
|
package awais.instagrabber.repositories;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.http.GET;
|
import retrofit2.http.GET;
|
||||||
import retrofit2.http.Path;
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.QueryMap;
|
||||||
|
|
||||||
public interface ProfileRepository {
|
public interface ProfileRepository {
|
||||||
|
|
||||||
@GET("api/v1/users/{uid}/info/")
|
@GET("api/v1/users/{uid}/info/")
|
||||||
Call<String> getUserInfo(@Path("uid") final String uid);
|
Call<String> getUserInfo(@Path("uid") final String uid);
|
||||||
|
|
||||||
|
@GET("/graphql/query/")
|
||||||
|
Call<String> fetch(@QueryMap Map<String, String> queryMap);
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ import java.util.List;
|
|||||||
|
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
|
||||||
public class FeedFetchResponse {
|
public class PostsFetchResponse {
|
||||||
private List<FeedModel> feedModels;
|
private List<FeedModel> feedModels;
|
||||||
private boolean hasNextPage;
|
private boolean hasNextPage;
|
||||||
private String nextCursor;
|
private String nextCursor;
|
||||||
|
|
||||||
public FeedFetchResponse(final List<FeedModel> feedModels, final boolean hasNextPage, final String nextCursor) {
|
public PostsFetchResponse(final List<FeedModel> feedModels, final boolean hasNextPage, final String nextCursor) {
|
||||||
this.feedModels = feedModels;
|
this.feedModels = feedModels;
|
||||||
this.hasNextPage = hasNextPage;
|
this.hasNextPage = hasNextPage;
|
||||||
this.nextCursor = nextCursor;
|
this.nextCursor = nextCursor;
|
@ -88,4 +88,5 @@ public final class Constants {
|
|||||||
public static final String DEFAULT_HASH_TAG_PIC = "https://www.instagram.com/static/images/hashtag/search-hashtag-default-avatar.png/1d8417c9a4f5.png";
|
public static final String DEFAULT_HASH_TAG_PIC = "https://www.instagram.com/static/images/hashtag/search-hashtag-default-avatar.png/1d8417c9a4f5.png";
|
||||||
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";
|
||||||
}
|
}
|
@ -31,6 +31,7 @@ import static awais.instagrabber.utils.Constants.MUTED_VIDEOS;
|
|||||||
import static awais.instagrabber.utils.Constants.PREF_DARK_THEME;
|
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.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;
|
||||||
@ -114,7 +115,7 @@ 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})
|
DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, PREF_POSTS_LAYOUT, PREF_PROFILE_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,
|
||||||
|
@ -25,7 +25,7 @@ import awais.instagrabber.models.PostChild;
|
|||||||
import awais.instagrabber.models.ProfileModel;
|
import awais.instagrabber.models.ProfileModel;
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.repositories.FeedRepository;
|
import awais.instagrabber.repositories.FeedRepository;
|
||||||
import awais.instagrabber.repositories.responses.FeedFetchResponse;
|
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
@ -58,7 +58,7 @@ public class FeedService extends BaseService {
|
|||||||
|
|
||||||
public void fetch(final int maxItemsToLoad,
|
public void fetch(final int maxItemsToLoad,
|
||||||
final String cursor,
|
final String cursor,
|
||||||
final ServiceCallback<FeedFetchResponse> callback) {
|
final ServiceCallback<PostsFetchResponse> callback) {
|
||||||
if (loadFromMock) {
|
if (loadFromMock) {
|
||||||
final Handler handler = new Handler();
|
final Handler handler = new Handler();
|
||||||
handler.postDelayed(() -> {
|
handler.postDelayed(() -> {
|
||||||
@ -96,9 +96,9 @@ public class FeedService extends BaseService {
|
|||||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||||
try {
|
try {
|
||||||
// Log.d(TAG, "onResponse: body: " + response.body());
|
// Log.d(TAG, "onResponse: body: " + response.body());
|
||||||
final FeedFetchResponse feedFetchResponse = parseResponse(response);
|
final PostsFetchResponse postsFetchResponse = parseResponse(response);
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback.onSuccess(feedFetchResponse);
|
callback.onSuccess(postsFetchResponse);
|
||||||
}
|
}
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(TAG, "onResponse", e);
|
Log.e(TAG, "onResponse", e);
|
||||||
@ -119,16 +119,16 @@ public class FeedService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private FeedFetchResponse parseResponse(@NonNull final Response<String> response) throws JSONException {
|
private PostsFetchResponse parseResponse(@NonNull final Response<String> response) throws JSONException {
|
||||||
if (TextUtils.isEmpty(response.body())) {
|
if (TextUtils.isEmpty(response.body())) {
|
||||||
Log.e(TAG, "parseResponse: feed response body is empty with status code: " + response.code());
|
Log.e(TAG, "parseResponse: feed response body is empty with status code: " + response.code());
|
||||||
return new FeedFetchResponse(Collections.emptyList(), false, null);
|
return new PostsFetchResponse(Collections.emptyList(), false, null);
|
||||||
}
|
}
|
||||||
return parseResponseBody(response.body());
|
return parseResponseBody(response.body());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private FeedFetchResponse parseResponseBody(@NonNull final String body)
|
private PostsFetchResponse parseResponseBody(@NonNull final String body)
|
||||||
throws JSONException {
|
throws JSONException {
|
||||||
final List<FeedModel> feedModels = new ArrayList<>();
|
final List<FeedModel> feedModels = new ArrayList<>();
|
||||||
final JSONObject timelineFeed = new JSONObject(body)
|
final JSONObject timelineFeed = new JSONObject(body)
|
||||||
@ -161,7 +161,6 @@ public class FeedService extends BaseService {
|
|||||||
final String displayUrl = feedItem.optString("display_url");
|
final String displayUrl = feedItem.optString("display_url");
|
||||||
if (TextUtils.isEmpty(displayUrl)) continue;
|
if (TextUtils.isEmpty(displayUrl)) continue;
|
||||||
final String resourceUrl;
|
final String resourceUrl;
|
||||||
|
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
resourceUrl = feedItem.getString("video_url");
|
resourceUrl = feedItem.getString("video_url");
|
||||||
} else {
|
} else {
|
||||||
@ -265,7 +264,7 @@ public class FeedService extends BaseService {
|
|||||||
final FeedModel feedModel = feedModelBuilder.build();
|
final FeedModel feedModel = feedModelBuilder.build();
|
||||||
feedModels.add(feedModel);
|
feedModels.add(feedModel);
|
||||||
}
|
}
|
||||||
return new FeedFetchResponse(feedModels, hasNextPage, endCursor);
|
return new PostsFetchResponse(feedModels, hasNextPage, endCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -4,12 +4,26 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
|
import awais.instagrabber.models.ProfileModel;
|
||||||
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.repositories.ProfileRepository;
|
import awais.instagrabber.repositories.ProfileRepository;
|
||||||
|
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||||
import awais.instagrabber.repositories.responses.UserInfo;
|
import awais.instagrabber.repositories.responses.UserInfo;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
@ -19,6 +33,7 @@ public class ProfileService extends BaseService {
|
|||||||
private static final String TAG = "ProfileService";
|
private static final String TAG = "ProfileService";
|
||||||
|
|
||||||
private final ProfileRepository repository;
|
private final ProfileRepository repository;
|
||||||
|
private final ProfileRepository wwwRepository;
|
||||||
|
|
||||||
private static ProfileService instance;
|
private static ProfileService instance;
|
||||||
|
|
||||||
@ -26,7 +41,11 @@ public class ProfileService extends BaseService {
|
|||||||
final Retrofit retrofit = getRetrofitBuilder()
|
final Retrofit retrofit = getRetrofitBuilder()
|
||||||
.baseUrl("https://i.instagram.com")
|
.baseUrl("https://i.instagram.com")
|
||||||
.build();
|
.build();
|
||||||
|
final Retrofit wwwRetrofit = getRetrofitBuilder()
|
||||||
|
.baseUrl("https://www.instagram.com")
|
||||||
|
.build();
|
||||||
repository = retrofit.create(ProfileRepository.class);
|
repository = retrofit.create(ProfileRepository.class);
|
||||||
|
wwwRepository = wwwRetrofit.create(ProfileRepository.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProfileService getInstance() {
|
public static ProfileService getInstance() {
|
||||||
@ -66,4 +85,201 @@ public class ProfileService extends BaseService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void fetchPosts(final ProfileModel profileModel,
|
||||||
|
final int postsPerPage,
|
||||||
|
final String cursor,
|
||||||
|
final ServiceCallback<PostsFetchResponse> callback) {
|
||||||
|
final Map<String, String> queryMap = new HashMap<>();
|
||||||
|
queryMap.put("query_hash", "18a7b935ab438c4514b1f742d8fa07a7");
|
||||||
|
queryMap.put("variables", "{" +
|
||||||
|
"\"id\":\"" + profileModel.getId() + "\"," +
|
||||||
|
"\"first\":" + postsPerPage + "," +
|
||||||
|
"\"after\":\"" + (cursor == null ? "" : cursor) + "\"" +
|
||||||
|
"}");
|
||||||
|
final Call<String> request = wwwRepository.fetch(queryMap);
|
||||||
|
request.enqueue(new Callback<String>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||||
|
try {
|
||||||
|
// Log.d(TAG, "onResponse: body: " + response.body());
|
||||||
|
final PostsFetchResponse postsFetchResponse = parseResponse(profileModel, response);
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onSuccess(postsFetchResponse);
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "onResponse", e);
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private PostsFetchResponse parseResponse(final ProfileModel profileModel, final Response<String> response) throws JSONException {
|
||||||
|
if (TextUtils.isEmpty(response.body())) {
|
||||||
|
Log.e(TAG, "parseResponse: feed response body is empty with status code: " + response.code());
|
||||||
|
return new PostsFetchResponse(Collections.emptyList(), false, null);
|
||||||
|
}
|
||||||
|
return parseResponseBody(profileModel, response.body());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PostsFetchResponse parseResponseBody(final ProfileModel profileModel, final String body) throws JSONException {
|
||||||
|
// Log.d(TAG, "parseResponseBody: body: " + body);
|
||||||
|
final List<FeedModel> feedModels = new ArrayList<>();
|
||||||
|
// return new FeedFetchResponse(feedModels, false, null);
|
||||||
|
final JSONObject mediaPosts = new JSONObject(body)
|
||||||
|
.getJSONObject("data")
|
||||||
|
.getJSONObject(Constants.EXTRAS_USER)
|
||||||
|
.getJSONObject("edge_owner_to_timeline_media");
|
||||||
|
final String endCursor;
|
||||||
|
final boolean hasNextPage;
|
||||||
|
final JSONObject pageInfo = mediaPosts.getJSONObject("page_info");
|
||||||
|
if (pageInfo.has("has_next_page")) {
|
||||||
|
hasNextPage = pageInfo.getBoolean("has_next_page");
|
||||||
|
endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null;
|
||||||
|
} else {
|
||||||
|
hasNextPage = false;
|
||||||
|
endCursor = null;
|
||||||
|
}
|
||||||
|
final JSONArray edges = mediaPosts.getJSONArray("edges");
|
||||||
|
for (int i = 0; i < edges.length(); ++i) {
|
||||||
|
final JSONObject mediaNode = edges.getJSONObject(i).getJSONObject("node");
|
||||||
|
final String mediaType = mediaNode.optString("__typename");
|
||||||
|
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType))
|
||||||
|
continue;
|
||||||
|
final boolean isVideo = mediaNode.getBoolean("is_video");
|
||||||
|
final long videoViews = mediaNode.optLong("video_view_count", 0);
|
||||||
|
|
||||||
|
final String displayUrl = mediaNode.optString("display_url");
|
||||||
|
if (TextUtils.isEmpty(displayUrl)) continue;
|
||||||
|
final String resourceUrl;
|
||||||
|
|
||||||
|
if (isVideo) {
|
||||||
|
resourceUrl = mediaNode.getString("video_url");
|
||||||
|
} else {
|
||||||
|
resourceUrl = mediaNode.has("display_resources") ? ResponseBodyUtils.getHighQualityImage(mediaNode) : displayUrl;
|
||||||
|
}
|
||||||
|
JSONObject tempJsonObject = mediaNode.optJSONObject("edge_media_preview_comment");
|
||||||
|
final long commentsCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
|
||||||
|
tempJsonObject = mediaNode.optJSONObject("edge_media_preview_like");
|
||||||
|
final long likesCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
|
||||||
|
tempJsonObject = mediaNode.optJSONObject("edge_media_to_caption");
|
||||||
|
final JSONArray captions = tempJsonObject != null ? tempJsonObject.getJSONArray("edges") : null;
|
||||||
|
String captionText = null;
|
||||||
|
if (captions != null && captions.length() > 0) {
|
||||||
|
if ((tempJsonObject = captions.optJSONObject(0)) != null &&
|
||||||
|
(tempJsonObject = tempJsonObject.optJSONObject("node")) != null) {
|
||||||
|
captionText = tempJsonObject.getString("text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final JSONObject location = mediaNode.optJSONObject("location");
|
||||||
|
// Log.d(TAG, "location: " + (location == null ? null : location.toString()));
|
||||||
|
String locationId = null;
|
||||||
|
String locationName = null;
|
||||||
|
if (location != null) {
|
||||||
|
locationName = location.optString("name");
|
||||||
|
if (location.has("id")) {
|
||||||
|
locationId = location.getString("id");
|
||||||
|
} else if (location.has("pk")) {
|
||||||
|
locationId = location.getString("pk");
|
||||||
|
}
|
||||||
|
// Log.d(TAG, "locationId: " + locationId);
|
||||||
|
}
|
||||||
|
int height = 0;
|
||||||
|
int width = 0;
|
||||||
|
final JSONObject dimensions = mediaNode.optJSONObject("dimensions");
|
||||||
|
if (dimensions != null) {
|
||||||
|
height = dimensions.optInt("height");
|
||||||
|
width = dimensions.optInt("width");
|
||||||
|
}
|
||||||
|
String thumbnailUrl = null;
|
||||||
|
try {
|
||||||
|
thumbnailUrl = mediaNode.getJSONArray("display_resources")
|
||||||
|
.getJSONObject(0)
|
||||||
|
.getString("src");
|
||||||
|
} catch (JSONException ignored) {}
|
||||||
|
final FeedModel.Builder builder = new FeedModel.Builder()
|
||||||
|
.setProfileModel(profileModel)
|
||||||
|
.setItemType(isVideo ? MediaItemType.MEDIA_TYPE_VIDEO
|
||||||
|
: MediaItemType.MEDIA_TYPE_IMAGE)
|
||||||
|
.setViewCount(videoViews)
|
||||||
|
.setPostId(mediaNode.getString(Constants.EXTRAS_ID))
|
||||||
|
.setDisplayUrl(resourceUrl)
|
||||||
|
.setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl : displayUrl)
|
||||||
|
.setShortCode(mediaNode.getString(Constants.EXTRAS_SHORTCODE))
|
||||||
|
.setPostCaption(captionText)
|
||||||
|
.setCommentsCount(commentsCount)
|
||||||
|
.setTimestamp(mediaNode.optLong("taken_at_timestamp", -1))
|
||||||
|
.setLiked(mediaNode.getBoolean("viewer_has_liked"))
|
||||||
|
.setBookmarked(mediaNode.getBoolean("viewer_has_saved"))
|
||||||
|
.setLikesCount(likesCount)
|
||||||
|
.setLocationName(locationName)
|
||||||
|
.setLocationId(locationId)
|
||||||
|
.setImageHeight(height)
|
||||||
|
.setImageWidth(width);
|
||||||
|
final boolean isSlider = "GraphSidecar".equals(mediaType) && mediaNode.has("edge_sidecar_to_children");
|
||||||
|
if (isSlider) {
|
||||||
|
builder.setItemType(MediaItemType.MEDIA_TYPE_SLIDER);
|
||||||
|
final JSONObject sidecar = mediaNode.optJSONObject("edge_sidecar_to_children");
|
||||||
|
if (sidecar != null) {
|
||||||
|
final JSONArray children = sidecar.optJSONArray("edges");
|
||||||
|
if (children != null) {
|
||||||
|
final List<PostChild> sliderItems = getSliderItems(children);
|
||||||
|
builder.setSliderItems(sliderItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final FeedModel feedModel = builder.build();
|
||||||
|
feedModels.add(feedModel);
|
||||||
|
// DownloadUtils.checkExistence(downloadDir, customDir, isSlider, model);
|
||||||
|
}
|
||||||
|
return new PostsFetchResponse(feedModels, hasNextPage, endCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private List<PostChild> getSliderItems(final JSONArray children) throws JSONException {
|
||||||
|
final List<PostChild> sliderItems = new ArrayList<>();
|
||||||
|
for (int j = 0; j < children.length(); ++j) {
|
||||||
|
final JSONObject childNode = children.optJSONObject(j).getJSONObject("node");
|
||||||
|
final boolean isChildVideo = childNode.optBoolean("is_video");
|
||||||
|
int height = 0;
|
||||||
|
int width = 0;
|
||||||
|
final JSONObject dimensions = childNode.optJSONObject("dimensions");
|
||||||
|
if (dimensions != null) {
|
||||||
|
height = dimensions.optInt("height");
|
||||||
|
width = dimensions.optInt("width");
|
||||||
|
}
|
||||||
|
String thumbnailUrl = null;
|
||||||
|
try {
|
||||||
|
thumbnailUrl = childNode.getJSONArray("display_resources")
|
||||||
|
.getJSONObject(0)
|
||||||
|
.getString("src");
|
||||||
|
} catch (JSONException ignored) {}
|
||||||
|
final PostChild sliderItem = new PostChild.Builder()
|
||||||
|
.setItemType(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
|
||||||
|
: MediaItemType.MEDIA_TYPE_IMAGE)
|
||||||
|
.setPostId(childNode.getString(Constants.EXTRAS_ID))
|
||||||
|
.setDisplayUrl(isChildVideo ? childNode.getString("video_url")
|
||||||
|
: childNode.getString("display_url"))
|
||||||
|
.setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl
|
||||||
|
: childNode.getString("display_url"))
|
||||||
|
.setVideoViews(childNode.optLong("video_view_count", 0))
|
||||||
|
.setHeight(height)
|
||||||
|
.setWidth(width)
|
||||||
|
.build();
|
||||||
|
// Log.d(TAG, "getSliderItems: sliderItem: " + sliderItem);
|
||||||
|
sliderItems.add(sliderItem);
|
||||||
|
}
|
||||||
|
return sliderItems;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,10 +37,4 @@
|
|||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
tools:listitem="@layout/item_feed_photo" />
|
tools:listitem="@layout/item_feed_photo" />
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
<!--<androidx.fragment.app.FragmentContainerView-->
|
|
||||||
<!-- android:id="@+id/frag_container"-->
|
|
||||||
<!-- android:layout_width="match_parent"-->
|
|
||||||
<!-- android:layout_height="match_parent"-->
|
|
||||||
<!-- app:layout_behavior="@string/appbar_scrolling_view_behavior" />-->
|
|
||||||
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>
|
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>
|
@ -272,19 +272,33 @@
|
|||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/swipeRefreshLayout"
|
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="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<awais.instagrabber.customviews.PostsRecyclerView
|
||||||
android:id="@+id/mainPosts"
|
android:id="@+id/posts_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"
|
android:clipToPadding="false"
|
||||||
tools:listitem="@layout/item_post" />
|
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"
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/layout"
|
android:id="@+id/layout"
|
||||||
android:title="Layout"
|
android:title="@string/layout"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
@ -1,13 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<!--<item-->
|
|
||||||
<!-- android:id="@+id/favourites"-->
|
<item
|
||||||
<!-- android:enabled="true"-->
|
android:id="@+id/layout"
|
||||||
<!-- android:icon="@drawable/ic_star_24"-->
|
android:title="@string/layout"
|
||||||
<!-- android:title="@string/title_favorites"-->
|
app:showAsAction="never" />
|
||||||
<!-- android:visible="false"-->
|
|
||||||
<!-- app:showAsAction="ifRoom" />-->
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/block"
|
android:id="@+id/block"
|
||||||
|
@ -324,6 +324,7 @@
|
|||||||
<string name="downloader_downloading_child">Download item %d of %d</string>
|
<string name="downloader_downloading_child">Download item %d of %d</string>
|
||||||
<string name="delete">Delete</string>
|
<string name="delete">Delete</string>
|
||||||
<string name="comment">Comment</string>
|
<string name="comment">Comment</string>
|
||||||
|
<string name="layout">Layout</string>
|
||||||
<plurals name="likes_count">
|
<plurals name="likes_count">
|
||||||
<item quantity="one">%d like</item>
|
<item quantity="one">%d like</item>
|
||||||
<item quantity="other">%d likes</item>
|
<item quantity="other">%d likes</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user