summaryrefslogtreecommitdiff
path: root/packages/SystemUI/tests
diff options
context:
space:
mode:
author Julia Reynolds <juliacr@google.com> 2020-09-16 16:32:48 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2020-09-16 16:32:48 +0000
commit125d1725cdf75dd3ae855ca6dfb194a074e2649e (patch)
treee4ad011545cb437945097f47d705a93904e92f6e /packages/SystemUI/tests
parentd0d1ca7ce89f967567c6dc022bdd2ab6f7008d2e (diff)
parent8ec5bcb3d0cd9efd27cda5e309755d8b9be2f57c (diff)
DO NOT MERGE Revert "Remove app ops indicators from notifications" am: 8ec5bcb3d0
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/12615554 Change-Id: Ib5170725586b432972ab4b29129c1cadb53d50e1
Diffstat (limited to 'packages/SystemUI/tests')
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java82
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java144
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java230
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java1
7 files changed, 537 insertions, 1 deletions
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index e967a5d607eb..60f0cd9da5f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -82,7 +82,8 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
- mFsc = new ForegroundServiceController(mAppOpsController, mMainHandler);
+ mFsc = new ForegroundServiceController(
+ mEntryManager, mAppOpsController, mMainHandler);
mListener = new ForegroundServiceNotificationListener(
mContext, mFsc, mEntryManager, mNotifPipeline,
mock(ForegroundServiceLifetimeExtender.class), mClock);
@@ -114,6 +115,85 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
}
@Test
+ public void testAppOps_appOpChangedBeforeNotificationExists() {
+ // GIVEN app op exists, but notification doesn't exist in NEM yet
+ NotificationEntry entry = createFgEntry();
+ mFsc.onAppOpChanged(
+ AppOpsManager.OP_CAMERA,
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
+ true);
+ assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+ // WHEN the notification is added
+ mEntryListener.onPendingEntryAdded(entry);
+
+ // THEN the app op is added to the entry
+ Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+ }
+
+ @Test
+ public void testAppOps_appOpAddedToForegroundNotif() {
+ // GIVEN a notification associated with a foreground service
+ NotificationEntry entry = addFgEntry();
+ when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
+
+ // WHEN we are notified of a new app op for this notification
+ mFsc.onAppOpChanged(
+ AppOpsManager.OP_CAMERA,
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
+ true);
+
+ // THEN the app op is added to the entry
+ Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+ // THEN notification views are updated since the notification is visible
+ verify(mEntryManager, times(1)).updateNotifications(anyString());
+ }
+
+ @Test
+ public void testAppOpsAlreadyAdded() {
+ // GIVEN a foreground service associated notification that already has the correct app op
+ NotificationEntry entry = addFgEntry();
+ entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA);
+ when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
+
+ // WHEN we are notified of the same app op for this notification
+ mFsc.onAppOpChanged(
+ AppOpsManager.OP_CAMERA,
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
+ true);
+
+ // THEN the app op still exists in the notification entry
+ Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+ // THEN notification views aren't updated since nothing changed
+ verify(mEntryManager, never()).updateNotifications(anyString());
+ }
+
+ @Test
+ public void testAppOps_appOpNotAddedToUnrelatedNotif() {
+ // GIVEN no notification entries correspond to the newly updated appOp
+ NotificationEntry entry = addFgEntry();
+ when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null);
+
+ // WHEN a new app op is detected
+ mFsc.onAppOpChanged(
+ AppOpsManager.OP_CAMERA,
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
+ true);
+
+ // THEN we won't see appOps on the entry
+ Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+ // THEN notification views aren't updated since nothing changed
+ verify(mEntryManager, never()).updateNotifications(anyString());
+ }
+
+ @Test
public void testAppOpsCRUD() {
// no crash on remove that doesn't exist
mFsc.onAppOpChanged(9, 1000, "pkg1", false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 80fa8cc7d931..92a2c8738344 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -211,6 +211,19 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
}
@Test
+ public void testUpdateNotificationViews_appOps() throws Exception {
+ NotificationEntry entry0 = createEntry();
+ entry0.setRow(spy(entry0.getRow()));
+ when(mEntryManager.getVisibleNotifications()).thenReturn(
+ Lists.newArrayList(entry0));
+ mListContainer.addContainerView(entry0.getRow());
+
+ mViewHierarchyManager.updateNotificationViews();
+
+ verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
+ }
+
+ @Test
public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
// GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()
mMadeReentrantCall = false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index ae39035e8666..314b19140e7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -72,6 +72,8 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
private Notification mNotification;
private AppOpsCoordinator mAppOpsCoordinator;
private NotifFilter mForegroundFilter;
+ private NotifCollectionListener mNotifCollectionListener;
+ private AppOpsController.Callback mAppOpsCallback;
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
private FakeSystemClock mClock = new FakeSystemClock();
@@ -108,6 +110,18 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
lifetimeExtenderCaptor.capture());
mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
+ // capture notifCollectionListener
+ ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
+ ArgumentCaptor.forClass(NotifCollectionListener.class);
+ verify(mNotifPipeline, times(1)).addCollectionListener(
+ notifCollectionCaptor.capture());
+ mNotifCollectionListener = notifCollectionCaptor.getValue();
+
+ // capture app ops callback
+ ArgumentCaptor<AppOpsController.Callback> appOpsCaptor =
+ ArgumentCaptor.forClass(AppOpsController.Callback.class);
+ verify(mAppOpsController).addCallback(any(int[].class), appOpsCaptor.capture());
+ mAppOpsCallback = appOpsCaptor.getValue();
}
@Test
@@ -201,4 +215,134 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
assertFalse(mForegroundNotifLifetimeExtender
.shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
}
+
+ @Test
+ public void testAppOpsUpdateOnlyAppliedToRelevantNotificationWithStandardLayout() {
+ // GIVEN three current notifications, two with the same key but from different users
+ NotificationEntry entry1 = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(1)
+ .build();
+ NotificationEntry entry2 = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(2)
+ .build();
+ NotificationEntry entry3_diffUser = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID + 1))
+ .setPkg(TEST_PKG)
+ .setId(2)
+ .build();
+ when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3_diffUser));
+
+ // GIVEN that only entry2 has a standard layout
+ when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG))
+ .thenReturn(new ArraySet<>(List.of(entry2.getKey())));
+
+ // WHEN a new app ops code comes in
+ mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
+ mExecutor.runAllReady();
+
+ // THEN entry2's app ops are updated, but no one else's are
+ assertEquals(
+ new ArraySet<>(),
+ entry1.mActiveAppOps);
+ assertEquals(
+ new ArraySet<>(List.of(47)),
+ entry2.mActiveAppOps);
+ assertEquals(
+ new ArraySet<>(),
+ entry3_diffUser.mActiveAppOps);
+ }
+
+ @Test
+ public void testAppOpsUpdateAppliedToAllNotificationsWithStandardLayouts() {
+ // GIVEN three notifications with standard layouts
+ NotificationEntry entry1 = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(1)
+ .build();
+ NotificationEntry entry2 = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(2)
+ .build();
+ NotificationEntry entry3 = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(3)
+ .build();
+ when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry3));
+ when(mForegroundServiceController.getStandardLayoutKeys(NOTIF_USER_ID, TEST_PKG))
+ .thenReturn(new ArraySet<>(List.of(entry1.getKey(), entry2.getKey(),
+ entry3.getKey())));
+
+ // WHEN a new app ops code comes in
+ mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
+ mExecutor.runAllReady();
+
+ // THEN all entries get updated
+ assertEquals(
+ new ArraySet<>(List.of(47)),
+ entry1.mActiveAppOps);
+ assertEquals(
+ new ArraySet<>(List.of(47)),
+ entry2.mActiveAppOps);
+ assertEquals(
+ new ArraySet<>(List.of(47)),
+ entry3.mActiveAppOps);
+ }
+
+ @Test
+ public void testAppOpsAreRemoved() {
+ // GIVEN One notification which is associated with app ops
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(2)
+ .build();
+ when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry));
+ when(mForegroundServiceController.getStandardLayoutKeys(0, TEST_PKG))
+ .thenReturn(new ArraySet<>(List.of(entry.getKey())));
+
+ // GIVEN that the notification's app ops are already [47, 33]
+ mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
+ mAppOpsCallback.onActiveStateChanged(33, NOTIF_USER_ID, TEST_PKG, true);
+ mExecutor.runAllReady();
+ assertEquals(
+ new ArraySet<>(List.of(47, 33)),
+ entry.mActiveAppOps);
+
+ // WHEN one of the app ops is removed
+ mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, false);
+ mExecutor.runAllReady();
+
+ // THEN the entry's active app ops are updated as well
+ assertEquals(
+ new ArraySet<>(List.of(33)),
+ entry.mActiveAppOps);
+ }
+
+ @Test
+ public void testNullAppOps() {
+ // GIVEN one notification with app ops
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(2)
+ .build();
+ entry.mActiveAppOps.clear();
+ entry.mActiveAppOps.addAll(List.of(47, 33));
+
+ // WHEN the notification is updated and the foreground service controller returns null for
+ // this notification
+ when(mForegroundServiceController.getAppOps(entry.getSbn().getUser().getIdentifier(),
+ entry.getSbn().getPackageName())).thenReturn(null);
+ mNotifCollectionListener.onEntryUpdated(entry);
+
+ // THEN the entry's active app ops is updated to empty
+ assertTrue(entry.mActiveAppOps.isEmpty());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
new file mode 100644
index 000000000000..43d8b50bcf72
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2018 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.row;
+
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@UiThreadTest
+public class AppOpsInfoTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test_package";
+ private static final int TEST_UID = 1;
+
+ private AppOpsInfo mAppOpsInfo;
+ private final PackageManager mMockPackageManager = mock(PackageManager.class);
+ private final NotificationGuts mGutsParent = mock(NotificationGuts.class);
+ private StatusBarNotification mSbn;
+ private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
+
+ @Before
+ public void setUp() throws Exception {
+ // Inflate the layout
+ final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ mAppOpsInfo = (AppOpsInfo) layoutInflater.inflate(R.layout.app_ops_info, null);
+ mAppOpsInfo.setGutsParent(mGutsParent);
+
+ // PackageManager must return a packageInfo and applicationInfo.
+ final PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = TEST_PACKAGE_NAME;
+ when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt()))
+ .thenReturn(packageInfo);
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = TEST_UID; // non-zero
+ when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+ applicationInfo);
+
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+ new Notification(), UserHandle.CURRENT, null, 0);
+ }
+
+ @Test
+ public void testBindNotification_SetsTextApplicationName() {
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
+ final TextView textView = mAppOpsInfo.findViewById(R.id.pkgname);
+ assertTrue(textView.getText().toString().contains("App Name"));
+ }
+
+ @Test
+ public void testBindNotification_SetsPackageIcon() {
+ final Drawable iconDrawable = mock(Drawable.class);
+ when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
+ .thenReturn(iconDrawable);
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
+ final ImageView iconView = mAppOpsInfo.findViewById(R.id.pkgicon);
+ assertEquals(iconDrawable, iconView.getDrawable());
+ }
+
+ @Test
+ public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ final CountDownLatch latch = new CountDownLatch(1);
+ mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
+ ArraySet<Integer> ops) -> {
+ assertEquals(TEST_PACKAGE_NAME, pkg);
+ assertEquals(expectedOps, ops);
+ assertEquals(TEST_UID, uid);
+ latch.countDown();
+ }, mSbn, mUiEventLogger, expectedOps);
+
+ final View settingsButton = mAppOpsInfo.findViewById(R.id.settings);
+ settingsButton.performClick();
+ // Verify that listener was triggered.
+ assertEquals(0, latch.getCount());
+ }
+
+ @Test
+ public void testBindNotification_LogsOpen() throws Exception {
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, new ArraySet<>());
+ assertEquals(1, mUiEventLogger.numLogs());
+ assertEquals(NotificationAppOpsEvent.NOTIFICATION_APP_OPS_OPEN.getId(),
+ mUiEventLogger.eventId(0));
+ }
+
+ @Test
+ public void testOk() {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ final CountDownLatch latch = new CountDownLatch(1);
+ mAppOpsInfo.bindGuts(mMockPackageManager, (View v, String pkg, int uid,
+ ArraySet<Integer> ops) -> {
+ assertEquals(TEST_PACKAGE_NAME, pkg);
+ assertEquals(expectedOps, ops);
+ assertEquals(TEST_UID, uid);
+ latch.countDown();
+ }, mSbn, mUiEventLogger, expectedOps);
+
+ final View okButton = mAppOpsInfo.findViewById(R.id.ok);
+ okButton.performClick();
+ assertEquals(1, latch.getCount());
+ verify(mGutsParent, times(1)).closeControls(eq(okButton), anyBoolean());
+ }
+
+ @Test
+ public void testPrompt_camera() {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+ TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+ assertEquals("This app is using the camera.", prompt.getText());
+ }
+
+ @Test
+ public void testPrompt_mic() {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_RECORD_AUDIO);
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+ TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+ assertEquals("This app is using the microphone.", prompt.getText());
+ }
+
+ @Test
+ public void testPrompt_overlay() {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+ TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+ assertEquals("This app is displaying over other apps on your screen.", prompt.getText());
+ }
+
+ @Test
+ public void testPrompt_camera_mic() {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ expectedOps.add(OP_RECORD_AUDIO);
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+ TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+ assertEquals("This app is using the microphone and camera.", prompt.getText());
+ }
+
+ @Test
+ public void testPrompt_camera_mic_overlay() {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ expectedOps.add(OP_RECORD_AUDIO);
+ expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+ TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+ assertEquals("This app is displaying over other apps on your screen and using"
+ + " the microphone and camera.", prompt.getText());
+ }
+
+ @Test
+ public void testPrompt_camera_overlay() {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_CAMERA);
+ expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+ TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+ assertEquals("This app is displaying over other apps on your screen and using"
+ + " the camera.", prompt.getText());
+ }
+
+ @Test
+ public void testPrompt_mic_overlay() {
+ ArraySet<Integer> expectedOps = new ArraySet<>();
+ expectedOps.add(OP_RECORD_AUDIO);
+ expectedOps.add(OP_SYSTEM_ALERT_WINDOW);
+ mAppOpsInfo.bindGuts(mMockPackageManager, null, mSbn, mUiEventLogger, expectedOps);
+ TextView prompt = mAppOpsInfo.findViewById(R.id.prompt);
+ assertEquals("This app is displaying over other apps on your screen and using"
+ + " the microphone.", prompt.getText());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 4758d2318889..2684cc29aa93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -35,10 +35,12 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.app.NotificationChannel;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArraySet;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -211,6 +213,46 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ public void testShowAppOps_noHeader() {
+ // public notification is custom layout - no header
+ mGroupRow.setSensitive(true, true);
+ mGroupRow.setAppOpsOnClickListener(null);
+ mGroupRow.showAppOpsIcons(null);
+ }
+
+ @Test
+ public void testShowAppOpsIcons_header() {
+ NotificationContentView publicLayout = mock(NotificationContentView.class);
+ mGroupRow.setPublicLayout(publicLayout);
+ NotificationContentView privateLayout = mock(NotificationContentView.class);
+ mGroupRow.setPrivateLayout(privateLayout);
+ NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+ when(mockContainer.getNotificationChildCount()).thenReturn(1);
+ mGroupRow.setChildrenContainer(mockContainer);
+
+ ArraySet<Integer> ops = new ArraySet<>();
+ ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
+ mGroupRow.showAppOpsIcons(ops);
+
+ verify(mockContainer, times(1)).showAppOpsIcons(ops);
+ verify(privateLayout, times(1)).showAppOpsIcons(ops);
+ verify(publicLayout, times(1)).showAppOpsIcons(ops);
+
+ }
+
+ @Test
+ public void testAppOpsOnClick() {
+ ExpandableNotificationRow.OnAppOpsClickListener l = mock(
+ ExpandableNotificationRow.OnAppOpsClickListener.class);
+ View view = mock(View.class);
+
+ mGroupRow.setAppOpsOnClickListener(l);
+
+ mGroupRow.getAppOpsOnClickListener().onClick(view);
+ verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
+ }
+
+ @Test
public void testHeadsUpAnimatingAwayListener() {
mGroupRow.setHeadsUpAnimatingAway(true);
Assert.assertEquals(true, mHeadsUpAnimatingAway);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index b02f2746ce7a..ed4f8b330e23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -76,6 +76,32 @@ public class NotificationContentViewTest extends SysuiTestCase {
@Test
@UiThreadTest
+ public void testShowAppOpsIcons() {
+ View mockContracted = mock(NotificationHeaderView.class);
+ when(mockContracted.findViewById(com.android.internal.R.id.mic))
+ .thenReturn(mockContracted);
+ View mockExpanded = mock(NotificationHeaderView.class);
+ when(mockExpanded.findViewById(com.android.internal.R.id.mic))
+ .thenReturn(mockExpanded);
+ View mockHeadsUp = mock(NotificationHeaderView.class);
+ when(mockHeadsUp.findViewById(com.android.internal.R.id.mic))
+ .thenReturn(mockHeadsUp);
+
+ mView.setContractedChild(mockContracted);
+ mView.setExpandedChild(mockExpanded);
+ mView.setHeadsUpChild(mockHeadsUp);
+
+ ArraySet<Integer> ops = new ArraySet<>();
+ ops.add(AppOpsManager.OP_RECORD_AUDIO);
+ mView.showAppOpsIcons(ops);
+
+ verify(mockContracted, times(1)).setVisibility(View.VISIBLE);
+ verify(mockExpanded, times(1)).setVisibility(View.VISIBLE);
+ verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE);
+ }
+
+ @Test
+ @UiThreadTest
public void testExpandButtonFocusIsCalled() {
View mockContractedEB = mock(NotificationExpandButton.class);
View mockContracted = mock(NotificationHeaderView.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index d2ff2ad8a684..0c6409b38d21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -421,6 +421,7 @@ public class NotificationTestHelper {
mBindStage,
mock(OnExpandClickListener.class),
mock(NotificationMediaManager.class),
+ mock(ExpandableNotificationRow.OnAppOpsClickListener.class),
mock(FalsingManager.class),
mStatusBarStateController,
mPeopleNotificationIdentifier);