1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar; 18 19 import static junit.framework.Assert.assertEquals; 20 import static junit.framework.Assert.assertFalse; 21 import static junit.framework.Assert.assertTrue; 22 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.verify; 25 import static org.mockito.Mockito.when; 26 27 import android.app.Notification; 28 import android.content.Context; 29 import android.os.Handler; 30 import android.os.Looper; 31 import android.os.SystemClock; 32 import android.os.UserHandle; 33 import android.service.notification.NotificationListenerService; 34 import android.testing.AndroidTestingRunner; 35 import android.testing.TestableLooper; 36 37 import androidx.annotation.NonNull; 38 import androidx.test.filters.SmallTest; 39 40 import com.android.systemui.SysuiTestCase; 41 import com.android.systemui.dump.DumpManager; 42 import com.android.systemui.flags.FeatureFlags; 43 import com.android.systemui.plugins.statusbar.StatusBarStateController; 44 import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.RemoteInputActiveExtender; 45 import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.RemoteInputHistoryExtender; 46 import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.SmartReplyHistoryExtender; 47 import com.android.systemui.statusbar.notification.NotificationEntryManager; 48 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 49 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; 50 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 51 import com.android.systemui.statusbar.phone.StatusBar; 52 import com.android.systemui.statusbar.policy.RemoteInputUriController; 53 54 import com.google.android.collect.Sets; 55 56 import org.junit.Before; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.mockito.Mock; 60 import org.mockito.MockitoAnnotations; 61 62 import java.util.Optional; 63 64 import dagger.Lazy; 65 66 @SmallTest 67 @RunWith(AndroidTestingRunner.class) 68 @TestableLooper.RunWithLooper 69 public class NotificationRemoteInputManagerTest extends SysuiTestCase { 70 private static final String TEST_PACKAGE_NAME = "test"; 71 private static final int TEST_UID = 0; 72 73 @Mock private NotificationPresenter mPresenter; 74 @Mock private RemoteInputController.Delegate mDelegate; 75 @Mock private NotificationRemoteInputManager.Callback mCallback; 76 @Mock private RemoteInputController mController; 77 @Mock private SmartReplyController mSmartReplyController; 78 @Mock private NotificationListenerService.RankingMap mRanking; 79 @Mock private ExpandableNotificationRow mRow; 80 @Mock private StatusBarStateController mStateController; 81 @Mock private RemoteInputUriController mRemoteInputUriController; 82 @Mock private NotificationClickNotifier mClickNotifier; 83 84 // Dependency mocks: 85 @Mock private NotificationEntryManager mEntryManager; 86 @Mock private NotificationLockscreenUserManager mLockscreenUserManager; 87 88 private TestableNotificationRemoteInputManager mRemoteInputManager; 89 private NotificationEntry mEntry; 90 private RemoteInputHistoryExtender mRemoteInputHistoryExtender; 91 private SmartReplyHistoryExtender mSmartReplyHistoryExtender; 92 private RemoteInputActiveExtender mRemoteInputActiveExtender; 93 private TestableNotificationRemoteInputManager.FakeLegacyRemoteInputLifetimeExtender 94 mLegacyRemoteInputLifetimeExtender; 95 96 @Before setUp()97 public void setUp() { 98 MockitoAnnotations.initMocks(this); 99 100 mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext, 101 mock(FeatureFlags.class), 102 mLockscreenUserManager, 103 mSmartReplyController, 104 mEntryManager, 105 mock(RemoteInputNotificationRebuilder.class), 106 () -> Optional.of(mock(StatusBar.class)), 107 mStateController, 108 Handler.createAsync(Looper.myLooper()), 109 mRemoteInputUriController, 110 mClickNotifier, 111 mock(ActionClickLogger.class), 112 mock(DumpManager.class)); 113 mEntry = new NotificationEntryBuilder() 114 .setPkg(TEST_PACKAGE_NAME) 115 .setOpPkg(TEST_PACKAGE_NAME) 116 .setUid(TEST_UID) 117 .setNotification(new Notification()) 118 .setUser(UserHandle.CURRENT) 119 .build(); 120 mEntry.setRow(mRow); 121 122 mRemoteInputManager.setUpWithPresenterForTest(mCallback, 123 mDelegate, mController); 124 for (NotificationLifetimeExtender extender : mRemoteInputManager.getLifetimeExtenders()) { 125 extender.setCallback( 126 mock(NotificationLifetimeExtender.NotificationSafeToRemoveCallback.class)); 127 } 128 } 129 130 @Test testPerformOnRemoveNotification()131 public void testPerformOnRemoveNotification() { 132 when(mController.isRemoteInputActive(mEntry)).thenReturn(true); 133 mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.getKey()); 134 135 assertFalse(mEntry.mRemoteEditImeVisible); 136 verify(mController).removeRemoteInput(mEntry, null); 137 } 138 139 @Test testShouldExtendLifetime_remoteInputActive()140 public void testShouldExtendLifetime_remoteInputActive() { 141 when(mController.isRemoteInputActive(mEntry)).thenReturn(true); 142 143 assertTrue(mRemoteInputManager.isRemoteInputActive(mEntry)); 144 assertTrue(mRemoteInputActiveExtender.shouldExtendLifetime(mEntry)); 145 } 146 147 @Test testShouldExtendLifetime_isSpinning()148 public void testShouldExtendLifetime_isSpinning() { 149 NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY = true; 150 when(mController.isSpinning(mEntry.getKey())).thenReturn(true); 151 152 assertTrue(mRemoteInputManager.shouldKeepForRemoteInputHistory(mEntry)); 153 assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry)); 154 } 155 156 @Test testShouldExtendLifetime_recentRemoteInput()157 public void testShouldExtendLifetime_recentRemoteInput() { 158 NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY = true; 159 mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime(); 160 161 assertTrue(mRemoteInputManager.shouldKeepForRemoteInputHistory(mEntry)); 162 assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry)); 163 } 164 165 @Test testShouldExtendLifetime_smartReplySending()166 public void testShouldExtendLifetime_smartReplySending() { 167 NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY = true; 168 when(mSmartReplyController.isSendingSmartReply(mEntry.getKey())).thenReturn(true); 169 170 assertTrue(mRemoteInputManager.shouldKeepForSmartReplyHistory(mEntry)); 171 assertTrue(mSmartReplyHistoryExtender.shouldExtendLifetime(mEntry)); 172 } 173 174 @Test testNotificationWithRemoteInputActiveIsRemovedOnCollapse()175 public void testNotificationWithRemoteInputActiveIsRemovedOnCollapse() { 176 mRemoteInputActiveExtender.setShouldManageLifetime(mEntry, true /* shouldManage */); 177 178 assertEquals(mLegacyRemoteInputLifetimeExtender.getEntriesKeptForRemoteInputActive(), 179 Sets.newArraySet(mEntry)); 180 181 mRemoteInputManager.onPanelCollapsed(); 182 183 assertTrue( 184 mLegacyRemoteInputLifetimeExtender.getEntriesKeptForRemoteInputActive().isEmpty()); 185 } 186 187 private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager { 188 TestableNotificationRemoteInputManager( Context context, FeatureFlags featureFlags, NotificationLockscreenUserManager lockscreenUserManager, SmartReplyController smartReplyController, NotificationEntryManager notificationEntryManager, RemoteInputNotificationRebuilder rebuilder, Lazy<Optional<StatusBar>> statusBarOptionalLazy, StatusBarStateController statusBarStateController, Handler mainHandler, RemoteInputUriController remoteInputUriController, NotificationClickNotifier clickNotifier, ActionClickLogger actionClickLogger, DumpManager dumpManager)189 TestableNotificationRemoteInputManager( 190 Context context, 191 FeatureFlags featureFlags, 192 NotificationLockscreenUserManager lockscreenUserManager, 193 SmartReplyController smartReplyController, 194 NotificationEntryManager notificationEntryManager, 195 RemoteInputNotificationRebuilder rebuilder, 196 Lazy<Optional<StatusBar>> statusBarOptionalLazy, 197 StatusBarStateController statusBarStateController, 198 Handler mainHandler, 199 RemoteInputUriController remoteInputUriController, 200 NotificationClickNotifier clickNotifier, 201 ActionClickLogger actionClickLogger, 202 DumpManager dumpManager) { 203 super( 204 context, 205 featureFlags, 206 lockscreenUserManager, 207 smartReplyController, 208 notificationEntryManager, 209 rebuilder, 210 statusBarOptionalLazy, 211 statusBarStateController, 212 mainHandler, 213 remoteInputUriController, 214 clickNotifier, 215 actionClickLogger, 216 dumpManager); 217 } 218 setUpWithPresenterForTest(Callback callback, RemoteInputController.Delegate delegate, RemoteInputController controller)219 public void setUpWithPresenterForTest(Callback callback, 220 RemoteInputController.Delegate delegate, 221 RemoteInputController controller) { 222 super.setUpWithCallback(callback, delegate); 223 mRemoteInputController = controller; 224 } 225 226 @NonNull 227 @Override createLegacyRemoteInputLifetimeExtender( Handler mainHandler, NotificationEntryManager notificationEntryManager, SmartReplyController smartReplyController)228 protected LegacyRemoteInputLifetimeExtender createLegacyRemoteInputLifetimeExtender( 229 Handler mainHandler, 230 NotificationEntryManager notificationEntryManager, 231 SmartReplyController smartReplyController) { 232 mLegacyRemoteInputLifetimeExtender = new FakeLegacyRemoteInputLifetimeExtender(); 233 return mLegacyRemoteInputLifetimeExtender; 234 } 235 236 class FakeLegacyRemoteInputLifetimeExtender extends LegacyRemoteInputLifetimeExtender { 237 238 @Override addLifetimeExtenders()239 protected void addLifetimeExtenders() { 240 mRemoteInputActiveExtender = new RemoteInputActiveExtender(); 241 mRemoteInputHistoryExtender = new RemoteInputHistoryExtender(); 242 mSmartReplyHistoryExtender = new SmartReplyHistoryExtender(); 243 mLifetimeExtenders.add(mRemoteInputHistoryExtender); 244 mLifetimeExtenders.add(mSmartReplyHistoryExtender); 245 mLifetimeExtenders.add(mRemoteInputActiveExtender); 246 } 247 } 248 249 } 250 } 251