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

Commit 54c677a8 authored by Chloris Kuo's avatar Chloris Kuo
Browse files

Support ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS in Enhanced Notifications

Show detail settings page from the default NAS app if it implements the new intent ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS.

Test: Robotest, manually test on device
Bug: 231492005
Change-Id: I6566cd9d615331a56728613583295637982bcd3f
Merged-In: I6566cd9d615331a56728613583295637982bcd3f
parent 105cda2f
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -156,7 +156,7 @@
            android:title="@string/notification_pulse_title"
            android:title="@string/notification_pulse_title"
            settings:controller="com.android.settings.notification.PulseNotificationPreferenceController"/>
            settings:controller="com.android.settings.notification.PulseNotificationPreferenceController"/>


        <SwitchPreference
        <com.android.settingslib.PrimarySwitchPreference
            android:key="notification_assistant"
            android:key="notification_assistant"
            android:order="23"
            android:order="23"
            android:title="@string/notification_assistant_title"
            android:title="@string/notification_assistant_title"
+52 −4
Original line number Original line Diff line number Diff line
@@ -18,35 +18,49 @@ package com.android.settings.notification;


import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings;
import android.service.notification.NotificationAssistantService;


import androidx.fragment.app.Fragment;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;


import com.android.settings.R;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.PrimarySwitchPreference;


import com.google.common.annotations.VisibleForTesting;
import com.google.common.annotations.VisibleForTesting;


import java.util.List;

public class NotificationAssistantPreferenceController extends TogglePreferenceController {
public class NotificationAssistantPreferenceController extends TogglePreferenceController {
    private static final String TAG = "NASPreferenceController";
    private static final String TAG = "NASPreferenceController";
    private static final String KEY_NAS = "notification_assistant";
    static final String KEY_NAS = "notification_assistant";


    private static final int AVAILABLE = 1;
    private static final int AVAILABLE = 1;
    private final UserManager mUserManager;
    private final UserManager mUserManager;
    private final PackageManager mPackageManager;
    private Fragment mFragment;
    private Fragment mFragment;
    private int mUserId = UserHandle.myUserId();
    private int mUserId = UserHandle.myUserId();


    @VisibleForTesting
    @VisibleForTesting
    protected NotificationBackend mNotificationBackend;
    protected NotificationBackend mNotificationBackend;
    private ComponentName mDefaultNASComponent;
    private Intent mNASSettingIntent;


    public NotificationAssistantPreferenceController(Context context) {
    public NotificationAssistantPreferenceController(Context context) {
        super(context, KEY_NAS);
        super(context, KEY_NAS);
        mUserManager = UserManager.get(context);
        mUserManager = UserManager.get(context);
        mNotificationBackend = new NotificationBackend();
        mNotificationBackend = new NotificationBackend();
        mPackageManager = context.getPackageManager();
        getDefaultNASIntent();
    }
    }



    @Override
    @Override
    public int getAvailabilityStatus() {
    public int getAvailabilityStatus() {
        return AVAILABLE;
        return AVAILABLE;
@@ -55,14 +69,13 @@ public class NotificationAssistantPreferenceController extends TogglePreferenceC
    @Override
    @Override
    public boolean isChecked() {
    public boolean isChecked() {
        ComponentName acn = mNotificationBackend.getAllowedNotificationAssistant();
        ComponentName acn = mNotificationBackend.getAllowedNotificationAssistant();
        ComponentName dcn = mNotificationBackend.getDefaultNotificationAssistant();
        return (acn != null && acn.equals(mDefaultNASComponent));
        return (acn != null && acn.equals(dcn));
    }
    }


    @Override
    @Override
    public boolean setChecked(boolean isChecked) {
    public boolean setChecked(boolean isChecked) {
        ComponentName cn = isChecked
        ComponentName cn = isChecked
                ? mNotificationBackend.getDefaultNotificationAssistant() : null;
                ? mDefaultNASComponent : null;
        if (isChecked) {
        if (isChecked) {
            if (mFragment == null) {
            if (mFragment == null) {
                throw new IllegalStateException("No fragment to start activity");
                throw new IllegalStateException("No fragment to start activity");
@@ -103,8 +116,43 @@ public class NotificationAssistantPreferenceController extends TogglePreferenceC
        mNotificationBackend = backend;
        mNotificationBackend = backend;
    }
    }


    @VisibleForTesting
    void getDefaultNASIntent() {
        mDefaultNASComponent = mNotificationBackend.getDefaultNotificationAssistant();
        if (mDefaultNASComponent != null) {
            mNASSettingIntent = new Intent(
                    NotificationAssistantService.ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS);
            mNASSettingIntent.setPackage(mDefaultNASComponent.getPackageName());
            mNASSettingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
    }

    @Override
    @Override
    public boolean isSliceable() {
    public boolean isSliceable() {
        return (mFragment != null && mFragment instanceof ConfigureNotificationSettings);
        return (mFragment != null && mFragment instanceof ConfigureNotificationSettings);
    }
    }

    private boolean isNASSettingActivityAvailable() {
        final List<ResolveInfo> resolved = mPackageManager.queryIntentActivities(mNASSettingIntent,
                PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_ALL));
        return (resolved != null && !resolved.isEmpty());
    }

    @Override
    public void updateState(Preference preference) {
        super.updateState(preference);
        if (isNASSettingActivityAvailable()) {
            preference.setIntent(mNASSettingIntent);
        } else {
            // Cannot find settings activity from the default NAS app
            preference.setIntent(null);
            preference.setOnPreferenceClickListener(
                    preference1 -> {
                        onPreferenceChange(preference1, !isChecked());
                        ((PrimarySwitchPreference) preference1).setChecked(isChecked());
                        return true;
                    }
            );
        }
    }
}
}
+72 −2
Original line number Original line Diff line number Diff line
@@ -16,7 +16,12 @@


package com.android.settings.notification;
package com.android.settings.notification;


import static android.service.notification.NotificationAssistantService.ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -29,16 +34,24 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.app.Application;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.UserManager;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings;


import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.core.app.ApplicationProvider;


import com.android.settings.testutils.shadow.ShadowSecureSettings;
import com.android.settings.testutils.shadow.ShadowSecureSettings;
import com.android.settingslib.PrimarySwitchPreference;


import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
@@ -47,9 +60,13 @@ import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowApplication;


import java.util.ArrayList;
import java.util.List;



@RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class NotificationAssistantPreferenceControllerTest {
public class NotificationAssistantPreferenceControllerTest {
@@ -67,23 +84,48 @@ public class NotificationAssistantPreferenceControllerTest {
    private NotificationBackend mBackend;
    private NotificationBackend mBackend;
    @Mock
    @Mock
    private UserManager mUserManager;
    private UserManager mUserManager;
    @Mock
    private PackageManager mPackageManager;
    private NotificationAssistantPreferenceController mPreferenceController;
    private NotificationAssistantPreferenceController mPreferenceController;
    ComponentName mNASComponent = new ComponentName("a", "b");
    ComponentName mNASComponent = new ComponentName("pkgname", "clsname");
    private PrimarySwitchPreference mPreference;
    private ShadowApplication mShadowApplication;


    @Before
    @Before
    public void setUp() {
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);
        mContext = spy(ApplicationProvider.getApplicationContext());
        mContext = spy(ApplicationProvider.getApplicationContext());
        ShadowApplication.getInstance().setSystemService(Context.USER_SERVICE, mUserManager);
        mPreference = spy(new PrimarySwitchPreference(mContext));
        mShadowApplication = ShadowApplication.getInstance();
        mShadowApplication.setSystemService(Context.USER_SERVICE, mUserManager);
        doReturn(mContext).when(mFragment).getContext();
        doReturn(mContext).when(mFragment).getContext();
        when(mFragment.getFragmentManager()).thenReturn(mFragmentManager);
        when(mFragment.getFragmentManager()).thenReturn(mFragmentManager);
        when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
        when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
        when(mBackend.getDefaultNotificationAssistant()).thenReturn(mNASComponent);
        when(mBackend.getDefaultNotificationAssistant()).thenReturn(mNASComponent);
        when(mContext.getPackageManager()).thenReturn(mPackageManager);
        mPreferenceController = new NotificationAssistantPreferenceController(mContext);
        mPreferenceController = new NotificationAssistantPreferenceController(mContext);
        mPreferenceController.setBackend(mBackend);
        mPreferenceController.setBackend(mBackend);
        mPreferenceController.setFragment(mFragment);
        mPreferenceController.setFragment(mFragment);
        mPreferenceController.getDefaultNASIntent();

        final PreferenceManager preferenceManager = new PreferenceManager(mContext);
        final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
        mPreference.setKey(NotificationAssistantPreferenceController.KEY_NAS);
        screen.addPreference(mPreference);
        mPreferenceController.displayPreference(screen);

        when(mUserManager.getProfileIds(eq(0), anyBoolean())).thenReturn(new int[] {0, 10});
        when(mUserManager.getProfileIds(eq(0), anyBoolean())).thenReturn(new int[] {0, 10});
        when(mUserManager.getProfileIds(eq(20), anyBoolean())).thenReturn(new int[] {20});
        when(mUserManager.getProfileIds(eq(20), anyBoolean())).thenReturn(new int[] {20});

        ActivityInfo activityInfo1 = new ActivityInfo();
        activityInfo1.packageName = "pkgname";
        activityInfo1.name = "name";
        ResolveInfo resolveInfo1 = new ResolveInfo();
        resolveInfo1.activityInfo = activityInfo1;
        List<ResolveInfo> resolvers1 = new ArrayList<>();
        resolvers1.add(resolveInfo1);
        when(mPackageManager.queryIntentActivities(any(Intent.class), any()))
                .thenReturn(resolvers1);
    }
    }


    @Test
    @Test
@@ -108,6 +150,34 @@ public class NotificationAssistantPreferenceControllerTest {
        verify(mBackend, times(1)).setNotificationAssistantGranted(null);
        verify(mBackend, times(1)).setNotificationAssistantGranted(null);
    }
    }


    @Test
    public void testUpdateState_SettingActivityAvailable() throws Exception {
        mPreferenceController.updateState(mPreference);
        assertNotNull(mPreference.getIntent());

        mPreference.performClick();
        Intent nextIntent = Shadows.shadowOf(
                (Application) ApplicationProvider.getApplicationContext()).getNextStartedActivity();
        assertEquals(nextIntent.getAction(), ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS);
    }

    @Test
    public void testUpdateState_SettingActivityUnavailable() throws Exception {
        when(mPackageManager.queryIntentActivities(any(Intent.class), any()))
                .thenReturn(null);
        mPreferenceController.updateState(mPreference);
        assertNull(mPreference.getIntent());

        mPreference.performClick();
        Intent nextIntent = Shadows.shadowOf(
                (Application) ApplicationProvider.getApplicationContext()).getNextStartedActivity();
        assertNull(nextIntent);
        // Verify a dialog is shown
        verify(mFragmentTransaction).add(
                any(NotificationAssistantDialogFragment.class), anyString());
        verify(mBackend, times(0)).setNotificationAssistantGranted(any());
    }

    @Test
    @Test
    @Config(shadows = ShadowSecureSettings.class)
    @Config(shadows = ShadowSecureSettings.class)
    public void testMigrationFromSetting_userEnable_multiProfile() throws Exception {
    public void testMigrationFromSetting_userEnable_multiProfile() throws Exception {