Do not animate QS state changes when not in view.
Skip QS state change animations when the target view is not fully
visible on screen.
Some tiles require extra time before the status is fully changed and
therefore will be animated later.
Change-Id: I08e76e0fdeab2b260cb7a41a117a6ff484ca3329
Fixes: 111680760
Test: visual and runtest systemui
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java
index c268d32..0cdb509a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java
@@ -29,7 +29,7 @@
super(context);
}
- public abstract void setIcon(State state);
+ public abstract void setIcon(State state, boolean allowAnimations);
public abstract void disableAnimation();
public abstract View getIconView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
index e7eefe8..376e6ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
@@ -43,9 +43,9 @@
R.dimen.qs_tile_icon_size));
}
- protected void updateIcon(ImageView iv, State state) {
+ protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
if (!(state.icon instanceof SignalIcon)) {
- super.updateIcon(iv, state);
+ super.updateIcon(iv, state, allowAnimations);
return;
} else if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) {
mSignalDrawable.setLevel(((SignalIcon) state.icon).getState());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index d9583af..ce90fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -18,14 +18,13 @@
import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.SlashImageView;
@@ -119,9 +118,9 @@
}
@Override
- public void setIcon(QSTile.State state) {
+ public void setIcon(State state, boolean allowAnimations) {
final SignalState s = (SignalState) state;
- setIcon(mSignal, s);
+ setIcon(mSignal, s, allowAnimations);
if (s.overlayIconId > 0) {
mOverlay.setVisibility(VISIBLE);
@@ -134,9 +133,9 @@
} else {
mSignal.setPaddingRelative(0, 0, 0, 0);
}
- final boolean shown = isShown();
- setVisibility(mIn, shown, s.activityIn);
- setVisibility(mOut, shown, s.activityOut);
+ final boolean shouldAnimate = allowAnimations && isShown();
+ setVisibility(mIn, shouldAnimate, s.activityIn);
+ setVisibility(mOut, shouldAnimate, s.activityOut);
}
private void setVisibility(View view, boolean shown, boolean visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index e7e756f..9dd5d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -84,16 +84,15 @@
layout(mIcon, iconLeft, top);
}
- public void setIcon(QSTile.State state) {
- setIcon((ImageView) mIcon, state);
+ public void setIcon(State state, boolean allowAnimations) {
+ setIcon((ImageView) mIcon, state, allowAnimations);
}
- protected void updateIcon(ImageView iv, State state) {
+ protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon;
if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
|| !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
- boolean shouldAnimate = iv.isShown() && mAnimationEnabled
- && iv.getDrawable() != null;
+ boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
Drawable d = icon != null
? shouldAnimate ? icon.getDrawable(mContext)
: icon.getInvisibleDrawable(mContext) : null;
@@ -128,7 +127,11 @@
}
}
- protected void setIcon(ImageView iv, QSTile.State state) {
+ private boolean shouldAnimate(ImageView iv) {
+ return mAnimationEnabled && iv.isShown() && iv.getDrawable() != null;
+ }
+
+ protected void setIcon(ImageView iv, QSTile.State state, boolean allowAnimations) {
if (state.disabledByPolicy) {
iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
} else {
@@ -137,8 +140,8 @@
if (state.state != mState) {
int color = getColor(state.state);
mState = state.state;
- if (iv.isShown() && mTint != 0) {
- animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state));
+ if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
+ animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
mTint = color;
} else {
if (iv instanceof AlphaControlledSlashImageView) {
@@ -148,10 +151,10 @@
setTint(iv, color);
}
mTint = color;
- updateIcon(iv, state);
+ updateIcon(iv, state, allowAnimations);
}
} else {
- updateIcon(iv, state);
+ updateIcon(iv, state, allowAnimations);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 91afef02..d42127e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -47,6 +47,7 @@
private static final String TAG = "QSTileBaseView";
private final H mHandler = new H();
+ private final int[] mLocInScreen = new int[2];
private final FrameLayout mIconFrame;
protected QSIconView mIcon;
protected RippleDrawable mRipple;
@@ -178,8 +179,9 @@
protected void handleStateChanged(QSTile.State state) {
int circleColor = getCircleColor(state.state);
+ boolean allowAnimations = animationsEnabled();
if (circleColor != mCircleColor) {
- if (mBg.isShown() && animationsEnabled()) {
+ if (allowAnimations) {
ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
.setDuration(QS_ANIM_LENGTH);
animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
@@ -192,7 +194,7 @@
}
setClickable(state.state != Tile.STATE_UNAVAILABLE);
- mIcon.setIcon(state);
+ mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
mAccessibilityClass = state.expandedAccessibilityClassName;
@@ -205,8 +207,17 @@
}
}
+ /* The view should not be animated if it's not on screen and no part of it is visible.
+ */
protected boolean animationsEnabled() {
- return true;
+ if (!isShown()) {
+ return false;
+ }
+ if (getAlpha() != 1f) {
+ return false;
+ }
+ getLocationOnScreen(mLocInScreen);
+ return mLocInScreen[1] >= -getHeight();
}
private int getCircleColor(int state) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index f9f4f497..c5e4043 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -59,14 +59,14 @@
// No current icon, only the static drawable should be used.
s.icon = mock(Icon.class);
when(iv.getDrawable()).thenReturn(null);
- mIconView.updateIcon(iv, s);
+ mIconView.updateIcon(iv, s, true);
verify(s.icon, never()).getDrawable(any());
verify(s.icon).getInvisibleDrawable(any());
// Has icon, should use the standard (animated) form.
s.icon = mock(Icon.class);
when(iv.getDrawable()).thenReturn(mock(Drawable.class));
- mIconView.updateIcon(iv, s);
+ mIconView.updateIcon(iv, s, true);
verify(s.icon).getDrawable(any());
verify(s.icon, never()).getInvisibleDrawable(any());
}
@@ -79,7 +79,7 @@
int desiredColor = mIconView.getColor(s.state);
when(iv.isShown()).thenReturn(true);
- mIconView.setIcon(iv, s);
+ mIconView.setIcon(iv, s, true);
verify(iv).setImageTintList(argThat(stateList -> stateList.getColors()[0] == desiredColor));
}
}