From 6e447d69bcc39cd6126d2b0a5d540888ddc9a325 Mon Sep 17 00:00:00 2001 From: Doris Ling Date: Thu, 7 Dec 2017 12:38:04 -0800 Subject: [PATCH] Add controllers for iinstant app related preferences. For AppInfoDashboardFragment: - add app installer preference into the preference screen instead of creating it dynamically. - add controller for the App Installer, Instant App buttons, and instant app domains preferences. Bug: 69384089 Test: make RunSettingsRoboTests Change-Id: I8d362cacb78077c173130018c33c4d00abfe9843 --- res/xml/app_info_settings.xml | 11 ++ .../AppInfoDashboardFragment.java | 124 ++++----------- .../settings/applications/AppStoreUtil.java | 5 - .../AppInstallerInfoPreferenceController.java | 69 ++++++++ ...InstallerPreferenceCategoryController.java | 35 +++++ ...InstantAppButtonsPreferenceController.java | 75 +++++++++ ...InstantAppDomainsPreferenceController.java | 60 +++++++ .../InstantAppButtonsController.java | 1 - .../AppInfoDashboardFragmentTest.java | 104 ------------- ...InstallerInfoPreferenceControllerTest.java | 147 ++++++++++++++++++ ...antAppButtonsPreferenceControllerTest.java | 133 ++++++++++++++++ ...antAppDomainsPreferenceControllerTest.java | 115 ++++++++++++++ 12 files changed, 672 insertions(+), 207 deletions(-) create mode 100644 src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java create mode 100644 src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java create mode 100644 src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java create mode 100644 src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java diff --git a/res/xml/app_info_settings.xml b/res/xml/app_info_settings.xml index 664210be1c3..99c76b8936f 100644 --- a/res/xml/app_info_settings.xml +++ b/res/xml/app_info_settings.xml @@ -132,6 +132,17 @@ + + + + + + + mCallbacks = new ArrayList<>(); @@ -180,7 +176,7 @@ public class AppInfoDashboardFragment extends DashboardFragment @VisibleForTesting ActionButtonPreference mActionButtons; - private InstantAppButtonsController mInstantAppButtonsController; + private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; /** * Callback to invoke when app info has been changed. @@ -335,8 +331,6 @@ public class AppInfoDashboardFragment extends DashboardFragment } setHasOptionsMenu(true); - - addDynamicPrefs(); } @Override @@ -381,6 +375,10 @@ public class AppInfoDashboardFragment extends DashboardFragment controllers.add(new AppOpenByDefaultPreferenceController(context, this)); controllers.add(new AppPermissionPreferenceController(context, this, packageName)); controllers.add(new AppVersionPreferenceController(context, this)); + controllers.add(new InstantAppDomainsPreferenceController(context, this)); + final AppInstallerInfoPreferenceController appInstallerInfoPreferenceController = + new AppInstallerInfoPreferenceController(context, this, packageName); + controllers.add(appInstallerInfoPreferenceController); for (AbstractPreferenceController controller : controllers) { mCallbacks.add((Callback) controller); @@ -388,6 +386,9 @@ public class AppInfoDashboardFragment extends DashboardFragment // The following are controllers for preferences that don't need to refresh the preference // state when app state changes. + mInstantAppButtonPreferenceController = + new InstantAppButtonsPreferenceController(context, this, packageName); + controllers.add(mInstantAppButtonPreferenceController); controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle)); controllers.add(new AppMemoryPreferenceController(context, this, lifecycle)); controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName)); @@ -407,6 +408,9 @@ public class AppInfoDashboardFragment extends DashboardFragment controllers.add(new PreferenceCategoryController( context, KEY_ADVANCED_APP_INFO_CATEGORY, advancedAppInfoControllers)); + controllers.add(new AppInstallerPreferenceCategoryController( + context, Arrays.asList(appInstallerInfoPreferenceController))); + return controllers; } @@ -444,8 +448,6 @@ public class AppInfoDashboardFragment extends DashboardFragment .styleActionBar(activity) .bindHeaderButtons(); - mInstantAppDomainsPreference = - (AppDomainsPreference) findPreference(KEY_INSTANT_APP_SUPPORTED_LINKS); } @Override @@ -536,24 +538,6 @@ public class AppInfoDashboardFragment extends DashboardFragment } } - /** - * Utility method to hide and show specific preferences based on whether the app being displayed - * is an Instant App or an installed app. - */ - @VisibleForTesting - void prepareInstantAppPrefs() { - final boolean isInstant = AppUtils.isInstant(mPackageInfo.applicationInfo); - if (isInstant) { - Set handledDomainSet = Utils.getHandledDomains(mPm, mPackageInfo.packageName); - String[] handledDomains = handledDomainSet.toArray(new String[handledDomainSet.size()]); - mInstantAppDomainsPreference.setTitles(handledDomains); - // Dummy values, unused in the implementation - mInstantAppDomainsPreference.setValues(new int[handledDomains.length]); - } else { - getPreferenceScreen().removePreference(mInstantAppDomainsPreference); - } - } - // Utility method to set application label and icon. private void setAppLabelAndIcon(PackageInfo pkgInfo) { final View appSnippet = mHeader.findViewById(R.id.entity_header); @@ -642,7 +626,6 @@ public class AppInfoDashboardFragment extends DashboardFragment checkForceStop(); setAppLabelAndIcon(mPackageInfo); initUninstallButtons(); - prepareInstantAppPrefs(); // Update the preference summaries. Activity context = getActivity(); @@ -676,7 +659,8 @@ public class AppInfoDashboardFragment extends DashboardFragment return true; } - protected AlertDialog createDialog(int id, int errorCode) { + @VisibleForTesting + AlertDialog createDialog(int id, int errorCode) { switch (id) { case DLG_DISABLE: return new AlertDialog.Builder(getActivity()) @@ -722,10 +706,7 @@ public class AppInfoDashboardFragment extends DashboardFragment .setNegativeButton(R.string.dlg_cancel, null) .create(); } - if (mInstantAppButtonsController != null) { - return mInstantAppButtonsController.createDialog(id); - } - return null; + return mInstantAppButtonPreferenceController.createDialog(id); } private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { @@ -870,57 +851,6 @@ public class AppInfoDashboardFragment extends DashboardFragment || (mUserManager.isSplitSystemUser() && userCount == 2); } - private void addDynamicPrefs() { - if (UserManager.get(getContext()).isManagedProfile()) { - return; - } - addAppInstallerInfoPref(getPreferenceScreen()); - maybeAddInstantAppButtons(); - } - - private void addAppInstallerInfoPref(PreferenceScreen screen) { - String installerPackageName = - AppStoreUtil.getInstallerPackageName(getContext(), mPackageName); - - final CharSequence installerLabel = Utils.getApplicationLabel(getContext(), - installerPackageName); - if (installerLabel == null) { - return; - } - final int detailsStringId = AppUtils.isInstant(mPackageInfo.applicationInfo) - ? R.string.instant_app_details_summary - : R.string.app_install_details_summary; - PreferenceCategory category = new PreferenceCategory(getPrefContext()); - category.setTitle(R.string.app_install_details_group_title); - screen.addPreference(category); - Preference pref = new Preference(getPrefContext()); - pref.setTitle(R.string.app_install_details_title); - pref.setKey("app_info_store"); - pref.setSummary(getString(detailsStringId, installerLabel)); - - Intent intent = - AppStoreUtil.getAppStoreLink(getContext(), installerPackageName, mPackageName); - if (intent != null) { - pref.setIntent(intent); - } else { - pref.setEnabled(false); - } - category.addPreference(pref); - } - - @VisibleForTesting - void maybeAddInstantAppButtons() { - if (AppUtils.isInstant(mPackageInfo.applicationInfo)) { - LayoutPreference buttons = (LayoutPreference) findPreference(KEY_INSTANT_APP_BUTTONS); - mInstantAppButtonsController = mApplicationFeatureProvider - .newInstantAppButtonsController(this, - buttons.findViewById(R.id.instant_app_button_container), - id -> showDialogInner(id, 0)) - .setPackageName(mPackageName) - .show(); - } - } - private void onPackageRemoved() { getActivity().finishActivity(SUB_INFO_FRAGMENT); getActivity().finishAndRemoveTask(); @@ -1041,9 +971,9 @@ public class AppInfoDashboardFragment extends DashboardFragment mFinishing = true; } - private void showDialogInner(int id, int moveErrorCode) { + public void showDialogInner(int id, int moveErrorCode) { DialogFragment newFragment = - AppInfoBase.MyAlertDialogFragment.newInstance(id, moveErrorCode); + MyAlertDialogFragment.newInstance(id, moveErrorCode); newFragment.setTargetFragment(this, 0); newFragment.show(getFragmentManager(), "dialog " + id); } @@ -1094,8 +1024,8 @@ public class AppInfoDashboardFragment extends DashboardFragment public static void startAppInfoFragment(Class fragment, int titleRes, String pkg, int uid, Activity source, int request, int sourceMetricsCategory) { Bundle args = new Bundle(); - args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg); - args.putInt(AppInfoBase.ARG_PACKAGE_UID, uid); + args.putString(ARG_PACKAGE_NAME, pkg); + args.putInt(ARG_PACKAGE_UID, uid); Intent intent = Utils.onBuildStartFragmentIntent(source, fragment.getName(), args, null, titleRes, null, false, sourceMetricsCategory); @@ -1116,16 +1046,16 @@ public class AppInfoDashboardFragment extends DashboardFragment public Dialog onCreateDialog(Bundle savedInstanceState) { int id = getArguments().getInt(ARG_ID); int errorCode = getArguments().getInt("moveError"); - Dialog dialog = ((AppInfoBase) getTargetFragment()).createDialog(id, errorCode); + Dialog dialog = ((AppInfoDashboardFragment) getTargetFragment()) + .createDialog(id, errorCode); if (dialog == null) { throw new IllegalArgumentException("unknown id " + id); } return dialog; } - public static AppInfoBase.MyAlertDialogFragment newInstance(int id, int errorCode) { - AppInfoBase.MyAlertDialogFragment - dialogFragment = new AppInfoBase.MyAlertDialogFragment(); + public static MyAlertDialogFragment newInstance(int id, int errorCode) { + MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment(); Bundle args = new Bundle(); args.putInt(ARG_ID, id); args.putInt("moveError", errorCode); diff --git a/src/com/android/settings/applications/AppStoreUtil.java b/src/com/android/settings/applications/AppStoreUtil.java index f9b95b0610b..13e551692c6 100644 --- a/src/com/android/settings/applications/AppStoreUtil.java +++ b/src/com/android/settings/applications/AppStoreUtil.java @@ -16,11 +16,9 @@ package com.android.settings.applications; - import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; -import android.net.Uri; import android.util.Log; // This class provides methods that help dealing with app stores. @@ -43,9 +41,6 @@ public class AppStoreUtil { } catch (IllegalArgumentException e) { Log.e(LOG_TAG, "Exception while retrieving the package installer of " + packageName, e); } - if (installerPackageName == null) { - return null; - } return installerPackageName; } diff --git a/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java new file mode 100644 index 00000000000..2449004b525 --- /dev/null +++ b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.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.appinfo; + +import android.content.Context; +import android.content.Intent; +import android.os.UserManager; +import android.support.v7.preference.Preference; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.AppStoreUtil; +import com.android.settingslib.applications.AppUtils; + +public class AppInstallerInfoPreferenceController extends AppInfoPreferenceControllerBase { + + private static final String KEY_APP_INSTALLER_INFO = "app_info_store"; + + private final String mPackageName; + private final String mInstallerPackage; + private final CharSequence mInstallerLabel; + + public AppInstallerInfoPreferenceController(Context context, AppInfoDashboardFragment parent, + String packageName) { + super(context, parent, KEY_APP_INSTALLER_INFO); + mPackageName = packageName; + mInstallerPackage = AppStoreUtil.getInstallerPackageName(mContext, mPackageName); + mInstallerLabel = Utils.getApplicationLabel(mContext, mInstallerPackage); + } + + @Override + public int getAvailabilityStatus() { + if (UserManager.get(mContext).isManagedProfile()) { + return DISABLED_FOR_USER; + } + return mInstallerLabel!= null ? AVAILABLE : DISABLED_FOR_USER; + } + + @Override + public void updateState(Preference preference) { + final int detailsStringId = AppUtils.isInstant(mParent.getPackageInfo().applicationInfo) + ? R.string.instant_app_details_summary + : R.string.app_install_details_summary; + preference.setSummary(mContext.getString(detailsStringId, mInstallerLabel)); + + Intent intent = AppStoreUtil.getAppStoreLink(mContext, mInstallerPackage, mPackageName); + if (intent != null) { + preference.setIntent(intent); + } else { + preference.setEnabled(false); + } + } + +} diff --git a/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java b/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java new file mode 100644 index 00000000000..0e6ffe8dcd2 --- /dev/null +++ b/src/com/android/settings/applications/appinfo/AppInstallerPreferenceCategoryController.java @@ -0,0 +1,35 @@ +/* + * 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.appinfo; + +import android.content.Context; + +import com.android.settings.widget.PreferenceCategoryController; +import com.android.settingslib.core.AbstractPreferenceController; + +import java.util.List; + +public class AppInstallerPreferenceCategoryController extends PreferenceCategoryController { + + private static final String KEY_APP_INSTALLER_INFO_CATEGORY = "app_installer"; + + public AppInstallerPreferenceCategoryController(Context context, + List childrenControllers) { + super(context, KEY_APP_INSTALLER_INFO_CATEGORY, childrenControllers); + } + +} diff --git a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java new file mode 100644 index 00000000000..e35fa76b85f --- /dev/null +++ b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java @@ -0,0 +1,75 @@ +/* + * 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.appinfo; + +import android.app.AlertDialog; +import android.content.Context; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.applications.LayoutPreference; +import com.android.settings.applications.instantapps.InstantAppButtonsController; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.applications.AppUtils; + +public class InstantAppButtonsPreferenceController extends BasePreferenceController { + + private static final String KEY_INSTANT_APP_BUTTONS = "instant_app_buttons"; + + private final AppInfoDashboardFragment mParent; + private final String mPackageName; + private InstantAppButtonsController mInstantAppButtonsController; + + public InstantAppButtonsPreferenceController(Context context, AppInfoDashboardFragment parent, + String packageName) { + super(context, KEY_INSTANT_APP_BUTTONS); + mParent = parent; + mPackageName = packageName; + } + + @Override + public int getAvailabilityStatus() { + return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo) + ? AVAILABLE : DISABLED_FOR_USER; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + LayoutPreference buttons = + (LayoutPreference) screen.findPreference(KEY_INSTANT_APP_BUTTONS); + mInstantAppButtonsController = getApplicationFeatureProvider() + .newInstantAppButtonsController(mParent, + buttons.findViewById(R.id.instant_app_button_container), + id -> mParent.showDialogInner(id, 0)) + .setPackageName(mPackageName) + .show(); + } + + public AlertDialog createDialog(int id) { + return mInstantAppButtonsController.createDialog(id); + } + + @VisibleForTesting + ApplicationFeatureProvider getApplicationFeatureProvider() { + return FeatureFactory.getFactory(mContext).getApplicationFeatureProvider(mContext); + } +} diff --git a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java new file mode 100644 index 00000000000..1d2229127da --- /dev/null +++ b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java @@ -0,0 +1,60 @@ +/* + * 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.appinfo; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.support.v7.preference.Preference; + +import com.android.settings.Utils; +import com.android.settings.applications.AppDomainsPreference; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settingslib.applications.AppUtils; + +import java.util.Set; + +public class InstantAppDomainsPreferenceController extends AppInfoPreferenceControllerBase { + + private static final String KEY_INSTANT_APP_SUPPORTED_LINKS = + "instant_app_launch_supported_domain_urls"; + + private PackageManager mPackageManager; + + public InstantAppDomainsPreferenceController(Context context, AppInfoDashboardFragment parent) { + super(context, parent, KEY_INSTANT_APP_SUPPORTED_LINKS); + mPackageManager = mContext.getPackageManager(); + } + + @Override + public int getAvailabilityStatus() { + return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo) + ? AVAILABLE : DISABLED_FOR_USER; + } + + @Override + public void updateState(Preference preference) { + final AppDomainsPreference instantAppDomainsPreference = (AppDomainsPreference) preference; + final Set handledDomainSet = + Utils.getHandledDomains(mPackageManager, mParent.getPackageInfo().packageName); + final String[] handledDomains = + handledDomainSet.toArray(new String[handledDomainSet.size()]); + instantAppDomainsPreference.setTitles(handledDomains); + // Dummy values, unused in the implementation + instantAppDomainsPreference.setValues(new int[handledDomains.length]); + } + +} diff --git a/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java b/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java index 28e612c98ff..42474a8b297 100644 --- a/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java +++ b/src/com/android/settings/applications/instantapps/InstantAppButtonsController.java @@ -21,7 +21,6 @@ import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.UserHandle; import android.view.View; import android.widget.Button; diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java index d710d7c1953..e742549e720 100644 --- a/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java @@ -23,13 +23,10 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AlertDialog; import android.app.AppOpsManager; -import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -37,16 +34,10 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.UserManager; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceManager; -import android.support.v7.preference.PreferenceScreen; -import android.view.View; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; -import com.android.settings.applications.instantapps.InstantAppButtonsController; -import com.android.settings.applications.instantapps.InstantAppButtonsController.ShowDialogDelegate; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.widget.ActionButtonPreferenceTest; @@ -246,101 +237,6 @@ public final class AppInfoDashboardFragmentTest { verify(mAppDetail.mActionButtons).setButton2Visible(false); } - @Test - public void instantApps_buttonControllerHandlesDialog() { - InstantAppButtonsController mockController = mock(InstantAppButtonsController.class); - ReflectionHelpers.setField( - mAppDetail, "mInstantAppButtonsController", mockController); - // Make sure first that button controller is not called for supported dialog id - AlertDialog mockDialog = mock(AlertDialog.class); - when(mockController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP)) - .thenReturn(mockDialog); - assertThat(mAppDetail.createDialog(InstantAppButtonsController.DLG_CLEAR_APP, 0)) - .isEqualTo(mockDialog); - verify(mockController).createDialog(InstantAppButtonsController.DLG_CLEAR_APP); - } - - // A helper class for testing the InstantAppButtonsController - it lets us look up the - // preference associated with a key for instant app buttons and get back a mock - // LayoutPreference (to avoid a null pointer exception). - public static class InstalledAppDetailsWithMockInstantButtons extends InstalledAppDetails { - @Mock - private LayoutPreference mInstantButtons; - - public InstalledAppDetailsWithMockInstantButtons() { - super(); - MockitoAnnotations.initMocks(this); - } - - @Override - public Preference findPreference(CharSequence key) { - if (key == "instant_app_buttons") { - return mInstantButtons; - } - return super.findPreference(key); - } - } - - @Test - public void instantApps_instantSpecificButtons() { - // Make this app appear to be instant. - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - final PackageInfo packageInfo = mock(PackageInfo.class); - - final InstalledAppDetailsWithMockInstantButtons - fragment = new InstalledAppDetailsWithMockInstantButtons(); - ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo); - ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - - final InstantAppButtonsController buttonsController = - mock(InstantAppButtonsController.class); - when(buttonsController.setPackageName(nullable(String.class))) - .thenReturn(buttonsController); - when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController( - nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class))) - .thenReturn(buttonsController); - - fragment.maybeAddInstantAppButtons(); - verify(buttonsController).setPackageName(nullable(String.class)); - verify(buttonsController).show(); - } - - @Test - public void instantApps_removeCorrectPref() { - PreferenceScreen mockPreferenceScreen = mock(PreferenceScreen.class); - PreferenceManager mockPreferenceManager = mock(PreferenceManager.class); - AppDomainsPreference mockAppDomainsPref = mock(AppDomainsPreference.class); - PackageInfo mockPackageInfo = mock(PackageInfo.class); - PackageManager mockPackageManager = mock(PackageManager.class); - ReflectionHelpers.setField( - mAppDetail, "mInstantAppDomainsPreference", mockAppDomainsPref); - ReflectionHelpers.setField( - mAppDetail, "mPreferenceManager", mockPreferenceManager); - ReflectionHelpers.setField( - mAppDetail, "mPackageInfo", mockPackageInfo); - ReflectionHelpers.setField( - mAppDetail, "mPm", mockPackageManager); - when(mockPreferenceManager.getPreferenceScreen()).thenReturn(mockPreferenceScreen); - - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - mAppDetail.prepareInstantAppPrefs(); - - // For the non instant case we remove the app domain pref, and leave the launch pref - verify(mockPreferenceScreen).removePreference(mockAppDomainsPref); - - // For the instant app case we remove the launch preff, and leave the app domain pref - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - - mAppDetail.prepareInstantAppPrefs(); - // Will be 1 still due to above call - verify(mockPreferenceScreen, times(1)) - .removePreference(mockAppDomainsPref); - } - @Test public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() { doReturn(true).when(mAppDetail).refreshUi(); diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java new file mode 100644 index 00000000000..ffbc8f55954 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java @@ -0,0 +1,147 @@ +/* + * 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.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.UserManager; +import android.support.v7.preference.Preference; + +import com.android.settings.TestConfig; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AppInstallerInfoPreferenceControllerTest { + + @Mock + private UserManager mUserManager; + @Mock + private PackageManager mPackageManager; + @Mock + private ApplicationInfo mAppInfo; + @Mock + private AppInfoDashboardFragment mFragment; + @Mock + private Preference mPreference; + + private Context mContext; + private AppInstallerInfoPreferenceController mController; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + final String installerPackage = "Installer1"; + when(mPackageManager.getInstallerPackageName(anyString())).thenReturn(installerPackage); + when(mPackageManager.getApplicationInfo(eq(installerPackage), anyInt())) + .thenReturn(mAppInfo); + mController = new AppInstallerInfoPreferenceController(mContext, mFragment, "Package1"); + } + + @Test + public void getAvailabilityStatus_managedProfile_shouldReturnDisabled() { + when(mUserManager.isManagedProfile()).thenReturn(true); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); + } + + @Test + public void getAvailabilityStatus_noAppLabel_shouldReturnDisabled() { + when(mUserManager.isManagedProfile()).thenReturn(false); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); + } + + @Test + public void getAvailabilityStatus_hasAppLabel_shouldReturnAvailable() { + when(mUserManager.isManagedProfile()).thenReturn(false); + when(mAppInfo.loadLabel(mPackageManager)).thenReturn("Label1"); + mController = new AppInstallerInfoPreferenceController(mContext, mFragment, "Package1"); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE); + } + + @Test + public void updateState_shouldSetSummary() { + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + + mController.updateState(mPreference); + + verify(mPreference).setSummary(any()); + } + + @Test + public void updateState_noAppStoreLink_shouldDisablePreference() { + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(null); + + mController.updateState(mPreference); + + verify(mPreference).setEnabled(false); + } + + @Test + public void updateState_hasAppStoreLink_shouldSetPreferenceIntent() { + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + final ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.packageName = "Pkg1"; + resolveInfo.activityInfo.name = "Name1"; + when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo); + + mController.updateState(mPreference); + + verify(mPreference, never()).setEnabled(false); + verify(mPreference).setIntent(any(Intent.class)); + } + +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java new file mode 100644 index 00000000000..121659538a4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java @@ -0,0 +1,133 @@ +/* + * 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.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AlertDialog; +import android.app.Fragment; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.support.v7.preference.PreferenceScreen; +import android.view.View; + +import com.android.settings.TestConfig; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.LayoutPreference; +import com.android.settings.applications.instantapps.InstantAppButtonsController; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.instantapps.InstantAppDataProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class InstantAppButtonsPreferenceControllerTest { + + @Mock + private PackageManager mPackageManager; + @Mock + private ApplicationInfo mAppInfo; + @Mock + private AppInfoDashboardFragment mFragment; + + private Context mContext; + private InstantAppButtonsPreferenceController mController; + private FakeFeatureFactory mFeatureFactory; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + MockitoAnnotations.initMocks(this); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mContext = spy(RuntimeEnvironment.application); + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + mController = + spy(new InstantAppButtonsPreferenceController(mContext, mFragment, "Package1")); + } + + @Test + public void getAvailabilityStatus_notInstantApp_shouldReturnDisabled() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); + } + + @Test + public void getAvailabilityStatus_isInstantApp_shouldReturnAvailable() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> true)); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE); + } + + @Test + public void displayPreference_shouldSetPreferenceTitle() { + final PreferenceScreen screen = mock(PreferenceScreen.class); + final LayoutPreference preference = mock(LayoutPreference.class); + when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference); + when(mController.getApplicationFeatureProvider()) + .thenReturn(mFeatureFactory.applicationFeatureProvider); + final InstantAppButtonsController buttonsController = + mock(InstantAppButtonsController.class); + when(buttonsController.setPackageName(nullable(String.class))) + .thenReturn(buttonsController); + when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController( + nullable(Fragment.class), nullable(View.class), + nullable(InstantAppButtonsController.ShowDialogDelegate.class))) + .thenReturn(buttonsController); + + mController.displayPreference(screen); + + verify(buttonsController).setPackageName(nullable(String.class)); + verify(buttonsController).show(); + } + + @Test + public void createDialog_shouldReturnDialogFromButtonController() { + final InstantAppButtonsController buttonsController = + mock(InstantAppButtonsController.class); + ReflectionHelpers.setField( + mController, "mInstantAppButtonsController", buttonsController); + final AlertDialog mockDialog = mock(AlertDialog.class); + when(buttonsController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP)) + .thenReturn(mockDialog); + + assertThat(mController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP)) + .isEqualTo(mockDialog); + } + +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java new file mode 100644 index 00000000000..f1776e88e7d --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.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.applications.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.util.ArraySet; + +import com.android.settings.TestConfig; +import com.android.settings.applications.AppDomainsPreference; +import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.instantapps.InstantAppDataProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class InstantAppDomainsPreferenceControllerTest { + + @Mock + private PackageManager mPackageManager; + @Mock + private ApplicationInfo mAppInfo; + @Mock + private AppInfoDashboardFragment mFragment; + @Mock + private AppDomainsPreference mPreference; + + private Context mContext; + private InstantAppDomainsPreferenceController mController; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = mAppInfo; + packageInfo.packageName = "Package1"; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + mController = new InstantAppDomainsPreferenceController(mContext, mFragment); + } + + @Test + public void getAvailabilityStatus_notInstantApp_shouldReturnDisabled() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_FOR_USER); + } + + @Test + public void getAvailabilityStatus_isInstantApp_shouldReturnAvailable() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> true)); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE); + } + + @Test + public void updateState_shouldSetPreferenceTitle() { + final String[] domain = { "Domain1" }; + final ArraySet domains = new ArraySet<>(); + domains.add(domain[0]); + final List infoList = new ArrayList<>(); + final IntentFilterVerificationInfo info = + new IntentFilterVerificationInfo("Package1", domains); + infoList.add(info); + + when(mPackageManager.getIntentFilterVerifications("Package1")).thenReturn(infoList); + + mController.updateState(mPreference); + + verify(mPreference).setTitles(domain); + } + +}