From 9ff8efab69ec43c16cce41553ab3239a6613a050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Sch=C3=A4ttgen?= Date: Sun, 20 Aug 2023 17:29:08 +0200 Subject: [PATCH] Respect system animation setting Co-authored-by: Alexander Bakker --- .../aegis/helpers/AnimationsHelper.java | 54 +++++++++++++++++++ .../aegis/ui/EditEntryActivity.java | 5 +- .../preferences/IconPacksManagerFragment.java | 13 +++++ .../preferences/PreferencesFragment.java | 13 +++++ .../aegis/ui/intro/IntroBaseActivity.java | 6 ++- .../aegis/ui/views/EntryHolder.java | 14 ++--- .../aegis/ui/views/EntryListView.java | 40 +++++++++----- .../aegis/ui/views/TotpProgressBar.java | 4 +- .../res/layout/fragment_entry_list_view.xml | 1 - 9 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/beemdevelopment/aegis/helpers/AnimationsHelper.java diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/AnimationsHelper.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/AnimationsHelper.java new file mode 100644 index 0000000000..98d8c36756 --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/AnimationsHelper.java @@ -0,0 +1,54 @@ +package com.beemdevelopment.aegis.helpers; + +import android.content.Context; +import android.provider.Settings; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.LayoutAnimationController; + +public class AnimationsHelper { + private AnimationsHelper() { + + } + + public static Animation loadScaledAnimation(Context context, int animationResId) { + return loadScaledAnimation(context, animationResId, Scale.ANIMATOR); + } + + public static Animation loadScaledAnimation(Context context, int animationResId, Scale scale) { + Animation animation = AnimationUtils.loadAnimation(context, animationResId); + long newDuration = (long) (animation.getDuration() * scale.getValue(context)); + animation.setDuration(newDuration); + return animation; + } + + public static LayoutAnimationController loadScaledLayoutAnimation(Context context, int animationResId) { + return loadScaledLayoutAnimation(context, animationResId, Scale.ANIMATOR); + } + + public static LayoutAnimationController loadScaledLayoutAnimation(Context context, int animationResId, Scale scale) { + LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(context, animationResId); + Animation animation = controller.getAnimation(); + animation.setDuration((long) (animation.getDuration() * scale.getValue(context))); + return controller; + } + + public enum Scale { + ANIMATOR(Settings.Global.ANIMATOR_DURATION_SCALE), + TRANSITION(Settings.Global.TRANSITION_ANIMATION_SCALE); + + private final String _setting; + + Scale(String setting) { + _setting = setting; + } + + public float getValue(Context context) { + return Settings.Global.getFloat(context.getContentResolver(), _setting, 1.0f); + } + + public boolean isZero(Context context) { + return getValue(context) == 0; + } + } +} diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java index 05350118b5..95de25e222 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java @@ -33,6 +33,7 @@ import com.beemdevelopment.aegis.encoding.Base32; import com.beemdevelopment.aegis.encoding.EncodingException; import com.beemdevelopment.aegis.encoding.Hex; +import com.beemdevelopment.aegis.helpers.AnimationsHelper; import com.beemdevelopment.aegis.helpers.DropdownHelper; import com.beemdevelopment.aegis.helpers.EditTextHelper; import com.beemdevelopment.aegis.helpers.IconViewHelper; @@ -376,12 +377,12 @@ private void setGroup(String groupName) { private void openAdvancedSettings() { Animation fadeOut = new AlphaAnimation(1, 0); fadeOut.setInterpolator(new AccelerateInterpolator()); - fadeOut.setDuration(220); + fadeOut.setDuration(220 * (long) AnimationsHelper.Scale.ANIMATOR.getValue(this)); _advancedSettingsHeader.startAnimation(fadeOut); Animation fadeIn = new AlphaAnimation(0, 1); fadeIn.setInterpolator(new AccelerateInterpolator()); - fadeIn.setDuration(250); + fadeIn.setDuration(250 * (long) AnimationsHelper.Scale.ANIMATOR.getValue(this)); fadeOut.setAnimationListener(new SimpleAnimationEndListener((a) -> { _advancedSettingsHeader.setVisibility(View.GONE); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/IconPacksManagerFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/IconPacksManagerFragment.java index 651506c820..acff93c5ac 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/IconPacksManagerFragment.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/IconPacksManagerFragment.java @@ -6,16 +6,19 @@ import android.os.Bundle; import android.text.method.LinkMovementMethod; import android.view.View; +import android.view.animation.Animation; import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.beemdevelopment.aegis.R; +import com.beemdevelopment.aegis.helpers.AnimationsHelper; import com.beemdevelopment.aegis.helpers.FabScrollHelper; import com.beemdevelopment.aegis.icons.IconPack; import com.beemdevelopment.aegis.icons.IconPackException; @@ -81,6 +84,16 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { updateEmptyState(); } + @Override + @Nullable + public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { + if (nextAnim != 0) { + return AnimationsHelper.loadScaledAnimation(requireContext(), nextAnim, AnimationsHelper.Scale.TRANSITION); + } + + return super.onCreateAnimation(transit, enter, nextAnim); + } + @Override public void onRemoveIconPack(IconPack pack) { Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext()) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/PreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/PreferencesFragment.java index 88e53ff7f2..4b693e2afb 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/PreferencesFragment.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/PreferencesFragment.java @@ -3,14 +3,17 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.view.animation.Animation; import androidx.annotation.CallSuper; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.R; +import com.beemdevelopment.aegis.helpers.AnimationsHelper; import com.beemdevelopment.aegis.ui.dialogs.Dialogs; import com.beemdevelopment.aegis.vault.VaultManager; import com.beemdevelopment.aegis.vault.VaultRepositoryException; @@ -62,6 +65,16 @@ public Intent getResult() { return _result; } + @Override + @Nullable + public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { + if (nextAnim != 0) { + return AnimationsHelper.loadScaledAnimation(requireContext(), nextAnim, AnimationsHelper.Scale.TRANSITION); + } + + return super.onCreateAnimation(transit, enter, nextAnim); + } + public void setResult(Intent result) { _result = result; requireActivity().setResult(Activity.RESULT_OK, _result); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/intro/IntroBaseActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/intro/IntroBaseActivity.java index 41a54fb9c4..ce4c89c154 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/intro/IntroBaseActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/intro/IntroBaseActivity.java @@ -14,6 +14,7 @@ import androidx.viewpager2.widget.ViewPager2; import com.beemdevelopment.aegis.R; +import com.beemdevelopment.aegis.helpers.AnimationsHelper; import com.beemdevelopment.aegis.ui.AegisActivity; import java.lang.ref.WeakReference; @@ -135,7 +136,10 @@ private void setPagerPosition(int pos) { Class newSlide = _slides.get(pos); if (!onBeforeSlideChanged(oldSlide, newSlide)) { - _pager.setCurrentItem(pos); + // We can't easily control the speed of the smooth scroll animation, but we + // can at least disable it if animations are disabled + boolean smoothScroll = !AnimationsHelper.Scale.TRANSITION.isZero(this); + _pager.setCurrentItem(pos, smoothScroll); } onAfterSlideChanged(oldSlide, newSlide); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java index ed4be2354f..25bdf2ab9c 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java @@ -4,7 +4,6 @@ import android.os.Handler; import android.view.View; import android.view.animation.Animation; -import android.view.animation.AnimationUtils; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; @@ -17,6 +16,7 @@ import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.ViewMode; +import com.beemdevelopment.aegis.helpers.AnimationsHelper; import com.beemdevelopment.aegis.helpers.IconViewHelper; import com.beemdevelopment.aegis.helpers.TextDrawableHelper; import com.beemdevelopment.aegis.helpers.ThemeHelper; @@ -91,8 +91,8 @@ public EntryHolder(final View view) { _progressBar.getProgressDrawable().setColorFilter(primaryColorId, PorterDuff.Mode.SRC_IN); _view.setBackground(_view.getContext().getResources().getDrawable(R.color.card_background)); - _scaleIn = AnimationUtils.loadAnimation(view.getContext(), R.anim.item_scale_in); - _scaleOut = AnimationUtils.loadAnimation(view.getContext(), R.anim.item_scale_out); + _scaleIn = AnimationsHelper.loadScaledAnimation(view.getContext(), R.anim.item_scale_in); + _scaleOut = AnimationsHelper.loadScaledAnimation(view.getContext(), R.anim.item_scale_out); _refresher = new UiRefresher(new UiRefresher.Listener() { @Override @@ -379,10 +379,10 @@ public void highlight() { public void animateCopyText(boolean includeSlideAnimation) { _animationHandler.removeCallbacksAndMessages(null); - Animation slideDownFadeIn = AnimationUtils.loadAnimation(itemView.getContext(), R.anim.slide_down_fade_in); - Animation slideDownFadeOut = AnimationUtils.loadAnimation(itemView.getContext(), R.anim.slide_down_fade_out); - Animation fadeOut = AnimationUtils.loadAnimation(itemView.getContext(), R.anim.fade_out); - Animation fadeIn = AnimationUtils.loadAnimation(itemView.getContext(), R.anim.fade_in); + Animation slideDownFadeIn = AnimationsHelper.loadScaledAnimation(itemView.getContext(), R.anim.slide_down_fade_in); + Animation slideDownFadeOut = AnimationsHelper.loadScaledAnimation(itemView.getContext(), R.anim.slide_down_fade_out); + Animation fadeOut = AnimationsHelper.loadScaledAnimation(itemView.getContext(), R.anim.fade_out); + Animation fadeIn = AnimationsHelper.loadScaledAnimation(itemView.getContext(), R.anim.fade_in); if (includeSlideAnimation) { _profileCopied.startAnimation(slideDownFadeIn); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java index ac5567efef..4a9183a931 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java @@ -10,7 +10,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; import android.widget.Button; import android.widget.LinearLayout; @@ -30,6 +29,7 @@ import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.SortCategory; import com.beemdevelopment.aegis.ViewMode; +import com.beemdevelopment.aegis.helpers.AnimationsHelper; import com.beemdevelopment.aegis.helpers.MetricsHelper; import com.beemdevelopment.aegis.helpers.SimpleItemTouchHelperCallback; import com.beemdevelopment.aegis.helpers.ThemeHelper; @@ -101,6 +101,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa // set up the recycler view _recyclerView = view.findViewById(R.id.rvKeyProfiles); + _recyclerView.setItemAnimator(null); _recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { @@ -141,10 +142,6 @@ public int getSpanSize(int position) { _touchHelper.attachToRecyclerView(_recyclerView); _recyclerView.setAdapter(_adapter); - int resId = R.anim.layout_animation_fall_down; - LayoutAnimationController animation = AnimationUtils.loadLayoutAnimation(requireContext(), resId); - _recyclerView.setLayoutAnimation(animation); - _refresher = new UiRefresher(new UiRefresher.Listener() { @Override public void onRefresh() { @@ -386,13 +383,25 @@ public void addEntry(VaultEntry entry, boolean focusEntry) { if (focusEntry && position >= 0) { if ((_recyclerView.canScrollVertically(1) && position > layoutManager.findLastCompletelyVisibleItemPosition()) || (_recyclerView.canScrollVertically(-1) && position < layoutManager.findFirstCompletelyVisibleItemPosition())) { + boolean smoothScroll = !AnimationsHelper.Scale.TRANSITION.isZero(requireContext()); RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() { + private void handleScroll() { + _recyclerView.removeOnScrollListener(this); + _recyclerView.setOnTouchListener(null); + tempHighlightEntry(entry); + } + @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { - if (newState == RecyclerView.SCROLL_STATE_IDLE) { - _recyclerView.removeOnScrollListener(this); - _recyclerView.setOnTouchListener(null); - tempHighlightEntry(entry); + if (smoothScroll && newState == RecyclerView.SCROLL_STATE_IDLE) { + handleScroll(); + } + } + + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + if (!smoothScroll) { + handleScroll(); } } }; @@ -406,7 +415,13 @@ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newStat return false; }); - _recyclerView.smoothScrollToPosition(position); + // We can't easily control the speed of the smooth scroll animation, but we + // can at least disable it if animations are disabled + if (smoothScroll) { + _recyclerView.smoothScrollToPosition(position); + } else { + _recyclerView.scrollToPosition(position); + } } else { tempHighlightEntry(entry); } @@ -444,10 +459,9 @@ public void replaceEntry(UUID uuid, VaultEntry newEntry) { } public void runEntriesAnimation() { - final LayoutAnimationController controller = - AnimationUtils.loadLayoutAnimation(requireContext(), R.anim.layout_animation_fall_down); + LayoutAnimationController animationController = AnimationsHelper.loadScaledLayoutAnimation(requireContext(), R.anim.layout_animation_fall_down); - _recyclerView.setLayoutAnimation(controller); + _recyclerView.setLayoutAnimation(animationController); _recyclerView.scheduleLayoutAnimation(); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/TotpProgressBar.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/TotpProgressBar.java index cad6f95fc1..cbdbdb874f 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/TotpProgressBar.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/TotpProgressBar.java @@ -4,13 +4,13 @@ import android.content.Context; import android.os.Build; import android.os.Handler; -import android.provider.Settings; import android.util.AttributeSet; import android.view.animation.LinearInterpolator; import android.widget.ProgressBar; import androidx.annotation.RequiresApi; +import com.beemdevelopment.aegis.helpers.AnimationsHelper; import com.beemdevelopment.aegis.otp.TotpInfo; public class TotpProgressBar extends ProgressBar { @@ -42,7 +42,7 @@ public void setPeriod(int period) { public void start() { stop(); _handler = new Handler(); - _animDurationScale = Settings.Global.getFloat(getContext().getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f); + _animDurationScale = AnimationsHelper.Scale.ANIMATOR.getValue(getContext()); refresh(); } diff --git a/app/src/main/res/layout/fragment_entry_list_view.xml b/app/src/main/res/layout/fragment_entry_list_view.xml index be0748d880..7bca520743 100644 --- a/app/src/main/res/layout/fragment_entry_list_view.xml +++ b/app/src/main/res/layout/fragment_entry_list_view.xml @@ -39,7 +39,6 @@ android:layout_height="0dp" android:scrollbars="vertical" android:id="@+id/rvKeyProfiles" - android:layoutAnimation="@anim/layout_animation_fall_down" android:layout_weight="1"/>