diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.java b/app/src/main/java/awais/instagrabber/activities/MainActivity.java index c556eaf3..3986e4f4 100644 --- a/app/src/main/java/awais/instagrabber/activities/MainActivity.java +++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.java @@ -21,6 +21,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AutoCompleteTextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -40,7 +41,6 @@ import androidx.navigation.ui.NavigationUI; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.bottomnavigation.BottomNavigationView; -import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Arrays; @@ -53,7 +53,6 @@ import awais.instagrabber.R; import awais.instagrabber.adapters.SuggestionsAdapter; import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.SuggestionsFetcher; -import awais.instagrabber.customviews.helpers.CustomHideBottomViewOnScrollBehavior; import awais.instagrabber.databinding.ActivityMainBinding; import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.main.FeedFragment; @@ -80,29 +79,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage R.id.profileFragment, R.id.discoverFragment, R.id.morePreferencesFragment); - private static final List KEEP_SCROLL_BEHAVIOUR_DESTINATIONS = Arrays.asList( - R.id.directMessagesInboxFragment, - R.id.feedFragment, - R.id.profileFragment, - R.id.discoverFragment, - R.id.morePreferencesFragment, - R.id.settingsPreferencesFragment, - R.id.aboutFragment, - R.id.hashTagFragment, - R.id.locationFragment, - R.id.savedViewerFragment, - R.id.commentsViewerFragment, - R.id.followViewerFragment, - R.id.directMessagesSettingsFragment, - R.id.notificationsViewer, - R.id.themePreferencesFragment, - R.id.favoritesFragment, - R.id.backupPreferencesFragment, - R.id.directMessagesThreadFragment - ); private static final Map NAV_TO_MENU_ID_MAP = new HashMap<>(); - private static final List REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = ImmutableList.of(R.id.commentsViewerFragment, - R.id.directMessagesThreadFragment); private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex"; private ActivityMainBinding binding; @@ -152,7 +129,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage if (savedInstanceState == null) { setupBottomNavigationBar(true); } - setupScrollingListener(); setupSuggestions(); final boolean checkUpdates = settingsHelper.getBoolean(Constants.CHECK_UPDATES); if (checkUpdates) FlavorTown.updateCheck(this); @@ -233,7 +209,9 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage if (isTaskRoot() && isBackStackEmpty) { finishAfterTransition(); } else { - super.onBackPressed(); + try { + super.onBackPressed(); + } catch (Exception ignored) {} } } @@ -281,12 +259,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage }); } - private void setupScrollingListener() { - final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.bottomNavView.getLayoutParams(); - layoutParams.setBehavior(new CustomHideBottomViewOnScrollBehavior()); - binding.bottomNavView.requestLayout(); - } - private boolean setupSearchView() { final View actionView = searchMenuItem.getActionView(); if (!(actionView instanceof SearchView)) return false; @@ -478,16 +450,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage @SuppressLint("RestrictedApi") final Deque backStack = navController.getBackStack(); setupMenu(backStack.size(), destinationId); binding.bottomNavView.setVisibility(SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId) ? View.VISIBLE : View.GONE); - if (KEEP_SCROLL_BEHAVIOUR_DESTINATIONS.contains(destinationId)) { - setScrollingBehaviour(); - } else { - removeScrollingBehaviour(); - } - if (REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS.contains(destinationId)) { - removeCollapsingToolbarScrollFlags(); - } else { - setCollapsingToolbarScrollFlags(); - } }); } @@ -514,22 +476,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage binding.mainNavHost.requestLayout(); } - private void setCollapsingToolbarScrollFlags() { - final CollapsingToolbarLayout collapsingToolbarLayout = binding.collapsingToolbarLayout; - final AppBarLayout.LayoutParams toolbarLayoutLayoutParams = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams(); - toolbarLayoutLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL - | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP - | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); - binding.collapsingToolbarLayout.requestLayout(); - } - - private void removeCollapsingToolbarScrollFlags() { - final CollapsingToolbarLayout collapsingToolbarLayout = binding.collapsingToolbarLayout; - final AppBarLayout.LayoutParams toolbarLayoutLayoutParams = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams(); - toolbarLayoutLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL); - binding.collapsingToolbarLayout.requestLayout(); - } - private void handleIntent(final Intent intent) { if (intent == null) return; final String action = intent.getAction(); @@ -601,11 +547,16 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage .create(); alertDialog.show(); new PostFetcher(shortCode, feedModel -> { - final PostViewV2Fragment fragment = PostViewV2Fragment - .builder(feedModel) - .build(); - fragment.setOnShowListener(dialog -> alertDialog.dismiss()); - fragment.show(getSupportFragmentManager(), "post_view"); + if (feedModel != null) { + final PostViewV2Fragment fragment = PostViewV2Fragment + .builder(feedModel) + .build(); + fragment.setOnShowListener(dialog -> alertDialog.dismiss()); + fragment.show(getSupportFragmentManager(), "post_view"); + } + else { + Toast.makeText(getApplicationContext(), R.string.post_not_found, Toast.LENGTH_SHORT).show(); + } }).execute(); } @@ -654,23 +605,17 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage return binding.bottomNavView; } - // public void fitSystemWindows(final boolean fit) { - // binding.appBarLayout.setBackground(null); - // binding.appBarLayout.setFitsSystemWindows(fit); - // binding.collapsingToolbarLayout.setBackground(null); - // binding.collapsingToolbarLayout.setFitsSystemWindows(fit); - // final Drawable toolbarBackground = binding.toolbar.getBackground(); - // binding.toolbar.setFitsSystemWindows(fit); - // binding.toolbar.setBackground(null); - // binding.toolbar.setClickable(false); - // } - // - // public int getNavHostContainerId() { - // return binding.mainNavHost.getId(); - // } + public void setCollapsingView(@NonNull final View view) { + binding.collapsingToolbarLayout.addView(view, 0); + } + + public void removeCollapsingView(@NonNull final View view) { + binding.collapsingToolbarLayout.removeView(view); + } public void setToolbar(final Toolbar toolbar) { binding.appBarLayout.setVisibility(View.GONE); + removeScrollingBehaviour(); setSupportActionBar(toolbar); if (currentNavControllerLiveData == null) return; setupNavigation(toolbar, currentNavControllerLiveData.getValue()); @@ -678,8 +623,13 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage public void resetToolbar() { binding.appBarLayout.setVisibility(View.VISIBLE); + setScrollingBehaviour(); setSupportActionBar(binding.toolbar); if (currentNavControllerLiveData == null) return; setupNavigation(binding.toolbar, currentNavControllerLiveData.getValue()); } + + public CollapsingToolbarLayout getCollapsingToolbarView() { + return binding.collapsingToolbarLayout; + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java index aef705c1..a23b5d15 100644 --- a/app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java @@ -22,7 +22,7 @@ public final class SliderItemsAdapter extends ListAdapter DIFF_CALLBACK = new DiffUtil.ItemCallback() { @Override diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java index 325aa720..613c2250 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java @@ -135,6 +135,16 @@ public class SliderVideoViewHolder extends SliderItemViewHolder { videoPlayerViewHelper.releasePlayer(); } + public void resetPlayerTimeline() { + if (videoPlayerViewHelper == null) return; + videoPlayerViewHelper.resetTimeline(); + } + + public void removeCallbacks() { + if (videoPlayerViewHelper == null) return; + videoPlayerViewHelper.removeCallbacks(); + } + // private void setDimensions(final FeedModel feedModel, final int spanCount, final boolean animate) { // final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams(); // final int deviceWidth = Utils.displayMetrics.widthPixels; diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java index 678d2d0c..e2e4fe2c 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ProfileFetcher.java @@ -15,11 +15,13 @@ import awais.instagrabber.BuildConfig; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.ProfileModel; import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.NetworkUtils; import awais.instagrabber.utils.TextUtils; import awaisomereport.LogCollector; import static awais.instagrabber.utils.Utils.logCollector; +import static awais.instagrabber.utils.Utils.settingsHelper; public final class ProfileFetcher extends AsyncTask { private final FetchListener fetchListener; @@ -43,21 +45,23 @@ public final class ProfileFetcher extends AsyncTask { if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { final JSONObject user = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject(Constants.EXTRAS_USER); + final String cookie = settingsHelper.getString(Constants.COOKIE); + boolean isPrivate = user.getBoolean("is_private"); - boolean reallyPrivate = isPrivate; + final String id = user.getString(Constants.EXTRAS_ID); + final String uid = CookieUtils.getUserIdFromCookie(cookie); final JSONObject timelineMedia = user.getJSONObject("edge_owner_to_timeline_media"); if (timelineMedia.has("edges")) { final JSONArray edges = timelineMedia.getJSONArray("edges"); - if (edges.length() > 0 && timelineMedia.getLong("count") > 0L) reallyPrivate = false; } String url = user.optString("external_url"); if (TextUtils.isEmpty(url)) url = null; result = new ProfileModel(isPrivate, - reallyPrivate, + user.optBoolean("followed_by_viewer") ? false : (id.equals(uid) ? false : isPrivate), user.getBoolean("is_verified"), - user.getString(Constants.EXTRAS_ID), + id, userName, user.getString("full_name"), user.getString("biography"), diff --git a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/InboxFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/InboxFetcher.java index 355b3ed1..3f9e98a4 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/InboxFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/InboxFetcher.java @@ -66,9 +66,6 @@ public final class InboxFetcher extends AsyncTask { return null; } JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)); - // try (FileWriter fileWriter = new FileWriter(new File("/sdcard/test.json"))) { - // fileWriter.write(data.toString(2)); - // } final long seqId = data.optLong("seq_id"); final int pendingRequestsCount = data.optInt("pending_requests_total"); @@ -94,8 +91,8 @@ public final class InboxFetcher extends AsyncTask { } result = new InboxModel(hasOlder, hasPendingTopRequests, - blendedInboxEnabled, unseenCount, pendingRequestsCount, - seqId, unseenCountTimestamp, oldestCursor, inboxThreadModels); + blendedInboxEnabled, unseenCount, pendingRequestsCount, + seqId, unseenCountTimestamp, oldestCursor, inboxThreadModels); conn.disconnect(); } catch (final Exception e) { diff --git a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java index aee8930f..e5771537 100644 --- a/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java +++ b/app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java @@ -216,6 +216,7 @@ public class PostsRecyclerView extends RecyclerView { if (progressPercent != 100) continue; final String url = progress.getString(DownloadWorker.URL); final List feedModels = feedViewModel.getList().getValue(); + if (feedModels == null) continue; for (int i = 0; i < feedModels.size(); i++) { final FeedModel feedModel = feedModels.get(i); final List displayUrls = getDisplayUrl(feedModel); @@ -254,10 +255,15 @@ public class PostsRecyclerView extends RecyclerView { post(() -> { TransitionManager.beginDelayedTransition(this, transition); feedAdapter.notifyDataSetChanged(); + final int itemDecorationCount = getItemDecorationCount(); if (!layoutPreferences.getHasGap()) { - removeItemDecoration(gridSpacingItemDecoration); + if (itemDecorationCount == 1) { + removeItemDecoration(gridSpacingItemDecoration); + } } else { - addItemDecoration(gridSpacingItemDecoration); + if (itemDecorationCount == 0) { + addItemDecoration(gridSpacingItemDecoration); + } } if (layoutPreferences.getType() == PostsLayoutPreferences.PostsLayoutType.LINEAR) { if (layoutManager.getSpanCount() != 1) { @@ -277,9 +283,13 @@ public class PostsRecyclerView extends RecyclerView { } public void refresh() { - lazyLoader.resetState(); - postFetcher.reset(); - postFetcher.fetch(); + if (lazyLoader != null) { + lazyLoader.resetState(); + } + if (postFetcher != null) { + postFetcher.reset(); + postFetcher.fetch(); + } dispatchFetchStatus(); } diff --git a/app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java b/app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java index e6e16b0a..c00b6e39 100644 --- a/app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java +++ b/app/src/main/java/awais/instagrabber/customviews/SharedElementTransitionDialogFragment.java @@ -138,7 +138,6 @@ public abstract class SharedElementTransitionDialogFragment extends DialogFragme final int initY = location[1]; destView.setX(initX); destView.setY(initY - Utils.getStatusBarHeight(getContext())); - destView.requestLayout(); boundsCalculatedCount++; if (startCalled) { startPostponedEnterTransition(); diff --git a/app/src/main/java/awais/instagrabber/customviews/VerticalDragHelper.java b/app/src/main/java/awais/instagrabber/customviews/VerticalDragHelper.java index e7527e9b..4420111e 100644 --- a/app/src/main/java/awais/instagrabber/customviews/VerticalDragHelper.java +++ b/app/src/main/java/awais/instagrabber/customviews/VerticalDragHelper.java @@ -1,24 +1,22 @@ package awais.instagrabber.customviews; import android.content.Context; -import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewParent; import androidx.annotation.NonNull; public class VerticalDragHelper { - private static final String TAG = "VerticalDragHelper"; - private static final float PIXELS_PER_SECOND = 10; + // private static final String TAG = "VerticalDragHelper"; + private static final double SWIPE_THRESHOLD_VELOCITY = 80; private final View view; private GestureDetector gestureDetector; private Context context; - private float flingVelocity; + private double flingVelocity; private OnVerticalDragListener onVerticalDragListener; private final GestureDetector.OnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() { @@ -31,32 +29,13 @@ public class VerticalDragHelper { @Override public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) { - float maxFlingVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity(); - float velocityPercentY = velocityY / maxFlingVelocity; - float normalizedVelocityY = velocityPercentY * PIXELS_PER_SECOND; - if (Math.abs(normalizedVelocityY) > 4) { - flingVelocity = normalizedVelocityY; + double yDir = e1.getY() - e2.getY(); + // Log.d(TAG, "onFling: yDir: " + yDir); + if (yDir < -SWIPE_THRESHOLD_VELOCITY || yDir > SWIPE_THRESHOLD_VELOCITY) { + flingVelocity = yDir; } return super.onFling(e1, e2, velocityX, velocityY); } - - - }; - - private final GestureDetector.OnGestureListener dragPreventionGestureListener = new GestureDetector.SimpleOnGestureListener() { - float prevDistanceY = 0; - - @Override - public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) { - Log.d(TAG, "onScroll: distanceX: " + distanceX + ", distanceY: " + distanceY); - return super.onScroll(e1, e2, distanceX, distanceY); - } - - @Override - public boolean onSingleTapUp(final MotionEvent e) { - Log.d(TAG, "onSingleTapUp"); - return super.onSingleTapUp(e); - } }; private float prevRawY; @@ -64,7 +43,6 @@ public class VerticalDragHelper { private float prevRawX; private float dX; private float prevDY; - private GestureDetector dragPreventionGestureDetector; public VerticalDragHelper(@NonNull final View view) { this.view = view; @@ -80,14 +58,12 @@ public class VerticalDragHelper { protected void init() { gestureDetector = new GestureDetector(context, gestureListener); - dragPreventionGestureDetector = new GestureDetector(context, dragPreventionGestureListener); } public boolean onDragTouch(final MotionEvent event) { if (onVerticalDragListener == null) { return false; } - // dragPreventionGestureDetector.onTouchEvent(event); if (gestureDetector.onTouchEvent(event)) { return true; } @@ -151,53 +127,11 @@ public class VerticalDragHelper { return gestureDetector.onTouchEvent(event); } - private final static int DIRECTION_UP = 0; - private final static int DIRECTION_DOWN = 1; - float prevY = -1; - int edgeHitCount = 0; - float prevDirection = -1; - - - // private boolean shouldPreventDrag(final MotionEvent event) { - // switch (event.getAction()) { - // case MotionEvent.ACTION_DOWN: - // if (!firstDrag) { - // firstDrag = true; - // } - // return false; - // case MotionEvent.ACTION_MOVE: - // float y = event.getY(); - // int direction = -2; - // if (prevY != -1) { - // final float dy = y - prevY; - // // Log.d(TAG, "shouldPreventDrag: dy: " + dy); - // if (dy > 0) { - // direction = DIRECTION_DOWN; - // // move direction is down - // } else { - // direction = DIRECTION_UP; - // // move direction is up - // } - // } - // prevY = y; - // if (prevDirection == direction) { - // edgeHitCount++; - // } else { - // edgeHitCount = 1; - // } - // if (edgeHitCount >= 2) { - // return false; - // } - // return true; - // break; - // } - // } - public interface OnVerticalDragListener { void onDrag(final float dY); void onDragEnd(); - void onFling(final float flingVelocity); + void onFling(final double flingVelocity); } } diff --git a/app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java b/app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java index 04e07854..987f9dd5 100644 --- a/app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java +++ b/app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java @@ -10,6 +10,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.view.ContextThemeWrapper; +import androidx.appcompat.widget.AppCompatTextView; import androidx.appcompat.widget.PopupMenu; import com.facebook.drawee.backends.pipeline.Fresco; @@ -26,6 +27,8 @@ import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.audio.AudioListener; import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.material.slider.LabelFormatter; +import com.google.android.material.slider.Slider; import awais.instagrabber.R; import awais.instagrabber.databinding.LayoutExoCustomControlsBinding; @@ -39,6 +42,8 @@ import static com.google.android.exoplayer2.Player.STATE_READY; public class VideoPlayerViewHelper implements Player.EventListener { private static final String TAG = "VideoPlayerViewHelper"; + private static final long INITIAL_DELAY = 0; + private static final long RECURRING_DELAY = 60; private final Context context; private final awais.instagrabber.databinding.LayoutVideoPlayerWithThumbnailBinding binding; @@ -52,6 +57,62 @@ public class VideoPlayerViewHelper implements Player.EventListener { private final DefaultDataSourceFactory dataSourceFactory; private SimpleExoPlayer player; private PopupMenu speedPopup; + private Runnable positionChecker; + + private final Handler positionUpdateHandler = new Handler(); + private final Player.EventListener listener = new Player.EventListener() { + @Override + public void onPlaybackStateChanged(final int state) { + switch (state) { + case Player.STATE_BUFFERING: + case STATE_IDLE: + case STATE_ENDED: + positionUpdateHandler.removeCallbacks(positionChecker); + return; + case STATE_READY: + setupTimeline(); + positionUpdateHandler.postDelayed(positionChecker, INITIAL_DELAY); + break; + } + } + + @Override + public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason) { + updatePlayPauseDrawable(playWhenReady); + } + }; + private final AudioListener audioListener = new AudioListener() { + @Override + public void onVolumeChanged(final float volume) { + updateMuteIcon(volume); + } + }; + private final Slider.OnChangeListener onChangeListener = (slider, value, fromUser) -> { + if (!fromUser) return; + long actualValue = (long) value; + if (actualValue < 0) { + actualValue = 0; + } else if (actualValue > player.getDuration()) { + actualValue = player.getDuration(); + } + player.seekTo(actualValue); + }; + private final View.OnClickListener onClickListener = v -> player.setPlayWhenReady(!player.getPlayWhenReady()); + private final LabelFormatter labelFormatter = value -> TextUtils.millisToTimeString((long) value); + private final View.OnClickListener muteOnClickListener = v -> toggleMute(); + private final View.OnClickListener rewOnClickListener = v -> { + final long positionMs = player.getCurrentPosition() - 5000; + player.seekTo(positionMs < 0 ? 0 : positionMs); + }; + private final View.OnClickListener ffOnClickListener = v -> { + long positionMs = player.getCurrentPosition() + 5000; + long duration = player.getDuration(); + if (duration == TIME_UNSET) { + duration = 0; + } + player.seekTo(Math.min(positionMs, duration)); + }; + private final View.OnClickListener showMenu = this::showMenu; public VideoPlayerViewHelper(@NonNull final Context context, @NonNull final LayoutVideoPlayerWithThumbnailBinding binding, @@ -130,6 +191,10 @@ public class VideoPlayerViewHelper implements Player.EventListener { player = new SimpleExoPlayer.Builder(context) .setLooper(Looper.getMainLooper()) .build(); + positionChecker = new PositionCheckRunnable(positionUpdateHandler, + player, + controlsBinding.timeline, + controlsBinding.fromTime); player.addListener(this); player.setVolume(initialVolume); player.setPlayWhenReady(true); @@ -148,87 +213,22 @@ public class VideoPlayerViewHelper implements Player.EventListener { binding.playerView.setUseController(false); if (player == null) { enableControls(false); - controlsBinding.playPause.setEnabled(true); - controlsBinding.playPause.setOnClickListener(v -> binding.thumbnailParent.performClick()); + // controlsBinding.playPause.setEnabled(true); + // controlsBinding.playPause.setOnClickListener(new NoPlayerPlayPauseClickListener(binding.thumbnailParent)); return; } enableControls(true); - final Handler handler = new Handler(); - final long initialDelay = 0; - final long recurringDelay = 60; - final Runnable positionChecker = new Runnable() { - @Override - public void run() { - handler.removeCallbacks(this); - if (player == null) return; - final long currentPosition = player.getCurrentPosition(); - final long duration = player.getDuration(); - if (duration == TIME_UNSET) { - controlsBinding.timeline.setValueFrom(0); - controlsBinding.timeline.setValueTo(0); - controlsBinding.timeline.setEnabled(false); - return; - } - controlsBinding.timeline.setValue(Math.min(currentPosition, duration)); - controlsBinding.fromTime.setText(TextUtils.millisToTimeString(currentPosition)); - handler.postDelayed(this, recurringDelay); - } - }; updatePlayPauseDrawable(player.getPlayWhenReady()); updateMuteIcon(player.getVolume()); - player.addListener(new Player.EventListener() { - @Override - public void onPlaybackStateChanged(final int state) { - switch (state) { - case Player.STATE_BUFFERING: - case STATE_IDLE: - case STATE_ENDED: - handler.removeCallbacks(positionChecker); - return; - case STATE_READY: - setupTimeline(); - handler.postDelayed(positionChecker, initialDelay); - break; - } - } - - @Override - public void onPlayWhenReadyChanged(final boolean playWhenReady, final int reason) { - updatePlayPauseDrawable(playWhenReady); - } - }); - player.addAudioListener(new AudioListener() { - @Override - public void onVolumeChanged(final float volume) { - updateMuteIcon(volume); - } - }); - controlsBinding.timeline.addOnChangeListener((slider, value, fromUser) -> { - if (!fromUser) return; - long actualValue = (long) value; - if (actualValue < 0) { - actualValue = 0; - } else if (actualValue > player.getDuration()) { - actualValue = player.getDuration(); - } - player.seekTo(actualValue); - }); - controlsBinding.timeline.setLabelFormatter(value -> TextUtils.millisToTimeString((long) value)); - controlsBinding.playPause.setOnClickListener(v -> player.setPlayWhenReady(!player.getPlayWhenReady())); - controlsBinding.mute.setOnClickListener(v -> toggleMute()); - controlsBinding.rewWithAmount.setOnClickListener(v -> { - final long positionMs = player.getCurrentPosition() - 5000; - player.seekTo(positionMs < 0 ? 0 : positionMs); - }); - controlsBinding.ffWithAmount.setOnClickListener(v -> { - long positionMs = player.getCurrentPosition() + 5000; - long duration = player.getDuration(); - if (duration == TIME_UNSET) { - duration = 0; - } - player.seekTo(Math.min(positionMs, duration)); - }); - controlsBinding.speed.setOnClickListener(this::showMenu); + player.addListener(listener); + player.addAudioListener(audioListener); + controlsBinding.timeline.addOnChangeListener(onChangeListener); + controlsBinding.timeline.setLabelFormatter(labelFormatter); + controlsBinding.playPause.setOnClickListener(onClickListener); + controlsBinding.mute.setOnClickListener(muteOnClickListener); + controlsBinding.rewWithAmount.setOnClickListener(rewOnClickListener); + controlsBinding.ffWithAmount.setOnClickListener(ffOnClickListener); + controlsBinding.speed.setOnClickListener(showMenu); } private void setupTimeline() { @@ -242,12 +242,18 @@ public class VideoPlayerViewHelper implements Player.EventListener { private void enableControls(final boolean enable) { controlsBinding.speed.setEnabled(enable); + controlsBinding.speed.setClickable(enable); controlsBinding.mute.setEnabled(enable); + controlsBinding.mute.setClickable(enable); controlsBinding.ffWithAmount.setEnabled(enable); + controlsBinding.ffWithAmount.setClickable(enable); controlsBinding.rewWithAmount.setEnabled(enable); + controlsBinding.rewWithAmount.setClickable(enable); controlsBinding.fromTime.setEnabled(enable); controlsBinding.toTime.setEnabled(enable); controlsBinding.playPause.setEnabled(enable); + controlsBinding.playPause.setClickable(enable); + controlsBinding.timeline.setEnabled(enable); } public void showMenu(View anchor) { @@ -302,10 +308,10 @@ public class VideoPlayerViewHelper implements Player.EventListener { private void updateMuteIcon(final float volume) { if (volume == 0) { - controlsBinding.mute.setIconResource(R.drawable.ic_volume_off_24); + controlsBinding.mute.setIconResource(R.drawable.ic_volume_off_24_states); return; } - controlsBinding.mute.setIconResource(R.drawable.ic_volume_up_24); + controlsBinding.mute.setIconResource(R.drawable.ic_volume_up_24_states); } private void updatePlayPauseDrawable(final boolean playWhenReady) { @@ -313,7 +319,7 @@ public class VideoPlayerViewHelper implements Player.EventListener { controlsBinding.playPause.setIconResource(R.drawable.ic_pause_24); return; } - controlsBinding.playPause.setIconResource(R.drawable.ic_play_arrow_24); + controlsBinding.playPause.setIconResource(R.drawable.ic_play_states); } @Override @@ -338,23 +344,88 @@ public class VideoPlayerViewHelper implements Player.EventListener { return vol; } - public void togglePlayback() { - if (player == null) return; - final int playbackState = player.getPlaybackState(); - if (playbackState == STATE_IDLE || playbackState == STATE_ENDED) return; - final boolean playWhenReady = player.getPlayWhenReady(); - player.setPlayWhenReady(!playWhenReady); - } + // public void togglePlayback() { + // if (player == null) return; + // final int playbackState = player.getPlaybackState(); + // if (playbackState == STATE_IDLE || playbackState == STATE_ENDED) return; + // final boolean playWhenReady = player.getPlayWhenReady(); + // player.setPlayWhenReady(!playWhenReady); + // } public void releasePlayer() { if (player == null) return; player.release(); player = null; + if (positionUpdateHandler != null && positionChecker != null) { + positionUpdateHandler.removeCallbacks(positionChecker); + } } public void pause() { if (player == null) return; player.pause(); + if (positionUpdateHandler != null && positionChecker != null) { + positionUpdateHandler.removeCallbacks(positionChecker); + } + } + + public void resetTimeline() { + if (player == null) { + enableControls(false); + return; + } + setupTimeline(); + final long currentPosition = player.getCurrentPosition(); + controlsBinding.timeline.setValue(Math.min(currentPosition, player.getDuration())); + setupControls(); + } + + public void removeCallbacks() { + if (player != null) { + player.removeListener(listener); + player.removeAudioListener(audioListener); + } + controlsBinding.timeline.removeOnChangeListener(onChangeListener); + controlsBinding.timeline.setLabelFormatter(null); + controlsBinding.playPause.setOnClickListener(null); + controlsBinding.mute.setOnClickListener(null); + controlsBinding.rewWithAmount.setOnClickListener(null); + controlsBinding.ffWithAmount.setOnClickListener(null); + controlsBinding.speed.setOnClickListener(null); + } + + private static class PositionCheckRunnable implements Runnable { + private final Handler positionUpdateHandler; + private final SimpleExoPlayer player; + private final Slider timeline; + private final AppCompatTextView fromTime; + + public PositionCheckRunnable(final Handler positionUpdateHandler, + final SimpleExoPlayer simpleExoPlayer, + final Slider slider, + final AppCompatTextView fromTime) { + this.positionUpdateHandler = positionUpdateHandler; + this.player = simpleExoPlayer; + this.timeline = slider; + this.fromTime = fromTime; + } + + @Override + public void run() { + positionUpdateHandler.removeCallbacks(this); + if (player == null) return; + final long currentPosition = player.getCurrentPosition(); + final long duration = player.getDuration(); + if (duration == TIME_UNSET) { + timeline.setValueFrom(0); + timeline.setValueTo(0); + timeline.setEnabled(false); + return; + } + timeline.setValue(Math.min(currentPosition, duration)); + fromTime.setText(TextUtils.millisToTimeString(currentPosition)); + positionUpdateHandler.postDelayed(this, RECURRING_DELAY); + } } public interface VideoPlayerCallback { diff --git a/app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java index b5005b39..9b9de1e2 100644 --- a/app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java +++ b/app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java @@ -33,12 +33,14 @@ import static awais.instagrabber.utils.DownloadUtils.PERMS; public class RestoreBackupDialogFragment extends DialogFragment { private static final int STORAGE_PERM_REQUEST_CODE = 8020; - private final OnResultListener onResultListener; + private OnResultListener onResultListener; private DialogRestoreBackupBinding binding; private File file; private boolean isEncrypted; + public RestoreBackupDialogFragment() {} + public RestoreBackupDialogFragment(final OnResultListener onResultListener) { this.onResultListener = onResultListener; } diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 14cec315..593e5750 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -24,6 +24,7 @@ import androidx.activity.OnBackPressedDispatcher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; +import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.content.PermissionChecker; import androidx.fragment.app.Fragment; import androidx.navigation.NavController; @@ -46,8 +47,8 @@ import awais.instagrabber.asyncs.HashtagFetcher; import awais.instagrabber.asyncs.HashtagPostFetchService; import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; -import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; import awais.instagrabber.databinding.FragmentHashtagBinding; +import awais.instagrabber.databinding.LayoutHashtagDetailsBinding; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.HashtagModel; @@ -79,7 +80,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private MainActivity fragmentActivity; private FragmentHashtagBinding binding; - private NestedCoordinatorLayout root; + private CoordinatorLayout root; private boolean shouldRefresh = true; private boolean hasStories = false; private boolean opening = false; @@ -94,7 +95,8 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private Set selectedFeedModels; private FeedModel downloadFeedModel; private int downloadChildPosition = -1; - private PostsLayoutPreferences layoutPreferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_HASHTAG_POSTS_LAYOUT)); + private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_HASHTAG_POSTS_LAYOUT); + private LayoutHashtagDetailsBinding hashtagDetailsBinding; private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) { @Override @@ -198,15 +200,18 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe Utils.openEmailAddress(getContext(), emailId); } - private void openPostDialog(final FeedModel feedModel, + private void openPostDialog(@NonNull final FeedModel feedModel, final View profilePicView, final View mainPostImage, final int position) { if (opening) return; if (TextUtils.isEmpty(feedModel.getProfileModel().getUsername())) { opening = true; - new PostFetcher(feedModel.getShortCode(), newFeedModel -> openPostDialog(newFeedModel, profilePicView, mainPostImage, position)) - .execute(); + new PostFetcher(feedModel.getShortCode(), newFeedModel -> { + opening = false; + if (newFeedModel == null) return; + openPostDialog(newFeedModel, profilePicView, mainPostImage, position); + }).execute(); return; } opening = true; @@ -272,10 +277,13 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { if (root != null) { shouldRefresh = false; + fragmentActivity.setCollapsingView(hashtagDetailsBinding.getRoot()); return root; } binding = FragmentHashtagBinding.inflate(inflater, container, false); root = binding.getRoot(); + hashtagDetailsBinding = LayoutHashtagDetailsBinding.inflate(inflater, fragmentActivity.getCollapsingToolbarView(), false); + fragmentActivity.setCollapsingView(hashtagDetailsBinding.getRoot()); return root; } @@ -332,6 +340,14 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe } } + @Override + public void onDestroyView() { + super.onDestroyView(); + if (hashtagDetailsBinding != null) { + fragmentActivity.removeCollapsingView(hashtagDetailsBinding.getRoot()); + } + } + private void init() { if (getArguments() == null) return; final String cookie = settingsHelper.getString(Constants.COOKIE); @@ -374,21 +390,21 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private void setHashtagDetails() { if (isLoggedIn) { - binding.btnFollowTag.setVisibility(View.VISIBLE); - binding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow); - binding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing() - ? R.drawable.ic_outline_person_add_disabled_24 - : R.drawable.ic_outline_person_add_24); - binding.btnFollowTag.setOnClickListener(v -> { + hashtagDetailsBinding.btnFollowTag.setVisibility(View.VISIBLE); + hashtagDetailsBinding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow); + hashtagDetailsBinding.btnFollowTag.setChipIconResource(hashtagModel.getFollowing() + ? R.drawable.ic_outline_person_add_disabled_24 + : R.drawable.ic_outline_person_add_24); + hashtagDetailsBinding.btnFollowTag.setOnClickListener(v -> { final String cookie = settingsHelper.getString(Constants.COOKIE); final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); if (csrfToken != null) { - binding.btnFollowTag.setClickable(false); + hashtagDetailsBinding.btnFollowTag.setClickable(false); if (!hashtagModel.getFollowing()) { tagsService.follow(hashtag.substring(1), csrfToken, new ServiceCallback() { @Override public void onSuccess(final Boolean result) { - binding.btnFollowTag.setClickable(true); + hashtagDetailsBinding.btnFollowTag.setClickable(true); if (!result) { Log.e(TAG, "onSuccess: result is false"); return; @@ -398,7 +414,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe @Override public void onFailure(@NonNull final Throwable t) { - binding.btnFollowTag.setClickable(true); + hashtagDetailsBinding.btnFollowTag.setClickable(true); Log.e(TAG, "onFailure: ", t); final String message = t.getMessage(); Snackbar.make(root, @@ -413,7 +429,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe tagsService.unfollow(hashtag.substring(1), csrfToken, new ServiceCallback() { @Override public void onSuccess(final Boolean result) { - binding.btnFollowTag.setClickable(true); + hashtagDetailsBinding.btnFollowTag.setClickable(true); if (!result) { Log.e(TAG, "onSuccess: result is false"); return; @@ -423,7 +439,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe @Override public void onFailure(@NonNull final Throwable t) { - binding.btnFollowTag.setClickable(true); + hashtagDetailsBinding.btnFollowTag.setClickable(true); Log.e(TAG, "onFailure: ", t); final String message = t.getMessage(); Snackbar.make(root, @@ -436,22 +452,22 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe } }); } else { - binding.btnFollowTag.setVisibility(View.GONE); + hashtagDetailsBinding.btnFollowTag.setVisibility(View.GONE); } final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG); final boolean isFav = favorite != null; - binding.favChip.setVisibility(View.VISIBLE); - binding.favChip.setChipIconResource(isFav ? R.drawable.ic_star_check_24 - : R.drawable.ic_outline_star_plus_24); - binding.favChip.setText(isFav ? R.string.favorite_short : R.string.add_to_favorites); - binding.favChip.setOnClickListener(v -> { + hashtagDetailsBinding.favChip.setVisibility(View.VISIBLE); + hashtagDetailsBinding.favChip.setChipIconResource(isFav ? R.drawable.ic_star_check_24 + : R.drawable.ic_outline_star_plus_24); + hashtagDetailsBinding.favChip.setText(isFav ? R.string.favorite_short : R.string.add_to_favorites); + hashtagDetailsBinding.favChip.setOnClickListener(v -> { final DataBox.FavoriteModel fav = Utils.dataBox.getFavorite(hashtag.substring(1), FavoriteType.HASHTAG); final boolean isFavorite = fav != null; final String message; if (isFavorite) { Utils.dataBox.deleteFavorite(hashtag.substring(1), FavoriteType.HASHTAG); - binding.favChip.setText(R.string.add_to_favorites); - binding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); + hashtagDetailsBinding.favChip.setText(R.string.add_to_favorites); + hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); message = getString(R.string.removed_from_favs); } else { Utils.dataBox.addOrUpdateFavorite(new DataBox.FavoriteModel( @@ -462,8 +478,8 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe null, new Date() )); - binding.favChip.setText(R.string.favorite_short); - binding.favChip.setChipIconResource(R.drawable.ic_star_check_24); + hashtagDetailsBinding.favChip.setText(R.string.favorite_short); + hashtagDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); message = getString(R.string.added_to_favs); } final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); @@ -472,14 +488,14 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe .setAnchorView(fragmentActivity.getBottomNavView()) .show(); }); - binding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic()); + hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic()); final String postCount = String.valueOf(hashtagModel.getPostCount()); final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline, postCount)); span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); - binding.mainTagPostCount.setText(span); - binding.mainTagPostCount.setVisibility(View.VISIBLE); - binding.mainHashtagImage.setOnClickListener(v -> { + hashtagDetailsBinding.mainTagPostCount.setText(span); + hashtagDetailsBinding.mainTagPostCount.setVisibility(View.VISIBLE); + hashtagDetailsBinding.mainHashtagImage.setOnClickListener(v -> { if (!hasStories) return; // show stories final NavDirections action = HashTagFragmentDirections @@ -501,7 +517,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe @Override public void onSuccess(final List storyModels) { if (storyModels != null && !storyModels.isEmpty()) { - binding.mainHashtagImage.setStoriesBorder(); + hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(); hasStories = true; } else { hasStories = false; diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 1171b095..c66543fc 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -27,6 +27,7 @@ import androidx.activity.OnBackPressedDispatcher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; +import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.content.PermissionChecker; import androidx.fragment.app.Fragment; import androidx.navigation.NavController; @@ -49,8 +50,8 @@ import awais.instagrabber.asyncs.LocationFetcher; import awais.instagrabber.asyncs.LocationPostFetchService; import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; -import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; import awais.instagrabber.databinding.FragmentLocationBinding; +import awais.instagrabber.databinding.LayoutLocationDetailsBinding; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.LocationModel; @@ -79,7 +80,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private MainActivity fragmentActivity; private FragmentLocationBinding binding; - private NestedCoordinatorLayout root; + private CoordinatorLayout root; private boolean shouldRefresh = true; private boolean hasStories = false; private boolean opening = false; @@ -93,8 +94,8 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private Set selectedFeedModels; private FeedModel downloadFeedModel; private int downloadChildPosition = -1; - private PostsLayoutPreferences layoutPreferences = PostsLayoutPreferences - .fromJson(settingsHelper.getString(Constants.PREF_LOCATION_POSTS_LAYOUT)); + private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_LOCATION_POSTS_LAYOUT); + private LayoutLocationDetailsBinding locationDetailsBinding; private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) { @Override @@ -197,15 +198,18 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR Utils.openEmailAddress(getContext(), emailId); } - private void openPostDialog(final FeedModel feedModel, + private void openPostDialog(@NonNull final FeedModel feedModel, final View profilePicView, final View mainPostImage, final int position) { if (opening) return; if (TextUtils.isEmpty(feedModel.getProfileModel().getUsername())) { opening = true; - new PostFetcher(feedModel.getShortCode(), newFeedModel -> openPostDialog(newFeedModel, profilePicView, mainPostImage, position)) - .execute(); + new PostFetcher(feedModel.getShortCode(), newFeedModel -> { + opening = false; + if (newFeedModel == null) return; + openPostDialog(newFeedModel, profilePicView, mainPostImage, position); + }).execute(); return; } opening = true; @@ -273,10 +277,13 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR @Nullable final Bundle savedInstanceState) { if (root != null) { shouldRefresh = false; + fragmentActivity.setCollapsingView(locationDetailsBinding.getRoot()); return root; } binding = FragmentLocationBinding.inflate(inflater, container, false); root = binding.getRoot(); + locationDetailsBinding = LayoutLocationDetailsBinding.inflate(inflater, fragmentActivity.getCollapsingToolbarView(), false); + fragmentActivity.setCollapsingView(locationDetailsBinding.getRoot()); return root; } @@ -333,14 +340,22 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } } + @Override + public void onDestroyView() { + super.onDestroyView(); + if (locationDetailsBinding != null) { + fragmentActivity.removeCollapsingView(locationDetailsBinding.getRoot()); + } + } + private void init() { if (getArguments() == null) return; final String cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null; final LocationFragmentArgs fragmentArgs = LocationFragmentArgs.fromBundle(getArguments()); locationId = fragmentArgs.getLocationId(); - binding.favChip.setVisibility(View.GONE); - binding.btnMap.setVisibility(View.GONE); + locationDetailsBinding.favChip.setVisibility(View.GONE); + locationDetailsBinding.btnMap.setVisibility(View.GONE); setTitle(); fetchLocationModel(); } @@ -380,68 +395,68 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private void setupLocationDetails() { final String locationId = locationModel.getId(); // binding.swipeRefreshLayout.setRefreshing(true); - binding.mainLocationImage.setImageURI(locationModel.getSdProfilePic()); + locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic()); final String postCount = String.valueOf(locationModel.getPostCount()); final SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count_inline, postCount)); span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); - binding.mainLocPostCount.setText(span); - binding.mainLocPostCount.setVisibility(View.VISIBLE); - binding.locationFullName.setText(locationModel.getName()); + locationDetailsBinding.mainLocPostCount.setText(span); + locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE); + locationDetailsBinding.locationFullName.setText(locationModel.getName()); CharSequence biography = locationModel.getBio(); // binding.locationBiography.setCaptionIsExpandable(true); // binding.locationBiography.setCaptionIsExpanded(true); if (TextUtils.isEmpty(biography)) { - binding.locationBiography.setVisibility(View.GONE); + locationDetailsBinding.locationBiography.setVisibility(View.GONE); } else if (TextUtils.hasMentions(biography)) { - binding.locationBiography.setVisibility(View.VISIBLE); + locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE); biography = TextUtils.getMentionText(biography); - binding.locationBiography.setText(biography, TextView.BufferType.SPANNABLE); + locationDetailsBinding.locationBiography.setText(biography, TextView.BufferType.SPANNABLE); // binding.locationBiography.setMentionClickListener(mentionClickListener); } else { - binding.locationBiography.setVisibility(View.VISIBLE); - binding.locationBiography.setText(biography); - binding.locationBiography.setMentionClickListener(null); + locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE); + locationDetailsBinding.locationBiography.setText(biography); + locationDetailsBinding.locationBiography.setMentionClickListener(null); } if (!locationModel.getGeo().startsWith("geo:0.0,0.0?z=17")) { - binding.btnMap.setVisibility(View.VISIBLE); - binding.btnMap.setOnClickListener(v -> { + locationDetailsBinding.btnMap.setVisibility(View.VISIBLE); + locationDetailsBinding.btnMap.setOnClickListener(v -> { final Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(locationModel.getGeo())); startActivity(intent); }); } else { - binding.btnMap.setVisibility(View.GONE); - binding.btnMap.setOnClickListener(null); + locationDetailsBinding.btnMap.setVisibility(View.GONE); + locationDetailsBinding.btnMap.setOnClickListener(null); } final String url = locationModel.getUrl(); if (TextUtils.isEmpty(url)) { - binding.locationUrl.setVisibility(View.GONE); + locationDetailsBinding.locationUrl.setVisibility(View.GONE); } else if (!url.startsWith("http")) { - binding.locationUrl.setVisibility(View.VISIBLE); - binding.locationUrl.setText(TextUtils.getSpannableUrl("http://" + url)); + locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE); + locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl("http://" + url)); } else { - binding.locationUrl.setVisibility(View.VISIBLE); - binding.locationUrl.setText(TextUtils.getSpannableUrl(url)); + locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE); + locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl(url)); } final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(locationId, FavoriteType.LOCATION); final boolean isFav = favorite != null; - binding.favChip.setVisibility(View.VISIBLE); - binding.favChip.setChipIconResource(isFav ? R.drawable.ic_star_check_24 - : R.drawable.ic_outline_star_plus_24); - binding.favChip.setText(isFav ? R.string.favorite_short : R.string.add_to_favorites); - binding.favChip.setOnClickListener(v -> { + locationDetailsBinding.favChip.setVisibility(View.VISIBLE); + locationDetailsBinding.favChip.setChipIconResource(isFav ? R.drawable.ic_star_check_24 + : R.drawable.ic_outline_star_plus_24); + locationDetailsBinding.favChip.setText(isFav ? R.string.favorite_short : R.string.add_to_favorites); + locationDetailsBinding.favChip.setOnClickListener(v -> { final DataBox.FavoriteModel fav = Utils.dataBox.getFavorite(locationId, FavoriteType.LOCATION); final boolean isFavorite = fav != null; final String message; if (isFavorite) { Utils.dataBox.deleteFavorite(locationId, FavoriteType.LOCATION); - binding.favChip.setText(R.string.add_to_favorites); - binding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); + locationDetailsBinding.favChip.setText(R.string.add_to_favorites); + locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); message = getString(R.string.removed_from_favs); } else { Utils.dataBox.addOrUpdateFavorite(new DataBox.FavoriteModel( @@ -452,8 +467,8 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR locationModel.getSdProfilePic(), new Date() )); - binding.favChip.setText(R.string.favorite_short); - binding.favChip.setChipIconResource(R.drawable.ic_star_check_24); + locationDetailsBinding.favChip.setText(R.string.favorite_short); + locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); message = getString(R.string.added_to_favs); } final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); @@ -462,7 +477,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR .setAnchorView(fragmentActivity.getBottomNavView()) .show(); }); - binding.mainLocationImage.setOnClickListener(v -> { + locationDetailsBinding.mainLocationImage.setOnClickListener(v -> { if (hasStories) { // show stories final NavDirections action = LocationFragmentDirections @@ -484,7 +499,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR @Override public void onSuccess(final List storyModels) { if (storyModels != null && !storyModels.isEmpty()) { - binding.mainLocationImage.setStoriesBorder(); + locationDetailsBinding.mainLocationImage.setStoriesBorder(); hasStories = true; } storiesFetching = false; diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java index 2b7b6e7c..5564d0e1 100644 --- a/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java @@ -35,6 +35,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.PermissionChecker; +import androidx.core.view.ViewCompat; import androidx.core.widget.NestedScrollView; import androidx.fragment.app.DialogFragment; import androidx.navigation.NavController; @@ -71,6 +72,7 @@ import awais.instagrabber.customviews.drawee.AnimatedZoomableController; import awais.instagrabber.databinding.DialogPostViewBinding; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.PostChild; +import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; @@ -111,6 +113,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { private int sliderPosition = -1; private DialogInterface.OnShowListener onShowListener; private boolean isLoggedIn; + private boolean hasBeenToggled = false; private final VerticalDragHelper.OnVerticalDragListener onVerticalDragListener = new VerticalDragHelper.OnVerticalDragListener() { @@ -125,8 +128,8 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { @Override public void onDragEnd() { // animate and dismiss if user drags the view more that 30% of the view - if (Math.abs(binding.getRoot().getY()) > Utils.displayMetrics.heightPixels * 0.35) { - animateAndDismiss(binding.getRoot().getY() < 0 ? -1 : 1); + if (Math.abs(binding.getRoot().getY()) > Utils.displayMetrics.heightPixels * 0.25) { + animateAndDismiss(binding.getRoot().getY() < 0 ? 1 : -1); return; } // animate back the view to proper position @@ -134,16 +137,16 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { } @Override - public void onFling(final float flingVelocity) { + public void onFling(final double flingVelocity) { // animate and dismiss if user flings up/down - animateAndDismiss(flingVelocity < 0 ? -1 : 1); + animateAndDismiss(flingVelocity > 0 ? 1 : -1); } private void animateAndDismiss(final int direction) { final int height = binding.getRoot().getHeight(); final int finalYDist = height + Utils.getStatusBarHeight(context); // less than 0 means up direction, else down - final int finalY = direction < 0 ? -finalYDist : finalYDist; + final int finalY = direction > 0 ? -finalYDist : finalYDist; animateY(binding.getRoot(), finalY, 200, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(final Animator animation) { @@ -231,6 +234,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { return; } feedModel = (FeedModel) feedModelSerializable; + if (feedModel == null) return; if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_SLIDER) { sliderPosition = arguments.getInt(ARG_SLIDER_POSITION, 0); } @@ -242,12 +246,20 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { binding = DialogPostViewBinding.inflate(inflater, container, false); - return binding.getRoot(); + final ConstraintLayout root = binding.getRoot(); + final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + root.getViewTreeObserver().removeOnPreDrawListener(this); + return false; + } + }; + root.getViewTreeObserver().addOnPreDrawListener(preDrawListener); + return root; } @Override public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { - setupToolbar(); init(); } @@ -292,8 +304,8 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { } @Override - public void onDestroy() { - super.onDestroy(); + public void onDestroyView() { + super.onDestroyView(); switch (feedModel.getItemType()) { case MEDIA_TYPE_VIDEO: if (videoPlayerViewHelper != null) { @@ -311,6 +323,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { @Override public void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); + if (feedModel == null) return; if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_SLIDER) { outState.putInt(ARG_SLIDER_POSITION, sliderPosition); } @@ -400,6 +413,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { } private void init() { + if (feedModel == null) return; final String cookie = settingsHelper.getString(Constants.COOKIE); isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null; if (!wasPaused && (sharedProfilePicElement != null || sharedMainPostElement != null)) { @@ -422,12 +436,14 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { private void setupComment() { binding.comment.setOnClickListener(v -> { + final ProfileModel profileModel = feedModel.getProfileModel(); + if (profileModel == null) return; final NavController navController = getNavController(); if (navController == null) return; final Bundle bundle = new Bundle(); bundle.putString("shortCode", feedModel.getShortCode()); bundle.putString("postId", feedModel.getPostId()); - bundle.putString("postUserId", feedModel.getProfileModel().getId()); + bundle.putString("postUserId", profileModel.getId()); navController.navigate(R.id.action_global_commentsViewerFragment, bundle); }); binding.comment.setOnLongClickListener(v -> { @@ -505,12 +521,12 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { final String userId = CookieUtils.getUserIdFromCookie(COOKIE); final String csrfToken = CookieUtils.getCsrfTokenFromCookie(COOKIE); v.setEnabled(false); - final int textRes; - if (!feedModel.getLike()) { - textRes = R.string.liking; - } else { - textRes = R.string.unliking; - } + // final int textRes; + // if (!feedModel.getLike()) { + // textRes = R.string.liking; + // } else { + // textRes = R.string.unliking; + // } if (!feedModel.getLike()) { mediaService.like(feedModel.getPostId(), userId, csrfToken, likeCallback); } else { @@ -526,15 +542,15 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { private void setLikedResources(final boolean liked) { final int iconResource; final int tintResource; - final int textResId; + // final int textResId; if (liked) { iconResource = R.drawable.ic_like; tintResource = R.color.red_600; - textResId = R.string.unlike_without_count; + // textResId = R.string.unlike_without_count; } else { iconResource = R.drawable.ic_not_liked; tintResource = R.color.white; - textResId = R.string.like_without_count; + // textResId = R.string.like_without_count; } binding.like.setIconResource(iconResource); binding.like.setIconTintResource(tintResource); @@ -586,12 +602,12 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { final String userId = CookieUtils.getUserIdFromCookie(COOKIE); final String csrfToken = CookieUtils.getCsrfTokenFromCookie(COOKIE); binding.save.setEnabled(false); - final int textRes; - if (!feedModel.isSaved()) { - textRes = R.string.saving; - } else { - textRes = R.string.removing; - } + // final int textRes; + // if (!feedModel.isSaved()) { + // textRes = R.string.saving; + // } else { + // textRes = R.string.removing; + // } if (!feedModel.isSaved()) { mediaService.save(feedModel.getPostId(), userId, csrfToken, saveCallback); } else { @@ -607,15 +623,15 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { private void setSavedResources(final boolean saved) { final int iconResource; final int tintResource; - final int textResId; + // final int textResId; if (saved) { iconResource = R.drawable.ic_class_24; tintResource = R.color.blue_700; - textResId = R.string.saved; + // textResId = R.string.saved; } else { iconResource = R.drawable.ic_outline_class_24; tintResource = R.color.white; - textResId = R.string.save; + // textResId = R.string.save; } binding.save.setIconResource(iconResource); binding.save.setIconTintResource(tintResource); @@ -626,7 +642,12 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { if (!wasPaused && sharedProfilePicElement != null) { addSharedElement(sharedProfilePicElement, binding.profilePic); } - final String uri = feedModel.getProfileModel().getSdProfilePic(); + final ProfileModel profileModel = feedModel.getProfileModel(); + if (profileModel == null) { + binding.profilePic.setVisibility(View.GONE); + return; + } + final String uri = profileModel.getSdProfilePic(); final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uri)).build(); final DraweeController controller = Fresco .newDraweeControllerBuilder() @@ -647,23 +668,22 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { }) .build(); binding.profilePic.setController(controller); - binding.profilePic.setOnClickListener(v -> navigateToProfile("@" + feedModel.getProfileModel().getUsername())); - } - - private void setupToolbar() { - // fragmentActivity.fitSystemWindows(true); - // final ActionBar actionBar = fragmentActivity.getSupportActionBar(); - // if (actionBar == null) return; - // actionBar.setTitle(null); - // actionBar.setSubtitle(null); + binding.profilePic.setOnClickListener(v -> navigateToProfile("@" + profileModel.getUsername())); } private void setupTitles() { - binding.title.setText(feedModel.getProfileModel().getUsername()); - binding.righttitle.setText(feedModel.getProfileModel().getName()); - binding.isVerified.setVisibility(feedModel.getProfileModel().isVerified() ? View.VISIBLE : View.GONE); - binding.title.setOnClickListener(v -> navigateToProfile("@" + feedModel.getProfileModel().getUsername())); - binding.righttitle.setOnClickListener(v -> navigateToProfile("@" + feedModel.getProfileModel().getUsername())); + final ProfileModel profileModel = feedModel.getProfileModel(); + if (profileModel == null) { + binding.title.setVisibility(View.GONE); + binding.righttitle.setVisibility(View.GONE); + binding.subtitle.setVisibility(View.GONE); + return; + } + binding.title.setText(profileModel.getUsername()); + binding.righttitle.setText(profileModel.getName()); + binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); + binding.title.setOnClickListener(v -> navigateToProfile("@" + profileModel.getUsername())); + binding.righttitle.setOnClickListener(v -> navigateToProfile("@" + profileModel.getUsername())); final String locationName = feedModel.getLocationName(); if (!TextUtils.isEmpty(locationName)) { binding.subtitle.setText(locationName); @@ -761,7 +781,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { return true; }); binding.share.setOnClickListener(v -> { - final boolean isPrivate = feedModel.getProfileModel().isPrivate(); + final ProfileModel profileModel = feedModel.getProfileModel(); + if (profileModel == null) return; + final boolean isPrivate = profileModel.isPrivate(); if (isPrivate) Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_LONG).show(); Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); @@ -849,6 +871,20 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { if (!wasPaused && sharedMainPostElement != null) { addSharedElement(sharedMainPostElement, binding.sliderParent); } + final boolean hasVideo = feedModel.getSliderItems() + .stream() + .anyMatch(postChild -> postChild.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO); + if (hasVideo) { + final View child = binding.sliderParent.getChildAt(0); + if (child instanceof RecyclerView) { + ((RecyclerView) child).setItemViewCacheSize(feedModel.getSliderItems().size()); + ((RecyclerView) child).addRecyclerListener(holder -> { + if (holder instanceof SliderVideoViewHolder) { + ((SliderVideoViewHolder) holder).releasePlayer(); + } + }); + } + } sliderItemsAdapter = new SliderItemsAdapter(onVerticalDragListener, binding.playerControls, true, new SliderCallbackAdapter() { @Override public void onThumbnailLoaded(final int position) { @@ -858,19 +894,17 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { @Override public void onItemClicked(final int position) { - toggleDetails(); } @Override public void onPlayerPlay(final int position) { - if (!detailsVisible) return; - toggleDetails(); + if (!detailsVisible || hasBeenToggled) return; showPlayerControls(); } @Override public void onPlayerPause(final int position) { - if (detailsVisible) return; + if (detailsVisible || hasBeenToggled) return; toggleDetails(); } }); @@ -903,7 +937,22 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { final String text = (position + 1) + "/" + size; binding.mediaCounter.setText(text); final PostChild postChild = feedModel.getSliderItems().get(position); + final View view = binding.sliderParent.getChildAt(0); + if (prevPosition != -1) { + if (view instanceof RecyclerView) { + final RecyclerView.ViewHolder viewHolder = ((RecyclerView) view).findViewHolderForAdapterPosition(prevPosition); + if (viewHolder instanceof SliderVideoViewHolder) { + ((SliderVideoViewHolder) viewHolder).removeCallbacks(); + } + } + } if (postChild.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) { + if (view instanceof RecyclerView) { + final RecyclerView.ViewHolder viewHolder = ((RecyclerView) view).findViewHolderForAdapterPosition(position); + if (viewHolder instanceof SliderVideoViewHolder) { + ((SliderVideoViewHolder) viewHolder).resetPlayerTimeline(); + } + } enablePlayerControls(true); return; } @@ -1029,9 +1078,15 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { private void showPlayerControls() { hideCaption(); - // previously invisible view View view = binding.playerControls.getRoot(); + if (view != null && view.getVisibility() == View.VISIBLE) { + return; + } + if (!ViewCompat.isAttachedToWindow(view)) { + view.setVisibility(View.VISIBLE); + return; + } // get the center for the clipping circle int cx = view.getWidth() / 2; // int cy = view.getHeight() / 2; @@ -1052,6 +1107,13 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { private void hidePlayerControls() { // previously visible view final View view = binding.playerControls.getRoot(); + if (view != null && view.getVisibility() == View.GONE) { + return; + } + if (!ViewCompat.isAttachedToWindow(view)) { + view.setVisibility(View.GONE); + return; + } // get the center for the clipping circle int cx = view.getWidth() / 2; @@ -1078,6 +1140,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { } private void toggleDetails() { + hasBeenToggled = true; binding.getRoot().post(() -> { TransitionManager.beginDelayedTransition(binding.getRoot()); if (detailsVisible) { @@ -1111,7 +1174,9 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment { } binding.profilePic.setVisibility(View.VISIBLE); binding.title.setVisibility(View.VISIBLE); - binding.isVerified.setVisibility(feedModel.getProfileModel().isVerified() ? View.VISIBLE : View.GONE); + binding.isVerified.setVisibility(feedModel.getProfileModel() != null + ? feedModel.getProfileModel().isVerified() ? View.VISIBLE : View.GONE + : View.GONE); binding.righttitle.setVisibility(View.VISIBLE); binding.topBg.setVisibility(View.VISIBLE); if (!TextUtils.isEmpty(binding.subtitle.getText())) { diff --git a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java index 2a2439aa..fbb9f8f7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java @@ -45,7 +45,6 @@ import awais.instagrabber.utils.Utils; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; -import static awais.instagrabber.utils.Utils.settingsHelper; public final class SavedViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final int STORAGE_PERM_REQUEST_CODE = 8020; @@ -62,7 +61,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL private Set selectedFeedModels; private FeedModel downloadFeedModel; private int downloadChildPosition = -1; - private PostsLayoutPreferences layoutPreferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(getPostsLayoutPreferenceKey())); + private PostsLayoutPreferences layoutPreferences; private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) { @Override @@ -275,46 +274,8 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL username = fragmentArgs.getUsername(); profileId = fragmentArgs.getProfileId(); type = fragmentArgs.getType(); + layoutPreferences = Utils.getPostsLayoutPreferences(getPostsLayoutPreferenceKey()); setupPosts(); - // postsAdapter = new PostsAdapter((postModel, position) -> { - // if (postsAdapter.isSelecting()) { - // if (actionMode == null) return; - // final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size()); - // actionMode.setTitle(title); - // return; - // } - // if (checkAndResetAction()) return; - // final List postModels = postsViewModel.getList().getValue(); - // if (postModels == null || postModels.size() == 0) return; - // if (postModels.get(0) == null) return; - // final String postId = postModels.get(0).getPostId(); - // final boolean isId = postId != null; - // final String[] idsOrShortCodes = new String[postModels.size()]; - // for (int i = 0; i < postModels.size(); i++) { - // final PostModel tempPostModel = postModels.get(i); - // final String tempId = tempPostModel.getPostId(); - // final String finalPostId = type == PostItemType.LIKED ? tempId.substring(0, tempId.indexOf("_")) : tempId; - // idsOrShortCodes[i] = isId ? finalPostId - // : tempPostModel.getShortCode(); - // } - // final NavDirections action = ProfileFragmentDirections.actionGlobalPostViewFragment( - // position, - // idsOrShortCodes, - // isId); - // NavHostFragment.findNavController(this).navigate(action); - // }, (model, position) -> { - // if (!postsAdapter.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; - // }); } private void setupPosts() { diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index 5501f5b5..40f04fd3 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -484,7 +484,7 @@ public class StoryViewerFragment extends Fragment { if (isHighlight) { final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel; final List models = highlightsViewModel.getList().getValue(); - if (models == null) return; + if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) return; final HighlightModel model = models.get(currentFeedStoryIndex); currentStoryMediaId = model.getId(); currentStoryUsername = model.getTitle(); diff --git a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java index 87beef53..81830779 100644 --- a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java @@ -22,6 +22,7 @@ import androidx.activity.OnBackPressedCallback; import androidx.activity.OnBackPressedDispatcher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.content.PermissionChecker; import androidx.core.graphics.ColorUtils; import androidx.fragment.app.Fragment; @@ -46,7 +47,6 @@ import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.asyncs.DiscoverPostFetchService; import awais.instagrabber.customviews.PrimaryActionModeCallback; -import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; import awais.instagrabber.databinding.FragmentTopicPostsBinding; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.fragments.main.DiscoverFragmentDirections; @@ -60,7 +60,6 @@ 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; @@ -68,14 +67,14 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O private MainActivity fragmentActivity; private FragmentTopicPostsBinding binding; - private NestedCoordinatorLayout root; + private CoordinatorLayout root; private boolean shouldRefresh = true; private TopicCluster topicCluster; private ActionMode actionMode; private Set selectedFeedModels; private FeedModel downloadFeedModel; private int downloadChildPosition = -1; - private PostsLayoutPreferences layoutPreferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_TOPIC_POSTS_LAYOUT)); + private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_TOPIC_POSTS_LAYOUT); private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) { @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java index 442296fa..37937d27 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java @@ -10,6 +10,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.MutableLiveData; @@ -29,7 +30,6 @@ import java.util.List; import awais.instagrabber.BuildConfig; import awais.instagrabber.adapters.DirectMessageInboxAdapter; import awais.instagrabber.asyncs.direct_messages.InboxFetcher; -import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; import awais.instagrabber.databinding.FragmentDirectMessagesInboxBinding; import awais.instagrabber.interfaces.FetchListener; @@ -41,7 +41,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh private static final String TAG = "DirectMessagesInboxFrag"; private FragmentActivity fragmentActivity; - private NestedCoordinatorLayout root; + private CoordinatorLayout root; private RecyclerView inboxList; private RecyclerLazyLoader lazyLoader; private LinearLayoutManager layoutManager; diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java index f13ad99b..b6605ddc 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java @@ -21,7 +21,6 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.AppCompatButton; import androidx.appcompat.widget.AppCompatImageView; import androidx.fragment.app.Fragment; -import androidx.navigation.NavDirections; import androidx.navigation.fragment.NavHostFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -35,7 +34,6 @@ import java.util.Arrays; import java.util.List; import awais.instagrabber.BuildConfig; -import awais.instagrabber.ProfileNavGraphDirections; import awais.instagrabber.R; import awais.instagrabber.adapters.DirectMessageMembersAdapter; import awais.instagrabber.asyncs.direct_messages.DirectMessageInboxThreadFetcher; @@ -68,6 +66,7 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr @Override public void onResult(final InboxThreadModel threadModel) { + if (threadModel == null) return; final List adminList = Arrays.asList(threadModel.getAdmins()); final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); if (userIdFromCookie == null) return; @@ -93,8 +92,9 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr final Object tag = v.getTag(); if (tag instanceof ProfileModel) { ProfileModel model = (ProfileModel) tag; - final NavDirections action = DirectMessageThreadFragmentDirections.actionGlobalProfileFragment("@" + model.getUsername()); - NavHostFragment.findNavController(this).navigate(action); + final Bundle bundle = new Bundle(); + bundle.putString("username", "@" + model.getUsername()); + NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle); } }; @@ -110,8 +110,9 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr }); final DialogInterface.OnClickListener clickListener = (d, w) -> { if (w == 0) { - final NavDirections action = DirectMessageThreadFragmentDirections.actionGlobalProfileFragment("@" + model.getUsername()); - NavHostFragment.findNavController(this).navigate(action); + final Bundle bundle = new Bundle(); + bundle.putString("username", "@" + model.getUsername()); + NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle); } else if (w == 1) { new ChangeSettings(titleText.getText().toString()).execute("remove_users", model.getId()); onRefresh(); diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java index a225a376..087468cb 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java @@ -18,8 +18,8 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.widget.ArrayAdapter; -import android.widget.LinearLayout; import android.widget.Toast; import androidx.annotation.NonNull; @@ -33,7 +33,6 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; -import androidx.navigation.NavController; import androidx.navigation.NavDirections; import androidx.navigation.fragment.NavHostFragment; import androidx.recyclerview.widget.LinearLayoutManager; @@ -54,7 +53,6 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; -import awais.instagrabber.ProfileNavGraphDirections; import awais.instagrabber.R; import awais.instagrabber.adapters.DirectMessageItemsAdapter; import awais.instagrabber.asyncs.ImageUploader; @@ -71,7 +69,6 @@ import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.direct_messages.DirectItemModel; import awais.instagrabber.models.direct_messages.InboxThreadModel; import awais.instagrabber.models.enums.DirectItemType; -import awais.instagrabber.models.enums.DownloadMethod; import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.UserInboxDirection; import awais.instagrabber.utils.Constants; @@ -479,9 +476,16 @@ public class DirectMessageThreadFragment extends Fragment { if (text != null) { binding.commentText.setText(""); } else { - final LinearLayout dim = (LinearLayout) binding.messageList.findViewWithTag(directItemModel).getParent(); - if (dim.findViewById(R.id.liked_container) != null) { - dim.findViewById(R.id.liked_container).setVisibility(delete ? View.GONE : View.VISIBLE); + final View viewWithTag = binding.messageList.findViewWithTag(directItemModel); + if (viewWithTag != null) { + final ViewParent dim = viewWithTag.getParent(); + if (dim instanceof View) { + final View dimView = (View) dim; + final View likedContainer = dimView.findViewById(R.id.liked_container); + if (likedContainer != null) { + likedContainer.setVisibility(delete ? View.GONE : View.VISIBLE); + } + } } directItemModel.setLiked(); } @@ -549,8 +553,9 @@ public class DirectMessageThreadFragment extends Fragment { } private void searchUsername(final String text) { - final NavDirections action = DirectMessageThreadFragmentDirections.actionGlobalProfileFragment("@" + text); - NavHostFragment.findNavController(this).navigate(action); + final Bundle bundle = new Bundle(); + bundle.putString("username", "@" + text); + NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle); } public static class DirectItemModelListViewModel extends ViewModel { diff --git a/app/src/main/java/awais/instagrabber/fragments/main/DiscoverFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/DiscoverFragment.java index db91702c..adc6fe43 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/DiscoverFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/DiscoverFragment.java @@ -2,13 +2,10 @@ package awais.instagrabber.fragments.main; import android.os.Bundle; import android.util.Log; -import android.view.ActionMode; import android.view.LayoutInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.coordinatorlayout.widget.CoordinatorLayout; @@ -18,10 +15,8 @@ import androidx.navigation.fragment.FragmentNavigator; import androidx.navigation.fragment.NavHostFragment; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.DiscoverTopicsAdapter; -import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration; import awais.instagrabber.databinding.FragmentDiscoverBinding; import awais.instagrabber.utils.Utils; @@ -35,77 +30,10 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR private MainActivity fragmentActivity; private CoordinatorLayout root; private FragmentDiscoverBinding binding; - private ActionMode actionMode; private TopicClusterViewModel topicClusterViewModel; private boolean shouldRefresh = true; private DiscoverService discoverService; - // private final FetchListener postsFetchListener = new FetchListener() { - // @Override - // public void doBefore() {} - // - // @Override - // public void onResult(final DiscoverItemModel[] result) { - // if (result == null || result.length <= 0) { - // binding.swipeRefreshLayout.setRefreshing(false); - // final Context context = getContext(); - // if (context == null) return; - // Toast.makeText(context, R.string.discover_empty, Toast.LENGTH_SHORT).show(); - // return; - // } - // List current = discoverItemViewModel.getList().getValue(); - // final List resultList = Arrays.asList(result); - // current = current == null ? new ArrayList<>() : new ArrayList<>(current); // copy to modifiable list - // if (isPullToRefresh) { - // current = resultList; - // isPullToRefresh = false; - // } else { - // current.addAll(resultList); - // } - // discoverItemViewModel.getList().postValue(current); - // binding.swipeRefreshLayout.setRefreshing(false); - // final DiscoverItemModel discoverItemModel = result[result.length - 1]; - // if (discoverItemModel != null) { - // discoverEndMaxId = discoverItemModel.getNextMaxId(); - // discoverHasMore = discoverItemModel.hasMore(); - // discoverItemModel.setMore(false, null); - // } - // } - // }; - private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) { - @Override - public void handleOnBackPressed() { - setEnabled(false); - remove(); - // if (discoverAdapter == null) return; - // discoverAdapter.clearSelection(); - } - }; - private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback( - R.menu.multi_select_download_menu, - new PrimaryActionModeCallback.CallbacksHelper() { - @Override - public void onDestroy(final ActionMode mode) { - onBackPressedCallback.handleOnBackPressed(); - } - - @Override - public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { - if (item.getItemId() == R.id.action_download) { - // if (discoverAdapter == null) return false; - // final Context context = getContext(); - // if (context == null) return false; - // DownloadUtils.batchDownload(context, - // null, - // DownloadMethod.DOWNLOAD_DISCOVER, - // discoverAdapter.getSelectedModels()); - // checkAndResetAction(); - return true; - } - return false; - } - }); - @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -135,31 +63,6 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR } private void init() { - // setExitSharedElementCallback(new SharedElementCallback() { - // @Override - // public void onSharedElementsArrived(final List sharedElementNames, - // final List sharedElements, - // final OnSharedElementsReadyListener listener) { - // super.onSharedElementsArrived(sharedElementNames, sharedElements, listener); - // Log.d(TAG, "onSharedElementsArrived: sharedElementNames: " + sharedElementNames); - // } - // - // @Override - // public void onSharedElementEnd(final List sharedElementNames, - // final List sharedElements, - // final List sharedElementSnapshots) { - // super.onSharedElementEnd(sharedElementNames, sharedElements, sharedElementSnapshots); - // Log.d(TAG, "onSharedElementEnd: sharedElementNames: " + sharedElementNames); - // } - // - // @Override - // public void onSharedElementStart(final List sharedElementNames, - // final List sharedElements, - // final List sharedElementSnapshots) { - // super.onSharedElementStart(sharedElementNames, sharedElements, sharedElementSnapshots); - // Log.d(TAG, "onSharedElementStart: sharedElementNames: " + sharedElementNames); - // } - // }); setupTopics(); fetchTopics(); } @@ -175,7 +78,6 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR 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()); @@ -184,86 +86,12 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR topicClusterViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList); } - private void setupExplore() { - // discoverItemViewModel = new ViewModelProvider(fragmentActivity).get(DiscoverItemViewModel.class); - // final Context context = getContext(); - // if (context == null) return; - // final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(context, Utils.convertDpToPx(110)); - // binding.postsRecyclerView.setLayoutManager(layoutManager); - // binding.postsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4))); - // binding.discoverType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - // @Override - // public void onItemSelected(AdapterView parent, View view, int pos, long id) { - // if (topicIds == null || topicIds.length <= 0) return; - // currentTopic = topicIds[pos]; - // onRefresh(); - // } - // - // @Override - // public void onNothingSelected(AdapterView parent) {} - // }); - // discoverAdapter = new DiscoverAdapter((model, position) -> { - // if (discoverAdapter.isSelecting()) { - // if (actionMode == null) return; - // final String title = getString(R.string.number_selected, discoverAdapter.getSelectedModels().size()); - // actionMode.setTitle(title); - // return; - // } - // if (checkAndResetAction()) return; - // final List 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() { @Override public void onSuccess(final DiscoverService.TopicalExploreResponse result) { + if (result == null) return; topicClusterViewModel.getList().postValue(result.getClusters()); binding.swipeRefreshLayout.setRefreshing(false); } @@ -275,25 +103,4 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR } }); } - - // private void fetchPosts() { - // binding.swipeRefreshLayout.setRefreshing(true); - // new DiscoverFetcher(currentTopic, discoverEndMaxId, rankToken, postsFetchListener, false) - // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - // } - - private boolean checkAndResetAction() { - if (!onBackPressedCallback.isEnabled() && actionMode == null) { - return false; - } - if (onBackPressedCallback.isEnabled()) { - onBackPressedCallback.setEnabled(false); - onBackPressedCallback.remove(); - } - if (actionMode != null) { - actionMode.finish(); - actionMode = null; - } - return true; - } } diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index c75512e2..afc14848 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -28,6 +28,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.common.collect.ImmutableList; import java.util.List; @@ -54,7 +55,6 @@ import awais.instagrabber.webservices.StoriesService; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; -import static awais.instagrabber.utils.Utils.settingsHelper; public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "FeedFragment"; @@ -72,7 +72,8 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre private Set selectedFeedModels; private FeedModel downloadFeedModel; private int downloadChildPosition = -1; - private PostsLayoutPreferences layoutPreferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_POSTS_LAYOUT)); + private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_POSTS_LAYOUT); + private RecyclerView storiesRecyclerView; private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() { @Override @@ -249,6 +250,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre final Bundle savedInstanceState) { if (root != null) { shouldRefresh = false; + if (storiesRecyclerView != null) { + fragmentActivity.setCollapsingView(storiesRecyclerView); + } return root; } binding = FragmentFeedBinding.inflate(inflater, container, false); @@ -279,18 +283,10 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre return super.onOptionsItemSelected(item); } - @Override - public void onPause() { - super.onPause(); - // if (videoAwareRecyclerScroller != null) { - // videoAwareRecyclerScroller.stopPlaying(); - // } - } - @Override public void onResume() { super.onResume(); - binding.feedSwipeRefreshLayout.setRefreshing(false); + updateSwipeRefreshState(); // if (videoAwareRecyclerScroller != null && shouldAutoPlay) { // videoAwareRecyclerScroller.startPlaying(); // } @@ -302,6 +298,14 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre fetchStories(); } + @Override + public void onDestroyView() { + super.onDestroyView(); + if (storiesRecyclerView != null) { + fragmentActivity.removeCollapsingView(storiesRecyclerView); + } + } + @Override public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); @@ -349,8 +353,15 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre }); final Context context = getContext(); if (context == null) return; - binding.feedStoriesRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)); - binding.feedStoriesRecyclerView.setAdapter(feedStoriesAdapter); + storiesRecyclerView = new RecyclerView(context); + final CollapsingToolbarLayout.LayoutParams params = new CollapsingToolbarLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + params.setMargins(0, Utils.getActionBarHeight(context), 0, 0); + storiesRecyclerView.setLayoutParams(params); + storiesRecyclerView.setClipToPadding(false); + storiesRecyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)); + storiesRecyclerView.setAdapter(feedStoriesAdapter); + fragmentActivity.setCollapsingView(storiesRecyclerView); feedStoriesViewModel.getList().observe(fragmentActivity, feedStoriesAdapter::submitList); fetchStories(); } @@ -387,6 +398,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre public void scrollToTop() { binding.feedRecyclerView.smoothScrollToPosition(0); - binding.storiesContainer.setExpanded(true); + // binding.storiesContainer.setExpanded(true); } } diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index afc05d6a..022e056e 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -13,6 +13,7 @@ import android.text.style.RelativeSizeSpan; import android.text.style.StyleSpan; import android.util.Log; import android.view.ActionMode; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -60,8 +61,8 @@ import awais.instagrabber.asyncs.UsernameFetcher; import awais.instagrabber.asyncs.direct_messages.CreateThreadAction; import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper; -import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; import awais.instagrabber.databinding.FragmentProfileBinding; +import awais.instagrabber.databinding.LayoutProfileDetailsBinding; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.ProfilePicDialogFragment; import awais.instagrabber.fragments.PostViewV2Fragment; @@ -117,7 +118,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private Set selectedFeedModels; private FeedModel downloadFeedModel; private int downloadChildPosition = -1; - private PostsLayoutPreferences layoutPreferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_PROFILE_POSTS_LAYOUT)); + private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_PROFILE_POSTS_LAYOUT); private final Runnable usernameSettingRunnable = () -> { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); @@ -281,6 +282,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } } }; + private LayoutProfileDetailsBinding profileDetailsBinding; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { @@ -306,21 +308,26 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe final boolean isSame = ("@" + profileModelUsername).equals(this.username); if (isSame) { setUsernameDelayed(); + fragmentActivity.setCollapsingView(profileDetailsBinding.getRoot()); shouldRefresh = false; return root; } } if (username == null || !username.equals(this.username)) { + fragmentActivity.setCollapsingView(profileDetailsBinding.getRoot()); shouldRefresh = true; return root; } } setUsernameDelayed(); + fragmentActivity.setCollapsingView(profileDetailsBinding.getRoot()); shouldRefresh = false; return root; } binding = FragmentProfileBinding.inflate(inflater, container, false); root = binding.getRoot(); + profileDetailsBinding = LayoutProfileDetailsBinding.inflate(inflater, fragmentActivity.getCollapsingToolbarView(), false); + fragmentActivity.setCollapsingView(profileDetailsBinding.getRoot()); return root; } @@ -431,6 +438,14 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } } + @Override + public void onDestroyView() { + super.onDestroyView(); + if (profileDetailsBinding != null) { + fragmentActivity.removeCollapsingView(profileDetailsBinding.getRoot()); + } + } + @Override public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); @@ -457,12 +472,13 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe setUsernameDelayed(); } if (TextUtils.isEmpty(username) && !isLoggedIn) { - binding.infoContainer.setVisibility(View.GONE); + profileDetailsBinding.infoContainer.setVisibility(View.GONE); binding.swipeRefreshLayout.setEnabled(false); binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24); binding.privatePage2.setText(R.string.no_acc); - final NestedCoordinatorLayout.LayoutParams layoutParams = (NestedCoordinatorLayout.LayoutParams) binding.privatePage.getLayoutParams(); + final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.privatePage.getLayoutParams(); layoutParams.topMargin = 0; + layoutParams.gravity = Gravity.CENTER; binding.privatePage.setLayoutParams(layoutParams); binding.privatePage.setVisibility(View.VISIBLE); return; @@ -531,7 +547,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } else { binding.postsRecyclerView.refresh(); } - binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); + profileDetailsBinding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); final String profileId = profileModel.getId(); final String myId = CookieUtils.getUserIdFromCookie(cookie); if (isLoggedIn) { @@ -539,14 +555,14 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } setupButtons(profileId, myId); if (!profileId.equals(myId)) { - binding.favCb.setVisibility(View.VISIBLE); + profileDetailsBinding.favCb.setVisibility(View.VISIBLE); final boolean isFav = Utils.dataBox.getFavorite(username.substring(1), FavoriteType.USER) != null; - binding.favCb.setChecked(isFav); - binding.favCb.setButtonDrawable(isFav ? R.drawable.ic_star_check_24 : R.drawable.ic_outline_star_plus_24); + profileDetailsBinding.favCb.setChecked(isFav); + profileDetailsBinding.favCb.setButtonDrawable(isFav ? R.drawable.ic_star_check_24 : R.drawable.ic_outline_star_plus_24); } else { - binding.favCb.setVisibility(View.GONE); + profileDetailsBinding.favCb.setVisibility(View.GONE); } - binding.mainProfileImage.setImageURI(profileModel.getHdProfilePic()); + profileDetailsBinding.mainProfileImage.setImageURI(profileModel.getHdProfilePic()); final long followersCount = profileModel.getFollowersCount(); final long followingCount = profileModel.getFollowingCount(); @@ -557,7 +573,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe postCount)); span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); - binding.mainPostCount.setText(span); + profileDetailsBinding.mainPostCount.setText(span); final String followersCountStr = String.valueOf(followersCount); final int followersCountStrLen = followersCountStr.length(); @@ -565,7 +581,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe followersCountStr)); span.setSpan(new RelativeSizeSpan(1.2f), 0, followersCountStrLen, 0); span.setSpan(new StyleSpan(Typeface.BOLD), 0, followersCountStrLen, 0); - binding.mainFollowers.setText(span); + profileDetailsBinding.mainFollowers.setText(span); final String followingCountStr = String.valueOf(followingCount); final int followingCountStrLen = followingCountStr.length(); @@ -573,68 +589,67 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe followingCountStr)); span.setSpan(new RelativeSizeSpan(1.2f), 0, followingCountStrLen, 0); span.setSpan(new StyleSpan(Typeface.BOLD), 0, followingCountStrLen, 0); - binding.mainFollowing.setText(span); + profileDetailsBinding.mainFollowing.setText(span); - binding.mainFullName.setText(TextUtils.isEmpty(profileModel.getName()) ? profileModel.getUsername() - : profileModel.getName()); + profileDetailsBinding.mainFullName.setText(TextUtils.isEmpty(profileModel.getName()) ? profileModel.getUsername() + : profileModel.getName()); final String biography = profileModel.getBiography(); if (!TextUtils.isEmpty(biography)) { - binding.mainBiography.setText(biography); - binding.mainBiography.addOnHashtagListener(autoLinkItem -> { + profileDetailsBinding.mainBiography.setText(biography); + profileDetailsBinding.mainBiography.addOnHashtagListener(autoLinkItem -> { final NavController navController = NavHostFragment.findNavController(this); final Bundle bundle = new Bundle(); final String originalText = autoLinkItem.getOriginalText().trim(); bundle.putString(ARG_HASHTAG, originalText); navController.navigate(R.id.action_global_hashTagFragment, bundle); }); - binding.mainBiography.addOnMentionClickListener(autoLinkItem -> { + profileDetailsBinding.mainBiography.addOnMentionClickListener(autoLinkItem -> { final String originalText = autoLinkItem.getOriginalText().trim(); navigateToProfile(originalText); }); - binding.mainBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(getContext(), - autoLinkItem.getOriginalText().trim())); - binding.mainBiography.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim())); - binding.mainBiography.setOnLongClickListener(v -> { - if (context != null) Utils.copyText(context, biography); + profileDetailsBinding.mainBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(getContext(), + autoLinkItem.getOriginalText() + .trim())); + profileDetailsBinding.mainBiography + .addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim())); + profileDetailsBinding.mainBiography.setOnLongClickListener(v -> { + Utils.copyText(context, biography); return true; }); } final String url = profileModel.getUrl(); if (TextUtils.isEmpty(url)) { - binding.mainUrl.setVisibility(View.GONE); + profileDetailsBinding.mainUrl.setVisibility(View.GONE); } else { - binding.mainUrl.setVisibility(View.VISIBLE); - binding.mainUrl.setText(url); - binding.mainUrl.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim())); - binding.mainUrl.setOnLongClickListener(v -> { - if (context != null) Utils.copyText(context, url); + profileDetailsBinding.mainUrl.setVisibility(View.VISIBLE); + profileDetailsBinding.mainUrl.setText(url); + profileDetailsBinding.mainUrl.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim())); + profileDetailsBinding.mainUrl.setOnLongClickListener(v -> { + Utils.copyText(context, url); return true; }); } - if (!profileModel.isReallyPrivate() && isLoggedIn) { - binding.mainFollowing.setClickable(true); - binding.mainFollowers.setClickable(true); - + if (!profileModel.isReallyPrivate()) { if (isLoggedIn) { + profileDetailsBinding.mainFollowing.setClickable(true); + profileDetailsBinding.mainFollowers.setClickable(true); final View.OnClickListener followClickListener = v -> { final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToFollowViewerFragment( profileId, - v == binding.mainFollowers, + v == profileDetailsBinding.mainFollowers, profileModel.getUsername()); NavHostFragment.findNavController(this).navigate(action); }; - - binding.mainFollowers.setOnClickListener(followersCount > 0 ? followClickListener : null); - binding.mainFollowing.setOnClickListener(followingCount > 0 ? followClickListener : null); + profileDetailsBinding.mainFollowers.setOnClickListener(followersCount > 0 ? followClickListener : null); + profileDetailsBinding.mainFollowing.setOnClickListener(followingCount > 0 ? followClickListener : null); } - binding.swipeRefreshLayout.setRefreshing(true); binding.postsRecyclerView.setVisibility(View.VISIBLE); fetchPosts(); } else { - binding.mainFollowers.setClickable(false); - binding.mainFollowing.setClickable(false); + profileDetailsBinding.mainFollowers.setClickable(false); + profileDetailsBinding.mainFollowing.setClickable(false); binding.swipeRefreshLayout.setRefreshing(false); // error binding.privatePage1.setImageResource(R.drawable.lock); @@ -647,27 +662,27 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe 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); + profileDetailsBinding.btnTagged.setVisibility(View.VISIBLE); + profileDetailsBinding.btnSaved.setVisibility(View.VISIBLE); + profileDetailsBinding.btnLiked.setVisibility(View.VISIBLE); + profileDetailsBinding.btnDM.setVisibility(View.GONE); + profileDetailsBinding.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); + profileDetailsBinding.btnTagged.setVisibility(View.GONE); + profileDetailsBinding.btnSaved.setVisibility(View.GONE); + profileDetailsBinding.btnLiked.setVisibility(View.GONE); + profileDetailsBinding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism? + profileDetailsBinding.btnFollow.setVisibility(View.VISIBLE); if (profileModel.getFollowing()) { - binding.btnFollow.setText(R.string.unfollow); - binding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_disabled_24); + profileDetailsBinding.btnFollow.setText(R.string.unfollow); + profileDetailsBinding.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); + profileDetailsBinding.btnFollow.setText(R.string.cancel); + profileDetailsBinding.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); + profileDetailsBinding.btnFollow.setText(R.string.follow); + profileDetailsBinding.btnFollow.setIconResource(R.drawable.ic_outline_person_add_24); } if (restrictMenuItem != null) { restrictMenuItem.setVisible(true); @@ -677,7 +692,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe restrictMenuItem.setTitle(R.string.restrict); } } - binding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE); + profileDetailsBinding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE); if (blockMenuItem != null) { blockMenuItem.setVisible(true); if (profileModel.getBlocked()) { @@ -708,7 +723,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe @Override public void onSuccess(final List storyModels) { if (storyModels != null && !storyModels.isEmpty()) { - binding.mainProfileImage.setStoriesBorder(); + profileDetailsBinding.mainProfileImage.setStoriesBorder(); hasStories = true; } } @@ -722,15 +737,15 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe result -> { highlightsFetching = false; if (result != null) { - binding.highlightsList.setVisibility(View.VISIBLE); + profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE); highlightsViewModel.getList().postValue(result); - } else binding.highlightsList.setVisibility(View.GONE); + } else profileDetailsBinding.highlightsList.setVisibility(View.GONE); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private void setupCommonListeners() { final String userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); - binding.btnFollow.setOnClickListener(v -> { + profileDetailsBinding.btnFollow.setOnClickListener(v -> { if (profileModel.getFollowing() || profileModel.getRequested()) { friendshipService.unfollow( userIdFromCookie, @@ -767,29 +782,29 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe }); } }); - binding.btnSaved.setOnClickListener(v -> { + profileDetailsBinding.btnSaved.setOnClickListener(v -> { final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment(profileModel.getUsername(), profileModel.getId(), PostItemType.SAVED); NavHostFragment.findNavController(this).navigate(action); }); - binding.btnLiked.setOnClickListener(v -> { + profileDetailsBinding.btnLiked.setOnClickListener(v -> { final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment(profileModel.getUsername(), profileModel.getId(), PostItemType.LIKED); NavHostFragment.findNavController(this).navigate(action); }); - binding.btnTagged.setOnClickListener(v -> { + profileDetailsBinding.btnTagged.setOnClickListener(v -> { final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment(profileModel.getUsername(), profileModel.getId(), PostItemType.TAGGED); NavHostFragment.findNavController(this).navigate(action); }); - binding.btnDM.setOnClickListener(v -> new CreateThreadAction(cookie, profileModel.getId(), threadId -> { + profileDetailsBinding.btnDM.setOnClickListener(v -> new CreateThreadAction(cookie, profileModel.getId(), threadId -> { final NavDirections action = ProfileFragmentDirections.actionProfileFragmentToDMThreadFragment(threadId, profileModel.getUsername()); NavHostFragment.findNavController(this).navigate(action); }).execute()); - binding.mainProfileImage.setOnClickListener(v -> { + profileDetailsBinding.mainProfileImage.setOnClickListener(v -> { if (!hasStories) { // show profile pic showProfilePicDialog(); @@ -818,7 +833,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe .setNegativeButton(R.string.cancel, null) .show(); }); - binding.favCb.setOnCheckedChangeListener((buttonView, isChecked) -> { + profileDetailsBinding.favCb.setOnCheckedChangeListener((buttonView, isChecked) -> { // do not do anything if state matches the db, as listener is set before profile details are set final String finalUsername = username.startsWith("@") ? username.substring(1) : username; final DataBox.FavoriteModel favorite = Utils.dataBox.getFavorite(finalUsername, FavoriteType.USER); @@ -826,7 +841,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe return; } buttonView.setVisibility(View.GONE); - binding.favProgress.setVisibility(View.VISIBLE); + profileDetailsBinding.favProgress.setVisibility(View.VISIBLE); final String message; if (isChecked) { final DataBox.FavoriteModel model = new DataBox.FavoriteModel( @@ -838,20 +853,20 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe new Date() ); Utils.dataBox.addOrUpdateFavorite(model); - binding.favCb.setButtonDrawable(R.drawable.ic_star_check_24); + profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_star_check_24); message = getString(R.string.added_to_favs); } else { Utils.dataBox.deleteFavorite(finalUsername, FavoriteType.USER); message = getString(R.string.removed_from_favs); - binding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24); + profileDetailsBinding.favCb.setButtonDrawable(R.drawable.ic_outline_star_plus_24); } final Snackbar snackbar = Snackbar.make(root, message, BaseTransientBottomBar.LENGTH_LONG); snackbar.setAction(R.string.ok, v -> snackbar.dismiss()) .setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) .setAnchorView(fragmentActivity.getBottomNavView()) .show(); - binding.favProgress.setVisibility(View.GONE); - binding.favCb.setVisibility(View.VISIBLE); + profileDetailsBinding.favProgress.setVisibility(View.GONE); + profileDetailsBinding.favCb.setVisibility(View.VISIBLE); }); } @@ -900,8 +915,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe final Context context = getContext(); if (context == null) return; final RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false); - binding.highlightsList.setLayoutManager(layoutManager); - binding.highlightsList.setAdapter(highlightsAdapter); + profileDetailsBinding.highlightsList.setLayoutManager(layoutManager); + profileDetailsBinding.highlightsList.setAdapter(highlightsAdapter); highlightsViewModel.getList().observe(getViewLifecycleOwner(), highlightModels -> highlightsAdapter.submitList(highlightModels)); } diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index 387e8aaf..0f4cb41c 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -453,7 +453,7 @@ public final class ResponseBodyUtils { case VIDEO_CALL_EVENT: { final JSONObject videoCallEvent = itemObject.getJSONObject("video_call_event"); - videoCallEventModel = new DirectItemModel.DirectItemVideoCallEventModel(videoCallEvent.getLong("vc_id"), + videoCallEventModel = new DirectItemModel.DirectItemVideoCallEventModel(videoCallEvent.optLong("vc_id"), videoCallEvent.optBoolean("thread_has_audio_only_call"), videoCallEvent.getString("action"), videoCallEvent.getString("description")); @@ -743,13 +743,13 @@ public final class ResponseBodyUtils { String thumbnailUrl = null; try { thumbnailUrl = feedItem.getJSONArray("display_resources") - .getJSONObject(0) - .getString("src"); + .getJSONObject(0) + .getString("src"); } catch (JSONException ignored) {} final FeedModel.Builder feedModelBuilder = new FeedModel.Builder() .setProfileModel(profileModel) .setItemType(isVideo ? MediaItemType.MEDIA_TYPE_VIDEO - : MediaItemType.MEDIA_TYPE_IMAGE) + : MediaItemType.MEDIA_TYPE_IMAGE) .setViewCount(videoViews) .setPostId(feedItem.getString(Constants.EXTRAS_ID)) .setDisplayUrl(resourceUrl) @@ -843,17 +843,17 @@ public final class ResponseBodyUtils { String thumbnailUrl = null; try { thumbnailUrl = childNode.getJSONArray("display_resources") - .getJSONObject(0) - .getString("src"); + .getJSONObject(0) + .getString("src"); } catch (JSONException ignored) {} final PostChild sliderItem = new PostChild.Builder() .setItemType(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO - : MediaItemType.MEDIA_TYPE_IMAGE) + : MediaItemType.MEDIA_TYPE_IMAGE) .setPostId(childNode.getString(Constants.EXTRAS_ID)) .setDisplayUrl(isChildVideo ? childNode.getString("video_url") - : childNode.getString("display_url")) + : childNode.getString("display_url")) .setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl - : childNode.getString("display_url")) + : childNode.getString("display_url")) .setVideoViews(childNode.optLong("video_view_count", 0)) .setHeight(height) .setWidth(width) diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index ee6c06a5..e3982f86 100755 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -1,6 +1,7 @@ package awais.instagrabber.utils; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ContentResolver; @@ -11,6 +12,7 @@ import android.net.Uri; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; +import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.webkit.MimeTypeMap; @@ -34,6 +36,7 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import awais.instagrabber.R; +import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.enums.FavoriteType; import awaisomereport.LogCollector; @@ -51,6 +54,7 @@ public final class Utils { public static SimpleDateFormat datetimeParser; public static SimpleCache simpleCache; private static int statusBarHeight; + private static int actionBarHeight; public static int convertDpToPx(final float dp) { if (displayMetrics == null) @@ -162,13 +166,30 @@ public final class Utils { return statusBarHeight; } + public static int getActionBarHeight(@NonNull final Context context) { + if (actionBarHeight > 0) { + return actionBarHeight; + } + final TypedValue tv = new TypedValue(); + if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) { + actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, displayMetrics); + } + return actionBarHeight; + } + public static void openURL(final Context context, final String url) { if (context == null || TextUtils.isEmpty(url)) { return; } final Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(url)); - context.startActivity(i); + try { + context.startActivity(i); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "openURL: No activity found to handle URL view", e); + } catch (Exception e) { + Log.e(TAG, "openURL", e); + } } public static void openEmailAddress(final Context context, final String emailAddress) { @@ -190,4 +211,13 @@ public final class Utils { view.getTop() - view.getHeight() - 4); toast.show(); } + + public static PostsLayoutPreferences getPostsLayoutPreferences(final String layoutPreferenceKey) { + PostsLayoutPreferences layoutPreferences = PostsLayoutPreferences.fromJson(settingsHelper.getString(layoutPreferenceKey)); + if (layoutPreferences == null) { + layoutPreferences = PostsLayoutPreferences.builder().build(); + settingsHelper.putString(layoutPreferenceKey, layoutPreferences.getJson()); + } + return layoutPreferences; + } } diff --git a/app/src/main/java/awaisomereport/CrashReporter.java b/app/src/main/java/awaisomereport/CrashReporter.java index e1788356..93e5b0f0 100755 --- a/app/src/main/java/awaisomereport/CrashReporter.java +++ b/app/src/main/java/awaisomereport/CrashReporter.java @@ -70,7 +70,7 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler { .append("\r\nDEVICE : ").append(Build.DEVICE) .append("\r\nPRODUCT : ").append(Build.PRODUCT) .append("\r\nHOST : ").append(Build.HOST) - .append("\r\nTAGS : ").append(Build.TAGS) + .append("\r\nTAGS : ").append(Build.TAGS); reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n"); final Writer result = new StringWriter(); diff --git a/app/src/main/res/drawable/ic_forward_5_24_a50.xml b/app/src/main/res/drawable/ic_forward_5_24_a50.xml new file mode 100644 index 00000000..1340c13f --- /dev/null +++ b/app/src/main/res/drawable/ic_forward_5_24_a50.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/drawable/ic_forward_5_24_states.xml b/app/src/main/res/drawable/ic_forward_5_24_states.xml new file mode 100644 index 00000000..4c096286 --- /dev/null +++ b/app/src/main/res/drawable/ic_forward_5_24_states.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_play_arrow_24_a50.xml b/app/src/main/res/drawable/ic_play_arrow_24_a50.xml new file mode 100644 index 00000000..249e0f6d --- /dev/null +++ b/app/src/main/res/drawable/ic_play_arrow_24_a50.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_play_states.xml b/app/src/main/res/drawable/ic_play_states.xml new file mode 100644 index 00000000..3f27a8c9 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_states.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_replay_5_24_a50.xml b/app/src/main/res/drawable/ic_replay_5_24_a50.xml new file mode 100644 index 00000000..a0904aca --- /dev/null +++ b/app/src/main/res/drawable/ic_replay_5_24_a50.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/drawable/ic_replay_5_24_states.xml b/app/src/main/res/drawable/ic_replay_5_24_states.xml new file mode 100644 index 00000000..19bad1b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_replay_5_24_states.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_volume_off_24_a50.xml b/app/src/main/res/drawable/ic_volume_off_24_a50.xml new file mode 100644 index 00000000..5bbfbe9a --- /dev/null +++ b/app/src/main/res/drawable/ic_volume_off_24_a50.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_volume_off_24_states.xml b/app/src/main/res/drawable/ic_volume_off_24_states.xml new file mode 100644 index 00000000..71b5d07b --- /dev/null +++ b/app/src/main/res/drawable/ic_volume_off_24_states.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_volume_up_24_a50.xml b/app/src/main/res/drawable/ic_volume_up_24_a50.xml new file mode 100644 index 00000000..9c518f60 --- /dev/null +++ b/app/src/main/res/drawable/ic_volume_up_24_a50.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_volume_up_24_states.xml b/app/src/main/res/drawable/ic_volume_up_24_states.xml new file mode 100644 index 00000000..3a947b21 --- /dev/null +++ b/app/src/main/res/drawable/ic_volume_up_24_states.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/lock.xml b/app/src/main/res/drawable/lock.xml index 4bb23a77..aa15a437 100755 --- a/app/src/main/res/drawable/lock.xml +++ b/app/src/main/res/drawable/lock.xml @@ -1,12 +1,11 @@ diff --git a/app/src/main/res/drawable/speed_text_color_states.xml b/app/src/main/res/drawable/speed_text_color_states.xml new file mode 100644 index 00000000..2e62e4fd --- /dev/null +++ b/app/src/main/res/drawable/speed_text_color_states.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ad84f7a4..866770aa 100755 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -20,14 +20,17 @@ android:id="@+id/collapsingToolbarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" - app:layout_scrollFlags="scroll|snap|enterAlways" + android:animateLayoutChanges="true" + android:minHeight="?attr/actionBarSize" + app:layout_scrollFlags="scroll|enterAlwaysCollapsed|exitUntilCollapsed" app:titleEnabled="false"> @@ -45,5 +48,6 @@ android:layout_height="wrap_content" android:layout_gravity="bottom" app:labelVisibilityMode="labeled" + app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior" app:menu="@menu/main_bottom_navigation_menu" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_direct_messages_inbox.xml b/app/src/main/res/layout/fragment_direct_messages_inbox.xml index e5a8f69f..a77b7cff 100644 --- a/app/src/main/res/layout/fragment_direct_messages_inbox.xml +++ b/app/src/main/res/layout/fragment_direct_messages_inbox.xml @@ -1,5 +1,5 @@ - - + diff --git a/app/src/main/res/layout/fragment_direct_messages_thread.xml b/app/src/main/res/layout/fragment_direct_messages_thread.xml index dc226234..5248fa19 100644 --- a/app/src/main/res/layout/fragment_direct_messages_thread.xml +++ b/app/src/main/res/layout/fragment_direct_messages_thread.xml @@ -11,7 +11,6 @@ android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" - app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_constraintBottom_toTopOf="@id/comment_container" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/fragment_discover.xml b/app/src/main/res/layout/fragment_discover.xml index ae5b93dd..6636e3bc 100644 --- a/app/src/main/res/layout/fragment_discover.xml +++ b/app/src/main/res/layout/fragment_discover.xml @@ -1,32 +1,11 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_feed.xml b/app/src/main/res/layout/fragment_feed.xml index b50138eb..da5f341f 100644 --- a/app/src/main/res/layout/fragment_feed.xml +++ b/app/src/main/res/layout/fragment_feed.xml @@ -1,28 +1,28 @@ - - + + + + - + + + + - - - + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_hashtag.xml b/app/src/main/res/layout/fragment_hashtag.xml index 2861987b..7bd26633 100644 --- a/app/src/main/res/layout/fragment_hashtag.xml +++ b/app/src/main/res/layout/fragment_hashtag.xml @@ -1,84 +1,10 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_location.xml b/app/src/main/res/layout/fragment_location.xml index 669f67d1..7bd26633 100644 --- a/app/src/main/res/layout/fragment_location.xml +++ b/app/src/main/res/layout/fragment_location.xml @@ -1,145 +1,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index 9680d4d2..a1baa934 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -1,276 +1,27 @@ - - + + + + + + + - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + tools:visibility="visible"> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_topic_posts.xml b/app/src/main/res/layout/fragment_topic_posts.xml index cc053d9c..4d82bbe3 100644 --- a/app/src/main/res/layout/fragment_topic_posts.xml +++ b/app/src/main/res/layout/fragment_topic_posts.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_exo_custom_controls.xml b/app/src/main/res/layout/layout_exo_custom_controls.xml index b8e90299..4292ebbc 100644 --- a/app/src/main/res/layout/layout_exo_custom_controls.xml +++ b/app/src/main/res/layout/layout_exo_custom_controls.xml @@ -53,13 +53,14 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:visibility="visible" - app:icon="@drawable/ic_replay_5_24" + app:icon="@drawable/ic_replay_5_24_states" app:iconSize="24dp" app:iconTint="@color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/play_pause" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/timeline" + tools:enabled="false" tools:visibility="visible" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_hashtag_details.xml b/app/src/main/res/layout/layout_hashtag_details.xml new file mode 100644 index 00000000..56ddc32e --- /dev/null +++ b/app/src/main/res/layout/layout_hashtag_details.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_location_details.xml b/app/src/main/res/layout/layout_location_details.xml new file mode 100644 index 00000000..9176519a --- /dev/null +++ b/app/src/main/res/layout/layout_location_details.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_profile_details.xml b/app/src/main/res/layout/layout_profile_details.xml new file mode 100644 index 00000000..0c46eab4 --- /dev/null +++ b/app/src/main/res/layout/layout_profile_details.xml @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/direct_messages_nav_graph.xml b/app/src/main/res/navigation/direct_messages_nav_graph.xml index 9839d56e..5ce1b064 100644 --- a/app/src/main/res/navigation/direct_messages_nav_graph.xml +++ b/app/src/main/res/navigation/direct_messages_nav_graph.xml @@ -44,6 +44,25 @@ android:id="@+id/action_global_notificationsViewerFragment" app:destination="@id/notification_viewer_nav_graph" /> + + + + + + + + #888888 #FFFFFF + #80FFFFFF #000000 #121212 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index abc392cf..1fed88f7 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -309,6 +309,7 @@ Show grid gap Disable animation Please wait for the current task to complete first! + Post not found! %d like %d likes