Set avalanche HUN duration based on next top HUN priority

Bug: 315362456
Flag: ACONFIG notification_throttle_hun DEVELOPMENT
Test: AvalancheControllerTest
Change-Id: I35962c0f97bc65f0df8688e4156be1daf82117e1
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 183a58a..be63301 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
+import com.android.systemui.statusbar.policy.HeadsUpManagerTestUtil.createFullScreenIntentEntry
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.settings.FakeGlobalSettings
 import com.android.systemui.util.time.FakeSystemClock
@@ -97,6 +98,12 @@
         return entry
     }
 
+    private fun createFsiHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry {
+        val entry = testableHeadsUpManager!!.createHeadsUpEntry()
+        entry.setEntry(createFullScreenIntentEntry(id, mContext))
+        return entry
+    }
+
     @Test
     fun testUpdate_isShowing_runsRunnable() {
         // Entry is showing
@@ -238,4 +245,68 @@
         // Next entry is shown
         Truth.assertThat(mAvalancheController.headsUpEntryShowing).isEqualTo(nextEntry)
     }
+
+    @Test
+    fun testGetDurationMs_lastEntry_useAutoDismissTime() {
+        // Entry is showing
+        val showingEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.headsUpEntryShowing = showingEntry
+
+        // Nothing is next
+        mAvalancheController.clearNext()
+
+        val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+        Truth.assertThat(durationMs).isEqualTo(5000)
+    }
+
+    @Test
+    fun testGetDurationMs_nextEntryLowerPriority_500() {
+        // Entry is showing
+        val showingEntry = createFsiHeadsUpEntry(id = 1)
+        mAvalancheController.headsUpEntryShowing = showingEntry
+
+        // There's another entry waiting to show next
+        val nextEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+        // Next entry has lower priority
+        Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(1)
+
+        val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+        Truth.assertThat(durationMs).isEqualTo(5000)
+    }
+
+    @Test
+    fun testGetDurationMs_nextEntrySamePriority_1000() {
+        // Entry is showing
+        val showingEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.headsUpEntryShowing = showingEntry
+
+        // There's another entry waiting to show next
+        val nextEntry = createHeadsUpEntry(id = 1)
+        mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+        // Same priority
+        Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(0)
+
+        val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+        Truth.assertThat(durationMs).isEqualTo(1000)
+    }
+
+    @Test
+    fun testGetDurationMs_nextEntryHigherPriority_500() {
+        // Entry is showing
+        val showingEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.headsUpEntryShowing = showingEntry
+
+        // There's another entry waiting to show next
+        val nextEntry = createFsiHeadsUpEntry(id = 1)
+        mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+        // Next entry has higher priority
+        Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(-1)
+
+        val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+        Truth.assertThat(durationMs).isEqualTo(500)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 830bcef..ed0d272 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -117,21 +117,6 @@
         return HeadsUpManagerTestUtil.createEntry(id, notif);
     }
 
-    private PendingIntent createFullScreenIntent() {
-        return PendingIntent.getActivity(
-                getContext(), 0, new Intent(getContext(), this.getClass()),
-                PendingIntent.FLAG_MUTABLE_UNAUDITED);
-    }
-
-    private NotificationEntry createFullScreenIntentEntry(int id) {
-        final Notification notif = new Notification.Builder(mContext, "")
-                .setSmallIcon(R.drawable.ic_person)
-                .setFullScreenIntent(createFullScreenIntent(), /* highPriority */ true)
-                .build();
-        return HeadsUpManagerTestUtil.createEntry(id, notif);
-    }
-
-
     private void useAccessibilityTimeout(boolean use) {
         if (use) {
             doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
@@ -239,7 +224,8 @@
     @Test
     public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() {
         final BaseHeadsUpManager hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+        final NotificationEntry notifEntry =
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
 
         // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
         hum.showNotification(notifEntry);
@@ -254,7 +240,8 @@
     @Test
     public void testShouldHeadsUpBecomePinned_wasUnpinned_false() {
         final BaseHeadsUpManager hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+        final NotificationEntry notifEntry =
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
 
         // Add notifEntry to ANM mAlertEntries map and make it unpinned
         hum.showNotification(notifEntry);
@@ -443,7 +430,8 @@
     @Test
     public void testIsSticky_hasFullScreenIntent_true() {
         final BaseHeadsUpManager hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+        final NotificationEntry notifEntry =
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
 
         hum.showNotification(notifEntry);
 
@@ -554,7 +542,8 @@
 
         // Needs full screen intent in order to be pinned
         final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry();
-        entryToPin.setEntry(createFullScreenIntentEntry(/* id = */ 0));
+        entryToPin.setEntry(
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext));
 
         // Note: the standard way to show a notification would be calling showNotification rather
         // than onAlertEntryAdded. However, in practice showNotification in effect adds
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
index c70b03b..bda8619 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.Intent;
 import android.os.UserHandle;
 
 import android.content.Context;
@@ -67,4 +69,16 @@
         return new NotificationEntryBuilder().setSbn(
                 HeadsUpManagerTestUtil.createSbn(id, context)).build();
     }
+
+    protected static NotificationEntry createFullScreenIntentEntry(int id, Context context) {
+        final PendingIntent intent = PendingIntent.getActivity(
+                context, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
+
+        final Notification notif = new Notification.Builder(context, "")
+                .setSmallIcon(com.android.systemui.res.R.drawable.ic_person)
+                .setFullScreenIntent(intent, /* highPriority */ true)
+                .build();
+        return HeadsUpManagerTestUtil.createEntry(id, notif);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 6aaf5d6..55a0f59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -131,21 +131,37 @@
     }
 
     /**
-     * Returns true if given HeadsUpEntry is the last one tracked by AvalancheController. Used by
-     * BaseHeadsUpManager.HeadsUpEntry.calculateFinishTime to shorten display duration during active
-     * avalanche.
+     * Returns duration based on
+     * 1) Whether HeadsUpEntry is the last one tracked byAvalancheController
+     * 2) The priority of the top HUN in the next batch Used by
+     *    BaseHeadsUpManager.HeadsUpEntry.calculateFinishTime to shorten display duration.
      */
-    fun shortenDuration(entry: HeadsUpEntry): Boolean {
+    fun getDurationMs(entry: HeadsUpEntry, autoDismissMs: Int): Int {
         if (!NotificationThrottleHun.isEnabled) {
-            // Use default display duration, like we always did before AvalancheController existed
-            return false
+            // Use default duration, like we did before AvalancheController existed
+            return autoDismissMs
         }
         val showingList: MutableList<HeadsUpEntry> = mutableListOf()
         headsUpEntryShowing?.let { showingList.add(it) }
-        val allEntryList = showingList + nextList
 
-        // Shorten duration if not last entry
-        return allEntryList.indexOf(entry) != allEntryList.size - 1
+        val entryList = showingList + nextList
+        if (entryList.indexOf(entry) == entryList.size - 1) {
+            // Use default duration if last entry
+            return autoDismissMs
+        }
+
+        nextList.sort()
+        val nextEntry = nextList[0]
+
+        if (nextEntry.compareNonTimeFields(entry) == -1) {
+            // Next entry is higher priority
+            return 500
+        } else if (nextEntry.compareNonTimeFields(entry) == 0) {
+            // Next entry is same priority
+            return 1000
+        } else {
+            return autoDismissMs
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index fae0f0c..639d604 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -767,7 +767,7 @@
             return mEarliestRemovalTime < mSystemClock.elapsedRealtime();
         }
 
-        public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+        public int compareNonTimeFields(HeadsUpEntry headsUpEntry) {
             boolean isPinned = mEntry.isRowPinned();
             boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
             if (isPinned && !otherPinned) {
@@ -797,7 +797,14 @@
             } else if (!mRemoteInputActive && headsUpEntry.mRemoteInputActive) {
                 return 1;
             }
+            return 0;
+        }
 
+        public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+            int nonTimeCompareResult = compareNonTimeFields(headsUpEntry);
+            if (nonTimeCompareResult != 0) {
+                return nonTimeCompareResult;
+            }
             if (mPostTime > headsUpEntry.mPostTime) {
                 return -1;
             } else if (mPostTime == headsUpEntry.mPostTime) {
@@ -920,10 +927,8 @@
             int requestedTimeOutMs;
             if (isStickyForSomeTime()) {
                 requestedTimeOutMs = mStickyForSomeTimeAutoDismissTime;
-            } else if (mAvalancheController.shortenDuration(this)) {
-                requestedTimeOutMs = 1000;
             } else {
-                requestedTimeOutMs = mAutoDismissTime;
+                requestedTimeOutMs = mAvalancheController.getDurationMs(this, mAutoDismissTime);
             }
             final long duration = getRecommendedHeadsUpTimeoutMs(requestedTimeOutMs);
             return mPostTime + duration;