Respect system animation setting

Co-authored-by: Alexander Bakker <ab@alexbakker.me>
This commit is contained in:
Michael Schättgen 2023-08-20 17:29:08 +02:00 committed by Alexander Bakker
parent f38b6ec586
commit 9ff8efab69
9 changed files with 124 additions and 26 deletions

View file

@ -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;
}
}
}

View file

@ -33,6 +33,7 @@ import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.encoding.Base32; import com.beemdevelopment.aegis.encoding.Base32;
import com.beemdevelopment.aegis.encoding.EncodingException; import com.beemdevelopment.aegis.encoding.EncodingException;
import com.beemdevelopment.aegis.encoding.Hex; import com.beemdevelopment.aegis.encoding.Hex;
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
import com.beemdevelopment.aegis.helpers.DropdownHelper; import com.beemdevelopment.aegis.helpers.DropdownHelper;
import com.beemdevelopment.aegis.helpers.EditTextHelper; import com.beemdevelopment.aegis.helpers.EditTextHelper;
import com.beemdevelopment.aegis.helpers.IconViewHelper; import com.beemdevelopment.aegis.helpers.IconViewHelper;
@ -376,12 +377,12 @@ public class EditEntryActivity extends AegisActivity {
private void openAdvancedSettings() { private void openAdvancedSettings() {
Animation fadeOut = new AlphaAnimation(1, 0); Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator()); fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(220); fadeOut.setDuration(220 * (long) AnimationsHelper.Scale.ANIMATOR.getValue(this));
_advancedSettingsHeader.startAnimation(fadeOut); _advancedSettingsHeader.startAnimation(fadeOut);
Animation fadeIn = new AlphaAnimation(0, 1); Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new AccelerateInterpolator()); fadeIn.setInterpolator(new AccelerateInterpolator());
fadeIn.setDuration(250); fadeIn.setDuration(250 * (long) AnimationsHelper.Scale.ANIMATOR.getValue(this));
fadeOut.setAnimationListener(new SimpleAnimationEndListener((a) -> { fadeOut.setAnimationListener(new SimpleAnimationEndListener((a) -> {
_advancedSettingsHeader.setVisibility(View.GONE); _advancedSettingsHeader.setVisibility(View.GONE);

View file

@ -6,16 +6,19 @@ import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.View; import android.view.View;
import android.view.animation.Animation;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
import com.beemdevelopment.aegis.helpers.FabScrollHelper; import com.beemdevelopment.aegis.helpers.FabScrollHelper;
import com.beemdevelopment.aegis.icons.IconPack; import com.beemdevelopment.aegis.icons.IconPack;
import com.beemdevelopment.aegis.icons.IconPackException; import com.beemdevelopment.aegis.icons.IconPackException;
@ -81,6 +84,16 @@ public class IconPacksManagerFragment extends Fragment implements IconPackAdapte
updateEmptyState(); 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 @Override
public void onRemoveIconPack(IconPack pack) { public void onRemoveIconPack(IconPack pack) {
Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext()) Dialogs.showSecureDialog(new AlertDialog.Builder(requireContext())

View file

@ -3,14 +3,17 @@ package com.beemdevelopment.aegis.ui.fragments.preferences;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.animation.Animation;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.Preferences;
import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
import com.beemdevelopment.aegis.ui.dialogs.Dialogs; import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
import com.beemdevelopment.aegis.vault.VaultManager; import com.beemdevelopment.aegis.vault.VaultManager;
import com.beemdevelopment.aegis.vault.VaultRepositoryException; import com.beemdevelopment.aegis.vault.VaultRepositoryException;
@ -62,6 +65,16 @@ public abstract class PreferencesFragment extends PreferenceFragmentCompat {
return _result; 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) { public void setResult(Intent result) {
_result = result; _result = result;
requireActivity().setResult(Activity.RESULT_OK, _result); requireActivity().setResult(Activity.RESULT_OK, _result);

View file

@ -14,6 +14,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2; import androidx.viewpager2.widget.ViewPager2;
import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
import com.beemdevelopment.aegis.ui.AegisActivity; import com.beemdevelopment.aegis.ui.AegisActivity;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -135,7 +136,10 @@ public abstract class IntroBaseActivity extends AegisActivity implements IntroAc
Class<? extends SlideFragment> newSlide = _slides.get(pos); Class<? extends SlideFragment> newSlide = _slides.get(pos);
if (!onBeforeSlideChanged(oldSlide, newSlide)) { 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); onAfterSlideChanged(oldSlide, newSlide);

View file

@ -4,7 +4,6 @@ import android.graphics.PorterDuff;
import android.os.Handler; import android.os.Handler;
import android.view.View; import android.view.View;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
@ -17,6 +16,7 @@ import com.beemdevelopment.aegis.AccountNamePosition;
import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.Preferences;
import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.ViewMode; import com.beemdevelopment.aegis.ViewMode;
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
import com.beemdevelopment.aegis.helpers.IconViewHelper; import com.beemdevelopment.aegis.helpers.IconViewHelper;
import com.beemdevelopment.aegis.helpers.TextDrawableHelper; import com.beemdevelopment.aegis.helpers.TextDrawableHelper;
import com.beemdevelopment.aegis.helpers.ThemeHelper; import com.beemdevelopment.aegis.helpers.ThemeHelper;
@ -91,8 +91,8 @@ public class EntryHolder extends RecyclerView.ViewHolder {
_progressBar.getProgressDrawable().setColorFilter(primaryColorId, PorterDuff.Mode.SRC_IN); _progressBar.getProgressDrawable().setColorFilter(primaryColorId, PorterDuff.Mode.SRC_IN);
_view.setBackground(_view.getContext().getResources().getDrawable(R.color.card_background)); _view.setBackground(_view.getContext().getResources().getDrawable(R.color.card_background));
_scaleIn = AnimationUtils.loadAnimation(view.getContext(), R.anim.item_scale_in); _scaleIn = AnimationsHelper.loadScaledAnimation(view.getContext(), R.anim.item_scale_in);
_scaleOut = AnimationUtils.loadAnimation(view.getContext(), R.anim.item_scale_out); _scaleOut = AnimationsHelper.loadScaledAnimation(view.getContext(), R.anim.item_scale_out);
_refresher = new UiRefresher(new UiRefresher.Listener() { _refresher = new UiRefresher(new UiRefresher.Listener() {
@Override @Override
@ -379,10 +379,10 @@ public class EntryHolder extends RecyclerView.ViewHolder {
public void animateCopyText(boolean includeSlideAnimation) { public void animateCopyText(boolean includeSlideAnimation) {
_animationHandler.removeCallbacksAndMessages(null); _animationHandler.removeCallbacksAndMessages(null);
Animation slideDownFadeIn = AnimationUtils.loadAnimation(itemView.getContext(), R.anim.slide_down_fade_in); Animation slideDownFadeIn = AnimationsHelper.loadScaledAnimation(itemView.getContext(), R.anim.slide_down_fade_in);
Animation slideDownFadeOut = AnimationUtils.loadAnimation(itemView.getContext(), R.anim.slide_down_fade_out); Animation slideDownFadeOut = AnimationsHelper.loadScaledAnimation(itemView.getContext(), R.anim.slide_down_fade_out);
Animation fadeOut = AnimationUtils.loadAnimation(itemView.getContext(), R.anim.fade_out); Animation fadeOut = AnimationsHelper.loadScaledAnimation(itemView.getContext(), R.anim.fade_out);
Animation fadeIn = AnimationUtils.loadAnimation(itemView.getContext(), R.anim.fade_in); Animation fadeIn = AnimationsHelper.loadScaledAnimation(itemView.getContext(), R.anim.fade_in);
if (includeSlideAnimation) { if (includeSlideAnimation) {
_profileCopied.startAnimation(slideDownFadeIn); _profileCopied.startAnimation(slideDownFadeIn);

View file

@ -10,7 +10,6 @@ import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController; import android.view.animation.LayoutAnimationController;
import android.widget.Button; import android.widget.Button;
import android.widget.LinearLayout; import android.widget.LinearLayout;
@ -30,6 +29,7 @@ import com.beemdevelopment.aegis.Preferences;
import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.SortCategory; import com.beemdevelopment.aegis.SortCategory;
import com.beemdevelopment.aegis.ViewMode; import com.beemdevelopment.aegis.ViewMode;
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
import com.beemdevelopment.aegis.helpers.MetricsHelper; import com.beemdevelopment.aegis.helpers.MetricsHelper;
import com.beemdevelopment.aegis.helpers.SimpleItemTouchHelperCallback; import com.beemdevelopment.aegis.helpers.SimpleItemTouchHelperCallback;
import com.beemdevelopment.aegis.helpers.ThemeHelper; import com.beemdevelopment.aegis.helpers.ThemeHelper;
@ -101,6 +101,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
// set up the recycler view // set up the recycler view
_recyclerView = view.findViewById(R.id.rvKeyProfiles); _recyclerView = view.findViewById(R.id.rvKeyProfiles);
_recyclerView.setItemAnimator(null);
_recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { _recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override @Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) { public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
@ -141,10 +142,6 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
_touchHelper.attachToRecyclerView(_recyclerView); _touchHelper.attachToRecyclerView(_recyclerView);
_recyclerView.setAdapter(_adapter); _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() { _refresher = new UiRefresher(new UiRefresher.Listener() {
@Override @Override
public void onRefresh() { public void onRefresh() {
@ -386,14 +383,26 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
if (focusEntry && position >= 0) { if (focusEntry && position >= 0) {
if ((_recyclerView.canScrollVertically(1) && position > layoutManager.findLastCompletelyVisibleItemPosition()) if ((_recyclerView.canScrollVertically(1) && position > layoutManager.findLastCompletelyVisibleItemPosition())
|| (_recyclerView.canScrollVertically(-1) && position < layoutManager.findFirstCompletelyVisibleItemPosition())) { || (_recyclerView.canScrollVertically(-1) && position < layoutManager.findFirstCompletelyVisibleItemPosition())) {
boolean smoothScroll = !AnimationsHelper.Scale.TRANSITION.isZero(requireContext());
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() { RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
@Override private void handleScroll() {
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
_recyclerView.removeOnScrollListener(this); _recyclerView.removeOnScrollListener(this);
_recyclerView.setOnTouchListener(null); _recyclerView.setOnTouchListener(null);
tempHighlightEntry(entry); tempHighlightEntry(entry);
} }
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
if (smoothScroll && newState == RecyclerView.SCROLL_STATE_IDLE) {
handleScroll();
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (!smoothScroll) {
handleScroll();
}
} }
}; };
_recyclerView.addOnScrollListener(scrollListener); _recyclerView.addOnScrollListener(scrollListener);
@ -406,7 +415,13 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
return false; return false;
}); });
// 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); _recyclerView.smoothScrollToPosition(position);
} else {
_recyclerView.scrollToPosition(position);
}
} else { } else {
tempHighlightEntry(entry); tempHighlightEntry(entry);
} }
@ -444,10 +459,9 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
} }
public void runEntriesAnimation() { public void runEntriesAnimation() {
final LayoutAnimationController controller = LayoutAnimationController animationController = AnimationsHelper.loadScaledLayoutAnimation(requireContext(), R.anim.layout_animation_fall_down);
AnimationUtils.loadLayoutAnimation(requireContext(), R.anim.layout_animation_fall_down);
_recyclerView.setLayoutAnimation(controller); _recyclerView.setLayoutAnimation(animationController);
_recyclerView.scheduleLayoutAnimation(); _recyclerView.scheduleLayoutAnimation();
} }

View file

@ -4,13 +4,13 @@ import android.animation.ObjectAnimator;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.provider.Settings;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.animation.LinearInterpolator; import android.view.animation.LinearInterpolator;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
import com.beemdevelopment.aegis.otp.TotpInfo; import com.beemdevelopment.aegis.otp.TotpInfo;
public class TotpProgressBar extends ProgressBar { public class TotpProgressBar extends ProgressBar {
@ -42,7 +42,7 @@ public class TotpProgressBar extends ProgressBar {
public void start() { public void start() {
stop(); stop();
_handler = new Handler(); _handler = new Handler();
_animDurationScale = Settings.Global.getFloat(getContext().getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f); _animDurationScale = AnimationsHelper.Scale.ANIMATOR.getValue(getContext());
refresh(); refresh();
} }

View file

@ -39,7 +39,6 @@
android:layout_height="0dp" android:layout_height="0dp"
android:scrollbars="vertical" android:scrollbars="vertical"
android:id="@+id/rvKeyProfiles" android:id="@+id/rvKeyProfiles"
android:layoutAnimation="@anim/layout_animation_fall_down"
android:layout_weight="1"/> android:layout_weight="1"/>
<LinearLayout <LinearLayout