diff options
7 files changed, 142 insertions, 51 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index ad09febd74a9..a155dc4d7639 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -104,6 +104,13 @@ flag { } flag { + name: "notifications_heads_up_refactor" + namespace: "systemui" + description: "Use HeadsUpInteractor to feed HUN updates to the NSSL." + bug: "325936094" +} + +flag { name: "pss_app_selector_abrupt_exit_fix" namespace: "systemui" description: "Fixes the app selector abruptly disappearing without an animation, when the" 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 be63301e5749..30564bb6eb84 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 @@ -60,7 +60,7 @@ class AvalancheControllerTest : SysuiTestCase() { private val mGlobalSettings = FakeGlobalSettings() private val mSystemClock = FakeSystemClock() private val mExecutor = FakeExecutor(mSystemClock) - private var testableHeadsUpManager: BaseHeadsUpManager? = null + private lateinit var testableHeadsUpManager: BaseHeadsUpManager @Before fun setUp() { @@ -88,20 +88,15 @@ class AvalancheControllerTest : SysuiTestCase() { } private fun createHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry { - val entry = testableHeadsUpManager!!.createHeadsUpEntry() - - entry.setEntry( + return testableHeadsUpManager.createHeadsUpEntry( NotificationEntryBuilder() .setSbn(HeadsUpManagerTestUtil.createSbn(id, Notification.Builder(mContext, ""))) .build() ) - return entry } private fun createFsiHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry { - val entry = testableHeadsUpManager!!.createHeadsUpEntry() - entry.setEntry(createFullScreenIntentEntry(id, mContext)) - return entry + return testableHeadsUpManager.createHeadsUpEntry(createFullScreenIntentEntry(id, mContext)) } @Test 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 ed0d272cd848..3dc449514699 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 @@ -38,7 +38,6 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.PendingIntent; import android.app.Person; -import android.content.Intent; import android.testing.TestableLooper; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -498,16 +497,16 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() { final BaseHeadsUpManager hum = createHeadsUpManager(); - final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry(); - ongoingCall.setEntry(new NotificationEntryBuilder() - .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0, - new Notification.Builder(mContext, "") - .setCategory(Notification.CATEGORY_CALL) - .setOngoing(true))) - .build()); + final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry( + new NotificationEntryBuilder() + .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0, + new Notification.Builder(mContext, "") + .setCategory(Notification.CATEGORY_CALL) + .setOngoing(true))) + .build()); - final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); - activeRemoteInput.setEntry(HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext)); + final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( + HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext)); activeRemoteInput.mRemoteInputActive = true; assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0); @@ -518,18 +517,18 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() { final BaseHeadsUpManager hum = createHeadsUpManager(); - final BaseHeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry(); final Person person = new Person.Builder().setName("person").build(); final PendingIntent intent = mock(PendingIntent.class); - incomingCall.setEntry(new NotificationEntryBuilder() - .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0, - new Notification.Builder(mContext, "") - .setStyle(Notification.CallStyle - .forIncomingCall(person, intent, intent)))) - .build()); - - final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(); - activeRemoteInput.setEntry(HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext)); + final BaseHeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry( + new NotificationEntryBuilder() + .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0, + new Notification.Builder(mContext, "") + .setStyle(Notification.CallStyle + .forIncomingCall(person, intent, intent)))) + .build()); + + final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( + HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext)); activeRemoteInput.mRemoteInputActive = true; assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0); @@ -541,8 +540,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { final BaseHeadsUpManager hum = createHeadsUpManager(); // Needs full screen intent in order to be pinned - final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(); - entryToPin.setEntry( + final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext)); // Note: the standard way to show a notification would be calling showNotification rather diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java index d8f77f054b49..3c9dc6345d31 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java @@ -54,9 +54,10 @@ class TestableHeadsUpManager extends BaseHeadsUpManager { mStickyForSomeTimeAutoDismissTime = BaseHeadsUpManagerTest.TEST_STICKY_AUTO_DISMISS_TIME; } + @NonNull @Override - protected HeadsUpEntry createHeadsUpEntry() { - mLastCreatedEntry = spy(super.createHeadsUpEntry()); + protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) { + mLastCreatedEntry = spy(super.createHeadsUpEntry(entry)); return mLastCreatedEntry; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt new file mode 100644 index 000000000000..62641fe2f229 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.shared + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the notifications heads up refactor flag state. */ +@Suppress("NOTHING_TO_INLINE") +object NotificationsHeadsUpRefactor { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the refactor enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.notificationsHeadsUpRefactor() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 24be3db6231f..86bb844e7be3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.collection.provider.OnReorder import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.AnimationStateHandler; import com.android.systemui.statusbar.policy.AvalancheController; @@ -94,6 +95,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp @Override public HeadsUpEntryPhone acquire() { + NotificationsHeadsUpRefactor.assertInLegacyMode(); if (!mPoolObjects.isEmpty()) { return mPoolObjects.pop(); } @@ -102,6 +104,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp @Override public boolean release(@NonNull HeadsUpEntryPhone instance) { + NotificationsHeadsUpRefactor.assertInLegacyMode(); mPoolObjects.push(instance); return true; } @@ -371,15 +374,24 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp /////////////////////////////////////////////////////////////////////////////////////////////// // HeadsUpManager utility (protected) methods overrides: + @NonNull @Override - protected HeadsUpEntry createHeadsUpEntry() { - return mEntryPool.acquire(); + protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) { + if (NotificationsHeadsUpRefactor.isEnabled()) { + return new HeadsUpEntryPhone(entry); + } else { + HeadsUpEntryPhone headsUpEntry = mEntryPool.acquire(); + headsUpEntry.setEntry(entry); + return headsUpEntry; + } } @Override protected void onEntryRemoved(HeadsUpEntry headsUpEntry) { super.onEntryRemoved(headsUpEntry); - mEntryPool.release((HeadsUpEntryPhone) headsUpEntry); + if (!NotificationsHeadsUpRefactor.isEnabled()) { + mEntryPool.release((HeadsUpEntryPhone) headsUpEntry); + } } @Override @@ -439,14 +451,22 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp */ private boolean extended; - @Override public boolean isSticky() { return super.isSticky() || mGutsShownPinned; } - public void setEntry(@NonNull final NotificationEntry entry) { - Runnable removeHeadsUpRunnable = () -> { + public HeadsUpEntryPhone() { + super(); + } + + public HeadsUpEntryPhone(NotificationEntry entry) { + super(entry); + } + + @Override + protected Runnable createRemoveRunnable(NotificationEntry entry) { + return () -> { if (!mVisualStabilityProvider.isReorderingAllowed() // We don't want to allow reordering while pulsing, but headsup need to // time out anyway @@ -460,8 +480,6 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp removeEntry(entry.getKey()); } }; - - setEntry(entry, removeHeadsUpRunnable); } @Override 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 50de3cba6b59..6f7e0468c246 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java @@ -39,6 +39,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; +import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.settings.GlobalSettings; @@ -162,11 +163,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { */ @Override public void showNotification(@NonNull NotificationEntry entry) { - HeadsUpEntry headsUpEntry = createHeadsUpEntry(); - - // Attach NotificationEntry for AvalancheController to log key and - // record mPostTime for AvalancheController sorting - headsUpEntry.setEntry(entry); + HeadsUpEntry headsUpEntry = createHeadsUpEntry(entry); Runnable runnable = () -> { // TODO(b/315362456) log outside runnable too @@ -375,7 +372,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } /** - * Remove a notification and reset the entry. + * Remove a notification from the alerting entries. * @param key key of notification to remove */ protected final void removeEntry(@NonNull String key) { @@ -395,7 +392,11 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { mHeadsUpEntryMap.remove(key); onEntryRemoved(headsUpEntry); entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - headsUpEntry.reset(); + if (NotificationsHeadsUpRefactor.isEnabled()) { + headsUpEntry.cancelAutoRemovalCallbacks("removeEntry"); + } else { + headsUpEntry.reset(); + } }; mAvalancheController.delete(headsUpEntry, runnable, "removeEntry"); } @@ -657,8 +658,8 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } @NonNull - protected HeadsUpEntry createHeadsUpEntry() { - return new HeadsUpEntry(); + protected HeadsUpEntry createHeadsUpEntry(NotificationEntry entry) { + return new HeadsUpEntry(entry); } /** @@ -694,11 +695,23 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { @Nullable private Runnable mCancelRemoveRunnable; + public HeadsUpEntry() { + NotificationsHeadsUpRefactor.assertInLegacyMode(); + } + + public HeadsUpEntry(NotificationEntry entry) { + // Attach NotificationEntry for AvalancheController to log key and + // record mPostTime for AvalancheController sorting + setEntry(entry, createRemoveRunnable(entry)); + } + + /** Attach a NotificationEntry. */ public void setEntry(@NonNull final NotificationEntry entry) { - setEntry(entry, () -> removeEntry(entry.getKey())); + NotificationsHeadsUpRefactor.assertInLegacyMode(); + setEntry(entry, createRemoveRunnable(entry)); } - public void setEntry(@NonNull final NotificationEntry entry, + private void setEntry(@NonNull final NotificationEntry entry, @Nullable Runnable removeRunnable) { mEntry = entry; mRemoveRunnable = removeRunnable; @@ -847,6 +860,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } public void reset() { + NotificationsHeadsUpRefactor.assertInLegacyMode(); cancelAutoRemovalCallbacks("reset()"); mEntry = null; mRemoveRunnable = null; @@ -919,6 +933,11 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager { } } + /** Creates a runnable to remove this notification from the alerting entries. */ + protected Runnable createRemoveRunnable(NotificationEntry entry) { + return () -> removeEntry(entry.getKey()); + } + /** * Calculate what the post time of a notification is at some current time. * @return the post time |