Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ddf1958b authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Update NLS listing page

- Organized by allowed/not allowed
- Include paired devices if there are any

Test: settings robotests
Fixes: 181125174
Change-Id: Id64ee0ebd9b40a92d54a03d92fec3ff0bb3b926d
parent ef664645
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -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">

    <PreferenceCategory
        android:key="allowed"
        android:title="@string/app_permission_summary_allowed"/>

    <PreferenceCategory
        android:key="not_allowed"
        android:title="@string/app_permission_summary_not_allowed"/>
</PreferenceScreen>
+3 −37
Original line number Diff line number Diff line
@@ -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<String> associatedMacAddrs =
                    mCdm.getAssociations(mCn.getPackageName(), mUserId);
            if (associatedMacAddrs != null) {
                for (String assocMac : associatedMacAddrs) {
                    final Collection<CachedBluetoothDevice> 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();
    }
}
+22 −6
Original line number Diff line number Diff line
@@ -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();
    }
+34 −0
Original line number Diff line number Diff line
@@ -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<String> associatedMacAddrs = cdm.getAssociations(pkg, userId);
            if (associatedMacAddrs != null) {
                for (String assocMac : associatedMacAddrs) {
                    final Collection<CachedBluetoothDevice> 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(
+91 −0
Original line number Diff line number Diff line
@@ -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<CachedBluetoothDevice> 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<String> 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<String> macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20");
        when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs);

        Collection<CachedBluetoothDevice> 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<String> macs = ImmutableList.of("00:00:00:00:00:10", "00:00:00:00:00:20");
        when(mCdm.getAssociations(mCn.getPackageName(), 0)).thenReturn(macs);

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