Merge "Better Support for profiles in "People that can interrupt"" into main

This commit is contained in:
Treehugger Robot
2024-10-07 20:11:09 +00:00
committed by Android (Google) Code Review
6 changed files with 520 additions and 40 deletions

View File

@@ -0,0 +1,260 @@
/*
* Copyright (C) 2024 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.notification.modes;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf;
import android.app.Flags;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.ContactsContract;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.notification.modes.ZenHelperBackend.Contact;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@RunWith(RobolectricTestRunner.class)
@EnableFlags(Flags.FLAG_MODES_UI)
public class ZenHelperBackendTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
private ZenHelperBackend mBackend;
private HashMap<Integer, FakeContactsProvider> mContactsProviders = new HashMap<>();
private int mUserId;
@Before
public void setUp() {
mContext = RuntimeEnvironment.getApplication();
mBackend = new ZenHelperBackend(mContext);
mUserId = mContext.getUserId();
addContactsProvider(mUserId);
}
private int addMainUserProfile() {
UserInfo workProfile = new UserInfo(mUserId + 10, "Work Profile", 0);
workProfile.userType = UserManager.USER_TYPE_PROFILE_MANAGED;
UserManager userManager = mContext.getSystemService(UserManager.class);
shadowOf(userManager).addProfile(mUserId, workProfile.id, workProfile);
addContactsProvider(workProfile.id);
return workProfile.id;
}
private void addContactsProvider(int userId) {
ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = String.format("%s@%s", userId, ContactsContract.AUTHORITY);
mContactsProviders.put(userId, Robolectric.buildContentProvider(FakeContactsProvider.class)
.create(providerInfo).get());
}
private void addContact(int userId, String name, boolean starred) {
mContactsProviders.get(userId).addContact(name, starred);
}
@Test
public void getAllContacts_singleProfile() {
addContact(mUserId, "Huey", false);
addContact(mUserId, "Dewey", true);
addContact(mUserId, "Louie", false);
ImmutableList<Contact> allContacts = mBackend.getAllContacts();
assertThat(allContacts).containsExactly(
new Contact(UserHandle.of(mUserId), 1, "Huey", null),
new Contact(UserHandle.of(mUserId), 2, "Dewey", null),
new Contact(UserHandle.of(mUserId), 3, "Louie", null));
}
@Test
public void getAllContacts_multipleProfiles() {
int profileId = addMainUserProfile();
addContact(mUserId, "Huey", false);
addContact(mUserId, "Dewey", true);
addContact(mUserId, "Louie", false);
addContact(profileId, "Fry", false);
addContact(profileId, "Bender", true);
ImmutableList<Contact> allContacts = mBackend.getAllContacts();
assertThat(allContacts).containsExactly(
new Contact(UserHandle.of(mUserId), 1, "Huey", null),
new Contact(UserHandle.of(mUserId), 2, "Dewey", null),
new Contact(UserHandle.of(mUserId), 3, "Louie", null),
new Contact(UserHandle.of(profileId), 1, "Fry", null),
new Contact(UserHandle.of(profileId), 2, "Bender", null));
}
@Test
public void getStarredContacts_singleProfile() {
addContact(mUserId, "Huey", false);
addContact(mUserId, "Dewey", true);
addContact(mUserId, "Louie", false);
ImmutableList<Contact> allContacts = mBackend.getStarredContacts();
assertThat(allContacts).containsExactly(
new Contact(UserHandle.of(mUserId), 2, "Dewey", null));
}
@Test
public void getStarredContacts_multipleProfiles() {
int profileId = addMainUserProfile();
addContact(mUserId, "Huey", false);
addContact(mUserId, "Dewey", true);
addContact(mUserId, "Louie", false);
addContact(profileId, "Fry", false);
addContact(profileId, "Bender", true);
ImmutableList<Contact> allContacts = mBackend.getStarredContacts();
assertThat(allContacts).containsExactly(
new Contact(UserHandle.of(mUserId), 2, "Dewey", null),
new Contact(UserHandle.of(profileId), 2, "Bender", null));
}
@Test
public void getAllContactsCount_singleProfile() {
addContact(mUserId, "Huey", false);
addContact(mUserId, "Dewey", true);
addContact(mUserId, "Louie", false);
assertThat(mBackend.getAllContactsCount()).isEqualTo(3);
}
@Test
public void getAllContactsCount_multipleProfiles() {
int profileId = addMainUserProfile();
addContact(mUserId, "Huey", false);
addContact(mUserId, "Dewey", true);
addContact(mUserId, "Louie", false);
addContact(profileId, "Fry", false);
addContact(profileId, "Bender", true);
assertThat(mBackend.getAllContactsCount()).isEqualTo(5);
}
private static class FakeContactsProvider extends ContentProvider {
private record ContactRow(int id, String name, boolean starred) {}
private final ArrayList<ContactRow> mContacts = new ArrayList<>();
FakeContactsProvider() {
}
@Override
public boolean onCreate() {
return true;
}
public int addContact(String name, boolean starred) {
mContacts.add(new ContactRow(mContacts.size() + 1, name, starred));
return mContacts.size();
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder) {
Uri baseUri = ContentProvider.getUriWithoutUserId(uri);
if (!ContactsContract.Contacts.CONTENT_URI.equals(baseUri)) {
throw new IllegalArgumentException("Unsupported uri for fake: " + uri);
}
if (projection == null || !Iterables.elementsEqual(ImmutableList.copyOf(projection),
ImmutableList.of(ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
ContactsContract.Contacts.PHOTO_THUMBNAIL_URI))) {
throw new IllegalArgumentException(
"Unsupported projection for fake: " + Arrays.toString(projection));
}
if (selection != null && !selection.equals(ContactsContract.Data.STARRED + "=1")) {
throw new IllegalArgumentException("Unsupported selection for fake: " + selection);
}
boolean selectingStarred = selection != null; // Checked as only valid selection above
MatrixCursor cursor = new MatrixCursor(projection);
for (ContactRow contactRow : mContacts) {
if (!selectingStarred || contactRow.starred) {
cursor.addRow(ImmutableList.of(contactRow.id, contactRow.name, Uri.EMPTY));
}
}
return cursor;
}
@Override
@Nullable
public String getType(@NonNull Uri uri) {
return "";
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
throw new UnsupportedOperationException();
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection,
@Nullable String[] selectionArgs) {
throw new UnsupportedOperationException();
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values,
@Nullable String selection, @Nullable String[] selectionArgs) {
throw new UnsupportedOperationException();
}
}
}

View File

@@ -43,6 +43,7 @@ import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ConversationChannelWrapper;
@@ -229,13 +230,13 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
private void setUpContacts(Collection<Integer> allIds, Collection<Integer> starredIds) {
when(mHelperBackend.getAllContacts()).thenReturn(ImmutableList.copyOf(
allIds.stream()
.map(id -> new Contact(id, "#" + id, Uri.parse("photo://" + id)))
allIds.stream().map(id -> new Contact(UserHandle.SYSTEM, id, "#" + id,
Uri.parse("photo://" + id)))
.toList()));
when(mHelperBackend.getStarredContacts()).thenReturn(ImmutableList.copyOf(
starredIds.stream()
.map(id -> new Contact(id, "#" + id, Uri.parse("photo://" + id)))
starredIds.stream().map(id -> new Contact(UserHandle.SYSTEM, id, "#" + id,
Uri.parse("photo://" + id)))
.toList()));
}
@@ -253,6 +254,6 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
}
private static ColorDrawable photoOf(Contact contact) {
return new ColorDrawable((int) contact.id());
return new ColorDrawable((int) contact.contactId());
}
}

View File

@@ -39,21 +39,41 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import android.app.Dialog;
import android.app.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Contacts;
import android.service.notification.ZenPolicy;
import android.service.notification.ZenPolicy.ConversationSenders;
import android.service.notification.ZenPolicy.PeopleType;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.testing.EmptyFragmentActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.TwoStatePreference;
import androidx.test.core.content.pm.PackageInfoBuilder;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import com.android.settings.R;
import com.android.settingslib.notification.modes.TestModeBuilder;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
@@ -70,6 +90,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowDialog;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -84,7 +105,13 @@ public final class ZenModePrioritySendersPreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public ActivityScenarioRule<EmptyFragmentActivity> mActivityScenario =
new ActivityScenarioRule<>(EmptyFragmentActivity.class);
private Context mContext;
private Activity mActivity;
private PackageManager mPackageManager;
@Mock private ZenModesBackend mBackend;
@Mock private ZenHelperBackend mHelperBackend;
@@ -97,6 +124,9 @@ public final class ZenModePrioritySendersPreferenceControllerTest {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
mActivityScenario.getScenario().onActivity(activity -> mActivity = activity);
mPackageManager = mContext.getPackageManager();
mMessagesController = new ZenModePrioritySendersPreferenceController(mContext, "messages",
true, mBackend, mHelperBackend);
@@ -114,8 +144,8 @@ public final class ZenModePrioritySendersPreferenceControllerTest {
mPreferenceScreen.addPreference(mMessagesPrefCategory);
when(mHelperBackend.getStarredContacts()).thenReturn(ImmutableList.of());
when(mHelperBackend.getAllContacts()).thenReturn(
ImmutableList.of(new ZenHelperBackend.Contact(1, "The only contact", null)));
when(mHelperBackend.getAllContacts()).thenReturn(ImmutableList.of(
new ZenHelperBackend.Contact(UserHandle.SYSTEM, 1, "The only contact", null)));
when(mHelperBackend.getAllContactsCount()).thenReturn(1);
when(mHelperBackend.getImportantConversations()).thenReturn(ImmutableList.of());
@@ -1439,4 +1469,142 @@ public final class ZenModePrioritySendersPreferenceControllerTest {
assertThat(captor.getValue().getPolicy().getPriorityCallSenders())
.isEqualTo(PEOPLE_TYPE_NONE);
}
@Test
public void displayPreference_hasContactsApp_hasSettingsButton() {
String contactsPackage = mContext.getString(R.string.config_contacts_package_name);
setUpContactsApp(contactsPackage, /* withPreciseIntents= */ false);
mCallsController.displayPreference(mPreferenceScreen);
SelectorWithWidgetPreference contactsPref = getBoundSelectorPreference(KEY_STARRED);
assertThat(((View) contactsPref.getExtraWidget().getParent()).getVisibility()).isEqualTo(
View.VISIBLE);
}
@Test
public void displayPreference_noContactsApp_noSettingsButton() {
String contactsPackage = mContext.getString(R.string.config_contacts_package_name);
shadowOf(mPackageManager).removePackage(contactsPackage);
mCallsController.displayPreference(mPreferenceScreen);
SelectorWithWidgetPreference contactsPref = getBoundSelectorPreference(KEY_STARRED);
assertThat(((View) contactsPref.getExtraWidget().getParent()).getVisibility()).isEqualTo(
View.GONE);
}
@Test
public void contactsSettingsClick_usesBestIntent() {
String contactsPackage = mContext.getString(R.string.config_contacts_package_name);
setUpContactsApp(contactsPackage, /* withPreciseIntents= */ true);
mCallsController.displayPreference(mPreferenceScreen);
mCallsController.updateZenMode(mCallsPrefCategory, TestModeBuilder.EXAMPLE);
SelectorWithWidgetPreference contactsPref = getBoundSelectorPreference(KEY_CONTACTS);
contactsPref.getExtraWidget().performClick();
Intent nextActivity = shadowOf(mActivity).getNextStartedActivity();
assertThat(nextActivity).isNotNull();
assertThat(nextActivity.getPackage()).isEqualTo(contactsPackage);
assertThat(nextActivity.getAction()).isEqualTo(Contacts.Intents.UI.LIST_DEFAULT);
}
@Test
public void starredContactsSettingsClick_usesBestIntent() {
String contactsPackage = mContext.getString(R.string.config_contacts_package_name);
setUpContactsApp(contactsPackage, /* withPreciseIntents= */ true);
mCallsController.displayPreference(mPreferenceScreen);
mCallsController.updateZenMode(mCallsPrefCategory, TestModeBuilder.EXAMPLE);
SelectorWithWidgetPreference contactsPref = getBoundSelectorPreference(KEY_STARRED);
contactsPref.getExtraWidget().performClick();
Intent nextActivity = shadowOf(mActivity).getNextStartedActivity();
assertThat(nextActivity).isNotNull();
assertThat(nextActivity.getPackage()).isEqualTo(contactsPackage);
assertThat(nextActivity.getAction()).isEqualTo(Contacts.Intents.UI.LIST_STARRED_ACTION);
}
@Test
public void contactsSettingsClick_usesFallbackIntent() {
String contactsPackage = mContext.getString(R.string.config_contacts_package_name);
setUpContactsApp(contactsPackage, /* withPreciseIntents= */ false);
mCallsController.displayPreference(mPreferenceScreen);
mCallsController.updateZenMode(mCallsPrefCategory, TestModeBuilder.EXAMPLE);
SelectorWithWidgetPreference contactsPref = getBoundSelectorPreference(KEY_CONTACTS);
contactsPref.getExtraWidget().performClick();
Intent nextActivity = shadowOf(mActivity).getNextStartedActivity();
assertThat(nextActivity).isNotNull();
assertThat(nextActivity.getPackage()).isEqualTo(contactsPackage);
assertThat(nextActivity.getAction()).isEqualTo(Intent.ACTION_MAIN);
}
@Test
public void contactsSettingsClick_multipleProfiles_showsProfileChooserDialog() {
String contactsPackage = mContext.getString(R.string.config_contacts_package_name);
setUpContactsApp(contactsPackage, /* withPreciseIntents= */ true);
UserInfo workProfile = new UserInfo(mContext.getUserId() + 10, "Work Profile", 0);
workProfile.userType = UserManager.USER_TYPE_PROFILE_MANAGED;
UserManager userManager = mContext.getSystemService(UserManager.class);
shadowOf(userManager).addProfile(mContext.getUserId(), workProfile.id, workProfile);
mCallsController.displayPreference(mPreferenceScreen);
mCallsController.updateZenMode(mCallsPrefCategory, TestModeBuilder.EXAMPLE);
SelectorWithWidgetPreference contactsPref = getBoundSelectorPreference(KEY_CONTACTS);
contactsPref.getExtraWidget().performClick();
Dialog profileSelectDialog = ShadowDialog.getLatestDialog();
assertThat(profileSelectDialog).isNotNull();
TextView dialogTitle = profileSelectDialog.findViewById(android.R.id.title);
assertThat(dialogTitle.getText().toString()).isEqualTo("Choose profile");
}
private void setUpContactsApp(String contactsPackage, boolean withPreciseIntents) {
ComponentName contactsActivity = new ComponentName(contactsPackage, "ContactsActivity");
shadowOf(mPackageManager).installPackage(
PackageInfoBuilder.newBuilder()
.setPackageName(contactsPackage)
.build());
shadowOf(mPackageManager).addActivityIfNotPresent(contactsActivity);
// Fallback / default intent filter.
IntentFilter mainFilter = new IntentFilter(Intent.ACTION_MAIN);
mainFilter.addCategory(Intent.CATEGORY_DEFAULT);
mainFilter.addCategory(Intent.CATEGORY_APP_CONTACTS);
shadowOf(mPackageManager).addIntentFilterForActivity(contactsActivity, mainFilter);
if (withPreciseIntents) {
IntentFilter listFilter = new IntentFilter(Contacts.Intents.UI.LIST_DEFAULT);
listFilter.addCategory(Intent.CATEGORY_DEFAULT);
shadowOf(mPackageManager).addIntentFilterForActivity(contactsActivity, listFilter);
IntentFilter starredFilter = new IntentFilter(Contacts.Intents.UI.LIST_STARRED_ACTION);
starredFilter.addCategory(Intent.CATEGORY_DEFAULT);
shadowOf(mPackageManager).addIntentFilterForActivity(contactsActivity, starredFilter);
}
}
private SelectorWithWidgetPreference getBoundSelectorPreference(String key) {
SelectorWithWidgetPreference selectorPref = checkNotNull(
mCallsPrefCategory.findPreference(key));
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(selectorPref.getLayoutResource(), null);
LinearLayout widgetView = view.findViewById(android.R.id.widget_frame);
assertThat(widgetView).isNotNull();
inflater.inflate(selectorPref.getWidgetLayoutResource(), widgetView, true);
PreferenceViewHolder viewHolder = PreferenceViewHolder.createInstanceForTests(view);
selectorPref.onBindViewHolder(viewHolder);
return selectorPref;
}
}