Merge "Update ScreenRecorder to handle StopReason" into main
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 02c7901..049189f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28277,7 +28277,7 @@
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
- if (mParent != null) {
+ if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 81734a3..d0d4af6 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -116,3 +116,14 @@
description: "Relax the assumption of non-match parent activity"
bug: "356277166"
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "allow_multiple_adjacent_task_fragments"
+ description: "Refactor to allow more than 2 adjacent TaskFragments"
+ bug: "373709676"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index 43c404e..ae3ad36 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -213,35 +213,6 @@
assertTrue(autofillableViews.containsAll(Arrays.asList(viewA, viewC)));
}
- @Test
- public void testMeasureCache() {
- final int spec1 = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST);
- final int spec2 = View.MeasureSpec.makeMeasureSpec(50, View.MeasureSpec.AT_MOST);
- final Context context = getInstrumentation().getContext();
- final View child = new View(context);
- final TestView parent = new TestView(context, 0);
- parent.addView(child);
-
- child.setPadding(1, 2, 3, 4);
- parent.measure(spec1, spec1);
- assertEquals(4, parent.getMeasuredWidth());
- assertEquals(6, parent.getMeasuredHeight());
-
- child.setPadding(5, 6, 7, 8);
- parent.measure(spec2, spec2);
- assertEquals(12, parent.getMeasuredWidth());
- assertEquals(14, parent.getMeasuredHeight());
-
- // This ends the state of forceLayout.
- parent.layout(0, 0, 50, 50);
-
- // The cached values should be cleared after the new setPadding is called. And the measured
- // width and height should be up-to-date.
- parent.measure(spec1, spec1);
- assertEquals(12, parent.getMeasuredWidth());
- assertEquals(14, parent.getMeasuredHeight());
- }
-
private static void getUnobscuredTouchableRegion(Region outRegion, View view) {
outRegion.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
final ViewParent parent = view.getParent();
@@ -269,19 +240,6 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// We don't layout this view.
}
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int measuredWidth = 0;
- int measuredHeight = 0;
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- measuredWidth += child.getPaddingLeft() + child.getPaddingRight();
- measuredHeight += child.getPaddingTop() + child.getPaddingBottom();
- }
- setMeasuredDimension(measuredWidth, measuredHeight);
- }
}
public static class AutofillableTestView extends TestView {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt
new file mode 100644
index 0000000..44a8518
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) 2019 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.headsup
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.Person
+import android.os.Handler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.kosmos.KosmosJavaAdapter
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl.HeadsUpEntry
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.settings.FakeGlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.eq
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWithLooper
+@RunWith(ParameterizedAndroidJunit4::class)
+// TODO(b/378142453): Merge this with HeadsUpManagerImplTest.
+open class HeadsUpManagerImplOldTest(flags: FlagsParameterization?) : SysuiTestCase() {
+ protected var mKosmos: KosmosJavaAdapter = KosmosJavaAdapter(this)
+
+ @JvmField @Rule var rule: MockitoRule = MockitoJUnit.rule()
+
+ private val mUiEventLoggerFake = UiEventLoggerFake()
+
+ private val mLogger: HeadsUpManagerLogger = Mockito.spy(HeadsUpManagerLogger(logcatLogBuffer()))
+
+ @Mock private val mBgHandler: Handler? = null
+
+ @Mock private val dumpManager: DumpManager? = null
+
+ @Mock private val mShadeInteractor: ShadeInteractor? = null
+ private var mAvalancheController: AvalancheController? = null
+
+ @Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null
+
+ protected val mGlobalSettings: FakeGlobalSettings = FakeGlobalSettings()
+ protected val mSystemClock: FakeSystemClock = FakeSystemClock()
+ protected val mExecutor: FakeExecutor = FakeExecutor(mSystemClock)
+
+ @Mock protected var mRow: ExpandableNotificationRow? = null
+
+ private fun createHeadsUpManager(): HeadsUpManagerImpl {
+ return TestableHeadsUpManager(
+ mContext,
+ mLogger,
+ mKosmos.statusBarStateController,
+ mKosmos.keyguardBypassController,
+ GroupMembershipManagerImpl(),
+ mKosmos.visualStabilityProvider,
+ mKosmos.configurationController,
+ mExecutor,
+ mGlobalSettings,
+ mSystemClock,
+ mAccessibilityMgr,
+ mUiEventLoggerFake,
+ JavaAdapter(mKosmos.testScope),
+ mShadeInteractor,
+ mAvalancheController,
+ )
+ }
+
+ private fun createStickyEntry(id: Int): NotificationEntry {
+ val notif =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFullScreenIntent(
+ Mockito.mock(PendingIntent::class.java), /* highPriority */
+ true,
+ )
+ .build()
+ return HeadsUpManagerTestUtil.createEntry(id, notif)
+ }
+
+ private fun createStickyForSomeTimeEntry(id: Int): NotificationEntry {
+ val notif =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFlag(Notification.FLAG_FSI_REQUESTED_BUT_DENIED, true)
+ .build()
+ return HeadsUpManagerTestUtil.createEntry(id, notif)
+ }
+
+ private fun useAccessibilityTimeout(use: Boolean) {
+ if (use) {
+ Mockito.doReturn(TEST_A11Y_AUTO_DISMISS_TIME)
+ .`when`(mAccessibilityMgr!!)
+ .getRecommendedTimeoutMillis(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
+ } else {
+ Mockito.`when`(
+ mAccessibilityMgr!!.getRecommendedTimeoutMillis(
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.anyInt(),
+ )
+ )
+ .then { i: InvocationOnMock -> i.getArgument(0) }
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags!!)
+ }
+
+ @Throws(Exception::class)
+ override fun SysuiSetup() {
+ super.SysuiSetup()
+ mAvalancheController =
+ AvalancheController(dumpManager!!, mUiEventLoggerFake, mLogger, mBgHandler!!)
+ Mockito.`when`(mShadeInteractor!!.isAnyExpanded).thenReturn(MutableStateFlow(true))
+ Mockito.`when`(mKosmos.keyguardBypassController.bypassEnabled).thenReturn(false)
+ }
+
+ @Test
+ fun testHasNotifications_headsUpManagerMapNotEmpty_true() {
+ val bhum = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ bhum.showNotification(entry)
+
+ Truth.assertThat(bhum.mHeadsUpEntryMap).isNotEmpty()
+ Truth.assertThat(bhum.hasNotifications()).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testHasNotifications_avalancheMapNotEmpty_true() {
+ val bhum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
+ mAvalancheController!!.addToNext(headsUpEntry) {}
+
+ Truth.assertThat(mAvalancheController!!.getWaitingEntryList()).isNotEmpty()
+ Truth.assertThat(bhum.hasNotifications()).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testHasNotifications_false() {
+ val bhum = createHeadsUpManager()
+ Truth.assertThat(bhum.mHeadsUpEntryMap).isEmpty()
+ Truth.assertThat(mAvalancheController!!.getWaitingEntryList()).isEmpty()
+ Truth.assertThat(bhum.hasNotifications()).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testGetHeadsUpEntryList_includesAvalancheEntryList() {
+ val bhum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
+ mAvalancheController!!.addToNext(headsUpEntry) {}
+
+ Truth.assertThat(bhum.headsUpEntryList).contains(headsUpEntry)
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testGetHeadsUpEntry_returnsAvalancheEntry() {
+ val bhum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
+ mAvalancheController!!.addToNext(headsUpEntry) {}
+
+ Truth.assertThat(bhum.getHeadsUpEntry(notifEntry.key)).isEqualTo(headsUpEntry)
+ }
+
+ @Test
+ fun testShowNotification_addsEntry() {
+ val alm = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ alm.showNotification(entry)
+
+ assertThat(alm.isHeadsUpEntry(entry.key)).isTrue()
+ assertThat(alm.hasNotifications()).isTrue()
+ assertThat(alm.getEntry(entry.key)).isEqualTo(entry)
+ }
+
+ @Test
+ fun testShowNotification_autoDismisses() {
+ val alm = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ alm.showNotification(entry)
+ mSystemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
+
+ assertThat(alm.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_removeDeferred() {
+ val alm = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ alm.showNotification(entry)
+
+ val removedImmediately =
+ alm.removeNotification(entry.key, /* releaseImmediately= */ false, "removeDeferred")
+ assertThat(removedImmediately).isFalse()
+ assertThat(alm.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testRemoveNotification_forceRemove() {
+ val alm = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ alm.showNotification(entry)
+
+ val removedImmediately =
+ alm.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
+ assertThat(removedImmediately).isTrue()
+ assertThat(alm.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testReleaseAllImmediately() {
+ val alm = createHeadsUpManager()
+ for (i in 0 until TEST_NUM_NOTIFICATIONS) {
+ val entry = HeadsUpManagerTestUtil.createEntry(i, mContext)
+ entry.row = mRow
+ alm.showNotification(entry)
+ }
+
+ alm.releaseAllImmediately()
+
+ assertThat(alm.allEntries.count()).isEqualTo(0)
+ }
+
+ @Test
+ fun testCanRemoveImmediately_notShownLongEnough() {
+ val alm = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ alm.showNotification(entry)
+
+ // The entry has just been added so we should not remove immediately.
+ assertThat(alm.canRemoveImmediately(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testHunRemovedLogging() {
+ val hum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ val headsUpEntry = Mockito.mock(HeadsUpEntry::class.java)
+ Mockito.`when`(headsUpEntry.pinnedStatus)
+ .thenReturn(MutableStateFlow(PinnedStatus.NotPinned))
+ headsUpEntry.mEntry = notifEntry
+
+ hum.onEntryRemoved(headsUpEntry, "test")
+
+ Mockito.verify(mLogger, Mockito.times(1)).logNotificationActuallyRemoved(eq(notifEntry))
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
+ val hum = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ hum.showNotification(entry)
+ mSystemClock.advanceTime((TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME).toLong())
+
+ assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesWithDefaultTimeout() {
+ val hum = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ hum.showNotification(entry)
+ mSystemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
+ val hum = createHeadsUpManager()
+ val entry = createStickyForSomeTimeEntry(/* id= */ 0)
+ useAccessibilityTimeout(false)
+
+ hum.showNotification(entry)
+ mSystemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_sticky_neverAutoDismisses() {
+ val hum = createHeadsUpManager()
+ val entry = createStickyEntry(/* id= */ 0)
+ useAccessibilityTimeout(false)
+
+ hum.showNotification(entry)
+ mSystemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME).toLong()
+ )
+
+ assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_autoDismissesWithAccessibilityTimeout() {
+ val hum = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(true)
+
+ hum.showNotification(entry)
+ mSystemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
+ val hum = createHeadsUpManager()
+ val entry = createStickyForSomeTimeEntry(/* id= */ 0)
+ useAccessibilityTimeout(true)
+
+ hum.showNotification(entry)
+ mSystemClock.advanceTime(
+ (TEST_TOUCH_ACCEPTANCE_TIME +
+ (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+ .toLong()
+ )
+
+ assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+ }
+
+ @Test
+ fun testRemoveNotification_beforeMinimumDisplayTime() {
+ val hum = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ hum.showNotification(entry)
+
+ val removedImmediately =
+ hum.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ false,
+ "beforeMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isFalse()
+ assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+
+ mSystemClock.advanceTime(
+ ((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong()
+ )
+
+ assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_afterMinimumDisplayTime() {
+ val hum = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ useAccessibilityTimeout(false)
+
+ hum.showNotification(entry)
+ mSystemClock.advanceTime(
+ ((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong()
+ )
+
+ assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+
+ val removedImmediately =
+ hum.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ false,
+ "afterMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isTrue()
+ assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testRemoveNotification_releaseImmediately() {
+ val hum = createHeadsUpManager()
+ val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ hum.showNotification(entry)
+
+ val removedImmediately =
+ hum.removeNotification(
+ entry.key,
+ /* releaseImmediately = */ true,
+ "afterMinimumDisplayTime",
+ )
+ assertThat(removedImmediately).isTrue()
+ assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testIsSticky_rowPinnedAndExpanded_true() {
+ val hum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+ Mockito.`when`(mRow!!.isPinned).thenReturn(true)
+ notifEntry.row = mRow
+
+ hum.showNotification(notifEntry)
+
+ val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.setExpanded(true)
+
+ assertThat(hum.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_remoteInputActive_true() {
+ val hum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ hum.showNotification(notifEntry)
+
+ val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.mRemoteInputActive = true
+
+ assertThat(hum.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_hasFullScreenIntent_true() {
+ val hum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+
+ hum.showNotification(notifEntry)
+
+ assertThat(hum.isSticky(notifEntry.key)).isTrue()
+ }
+
+ @Test
+ fun testIsSticky_stickyForSomeTime_false() {
+ val hum = createHeadsUpManager()
+ val entry = createStickyForSomeTimeEntry(/* id= */ 0)
+
+ hum.showNotification(entry)
+
+ assertThat(hum.isSticky(entry.key)).isFalse()
+ }
+
+ @Test
+ fun testIsSticky_false() {
+ val hum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ hum.showNotification(notifEntry)
+
+ val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
+ headsUpEntry!!.setExpanded(false)
+ headsUpEntry.mRemoteInputActive = false
+
+ assertThat(hum.isSticky(notifEntry.key)).isFalse()
+ }
+
+ @Test
+ fun testCompareTo_withNullEntries() {
+ val hum = createHeadsUpManager()
+ val alertEntry = NotificationEntryBuilder().setTag("alert").build()
+
+ hum.showNotification(alertEntry)
+
+ assertThat(hum.compare(alertEntry, null)).isLessThan(0)
+ assertThat(hum.compare(null, alertEntry)).isGreaterThan(0)
+ assertThat(hum.compare(null, null)).isEqualTo(0)
+ }
+
+ @Test
+ fun testCompareTo_withNonAlertEntries() {
+ val hum = createHeadsUpManager()
+
+ val nonAlertEntry1 = NotificationEntryBuilder().setTag("nae1").build()
+ val nonAlertEntry2 = NotificationEntryBuilder().setTag("nae2").build()
+ val alertEntry = NotificationEntryBuilder().setTag("alert").build()
+ hum.showNotification(alertEntry)
+
+ assertThat(hum.compare(alertEntry, nonAlertEntry1)).isLessThan(0)
+ assertThat(hum.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0)
+ assertThat(hum.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0)
+ }
+
+ @Test
+ fun testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
+ val hum = createHeadsUpManager()
+
+ val ongoingCall =
+ hum.HeadsUpEntry(
+ NotificationEntryBuilder()
+ .setSbn(
+ HeadsUpManagerTestUtil.createSbn(
+ /* id = */ 0,
+ Notification.Builder(mContext, "")
+ .setCategory(Notification.CATEGORY_CALL)
+ .setOngoing(true),
+ )
+ )
+ .build()
+ )
+
+ val activeRemoteInput =
+ hum.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
+ activeRemoteInput.mRemoteInputActive = true
+
+ assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0)
+ assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0)
+ }
+
+ @Test
+ fun testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
+ val hum = createHeadsUpManager()
+
+ val person = Person.Builder().setName("person").build()
+ val intent = Mockito.mock(PendingIntent::class.java)
+ val incomingCall =
+ hum.HeadsUpEntry(
+ NotificationEntryBuilder()
+ .setSbn(
+ HeadsUpManagerTestUtil.createSbn(
+ /* id = */ 0,
+ Notification.Builder(mContext, "")
+ .setStyle(
+ Notification.CallStyle.forIncomingCall(person, intent, intent)
+ ),
+ )
+ )
+ .build()
+ )
+
+ val activeRemoteInput =
+ hum.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
+ activeRemoteInput.mRemoteInputActive = true
+
+ assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0)
+ assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0)
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testPinEntry_logsPeek_throttleEnabled() {
+ val hum = createHeadsUpManager()
+
+ // Needs full screen intent in order to be pinned
+ val entryToPin =
+ hum.HeadsUpEntry(
+ 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
+ // the notification and then updates it; in order to not log twice, the entry needs
+ // to have a functional ExpandableNotificationRow that can keep track of whether it's
+ // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+ hum.onEntryAdded(entryToPin)
+
+ assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(2)
+ assertThat(
+ AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(),
+ ).isEqualTo(mUiEventLoggerFake.eventId(0))
+ assertThat(
+ HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id,
+ ).isEqualTo(mUiEventLoggerFake.eventId(1))
+ }
+
+ @Test
+ @DisableFlags(NotificationThrottleHun.FLAG_NAME)
+ fun testPinEntry_logsPeek_throttleDisabled() {
+ val hum = createHeadsUpManager()
+
+ // Needs full screen intent in order to be pinned
+ val entryToPin =
+ hum.HeadsUpEntry(
+ 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
+ // the notification and then updates it; in order to not log twice, the entry needs
+ // to have a functional ExpandableNotificationRow that can keep track of whether it's
+ // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+ hum.onEntryAdded(entryToPin)
+
+ assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(
+ HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id,
+ ).isEqualTo(mUiEventLoggerFake.eventId(0))
+ }
+
+ @Test
+ fun testSetUserActionMayIndirectlyRemove() {
+ val hum = createHeadsUpManager()
+ val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+ hum.showNotification(notifEntry)
+
+ assertThat(hum.canRemoveImmediately(notifEntry.key)).isFalse()
+
+ hum.setUserActionMayIndirectlyRemove(notifEntry)
+
+ assertThat(hum.canRemoveImmediately(notifEntry.key)).isTrue()
+ }
+
+ companion object {
+ const val TEST_TOUCH_ACCEPTANCE_TIME: Int = 200
+ const val TEST_A11Y_AUTO_DISMISS_TIME: Int = 1000
+
+ const val TEST_MINIMUM_DISPLAY_TIME: Int = 400
+ const val TEST_AUTO_DISMISS_TIME: Int = 600
+ const val TEST_STICKY_AUTO_DISMISS_TIME: Int = 800
+
+ // Number of notifications to use in tests requiring multiple notifications
+ private const val TEST_NUM_NOTIFICATIONS = 4
+
+ init {
+ Truth.assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME)
+ Truth.assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME)
+ Truth.assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME)
+ }
+
+ @get:Parameters(name = "{0}")
+ @JvmStatic
+ val flags: List<FlagsParameterization>
+ get() = FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java
deleted file mode 100644
index 01f78cb..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
- * Copyright (C) 2019 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.headsup;
-
-import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
-
-import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-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.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.os.Handler;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.res.R;
-import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.kotlin.JavaAdapter;
-import com.android.systemui.util.settings.FakeGlobalSettings;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import kotlinx.coroutines.flow.StateFlowKt;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
-import java.util.List;
-
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(ParameterizedAndroidJunit4.class)
-// TODO(b/378142453): Merge this with HeadsUpManagerPhoneTest.
-public class HeadsUpManagerImplTest extends SysuiTestCase {
- protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
-
- @Rule
- public MockitoRule rule = MockitoJUnit.rule();
-
- static final int TEST_TOUCH_ACCEPTANCE_TIME = 200;
- static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000;
-
- private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
-
- private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
- @Mock private Handler mBgHandler;
- @Mock private DumpManager dumpManager;
- @Mock private ShadeInteractor mShadeInteractor;
- private AvalancheController mAvalancheController;
-
- @Mock private AccessibilityManagerWrapper mAccessibilityMgr;
-
- protected static final int TEST_MINIMUM_DISPLAY_TIME = 400;
- protected static final int TEST_AUTO_DISMISS_TIME = 600;
- protected static final int TEST_STICKY_AUTO_DISMISS_TIME = 800;
- // Number of notifications to use in tests requiring multiple notifications
- private static final int TEST_NUM_NOTIFICATIONS = 4;
-
- protected final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
- protected final FakeSystemClock mSystemClock = new FakeSystemClock();
- protected final FakeExecutor mExecutor = new FakeExecutor(mSystemClock);
-
- @Mock protected ExpandableNotificationRow mRow;
-
- static {
- assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
- assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
- assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME);
- }
-
- private HeadsUpManagerImpl createHeadsUpManager() {
- return new TestableHeadsUpManager(
- mContext,
- mLogger,
- mKosmos.getStatusBarStateController(),
- mKosmos.getKeyguardBypassController(),
- new GroupMembershipManagerImpl(),
- mKosmos.getVisualStabilityProvider(),
- mKosmos.getConfigurationController(),
- mExecutor,
- mGlobalSettings,
- mSystemClock,
- mAccessibilityMgr,
- mUiEventLoggerFake,
- new JavaAdapter(mKosmos.getTestScope()),
- mShadeInteractor,
- mAvalancheController);
- }
-
- private NotificationEntry createStickyEntry(int id) {
- final Notification notif = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFullScreenIntent(mock(PendingIntent.class), /* highPriority */ true)
- .build();
- return HeadsUpManagerTestUtil.createEntry(id, notif);
- }
-
- private NotificationEntry createStickyForSomeTimeEntry(int id) {
- final Notification notif = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFlag(FLAG_FSI_REQUESTED_BUT_DENIED, true)
- .build();
- return HeadsUpManagerTestUtil.createEntry(id, notif);
- }
-
- private void useAccessibilityTimeout(boolean use) {
- if (use) {
- doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
- .getRecommendedTimeoutMillis(anyInt(), anyInt());
- } else {
- when(mAccessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt())).then(
- i -> i.getArgument(0));
- }
- }
-
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getFlags() {
- return FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME);
- }
-
- public HeadsUpManagerImplTest(FlagsParameterization flags) {
- mSetFlagsRule.setFlagsParameterization(flags);
- }
-
- @Override
- public void SysuiSetup() throws Exception {
- super.SysuiSetup();
- mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake, mLogger,
- mBgHandler);
- when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(true));
- when(mKosmos.getKeyguardBypassController().getBypassEnabled()).thenReturn(false);
- }
-
- @Test
- public void testHasNotifications_headsUpManagerMapNotEmpty_true() {
- final HeadsUpManagerImpl bhum = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
- bhum.showNotification(entry);
-
- assertThat(bhum.mHeadsUpEntryMap).isNotEmpty();
- assertThat(bhum.hasNotifications()).isTrue();
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- public void testHasNotifications_avalancheMapNotEmpty_true() {
- final HeadsUpManagerImpl bhum = createHeadsUpManager();
- final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
- mContext);
- final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
- mAvalancheController.addToNext(headsUpEntry, () -> {});
-
- assertThat(mAvalancheController.getWaitingEntryList()).isNotEmpty();
- assertThat(bhum.hasNotifications()).isTrue();
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- public void testHasNotifications_false() {
- final HeadsUpManagerImpl bhum = createHeadsUpManager();
- assertThat(bhum.mHeadsUpEntryMap).isEmpty();
- assertThat(mAvalancheController.getWaitingEntryList()).isEmpty();
- assertThat(bhum.hasNotifications()).isFalse();
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- public void testGetHeadsUpEntryList_includesAvalancheEntryList() {
- final HeadsUpManagerImpl bhum = createHeadsUpManager();
- final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
- mContext);
- final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
- mAvalancheController.addToNext(headsUpEntry, () -> {});
-
- assertThat(bhum.getHeadsUpEntryList()).contains(headsUpEntry);
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- public void testGetHeadsUpEntry_returnsAvalancheEntry() {
- final HeadsUpManagerImpl bhum = createHeadsUpManager();
- final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
- mContext);
- final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
- mAvalancheController.addToNext(headsUpEntry, () -> {});
-
- assertThat(bhum.getHeadsUpEntry(notifEntry.getKey())).isEqualTo(headsUpEntry);
- }
-
- @Test
- public void testShowNotification_addsEntry() {
- final HeadsUpManagerImpl alm = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
- alm.showNotification(entry);
-
- assertTrue(alm.isHeadsUpEntry(entry.getKey()));
- assertTrue(alm.hasNotifications());
- assertEquals(entry, alm.getEntry(entry.getKey()));
- }
-
- @Test
- public void testShowNotification_autoDismisses() {
- final HeadsUpManagerImpl alm = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
- alm.showNotification(entry);
- mSystemClock.advanceTime(TEST_AUTO_DISMISS_TIME * 3 / 2);
-
- assertFalse(alm.isHeadsUpEntry(entry.getKey()));
- }
-
- @Test
- public void testRemoveNotification_removeDeferred() {
- final HeadsUpManagerImpl alm = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
- alm.showNotification(entry);
-
- final boolean removedImmediately = alm.removeNotification(
- entry.getKey(), /* releaseImmediately = */ false, "removeDeferred");
- assertFalse(removedImmediately);
- assertTrue(alm.isHeadsUpEntry(entry.getKey()));
- }
-
- @Test
- public void testRemoveNotification_forceRemove() {
- final HeadsUpManagerImpl alm = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
- alm.showNotification(entry);
-
- final boolean removedImmediately = alm.removeNotification(
- entry.getKey(), /* releaseImmediately = */ true, "forceRemove");
- assertTrue(removedImmediately);
- assertFalse(alm.isHeadsUpEntry(entry.getKey()));
- }
-
- @Test
- public void testReleaseAllImmediately() {
- final HeadsUpManagerImpl alm = createHeadsUpManager();
- for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(i, mContext);
- entry.setRow(mRow);
- alm.showNotification(entry);
- }
-
- alm.releaseAllImmediately();
-
- assertEquals(0, alm.getAllEntries().count());
- }
-
- @Test
- public void testCanRemoveImmediately_notShownLongEnough() {
- final HeadsUpManagerImpl alm = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
- alm.showNotification(entry);
-
- // The entry has just been added so we should not remove immediately.
- assertFalse(alm.canRemoveImmediately(entry.getKey()));
- }
-
- @Test
- public void testHunRemovedLogging() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
- mContext);
- final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = mock(
- HeadsUpManagerImpl.HeadsUpEntry.class);
- when(headsUpEntry.getPinnedStatus())
- .thenReturn(StateFlowKt.MutableStateFlow(PinnedStatus.NotPinned));
- headsUpEntry.mEntry = notifEntry;
-
- hum.onEntryRemoved(headsUpEntry, "test");
-
- verify(mLogger, times(1)).logNotificationActuallyRemoved(eq(notifEntry));
- }
-
-
- @Test
- public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
- useAccessibilityTimeout(false);
-
- hum.showNotification(entry);
- mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME);
-
- assertTrue(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testShowNotification_autoDismissesWithDefaultTimeout() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
- useAccessibilityTimeout(false);
-
- hum.showNotification(entry);
- mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
- + (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
-
- assertFalse(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
- useAccessibilityTimeout(false);
-
- hum.showNotification(entry);
- mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
- + (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2);
-
- assertTrue(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testShowNotification_sticky_neverAutoDismisses() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = createStickyEntry(/* id = */ 0);
- useAccessibilityTimeout(false);
-
- hum.showNotification(entry);
- mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME);
-
- assertTrue(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
- useAccessibilityTimeout(true);
-
- hum.showNotification(entry);
- mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
- + (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
-
- assertTrue(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
- useAccessibilityTimeout(true);
-
- hum.showNotification(entry);
- mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
- + (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
-
- assertTrue(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testRemoveNotification_beforeMinimumDisplayTime() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
- useAccessibilityTimeout(false);
-
- hum.showNotification(entry);
-
- final boolean removedImmediately = hum.removeNotification(
- entry.getKey(), /* releaseImmediately = */ false, "beforeMinimumDisplayTime");
- assertFalse(removedImmediately);
- assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-
- mSystemClock.advanceTime((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2);
-
- assertFalse(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testRemoveNotification_afterMinimumDisplayTime() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
- useAccessibilityTimeout(false);
-
- hum.showNotification(entry);
- mSystemClock.advanceTime((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2);
-
- assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-
- final boolean removedImmediately = hum.removeNotification(
- entry.getKey(), /* releaseImmediately = */ false, "afterMinimumDisplayTime");
- assertTrue(removedImmediately);
- assertFalse(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testRemoveNotification_releaseImmediately() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
- hum.showNotification(entry);
-
- final boolean removedImmediately = hum.removeNotification(
- entry.getKey(), /* releaseImmediately = */ true, "afterMinimumDisplayTime");
- assertTrue(removedImmediately);
- assertFalse(hum.isHeadsUpEntry(entry.getKey()));
- }
-
-
- @Test
- public void testIsSticky_rowPinnedAndExpanded_true() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
- mContext);
- when(mRow.isPinned()).thenReturn(true);
- notifEntry.setRow(mRow);
-
- hum.showNotification(notifEntry);
-
- final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
- notifEntry.getKey());
- headsUpEntry.setExpanded(true);
-
- assertTrue(hum.isSticky(notifEntry.getKey()));
- }
-
- @Test
- public void testIsSticky_remoteInputActive_true() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
- mContext);
-
- hum.showNotification(notifEntry);
-
- final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
- notifEntry.getKey());
- headsUpEntry.mRemoteInputActive = true;
-
- assertTrue(hum.isSticky(notifEntry.getKey()));
- }
-
- @Test
- public void testIsSticky_hasFullScreenIntent_true() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry notifEntry =
- HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
-
- hum.showNotification(notifEntry);
-
- assertTrue(hum.isSticky(notifEntry.getKey()));
- }
-
-
- @Test
- public void testIsSticky_stickyForSomeTime_false() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
-
- hum.showNotification(entry);
-
- assertFalse(hum.isSticky(entry.getKey()));
- }
-
-
- @Test
- public void testIsSticky_false() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
- mContext);
-
- hum.showNotification(notifEntry);
-
- final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
- notifEntry.getKey());
- headsUpEntry.setExpanded(false);
- headsUpEntry.mRemoteInputActive = false;
-
- assertFalse(hum.isSticky(notifEntry.getKey()));
- }
-
- @Test
- public void testCompareTo_withNullEntries() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build();
-
- hum.showNotification(alertEntry);
-
- assertThat(hum.compare(alertEntry, null)).isLessThan(0);
- assertThat(hum.compare(null, alertEntry)).isGreaterThan(0);
- assertThat(hum.compare(null, null)).isEqualTo(0);
- }
-
- @Test
- public void testCompareTo_withNonAlertEntries() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
-
- final NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag(
- "nae1").build();
- final NotificationEntry nonAlertEntry2 = new NotificationEntryBuilder().setTag(
- "nae2").build();
- final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build();
- hum.showNotification(alertEntry);
-
- assertThat(hum.compare(alertEntry, nonAlertEntry1)).isLessThan(0);
- assertThat(hum.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0);
- assertThat(hum.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0);
- }
-
- @Test
- public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
-
- final HeadsUpManagerImpl.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry(
- new NotificationEntryBuilder()
- .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0,
- new Notification.Builder(mContext, "")
- .setCategory(Notification.CATEGORY_CALL)
- .setOngoing(true)))
- .build());
-
- final HeadsUpManagerImpl.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(
- HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext));
- activeRemoteInput.mRemoteInputActive = true;
-
- assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
- assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0);
- }
-
- @Test
- public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
-
- final Person person = new Person.Builder().setName("person").build();
- final PendingIntent intent = mock(PendingIntent.class);
- final HeadsUpManagerImpl.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 HeadsUpManagerImpl.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(
- HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext));
- activeRemoteInput.mRemoteInputActive = true;
-
- assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0);
- assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0);
- }
-
- @Test
- @EnableFlags(NotificationThrottleHun.FLAG_NAME)
- public void testPinEntry_logsPeek_throttleEnabled() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
-
- // Needs full screen intent in order to be pinned
- final HeadsUpManagerImpl.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(
- 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
- // the notification and then updates it; in order to not log twice, the entry needs
- // to have a functional ExpandableNotificationRow that can keep track of whether it's
- // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
- hum.onEntryAdded(entryToPin);
-
- assertEquals(2, mUiEventLoggerFake.numLogs());
- assertEquals(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(),
- mUiEventLoggerFake.eventId(0));
- assertEquals(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
- mUiEventLoggerFake.eventId(1));
- }
-
- @Test
- @DisableFlags(NotificationThrottleHun.FLAG_NAME)
- public void testPinEntry_logsPeek_throttleDisabled() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
-
- // Needs full screen intent in order to be pinned
- final HeadsUpManagerImpl.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(
- 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
- // the notification and then updates it; in order to not log twice, the entry needs
- // to have a functional ExpandableNotificationRow that can keep track of whether it's
- // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
- hum.onEntryAdded(entryToPin);
-
- assertEquals(1, mUiEventLoggerFake.numLogs());
- assertEquals(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
- mUiEventLoggerFake.eventId(0));
- }
-
- @Test
- public void testSetUserActionMayIndirectlyRemove() {
- final HeadsUpManagerImpl hum = createHeadsUpManager();
- final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
- mContext);
-
- hum.showNotification(notifEntry);
-
- assertFalse(hum.canRemoveImmediately(notifEntry.getKey()));
-
- hum.setUserActionMayIndirectlyRemove(notifEntry);
-
- assertTrue(hum.canRemoveImmediately(notifEntry.getKey()));
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
similarity index 98%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
index 35d8253..65d282f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
@@ -59,7 +59,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper
-class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : HeadsUpManagerImplTest(flags) {
+class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplOldTest(flags) {
private val mHeadsUpManagerLogger = HeadsUpManagerLogger(logcatLogBuffer())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java
index 2b077ed..7ded1a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java
@@ -78,10 +78,10 @@
shadeInteractor,
avalancheController);
- mTouchAcceptanceDelay = HeadsUpManagerImplTest.TEST_TOUCH_ACCEPTANCE_TIME;
- mMinimumDisplayTime = HeadsUpManagerImplTest.TEST_MINIMUM_DISPLAY_TIME;
- mAutoDismissTime = HeadsUpManagerImplTest.TEST_AUTO_DISMISS_TIME;
- mStickyForSomeTimeAutoDismissTime = HeadsUpManagerImplTest.TEST_STICKY_AUTO_DISMISS_TIME;
+ mTouchAcceptanceDelay = HeadsUpManagerImplOldTest.TEST_TOUCH_ACCEPTANCE_TIME;
+ mMinimumDisplayTime = HeadsUpManagerImplOldTest.TEST_MINIMUM_DISPLAY_TIME;
+ mAutoDismissTime = HeadsUpManagerImplOldTest.TEST_AUTO_DISMISS_TIME;
+ mStickyForSomeTimeAutoDismissTime = HeadsUpManagerImplOldTest.TEST_STICKY_AUTO_DISMISS_TIME;
}
@NonNull
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt
new file mode 100644
index 0000000..8acf538
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.volume.dialog.domain.interactor
+
+import android.app.ActivityManager
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogSafetyWarningInteractorTest : SysuiTestCase() {
+
+ private val kosmos: Kosmos = testKosmos()
+
+ private lateinit var underTest: VolumeDialogSafetyWarningInteractor
+
+ @Before
+ fun setup() {
+ kosmos.useUnconfinedTestDispatcher()
+ underTest = kosmos.volumeDialogSafetyWarningInteractor
+ }
+
+ @Test
+ fun dismiss_isShowingSafetyWarning_isFalse() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+ underTest.onSafetyWarningDismissed()
+
+ assertThat(isShowingSafetyWarning).isFalse()
+ }
+ }
+
+ @Test
+ fun flagShowUi_isShowingSafetyWarning_isTrue() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+ fakeVolumeDialogController.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI)
+
+ assertThat(isShowingSafetyWarning).isTrue()
+ }
+ }
+
+ @Test
+ fun flagShowUiWarnings_isShowingSafetyWarning_isTrue() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+ fakeVolumeDialogController.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI_WARNINGS)
+
+ assertThat(isShowingSafetyWarning).isTrue()
+ }
+ }
+
+ @Test
+ fun invisibleAndNoFlags_isShowingSafetyWarning_isFalse() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+ volumeDialogVisibilityRepository.updateVisibility {
+ VolumeDialogVisibilityModel.Invisible
+ }
+
+ fakeVolumeDialogController.onShowSafetyWarning(0)
+
+ assertThat(isShowingSafetyWarning).isFalse()
+ }
+ }
+
+ @Test
+ fun visibleAndNoFlags_isShowingSafetyWarning_isTrue() =
+ with(kosmos) {
+ runTest {
+ val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+ volumeDialogVisibilityRepository.updateVisibility {
+ VolumeDialogVisibilityModel.Visible(
+ Events.SHOW_REASON_VOLUME_CHANGED,
+ false,
+ ActivityManager.LOCK_TASK_MODE_LOCKED,
+ )
+ }
+
+ fakeVolumeDialogController.onShowSafetyWarning(0)
+
+ assertThat(isShowingSafetyWarning).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
index bba0050..a42ae03 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
@@ -18,6 +18,7 @@
import android.content.res.Resources
import com.android.internal.R
+import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -36,7 +37,7 @@
constructor(
@Application private val applicationScope: CoroutineScope,
@Main private val resources: Resources,
- configurationRepository: ConfigurationRepository,
+ @GlobalConfig configurationRepository: ConfigurationRepository,
) {
/**
* Whether to enable emergency services calls while the SIM card is locked. This is disabled in
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 61cd7c7..641400a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -42,6 +42,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -71,7 +72,7 @@
private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor,
private val falsingCollector: FalsingCollector,
private val dismissCallbackRegistry: DismissCallbackRegistry,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val trustRepository: TrustRepository,
@Application private val applicationScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
index 1aaf4fb..ec9ee91 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
@@ -37,6 +37,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
@@ -62,7 +63,7 @@
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val repository: SimBouncerRepository,
private val telephonyManager: TelephonyManager,
- @Main private val resources: Resources,
+ @ShadeDisplayAware private val resources: Resources,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val euiccManager: EuiccManager?,
// TODO(b/307977401): Replace this with `MobileConnectionsInteractor` when available.
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
index 38fc2a8..84a423e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
@@ -21,11 +21,12 @@
import android.view.WindowManager
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.Utils
+import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
-import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxConfig
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -37,9 +38,9 @@
@Inject
constructor(
private val windowManager: WindowManager,
- private val context: Context,
+ @Application private val context: Context,
keyboardDockingIndicationInteractor: KeyboardDockingIndicationInteractor,
- @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
+ @GlobalConfig configurationInteractor: ConfigurationInteractor,
@Background private val backgroundScope: CoroutineScope,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index 094ec39..203a157 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -16,19 +16,31 @@
package com.android.systemui.volume.dialog
+import android.content.Context
+import android.media.AudioManager
import com.android.app.tracing.coroutines.coroutineScopeTraced
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.VolumeDialog
+import com.android.systemui.volume.SafetyWarningDialog
import com.android.systemui.volume.dialog.dagger.VolumeDialogPluginComponent
+import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogPluginViewModel
import javax.inject.Inject
+import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.suspendCancellableCoroutine
+@OptIn(ExperimentalCoroutinesApi::class)
class VolumeDialogPlugin
@Inject
constructor(
@Application private val applicationCoroutineScope: CoroutineScope,
+ private val context: Context,
+ private val audioManager: AudioManager,
private val volumeDialogPluginComponentFactory: VolumeDialogPluginComponent.Factory,
) : VolumeDialog {
@@ -41,14 +53,39 @@
coroutineScopeTraced("[Volume]plugin") {
pluginComponent =
volumeDialogPluginComponentFactory.create(this).also {
- it.viewModel().launchVolumeDialog()
+ bindPlugin(it.viewModel())
}
}
}
}
+ private fun CoroutineScope.bindPlugin(viewModel: VolumeDialogPluginViewModel) {
+ viewModel.launchVolumeDialog()
+
+ viewModel.isShowingSafetyWarning
+ .mapLatest { isShowingSafetyWarning ->
+ if (isShowingSafetyWarning) {
+ showSafetyWarningVisibility { viewModel.onSafetyWarningDismissed() }
+ }
+ }
+ .launchIn(this)
+ }
+
override fun destroy() {
job?.cancel()
pluginComponent = null
}
+
+ private suspend fun showSafetyWarningVisibility(onDismissed: () -> Unit) =
+ suspendCancellableCoroutine { continuation ->
+ val dialog =
+ object : SafetyWarningDialog(context, audioManager) {
+ override fun cleanUp() {
+ onDismissed()
+ continuation.resume(Unit)
+ }
+ }
+ dialog.show()
+ continuation.invokeOnCancellation { dialog.dismiss() }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt
new file mode 100644
index 0000000..f707d67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.volume.dialog.domain.interactor
+
+import android.media.AudioManager
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogSafetyWarningModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+
+private const val VISIBLE_FLAGS = AudioManager.FLAG_SHOW_UI or AudioManager.FLAG_SHOW_UI_WARNINGS
+
+@VolumeDialogPluginScope
+class VolumeDialogSafetyWarningInteractor
+@Inject
+constructor(
+ private val stateInteractor: VolumeDialogStateInteractor,
+ visibilityInteractor: VolumeDialogVisibilityInteractor,
+) {
+
+ val isShowingSafetyWarning: Flow<Boolean> =
+ stateInteractor.volumeDialogState.map {
+ when (it.isShowingSafetyWarning) {
+ is VolumeDialogSafetyWarningModel.Visible ->
+ if (it.isShowingSafetyWarning.flags and VISIBLE_FLAGS == 0) {
+ visibilityInteractor.dialogVisibility.first() is
+ VolumeDialogVisibilityModel.Visible
+ } else {
+ true
+ }
+ is VolumeDialogSafetyWarningModel.Invisible -> false
+ }
+ }
+
+ fun onSafetyWarningDismissed() {
+ stateInteractor.setSafetyWarning(VolumeDialogSafetyWarningModel.Invisible)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
index 5c7289b..51e7924 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
import com.android.systemui.volume.dialog.data.repository.VolumeDialogStateRepository
import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogSafetyWarningModel
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import javax.inject.Inject
@@ -61,6 +62,9 @@
oldState.copy(shouldShowA11ySlider = event.showA11yStream)
}
}
+ is VolumeDialogEventModel.ShowSafetyWarning -> {
+ setSafetyWarning(VolumeDialogSafetyWarningModel.Visible(event.flags))
+ }
else -> {
// do nothing
}
@@ -72,6 +76,10 @@
val volumeDialogState: Flow<VolumeDialogStateModel> = volumeDialogStateRepository.state
+ fun setSafetyWarning(model: VolumeDialogSafetyWarningModel) {
+ volumeDialogStateRepository.updateState { it.copy(isShowingSafetyWarning = model) }
+ }
+
/** Returns a copy of [model] filled with the values from [VolumeDialogController.State]. */
private fun VolumeDialogController.State.copyIntoModel(
model: VolumeDialogStateModel
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt
new file mode 100644
index 0000000..39fc222
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.volume.dialog.shared.model
+
+/** Models current Volume Safety Dialog state. */
+sealed interface VolumeDialogSafetyWarningModel {
+
+ /** Volume Safety Dialog is visible and has been shown with the [flags]. */
+ data class Visible(val flags: Int) : VolumeDialogSafetyWarningModel
+
+ /** Volume Safety Dialog is invisible. */
+ data object Invisible : VolumeDialogSafetyWarningModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
index 1792b99..838006d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
@@ -21,6 +21,8 @@
/** Models a state of the Volume Dialog. */
data class VolumeDialogStateModel(
val shouldShowA11ySlider: Boolean = false,
+ val isShowingSafetyWarning: VolumeDialogSafetyWarningModel =
+ VolumeDialogSafetyWarningModel.Invisible,
val streamModels: Map<Int, VolumeDialogStreamModel> = mapOf(),
val ringerModeInternal: Int = 0,
val ringerModeExternal: Int = 0,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index ff525f46..9bab1b0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -18,14 +18,17 @@
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogSafetyWarningInteractor
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import javax.inject.Inject
import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
@@ -34,29 +37,35 @@
class VolumeDialogPluginViewModel
@Inject
constructor(
+ @VolumeDialogPlugin private val coroutineScope: CoroutineScope,
private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+ private val dialogSafetyWarningInteractor: VolumeDialogSafetyWarningInteractor,
private val volumeDialogProvider: Provider<VolumeDialog>,
private val logger: VolumeDialogLogger,
) {
- suspend fun launchVolumeDialog() {
- coroutineScope {
- dialogVisibilityInteractor.dialogVisibility
- .mapLatest { visibilityModel ->
- with(visibilityModel) {
- if (this is VolumeDialogVisibilityModel.Visible) {
- showDialog()
- Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
- logger.onShow(reason)
- }
- if (this is VolumeDialogVisibilityModel.Dismissed) {
- Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
- logger.onDismiss(reason)
- }
+ fun launchVolumeDialog() {
+ dialogVisibilityInteractor.dialogVisibility
+ .mapLatest { visibilityModel ->
+ with(visibilityModel) {
+ if (this is VolumeDialogVisibilityModel.Visible) {
+ showDialog()
+ Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
+ logger.onShow(reason)
+ }
+ if (this is VolumeDialogVisibilityModel.Dismissed) {
+ Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
+ logger.onDismiss(reason)
}
}
- .launchIn(this)
- }
+ }
+ .launchIn(coroutineScope)
+ }
+
+ val isShowingSafetyWarning: Flow<Boolean> = dialogSafetyWarningInteractor.isShowingSafetyWarning
+
+ fun onSafetyWarningDismissed() {
+ dialogSafetyWarningInteractor.onSafetyWarningDismissed()
}
private fun showDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index 7a6ede4..b20dffb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -44,9 +44,9 @@
@Inject
constructor(
private val context: Context,
- dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+ private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
- volumeDialogStateInteractor: VolumeDialogStateInteractor,
+ private val volumeDialogStateInteractor: VolumeDialogStateInteractor,
devicePostureController: DevicePostureController,
configurationController: ConfigurationController,
) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt
new file mode 100644
index 0000000..ddd0014
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.volume.dialog.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.volumeDialogSafetyWarningInteractor: VolumeDialogSafetyWarningInteractor by
+ Kosmos.Fixture {
+ VolumeDialogSafetyWarningInteractor(
+ volumeDialogStateInteractor,
+ volumeDialogVisibilityInteractor,
+ )
+ }