Fix that the empty shade view would be visible sometimes
Refactored the state to be more clear and make sure the end runnable is always called.
Fixes: 78861878
Test: have no notification, hide view by scrolling up, get notification, observe
Change-Id: I51b00696f4b2dba565a0213c24a5a67a3c4099e0
(cherry picked from commit d60ef9ec8715b51ab2f8d341b88c3665cfadd300)
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index b4c2ba8..6c5cebc 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -22,7 +22,7 @@
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:visibility="gone">
- <FrameLayout
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
@@ -46,5 +46,5 @@
android:contentDescription="@string/accessibility_clear_all"
android:text="@string/clear_all_notifications_text"
android:textColor="?attr/wallpaperTextColor"/>
- </FrameLayout>
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
</com.android.systemui.statusbar.FooterView>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 011be88..4da1558 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -84,8 +84,7 @@
if (view instanceof EmptyShadeView) {
EmptyShadeView emptyShadeView = (EmptyShadeView) view;
boolean visible = this.clipTopAmount <= mEmptyText.getPaddingTop() * 0.6f;
- emptyShadeView.performVisibilityAnimation(
- visible && !emptyShadeView.willBeGone());
+ emptyShadeView.setContentVisible(visible && emptyShadeView.isVisible());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
index 0f4b621..dc5bb9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
@@ -98,7 +98,7 @@
if (view instanceof FooterView) {
FooterView footerView = (FooterView) view;
boolean visible = this.clipTopAmount < mClearAllTopPadding;
- footerView.performVisibilityAnimation(visible && !footerView.willBeGone());
+ footerView.setContentVisible(visible && footerView.isVisible());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index b2eb18e..f27ab9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -16,13 +16,13 @@
package com.android.systemui.statusbar;
-import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Interpolators;
/**
@@ -33,11 +33,19 @@
protected View mContent;
protected View mSecondaryView;
- private boolean mIsVisible;
- private boolean mIsSecondaryVisible;
- private boolean mAnimating;
- private boolean mSecondaryAnimating;
+ private boolean mIsVisible = true;
+ private boolean mContentVisible = true;
+ private boolean mIsSecondaryVisible = true;
private int mDuration = 260;
+ private boolean mContentAnimating;
+ private final Runnable mContentVisibilityEndRunnable = () -> {
+ mContentAnimating = false;
+ if (getVisibility() != View.GONE && !mIsVisible) {
+ setVisibility(GONE);
+ setWillBeGone(false);
+ notifyHeightChanged(false /* needsAnimation */);
+ }
+ };
public StackScrollerDecorView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -48,7 +56,8 @@
super.onFinishInflate();
mContent = findContentView();
mSecondaryView = findSecondaryView();
- setInvisible();
+ setVisible(false /* nowVisible */, false /* animate */);
+ setSecondaryVisible(false /* nowVisible */, false /* animate */);
}
@Override
@@ -62,72 +71,82 @@
return true;
}
- public void performVisibilityAnimation(boolean nowVisible) {
- performVisibilityAnimation(nowVisible, null /* onFinishedRunnable */);
- }
-
- public void performVisibilityAnimation(boolean nowVisible, Runnable onFinishedRunnable) {
- boolean oldVisible = isVisible();
- animateText(mContent, nowVisible, oldVisible, new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mAnimating = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimating = false;
- mIsVisible = nowVisible;
- if (onFinishedRunnable != null) {
- onFinishedRunnable.run();
- }
- }
- });
- }
-
- public void performSecondaryVisibilityAnimation(boolean nowVisible) {
- performSecondaryVisibilityAnimation(nowVisible, null /* onFinishedRunnable */);
- }
-
- public void performSecondaryVisibilityAnimation(boolean nowVisible,
- Runnable onFinishedRunnable) {
- boolean oldVisible = isSecondaryVisible();
- animateText(mSecondaryView, nowVisible, oldVisible, new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mSecondaryAnimating = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mSecondaryAnimating = false;
- mIsSecondaryVisible = nowVisible;
- if (onFinishedRunnable != null) {
- onFinishedRunnable.run();
- }
- }
- });
- }
-
/**
- * Check whether the secondary view is visible or not.<p/>
+ * Set the content of this view to be visible in an animated way.
*
- * @see #isVisible()
+ * @param contentVisible True if the content should be visible or false if it should be hidden.
*/
- public boolean isSecondaryVisible() {
- return mSecondaryView != null && (mIsSecondaryVisible ^ mSecondaryAnimating);
+ public void setContentVisible(boolean contentVisible) {
+ setContentVisible(contentVisible, true /* animate */);
+ }
+ /**
+ * Set the content of this view to be visible.
+ * @param contentVisible True if the content should be visible or false if it should be hidden.
+ * @param animate Should an animation be performed.
+ */
+ private void setContentVisible(boolean contentVisible, boolean animate) {
+ if (mContentVisible != contentVisible) {
+ mContentAnimating = animate;
+ setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable);
+ mContentVisible = contentVisible;
+ } if (!mContentAnimating) {
+ mContentVisibilityEndRunnable.run();
+ }
}
/**
- * Check whether the whole view is visible or not.<p/>
- * The view is considered visible if it matches one of following:
- * <ul>
- * <li> It's visible and there is no ongoing animation. </li>
- * <li> It's not visible but is animating, thus being eventually visible. </li>
- * </ul>
+ * Make this view visible. If {@code false} is passed, the view will fade out it's content
+ * and set the view Visibility to GONE. If only the content should be changed
+ * {@link #setContentVisible(boolean)} can be used.
+ *
+ * @param nowVisible should the view be visible
+ * @param animate should the change be animated.
+ */
+ public void setVisible(boolean nowVisible, boolean animate) {
+ if (mIsVisible != nowVisible) {
+ mIsVisible = nowVisible;
+ if (animate) {
+ if (nowVisible) {
+ setVisibility(VISIBLE);
+ setWillBeGone(false);
+ notifyHeightChanged(false /* needsAnimation */);
+ } else {
+ setWillBeGone(true);
+ }
+ setContentVisible(nowVisible, true /* animate */);
+ } else {
+ setVisibility(nowVisible ? VISIBLE : GONE);
+ setContentVisible(nowVisible, false /* animate */);
+ setWillBeGone(false);
+ notifyHeightChanged(false /* needsAnimation */);
+ }
+ }
+ }
+
+ /**
+ * Set the secondary view of this layout to visible.
+ *
+ * @param nowVisible should the secondary view be visible
+ * @param animate should the change be animated
+ */
+ public void setSecondaryVisible(boolean nowVisible, boolean animate) {
+ if (mIsSecondaryVisible != nowVisible) {
+ setViewVisible(mSecondaryView, nowVisible, animate, null /* endRunnable */);
+ mIsSecondaryVisible = nowVisible;
+ }
+ }
+
+ @VisibleForTesting
+ boolean isSecondaryVisible() {
+ return mIsSecondaryVisible;
+ }
+
+ /**
+ * Is this view visible. If a view is currently animating to gone, it will
+ * return {@code false}.
*/
public boolean isVisible() {
- return mIsVisible ^ mAnimating;
+ return mIsVisible;
}
void setDuration(int duration) {
@@ -135,43 +154,35 @@
}
/**
- * Animate the text to a new visibility.
- *
- * @param view Target view, maybe content view or dissmiss view
- * @param nowVisible Should it now be visible
- * @param oldVisible Is it visible currently
- * @param listener A listener that doing flag settings or other actions
+ * Animate a view to a new visibility.
+ * @param view Target view, maybe content view or dismiss view.
+ * @param nowVisible Should it now be visible.
+ * @param animate Should this be done in an animated way.
+ * @param endRunnable A runnable that is run when the animation is done.
*/
- private void animateText(View view, boolean nowVisible, boolean oldVisible,
- AnimatorListenerAdapter listener) {
+ private void setViewVisible(View view, boolean nowVisible,
+ boolean animate, Runnable endRunnable) {
if (view == null) {
return;
}
-
- if (nowVisible != oldVisible) {
- // Animate text
- float endValue = nowVisible ? 1.0f : 0.0f;
- Interpolator interpolator;
- if (nowVisible) {
- interpolator = Interpolators.ALPHA_IN;
- } else {
- interpolator = Interpolators.ALPHA_OUT;
+ // cancel any previous animations
+ view.animate().cancel();
+ float endValue = nowVisible ? 1.0f : 0.0f;
+ if (!animate) {
+ view.setAlpha(endValue);
+ if (endRunnable != null) {
+ endRunnable.run();
}
- view.animate()
- .alpha(endValue)
- .setInterpolator(interpolator)
- .setDuration(mDuration)
- .setListener(listener);
+ return;
}
- }
- public void setInvisible() {
- mContent.setAlpha(0.0f);
- if (mSecondaryView != null) {
- mSecondaryView.setAlpha(0.0f);
- }
- mIsVisible = false;
- mIsSecondaryVisible = false;
+ // Animate the view alpha
+ Interpolator interpolator = nowVisible ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT;
+ view.animate()
+ .alpha(endValue)
+ .setInterpolator(interpolator)
+ .setDuration(mDuration)
+ .withEndAction(endRunnable);
}
@Override
@@ -180,13 +191,13 @@
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
- performVisibilityAnimation(false);
+ setContentVisible(false);
}
@Override
public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
// TODO: use delay and duration
- performVisibilityAnimation(true);
+ setContentVisible(true);
}
@Override
@@ -194,13 +205,6 @@
return false;
}
- public void cancelAnimation() {
- mContent.animate().cancel();
- if (mSecondaryView != null) {
- mSecondaryView.animate().cancel();
- }
- }
-
protected abstract View findContentView();
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 5c14015..662956c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1453,11 +1453,11 @@
@VisibleForTesting
protected void updateFooter() {
- boolean showFooterView = mState != StatusBarState.KEYGUARD
- && mEntryManager.getNotificationData().getActiveNotifications().size() != 0
+ boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications();
+ boolean showFooterView = (showDismissView ||
+ mEntryManager.getNotificationData().getActiveNotifications().size() != 0)
+ && mState != StatusBarState.KEYGUARD
&& !mRemoteInputManager.getController().isRemoteInputActive();
- boolean showDismissView = mClearAllEnabled && mState != StatusBarState.KEYGUARD
- && hasActiveClearableNotifications();
mStackScroller.updateFooterView(showFooterView, showDismissView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index ee70019..dc4b697 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3922,10 +3922,6 @@
}
public void goToFullShade(long delay) {
- if (mFooterView != null) {
- mFooterView.setInvisible();
- }
- mEmptyShadeView.setInvisible();
mGoToFullShadeNeedsAnimation = true;
mGoToFullShadeDelay = delay;
mNeedsAnimation = true;
@@ -4073,17 +4069,7 @@
}
public void updateEmptyShadeView(boolean visible) {
- int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
- int newVisibility = visible ? VISIBLE : GONE;
-
- boolean changedVisibility = oldVisibility != newVisibility;
- if (changedVisibility) {
- if (newVisibility != GONE) {
- showFooterView(mEmptyShadeView);
- } else {
- hideFooterView(mEmptyShadeView, true);
- }
- }
+ mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
int oldTextRes = mEmptyShadeView.getTextResource();
int newTextRes = mStatusBar.areNotificationsHidden()
@@ -4097,48 +4083,9 @@
if (mFooterView == null) {
return;
}
- int oldVisibility = mFooterView.willBeGone() ? GONE : mFooterView.getVisibility();
- int newVisibility = visible ? VISIBLE : GONE;
- if (oldVisibility != newVisibility) {
- if (newVisibility != GONE) {
- showFooterView(mFooterView);
- } else {
- hideFooterView(mFooterView, mFooterView.isButtonVisible());
- }
- }
- if (mFooterView.isSecondaryVisible() != showDismissView) {
- mFooterView.performSecondaryVisibilityAnimation(showDismissView);
- }
- }
-
- private void showFooterView(StackScrollerDecorView footerView) {
- if (footerView.willBeGone()) {
- footerView.cancelAnimation();
- } else {
- footerView.setInvisible();
- }
- footerView.setVisibility(VISIBLE);
- footerView.setWillBeGone(false);
- updateContentHeight();
- notifyHeightChangeListener(footerView);
- }
-
- private void hideFooterView(StackScrollerDecorView footerView, boolean isButtonVisible) {
- Runnable onHideFinishRunnable = new Runnable() {
- @Override
- public void run() {
- footerView.setVisibility(GONE);
- footerView.setWillBeGone(false);
- updateContentHeight();
- notifyHeightChangeListener(footerView);
- }
- };
- if (isButtonVisible && mIsExpanded && mAnimationsEnabled) {
- footerView.setWillBeGone(true);
- footerView.performVisibilityAnimation(false, onHideFinishRunnable);
- } else {
- onHideFinishRunnable.run();
- }
+ boolean animate = mIsExpanded && mAnimationsEnabled;
+ mFooterView.setVisible(visible, animate);
+ mFooterView.setSecondaryVisible(showDismissView, animate);
}
public void setDismissAllInProgress(boolean dismissAllInProgress) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
index 96b0255..e6fdfa4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
@@ -23,39 +23,22 @@
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.Icon;
-import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -90,34 +73,18 @@
@Test
public void testPerformVisibilityAnimation() {
- mView.setInvisible();
+ mView.setVisible(false /* visible */, false /* animate */);
assertFalse(mView.isVisible());
- Runnable test = new Runnable() {
- @Override
- public void run() {
- assertEquals(1.0f, mView.findContentView().getAlpha());
- assertEquals(0.0f, mView.findSecondaryView().getAlpha());
- assertTrue(mView.isVisible());
- }
- };
- mView.performVisibilityAnimation(true, test);
+ mView.setVisible(true /* visible */, true /* animate */);
}
@Test
public void testPerformSecondaryVisibilityAnimation() {
- mView.setInvisible();
+ mView.setSecondaryVisible(false /* visible */, false /* animate */);
assertFalse(mView.isSecondaryVisible());
- Runnable test = new Runnable() {
- @Override
- public void run() {
- assertEquals(0.0f, mView.findContentView().getAlpha());
- assertEquals(1.0f, mView.findSecondaryView().getAlpha());
- assertTrue(mView.isSecondaryVisible());
- }
- };
- mView.performSecondaryVisibilityAnimation(true, test);
+ mView.setSecondaryVisible(true /* visible */, true /* animate */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index eeb4209..5400e3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -18,6 +18,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -28,12 +29,10 @@
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.TestableDependency;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FooterView;
import com.android.systemui.statusbar.NotificationBlockingHelperManager;
@@ -169,12 +168,11 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
when(view.willBeGone()).thenReturn(true);
- when(view.isSecondaryVisible()).thenReturn(true);
mStackScroller.updateFooterView(true, false);
- verify(view).setVisibility(View.VISIBLE);
- verify(view).performSecondaryVisibilityAnimation(false);
+ verify(view).setVisible(eq(true), anyBoolean());
+ verify(view).setSecondaryVisible(eq(false), anyBoolean());
}
@Test
@@ -182,11 +180,10 @@
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
when(view.willBeGone()).thenReturn(true);
- when(view.isSecondaryVisible()).thenReturn(false);
mStackScroller.updateFooterView(true, true);
- verify(view).setVisibility(View.VISIBLE);
- verify(view).performSecondaryVisibilityAnimation(true);
+ verify(view).setVisible(eq(true), anyBoolean());
+ verify(view).setSecondaryVisible(eq(true), anyBoolean());
}
}