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
This commit is contained in:
Chloris Kuo
2022-08-16 15:08:29 -07:00
parent 105cda2fbe
commit 54c677a83c
3 changed files with 125 additions and 7 deletions

View File

@@ -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"

View File

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

View File

@@ -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 {