diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml index c84a33d1699..dae3b42e523 100644 --- a/res/xml/enterprise_privacy_settings.xml +++ b/res/xml/enterprise_privacy_settings.xml @@ -62,6 +62,7 @@ android:key="enterprise_privacy_number_camera_access_packages" android:title="@string/enterprise_privacy_camera_access"/> + + + + + diff --git a/src/com/android/settings/applications/ApplicationFeatureProvider.java b/src/com/android/settings/applications/ApplicationFeatureProvider.java index e3ad4c9327c..3266fe0d7f8 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProvider.java +++ b/src/com/android/settings/applications/ApplicationFeatureProvider.java @@ -16,14 +16,14 @@ package com.android.settings.applications; -import com.android.settings.applications.instantapps.InstantAppButtonsController; - +import android.annotation.UserIdInt; import android.app.Fragment; import android.content.Intent; import android.view.View; +import com.android.settings.applications.instantapps.InstantAppButtonsController; + import java.util.List; -import java.util.Set; public interface ApplicationFeatureProvider { @@ -80,16 +80,18 @@ public interface ApplicationFeatureProvider { void listAppsWithAdminGrantedPermissions(String[] permissions, ListOfAppsCallback callback); /** - * Return the persistent preferred activities configured by the admin for the current user and - * all its managed profiles. A persistent preferred activity is an activity that the admin - * configured to always handle a given intent (e.g. open browser), even if the user has other - * apps installed that would also be able to handle the intent. + * Return the persistent preferred activities configured by the admin for the given user. + * A persistent preferred activity is an activity that the admin configured to always handle a + * given intent (e.g. open browser), even if the user has other apps installed that would also + * be able to handle the intent. * + * @param userId ID of the user for which to find persistent preferred activities * @param intent The intents for which to find persistent preferred activities * - * @return the persistent preferred activites for the given intent + * @return the persistent preferred activites for the given intents, ordered first by user id, + * then by package name */ - Set findPersistentPreferredActivities(Intent[] intents); + List findPersistentPreferredActivities(@UserIdInt int userId, Intent[] intents); /** * Callback that receives the number of packages installed on the device. @@ -104,30 +106,4 @@ public interface ApplicationFeatureProvider { interface ListOfAppsCallback { void onListOfAppsResult(List result); } - - public static class PersistentPreferredActivityInfo { - public final String packageName; - public final int userId; - - public PersistentPreferredActivityInfo(String packageName, int userId) { - this.packageName = packageName; - this.userId = userId; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof PersistentPreferredActivityInfo)) { - return false; - } - final PersistentPreferredActivityInfo otherActivityInfo - = (PersistentPreferredActivityInfo) other; - return otherActivityInfo.packageName.equals(packageName) - && otherActivityInfo.userId == userId; - } - - @Override - public int hashCode() { - return packageName.hashCode() ^ userId; - } - } } diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java index 353252d8572..a74479239ce 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java +++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -31,6 +32,7 @@ import android.view.View; import com.android.settings.applications.instantapps.InstantAppButtonsController; import com.android.settings.enterprise.DevicePolicyManagerWrapper; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -103,36 +105,34 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide } @Override - public Set findPersistentPreferredActivities( - Intent[] intents) { - final Set activities = new ArraySet<>(); - final List users = mUm.getUserProfiles(); + public List findPersistentPreferredActivities(int userId, Intent[] intents) { + final List preferredActivities = new ArrayList<>(); + final Set uniqueApps = new ArraySet<>(); + final UserInfo userInfo = mUm.getUserInfo(userId); for (final Intent intent : intents) { - for (final UserHandle user : users) { - final int userId = user.getIdentifier(); - try { - final ResolveInfo resolveInfo = mPms.findPersistentPreferredActivity(intent, - userId); - if (resolveInfo != null) { - ComponentInfo componentInfo = null; - if (resolveInfo.activityInfo != null) { - componentInfo = resolveInfo.activityInfo; - } else if (resolveInfo.serviceInfo != null) { - componentInfo = resolveInfo.serviceInfo; - } else if (resolveInfo.providerInfo != null) { - componentInfo = resolveInfo.providerInfo; - } - if (componentInfo != null) { - activities.add(new PersistentPreferredActivityInfo( - componentInfo.packageName, userId)); + try { + final ResolveInfo resolveInfo = + mPms.findPersistentPreferredActivity(intent, userId); + if (resolveInfo != null) { + ComponentInfo componentInfo = null; + if (resolveInfo.activityInfo != null) { + componentInfo = resolveInfo.activityInfo; + } else if (resolveInfo.serviceInfo != null) { + componentInfo = resolveInfo.serviceInfo; + } else if (resolveInfo.providerInfo != null) { + componentInfo = resolveInfo.providerInfo; + } + if (componentInfo != null) { + UserAppInfo info = new UserAppInfo(userInfo, componentInfo.applicationInfo); + if (uniqueApps.add(info)) { + preferredActivities.add(info); } } - } catch (RemoteException exception) { } + } catch (RemoteException exception) { } - } - return activities; + return preferredActivities; } private static class CurrentUserAndManagedProfilePolicyInstalledAppCounter diff --git a/src/com/android/settings/applications/EnterpriseDefaultApps.java b/src/com/android/settings/applications/EnterpriseDefaultApps.java new file mode 100644 index 00000000000..f48b955d229 --- /dev/null +++ b/src/com/android/settings/applications/EnterpriseDefaultApps.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 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; + +import android.content.Intent; +import android.net.Uri; +import android.provider.ContactsContract; +import android.provider.MediaStore; + +/** + * UI grouping of important intents that can be configured by device and profile owners. + */ +public enum EnterpriseDefaultApps { + BROWSER(new Intent[] { + buildIntent(Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE, "http:", null)}), + CALENDAR(new Intent[] { + buildIntent(Intent.ACTION_INSERT, null, null, "vnd.android.cursor.dir/event")}), + CAMERA(new Intent[] { + new Intent(MediaStore.ACTION_IMAGE_CAPTURE), + new Intent(MediaStore.ACTION_VIDEO_CAPTURE)}), + CONTACTS(new Intent[] { + buildIntent(Intent.ACTION_PICK, null, null, ContactsContract.Contacts.CONTENT_TYPE)}), + EMAIL(new Intent[] { + new Intent(Intent.ACTION_SENDTO), new Intent(Intent.ACTION_SEND), + new Intent(Intent.ACTION_SEND_MULTIPLE)}), + MAP(new Intent[] {buildIntent(Intent.ACTION_VIEW, null, "geo:", null)}), + PHONE(new Intent[] {new Intent(Intent.ACTION_DIAL), new Intent(Intent.ACTION_CALL)}); + private final Intent[] mIntents; + + EnterpriseDefaultApps(Intent[] intents) { + mIntents = intents; + } + + public Intent[] getIntents() { + return mIntents; + } + + private static Intent buildIntent(String action, String category, String protocol, + String type) { + final Intent intent = new Intent(action); + if (category != null) { + intent.addCategory(category); + } + if (protocol != null) { + intent.setData(Uri.parse(protocol)); + } + if (type != null) { + intent.setType(type); + } + return intent; + } + +} diff --git a/src/com/android/settings/applications/InstalledAppLister.java b/src/com/android/settings/applications/InstalledAppLister.java index 1c3b0e9b28a..d8e7c58d3d9 100644 --- a/src/com/android/settings/applications/InstalledAppLister.java +++ b/src/com/android/settings/applications/InstalledAppLister.java @@ -22,8 +22,7 @@ import android.os.UserManager; public abstract class InstalledAppLister extends AppLister { - public InstalledAppLister(PackageManagerWrapper packageManager, - UserManager userManager) { + public InstalledAppLister(PackageManagerWrapper packageManager, UserManager userManager) { super(packageManager, userManager); } diff --git a/src/com/android/settings/applications/UserAppInfo.java b/src/com/android/settings/applications/UserAppInfo.java index 3af5f19167f..04fa713daa9 100644 --- a/src/com/android/settings/applications/UserAppInfo.java +++ b/src/com/android/settings/applications/UserAppInfo.java @@ -43,9 +43,10 @@ public class UserAppInfo { if (other == null || getClass() != other.getClass()) { return false; } - final UserAppInfo that = (UserAppInfo) other; + // As UserInfo and AppInfo do not support hashcode/equals contract, assume + // equality based on corresponding identity fields. return that.userInfo.id == userInfo.id && TextUtils.equals(that.appInfo.packageName, appInfo.packageName); } diff --git a/src/com/android/settings/enterprise/ApplicationListFragment.java b/src/com/android/settings/enterprise/ApplicationListFragment.java index 9a887d8d10b..ff68a8bdb83 100644 --- a/src/com/android/settings/enterprise/ApplicationListFragment.java +++ b/src/com/android/settings/enterprise/ApplicationListFragment.java @@ -38,11 +38,6 @@ public abstract class ApplicationListFragment extends DashboardFragment static final String TAG = "EnterprisePrivacySettings"; - @Override - public int getMetricsCategory() { - return MetricsEvent.ENTERPRISE_PRIVACY_SETTINGS; - } - @Override protected String getLogTag() { return TAG; @@ -75,6 +70,11 @@ public abstract class ApplicationListFragment extends DashboardFragment FeatureFactory.getFactory(context).getApplicationFeatureProvider(context) .listAppsWithAdminGrantedPermissions(mPermissions, callback); } + + @Override + public int getMetricsCategory() { + return MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS; + } } public static class AdminGrantedPermissionCamera extends AdminGrantedPermission { @@ -100,6 +100,11 @@ public abstract class ApplicationListFragment extends DashboardFragment public EnterpriseInstalledPackages() { } + @Override + public int getMetricsCategory() { + return MetricsEvent.ENTERPRISE_PRIVACY_INSTALLED_APPS; + } + @Override public void buildApplicationList(Context context, ApplicationFeatureProvider.ListOfAppsCallback callback) { diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java index 5817cf2ae59..46ecb7e620f 100644 --- a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java +++ b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java @@ -17,10 +17,8 @@ package com.android.settings.enterprise; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java index 254940e0201..69e04164352 100644 --- a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java +++ b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java @@ -14,7 +14,6 @@ package com.android.settings.enterprise; import android.content.Context; -import android.content.res.Resources; import android.support.v7.preference.Preference; import com.android.settings.R; diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragment.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragment.java new file mode 100644 index 00000000000..6f173f17783 --- /dev/null +++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragment.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.content.Context; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.R; +import com.android.settings.core.PreferenceController; +import com.android.settings.dashboard.DashboardFragment; + +import java.util.ArrayList; +import java.util.List; + +/** + * Fragment for displaying a list of default applications set by profile or device admin. + */ +public class EnterpriseSetDefaultAppsListFragment extends DashboardFragment { + static final String TAG = "EnterprisePrivacySettings"; + + @Override + public int getMetricsCategory() { + return MetricsEvent.ENTERPRISE_PRIVACY_DEFAULT_APPS; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.enterprise_set_default_apps_settings; + } + + @Override + protected List getPreferenceControllers(Context context) { + final List controllers = new ArrayList(); + final EnterpriseSetDefaultAppsListPreferenceController controller = + new EnterpriseSetDefaultAppsListPreferenceController( + context, this, context.getPackageManager()); + controllers.add(controller); + return controllers; + } +} diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java new file mode 100644 index 00000000000..51b60b8f3b3 --- /dev/null +++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.os.Handler; +import android.os.UserHandle; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceCategory; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.applications.EnterpriseDefaultApps; +import com.android.settings.applications.UserAppInfo; +import com.android.settings.core.PreferenceController; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.users.UserFeatureProvider; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; + + +/** + * PreferenceController that builds a dynamic list of default apps set by device or profile owner. + */ +public class EnterpriseSetDefaultAppsListPreferenceController extends PreferenceController { + private final PackageManager mPm; + private final SettingsPreferenceFragment mParent; + private final ApplicationFeatureProvider mApplicationFeatureProvider; + private final EnterprisePrivacyFeatureProvider mEnterprisePrivacyFeatureProvider; + private final UserFeatureProvider mUserFeatureProvider; + + private List mUsers = Collections.emptyList(); + private List>> mApps = + Collections.emptyList(); + + public EnterpriseSetDefaultAppsListPreferenceController(Context context, + SettingsPreferenceFragment parent, PackageManager packageManager) { + super(context); + mPm = packageManager; + mParent = parent; + final FeatureFactory factory = FeatureFactory.getFactory(context); + mApplicationFeatureProvider = factory.getApplicationFeatureProvider(context); + mEnterprisePrivacyFeatureProvider = factory.getEnterprisePrivacyFeatureProvider(context); + mUserFeatureProvider = factory.getUserFeatureProvider(context); + buildAppList(); + } + + /** + * Builds data for UI. Updates mUsers and mApps so that they contain non-empty list. + */ + private void buildAppList() { + mUsers = new ArrayList<>(); + mApps = new ArrayList<>(); + for (UserHandle user : mUserFeatureProvider.getUserProfiles()) { + boolean hasDefaultsForUser = false; + EnumMap> userMap = null; + + for (EnterpriseDefaultApps typeOfDefault : EnterpriseDefaultApps.values()) { + List apps = mApplicationFeatureProvider. + findPersistentPreferredActivities(user.getIdentifier(), + typeOfDefault.getIntents()); + if (apps.isEmpty()) { + continue; + } + if (!hasDefaultsForUser) { + hasDefaultsForUser = true; + mUsers.add(apps.get(0).userInfo); + userMap = new EnumMap<>(EnterpriseDefaultApps.class); + mApps.add(userMap); + } + ArrayList applicationInfos = new ArrayList<>(); + for (UserAppInfo userAppInfo : apps) { + applicationInfos.add(userAppInfo.appInfo); + } + userMap.put(typeOfDefault, applicationInfos); + } + } + new Handler(mContext.getMainLooper()).post(() -> { updateUi(); }); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return null; + } + + private void updateUi() { + final Context prefContext = mParent.getPreferenceManager().getContext(); + final PreferenceScreen screen = mParent.getPreferenceScreen(); + if (screen == null) { + return; + } + if (!mEnterprisePrivacyFeatureProvider.isInCompMode() && mUsers.size() == 1) { + createPreferences(prefContext, screen, mApps.get(0)); + } else { + for (int i = 0; i < mUsers.size(); i++) { + final UserInfo userInfo = mUsers.get(i); + final PreferenceCategory category = new PreferenceCategory(prefContext); + screen.addPreference(category); + if (userInfo.isManagedProfile()) { + category.setTitle(R.string.managed_device_admin_title); + } else { + category.setTitle(R.string.personal_device_admin_title); + } + category.setOrder(i); + createPreferences(prefContext, category, mApps.get(i)); + } + } + } + + private void createPreferences(Context prefContext, PreferenceGroup group, + EnumMap> apps) { + if (group == null) { + return; + } + for (EnterpriseDefaultApps typeOfDefault : EnterpriseDefaultApps.values()) { + final List appsForCategory = apps.get(typeOfDefault); + if (appsForCategory == null || appsForCategory.isEmpty()) { + continue; + } + final Preference preference = new Preference(prefContext); + preference.setTitle(getTitle(prefContext, typeOfDefault, appsForCategory.size())); + preference.setSummary(buildSummaryString(prefContext, appsForCategory)); + preference.setOrder(typeOfDefault.ordinal()); + preference.setSelectable(false); + group.addPreference(preference); + } + } + + private CharSequence buildSummaryString(Context context, List apps) { + final CharSequence[] appNames = new String[apps.size()]; + for (int i = 0; i < apps.size(); i++) { + appNames[i] = apps.get(i).loadLabel(mPm); + } + if (apps.size() == 1) { + return appNames[0]; + } else if (apps.size() == 2) { + return context.getString(R.string.app_names_concatenation_template_2, appNames[0], + appNames[1]); + } else { + return context.getString(R.string.app_names_concatenation_template_3, appNames[0], + appNames[1], appNames[2]); + } + } + + private String getTitle(Context context, EnterpriseDefaultApps typeOfDefault, int appCount) { + switch (typeOfDefault) { + case BROWSER: + return context.getString(R.string.default_browser_title); + case CALENDAR: + return context.getString(R.string.default_calendar_app_title); + case CONTACTS: + return context.getString(R.string.default_contacts_app_title); + case PHONE: + return context.getResources() + .getQuantityString(R.plurals.default_phone_app_title, appCount); + case MAP: + return context.getString(R.string.default_map_app_title); + case EMAIL: + return context.getResources() + .getQuantityString(R.plurals.default_email_app_title, appCount); + case CAMERA: + return context.getResources() + .getQuantityString(R.plurals.default_camera_app_title, appCount); + default: + throw new IllegalStateException("Unknown type of default " + typeOfDefault); + } + } + +} diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java index ae76d63e43a..2f43a618c77 100644 --- a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java +++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java @@ -14,28 +14,29 @@ package com.android.settings.enterprise; import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.provider.ContactsContract; -import android.provider.MediaStore; +import android.os.UserHandle; import android.support.v7.preference.Preference; import com.android.settings.R; import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.applications.EnterpriseDefaultApps; import com.android.settings.core.DynamicAvailabilityPreferenceController; import com.android.settings.core.lifecycle.Lifecycle; import com.android.settings.overlay.FeatureFactory; +import com.android.settings.users.UserFeatureProvider; public class EnterpriseSetDefaultAppsPreferenceController extends DynamicAvailabilityPreferenceController { private static final String KEY_DEFAULT_APPS = "number_enterprise_set_default_apps"; - private final ApplicationFeatureProvider mFeatureProvider; + private final ApplicationFeatureProvider mApplicationFeatureProvider; + private final UserFeatureProvider mUserFeatureProvider; public EnterpriseSetDefaultAppsPreferenceController(Context context, Lifecycle lifecycle) { super(context, lifecycle); - mFeatureProvider = FeatureFactory.getFactory(context) - .getApplicationFeatureProvider(context); + final FeatureFactory factory = FeatureFactory.getFactory(context); + mApplicationFeatureProvider = factory.getApplicationFeatureProvider(context); + mUserFeatureProvider = factory.getUserFeatureProvider(context); } @Override @@ -56,47 +57,14 @@ public class EnterpriseSetDefaultAppsPreferenceController } private int getNumberOfEnterpriseSetDefaultApps() { - // Browser - int num = mFeatureProvider.findPersistentPreferredActivities(new Intent[] { - buildIntent(Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE, "http:", null)}).size(); - // Camera - num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] { - new Intent(MediaStore.ACTION_IMAGE_CAPTURE), - new Intent(MediaStore.ACTION_VIDEO_CAPTURE)}).size(); - // Map - num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] { - buildIntent(Intent.ACTION_VIEW, null, "geo:", null)}).size(); - // E-mail - num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] { - new Intent(Intent.ACTION_SENDTO), new Intent(Intent.ACTION_SEND), - new Intent(Intent.ACTION_SEND_MULTIPLE)}).size(); - // Calendar - num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] { - buildIntent(Intent.ACTION_INSERT, null, null, "vnd.android.cursor.dir/event")}) - .size(); - // Contacts - num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] { - buildIntent(Intent.ACTION_PICK, null, null, - ContactsContract.Contacts.CONTENT_TYPE)}).size(); - // Dialer - num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] { - new Intent(Intent.ACTION_DIAL), new Intent(Intent.ACTION_CALL)}).size(); - + int num = 0; + for (UserHandle user : mUserFeatureProvider.getUserProfiles()) { + for (EnterpriseDefaultApps app : EnterpriseDefaultApps.values()) { + num += mApplicationFeatureProvider + .findPersistentPreferredActivities(user.getIdentifier(), + app.getIntents()).size(); + } + } return num; } - - private static Intent buildIntent(String action, String category, String protocol, - String type) { - final Intent intent = new Intent(action); - if (category != null) { - intent.addCategory(category); - } - if (protocol != null) { - intent.setData(Uri.parse(protocol)); - } - if (type != null) { - intent.setType(type); - } - return intent; - } } diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index dcd2b51817a..e7d50883803 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -31,6 +31,7 @@ import com.android.settings.gestures.AssistGestureFeatureProvider; import com.android.settings.localepicker.LocaleFeatureProvider; import com.android.settings.security.SecurityFeatureProvider; import com.android.settings.search2.SearchFeatureProvider; +import com.android.settings.users.UserFeatureProvider; /** * Abstract class for creating feature controllers. Allows OEM implementations to define their own @@ -94,6 +95,8 @@ public abstract class FeatureFactory { public abstract SecurityFeatureProvider getSecurityFeatureProvider(); + public abstract UserFeatureProvider getUserFeatureProvider(Context context); + public static final class FactoryNotFoundException extends RuntimeException { public FactoryNotFoundException(Throwable throwable) { super("Unable to create factory. Did you misconfigure Proguard?", throwable); diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index 5b039b20f34..b39a2a096df 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -45,6 +45,8 @@ import com.android.settings.search2.SearchFeatureProvider; import com.android.settings.search2.SearchFeatureProviderImpl; import com.android.settings.security.SecurityFeatureProvider; import com.android.settings.security.SecurityFeatureProviderImpl; +import com.android.settings.users.UserFeatureProvider; +import com.android.settings.users.UserFeatureProviderImpl; import com.android.settings.vpn2.ConnectivityManagerWrapperImpl; /** @@ -63,6 +65,7 @@ public class FeatureFactoryImpl extends FeatureFactory { private SuggestionFeatureProvider mSuggestionFeatureProvider; private PowerUsageFeatureProvider mPowerUsageFeatureProvider; private AssistGestureFeatureProvider mAssistGestureFeatureProvider; + private UserFeatureProvider mUserFeatureProvider; @Override public SupportFeatureProvider getSupportFeatureProvider(Context context) { @@ -157,6 +160,14 @@ public class FeatureFactoryImpl extends FeatureFactory { return mSuggestionFeatureProvider; } + @Override + public UserFeatureProvider getUserFeatureProvider(Context context) { + if (mUserFeatureProvider == null) { + mUserFeatureProvider = new UserFeatureProviderImpl(context); + } + return mUserFeatureProvider; + } + @Override public AssistGestureFeatureProvider getAssistGestureFeatureProvider() { if (mAssistGestureFeatureProvider == null) { diff --git a/src/com/android/settings/users/UserFeatureProvider.java b/src/com/android/settings/users/UserFeatureProvider.java new file mode 100644 index 00000000000..9853255a2e8 --- /dev/null +++ b/src/com/android/settings/users/UserFeatureProvider.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 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.users; + +import android.os.UserHandle; + +import java.util.List; + +public interface UserFeatureProvider { + /** + * Returns a list of UserHandles for profiles associated with the user that the calling process + * is running on, including the user itself. + * + * @return A non-empty list of UserHandles associated with the calling user. + */ + List getUserProfiles(); +} diff --git a/src/com/android/settings/users/UserFeatureProviderImpl.java b/src/com/android/settings/users/UserFeatureProviderImpl.java new file mode 100644 index 00000000000..b88c831b865 --- /dev/null +++ b/src/com/android/settings/users/UserFeatureProviderImpl.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 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.users; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; + +import java.util.List; + +public class UserFeatureProviderImpl implements UserFeatureProvider { + UserManager mUm; + + public UserFeatureProviderImpl(Context context) { + mUm = (UserManager) context.getSystemService(Context.USER_SERVICE); + } + + @Override + public List getUserProfiles() { + return mUm.getUserProfiles(); + } +} diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider index c043f1bf54c..767edf8e046 100644 --- a/tests/robotests/assets/grandfather_not_implementing_index_provider +++ b/tests/robotests/assets/grandfather_not_implementing_index_provider @@ -9,3 +9,4 @@ com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCa com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionLocation com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMicrophone com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages +com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java index cb68ccb4f13..4c4ec46cce6 100644 --- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java @@ -20,13 +20,13 @@ import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.os.Build; import android.os.UserHandle; import android.os.UserManager; -import android.util.ArraySet; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; @@ -42,9 +42,9 @@ import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Set; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; @@ -200,8 +200,14 @@ public final class ApplicationFeatureProviderImplTest { @Test public void testFindPersistentPreferredActivities() throws Exception { + final UserInfo mainUser = new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN); + final UserInfo managedUser = new UserInfo(MANAGED_PROFILE_ID, "managed", + UserInfo.FLAG_MANAGED_PROFILE); + when(mUserManager.getUserProfiles()).thenReturn(Arrays.asList(new UserHandle(MAIN_USER_ID), new UserHandle(MANAGED_PROFILE_ID))); + when(mUserManager.getUserInfo(MAIN_USER_ID)).thenReturn(mainUser); + when(mUserManager.getUserInfo(MANAGED_PROFILE_ID)).thenReturn(managedUser); final Intent viewIntent = new Intent(Intent.ACTION_VIEW); final Intent editIntent = new Intent(Intent.ACTION_EDIT); @@ -222,17 +228,21 @@ public final class ApplicationFeatureProviderImplTest { when(mPackageManagerService.findPersistentPreferredActivity(sendIntent, MANAGED_PROFILE_ID)) .thenReturn(null); - final Set expectedActivities - = new ArraySet<>(); - expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_1, - MAIN_USER_ID)); - expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_1, - MANAGED_PROFILE_ID)); - expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_2, - MANAGED_PROFILE_ID)); + final List expectedMainUserActivities = new ArrayList<>(); + expectedMainUserActivities.add(new UserAppInfo(mainUser, + new ApplicationInfo(app1.activityInfo.applicationInfo))); + final List expectedManagedUserActivities = new ArrayList<>(); + expectedManagedUserActivities.add(new UserAppInfo(managedUser, + new ApplicationInfo(app1.activityInfo.applicationInfo))); + expectedManagedUserActivities.add(new UserAppInfo(managedUser, + new ApplicationInfo(app2.activityInfo.applicationInfo))); - assertThat(mProvider.findPersistentPreferredActivities( - new Intent[] {viewIntent, editIntent, sendIntent})).isEqualTo(expectedActivities); + assertThat(mProvider.findPersistentPreferredActivities(MAIN_USER_ID, + new Intent[] {viewIntent, editIntent, sendIntent})) + .isEqualTo(expectedMainUserActivities); + assertThat(mProvider.findPersistentPreferredActivities(MANAGED_PROFILE_ID, + new Intent[] {viewIntent, editIntent, sendIntent})) + .isEqualTo(expectedManagedUserActivities); } private void setUpUsersAndInstalledApps() { @@ -254,8 +264,11 @@ public final class ApplicationFeatureProviderImplTest { } private ResolveInfo createResolveInfo(String packageName) { + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.packageName = packageName; final ActivityInfo activityInfo = new ActivityInfo(); activityInfo.packageName = packageName; + activityInfo.applicationInfo = applicationInfo; final ResolveInfo resolveInfo = new ResolveInfo(); resolveInfo.activityInfo = activityInfo; return resolveInfo; diff --git a/tests/robotests/src/com/android/settings/applications/EnterpriseDefaultAppsTest.java b/tests/robotests/src/com/android/settings/applications/EnterpriseDefaultAppsTest.java new file mode 100644 index 00000000000..ad05c2752b2 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/EnterpriseDefaultAppsTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 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; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import static junit.framework.Assert.assertTrue; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class EnterpriseDefaultAppsTest { + @Test + public void testNumberOfIntentsCorrelateWithUI() throws Exception { + final int concatenation_templates[] = + new int[]{0 /* no need for single app name */, + R.string.app_names_concatenation_template_2, + R.string.app_names_concatenation_template_3}; + for (EnterpriseDefaultApps app : EnterpriseDefaultApps.values()) { + assertTrue("Number of intents should be limited by number of apps the UI can show", + app.getIntents().length <= concatenation_templates.length); + } + } +} diff --git a/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java b/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java index 5fbaf51216e..1936f807eb1 100644 --- a/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java +++ b/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java @@ -70,12 +70,6 @@ public class ApplicationListFragmentTest { mFragment = new ApplicationListFragmentTestable(mPreferenceManager, mScreen); } - @Test - public void getMetricsCategory() { - assertThat(mFragment.getMetricsCategory()) - .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_SETTINGS); - } - @Test public void getLogTag() { assertThat(mFragment.getLogTag()) @@ -98,6 +92,17 @@ public class ApplicationListFragmentTest { ApplicationListPreferenceController.class); } + @Test public void getCategories() { + assertThat(new ApplicationListFragment.AdminGrantedPermissionCamera().getMetricsCategory()) + .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS); + assertThat(new ApplicationListFragment.AdminGrantedPermissionLocation(). + getMetricsCategory()).isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS); + assertThat(new ApplicationListFragment.AdminGrantedPermissionMicrophone(). + getMetricsCategory()).isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS); + assertThat(new ApplicationListFragment.EnterpriseInstalledPackages().getMetricsCategory()) + .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_INSTALLED_APPS); + } + private static class ApplicationListFragmentTestable extends ApplicationListFragment { private final PreferenceManager mPreferenceManager; @@ -127,5 +132,10 @@ public class ApplicationListFragmentTest { public PreferenceScreen getPreferenceScreen() { return mPreferenceScreen; } + + @Override + public int getMetricsCategory() { + return MetricsEvent.VIEW_UNKNOWN; + } } } diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragmentTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragmentTest.java new file mode 100644 index 00000000000..e5c877fd907 --- /dev/null +++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListFragmentTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.content.Context; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceScreen; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.core.PreferenceController; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; + +import java.util.List; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Answers.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class EnterpriseSetDefaultAppsListFragmentTest { + @Mock(answer = RETURNS_DEEP_STUBS) + private PreferenceScreen mScreen; + @Mock(answer = RETURNS_DEEP_STUBS) + private PreferenceManager mPreferenceManager; + + private EnterpriseSetDefaultAppsListFragment mFragment; + private Context mContext; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = ShadowApplication.getInstance().getApplicationContext(); + when(mPreferenceManager.getContext()).thenReturn(mContext); + when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager); + mFragment = new EnterpriseSetDefaultAppsListFragmentTestable(mPreferenceManager, mScreen); + } + + @Test + public void getMetricsCategory() { + assertThat(mFragment.getMetricsCategory()) + .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_DEFAULT_APPS); + } + + @Test + public void getLogTag() { + assertThat(mFragment.getLogTag()).isEqualTo("EnterprisePrivacySettings"); + } + + @Test + public void getScreenResource() { + assertThat(mFragment.getPreferenceScreenResId()) + .isEqualTo(R.xml.enterprise_set_default_apps_settings); + } + + @Test + public void getPreferenceControllers() { + final List controllers = mFragment.getPreferenceControllers(mContext); + assertThat(controllers).isNotNull(); + assertThat(controllers.size()).isEqualTo(1); + int position = 0; + assertThat(controllers.get(position++)).isInstanceOf( + EnterpriseSetDefaultAppsListPreferenceController.class); + } + + private static class EnterpriseSetDefaultAppsListFragmentTestable extends + EnterpriseSetDefaultAppsListFragment { + + private final PreferenceManager mPreferenceManager; + private final PreferenceScreen mPreferenceScreen; + + public EnterpriseSetDefaultAppsListFragmentTestable(PreferenceManager preferenceManager, + PreferenceScreen screen) { + this.mPreferenceManager = preferenceManager; + this.mPreferenceScreen = screen; + } + + @Override + public PreferenceManager getPreferenceManager() { + return mPreferenceManager; + } + + @Override + public PreferenceScreen getPreferenceScreen() { + return mPreferenceScreen; + } + } + +} diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceControllerTest.java new file mode 100644 index 00000000000..6a1a7f76db5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceControllerTest.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.os.UserHandle; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.applications.EnterpriseDefaultApps; +import com.android.settings.applications.UserAppInfo; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.testutils.ApplicationTestUtils; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; + +import java.util.Arrays; +import java.util.Collections; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Answers.RETURNS_DEEP_STUBS; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class EnterpriseSetDefaultAppsListPreferenceControllerTest { + private static final int USER_ID = 0; + private static final int APP_UID = 0; + + private static final String APP_1 = "APP_1"; + private static final String APP_2 = "APP_2"; + private static final String BROWSER_TITLE = "Browser app"; + private static final String PHONE_TITLE = "Phone apps"; + + @Mock(answer = RETURNS_DEEP_STUBS) + private PreferenceScreen mScreen; + @Mock(answer = RETURNS_DEEP_STUBS) + private PreferenceManager mPrefenceManager; + @Mock(answer = RETURNS_DEEP_STUBS) + private PackageManager mPackageManager; + @Mock(answer = RETURNS_DEEP_STUBS) + private SettingsPreferenceFragment mFragment; + + private Context mContext; + private FakeFeatureFactory mFeatureFactory; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + ShadowApplication shadowContext = ShadowApplication.getInstance(); + mContext = spy(shadowContext.getApplicationContext()); + FakeFeatureFactory.setupForTest(mContext); + mFeatureFactory = (FakeFeatureFactory) FeatureFactory.getFactory(mContext); + when(mFragment.getPreferenceScreen()).thenReturn(mScreen); + when(mPrefenceManager.getContext()).thenReturn(mContext); + when(mFragment.getPreferenceManager()).thenReturn(mPrefenceManager); + + when(mContext.getString(R.string.default_browser_title)).thenReturn(BROWSER_TITLE); + Resources resources = spy(mContext.getResources()); + when(mContext.getResources()).thenReturn(resources); + when(resources.getQuantityString(R.plurals.default_phone_app_title, 2)) + .thenReturn(PHONE_TITLE); + when(mContext.getString(R.string.app_names_concatenation_template_2)) + .thenReturn("%1$s, %2$s"); + + when(mPackageManager.getText(eq(APP_1), anyInt(), any())).thenReturn(APP_1); + when(mPackageManager.getText(eq(APP_2), anyInt(), any())).thenReturn(APP_2); + } + + @Test + public void testMultipleAppsForOneTypeOfDefault() { + final UserInfo user = new UserInfo(USER_ID, "main", UserInfo.FLAG_ADMIN); + final ApplicationInfo appInfo1 = ApplicationTestUtils.buildInfo(APP_UID, APP_1, 0, 0); + final ApplicationInfo appInfo2 = ApplicationTestUtils.buildInfo(APP_UID, APP_2, 0, 0); + + when(mFeatureFactory.userFeatureProvider.getUserProfiles()) + .thenReturn(Arrays.asList(new UserHandle(USER_ID))); + when(mFeatureFactory.enterprisePrivacyFeatureProvider.isInCompMode()).thenReturn(false); + when(mFeatureFactory.applicationFeatureProvider + .findPersistentPreferredActivities(anyInt(), any())) + .thenReturn(Collections.emptyList()); + when(mFeatureFactory.applicationFeatureProvider + .findPersistentPreferredActivities(eq(USER_ID), + eq(EnterpriseDefaultApps.BROWSER.getIntents()))) + .thenReturn(Arrays.asList(new UserAppInfo(user, appInfo1))); + when(mFeatureFactory.applicationFeatureProvider + .findPersistentPreferredActivities(eq(USER_ID), + eq(EnterpriseDefaultApps.PHONE.getIntents()))).thenReturn( + Arrays.asList(new UserAppInfo(user, appInfo1), + new UserAppInfo(user, appInfo2))); + + new EnterpriseSetDefaultAppsListPreferenceController(mContext, mFragment, mPackageManager); + ShadowApplication.runBackgroundTasks(); + + ArgumentCaptor apps = ArgumentCaptor.forClass(Preference.class); + verify(mScreen, times(2)).addPreference(apps.capture()); + + assertThat(apps.getAllValues().get(0).getTitle()).isEqualTo(BROWSER_TITLE); + assertThat(apps.getAllValues().get(0).getSummary()).isEqualTo(APP_1); + + assertThat(apps.getAllValues().get(1).getTitle()).isEqualTo(PHONE_TITLE); + assertThat(apps.getAllValues().get(1).getSummary()).isEqualTo(APP_1 + ", " + APP_2); + } + + @Test + public void isAvailable() { + when(mFeatureFactory.userFeatureProvider.getUserProfiles()) + .thenReturn(Arrays.asList(new UserHandle(USER_ID))); + when(mFeatureFactory.applicationFeatureProvider + .findPersistentPreferredActivities(anyInt(), any())) + .thenReturn(Collections.emptyList()); + final EnterpriseSetDefaultAppsListPreferenceController controller = + new EnterpriseSetDefaultAppsListPreferenceController(mContext, mFragment, + mPackageManager); + assertThat(controller.isAvailable()).isTrue(); + } + + @Test + public void getPreferenceKey() { + when(mFeatureFactory.userFeatureProvider.getUserProfiles()) + .thenReturn(Arrays.asList(new UserHandle(USER_ID))); + when(mFeatureFactory.applicationFeatureProvider + .findPersistentPreferredActivities(anyInt(), any())) + .thenReturn(Collections.emptyList()); + final EnterpriseSetDefaultAppsListPreferenceController controller = + new EnterpriseSetDefaultAppsListPreferenceController(mContext, mFragment, + mPackageManager); + assertThat(controller.getPreferenceKey()).isNull(); + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java index 3455e80dc1c..34d9b244f85 100644 --- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java @@ -18,32 +18,35 @@ package com.android.settings.enterprise; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; -import android.net.Uri; -import android.provider.ContactsContract; -import android.provider.MediaStore; +import android.content.pm.ApplicationInfo; +import android.content.pm.UserInfo; +import android.os.UserHandle; +import android.os.UserManager; import android.support.v7.preference.Preference; -import android.util.ArraySet; import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; -import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.applications.EnterpriseDefaultApps; +import com.android.settings.applications.UserAppInfo; import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentMatcher; import org.mockito.Answers; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.when; @@ -56,6 +59,8 @@ public final class EnterpriseSetDefaultAppsPreferenceControllerTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private UserManager mUm; private FakeFeatureFactory mFeatureFactory; private EnterpriseSetDefaultAppsPreferenceController mController; @@ -69,50 +74,40 @@ public final class EnterpriseSetDefaultAppsPreferenceControllerTest { null /* lifecycle */); } - private static Intent buildIntent(String action, String category, String protocol, - String type) { - final Intent intent = new Intent(action); - if (category != null) { - intent.addCategory(category); + private void setEnterpriseSetDefaultApps(Intent[] intents, int number) { + final ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.packageName = "app"; + for (int i = 0; i < number; i++) { + final List apps = new ArrayList<>(number); + apps.add(new UserAppInfo(new UserInfo(i, "user." + i, UserInfo.FLAG_ADMIN), appInfo)); + when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(eq(i), + argThat(new MatchesIntents(intents)))).thenReturn(apps); } - if (protocol != null) { - intent.setData(Uri.parse(protocol)); - } - if (type != null) { - intent.setType(type); - } - return intent; } - private void setEnterpriseSetDefaultApps(Intent[] intents, int number) { - final Set apps - = new ArraySet<>(number); - for (int i = 0; i < number; i++) { - apps.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo("app", i)); + private void configureUsers(int number) { + final List users = new ArrayList<>(number); + for (int i = 0; i < 64; i++) { + users.add(new UserHandle(i)); } - when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities( - argThat(new MatchesIntents(intents)))).thenReturn(apps); + when(mFeatureFactory.userFeatureProvider.getUserProfiles()).thenReturn(users); } @Test public void testUpdateState() { - setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_VIEW, - Intent.CATEGORY_BROWSABLE, "http:", null)}, 1); - setEnterpriseSetDefaultApps(new Intent[] {new Intent(MediaStore.ACTION_IMAGE_CAPTURE), - new Intent(MediaStore.ACTION_VIDEO_CAPTURE)}, 2); - setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_VIEW, null, "geo:", - null)}, 4); - setEnterpriseSetDefaultApps(new Intent[] {new Intent(Intent.ACTION_SENDTO), - new Intent(Intent.ACTION_SEND), new Intent(Intent.ACTION_SEND_MULTIPLE)}, 8); - setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_INSERT, null, null, - "vnd.android.cursor.dir/event")}, 16); - setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_PICK, null, null, - ContactsContract.Contacts.CONTENT_TYPE)}, 32); - setEnterpriseSetDefaultApps(new Intent[] {new Intent(Intent.ACTION_DIAL), - new Intent(Intent.ACTION_CALL)}, 64); + setEnterpriseSetDefaultApps(EnterpriseDefaultApps.BROWSER.getIntents(), 1); + setEnterpriseSetDefaultApps(EnterpriseDefaultApps.CAMERA.getIntents(), 2); + setEnterpriseSetDefaultApps(EnterpriseDefaultApps.MAP.getIntents(), 4); + setEnterpriseSetDefaultApps(EnterpriseDefaultApps.EMAIL.getIntents(), 8); + setEnterpriseSetDefaultApps(EnterpriseDefaultApps.CALENDAR.getIntents(), 16); + setEnterpriseSetDefaultApps(EnterpriseDefaultApps.CONTACTS.getIntents(), 32); + setEnterpriseSetDefaultApps(EnterpriseDefaultApps.PHONE.getIntents(), 64); when(mContext.getResources().getQuantityString(R.plurals.enterprise_privacy_number_packages, 127, 127)).thenReturn("127 apps"); + // As setEnterpriseSetDefaultApps uses fake Users, we need to list them via UserManager. + configureUsers(64); + final Preference preference = new Preference(mContext, null, 0, 0); mController.updateState(preference); assertThat(preference.getSummary()).isEqualTo("127 apps"); @@ -120,13 +115,12 @@ public final class EnterpriseSetDefaultAppsPreferenceControllerTest { @Test public void testIsAvailable() { - when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities( - anyObject())).thenReturn( - new ArraySet()); + when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(anyInt(), + anyObject())).thenReturn(new ArrayList()); assertThat(mController.isAvailable()).isFalse(); - setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_VIEW, - Intent.CATEGORY_BROWSABLE, "http:", null)}, 1); + setEnterpriseSetDefaultApps(EnterpriseDefaultApps.BROWSER.getIntents(), 1); + configureUsers(1); assertThat(mController.isAvailable()).isTrue(); } diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index f4f1c632f58..68333e7f834 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -30,6 +30,7 @@ import com.android.settings.overlay.SupportFeatureProvider; import com.android.settings.security.SecurityFeatureProvider; import com.android.settings.search2.SearchFeatureProvider; import com.android.settings.overlay.SurveyFeatureProvider; +import com.android.settings.users.UserFeatureProvider; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@ -52,6 +53,7 @@ public class FakeFeatureFactory extends FeatureFactory { public final SurveyFeatureProvider surveyFeatureProvider; public final SecurityFeatureProvider securityFeatureProvider; public final SuggestionFeatureProvider suggestionsFeatureProvider; + public final UserFeatureProvider userFeatureProvider; public final AssistGestureFeatureProvider assistGestureFeatureProvider; /** @@ -86,6 +88,7 @@ public class FakeFeatureFactory extends FeatureFactory { surveyFeatureProvider = mock(SurveyFeatureProvider.class); securityFeatureProvider = mock(SecurityFeatureProvider.class); suggestionsFeatureProvider = mock(SuggestionFeatureProvider.class); + userFeatureProvider = mock(UserFeatureProvider.class); assistGestureFeatureProvider = mock(AssistGestureFeatureProvider.class); } @@ -144,6 +147,11 @@ public class FakeFeatureFactory extends FeatureFactory { return securityFeatureProvider; } + @Override + public UserFeatureProvider getUserFeatureProvider(Context context) { + return userFeatureProvider; + } + @Override public AssistGestureFeatureProvider getAssistGestureFeatureProvider() { return assistGestureFeatureProvider; diff --git a/tests/robotests/src/com/android/settings/users/UserFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/users/UserFeatureProviderImplTest.java new file mode 100644 index 00000000000..c794cdd36a2 --- /dev/null +++ b/tests/robotests/src/com/android/settings/users/UserFeatureProviderImplTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 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.users; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import java.util.Arrays; +import java.util.List; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class UserFeatureProviderImplTest { + public static final int FIRST_USER_ID = 0; + public static final int SECOND_USER_ID = 4; + + @Mock + private Context mContext; + @Mock + private UserManager mUserManager; + + private UserFeatureProviderImpl mFeatureProvider; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + mFeatureProvider = new UserFeatureProviderImpl(mContext); + } + + @Test + public void getUserProfiles() { + final List expected = + Arrays.asList(new UserHandle(FIRST_USER_ID), new UserHandle(SECOND_USER_ID)); + when(mUserManager.getUserProfiles()).thenReturn(expected); + final List userProfiles = mFeatureProvider.getUserProfiles(); + assertThat(userProfiles).isEqualTo(expected); + } +}