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");
- }
-}