Reinflate active notifications when SHOW_NOTIFICATION_SNOOZE setting changes.

Test: atest PreparationCoordinatorTest NotifUiAdjustmentProviderTest
Fixes: 185399616
Change-Id: I2cc194ad604953933fb14c52cecb6151a7deee11
This commit is contained in:
Jeff DeCew
2022-06-03 17:02:17 -04:00
parent 02d0ac1be7
commit e0cd5a8a30
4 changed files with 134 additions and 10 deletions

View File

@@ -31,6 +31,7 @@ class NotifUiAdjustment internal constructor(
val smartActions: List<Notification.Action>,
val smartReplies: List<CharSequence>,
val isConversation: Boolean,
val isSnoozeEnabled: Boolean,
val isMinimized: Boolean,
val needsRedaction: Boolean,
) {
@@ -42,6 +43,7 @@ class NotifUiAdjustment internal constructor(
): Boolean = when {
oldAdjustment === newAdjustment -> false
oldAdjustment.isConversation != newAdjustment.isConversation -> true
oldAdjustment.isSnoozeEnabled != newAdjustment.isSnoozeEnabled -> true
oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
oldAdjustment.needsRedaction != newAdjustment.needsRedaction -> true
areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
@@ -57,9 +59,9 @@ class NotifUiAdjustment internal constructor(
first.size != second.size -> true
else -> first.asSequence().zip(second.asSequence()).any {
(!TextUtils.equals(it.first.title, it.second.title)) ||
(areDifferent(it.first.getIcon(), it.second.getIcon())) ||
(it.first.actionIntent != it.second.actionIntent) ||
(areDifferent(it.first.remoteInputs, it.second.remoteInputs))
(areDifferent(it.first.getIcon(), it.second.getIcon())) ||
(it.first.actionIntent != it.second.actionIntent) ||
(areDifferent(it.first.remoteInputs, it.second.remoteInputs))
}
}
@@ -78,7 +80,7 @@ class NotifUiAdjustment internal constructor(
first.size != second.size -> true
else -> first.asSequence().zip(second.asSequence()).any {
(!TextUtils.equals(it.first.label, it.second.label)) ||
(areDifferent(it.first.choices, it.second.choices))
(areDifferent(it.first.choices, it.second.choices))
}
}
@@ -94,4 +96,4 @@ class NotifUiAdjustment internal constructor(
}
}
}
}
}

View File

@@ -16,12 +16,18 @@
package com.android.systemui.statusbar.notification.collection.inflation
import android.database.ContentObserver
import android.os.Handler
import android.os.UserHandle
import android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.util.ListenerSet
import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
/**
@@ -30,14 +36,23 @@ import javax.inject.Inject
*/
@SysUISingleton
class NotifUiAdjustmentProvider @Inject constructor(
@Main private val handler: Handler,
private val secureSettings: SecureSettings,
private val lockscreenUserManager: NotificationLockscreenUserManager,
private val sectionStyleProvider: SectionStyleProvider,
private val sectionStyleProvider: SectionStyleProvider
) {
private val dirtyListeners = ListenerSet<Runnable>()
private var isSnoozeEnabled = false
fun addDirtyListener(listener: Runnable) {
if (dirtyListeners.isEmpty()) {
lockscreenUserManager.addNotificationStateChangedListener(notifStateChangedListener)
updateSnoozeEnabled()
secureSettings.registerContentObserverForUser(
SHOW_NOTIFICATION_SNOOZE,
settingsObserver,
UserHandle.USER_ALL
)
}
dirtyListeners.addIfAbsent(listener)
}
@@ -46,6 +61,7 @@ class NotifUiAdjustmentProvider @Inject constructor(
dirtyListeners.remove(listener)
if (dirtyListeners.isEmpty()) {
lockscreenUserManager.removeNotificationStateChangedListener(notifStateChangedListener)
secureSettings.unregisterContentObserver(settingsObserver)
}
}
@@ -54,6 +70,17 @@ class NotifUiAdjustmentProvider @Inject constructor(
dirtyListeners.forEach(Runnable::run)
}
private val settingsObserver = object : ContentObserver(handler) {
override fun onChange(selfChange: Boolean) {
updateSnoozeEnabled()
dirtyListeners.forEach(Runnable::run)
}
}
private fun updateSnoozeEnabled() {
isSnoozeEnabled = secureSettings.getInt(SHOW_NOTIFICATION_SNOOZE, 0) == 1
}
private fun isEntryMinimized(entry: NotificationEntry): Boolean {
val section = entry.section ?: error("Entry must have a section to determine if minimized")
val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
@@ -73,7 +100,8 @@ class NotifUiAdjustmentProvider @Inject constructor(
smartActions = entry.ranking.smartActions,
smartReplies = entry.ranking.smartReplies,
isConversation = entry.ranking.isConversation,
isSnoozeEnabled = isSnoozeEnabled,
isMinimized = isEntryMinimized(entry),
needsRedaction = lockscreenUserManager.needsRedaction(entry),
)
}
}

View File

@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;
import android.os.Handler;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -59,6 +60,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -97,6 +99,8 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
@Mock private IStatusBarService mService;
@Mock private BindEventManagerImpl mBindEventManagerImpl;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private Handler mHandler;
@Mock private SecureSettings mSecureSettings;
@Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
private final SectionStyleProvider mSectionStyleProvider = new SectionStyleProvider();
@@ -110,8 +114,11 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mAdjustmentProvider =
new NotifUiAdjustmentProvider(mLockscreenUserManager, mSectionStyleProvider);
mAdjustmentProvider = new NotifUiAdjustmentProvider(
mHandler,
mSecureSettings,
mLockscreenUserManager,
mSectionStyleProvider);
mEntry = getNotificationEntryBuilder().setParent(ROOT_ENTRY).build();
mInflationError = new Exception(TEST_MESSAGE);
mErrorManager = new NotifInflationErrorManager();

View File

@@ -1,16 +1,47 @@
/*
* Copyright (C) 2020 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.collection.inflation
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -18,12 +49,39 @@ import org.mockito.Mockito.verify
class NotifUiAdjustmentProviderTest : SysuiTestCase() {
private val lockscreenUserManager: NotificationLockscreenUserManager = mock()
private val sectionStyleProvider: SectionStyleProvider = mock()
private val handler: Handler = mock()
private val secureSettings: SecureSettings = mock()
private val uri = FakeSettings().getUriFor(SHOW_NOTIFICATION_SNOOZE)
private val dirtyListener: Runnable = mock()
private val section = NotifSection(mock(), 0)
private val entry = NotificationEntryBuilder()
.setSection(section)
.setParent(GroupEntry.ROOT_ENTRY)
.build()
private lateinit var contentObserver: ContentObserver
private val adjustmentProvider = NotifUiAdjustmentProvider(
handler,
secureSettings,
lockscreenUserManager,
sectionStyleProvider,
)
@Before
fun setup() {
verifyNoMoreInteractions(secureSettings)
adjustmentProvider.addDirtyListener(dirtyListener)
verify(secureSettings).getInt(eq(SHOW_NOTIFICATION_SNOOZE), any())
contentObserver = withArgCaptor {
verify(secureSettings).registerContentObserverForUser(
eq(SHOW_NOTIFICATION_SNOOZE), capture(), any()
)
}
verifyNoMoreInteractions(secureSettings, dirtyListener)
}
@Test
fun notifLockscreenStateChangeWillNotifDirty() {
val dirtyListener = mock<Runnable>()
@@ -33,6 +91,35 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() {
verify(lockscreenUserManager).addNotificationStateChangedListener(capture())
}
notifLocksreenStateChangeListener.onNotificationStateChanged()
verify(dirtyListener).run();
verify(dirtyListener).run()
}
@Test
fun additionalAddDoesNotRegisterAgain() {
clearInvocations(secureSettings)
adjustmentProvider.addDirtyListener(mock())
verifyNoMoreInteractions(secureSettings)
}
@Test
fun onChangeWillQueryThenNotifyDirty() {
contentObserver.onChange(false, listOf(uri), 0, 0)
with(inOrder(secureSettings, dirtyListener)) {
verify(secureSettings).getInt(eq(SHOW_NOTIFICATION_SNOOZE), any())
verify(dirtyListener).run()
}
}
@Test
fun changingSnoozeChangesProvidedAdjustment() {
whenever(secureSettings.getInt(eq(SHOW_NOTIFICATION_SNOOZE), any())).thenReturn(0)
val original = adjustmentProvider.calculateAdjustment(entry)
assertThat(original.isSnoozeEnabled).isFalse()
whenever(secureSettings.getInt(eq(SHOW_NOTIFICATION_SNOOZE), any())).thenReturn(1)
contentObserver.onChange(false, listOf(uri), 0, 0)
val withSnoozing = adjustmentProvider.calculateAdjustment(entry)
assertThat(withSnoozing.isSnoozeEnabled).isTrue()
assertThat(withSnoozing).isNotEqualTo(original)
}
}