From d20f8669e8aefeab8e39a8ae8b604e3182894234 Mon Sep 17 00:00:00 2001 From: sk22 Date: Fri, 17 Feb 2023 13:20:22 +0100 Subject: [PATCH] Auto-hide FAB on scroll (#435) * feat(composeButton): hide fab on scroll * feat(composeButton): hide when scrolling in profile fragment * refactor(compose-fab): show fab after small scroll distance * refactor(compose-fab): code cleanup * feat(composeButton): hide when scrolling in profile * fix: duplicate fab var * feat(fab): show when scrolled to top * add option to turn it off --------- Co-authored-by: FineFindus <63370021+FineFindus@users.noreply.github.com> --- .../android/GlobalUserPreferences.java | 3 ++ .../fragments/AccountTimelineFragment.java | 4 ++ .../fragments/BaseStatusListFragment.java | 38 ++++++++++++++++- .../android/fragments/ProfileFragment.java | 42 ++++++++++++++++++- .../android/fragments/SettingsFragment.java | 5 +++ mastodon/src/main/res/values/strings_sk.xml | 1 + 6 files changed, 90 insertions(+), 3 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java b/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java index 4aeefc228..ce645c5c5 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java +++ b/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java @@ -43,6 +43,7 @@ public class GlobalUserPreferences{ public static boolean bottomEncoding; public static boolean collapseLongPosts; public static boolean spectatorMode; + public static boolean autoHideFab; public static String publishButtonText; public static ThemePreference theme; public static ColorPreference color; @@ -91,6 +92,7 @@ public class GlobalUserPreferences{ bottomEncoding=prefs.getBoolean("bottomEncoding", false); collapseLongPosts=prefs.getBoolean("collapseLongPosts", true); spectatorMode=prefs.getBoolean("spectatorMode", false); + autoHideFab=prefs.getBoolean("autoHideFab", true); publishButtonText=prefs.getString("publishButtonText", ""); theme=ThemePreference.values()[prefs.getInt("theme", 0)]; recentLanguages=fromJson(prefs.getString("recentLanguages", null), recentLanguagesType, new HashMap<>()); @@ -131,6 +133,7 @@ public class GlobalUserPreferences{ .putBoolean("prefixRepliesWithRe", prefixRepliesWithRe) .putBoolean("collapseLongPosts", collapseLongPosts) .putBoolean("spectatorMode", spectatorMode) + .putBoolean("autoHideFab", autoHideFab) .putString("publishButtonText", publishButtonText) .putBoolean("bottomEncoding", bottomEncoding) .putInt("theme", theme.ordinal()) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java index 41311e6a3..680db45eb 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java @@ -3,6 +3,10 @@ package org.joinmastodon.android.fragments; import android.app.Activity; import android.os.Bundle; import android.view.View; +import android.view.animation.TranslateAnimation; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses; diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index a63a1f8ab..231356c24 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -13,9 +13,12 @@ import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; +import android.view.animation.TranslateAnimation; +import android.widget.ImageButton; import android.widget.ImageButton; import android.widget.Toolbar; @@ -75,10 +78,11 @@ public abstract class BaseStatusListFragment exten protected DisplayItemsAdapter adapter; protected String accountID; protected PhotoViewer currentPhotoViewer; + protected ImageButton fab; + protected int scrollDiff = 0; protected HashMap knownAccounts=new HashMap<>(); protected HashMap relationships=new HashMap<>(); protected Rect tmpRect=new Rect(); - protected ImageButton fab; public BaseStatusListFragment(){ super(20); @@ -285,11 +289,42 @@ public abstract class BaseStatusListFragment exten @Override public void onViewCreated(View view, Bundle savedInstanceState){ super.onViewCreated(view, savedInstanceState); + fab=view.findViewById(R.id.fab); list.addOnScrollListener(new RecyclerView.OnScrollListener(){ @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){ if(currentPhotoViewer!=null) currentPhotoViewer.offsetView(-dx, -dy); + + if (fab!=null && GlobalUserPreferences.autoHideFab) { + if (dy > 0 && fab.getVisibility() == View.VISIBLE) { + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + 0, + fab.getHeight() * 2); + animate.setDuration(300); + animate.setFillAfter(true); + fab.startAnimation(animate); + fab.setVisibility(View.INVISIBLE); + scrollDiff = 0; + } else if (dy < 0 && fab.getVisibility() != View.VISIBLE) { + if (list.getChildLayoutPosition(list.getChildAt(0)) == 0 || scrollDiff > 400) { + fab.setVisibility(View.VISIBLE); + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + fab.getHeight() * 2, + 0); + animate.setDuration(300); + animate.setFillAfter(true); + fab.startAnimation(animate); + scrollDiff = 0; + } else { + scrollDiff += Math.abs(dy); + } + } + } } }); list.addItemDecoration(new StatusListItemDecoration()); @@ -327,7 +362,6 @@ public abstract class BaseStatusListFragment exten updateToolbar(); if (withComposeButton()) { - fab = view.findViewById(R.id.fab); fab.setVisibility(View.VISIBLE); fab.setOnClickListener(this::onFabClick); fab.setOnLongClickListener(this::onFabLongClick); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 2ca180bae..18f4b67aa 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -19,11 +19,13 @@ import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.ImageSpan; +import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; @@ -31,9 +33,11 @@ import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.inputmethod.InputMethodManager; +import android.view.animation.TranslateAnimation; import android.widget.Button; import android.widget.EditText; import android.widget.FrameLayout; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.RelativeLayout; @@ -144,10 +148,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList private Uri editNewAvatar, editNewCover; private String profileAccountID; private boolean refreshing; - private View fab; + private ImageButton fab; private WindowInsets childInsets; private PhotoViewer currentPhotoViewer; private boolean editModeLoading; + protected int scrollDiff = 0; private static final int MAX_FIELDS=4; @@ -749,6 +754,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username)); } + public ImageButton getFab() { + return fab; + } + private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){ int topBarsH=getToolbar().getHeight()+statusBarHeight; if(scrollY>avatarBorder.getTop()-topBarsH){ @@ -779,6 +788,37 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList if(currentPhotoViewer!=null){ currentPhotoViewer.offsetView(0, oldScrollY-scrollY); } + + if (GlobalUserPreferences.autoHideFab) { + int dy = scrollY - oldScrollY; + if (dy > 0 && fab.getVisibility() == View.VISIBLE) { + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + 0, + fab.getHeight() * 2); + animate.setDuration(300); + animate.setFillAfter(true); + fab.startAnimation(animate); + fab.setVisibility(View.INVISIBLE); + scrollDiff = 0; + } else if (dy < 0 && fab.getVisibility() != View.VISIBLE) { + if (v.getScrollY() == 0 || scrollDiff > 400) { + fab.setVisibility(View.VISIBLE); + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + fab.getHeight() * 2, + 0); + animate.setDuration(300); + animate.setFillAfter(true); + fab.startAnimation(animate); + scrollDiff = 0; + } else { + scrollDiff += Math.abs(dy); + } + } + } } private Fragment getFragmentForPage(int page){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java index 0e0d6a40d..34e7a522f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java @@ -248,6 +248,11 @@ public class SettingsFragment extends MastodonToolbarFragment{ GlobalUserPreferences.save(); needAppRestart=true; })); + items.add(new SwitchItem(R.string.sk_settings_hide_fab, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.autoHideFab, i->{ + GlobalUserPreferences.autoHideFab=i.checked; + GlobalUserPreferences.save(); + needAppRestart=true; + })); items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{ GlobalUserPreferences.translateButtonOpenedOnly=i.checked; GlobalUserPreferences.save(); diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index 7bad22ee0..a94de6489 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -261,4 +261,5 @@ Hide interaction buttons Follow from other account Followed from %s + Auto-hide Compose button \ No newline at end of file