Fix HUN launch animation

Bug: 189884891
Fixes: 189884891
Test: Post an auto-cancellable notification with MAX priority and click
      it after > 1s.
Change-Id: I301633e5487cd30b23c0b7b5fa22bcbd14b63625
This commit is contained in:
Jordan Demeulenaere
2021-06-04 13:04:38 +02:00
parent 20da155df4
commit 65a6a0e18a
8 changed files with 138 additions and 20 deletions

View File

@@ -204,6 +204,12 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
return;
}
NotificationEntry entry = alertEntry.mEntry;
// If the notification is animating, we will remove it at the end of the animation.
if (entry != null && entry.isExpandAnimationRunning()) {
return;
}
mAlertEntries.remove(key);
onAlertEntryRemoved(alertEntry);
entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);

View File

@@ -7,6 +7,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
import com.android.systemui.statusbar.policy.HeadsUpUtil
import kotlin.math.ceil
import kotlin.math.max
@@ -22,8 +23,8 @@ class NotificationLaunchAnimatorControllerProvider(
return NotificationLaunchAnimatorController(
notificationShadeWindowViewController,
notificationListContainer,
notification,
headsUpManager
headsUpManager,
notification
)
}
}
@@ -36,10 +37,11 @@ class NotificationLaunchAnimatorControllerProvider(
class NotificationLaunchAnimatorController(
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
private val notificationListContainer: NotificationListContainer,
private val notification: ExpandableNotificationRow,
private val headsUpManager: HeadsUpManagerPhone
private val headsUpManager: HeadsUpManagerPhone,
private val notification: ExpandableNotificationRow
) : ActivityLaunchAnimator.Controller {
private val notificationKey = notification.entry.sbn.key
private val notificationEntry = notification.entry
private val notificationKey = notificationEntry.sbn.key
override var launchContainer: ViewGroup
get() = notification.rootView as ViewGroup
@@ -82,6 +84,7 @@ class NotificationLaunchAnimatorController(
override fun onIntentStarted(willAnimate: Boolean) {
notificationShadeWindowViewController.setExpandAnimationRunning(willAnimate)
notificationEntry.isExpandAnimationRunning = willAnimate
if (!willAnimate) {
removeHun(animate = true)
@@ -93,6 +96,7 @@ class NotificationLaunchAnimatorController(
return
}
HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(notification, animate)
headsUpManager.removeNotification(notificationKey, true /* releaseImmediately */, animate)
}
@@ -100,6 +104,7 @@ class NotificationLaunchAnimatorController(
// TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
// here?
notificationShadeWindowViewController.setExpandAnimationRunning(false)
notificationEntry.isExpandAnimationRunning = false
removeHun(animate = true)
}
@@ -116,6 +121,7 @@ class NotificationLaunchAnimatorController(
notification.isExpandAnimationRunning = false
notificationShadeWindowViewController.setExpandAnimationRunning(false)
notificationEntry.isExpandAnimationRunning = false
notificationListContainer.setExpandingNotification(null)
applyParams(null)
removeHun(animate = false)

View File

@@ -179,6 +179,7 @@ public final class NotificationEntry extends ListEntry {
private boolean mIsAlerting;
public boolean mRemoteEditImeVisible;
private boolean mExpandAnimationRunning;
/**
* @param sbn the StatusBarNotification from system server
@@ -952,6 +953,16 @@ public final class NotificationEntry extends ListEntry {
return mIsAlerting;
}
/** Set whether this notification is currently used to animate a launch. */
public void setExpandAnimationRunning(boolean expandAnimationRunning) {
mExpandAnimationRunning = expandAnimationRunning;
}
/** Whether this notification is currently used to animate a launch. */
public boolean isExpandAnimationRunning() {
return mExpandAnimationRunning;
}
/** Information about a suggestion that is being edited. */
public static class EditedSuggestionInfo {

View File

@@ -591,7 +591,7 @@ public class ViewState implements Dumpable {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(child, false);
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
child.setTag(TAG_START_TRANSLATION_Y, null);
child.setTag(TAG_END_TRANSLATION_Y, null);

View File

@@ -28,8 +28,8 @@ class StatusBarLaunchAnimatorController(
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
statusBar.onLaunchAnimationEnd(isExpandingFullyAbove)
statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(false)
statusBar.onLaunchAnimationEnd(isExpandingFullyAbove)
}
override fun onLaunchAnimationProgress(

View File

@@ -282,15 +282,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
boolean showOverLockscreen) {
mLogger.logHandleClickAfterKeyguardDismissed(entry.getKey());
// TODO: Some of this code may be able to move to NotificationEntryManager.
String key = row.getEntry().getSbn().getKey();
if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(key)) {
// Release the HUN notification to the shade.
if (mPresenter.isPresenterFullyCollapsed()) {
HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
}
}
final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
entry, row, controller, intent,
isActivityIntent, animate);
@@ -337,7 +328,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
// bypass work challenge
if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId,
intent.getIntentSender(), notificationKey)) {
removeHUN(row);
removeHunAfterClick(row);
// Show work challenge, do not run PendingIntent and
// remove notification
collapseOnMainThread();
@@ -357,7 +348,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
final boolean canBubble = entry.canBubble();
if (canBubble) {
mLogger.logExpandingBubble(notificationKey);
removeHUN(row);
removeHunAfterClick(row);
expandBubbleStackOnMainThread(entry);
} else {
startNotificationIntent(intent, fillInIntent, entry, row, animate, isActivityIntent);
@@ -508,9 +499,14 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
}, null, false /* afterKeyguardGone */);
}
private void removeHUN(ExpandableNotificationRow row) {
private void removeHunAfterClick(ExpandableNotificationRow row) {
String key = row.getEntry().getSbn().getKey();
if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(key)) {
// Release the HUN notification to the shade.
if (mPresenter.isPresenterFullyCollapsed()) {
HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(row, true);
}
// In most cases, when FLAG_AUTO_CANCEL is set, the notification will
// become canceled shortly by NoMan, but we can't assume that.
mHeadsUpManager.removeNotification(key, true /* releaseImmediately */);

View File

@@ -31,7 +31,7 @@ public final class HeadsUpUtil {
* @param view The view to be set the flag to.
* @param clicked True to set as clicked. False to not-clicked.
*/
public static void setIsClickedHeadsUpNotification(View view, boolean clicked) {
public static void setNeedsHeadsUpDisappearAnimationAfterClick(View view, boolean clicked) {
view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
}

View File

@@ -0,0 +1,99 @@
package com.android.systemui.statusbar.notification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
import com.android.systemui.statusbar.policy.HeadsUpUtil
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
@Mock lateinit var notificationShadeWindowViewController: NotificationShadeWindowViewController
@Mock lateinit var notificationListContainer: NotificationListContainer
@Mock lateinit var headsUpManager: HeadsUpManagerPhone
private lateinit var notificationTestHelper: NotificationTestHelper
private lateinit var notification: ExpandableNotificationRow
private lateinit var controller: NotificationLaunchAnimatorController
private val notificationKey: String
get() = notification.entry.sbn.key
@get:Rule val rule = MockitoJUnit.rule()
@Before
fun setUp() {
allowTestableLooperAsMainThread()
notificationTestHelper =
NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
notification = notificationTestHelper.createRow()
controller = NotificationLaunchAnimatorController(
notificationShadeWindowViewController,
notificationListContainer,
headsUpManager,
notification
)
}
private fun flagNotificationAsHun() {
`when`(headsUpManager.isAlerting(notificationKey)).thenReturn(true)
}
@Test
fun testHunIsRemovedIfWeDontAnimateLaunch() {
flagNotificationAsHun()
controller.onIntentStarted(willAnimate = false)
assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, true /* animate */)
}
@Test
fun testHunIsRemovedWhenAnimationIsCancelled() {
flagNotificationAsHun()
controller.onLaunchAnimationCancelled()
assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, true /* animate */)
}
@Test
fun testHunIsRemovedWhenAnimationEnds() {
flagNotificationAsHun()
controller.onLaunchAnimationEnd(isExpandingFullyAbove = true)
assertFalse(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, false /* animate */)
}
@Test
fun testNotificationIsExpandingDuringAnimation() {
controller.onIntentStarted(willAnimate = true)
assertTrue(notification.entry.isExpandAnimationRunning)
}
}