Merge "Disable "Verify apps over usb" according to user restriction." into oc-dev
diff --git a/res/layout/styled_switch_bar.xml b/res/layout/styled_switch_bar.xml
new file mode 100644
index 0000000..dd61907
--- /dev/null
+++ b/res/layout/styled_switch_bar.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- For use in a LayoutPreference -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="0px"
+ android:layout_width="match_parent"
+ android:layout_weight="1" >
+
+ <com.android.settings.widget.SwitchBar
+ android:id="@+id/switch_bar"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_width="match_parent"
+ android:paddingStart="0dp"
+ android:background="@drawable/switchbar_background"
+ android:theme="?attr/switchBarTheme"
+ />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b255123..848d2eb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6558,7 +6558,10 @@
<!-- Notification Settings: Title for the option managing notifications per application. [CHAR LIMIT=30] -->
<string name="app_notifications_title">Notifications</string>
- <!-- [CHAR LIMIT=100] Notification importance slider title -->
+ <!-- [CHAR LIMIT=100] Notification channel title -->
+ <string name="notification_channel_title">Notification category</string>
+
+ <!-- [CHAR LIMIT=100] Notification importance screen title -->
<string name="notification_importance_title">Importance</string>
<!-- [CHAR LIMIT=100] Notification Importance: unspecified importance level description -->
@@ -6579,17 +6582,17 @@
<!-- [CHAR LIMIT=100] Notification Importance: high importance level description -->
<string name="notification_importance_high">Make sound and pop on screen</string>
- <!-- [CHAR LIMIT=100] Notification Importance summary: min importance level description -->
- <string name="notification_importance_min_summary">Low: No sound or visual interruption</string>
+ <!-- [CHAR LIMIT=100] Notification Importance title: min importance level title -->
+ <string name="notification_importance_min_title">Low</string>
- <!-- [CHAR LIMIT=100] Notification Importance summary: low importance level description -->
- <string name="notification_importance_low_summary">Medium: No sound</string>
+ <!-- [CHAR LIMIT=100] Notification Importance title: low importance level title -->
+ <string name="notification_importance_low_title">Medium</string>
- <!-- [CHAR LIMIT=100] Notification Importance summary: normal importance level description -->
- <string name="notification_importance_default_summary">High: Make sound</string>
+ <!-- [CHAR LIMIT=100] Notification Importance title: normal importance level title -->
+ <string name="notification_importance_default_title">High</string>
- <!-- [CHAR LIMIT=100] Notification Importance summary: high importance level description -->
- <string name="notification_importance_high_summary">Urgent: Make sound and pop on screen</string>
+ <!-- [CHAR LIMIT=100] Notification Importance title: high importance level title -->
+ <string name="notification_importance_high_title">Urgent</string>
<!-- [CHAR LIMIT=40] Notification importance title -->
<string name="allow_sound">Allow Sound</string>
@@ -6694,6 +6697,12 @@
<!-- [CHAR LIMIT=NONE] Text when loading app list in notification settings -->
<string name="loading_notification_apps">Loading apps...</string>
+ <!-- [CHAR LIMIT=NONE] Text appearing when app notifications are off -->
+ <string name="app_notifications_off_desc">Android is blocking this app\'s notifications from appearing on this device</string>
+
+ <!-- [CHAR LIMIT=NONE] Text appearing when channel notifications are off -->
+ <string name="channel_notifications_off_desc">Android is blocking this category of notifications from appearing on this device</string>
+
<!-- [CHAR LIMIT=NONE] App notification settings: channels title -->
<string name="notification_channels">Categories</string>
diff --git a/res/xml/app_list_disclosure_settings.xml b/res/xml/app_list_disclosure_settings.xml
new file mode 100644
index 0000000..2aae95e
--- /dev/null
+++ b/res/xml/app_list_disclosure_settings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+ android:key="app_list_disclosure_settings">
+ <PreferenceCategory
+ android:key="dashboard_tile_placeholder"/>
+
+ <com.android.settings.widget.FooterPreference
+ android:title="@string/enterprise_privacy_apps_count_estimation_info"
+ android:selectable="false"/>
+</PreferenceScreen>
diff --git a/res/xml/app_notification_settings.xml b/res/xml/app_notification_settings.xml
index ef8a45c..bfb91ef 100644
--- a/res/xml/app_notification_settings.xml
+++ b/res/xml/app_notification_settings.xml
@@ -18,13 +18,6 @@
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:title="@string/app_notifications_title"
android:key="app_notification_settings">
- <!-- Block -->
- <com.android.settingslib.RestrictedSwitchPreference
- android:key="block"
- android:title="@string/app_notification_block_title"
- android:summary="@string/app_notification_block_summary"
- settings:useAdditionalSummary="true"
- settings:restrictedSwitchSummary="@string/enabled_by_admin" />
<!-- Show badge -->
<com.android.settingslib.RestrictedSwitchPreference
diff --git a/res/xml/channel_notification_settings.xml b/res/xml/channel_notification_settings.xml
index ccaa724..7e5045a 100644
--- a/res/xml/channel_notification_settings.xml
+++ b/res/xml/channel_notification_settings.xml
@@ -18,16 +18,7 @@
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" >
<!-- Importance -->
- <!-- Block -->
- <com.android.settingslib.RestrictedSwitchPreference
- android:key="block"
- android:title="@string/app_notification_block_title"
- android:summary="@string/app_notification_block_summary"
- settings:useAdditionalSummary="true"
- settings:restrictedSwitchSummary="@string/enabled_by_admin" />
-
- <!-- Importance -->
- <com.android.settings.notification.RestrictedDropDownPreference
+ <Preference
android:key="importance"
android:title="@string/notification_importance_title" />
diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml
index c2b77b5..dae3b42 100644
--- a/res/xml/enterprise_privacy_settings.xml
+++ b/res/xml/enterprise_privacy_settings.xml
@@ -16,8 +16,8 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
- android:key="enterprise_privacy_settings"
- android:title="@string/enterprise_privacy_settings">
+ android:key="enterprise_privacy_settings"
+ android:title="@string/enterprise_privacy_settings">
<!-- Header -->
<Preference
@@ -46,18 +46,23 @@
<PreferenceCategory android:title="@string/enterprise_privacy_exposure_changes_category">
<com.android.settings.DividerPreference
+ android:fragment="com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages"
android:key="number_enterprise_installed_packages"
android:title="@string/enterprise_privacy_enterprise_installed_packages"/>
<com.android.settings.DividerPreference
+ android:fragment="com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionLocation"
android:key="enterprise_privacy_number_location_access_packages"
android:title="@string/enterprise_privacy_location_access"/>
<com.android.settings.DividerPreference
+ android:fragment="com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMicrophone"
android:key="enterprise_privacy_number_microphone_access_packages"
android:title="@string/enterprise_privacy_microphone_access"/>
<com.android.settings.DividerPreference
+ android:fragment="com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera"
android:key="enterprise_privacy_number_camera_access_packages"
android:title="@string/enterprise_privacy_camera_access"/>
<com.android.settings.DividerPreference
+ android:fragment="com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment"
android:key="number_enterprise_set_default_apps"
android:title="@string/enterprise_privacy_enterprise_set_default_apps"/>
<com.android.settings.DividerPreference
diff --git a/res/xml/enterprise_set_default_apps_settings.xml b/res/xml/enterprise_set_default_apps_settings.xml
new file mode 100644
index 0000000..b006f46
--- /dev/null
+++ b/res/xml/enterprise_set_default_apps_settings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+ android:key="enterprise_set_default_apps_settings">
+ <PreferenceCategory
+ android:key="dashboard_tile_placeholder"/>
+</PreferenceScreen>
diff --git a/res/xml/notification_importance.xml b/res/xml/notification_importance.xml
new file mode 100644
index 0000000..f801f3c
--- /dev/null
+++ b/res/xml/notification_importance.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/notification_importance_title">
+
+ <com.android.settings.widget.RadioButtonPreference
+ android:key="importance_high"
+ android:title="@string/notification_importance_high_title"
+ android:summary="@string/notification_importance_high"
+ />
+ <com.android.settings.widget.RadioButtonPreference
+ android:key="importance_default"
+ android:title="@string/notification_importance_default_title"
+ android:summary="@string/notification_importance_default" />
+ <com.android.settings.widget.RadioButtonPreference
+ android:key="importance_low"
+ android:title="@string/notification_importance_low_title"
+ android:summary="@string/notification_importance_low" />
+ <com.android.settings.widget.RadioButtonPreference
+ android:key="importance_min"
+ android:title="@string/notification_importance_min_title"
+ android:summary="@string/notification_importance_min" />
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/applications/AppLister.java b/src/com/android/settings/applications/AppLister.java
new file mode 100644
index 0000000..425afa0
--- /dev/null
+++ b/src/com/android/settings/applications/AppLister.java
@@ -0,0 +1,69 @@
+/*
+ * 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.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.AsyncTask;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Lists apps for current user that fit some criteria specified by includeInCount method
+ * implementation.
+ * This class is similar to {@link AppCounter} class, but but builds actual list of apps instead
+ * of just counting them.
+ */
+public abstract class AppLister extends AsyncTask<Void, Void, List<UserAppInfo>> {
+ protected final PackageManagerWrapper mPm;
+ protected final UserManager mUm;
+
+ public AppLister(PackageManagerWrapper packageManager, UserManager userManager) {
+ mPm = packageManager;
+ mUm = userManager;
+ }
+
+ @Override
+ protected List<UserAppInfo> doInBackground(Void... params) {
+ final List<UserAppInfo> result = new ArrayList<>();
+ for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
+ final List<ApplicationInfo> list =
+ mPm.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+ | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
+ user.id);
+ for (ApplicationInfo info : list) {
+ if (includeInCount(info)) {
+ result.add(new UserAppInfo(user, info));
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(List<UserAppInfo> list) {
+ onAppListBuilt(list);
+ }
+
+ protected abstract void onAppListBuilt(List<UserAppInfo> list);
+ protected abstract boolean includeInCount(ApplicationInfo info);
+}
diff --git a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
index 52f1da5..c7d0a62 100644
--- a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
+++ b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
@@ -31,7 +31,6 @@
public abstract class AppWithAdminGrantedPermissionsCounter extends AppCounter {
private final String[] mPermissions;
- private final PackageManagerWrapper mPackageManager;
private final IPackageManagerWrapper mPackageManagerService;
private final DevicePolicyManagerWrapper mDevicePolicyManager;
@@ -40,18 +39,24 @@
DevicePolicyManagerWrapper devicePolicyManager) {
super(context, packageManager);
mPermissions = permissions;
- mPackageManager = packageManager;
mPackageManagerService = packageManagerService;
mDevicePolicyManager = devicePolicyManager;
}
@Override
protected boolean includeInCount(ApplicationInfo info) {
+ return includeInCount(mPermissions, mDevicePolicyManager, mPm, mPackageManagerService,
+ info);
+ }
+
+ public static boolean includeInCount(String[] permissions,
+ DevicePolicyManagerWrapper devicePolicyManager, PackageManagerWrapper packageManager,
+ IPackageManagerWrapper packageManagerService, ApplicationInfo info) {
if (info.targetSdkVersion >= Build.VERSION_CODES.M) {
// The app uses run-time permissions. Check whether one or more of the permissions were
// granted by enterprise policy.
- for (final String permission : mPermissions) {
- if (mDevicePolicyManager.getPermissionGrantState(null /* admin */, info.packageName,
+ for (final String permission : permissions) {
+ if (devicePolicyManager.getPermissionGrantState(null /* admin */, info.packageName,
permission) == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED) {
return true;
}
@@ -61,14 +66,14 @@
// The app uses install-time permissions. Check whether the app requested one or more of the
// permissions and was installed by enterprise policy, implicitly granting permissions.
- if (mPackageManager.getInstallReason(info.packageName,
+ if (packageManager.getInstallReason(info.packageName,
new UserHandle(UserHandle.getUserId(info.uid)))
!= PackageManager.INSTALL_REASON_POLICY) {
return false;
}
try {
- for (final String permission : mPermissions) {
- if (mPackageManagerService.checkUidPermission(permission, info.uid)
+ for (final String permission : permissions) {
+ if (packageManagerService.checkUidPermission(permission, info.uid)
== PackageManager.PERMISSION_GRANTED) {
return true;
}
diff --git a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsLister.java b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsLister.java
new file mode 100644
index 0000000..b21f31f
--- /dev/null
+++ b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsLister.java
@@ -0,0 +1,46 @@
+/*
+ * 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.pm.ApplicationInfo;
+import android.os.UserManager;
+import com.android.settings.enterprise.DevicePolicyManagerWrapper;
+
+/**
+ * Lists installed apps across all users that have been granted one or more specific permissions by
+ * the admin.
+ */
+public abstract class AppWithAdminGrantedPermissionsLister extends AppLister {
+ private final String[] mPermissions;
+ private final IPackageManagerWrapper mPackageManagerService;
+ private final DevicePolicyManagerWrapper mDevicePolicyManager;
+
+ public AppWithAdminGrantedPermissionsLister(String[] permissions,
+ PackageManagerWrapper packageManager, IPackageManagerWrapper packageManagerService,
+ DevicePolicyManagerWrapper devicePolicyManager, UserManager userManager) {
+ super(packageManager, userManager);
+ mPermissions = permissions;
+ mPackageManagerService = packageManagerService;
+ mDevicePolicyManager = devicePolicyManager;
+ }
+
+ @Override
+ protected boolean includeInCount(ApplicationInfo info) {
+ return AppWithAdminGrantedPermissionsCounter.includeInCount(mPermissions,
+ mDevicePolicyManager, mPm, mPackageManagerService, info);
+ }
+}
diff --git a/src/com/android/settings/applications/ApplicationFeatureProvider.java b/src/com/android/settings/applications/ApplicationFeatureProvider.java
index 5e986db..3266fe0 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProvider.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProvider.java
@@ -16,13 +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 java.util.Set;
+import com.android.settings.applications.instantapps.InstantAppButtonsController;
+
+import java.util.List;
public interface ApplicationFeatureProvider {
@@ -49,6 +50,14 @@
void calculateNumberOfPolicyInstalledApps(boolean async, NumberOfAppsCallback callback);
/**
+ * Asynchronously builds the list of apps installed on the device via policy in the current user
+ * and all its managed profiles.
+ *
+ * @param callback The callback to invoke with the result
+ */
+ void listPolicyInstalledApps(ListOfAppsCallback callback);
+
+ /**
* Asynchronously calculates the total number of apps installed in the current user and all its
* managed profiles that have been granted one or more of the given permissions by the admin.
*
@@ -61,16 +70,28 @@
NumberOfAppsCallback 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.
+ * Asynchronously builds the list of apps installed in the current user and all its
+ * managed profiles that have been granted one or more of the given permissions by the admin.
*
+ * @param permissions Only consider apps that have been granted one or more of these permissions
+ * by the admin, either at run-time or install-time
+ * @param callback The callback to invoke with the result
+ */
+ void listAppsWithAdminGrantedPermissions(String[] permissions, ListOfAppsCallback callback);
+
+ /**
+ * 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<PersistentPreferredActivityInfo> findPersistentPreferredActivities(Intent[] intents);
+ List<UserAppInfo> findPersistentPreferredActivities(@UserIdInt int userId, Intent[] intents);
/**
* Callback that receives the number of packages installed on the device.
@@ -79,29 +100,10 @@
void onNumberOfAppsResult(int num);
}
- 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;
- }
+ /**
+ * Callback that receives the list of packages installed on the device.
+ */
+ interface ListOfAppsCallback {
+ void onListOfAppsResult(List<UserAppInfo> result);
}
}
diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
index 4171857..a744792 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
@@ -22,6 +22,7 @@
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 com.android.settings.applications.instantapps.InstantAppButtonsController;
import com.android.settings.enterprise.DevicePolicyManagerWrapper;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -74,6 +76,13 @@
}
@Override
+ public void listPolicyInstalledApps(ListOfAppsCallback callback) {
+ final CurrentUserPolicyInstalledAppLister lister =
+ new CurrentUserPolicyInstalledAppLister(mPm, mUm, callback);
+ lister.execute();
+ }
+
+ @Override
public void calculateNumberOfAppsWithAdminGrantedPermissions(String[] permissions,
boolean async, NumberOfAppsCallback callback) {
final CurrentUserAndManagedProfileAppWithAdminGrantedPermissionsCounter counter =
@@ -87,36 +96,43 @@
}
@Override
- public Set<PersistentPreferredActivityInfo> findPersistentPreferredActivities(
- Intent[] intents) {
- final Set<PersistentPreferredActivityInfo> activities = new ArraySet<>();
- final List<UserHandle> users = mUm.getUserProfiles();
+ public void listAppsWithAdminGrantedPermissions(String[] permissions,
+ ListOfAppsCallback callback) {
+ final CurrentUserAppWithAdminGrantedPermissionsLister lister =
+ new CurrentUserAppWithAdminGrantedPermissionsLister(permissions, mPm, mPms, mDpm,
+ mUm, callback);
+ lister.execute();
+ }
+
+ @Override
+ public List<UserAppInfo> findPersistentPreferredActivities(int userId, Intent[] intents) {
+ final List<UserAppInfo> preferredActivities = new ArrayList<>();
+ final Set<UserAppInfo> 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
@@ -152,4 +168,39 @@
mCallback.onNumberOfAppsResult(num);
}
}
+
+ private static class CurrentUserPolicyInstalledAppLister extends InstalledAppLister {
+ private ListOfAppsCallback mCallback;
+
+ CurrentUserPolicyInstalledAppLister(PackageManagerWrapper packageManager,
+ UserManager userManager, ListOfAppsCallback callback) {
+ super(packageManager, userManager);
+ mCallback = callback;
+ }
+
+ @Override
+ protected void onAppListBuilt(List<UserAppInfo> list) {
+ mCallback.onListOfAppsResult(list);
+ }
+ }
+
+ private static class CurrentUserAppWithAdminGrantedPermissionsLister extends
+ AppWithAdminGrantedPermissionsLister {
+ private ListOfAppsCallback mCallback;
+
+ CurrentUserAppWithAdminGrantedPermissionsLister(String[] permissions,
+ PackageManagerWrapper packageManager, IPackageManagerWrapper packageManagerService,
+ DevicePolicyManagerWrapper devicePolicyManager, UserManager userManager,
+ ListOfAppsCallback callback) {
+ super(permissions, packageManager, packageManagerService, devicePolicyManager,
+ userManager);
+ mCallback = callback;
+ }
+
+ @Override
+ protected void onAppListBuilt(List<UserAppInfo> list) {
+ mCallback.onListOfAppsResult(list);
+ }
+ }
+
}
diff --git a/src/com/android/settings/applications/EnterpriseDefaultApps.java b/src/com/android/settings/applications/EnterpriseDefaultApps.java
new file mode 100644
index 0000000..f48b955
--- /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/InstalledAppCounter.java b/src/com/android/settings/applications/InstalledAppCounter.java
index 8065d49..932face 100644
--- a/src/com/android/settings/applications/InstalledAppCounter.java
+++ b/src/com/android/settings/applications/InstalledAppCounter.java
@@ -31,21 +31,24 @@
public static final int IGNORE_INSTALL_REASON = -1;
private final int mInstallReason;
- private final PackageManagerWrapper mPackageManager;
public InstalledAppCounter(Context context, int installReason,
PackageManagerWrapper packageManager) {
super(context, packageManager);
mInstallReason = installReason;
- mPackageManager = packageManager;
}
@Override
protected boolean includeInCount(ApplicationInfo info) {
+ return includeInCount(mInstallReason, mPm, info);
+ }
+
+ public static boolean includeInCount(int installReason, PackageManagerWrapper pm,
+ ApplicationInfo info) {
final int userId = UserHandle.getUserId(info.uid);
- if (mInstallReason != IGNORE_INSTALL_REASON
- && mPackageManager.getInstallReason(info.packageName,
- new UserHandle(userId)) != mInstallReason) {
+ if (installReason != IGNORE_INSTALL_REASON
+ && pm.getInstallReason(info.packageName,
+ new UserHandle(userId)) != installReason) {
return false;
}
if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
@@ -57,7 +60,7 @@
Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setPackage(info.packageName);
- List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
+ List<ResolveInfo> intents = pm.queryIntentActivitiesAsUser(
launchIntent,
PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_AWARE
diff --git a/src/com/android/settings/applications/InstalledAppLister.java b/src/com/android/settings/applications/InstalledAppLister.java
new file mode 100644
index 0000000..d8e7c58
--- /dev/null
+++ b/src/com/android/settings/applications/InstalledAppLister.java
@@ -0,0 +1,33 @@
+/*
+ * 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.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+
+public abstract class InstalledAppLister extends AppLister {
+
+ public InstalledAppLister(PackageManagerWrapper packageManager, UserManager userManager) {
+ super(packageManager, userManager);
+ }
+
+ @Override
+ protected boolean includeInCount(ApplicationInfo info) {
+ return InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, mPm, info);
+ }
+}
diff --git a/src/com/android/settings/applications/UserAppInfo.java b/src/com/android/settings/applications/UserAppInfo.java
new file mode 100644
index 0000000..04fa713
--- /dev/null
+++ b/src/com/android/settings/applications/UserAppInfo.java
@@ -0,0 +1,58 @@
+/*
+ * 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.pm.ApplicationInfo;
+import android.content.pm.UserInfo;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Simple class for bringing together information about application and user for which it was
+ * installed.
+ */
+public class UserAppInfo {
+ public final UserInfo userInfo;
+ public final ApplicationInfo appInfo;
+
+ public UserAppInfo(UserInfo mUserInfo, ApplicationInfo mAppInfo) {
+ this.userInfo = mUserInfo;
+ this.appInfo = mAppInfo;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ 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);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(userInfo.id, appInfo.packageName);
+ }
+}
diff --git a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
index 0db9e15..f0aca01 100644
--- a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
+++ b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java
@@ -16,7 +16,6 @@
package com.android.settings.enterprise;
import android.content.Context;
-import android.content.Intent;
import android.support.v7.preference.Preference;
import com.android.settings.R;
@@ -32,6 +31,7 @@
private final String mPermissionGroup;
private final ApplicationFeatureProvider mFeatureProvider;
private final boolean mAsync;
+ private boolean mHasApps;
public AdminGrantedPermissionsPreferenceControllerBase(Context context, Lifecycle lifecycle,
boolean async, String[] permissions, String permissionGroup) {
@@ -41,6 +41,7 @@
mFeatureProvider = FeatureFactory.getFactory(context)
.getApplicationFeatureProvider(context);
mAsync = async;
+ mHasApps = false;
}
@Override
@@ -50,11 +51,13 @@
(num) -> {
if (num == 0) {
preference.setVisible(false);
+ mHasApps = false;
} else {
preference.setVisible(true);
preference.setSummary(mContext.getResources().getQuantityString(
R.plurals.enterprise_privacy_number_packages_lower_bound,
num, num));
+ mHasApps = true;
}
});
}
@@ -76,7 +79,8 @@
final Boolean[] haveAppsWithAdminGrantedPermissions = { null };
mFeatureProvider.calculateNumberOfAppsWithAdminGrantedPermissions(mPermissions,
false /* async */, (num) -> haveAppsWithAdminGrantedPermissions[0] = num > 0);
- return haveAppsWithAdminGrantedPermissions[0];
+ mHasApps = haveAppsWithAdminGrantedPermissions[0];
+ return mHasApps;
}
@Override
@@ -84,9 +88,9 @@
if (!getPreferenceKey().equals(preference.getKey())) {
return false;
}
- final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
- .putExtra(Intent.EXTRA_PERMISSION_NAME, mPermissionGroup);
- mContext.startActivity(intent);
- return true;
+ if (!mHasApps) {
+ return false;
+ }
+ return super.handlePreferenceTreeClick(preference);
}
}
diff --git a/src/com/android/settings/enterprise/ApplicationListFragment.java b/src/com/android/settings/enterprise/ApplicationListFragment.java
new file mode 100644
index 0000000..ff68a8b
--- /dev/null
+++ b/src/com/android/settings/enterprise/ApplicationListFragment.java
@@ -0,0 +1,115 @@
+/*
+ * 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.Manifest;
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base fragment for displaying a list of applications on a device.
+ * Inner static classes are concrete implementations.
+ */
+public abstract class ApplicationListFragment extends DashboardFragment
+ implements ApplicationListPreferenceController.ApplicationListBuilder {
+
+ static final String TAG = "EnterprisePrivacySettings";
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.app_list_disclosure_settings;
+ }
+
+ @Override
+ protected List<PreferenceController> getPreferenceControllers(Context context) {
+ final List controllers = new ArrayList<>();
+ ApplicationListPreferenceController controller = new ApplicationListPreferenceController(
+ context, this, context.getPackageManager(), this);
+ controllers.add(controller);
+ return controllers;
+ }
+
+ private abstract static class AdminGrantedPermission extends ApplicationListFragment {
+ private final String[] mPermissions;
+
+ public AdminGrantedPermission(String[] permissions) {
+ mPermissions = permissions;
+ }
+
+ @Override
+ public void buildApplicationList(Context context,
+ ApplicationFeatureProvider.ListOfAppsCallback callback) {
+ FeatureFactory.getFactory(context).getApplicationFeatureProvider(context)
+ .listAppsWithAdminGrantedPermissions(mPermissions, callback);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.ENTERPRISE_PRIVACY_PERMISSIONS;
+ }
+ }
+
+ public static class AdminGrantedPermissionCamera extends AdminGrantedPermission {
+ public AdminGrantedPermissionCamera() {
+ super(new String[] {Manifest.permission.CAMERA});
+ }
+ }
+
+ public static class AdminGrantedPermissionLocation extends AdminGrantedPermission {
+ public AdminGrantedPermissionLocation() {
+ super(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION});
+ }
+ }
+
+ public static class AdminGrantedPermissionMicrophone extends AdminGrantedPermission {
+ public AdminGrantedPermissionMicrophone() {
+ super(new String[] {Manifest.permission.RECORD_AUDIO});
+ }
+ }
+
+ public static class EnterpriseInstalledPackages extends ApplicationListFragment {
+ public EnterpriseInstalledPackages() {
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.ENTERPRISE_PRIVACY_INSTALLED_APPS;
+ }
+
+ @Override
+ public void buildApplicationList(Context context,
+ ApplicationFeatureProvider.ListOfAppsCallback callback) {
+ FeatureFactory.getFactory(context).getApplicationFeatureProvider(context).
+ listPolicyInstalledApps(callback);
+ }
+ }
+}
diff --git a/src/com/android/settings/enterprise/ApplicationListPreferenceController.java b/src/com/android/settings/enterprise/ApplicationListPreferenceController.java
new file mode 100644
index 0000000..f9fefa1
--- /dev/null
+++ b/src/com/android/settings/enterprise/ApplicationListPreferenceController.java
@@ -0,0 +1,86 @@
+/*
+ * 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.PackageManager;
+import android.support.v7.preference.Preference;
+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.UserAppInfo;
+import com.android.settings.core.PreferenceController;
+
+import java.util.List;
+
+/**
+ * PreferenceController that builds a dynamic list of applications provided by
+ * {@link ApplicationListBuilder} instance.
+ */
+public class ApplicationListPreferenceController extends PreferenceController
+ implements ApplicationFeatureProvider.ListOfAppsCallback {
+ private final PackageManager mPm;
+ private SettingsPreferenceFragment mParent;
+
+ public ApplicationListPreferenceController(Context context, ApplicationListBuilder builder,
+ PackageManager packageManager, SettingsPreferenceFragment parent) {
+ super(context);
+ mPm = packageManager;
+ mParent = parent;
+ builder.buildApplicationList(context, this);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return null;
+ }
+
+ @Override
+ public void onListOfAppsResult(List<UserAppInfo> result) {
+ final PreferenceScreen screen = mParent.getPreferenceScreen();
+ if (screen == null) {
+ return;
+ }
+ final Context prefContext = mParent.getPreferenceManager().getContext();
+ for (int position = 0; position < result.size(); position++) {
+ final UserAppInfo item = result.get(position);
+ final Preference preference = new Preference(prefContext);
+ preference.setLayoutResource(R.layout.preference_app);
+ preference.setTitle(item.appInfo.loadLabel(mPm));
+ preference.setIcon(item.appInfo.loadIcon(mPm));
+ preference.setOrder(position);
+ preference.setSelectable(false);
+ screen.addPreference(preference);
+ }
+ }
+
+ /**
+ * Simple interface for building application list within {
+ * @link ApplicationListPreferenceController}
+ */
+ public interface ApplicationListBuilder {
+ 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 5817cf2..46ecb7e 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 254940e..69e0416 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 0000000..6f173f1
--- /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<PreferenceController> getPreferenceControllers(Context context) {
+ final List controllers = new ArrayList<PreferenceController>();
+ 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 0000000..51b60b8
--- /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<UserInfo> mUsers = Collections.emptyList();
+ private List<EnumMap<EnterpriseDefaultApps, List<ApplicationInfo>>> 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<EnterpriseDefaultApps, List<ApplicationInfo>> userMap = null;
+
+ for (EnterpriseDefaultApps typeOfDefault : EnterpriseDefaultApps.values()) {
+ List<UserAppInfo> 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<ApplicationInfo> 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<EnterpriseDefaultApps, List<ApplicationInfo>> apps) {
+ if (group == null) {
+ return;
+ }
+ for (EnterpriseDefaultApps typeOfDefault : EnterpriseDefaultApps.values()) {
+ final List<ApplicationInfo> 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<ApplicationInfo> 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 ae76d63..2f43a61 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 @@
}
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/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index db5cf2f..e91a849 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -24,6 +24,7 @@
import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -33,17 +34,22 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.AppHeader;
-import com.android.settings.DimmableIconPreference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppHeaderController;
import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.LayoutPreference;
import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.FooterPreference;
import com.android.settings.widget.MasterSwitchPreference;
+import com.android.settings.widget.SwitchBar;
import com.android.settingslib.RestrictedSwitchPreference;
import java.text.Collator;
@@ -57,12 +63,14 @@
private static final String TAG = "AppNotificationSettings";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String KEY_BLOCK = "block";
private static final String KEY_IMPORTANCE = "allow_sound";
private List<NotificationChannelGroup> mChannelGroupList;
private List<PreferenceCategory> mChannelGroups = new ArrayList();
private RestrictedSwitchPreference mImportanceToggle;
+ private LayoutPreference mBlockBar;
+ private FooterPreference mDeletedChannels;
+ private SwitchBar mSwitchBar;
private boolean mShowLegacyChannelConfig = false;
@@ -88,8 +96,8 @@
addPreferencesFromResource(R.xml.app_notification_settings);
getPreferenceScreen().setOrderingAsAdded(true);
- mBlock = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BLOCK);
mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
+ mBlockedDesc = (FooterPreference) getPreferenceScreen().findPreference(KEY_BLOCKED_DESC);
setupBlock();
setupBadge();
@@ -127,11 +135,7 @@
.done(activity, getPrefContext());
getPreferenceScreen().addPreference(pref);
- if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) {
- Log.w(TAG, "Missing package or uid or packageinfo");
- finish();
- return;
- }
+ updateDependents(mAppRow.banned);
}
private void populateChannelList() {
@@ -187,12 +191,12 @@
int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
if (deletedChannelCount > 0) {
- DimmableIconPreference deletedPref = new DimmableIconPreference(getPrefContext());
- deletedPref.setSelectable(false);
- deletedPref.setTitle(getResources().getQuantityString(
+ mDeletedChannels = new FooterPreference(getPrefContext());
+ mDeletedChannels.setSelectable(false);
+ mDeletedChannels.setTitle(getResources().getQuantityString(
R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
- deletedPref.setIcon(R.drawable.ic_info);
- getPreferenceScreen().addPreference(deletedPref);
+ mDeletedChannels.setEnabled(false);
+ getPreferenceScreen().addPreference(mDeletedChannels);
}
}
updateDependents(mAppRow.banned);
@@ -214,7 +218,8 @@
channelArgs.putString(Settings.EXTRA_CHANNEL_ID, channel.getId());
Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
ChannelNotificationSettings.class.getName(),
- channelArgs, null, 0, null, false, getMetricsCategory());
+ channelArgs, null, R.string.notification_channel_title, null, false,
+ getMetricsCategory());
channelPref.setIntent(channelIntent);
channelPref.setOnPreferenceChangeListener(
@@ -227,6 +232,7 @@
channel.setImportance(importance);
channel.lockFields(
NotificationChannel.USER_LOCKED_IMPORTANCE);
+ channelPref.setSummary(getImportanceSummary(channel.getImportance()));
mBackend.updateChannel(mPkg, mUid, channel);
return true;
@@ -237,8 +243,7 @@
private void populateDefaultChannelPrefs() {
addPreferencesFromResource(R.xml.legacy_channel_notification_settings);
- mPriority =
- (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
+ mPriority = (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
mVisibilityOverride =
(RestrictedDropDownPreference) findPreference(KEY_VISIBILITY_OVERRIDE);
mImportanceToggle = (RestrictedSwitchPreference) findPreference(KEY_IMPORTANCE);
@@ -248,8 +253,11 @@
setupVisOverridePref(mChannel.getLockscreenVisibility());
setupImportanceToggle();
}
+ mSwitchBar.setChecked(!mAppRow.banned
+ && mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
}
+ // 'allow sound'
private void setupImportanceToggle() {
mImportanceToggle.setDisabledByAdmin(mSuspendedAppsAdmin);
mImportanceToggle.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT
@@ -268,6 +276,40 @@
});
}
+ protected void setupBlock() {
+ View switchBarContainer = LayoutInflater.from(
+ getPrefContext()).inflate(R.layout.styled_switch_bar, null);
+ mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
+ mSwitchBar.show();
+ mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
+ mSwitchBar.setChecked(!mAppRow.banned);
+ mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
+ @Override
+ public void onSwitchChanged(Switch switchView, boolean isChecked) {
+ if (mShowLegacyChannelConfig && mChannel != null) {
+ final int importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
+ mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
+ mChannel.setImportance(importance);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ mBackend.updateChannel(mPkg, mUid, mChannel);
+ }
+ mBackend.setNotificationsEnabledForPackage(mPkgInfo.packageName, mUid, isChecked);
+ updateDependents(!isChecked);
+ }
+ });
+
+ mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
+ mBlockBar.setOrder(-500);
+ mBlockBar.setKey(KEY_BLOCK);
+ getPreferenceScreen().addPreference(mBlockBar);
+
+ if (mAppRow.systemApp && !mAppRow.banned) {
+ setVisible(mBlockBar, false);
+ }
+
+ setupBlockDesc(R.string.app_notifications_off_desc);
+ }
+
private void setupBadge() {
mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
mBadge.setChecked(mAppRow.showBadge);
@@ -281,31 +323,14 @@
});
}
- private void setupBlock() {
- if (mAppRow.systemApp && !mAppRow.banned) {
- setVisible(mBlock, false);
- } else {
- mBlock.setDisabledByAdmin(mSuspendedAppsAdmin);
- mBlock.setChecked(mAppRow.banned);
- mBlock.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference,
- Object newValue) {
- final boolean blocked = (Boolean) newValue;
- mBackend.setNotificationsEnabledForPackage(mPkgInfo.packageName, mUid,
- !blocked);
- updateDependents(blocked);
- return true;
- }
- });
- }
- }
-
private void updateDependents(boolean banned) {
for (PreferenceCategory category : mChannelGroups) {
setVisible(category, !banned);
}
+ if (mDeletedChannels != null) {
+ setVisible(mDeletedChannels, !banned);
+ }
+ setVisible(mBlockedDesc, banned);
setVisible(mBadge, !banned);
if (mShowLegacyChannelConfig) {
setVisible(mImportanceToggle, !banned);
@@ -313,7 +338,7 @@
setVisible(mVisibilityOverride, !banned);
}
if (mAppRow.systemApp && !mAppRow.banned) {
- setVisible(mBlock, false);
+ setVisible(mBlockBar, false);
}
}
diff --git a/src/com/android/settings/notification/ChannelImportanceSettings.java b/src/com/android/settings/notification/ChannelImportanceSettings.java
new file mode 100644
index 0000000..b396b20
--- /dev/null
+++ b/src/com/android/settings/notification/ChannelImportanceSettings.java
@@ -0,0 +1,151 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MAX;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settings.widget.RadioButtonPreference;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChannelImportanceSettings extends NotificationSettingsBase
+ implements RadioButtonPreference.OnClickListener, Indexable {
+ private static final String TAG = "NotiImportance";
+
+ private static final String KEY_IMPORTANCE_HIGH = "importance_high";
+ private static final String KEY_IMPORTANCE_DEFAULT = "importance_default";
+ private static final String KEY_IMPORTANCE_LOW = "importance_low";
+ private static final String KEY_IMPORTANCE_MIN = "importance_min";
+
+ List<RadioButtonPreference> mImportances = new ArrayList<>();
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.NOTIFICATION_CHANNEL_IMPORTANCE;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
+ Log.w(TAG, "Missing package or uid or packageinfo or channel");
+ finish();
+ return;
+ }
+ createPreferenceHierarchy();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private PreferenceScreen createPreferenceHierarchy() {
+ PreferenceScreen root = getPreferenceScreen();
+ if (root != null) {
+ root.removeAll();
+ }
+ addPreferencesFromResource(R.xml.notification_importance);
+ root = getPreferenceScreen();
+
+ for (int i = 0; i < root.getPreferenceCount(); i++) {
+ Preference pref = root.getPreference(i);
+ if (pref instanceof RadioButtonPreference) {
+ RadioButtonPreference radioPref = (RadioButtonPreference) pref;
+ radioPref.setOnClickListener(this);
+ mImportances.add(radioPref);
+ }
+ }
+
+ switch (mChannel.getImportance()) {
+ case IMPORTANCE_MIN:
+ updateRadioButtons(KEY_IMPORTANCE_MIN);
+ break;
+ case IMPORTANCE_LOW:
+ updateRadioButtons(KEY_IMPORTANCE_LOW);
+ break;
+ case IMPORTANCE_DEFAULT:
+ updateRadioButtons(KEY_IMPORTANCE_DEFAULT);
+ break;
+ case IMPORTANCE_HIGH:
+ case IMPORTANCE_MAX:
+ updateRadioButtons(KEY_IMPORTANCE_HIGH);
+ break;
+ }
+
+ return root;
+ }
+
+ private void updateRadioButtons(String selectionKey) {
+ for (RadioButtonPreference pref : mImportances) {
+ if (selectionKey.equals(pref.getKey())) {
+ pref.setChecked(true);
+ } else {
+ pref.setChecked(false);
+ }
+ }
+ }
+
+ @Override
+ public void onRadioButtonClicked(RadioButtonPreference clicked) {
+ switch (clicked.getKey()) {
+ case KEY_IMPORTANCE_HIGH:
+ mChannel.setImportance(IMPORTANCE_HIGH);
+ break;
+ case KEY_IMPORTANCE_DEFAULT:
+ mChannel.setImportance(IMPORTANCE_DEFAULT);
+ break;
+ case KEY_IMPORTANCE_LOW:
+ mChannel.setImportance(IMPORTANCE_LOW);
+ break;
+ case KEY_IMPORTANCE_MIN:
+ mChannel.setImportance(IMPORTANCE_MIN);
+ break;
+ }
+ updateRadioButtons(clicked.getKey());
+ mChannel.lockFields(USER_LOCKED_IMPORTANCE);
+ mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, mChannel);
+ }
+
+ // This page exists per notification channel; should not be included
+ // in search
+ public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List<SearchIndexableResource> getXmlResourcesToIndex(
+ Context context, boolean enabled) {
+ return null;
+ }
+ };
+}
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index f6a7b42..0fb42d2 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -16,9 +16,7 @@
package com.android.settings.notification;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import android.app.Activity;
@@ -27,25 +25,31 @@
import android.content.Intent;
import android.content.pm.UserInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.DimmableIconPreference;
+import com.android.settings.AppHeader;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
+import com.android.settings.Utils;
import com.android.settings.applications.AppHeaderController;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.LayoutPreference;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.FooterPreference;
+import com.android.settings.widget.SwitchBar;
import com.android.settingslib.RestrictedSwitchPreference;
-import java.util.ArrayList;
-import java.util.List;
-
public class ChannelNotificationSettings extends NotificationSettingsBase {
private static final String TAG = "ChannelSettings";
@@ -53,10 +57,13 @@
protected static final String KEY_VIBRATE = "vibrate";
protected static final String KEY_RINGTONE = "ringtone";
+ protected Preference mImportance;
protected RestrictedSwitchPreference mLights;
protected RestrictedSwitchPreference mVibrate;
protected NotificationSoundPreference mRingtone;
+ protected LayoutPreference mBlockBar;
+
@Override
public int getMetricsCategory() {
return MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
@@ -75,33 +82,33 @@
getPreferenceScreen().removeAll();
}
addPreferencesFromResource(R.xml.channel_notification_settings);
- getPreferenceScreen().setOrderingAsAdded(true);
// load settings intent
ArrayMap<String, NotificationBackend.AppRow> rows = new ArrayMap<String, NotificationBackend.AppRow>();
rows.put(mAppRow.pkg, mAppRow);
collectConfigActivities(rows);
- mBlock = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BLOCK);
+ mBlockedDesc = (FooterPreference) getPreferenceScreen().findPreference(KEY_BLOCKED_DESC);
mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
- mImportance = (RestrictedDropDownPreference) findPreference(KEY_IMPORTANCE);
- mPriority =
- (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
+ mImportance = findPreference(KEY_IMPORTANCE);
+ mPriority = (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
mVisibilityOverride =
(RestrictedDropDownPreference) findPreference(KEY_VISIBILITY_OVERRIDE);
mLights = (RestrictedSwitchPreference) findPreference(KEY_LIGHTS);
mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE);
- if (mPkgInfo != null && mChannel != null) {
- setupPriorityPref(mChannel.canBypassDnd());
- setupVisOverridePref(mChannel.getLockscreenVisibility());
- setupLights();
- setupVibrate();
- setupRingtone();
- setupBlockAndImportance();
- updateDependents();
- }
+
+ setupPriorityPref(mChannel.canBypassDnd());
+ setupVisOverridePref(mChannel.getLockscreenVisibility());
+ setupLights();
+ setupVibrate();
+ setupRingtone();
+ setupBadge();
+ setupBlock();
+ setupImportance();
+ updateDependents();
+
final Activity activity = getActivity();
final Preference pref = FeatureFactory.getFactory(activity)
.getApplicationFeatureProvider(activity)
@@ -124,12 +131,13 @@
}
if (!TextUtils.isEmpty(mChannel.getDescription())) {
- DimmableIconPreference descPref = new DimmableIconPreference(getPrefContext());
+ FooterPreference descPref = new FooterPreference(getPrefContext());
descPref.setSelectable(false);
descPref.setSummary(mChannel.getDescription());
- descPref.setIcon(R.drawable.ic_info);
+ descPref.setEnabled(false);
getPreferenceScreen().addPreference(descPref);
}
+
}
private void setupLights() {
@@ -175,27 +183,38 @@
});
}
- protected void setupBlockAndImportance() {
+ protected void setupBlock() {
+ View switchBarContainer = LayoutInflater.from(
+ getPrefContext()).inflate(R.layout.styled_switch_bar, null);
+ SwitchBar switchBar = switchBarContainer.findViewById(R.id.switch_bar);
+ switchBar.show();
+ switchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
+ switchBar.setChecked(mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
+ switchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
+ @Override
+ public void onSwitchChanged(Switch switchView, boolean isChecked) {
+ int importance = isChecked ? IMPORTANCE_LOW : IMPORTANCE_NONE;
+ mImportance.setSummary(getImportanceSummary(importance));
+ mChannel.setImportance(importance);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ mBackend.updateChannel(mPkg, mUid, mChannel);
+ updateDependents();
+ }
+ });
+
+ mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
+ mBlockBar.setOrder(-500);
+ mBlockBar.setKey(KEY_BLOCK);
+ getPreferenceScreen().addPreference(mBlockBar);
+
if (mAppRow.systemApp && mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE) {
- setVisible(mBlock, false);
- } else {
- mBlock.setEnabled(mAppRow.systemApp);
- mBlock.setDisabledByAdmin(mSuspendedAppsAdmin);
- mBlock.setChecked(mChannel.getImportance() == NotificationManager.IMPORTANCE_NONE);
- mBlock.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean value = (Boolean) newValue;
- int importance = value ? IMPORTANCE_NONE : IMPORTANCE_LOW;
- mImportance.setValue(String.valueOf(importance));
- mChannel.setImportance(importance);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- updateDependents();
- return true;
- }
- });
+ setVisible(mBlockBar, false);
}
+
+ setupBlockDesc(R.string.channel_notifications_off_desc);
+ }
+
+ protected void setupBadge() {
mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
mBadge.setEnabled(mAppRow.showBadge);
mBadge.setChecked(mChannel.canShowBadge());
@@ -209,41 +228,21 @@
return true;
}
});
+ }
- mImportance.setDisabledByAdmin(mSuspendedAppsAdmin);
- final int numImportances = IMPORTANCE_HIGH - IMPORTANCE_MIN + 1;
- List<String> summaries = new ArrayList<>();
- List<String> values = new ArrayList<>();
-
- for (int i = 0; i < numImportances; i++) {
- int importance = i + 1;
- summaries.add(getImportanceSummary(importance));
- values.add(String.valueOf(importance));
- }
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
- // Add option to reset to letting the app decide
- summaries.add(getImportanceSummary(NotificationManager.IMPORTANCE_UNSPECIFIED));
- values.add(String.valueOf(NotificationManager.IMPORTANCE_UNSPECIFIED));
- }
- mImportance.setEntryValues(values.toArray(new String[0]));
- mImportance.setEntries(summaries.toArray(new String[0]));
- mImportance.setValue(String.valueOf(mChannel.getImportance()));
+ protected void setupImportance() {
+ Bundle channelArgs = new Bundle();
+ channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
+ channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
+ channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
+ channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
+ Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
+ ChannelImportanceSettings.class.getName(),
+ channelArgs, null, R.string.notification_importance_title, null,
+ false, getMetricsCategory());
+ mImportance.setIntent(channelIntent);
+ mImportance.setEnabled(mSuspendedAppsAdmin == null);
mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
- if (mAppRow.lockedImportance) {
- mImportance.setEnabled(false);
- } else {
- mImportance.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- int importance = Integer.parseInt((String) newValue);
- mChannel.setImportance(importance);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- updateDependents();
- return true;
- }
- });
- }
}
private boolean isLockScreenSecure() {
@@ -292,6 +291,7 @@
}
private void updateDependents() {
+ setVisible(mBlockedDesc, mChannel.getImportance() == IMPORTANCE_NONE);
setVisible(mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
setVisible(mImportance, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
setVisible(mLights, checkCanBeVisible(
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index ff0a512..c08f161 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -19,6 +19,7 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppInfoBase;
+import com.android.settings.widget.FooterPreference;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
@@ -39,6 +40,7 @@
import android.os.UserManager;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
+import android.support.v7.preference.DropDownPreference;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -63,6 +65,7 @@
protected static final String KEY_BYPASS_DND = "bypass_dnd";
protected static final String KEY_VISIBILITY_OVERRIDE = "visibility_override";
protected static final String KEY_IMPORTANCE = "importance";
+ protected static final String KEY_BLOCKED_DESC = "block_desc";
protected PackageManager mPm;
protected UserManager mUm;
@@ -73,11 +76,10 @@
protected int mUserId;
protected String mPkg;
protected PackageInfo mPkgInfo;
- protected RestrictedSwitchPreference mBlock;
protected RestrictedSwitchPreference mBadge;
- protected RestrictedDropDownPreference mImportance;
protected RestrictedSwitchPreference mPriority;
protected RestrictedDropDownPreference mVisibilityOverride;
+ protected FooterPreference mBlockedDesc;
protected EnforcedAdmin mSuspendedAppsAdmin;
protected boolean mDndVisualEffectsSuppressed;
@@ -325,6 +327,15 @@
mVisibilityOverride.setDisabledByAdmin(mSuspendedAppsAdmin);
}
+ protected void setupBlockDesc(int summaryResId) {
+ mBlockedDesc = new FooterPreference(getPrefContext());
+ mBlockedDesc.setSelectable(false);
+ mBlockedDesc.setTitle(summaryResId);
+ mBlockedDesc.setEnabled(false);
+ mBlockedDesc.setOrder(50);
+ getPreferenceScreen().addPreference(mBlockedDesc);
+ }
+
private void setRestrictedIfNotificationFeaturesDisabled(CharSequence entry,
CharSequence entryValue, int keyguardNotificationFeatures) {
diff --git a/src/com/android/settings/notification/NotificationSoundPreference.java b/src/com/android/settings/notification/NotificationSoundPreference.java
index c007485..71684d5 100644
--- a/src/com/android/settings/notification/NotificationSoundPreference.java
+++ b/src/com/android/settings/notification/NotificationSoundPreference.java
@@ -1,3 +1,19 @@
+/*
+ * 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.notification;
import android.content.ContentResolver;
diff --git a/src/com/android/settings/notification/NotificationSwitchBarPreference.java b/src/com/android/settings/notification/NotificationSwitchBarPreference.java
new file mode 100644
index 0000000..7729543
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationSwitchBarPreference.java
@@ -0,0 +1,81 @@
+/*
+ * 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.notification;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.widget.ToggleSwitch;
+import com.android.settingslib.RestrictedLockUtils;
+
+public class NotificationSwitchBarPreference extends LayoutPreference {
+ private ToggleSwitch mSwitch;
+ private boolean mChecked;
+ private boolean mEnableSwitch = true;
+
+ public NotificationSwitchBarPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ mSwitch = (ToggleSwitch) holder.findViewById(android.R.id.switch_widget);
+ if (mSwitch != null) {
+ mSwitch.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mSwitch.isEnabled()) {
+ return;
+ }
+ mChecked = !mChecked;
+ setChecked(mChecked);
+ if (!callChangeListener(mChecked)) {
+ setChecked(!mChecked);
+ }
+ }
+ });
+ mSwitch.setChecked(mChecked);
+ mSwitch.setEnabled(mEnableSwitch);
+ }
+ }
+
+ public boolean isChecked() {
+ return mSwitch != null && mSwitch.isEnabled() && mChecked;
+ }
+
+ public void setChecked(boolean checked) {
+ mChecked = checked;
+ if (mSwitch != null) {
+ mSwitch.setChecked(checked);
+ }
+ }
+
+ public void setSwitchEnabled(boolean enabled) {
+ mEnableSwitch = enabled;
+ if (mSwitch != null) {
+ mSwitch.setEnabled(enabled);
+ }
+ }
+
+ public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
+ setSwitchEnabled(admin == null);
+ }
+}
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index dcd2b51..e7d5088 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.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 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 5b039b2..b39a2a0 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.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 @@
private SuggestionFeatureProvider mSuggestionFeatureProvider;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private AssistGestureFeatureProvider mAssistGestureFeatureProvider;
+ private UserFeatureProvider mUserFeatureProvider;
@Override
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -158,6 +161,14 @@
}
@Override
+ public UserFeatureProvider getUserFeatureProvider(Context context) {
+ if (mUserFeatureProvider == null) {
+ mUserFeatureProvider = new UserFeatureProviderImpl(context);
+ }
+ return mUserFeatureProvider;
+ }
+
+ @Override
public AssistGestureFeatureProvider getAssistGestureFeatureProvider() {
if (mAssistGestureFeatureProvider == null) {
mAssistGestureFeatureProvider = new AssistGestureFeatureProviderImpl();
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index f7882ae..d1d6945 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -65,6 +65,7 @@
import com.android.settings.location.ScanningSettings;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.nfc.PaymentSettings;
+import com.android.settings.notification.ChannelImportanceSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.SoundSettings;
import com.android.settings.notification.ZenModePrioritySettings;
@@ -179,6 +180,8 @@
R.drawable.ic_settings_accessibility);
addIndex(AccessibilityShortcutPreferenceFragment.class, NO_DATA_RES_ID,
R.drawable.ic_settings_accessibility);
+ addIndex(ChannelImportanceSettings.class, NO_DATA_RES_ID,
+ R.drawable.ic_settings_notifications);
}
private SearchIndexableResources() {
diff --git a/src/com/android/settings/users/UserFeatureProvider.java b/src/com/android/settings/users/UserFeatureProvider.java
new file mode 100644
index 0000000..9853255
--- /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<UserHandle> getUserProfiles();
+}
diff --git a/src/com/android/settings/users/UserFeatureProviderImpl.java b/src/com/android/settings/users/UserFeatureProviderImpl.java
new file mode 100644
index 0000000..b88c831
--- /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<UserHandle> 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 f5462b0..767edf8 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -5,3 +5,8 @@
com.android.settings.fuelgauge.AdvancedPowerUsageDetail
com.android.settings.deviceinfo.StorageProfileFragment
com.android.settings.wifi.details.WifiNetworkDetailsFragment
+com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera
+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/AppWithAdminGrantedPermissionsCounterTest.java b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
index aeb3826..4a7dc8f 100644
--- a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
@@ -18,15 +18,18 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Build;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.enterprise.DevicePolicyManagerWrapper;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,102 +86,31 @@
@Mock private DevicePolicyManagerWrapper mDevicePolicyManager;
private int mAppCount = -1;
+ private ApplicationInfo mApp1;
+ private ApplicationInfo mApp2;
+ private ApplicationInfo mApp3;
+ private ApplicationInfo mApp4;
+ private ApplicationInfo mApp5;
+ private ApplicationInfo mApp6;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+
+ mApp1 = buildInfo(APP_1_UID, APP_1, 0 /* flags */, Build.VERSION_CODES.M);
+ mApp2 = buildInfo(APP_2_UID, APP_2, 0 /* flags */, Build.VERSION_CODES.M);
+ mApp3 = buildInfo(APP_3_UID, APP_3, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP);
+ mApp4 = buildInfo(APP_4_UID, APP_4, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP);
+ mApp5 = buildInfo(APP_5_UID, APP_5, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP);
+ mApp6 = buildInfo(APP_6_UID, APP_6, 0 /* flags */, Build.VERSION_CODES.M);
}
private void verifyCountInstalledApps(boolean async) throws Exception {
- // There are two users.
- when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
- new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
- new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
-
- // The first user has five apps installed:
- // * app1 uses run-time permissions. It has been granted one of the permissions by the
- // admin. It should be counted.
- // * app2 uses run-time permissions. It has not been granted any of the permissions by the
- // admin. It should not be counted.
- // * app3 uses install-time permissions. It was installed by the admin and requested one of
- // the permissions. It should be counted.
- // * app4 uses install-time permissions. It was not installed by the admin but did request
- // one of the permissions. It should not be counted.
- // * app5 uses install-time permissions. It was installed by the admin but did not request
- // any of the permissions. It should not be counted.
- when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
- | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
- | PackageManager.MATCH_ANY_USER,
- MAIN_USER_ID)).thenReturn(Arrays.asList(
- buildInfo(APP_1_UID, APP_1, 0 /* flags */, Build.VERSION_CODES.M),
- buildInfo(APP_2_UID, APP_2, 0 /* flags */, Build.VERSION_CODES.M),
- buildInfo(APP_3_UID, APP_3, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP),
- buildInfo(APP_4_UID, APP_4, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP),
- buildInfo(APP_5_UID, APP_5, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP)));
-
- // Grant run-time permissions as appropriate.
- when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_1))
- .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
- when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_2))
- .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
- when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_2), anyObject()))
- .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
- when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_3), anyObject()))
- .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
- when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_4), anyObject()))
- .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
- when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_5), anyObject()))
- .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
-
- // Grant install-time permissions as appropriate.
- when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_1_UID)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
- when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_2_UID)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
- when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_3_UID))
- .thenReturn(PackageManager.PERMISSION_DENIED);
- when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_3_UID))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
- when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_4_UID))
- .thenReturn(PackageManager.PERMISSION_DENIED);
- when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_4_UID))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
- when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_5_UID)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
-
- // app3 and app5 were installed by enterprise policy.
- final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
- when(mPackageManager.getInstallReason(APP_1, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
- when(mPackageManager.getInstallReason(APP_2, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
- when(mPackageManager.getInstallReason(APP_3, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_POLICY);
- when(mPackageManager.getInstallReason(APP_4, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
- when(mPackageManager.getInstallReason(APP_5, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_POLICY);
-
- // The second user has one app installed. This app uses run-time permissions. It has been
- // granted both permissions by the admin. It should be counted.
- when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
- | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
- MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(
- buildInfo(APP_6_UID, APP_6, 0 /* flags */, Build.VERSION_CODES.M)));
-
- // Grant run-time permissions as appropriate.
- when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_6), anyObject()))
- .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
-
- // Grant install-time permissions as appropriate.
- when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_6_UID)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
-
- // app6 was not installed by enterprise policy.
- final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
- when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
- .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ configureUserManager();
+ configurePackageManager();
+ configureRunTimePermissions();
+ configureInstallTimePermissions();
// Count the number of all apps installed that were granted on or more permissions by the
// admin.
@@ -201,6 +133,28 @@
}
@Test
+ public void testIncludeInCount() throws Exception {
+ configurePackageManager();
+ configureRunTimePermissions();
+ configureInstallTimePermissions();
+
+ assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+ mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp1)).isTrue();
+
+ assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+ mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp2)).isFalse();
+
+ assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+ mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp3)).isTrue();
+
+ assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+ mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp4)).isFalse();
+
+ assertThat(AppWithAdminGrantedPermissionsCounter.includeInCount(PERMISSIONS,
+ mDevicePolicyManager, mPackageManager, mPackageManagerService, mApp5)).isFalse();
+ }
+
+ @Test
public void testCountInstalledAppsSync() throws Exception {
verifyCountInstalledApps(false /* async */);
}
@@ -210,6 +164,90 @@
verifyCountInstalledApps(true /* async */);
}
+ private void configureInstallTimePermissions() throws RemoteException {
+ when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_1_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_2_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_3_UID))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_3_UID))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_4_UID))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_4_UID))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_5_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_6_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ }
+
+ private void configureRunTimePermissions() {
+ when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_1))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_2))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_2), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_3), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_4), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_5), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_6), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ }
+
+ private void configurePackageManager() {
+ // The first user has five apps installed:
+ // * app1 uses run-time permissions. It has been granted one of the permissions by the
+ // admin. It should be counted.
+ // * app2 uses run-time permissions. It has not been granted any of the permissions by the
+ // admin. It should not be counted.
+ // * app3 uses install-time permissions. It was installed by the admin and requested one of
+ // the permissions. It should be counted.
+ // * app4 uses install-time permissions. It was not installed by the admin but did request
+ // one of the permissions. It should not be counted.
+ // * app5 uses install-time permissions. It was installed by the admin but did not request
+ // any of the permissions. It should not be counted.
+ when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_ANY_USER,
+ MAIN_USER_ID)).thenReturn(Arrays.asList(mApp1, mApp2, mApp3, mApp4, mApp5));
+ // The second user has one app installed. This app uses run-time permissions. It has been
+ // granted both permissions by the admin. It should be counted.
+ when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+ MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(mApp6));
+
+ // app3 and app5 were installed by enterprise policy.
+ final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
+ when(mPackageManager.getInstallReason(APP_1, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_2, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_3, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ when(mPackageManager.getInstallReason(APP_4, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_5, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ // app6 was not installed by enterprise policy.
+ final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
+ when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+
+ }
+
+ private void configureUserManager() {
+ // There are two users.
+ when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
+ new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
+ new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
+ }
+
private class AppWithAdminGrantedPermissionsCounterTestable extends
AppWithAdminGrantedPermissionsCounter {
public AppWithAdminGrantedPermissionsCounterTestable(String[] permissions) {
diff --git a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsListerTest.java b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsListerTest.java
new file mode 100644
index 0000000..73bba04
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsListerTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.app.admin.DevicePolicyManager;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.enterprise.DevicePolicyManagerWrapper;
+
+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.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link AppWithAdminGrantedPermissionsLister}.
+ */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public final class AppWithAdminGrantedPermissionsListerTest {
+
+ private final String APP_1 = "app1";
+ private final String APP_2 = "app2";
+ private final String APP_3 = "app3";
+ private final String APP_4 = "app4";
+ private final String APP_5 = "app5";
+ private final String APP_6 = "app6";
+
+ private final int MAIN_USER_ID = 0;
+ private final int MANAGED_PROFILE_ID = 10;
+
+ private final int PER_USER_UID_RANGE = 100000;
+ private final int APP_1_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 1;
+ private final int APP_2_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 2;
+ private final int APP_3_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 3;
+ private final int APP_4_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 4;
+ private final int APP_5_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 5;
+ private final int APP_6_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE + 1;
+
+ private final String PERMISSION_1 = "some.permission.1";
+ private final String PERMISSION_2 = "some.permission.2";
+ private final String[] PERMISSIONS = {PERMISSION_1, PERMISSION_2};
+
+ @Mock private UserManager mUserManager;
+ @Mock private PackageManagerWrapper mPackageManager;
+ @Mock private IPackageManagerWrapper mPackageManagerService;
+ @Mock private DevicePolicyManagerWrapper mDevicePolicyManager;
+
+ private List<UserAppInfo> mAppList = Collections.emptyList();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void verifyListInstalledApps() throws Exception {
+ // There are two users.
+ when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
+ new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
+ new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
+
+ // The first user has five apps installed:
+ // * app1 uses run-time permissions. It has been granted one of the permissions by the
+ // admin. It should be listed.
+ // * app2 uses run-time permissions. It has not been granted any of the permissions by the
+ // admin. It should not be listed.
+ // * app3 uses install-time permissions. It was installed by the admin and requested one of
+ // the permissions. It should be listed.
+ // * app4 uses install-time permissions. It was not installed by the admin but did request
+ // one of the permissions. It should not be listed.
+ // * app5 uses install-time permissions. It was installed by the admin but did not request
+ // any of the permissions. It should not be listed.
+ when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_ANY_USER,
+ MAIN_USER_ID)).thenReturn(Arrays.asList(
+ buildInfo(APP_1_UID, APP_1, 0 /* flags */, Build.VERSION_CODES.M),
+ buildInfo(APP_2_UID, APP_2, 0 /* flags */, Build.VERSION_CODES.M),
+ buildInfo(APP_3_UID, APP_3, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP),
+ buildInfo(APP_4_UID, APP_4, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP),
+ buildInfo(APP_5_UID, APP_5, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP)));
+
+ // Grant run-time permissions as appropriate.
+ when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_1))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_2))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_2), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_3), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_4), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_5), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+
+ // Grant install-time permissions as appropriate.
+ when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_1_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_2_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_3_UID))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_3_UID))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_4_UID))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_4_UID))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_5_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+
+ // app3 and app5 were installed by enterprise policy.
+ final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
+ when(mPackageManager.getInstallReason(APP_1, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_2, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_3, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ when(mPackageManager.getInstallReason(APP_4, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_5, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+ // The second user has one app installed. This app uses run-time permissions. It has been
+ // granted both permissions by the admin. It should be listed.
+ when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+ MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(
+ buildInfo(APP_6_UID, APP_6, 0 /* flags */, Build.VERSION_CODES.M)));
+
+ // Grant run-time permissions as appropriate.
+ when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_6), anyObject()))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+
+ // Grant install-time permissions as appropriate.
+ when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_6_UID)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+
+ // app6 was not installed by enterprise policy.
+ final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
+ when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+
+ // List all apps installed that were granted one or more permissions by the
+ // admin.
+ (new AppWithAdminGrantedPermissionsListerTestable(PERMISSIONS)).execute();
+
+ // Wait for the background task to finish.
+ ShadowApplication.runBackgroundTasks();
+ assertThat(mAppList.size()).isEqualTo(3);
+ InstalledAppListerTest.verifyListUniqueness(mAppList);
+
+ assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_1, MAIN_USER_ID)).isTrue();
+ assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_2, MAIN_USER_ID)).isFalse();
+ assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_3, MAIN_USER_ID)).isTrue();
+ assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_4, MAIN_USER_ID)).isFalse();
+ assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_5, MAIN_USER_ID)).isFalse();
+ assertThat(InstalledAppListerTest.checkAppFound(mAppList, APP_6, MANAGED_PROFILE_ID)).
+ isTrue();
+
+ // Verify that installed packages were retrieved the current user and the user's managed
+ // profile only.
+ verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(), eq(MAIN_USER_ID));
+ verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(),
+ eq(MANAGED_PROFILE_ID));
+ verify(mPackageManager, atLeast(0)).getInstallReason(anyObject(), anyObject());
+ verifyNoMoreInteractions(mPackageManager);
+ }
+
+ private class AppWithAdminGrantedPermissionsListerTestable extends
+ AppWithAdminGrantedPermissionsLister {
+
+ public AppWithAdminGrantedPermissionsListerTestable(String[] permissions) {
+ super(permissions, mPackageManager, mPackageManagerService,
+ mDevicePolicyManager, mUserManager);
+ }
+
+ @Override
+ protected void onAppListBuilt(List<UserAppInfo> list) {
+ mAppList = list;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
index f46bb90..4c4ec46 100644
--- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
@@ -20,19 +20,20 @@
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;
import com.android.settings.enterprise.DevicePolicyManagerWrapper;
import com.android.settings.testutils.ApplicationTestUtils;
import com.android.settings.testutils.shadow.ShadowUserManager;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,8 +42,9 @@
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Set;
+import java.util.List;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -76,6 +78,7 @@
private ApplicationFeatureProvider mProvider;
private int mAppCount = -1;
+ private List<UserAppInfo> mAppList = null;
@Before
public void setUp() {
@@ -106,6 +109,22 @@
}
@Test
+ public void testListPolicyInstalledApps() {
+ setUpUsersAndInstalledApps();
+
+ when(mPackageManager.getInstallReason(APP_1, new UserHandle(MAIN_USER_ID)))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_2, new UserHandle(MANAGED_PROFILE_ID)))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+ mAppList = null;
+ mProvider.listPolicyInstalledApps((list) -> mAppList = list);
+ assertThat(mAppList).isNotNull();
+ assertThat(mAppList.size()).isEqualTo(1);
+ assertThat(mAppList.get(0).appInfo.packageName).isEqualTo(APP_2);
+ }
+
+ @Test
public void testCalculateNumberOfInstalledAppsSync() {
verifyCalculateNumberOfPolicyInstalledApps(false /* async */);
}
@@ -139,7 +158,6 @@
ShadowApplication.runBackgroundTasks();
}
assertThat(mAppCount).isEqualTo(2);
-
}
@Test
@@ -153,9 +171,43 @@
}
@Test
+ public void testListAppsWithAdminGrantedPermissions()
+ throws Exception {
+ setUpUsersAndInstalledApps();
+
+ when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ when(mDevicePolicyManager.getPermissionGrantState(null, APP_2, PERMISSION))
+ .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION, APP_1_UID))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mPackageManagerService.checkUidPermission(PERMISSION, APP_2_UID))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mPackageManager.getInstallReason(APP_1, new UserHandle(MAIN_USER_ID)))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_2, new UserHandle(MANAGED_PROFILE_ID)))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+ mAppList = null;
+ mProvider.listAppsWithAdminGrantedPermissions(new String[] {PERMISSION},
+ (list) -> mAppList = list);
+ assertThat(mAppList).isNotNull();
+ assertThat(mAppList.size()).isEqualTo(2);
+ assertThat(Arrays.asList(mAppList.get(0).appInfo.packageName,
+ mAppList.get(1).appInfo.packageName).containsAll(Arrays.asList(APP_1, APP_2)))
+ .isTrue();
+ }
+
+ @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);
@@ -176,17 +228,21 @@
when(mPackageManagerService.findPersistentPreferredActivity(sendIntent, MANAGED_PROFILE_ID))
.thenReturn(null);
- final Set<ApplicationFeatureProvider.PersistentPreferredActivityInfo> 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<UserAppInfo> expectedMainUserActivities = new ArrayList<>();
+ expectedMainUserActivities.add(new UserAppInfo(mainUser,
+ new ApplicationInfo(app1.activityInfo.applicationInfo)));
+ final List<UserAppInfo> 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() {
@@ -208,8 +264,11 @@
}
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 0000000..ad05c27
--- /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/applications/InstalledAppCounterTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
index 46a3359..37fa9d7 100644
--- a/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
@@ -22,12 +22,14 @@
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 com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.shadow.ShadowUserManager;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,16 +77,38 @@
private final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE;
private final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE;
- @Mock private UserManager mUserManager;
- @Mock private Context mContext;
- @Mock private PackageManagerWrapper mPackageManager;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private PackageManagerWrapper mPackageManager;
private int mInstalledAppCount = -1;
+ private ApplicationInfo mApp1;
+ private ApplicationInfo mApp2;
+ private ApplicationInfo mApp3;
+ private ApplicationInfo mApp4;
+ private ApplicationInfo mApp5;
+ private ApplicationInfo mApp6;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+
+ mApp1 = buildInfo(MAIN_USER_APP_UID, APP_1,
+ ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */);
+ mApp2 = buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */,
+ 0 /* targetSdkVersion */);
+ mApp3 = buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM,
+ 0 /* targetSdkVersion */);
+ mApp4 = buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM,
+ 0 /* targetSdkVersion */);
+ mApp5 = buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */,
+ 0 /* targetSdkVersion */);
+ mApp6 = buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM,
+ 0 /* targetSdkVersion */);
}
private void expectQueryIntentActivities(int userId, String packageName, boolean launchable) {
@@ -93,7 +117,7 @@
eq(PackageManager.GET_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE),
eq(userId))).thenReturn(launchable ? Arrays.asList(new ResolveInfo())
- : new ArrayList<ResolveInfo>());
+ : new ArrayList<ResolveInfo>());
}
private void testCountInstalledAppsAcrossAllUsers(boolean async) {
@@ -101,58 +125,7 @@
when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
-
- // The first user has four apps installed:
- // * app1 is an updated system app. It should be counted.
- // * app2 is a user-installed app. It should be counted.
- // * app3 is a system app that provides a launcher icon. It should be counted.
- // * app4 is a system app that provides no launcher icon. It should not be counted.
- when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
- | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
- | PackageManager.MATCH_ANY_USER,
- MAIN_USER_ID)).thenReturn(Arrays.asList(
- buildInfo(MAIN_USER_APP_UID, APP_1,
- ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */),
- buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */,
- 0 /* targetSdkVersion */),
- buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM,
- 0 /* targetSdkVersion */),
- buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM,
- 0 /* targetSdkVersion */)));
- // For system apps, InstalledAppCounter checks whether they handle the default launcher
- // intent to decide whether to include them in the count of installed apps or not.
- expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */);
- expectQueryIntentActivities(MAIN_USER_ID, APP_4, false /* launchable */);
-
- // app1, app3 and app4 are installed by enterprise policy.
- final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
- when(mPackageManager.getInstallReason(APP_1, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_POLICY);
- when(mPackageManager.getInstallReason(APP_2, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
- when(mPackageManager.getInstallReason(APP_3, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_POLICY);
- when(mPackageManager.getInstallReason(APP_4, mainUser))
- .thenReturn(PackageManager.INSTALL_REASON_POLICY);
-
- // The second user has two apps installed:
- // * app5 is a user-installed app. It should be counted.
- // * app6 is a system app that provides a launcher icon. It should be counted.
- when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
- | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
- MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(
- buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */,
- 0 /* targetSdkVersion */),
- buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM,
- 0 /* targetSdkVersion */)));
- expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */);
-
- // app5 is installed by enterprise policy.
- final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
- when(mPackageManager.getInstallReason(APP_5, managedProfileUser))
- .thenReturn(PackageManager.INSTALL_REASON_POLICY);
- when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
- .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ configurePackageManager();
// Count the number of all apps installed, irrespective of install reason.
count(InstalledAppCounter.IGNORE_INSTALL_REASON, async);
@@ -173,6 +146,36 @@
}
@Test
+ public void testIncludeInCount() {
+ configurePackageManager();
+ assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+ mPackageManager, mApp1)).isTrue();
+ assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+ mPackageManager, mApp2)).isTrue();
+ assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+ mPackageManager, mApp3)).isTrue();
+ assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+ mPackageManager, mApp4)).isFalse();
+ assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+ mPackageManager, mApp5)).isTrue();
+ assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON,
+ mPackageManager, mApp6)).isTrue();
+
+ assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+ mPackageManager, mApp1)).isTrue();
+ assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+ mPackageManager, mApp2)).isFalse();
+ assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+ mPackageManager, mApp3)).isTrue();
+ assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+ mPackageManager, mApp4)).isFalse();
+ assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+ mPackageManager, mApp5)).isTrue();
+ assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY,
+ mPackageManager, mApp6)).isFalse();
+ }
+
+ @Test
public void testCountInstalledAppsAcrossAllUsersSync() {
testCountInstalledAppsAcrossAllUsers(false /* async */);
}
@@ -194,6 +197,48 @@
}
}
+ private void configurePackageManager() {
+ // The first user has four apps installed:
+ // * app1 is an updated system app. It should be counted.
+ // * app2 is a user-installed app. It should be counted.
+ // * app3 is a system app that provides a launcher icon. It should be counted.
+ // * app4 is a system app that provides no launcher icon. It should not be counted.
+ when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_ANY_USER,
+ MAIN_USER_ID)).thenReturn(Arrays.asList(mApp1, mApp2, mApp3, mApp4));
+ // For system apps, InstalledAppCounter checks whether they handle the default launcher
+ // intent to decide whether to include them in the count of installed apps or not.
+ expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */);
+ expectQueryIntentActivities(MAIN_USER_ID, APP_4, false /* launchable */);
+
+ // app1, app3 and app4 are installed by enterprise policy.
+ final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
+ when(mPackageManager.getInstallReason(APP_1, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ when(mPackageManager.getInstallReason(APP_2, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_3, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ when(mPackageManager.getInstallReason(APP_4, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+ // The second user has two apps installed:
+ // * app5 is a user-installed app. It should be counted.
+ // * app6 is a system app that provides a launcher icon. It should be counted.
+ when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,MANAGED_PROFILE_ID))
+ .thenReturn(Arrays.asList(mApp5, mApp6));
+ expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */);
+
+ // app5 is installed by enterprise policy.
+ final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
+ when(mPackageManager.getInstallReason(APP_5, managedProfileUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ }
+
private class InstalledAppCounterTestable extends InstalledAppCounter {
public InstalledAppCounterTestable(int installReason) {
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppListerTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppListerTest.java
new file mode 100644
index 0000000..76421c2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/InstalledAppListerTest.java
@@ -0,0 +1,233 @@
+/*
+ * 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.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+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.ArgumentMatcher;
+import org.mockito.Mock;
+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.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link InstalledAppLister}.
+ */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public final class InstalledAppListerTest {
+
+ private final String APP_1 = "app1";
+ private final String APP_2 = "app2";
+ private final String APP_3 = "app3";
+ private final String APP_4 = "app4";
+ private final String APP_5 = "app5";
+ private final String APP_6 = "app6";
+
+ private final int MAIN_USER_ID = 0;
+ private final int MANAGED_PROFILE_ID = 10;
+
+ private final int PER_USER_UID_RANGE = 100000;
+ private final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE;
+ private final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE;
+
+ @Mock private UserManager mUserManager;
+ @Mock private PackageManagerWrapper mPackageManager;
+
+ private List<UserAppInfo> mInstalledAppList = Collections.emptyList();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private void expectQueryIntentActivities(int userId, String packageName, boolean launchable) {
+ when(mPackageManager.queryIntentActivitiesAsUser(
+ argThat(new IsLaunchIntentFor(packageName)),
+ eq(PackageManager.GET_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE),
+ eq(userId))).thenReturn(launchable ? Arrays.asList(new ResolveInfo())
+ : new ArrayList<ResolveInfo>());
+ }
+
+ @Test
+ public void testCountInstalledAppsAcrossAllUsers() {
+ // There are two users.
+ when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
+ new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
+ new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)));
+
+ // The first user has four apps installed:
+ // * app1 is an updated system app. It should be listed.
+ // * app2 is a user-installed app. It should be listed.
+ // * app3 is a system app that provides a launcher icon. It should be listed.
+ // * app4 is a system app that provides no launcher icon. It should not be listed.
+ when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_ANY_USER,
+ MAIN_USER_ID)).thenReturn(Arrays.asList(
+ buildInfo(MAIN_USER_APP_UID, APP_1,
+ ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */),
+ buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */,
+ 0 /* targetSdkVersion */),
+ buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM,
+ 0 /* targetSdkVersion */),
+ buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM,
+ 0 /* targetSdkVersion */)));
+ // For system apps, InstalledAppLister checks whether they handle the default launcher
+ // intent to decide whether to include them in the list of installed apps or not.
+ expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */);
+ expectQueryIntentActivities(MAIN_USER_ID, APP_4, false /* launchable */);
+
+ // app1, app3 and app4 are installed by enterprise policy.
+ final UserHandle mainUser = new UserHandle(MAIN_USER_ID);
+ when(mPackageManager.getInstallReason(APP_1, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ when(mPackageManager.getInstallReason(APP_2, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+ when(mPackageManager.getInstallReason(APP_3, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ when(mPackageManager.getInstallReason(APP_4, mainUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+
+ // The second user has two apps installed:
+ // * app5 is a user-installed app. It should be listed.
+ // * app6 is a system app that provides a launcher icon. It should be listed.
+ when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+ MANAGED_PROFILE_ID)).thenReturn(Arrays.asList(
+ buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */,
+ 0 /* targetSdkVersion */),
+ buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM,
+ 0 /* targetSdkVersion */)));
+ expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */);
+
+ // app5 is installed by enterprise policy.
+ final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID);
+ when(mPackageManager.getInstallReason(APP_5, managedProfileUser))
+ .thenReturn(PackageManager.INSTALL_REASON_POLICY);
+ when(mPackageManager.getInstallReason(APP_6, managedProfileUser))
+ .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN);
+
+ // List apps, considering apps installed by enterprise policy only.
+ mInstalledAppList = Collections.emptyList();
+ final InstalledAppListerTestable counter = new InstalledAppListerTestable();
+ counter.execute();
+ // Wait for the background task to finish.
+ ShadowApplication.runBackgroundTasks();
+
+ assertThat(mInstalledAppList.size()).isEqualTo(3);
+
+ assertThat(checkAppFound(mInstalledAppList, APP_1, MAIN_USER_ID)).isTrue();
+ assertThat(checkAppFound(mInstalledAppList, APP_2, MAIN_USER_ID)).isFalse();
+ assertThat(checkAppFound(mInstalledAppList, APP_3, MAIN_USER_ID)).isTrue();
+ assertThat(checkAppFound(mInstalledAppList, APP_4, MAIN_USER_ID)).isFalse();
+ assertThat(checkAppFound(mInstalledAppList, APP_5, MANAGED_PROFILE_ID)).isTrue();
+ assertThat(checkAppFound(mInstalledAppList, APP_6, MANAGED_PROFILE_ID)).isFalse();
+
+ // Verify that installed packages were retrieved for the current user and the user's
+ // managed profile.
+ verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(), eq(MAIN_USER_ID));
+ verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(),
+ eq(MANAGED_PROFILE_ID));
+ verify(mPackageManager, atLeast(0)).queryIntentActivitiesAsUser(anyObject(), anyInt(),
+ anyInt());
+ }
+
+ public static boolean checkAppFound(List<UserAppInfo> mInstalledAppList, String appId,
+ int userId) {
+ for (UserAppInfo info : mInstalledAppList) {
+ if (appId.equals(info.appInfo.packageName) && (info.userInfo.id == userId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void verifyListUniqueness(List<UserAppInfo> list) {
+ assertThat((new HashSet<>(list)).size()).isEqualTo(list.size());
+ }
+
+ private class InstalledAppListerTestable extends InstalledAppLister {
+ public InstalledAppListerTestable() {
+ super(mPackageManager, mUserManager);
+ }
+
+ @Override
+ protected void onAppListBuilt(List<UserAppInfo> list) {
+ mInstalledAppList = list;
+ }
+ }
+
+ private static class IsLaunchIntentFor extends ArgumentMatcher<Intent> {
+ private final String mPackageName;
+
+ IsLaunchIntentFor(String packageName) {
+ mPackageName = packageName;
+ }
+
+ @Override
+ public boolean matches(Object i) {
+ final Intent intent = (Intent) i;
+ if (intent == null) {
+ return false;
+ }
+ if (!Intent.ACTION_MAIN.equals(intent.getAction())) {
+ return false;
+ }
+ final Set<String> categories = intent.getCategories();
+ if (categories == null || categories.size() != 1 ||
+ !categories.contains(Intent.CATEGORY_LAUNCHER)) {
+ return false;
+ }
+ if (!mPackageName.equals(intent.getPackage())) {
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
index f001bd1..96ce081 100644
--- a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java
@@ -17,8 +17,6 @@
package com.android.settings.enterprise;
import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
import android.support.v7.preference.Preference;
import com.android.settings.R;
@@ -28,24 +26,21 @@
import org.junit.Before;
import org.junit.Test;
import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Common base for testing subclasses of {@link AdminGrantedPermissionsPreferenceControllerBase}.
*/
public abstract class AdminGrantedPermissionsPreferenceControllerTestBase {
-
protected final String mKey;
protected final String[] mPermissions;
protected final String mPermissionGroup;
@@ -123,19 +118,8 @@
@Test
public void testHandlePreferenceTreeClick() {
- final Preference preference = new Preference(mContext, null, 0, 0);
- preference.setKey(mKey);
-
- assertThat(mController.handlePreferenceTreeClick(preference)).isTrue();
-
- final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).startActivity(argumentCaptor.capture());
-
- final Intent intent = argumentCaptor.getValue();
-
- assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_APPS);
- assertThat(intent.getStringExtra(Intent.EXTRA_PERMISSION_NAME)).
- isEqualTo(mPermissionGroup);
+ assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0)))
+ .isFalse();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java b/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java
new file mode 100644
index 0000000..1936f80
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/ApplicationListFragmentTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.UserInfo;
+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.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.UserAppInfo;
+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.ArrayList;
+import java.util.List;
+
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+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 ApplicationListFragmentTest {
+ private static final int USER_ID = 0;
+ private static final int USER_APP_UID = 0;
+
+ private static final String APP = "APP";
+
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private PreferenceManager mPreferenceManager;
+
+ private ApplicationListFragment mFragment;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = ShadowApplication.getInstance().getApplicationContext();
+ when(mPreferenceManager.getContext()).thenReturn(mContext);
+
+ mFragment = new ApplicationListFragmentTestable(mPreferenceManager, mScreen);
+ }
+
+ @Test
+ public void getLogTag() {
+ assertThat(mFragment.getLogTag())
+ .isEqualTo("EnterprisePrivacySettings");
+ }
+
+ @Test
+ public void getScreenResource() {
+ assertThat(mFragment.getPreferenceScreenResId())
+ .isEqualTo(R.xml.app_list_disclosure_settings);
+ }
+
+ @Test
+ public void getPreferenceControllers() {
+ final List<PreferenceController> controllers = mFragment.getPreferenceControllers(mContext);
+ assertThat(controllers).isNotNull();
+ assertThat(controllers.size()).isEqualTo(1);
+ int position = 0;
+ assertThat(controllers.get(position++)).isInstanceOf(
+ 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;
+ private final PreferenceScreen mPreferenceScreen;
+
+ public ApplicationListFragmentTestable(PreferenceManager preferenceManager,
+ PreferenceScreen screen) {
+ this.mPreferenceManager = preferenceManager;
+ this.mPreferenceScreen = screen;
+ }
+
+ @Override
+ public void buildApplicationList(Context context,
+ ApplicationFeatureProvider.ListOfAppsCallback callback) {
+ final List<UserAppInfo> apps = new ArrayList<>();
+ final UserInfo user = new UserInfo(USER_ID, "main", UserInfo.FLAG_ADMIN);
+ apps.add(new UserAppInfo(user, buildInfo(USER_APP_UID, APP, 0, 0)));
+ callback.onListOfAppsResult(apps);
+ }
+
+ @Override
+ public PreferenceManager getPreferenceManager() {
+ return mPreferenceManager;
+ }
+
+ @Override
+ public PreferenceScreen getPreferenceScreen() {
+ return mPreferenceScreen;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.VIEW_UNKNOWN;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java
new file mode 100644
index 0000000..56a6c62
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/ApplicationListPreferenceControllerTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.PackageManager;
+import android.content.pm.UserInfo;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.applications.UserAppInfo;
+
+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.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.settings.testutils.ApplicationTestUtils.buildInfo;
+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.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 ApplicationListPreferenceControllerTest {
+
+ private static final int MAIN_USER_ID = 0;
+
+ private static final int MANAGED_PROFILE_ID = 10;
+ private static final int PER_USER_UID_RANGE = 100000;
+ private static final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE;
+ private static final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE;
+
+ private static final String APP_1 = "APP_1";
+ private static final String APP_2 = "APP_2";
+ private static final String APP_3 = "APP_3";
+
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private PackageManager mPackageManager;
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private SettingsPreferenceFragment mFragment;
+
+ private Context mContext;
+ private ApplicationListPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ final ShadowApplication shadowContext = ShadowApplication.getInstance();
+ mContext = shadowContext.getApplicationContext();
+ when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+ when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
+ when(mPackageManager.getText(eq(APP_1), anyInt(), any())).thenReturn(APP_1);
+ when(mPackageManager.getText(eq(APP_2), anyInt(), any())).thenReturn(APP_2);
+ when(mPackageManager.getText(eq(APP_3), anyInt(), any())).thenReturn(APP_3);
+
+ mController = new ApplicationListPreferenceController(mContext, new ThreeAppsBuilder(),
+ mPackageManager, mFragment);
+ }
+
+ @Test
+ public void checkNumberAndTitlesOfApps() {
+ ArgumentCaptor<Preference> apps = ArgumentCaptor.forClass(Preference.class);
+ verify(mScreen, times(3)).addPreference(apps.capture());
+ final Set<String> expectedPackages = new HashSet<>(Arrays.asList(APP_1, APP_2, APP_3));
+ final Set<String> packages = new HashSet<>();
+
+ for (Preference p : apps.getAllValues()) {
+ packages.add(p.getTitle().toString());
+ }
+ assertThat(packages).isEqualTo(expectedPackages);
+ }
+
+ @Test
+ public void isAvailable() {
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void getPreferenceKey() {
+ assertThat(mController.getPreferenceKey()).isNull();
+ }
+
+ private static class ThreeAppsBuilder
+ implements ApplicationListPreferenceController.ApplicationListBuilder {
+ @Override
+ public void buildApplicationList(Context context,
+ ApplicationFeatureProvider.ListOfAppsCallback callback) {
+ final List<UserAppInfo> apps = new ArrayList<>();
+ final UserInfo user = new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN);
+ apps.add(new UserAppInfo(user, buildInfo(MAIN_USER_APP_UID, APP_1, 0, 0)));
+ apps.add(new UserAppInfo(user, buildInfo(MAIN_USER_APP_UID, APP_2, 0, 0)));
+ apps.add(new UserAppInfo(user, buildInfo(MANAGED_PROFILE_APP_UID, APP_3, 0, 0)));
+ callback.onListOfAppsResult(apps);
+ }
+ }
+}
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 0000000..e5c877f
--- /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<PreferenceController> 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 0000000..6a1a7f7
--- /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<Preference> 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 3455e80..34d9b24 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 @@
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 @@
@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 @@
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<UserAppInfo> 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<ApplicationFeatureProvider.PersistentPreferredActivityInfo> 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<UserHandle> 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 @@
@Test
public void testIsAvailable() {
- when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(
- anyObject())).thenReturn(
- new ArraySet<ApplicationFeatureProvider.PersistentPreferredActivityInfo>());
+ when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(anyInt(),
+ anyObject())).thenReturn(new ArrayList<UserAppInfo>());
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 f4f1c63..68333e7 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.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 final SurveyFeatureProvider surveyFeatureProvider;
public final SecurityFeatureProvider securityFeatureProvider;
public final SuggestionFeatureProvider suggestionsFeatureProvider;
+ public final UserFeatureProvider userFeatureProvider;
public final AssistGestureFeatureProvider assistGestureFeatureProvider;
/**
@@ -86,6 +88,7 @@
surveyFeatureProvider = mock(SurveyFeatureProvider.class);
securityFeatureProvider = mock(SecurityFeatureProvider.class);
suggestionsFeatureProvider = mock(SuggestionFeatureProvider.class);
+ userFeatureProvider = mock(UserFeatureProvider.class);
assistGestureFeatureProvider = mock(AssistGestureFeatureProvider.class);
}
@@ -145,6 +148,11 @@
}
@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 0000000..c794cdd
--- /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<UserHandle> expected =
+ Arrays.asList(new UserHandle(FIRST_USER_ID), new UserHandle(SECOND_USER_ID));
+ when(mUserManager.getUserProfiles()).thenReturn(expected);
+ final List<UserHandle> userProfiles = mFeatureProvider.getUserProfiles();
+ assertThat(userProfiles).isEqualTo(expected);
+ }
+}