diff --git a/res/values/strings.xml b/res/values/strings.xml index 019c444fd13..9b1eab50c07 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8072,6 +8072,11 @@ %d app allowed access to your camera by your admin %d apps allowed access to your camera by your admin + + + %d default app set by your admin + %d default apps set by your admin + Always-on VPN turned on diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml index 4bb2a8c746d..5fcb5b29ba2 100644 --- a/res/xml/enterprise_privacy_settings.xml +++ b/res/xml/enterprise_privacy_settings.xml @@ -75,6 +75,10 @@ android:key="enterprise_privacy_number_camera_access_packages" settings:allowDividerBelow="true" settings:multiLine="true"/> + findPersistentPreferredActivities(Intent[] intents); + /** * Callback that receives the number of packages installed on the device. */ interface NumberOfAppsCallback { 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; + } + } } diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java index ff7f0f0db01..2d2bce060c1 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java +++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java @@ -18,25 +18,34 @@ package com.android.settings.applications; import android.app.Fragment; import android.content.Context; -import android.content.pm.IPackageManager; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ComponentInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; +import android.os.RemoteException; +import android.os.UserHandle; import android.os.UserManager; +import android.util.ArraySet; import android.view.View; import com.android.settings.enterprise.DevicePolicyManagerWrapper; import java.util.List; +import java.util.Set; public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvider { private final Context mContext; private final PackageManagerWrapper mPm; - private final IPackageManager mPms; + private final IPackageManagerWrapper mPms; private final DevicePolicyManagerWrapper mDpm; private final UserManager mUm; public ApplicationFeatureProviderImpl(Context context, PackageManagerWrapper pm, - IPackageManager pms, DevicePolicyManagerWrapper dpm) { + IPackageManagerWrapper pms, DevicePolicyManagerWrapper dpm) { mContext = context.getApplicationContext(); mPm = pm; mPms = pms; @@ -61,6 +70,39 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide callback).execute(); } + @Override + public Set findPersistentPreferredActivities( + Intent[] intents) { + final Set activities = new ArraySet<>(); + final List users = mUm.getUserProfiles(); + 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)); + } + } + } catch (RemoteException exception) { + } + } + + } + return activities; + } + private static class AllUserInstalledAppCounter extends InstalledAppCounter { private NumberOfAppsCallback mCallback; @@ -86,7 +128,7 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide private NumberOfAppsCallback mCallback; AllUserAppWithAdminGrantedPermissionsCounter(Context context, String[] permissions, - PackageManagerWrapper packageManager, IPackageManager packageManagerService, + PackageManagerWrapper packageManager, IPackageManagerWrapper packageManagerService, DevicePolicyManagerWrapper devicePolicyManager, NumberOfAppsCallback callback) { super(context, permissions, packageManager, packageManagerService, devicePolicyManager); mCallback = callback; diff --git a/src/com/android/settings/applications/IPackageManagerWrapper.java b/src/com/android/settings/applications/IPackageManagerWrapper.java new file mode 100644 index 00000000000..f88598527e3 --- /dev/null +++ b/src/com/android/settings/applications/IPackageManagerWrapper.java @@ -0,0 +1,44 @@ +/* + * 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.ResolveInfo; +import android.os.RemoteException; + +/** + * This interface replicates a subset of the android.content.pm.IPackageManager (PMS). The interface + * exists so that we can use a thin wrapper around the PMS in production code and a mock in tests. + * We cannot directly mock or shadow the PMS, because some of the methods we rely on are newer than + * the API version supported by Robolectric. + */ +public interface IPackageManagerWrapper { + + /** + * Calls {@code IPackageManager.checkUidPermission()}. + * + * @see android.content.pm.IPackageManager#checkUidPermission + */ + int checkUidPermission(String permName, int uid) throws RemoteException; + + /** + * Calls {@code IPackageManager.findPersistentPreferredActivity()}. + * + * @see android.content.pm.IPackageManager#findPersistentPreferredActivity + */ + ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) throws RemoteException; +} diff --git a/src/com/android/settings/applications/IPackageManagerWrapperImpl.java b/src/com/android/settings/applications/IPackageManagerWrapperImpl.java new file mode 100644 index 00000000000..5ea15b9ad64 --- /dev/null +++ b/src/com/android/settings/applications/IPackageManagerWrapperImpl.java @@ -0,0 +1,42 @@ +/* + * 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.IPackageManager; +import android.content.pm.ResolveInfo; +import android.os.RemoteException; + +public class IPackageManagerWrapperImpl implements IPackageManagerWrapper { + + private final IPackageManager mPms; + + public IPackageManagerWrapperImpl(IPackageManager pms) { + mPms = pms; + } + + @Override + public int checkUidPermission(String permName, int uid) throws RemoteException { + return mPms.checkUidPermission(permName, uid); + } + + @Override + public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) + throws RemoteException { + return mPms.findPersistentPreferredActivity(intent, userId); + } +} diff --git a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java index 7d6d8e0ef92..75d3b1070ba 100644 --- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java +++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java @@ -59,6 +59,7 @@ public class EnterprisePrivacySettings extends DashboardFragment { controllers.add(new AdminGrantedLocationPermissionsPreferenceController(context)); controllers.add(new AdminGrantedMicrophonePermissionPreferenceController(context)); controllers.add(new AdminGrantedCameraPermissionPreferenceController(context)); + controllers.add(new EnterpriseSetDefaultAppsPreferenceController(context)); controllers.add(new AlwaysOnVpnPrimaryUserPreferenceController(context)); controllers.add(new AlwaysOnVpnManagedProfilePreferenceController(context)); controllers.add(new GlobalHttpProxyPreferenceController(context)); diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java new file mode 100644 index 00000000000..23627cdb0fa --- /dev/null +++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java @@ -0,0 +1,101 @@ +/* + * 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.Intent; +import android.net.Uri; +import android.provider.ContactsContract; +import android.provider.MediaStore; +import android.support.v7.preference.Preference; + +import com.android.settings.R; +import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.core.PreferenceController; +import com.android.settings.overlay.FeatureFactory; + +public class EnterpriseSetDefaultAppsPreferenceController extends PreferenceController { + + private static final String KEY_DEFAULT_APPS = "number_enterprise_set_default_apps"; + private final ApplicationFeatureProvider mFeatureProvider; + + public EnterpriseSetDefaultAppsPreferenceController(Context context) { + super(context); + mFeatureProvider = FeatureFactory.getFactory(context) + .getApplicationFeatureProvider(context); + } + + @Override + public void updateState(Preference preference) { + // 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(); + + if (num == 0) { + preference.setVisible(false); + } else { + preference.setVisible(true); + preference.setTitle(mContext.getResources().getQuantityString( + R.plurals.enterprise_privacy_number_enterprise_set_default_apps, + num, num)); + } + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_DEFAULT_APPS; + } + + private static Intent buildIntent(String action, String category, String protocol, + String type) { + final Intent intent = new Intent(action); + if (category != null) { + intent.addCategory(category); + } + if (protocol != null) { + intent.setData(Uri.parse(protocol)); + } + if (type != null) { + intent.setType(type); + } + return intent; + } +} diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index 2c812416502..c45bf0e2cce 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -25,6 +25,7 @@ import android.support.annotation.Keep; import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.applications.ApplicationFeatureProviderImpl; +import com.android.settings.applications.IPackageManagerWrapperImpl; import com.android.settings.applications.PackageManagerWrapperImpl; import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.core.instrumentation.MetricsFeatureProviderImpl; @@ -90,7 +91,7 @@ public class FeatureFactoryImpl extends FeatureFactory { if (mApplicationFeatureProvider == null) { mApplicationFeatureProvider = new ApplicationFeatureProviderImpl(context, new PackageManagerWrapperImpl(context.getPackageManager()), - AppGlobals.getPackageManager(), + new IPackageManagerWrapperImpl(AppGlobals.getPackageManager()), new DevicePolicyManagerWrapperImpl((DevicePolicyManager) context .getSystemService(Context.DEVICE_POLICY_SERVICE))); } diff --git a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java index 9fc416d4fef..3dd3a651f97 100644 --- a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java @@ -18,7 +18,6 @@ package com.android.settings.applications; import android.app.admin.DevicePolicyManager; import android.content.Context; -import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Build; @@ -79,7 +78,7 @@ public final class AppWithAdminGrantedPermissionsCounterTest { @Mock private Context mContext; @Mock private PackageManagerWrapper mPackageManager; - @Mock private IPackageManager mPackageManagerService; + @Mock private IPackageManagerWrapper mPackageManagerService; @Mock private DevicePolicyManagerWrapper mDevicePolicyManager; private List mUsersToCount; diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java index aba4a125259..2f344dc029b 100644 --- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java @@ -18,12 +18,15 @@ package com.android.settings.applications; import android.app.admin.DevicePolicyManager; import android.content.Context; -import android.content.pm.IPackageManager; +import android.content.Intent; +import android.content.pm.ActivityInfo; 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; @@ -39,10 +42,11 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; import java.util.Arrays; +import java.util.Set; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import org.robolectric.shadows.ShadowApplication; + /** * Tests for {@link ApplicationFeatureProviderImpl}. */ @@ -66,7 +70,7 @@ public final class ApplicationFeatureProviderImplTest { private @Mock UserManager mUserManager; private @Mock Context mContext; private @Mock PackageManagerWrapper mPackageManager; - @Mock private IPackageManager mPackageManagerService; + @Mock private IPackageManagerWrapper mPackageManagerService; @Mock private DevicePolicyManagerWrapper mDevicePolicyManager; private ApplicationFeatureProvider mProvider; @@ -139,6 +143,43 @@ public final class ApplicationFeatureProviderImplTest { } + @Test + public void testFindPersistentPreferredActivities() throws Exception { + when(mUserManager.getUserProfiles()).thenReturn(Arrays.asList(new UserHandle(MAIN_USER_ID), + new UserHandle(MANAGED_PROFILE_ID))); + + final Intent viewIntent = new Intent(Intent.ACTION_VIEW); + final Intent editIntent = new Intent(Intent.ACTION_EDIT); + final Intent sendIntent = new Intent(Intent.ACTION_SEND); + + final ResolveInfo app1 = createResolveInfo(APP_1); + final ResolveInfo app2 = createResolveInfo(APP_2); + when(mPackageManagerService.findPersistentPreferredActivity(viewIntent, MAIN_USER_ID)) + .thenReturn(app1); + when(mPackageManagerService.findPersistentPreferredActivity(viewIntent, MANAGED_PROFILE_ID)) + .thenReturn(app1); + when(mPackageManagerService.findPersistentPreferredActivity(editIntent, MAIN_USER_ID)) + .thenReturn(null); + when(mPackageManagerService.findPersistentPreferredActivity(editIntent, MANAGED_PROFILE_ID)) + .thenReturn(app2); + when(mPackageManagerService.findPersistentPreferredActivity(sendIntent, MAIN_USER_ID)) + .thenReturn(app1); + when(mPackageManagerService.findPersistentPreferredActivity(sendIntent, MANAGED_PROFILE_ID)) + .thenReturn(null); + + final Set expectedActivities + = new ArraySet<>(); + expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_1, + MAIN_USER_ID)); + expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_1, + MANAGED_PROFILE_ID)); + expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_2, + MANAGED_PROFILE_ID)); + + assertThat(mProvider.findPersistentPreferredActivities( + new Intent[] {viewIntent, editIntent, sendIntent})).isEqualTo(expectedActivities); + } + private void setUpUsersAndInstalledApps() { when(mUserManager.getUsers(true)).thenReturn(Arrays.asList( new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN), @@ -156,4 +197,12 @@ public final class ApplicationFeatureProviderImplTest { ApplicationTestUtils.buildInfo(APP_2_UID, APP_2, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP))); } + + private ResolveInfo createResolveInfo(String packageName) { + final ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.packageName = packageName; + final ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = activityInfo; + return resolveInfo; + } } diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java index 3dd1fd7c5db..b55b5125eda 100644 --- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java @@ -81,6 +81,10 @@ public final class EnterpriseInstalledPackagesPreferenceControllerTest { final Preference preference = new Preference(mContext, null, 0, 0); preference.setVisible(true); + setNumberOfEnterpriseInstalledPackages(0); + mController.updateState(preference); + assertThat(preference.isVisible()).isFalse(); + setNumberOfEnterpriseInstalledPackages(20); when(mContext.getResources().getQuantityString( R.plurals.enterprise_privacy_number_enterprise_installed_packages, 20, 20)) @@ -88,10 +92,6 @@ public final class EnterpriseInstalledPackagesPreferenceControllerTest { mController.updateState(preference); assertThat(preference.getTitle()).isEqualTo("20 packages"); assertThat(preference.isVisible()).isTrue(); - - setNumberOfEnterpriseInstalledPackages(0); - mController.updateState(preference); - assertThat(preference.isVisible()).isFalse(); } @Test diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java index de4d02e3e9a..255976933b4 100644 --- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java +++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java @@ -73,7 +73,7 @@ public final class EnterprisePrivacySettingsTest { final List controllers = mSettings.getPreferenceControllers( ShadowApplication.getInstance().getApplicationContext()); assertThat(controllers).isNotNull(); - assertThat(controllers.size()).isEqualTo(11); + assertThat(controllers.size()).isEqualTo(12); assertThat(controllers.get(0)).isInstanceOf(InstalledPackagesPreferenceController.class); assertThat(controllers.get(1)).isInstanceOf(NetworkLogsPreferenceController.class); assertThat(controllers.get(2)).isInstanceOf(BugReportsPreferenceController.class); @@ -87,9 +87,11 @@ public final class EnterprisePrivacySettingsTest { assertThat(controllers.get(7)).isInstanceOf( AdminGrantedCameraPermissionPreferenceController.class); assertThat(controllers.get(8)).isInstanceOf( - AlwaysOnVpnPrimaryUserPreferenceController.class); + EnterpriseSetDefaultAppsPreferenceController.class); assertThat(controllers.get(9)).isInstanceOf( + AlwaysOnVpnPrimaryUserPreferenceController.class); + assertThat(controllers.get(10)).isInstanceOf( AlwaysOnVpnManagedProfilePreferenceController.class); - assertThat(controllers.get(10)).isInstanceOf(GlobalHttpProxyPreferenceController.class); + assertThat(controllers.get(11)).isInstanceOf(GlobalHttpProxyPreferenceController.class); } } diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java new file mode 100644 index 00000000000..84520a51c6d --- /dev/null +++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java @@ -0,0 +1,170 @@ +/* + * 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.Intent; +import android.content.res.Resources; +import android.net.Uri; +import android.provider.ContactsContract; +import android.provider.MediaStore; +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.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.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import java.util.Set; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.anyObject; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link EnterpriseSetDefaultAppsPreferenceController}. + */ +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public final class EnterpriseSetDefaultAppsPreferenceControllerTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + private FakeFeatureFactory mFeatureFactory; + + private EnterpriseSetDefaultAppsPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(mContext); + mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + mController = new EnterpriseSetDefaultAppsPreferenceController(mContext); + } + + 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; + } + + private void setEnterpriseSetDefaultApps(Intent[] intents, int number) { + final Set apps + = new ArraySet<>(number); + for (int i = 0; i < number; i++) { + apps.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo("app", i)); + } + when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities( + argThat(new MatchesIntents(intents)))).thenReturn(apps); + } + + @Test + public void testUpdateState() { + final Preference preference = new Preference(mContext, null, 0, 0); + preference.setVisible(true); + + when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities( + anyObject())).thenReturn( + new ArraySet()); + mController.updateState(preference); + assertThat(preference.isVisible()).isFalse(); + + 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); + when(mContext.getResources().getQuantityString( + R.plurals.enterprise_privacy_number_enterprise_set_default_apps, 127, 127)) + .thenReturn("127 apps"); + mController.updateState(preference); + assertThat(preference.getTitle()).isEqualTo("127 apps"); + assertThat(preference.isVisible()).isTrue(); + } + + @Test + public void testIsAvailable() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void testHandlePreferenceTreeClick() { + assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0))) + .isFalse(); + } + + @Test + public void testGetPreferenceKey() { + assertThat(mController.getPreferenceKey()) + .isEqualTo("number_enterprise_set_default_apps"); + } + + private static class MatchesIntents extends ArgumentMatcher { + private final Intent[] mExpectedIntents; + + MatchesIntents(Intent[] intents) { + mExpectedIntents = intents; + } + + @Override + public boolean matches(Object object) { + final Intent[] actualIntents = (Intent[]) object; + if (actualIntents == null) { + return false; + } + if (actualIntents.length != mExpectedIntents.length) { + return false; + } + for (int i = 0; i < mExpectedIntents.length; i++) { + if (!mExpectedIntents[i].filterEquals(actualIntents[i])) { + return false; + } + } + return true; + } + } +}