From ddf1958bdcd4f2e57e2709b3aad410f77d06e3d4 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 4 Mar 2021 10:26:42 -0500 Subject: [PATCH] Update NLS listing page - Organized by allowed/not allowed - Include paired devices if there are any Test: settings robotests Fixes: 181125174 Change-Id: Id64ee0ebd9b40a92d54a03d92fec3ff0bb3b926d --- res/xml/notification_access_settings.xml | 11 +- .../HeaderPreferenceController.java | 40 +----- .../NotificationAccessSettings.java | 28 +++- .../notification/NotificationBackend.java | 34 +++++ .../notification/NotificationBackendTest.java | 91 ++++++++++++ .../HeaderPreferenceControllerTest.java | 135 ------------------ 6 files changed, 160 insertions(+), 179 deletions(-) delete mode 100644 tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceControllerTest.java diff --git a/res/xml/notification_access_settings.xml b/res/xml/notification_access_settings.xml index 43f7c492b37..b523d7de691 100644 --- a/res/xml/notification_access_settings.xml +++ b/res/xml/notification_access_settings.xml @@ -21,4 +21,13 @@ android:key="notification_access_screen" android:title="@string/manage_notification_access_title" settings:searchable="false" - settings:controller="com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessScreenPreferenceController" /> + settings:controller="com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessScreenPreferenceController"> + + + + + diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java index 36f884ffe6b..1144f12132d 100644 --- a/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java +++ b/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceController.java @@ -21,9 +21,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.os.RemoteException; import android.util.IconDrawableFactory; -import android.util.Log; import android.view.View; import androidx.lifecycle.LifecycleObserver; @@ -34,17 +32,13 @@ import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.notification.NotificationBackend; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; -import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.widget.LayoutPreference; -import java.util.Collection; -import java.util.List; -import java.util.Objects; - public class HeaderPreferenceController extends BasePreferenceController implements PreferenceControllerMixin, LifecycleObserver { @@ -122,7 +116,8 @@ public class HeaderPreferenceController extends BasePreferenceController .getBadgedIcon(mPackageInfo.applicationInfo)) .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm)) .setSummary(mServiceName) - .setSecondSummary(getDeviceList()) + .setSecondSummary(new NotificationBackend().getDeviceList( + mCdm, mBm, mCn.getPackageName(), mUserId)) .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo)) .setPackageName(mPackageInfo.packageName) .setUid(mPackageInfo.applicationInfo.uid) @@ -139,33 +134,4 @@ public class HeaderPreferenceController extends BasePreferenceController mHeaderController.styleActionBar(mFragment.getActivity()); } } - - protected CharSequence getDeviceList() { - boolean multiple = false; - StringBuilder sb = new StringBuilder(); - - try { - List associatedMacAddrs = - mCdm.getAssociations(mCn.getPackageName(), mUserId); - if (associatedMacAddrs != null) { - for (String assocMac : associatedMacAddrs) { - final Collection cachedDevices = - mBm.getCachedDeviceManager().getCachedDevicesCopy(); - for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) { - if (Objects.equals(assocMac, cachedBluetoothDevice.getAddress())) { - if (multiple) { - sb.append(", "); - } else { - multiple = true; - } - sb.append(cachedBluetoothDevice.getName()); - } - } - } - } - } catch (RemoteException e) { - Log.w(TAG, "Error calling CDM", e); - } - return sb.toString(); - } } diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java index 12d6295ef90..a642d9c5b33 100644 --- a/src/com/android/settings/notification/NotificationAccessSettings.java +++ b/src/com/android/settings/notification/NotificationAccessSettings.java @@ -20,12 +20,14 @@ import android.annotation.Nullable; import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; +import android.companion.ICompanionDeviceManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.os.Bundle; +import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -35,6 +37,7 @@ import android.util.Log; import android.view.View; import android.widget.Toast; +import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; import com.android.settings.R; @@ -57,6 +60,9 @@ import java.util.List; @SearchIndexable public class NotificationAccessSettings extends EmptyTextSettings { private static final String TAG = "NotifAccessSettings"; + private static final String ALLOWED_KEY = "allowed"; + private static final String NOT_ALLOWED_KEY = "not_allowed"; + private static final ManagedServiceSettings.Config CONFIG = new ManagedServiceSettings.Config.Builder() .setTag(TAG) @@ -76,6 +82,7 @@ public class NotificationAccessSettings extends EmptyTextSettings { private DevicePolicyManager mDpm; private ServiceListing mServiceListing; private IconDrawableFactory mIconDrawableFactory; + private NotificationBackend mBackend = new NotificationBackend(); @Override public void onCreate(Bundle icicle) { @@ -93,7 +100,6 @@ public class NotificationAccessSettings extends EmptyTextSettings { .setTag(CONFIG.tag) .build(); mServiceListing.addCallback(this::updateList); - setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext)); if (UserManager.get(mContext).isManagedProfile()) { // Apps in the work profile do not support notification listeners. @@ -127,7 +133,11 @@ public class NotificationAccessSettings extends EmptyTextSettings { final int managedProfileId = Utils.getManagedProfileId(um, UserHandle.myUserId()); final PreferenceScreen screen = getPreferenceScreen(); - screen.removeAll(); + final PreferenceCategory allowedCategory = screen.findPreference(ALLOWED_KEY); + allowedCategory.removeAll(); + final PreferenceCategory notAllowedCategory = screen.findPreference(NOT_ALLOWED_KEY); + notAllowedCategory.removeAll(); + services.sort(new PackageItemInfo.DisplayNameComparator(mPm)); for (ServiceInfo service : services) { final ComponentName cn = new ComponentName(service.packageName, service.name); @@ -145,9 +155,11 @@ public class NotificationAccessSettings extends EmptyTextSettings { pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo, UserHandle.getUserId(service.applicationInfo.uid))); pref.setKey(cn.flattenToString()); - pref.setSummary(mNm.isNotificationListenerAccessGranted(cn) - ? R.string.app_permission_summary_allowed - : R.string.app_permission_summary_not_allowed); + pref.setSummary(mBackend.getDeviceList(ICompanionDeviceManager.Stub.asInterface( + ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE)), + com.android.settings.bluetooth.Utils.getLocalBtManager(mContext), + service.packageName, + UserHandle.myUserId())); if (managedProfileId != UserHandle.USER_NULL && !mDpm.isNotificationListenerServicePermitted( service.packageName, managedProfileId)) { @@ -173,7 +185,11 @@ public class NotificationAccessSettings extends EmptyTextSettings { return true; }); pref.setKey(cn.flattenToString()); - screen.addPreference(pref); + if (mNm.isNotificationListenerAccessGranted(cn)) { + allowedCategory.addPreference(pref); + } else { + notAllowedCategory.addPreference(pref); + } } highlightPreferenceIfNeeded(); } diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java index 8a7e737dbb8..4347ca55104 100644 --- a/src/com/android/settings/notification/NotificationBackend.java +++ b/src/com/android/settings/notification/NotificationBackend.java @@ -30,6 +30,7 @@ import android.app.NotificationManager; import android.app.role.RoleManager; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; +import android.companion.ICompanionDeviceManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -55,14 +56,18 @@ import androidx.annotation.VisibleForTesting; import com.android.settingslib.R; import com.android.settingslib.Utils; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.notification.ConversationIconFactory; import com.android.settingslib.utils.StringUtil; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; public class NotificationBackend { private static final String TAG = "NotificationBackend"; @@ -138,6 +143,35 @@ public class NotificationBackend { } } + static public CharSequence getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm, + String pkg, int userId) { + boolean multiple = false; + StringBuilder sb = new StringBuilder(); + + try { + List associatedMacAddrs = cdm.getAssociations(pkg, userId); + if (associatedMacAddrs != null) { + for (String assocMac : associatedMacAddrs) { + final Collection cachedDevices = + lbm.getCachedDeviceManager().getCachedDevicesCopy(); + for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) { + if (Objects.equals(assocMac, cachedBluetoothDevice.getAddress())) { + if (multiple) { + sb.append(", "); + } else { + multiple = true; + } + sb.append(cachedBluetoothDevice.getName()); + } + } + } + } + } catch (RemoteException e) { + Log.w(TAG, "Error calling CDM", e); + } + return sb.toString(); + } + public boolean isSystemApp(Context context, ApplicationInfo app) { try { PackageInfo info = context.getPackageManager().getPackageInfo( diff --git a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java index cb5060914b8..17471b5d90c 100644 --- a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java +++ b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java @@ -26,24 +26,50 @@ import static org.mockito.Mockito.when; import android.app.role.RoleManager; import android.app.usage.UsageEvents; +import android.bluetooth.BluetoothAdapter; +import android.companion.ICompanionDeviceManager; +import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Parcel; import com.android.settings.notification.NotificationBackend.AppRow; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.google.common.collect.ImmutableList; + +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; +import java.util.Collection; import java.util.List; @RunWith(RobolectricTestRunner.class) public class NotificationBackendTest { + @Mock + LocalBluetoothManager mBm; + @Mock + ICompanionDeviceManager mCdm; + @Mock + CachedBluetoothDeviceManager mCbm; + ComponentName mCn = new ComponentName("a", "b"); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mBm.getCachedDeviceManager()).thenReturn(mCbm); + } + @Test public void testMarkAppRow_unblockablePackage() { AppRow appRow = new AppRow(); @@ -138,4 +164,69 @@ public class NotificationBackendTest { parcel.setDataPosition(0); return UsageEvents.CREATOR.createFromParcel(parcel); } + + @Test + public void getDeviceList_noAssociations() throws Exception { + when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(null); + + Collection cachedDevices = new ArrayList<>(); + CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class); + when(cbd1.getAddress()).thenReturn("00:00:00:00:00:10"); + when(cbd1.getName()).thenReturn("Device 1"); + cachedDevices.add(cbd1); + when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices); + + BluetoothAdapter.getDefaultAdapter().enable(); + + assertThat(new NotificationBackend().getDeviceList( + mCdm, mBm, mCn.getPackageName(), 0).toString()).isEmpty(); + } + + @Test + public void getDeviceList_associationsButNoDevice() throws Exception { + List macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20"); + when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs); + + when(mCbm.getCachedDevicesCopy()).thenReturn(new ArrayList<>()); + + assertThat(new NotificationBackend().getDeviceList( + mCdm, mBm, mCn.getPackageName(), 0).toString()).isEmpty(); + } + + @Test + public void getDeviceList_singleDevice() throws Exception { + List macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20"); + when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs); + + Collection cachedDevices = new ArrayList<>(); + CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class); + when(cbd1.getAddress()).thenReturn(macs.get(0)); + when(cbd1.getName()).thenReturn("Device 1"); + cachedDevices.add(cbd1); + when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices); + + assertThat(new NotificationBackend().getDeviceList( + mCdm, mBm, mCn.getPackageName(), 0).toString()).isEqualTo("Device 1"); + } + + @Test + public void getDeviceList_multipleDevices() throws Exception { + List macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20"); + when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs); + + Collection cachedDevices = new ArrayList<>(); + CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class); + when(cbd1.getAddress()).thenReturn(macs.get(0)); + when(cbd1.getName()).thenReturn("Device 1"); + cachedDevices.add(cbd1); + + CachedBluetoothDevice cbd2 = mock(CachedBluetoothDevice.class); + when(cbd2.getAddress()).thenReturn(macs.get(1)); + when(cbd2.getName()).thenReturn("Device 2"); + cachedDevices.add(cbd2); + when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices); + + assertThat(new NotificationBackend().getDeviceList( + mCdm, mBm, mCn.getPackageName(), 0).toString()).isEqualTo("Device 1, Device 2"); + } } diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceControllerTest.java deleted file mode 100644 index 40ad1e08b17..00000000000 --- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/HeaderPreferenceControllerTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2021 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.settings.applications.specialaccess.notificationaccess; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.bluetooth.BluetoothAdapter; -import android.companion.ICompanionDeviceManager; -import android.content.ComponentName; -import android.content.Context; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; -import com.android.settingslib.bluetooth.LocalBluetoothManager; - -import com.google.common.collect.ImmutableList; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -public class HeaderPreferenceControllerTest { - - private Context mContext; - private HeaderPreferenceController mController; - @Mock - LocalBluetoothManager mBm; - @Mock - ICompanionDeviceManager mCdm; - @Mock - CachedBluetoothDeviceManager mCbm; - ComponentName mCn = new ComponentName("a", "b"); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = ApplicationProvider.getApplicationContext(); - - when(mBm.getCachedDeviceManager()).thenReturn(mCbm); - - mController = new HeaderPreferenceController(mContext, "key"); - mController.setCn(mCn); - mController.setUserId(0); - mController.setBluetoothManager(mBm); - mController.setCdm(mCdm); - } - - @Test - public void getDeviceList_noAssociations() throws Exception { - when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(null); - - Collection cachedDevices = new ArrayList<>(); - CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class); - when(cbd1.getAddress()).thenReturn("00:00:00:00:00:10"); - when(cbd1.getName()).thenReturn("Device 1"); - cachedDevices.add(cbd1); - when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices); - - BluetoothAdapter.getDefaultAdapter().enable(); - - assertThat(mController.getDeviceList().toString()).isEmpty(); - } - - @Test - public void getDeviceList_associationsButNoDevice() throws Exception { - List macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20"); - when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs); - - when(mCbm.getCachedDevicesCopy()).thenReturn(new ArrayList<>()); - - assertThat(mController.getDeviceList().toString()).isEmpty(); - } - - @Test - public void getDeviceList_singleDevice() throws Exception { - List macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20"); - when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs); - - Collection cachedDevices = new ArrayList<>(); - CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class); - when(cbd1.getAddress()).thenReturn(macs.get(0)); - when(cbd1.getName()).thenReturn("Device 1"); - cachedDevices.add(cbd1); - when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices); - - assertThat(mController.getDeviceList().toString()).isEqualTo("Device 1"); - } - - @Test - public void getDeviceList_multipleDevices() throws Exception { - List macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20"); - when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs); - - Collection cachedDevices = new ArrayList<>(); - CachedBluetoothDevice cbd1 = mock(CachedBluetoothDevice.class); - when(cbd1.getAddress()).thenReturn(macs.get(0)); - when(cbd1.getName()).thenReturn("Device 1"); - cachedDevices.add(cbd1); - - CachedBluetoothDevice cbd2 = mock(CachedBluetoothDevice.class); - when(cbd2.getAddress()).thenReturn(macs.get(1)); - when(cbd2.getName()).thenReturn("Device 2"); - cachedDevices.add(cbd2); - when(mCbm.getCachedDevicesCopy()).thenReturn(cachedDevices); - - assertThat(mController.getDeviceList().toString()).isEqualTo("Device 1, Device 2"); - } -}