diff --git a/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java b/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java new file mode 100644 index 00000000000..b10d06c9b55 --- /dev/null +++ b/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceController.java @@ -0,0 +1,318 @@ +/* + * 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.Activity; +import android.app.ActivityManager; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.PreferenceScreen; +import android.util.Log; +import android.webkit.IWebViewUpdateService; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.widget.ActionButtonPreference; +import com.android.settings.wrapper.DevicePolicyManagerWrapper; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.ApplicationsState.AppEntry; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class AppActionButtonPreferenceController extends BasePreferenceController + implements AppInfoDashboardFragment.Callback { + + private static final String TAG = "AppActionButtonControl"; + private static final String KEY_ACTION_BUTTONS = "action_buttons"; + + @VisibleForTesting + ActionButtonPreference mActionButtons; + private final AppInfoDashboardFragment mParent; + private final String mPackageName; + private final HashSet mHomePackages = new HashSet<>(); + private final ApplicationFeatureProvider mApplicationFeatureProvider; + + private int mUserId; + private DevicePolicyManagerWrapper mDpm; + private UserManager mUserManager; + private PackageManager mPm; + + private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final boolean enabled = getResultCode() != Activity.RESULT_CANCELED; + Log.d(TAG, "Got broadcast response: Restart status for " + + mParent.getAppEntry().info.packageName + " " + enabled); + updateForceStopButton(enabled); + } + }; + + public AppActionButtonPreferenceController(Context context, AppInfoDashboardFragment parent, + String packageName) { + super(context, KEY_ACTION_BUTTONS); + mParent = parent; + mPackageName = packageName; + mUserId = UserHandle.myUserId(); + mApplicationFeatureProvider = FeatureFactory.getFactory(context) + .getApplicationFeatureProvider(context); + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mActionButtons = ((ActionButtonPreference) screen.findPreference(KEY_ACTION_BUTTONS)) + .setButton2Text(R.string.force_stop) + .setButton2Positive(false) + .setButton2Enabled(false); + } + + @Override + public void refreshUi() { + if (mPm == null) { + mPm = mContext.getPackageManager(); + } + if (mDpm == null) { + mDpm = new DevicePolicyManagerWrapper( + (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)); + } + if (mUserManager == null) { + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + } + final AppEntry appEntry = mParent.getAppEntry(); + final PackageInfo packageInfo = mParent.getPackageInfo(); + + // Get list of "home" apps and trace through any meta-data references + final List homeActivities = new ArrayList(); + mPm.getHomeActivities(homeActivities); + mHomePackages.clear(); + for (int i = 0; i< homeActivities.size(); i++) { + final ResolveInfo ri = homeActivities.get(i); + final String activityPkg = ri.activityInfo.packageName; + mHomePackages.add(activityPkg); + + // Also make sure to include anything proxying for the home app + final Bundle metadata = ri.activityInfo.metaData; + if (metadata != null) { + final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); + if (signaturesMatch(metaPkg, activityPkg)) { + mHomePackages.add(metaPkg); + } + } + } + + checkForceStop(appEntry, packageInfo); + initUninstallButtons(appEntry, packageInfo); + } + + @VisibleForTesting + void initUninstallButtons(AppEntry appEntry, PackageInfo packageInfo) { + final boolean isBundled = (appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + boolean enabled; + if (isBundled) { + enabled = handleDisableable(appEntry, packageInfo); + } else { + enabled = initUninstallButtonForUserApp(); + } + // If this is a device admin, it can't be uninstalled or disabled. + // We do this here so the text of the button is still set correctly. + if (isBundled && mDpm.packageHasActiveAdmins(packageInfo.packageName)) { + enabled = false; + } + + // We don't allow uninstalling DO/PO on *any* users, because if it's a system app, + // "uninstall" is actually "downgrade to the system version + disable", and "downgrade" + // will clear data on all users. + if (Utils.isProfileOrDeviceOwner(mUserManager, mDpm, packageInfo.packageName)) { + enabled = false; + } + + // Don't allow uninstalling the device provisioning package. + if (Utils.isDeviceProvisioningPackage(mContext.getResources(), appEntry.info.packageName)) { + enabled = false; + } + + // If the uninstall intent is already queued, disable the uninstall button + if (mDpm.isUninstallInQueue(mPackageName)) { + enabled = false; + } + + // Home apps need special handling. Bundled ones we don't risk downgrading + // because that can interfere with home-key resolution. Furthermore, we + // can't allow uninstallation of the only home app, and we don't want to + // allow uninstallation of an explicitly preferred one -- the user can go + // to Home settings and pick a different one, after which we'll permit + // uninstallation of the now-not-default one. + if (enabled && mHomePackages.contains(packageInfo.packageName)) { + if (isBundled) { + enabled = false; + } else { + ArrayList homeActivities = new ArrayList(); + ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); + if (currentDefaultHome == null) { + // No preferred default, so permit uninstall only when + // there is more than one candidate + enabled = (mHomePackages.size() > 1); + } else { + // There is an explicit default home app -- forbid uninstall of + // that one, but permit it for installed-but-inactive ones. + enabled = !packageInfo.packageName.equals(currentDefaultHome.getPackageName()); + } + } + } + + if (RestrictedLockUtils.hasBaseUserRestriction( + mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId)) { + enabled = false; + } + + try { + final IWebViewUpdateService webviewUpdateService = + IWebViewUpdateService.Stub.asInterface( + ServiceManager.getService("webviewupdate")); + if (webviewUpdateService.isFallbackPackage(appEntry.info.packageName)) { + enabled = false; + } + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + mActionButtons.setButton1Enabled(enabled); + if (enabled) { + // Register listener + mActionButtons.setButton1OnClickListener(v -> mParent.handleUninstallButtonClick()); + } + } + + @VisibleForTesting + boolean initUninstallButtonForUserApp() { + boolean enabled = true; + final PackageInfo packageInfo = mParent.getPackageInfo(); + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0 + && mUserManager.getUsers().size() >= 2) { + // When we have multiple users, there is a separate menu + // to uninstall for all users. + enabled = false; + } else if (AppUtils.isInstant(packageInfo.applicationInfo)) { + enabled = false; + mActionButtons.setButton1Visible(false); + } + mActionButtons.setButton1Text(R.string.uninstall_text).setButton1Positive(false); + return enabled; + } + + @VisibleForTesting + boolean handleDisableable(AppEntry appEntry, PackageInfo packageInfo) { + boolean disableable = false; + // Try to prevent the user from bricking their phone + // by not allowing disabling of apps signed with the + // system cert and any launcher app in the system. + if (mHomePackages.contains(appEntry.info.packageName) + || Utils.isSystemPackage(mContext.getResources(), mPm, packageInfo)) { + // Disable button for core system applications. + mActionButtons + .setButton1Text(R.string.disable_text) + .setButton1Positive(false); + } else if (appEntry.info.enabled && appEntry.info.enabledSetting + != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { + mActionButtons + .setButton1Text(R.string.disable_text) + .setButton1Positive(false); + disableable = !mApplicationFeatureProvider.getKeepEnabledPackages() + .contains(appEntry.info.packageName); + } else { + mActionButtons + .setButton1Text(R.string.enable_text) + .setButton1Positive(true); + disableable = true; + } + + return disableable; + } + + private void updateForceStopButton(boolean enabled) { + final boolean disallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction( + mContext, UserManager.DISALLOW_APPS_CONTROL, mUserId); + mActionButtons + .setButton2Enabled(disallowedBySystem ? false : enabled) + .setButton2OnClickListener( + disallowedBySystem ? null : v -> mParent.handleForceStopButtonClick()); + } + + void checkForceStop(AppEntry appEntry, PackageInfo packageInfo) { + if (mDpm.packageHasActiveAdmins(packageInfo.packageName)) { + // User can't force stop device admin. + Log.w(TAG, "User can't force stop device admin"); + updateForceStopButton(false); + } else if (AppUtils.isInstant(packageInfo.applicationInfo)) { + updateForceStopButton(false); + mActionButtons.setButton2Visible(false); + } else if ((appEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) { + // If the app isn't explicitly stopped, then always show the + // force stop button. + Log.w(TAG, "App is not explicitly stopped"); + updateForceStopButton(true); + } else { + final Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, + Uri.fromParts("package", appEntry.info.packageName, null)); + intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { appEntry.info.packageName }); + intent.putExtra(Intent.EXTRA_UID, appEntry.info.uid); + intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(appEntry.info.uid)); + Log.d(TAG, "Sending broadcast to query restart status for " + + appEntry.info.packageName); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, + mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); + } + } + + private boolean signaturesMatch(String pkg1, String pkg2) { + if (pkg1 != null && pkg2 != null) { + try { + return mPm.checkSignatures(pkg1, pkg2) >= PackageManager.SIGNATURE_MATCH; + } catch (Exception e) { + // e.g. named alternate package not found during lookup; + // this is an expected case sometimes + } + } + return false; + } + +} diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 0929dad3809..57e2b0c1f84 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -25,7 +25,6 @@ import android.app.Dialog; import android.app.DialogFragment; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -34,13 +33,10 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.support.annotation.VisibleForTesting; @@ -50,7 +46,6 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.webkit.IWebViewUpdateService; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.DeviceAdminAdd; @@ -58,13 +53,10 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; -import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.dashboard.DashboardFragment; -import com.android.settings.overlay.FeatureFactory; -import com.android.settings.widget.ActionButtonPreference; import com.android.settings.widget.EntityHeaderController; import com.android.settings.widget.PreferenceCategoryController; import com.android.settings.wrapper.DevicePolicyManagerWrapper; @@ -78,7 +70,6 @@ import com.android.settingslib.core.lifecycle.Lifecycle; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; /** @@ -115,8 +106,8 @@ public class AppInfoDashboardFragment extends DashboardFragment private static final int DLG_FORCE_STOP = DLG_BASE + 1; private static final int DLG_DISABLE = DLG_BASE + 2; private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; + private static final String KEY_HEADER = "header_view"; - private static final String KEY_ACTION_BUTTONS = "action_buttons"; private static final String KEY_ADVANCED_APP_INFO_CATEGORY = "advanced_app_info"; public static final String ARG_PACKAGE_NAME = "package"; @@ -127,7 +118,6 @@ public class AppInfoDashboardFragment extends DashboardFragment private EnforcedAdmin mAppsControlDisallowedAdmin; private boolean mAppsControlDisallowedBySystem; - private ApplicationFeatureProvider mApplicationFeatureProvider; private ApplicationsState mState; private ApplicationsState.Session mSession; private ApplicationsState.AppEntry mAppEntry; @@ -143,8 +133,6 @@ public class AppInfoDashboardFragment extends DashboardFragment private boolean mListeningToPackageRemove; - private final HashSet mHomePackages = new HashSet<>(); - private boolean mInitialized; private boolean mShowUninstalled; private LayoutPreference mHeader; @@ -153,10 +141,8 @@ public class AppInfoDashboardFragment extends DashboardFragment private List mCallbacks = new ArrayList<>(); - @VisibleForTesting - ActionButtonPreference mActionButtons; - private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; + private AppActionButtonPreferenceController mAppActionButtonPreferenceController; /** * Callback to invoke when app info has been changed. @@ -165,139 +151,17 @@ public class AppInfoDashboardFragment extends DashboardFragment void refreshUi(); } - @VisibleForTesting - boolean handleDisableable() { - boolean disableable = false; - // Try to prevent the user from bricking their phone - // by not allowing disabling of apps signed with the - // system cert and any launcher app in the system. - if (mHomePackages.contains(mAppEntry.info.packageName) - || Utils.isSystemPackage(getContext().getResources(), mPm, mPackageInfo)) { - // Disable button for core system applications. - mActionButtons - .setButton1Text(R.string.disable_text) - .setButton1Positive(false); - } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { - mActionButtons - .setButton1Text(R.string.disable_text) - .setButton1Positive(false); - disableable = !mApplicationFeatureProvider.getKeepEnabledPackages() - .contains(mAppEntry.info.packageName); - } else { - mActionButtons - .setButton1Text(R.string.enable_text) - .setButton1Positive(true); - disableable = true; - } - - return disableable; - } - private boolean isDisabledUntilUsed() { return mAppEntry.info.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; } - private void initUninstallButtons() { - final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - boolean enabled; - if (isBundled) { - enabled = handleDisableable(); - } else { - enabled = initUninstallButtonForUserApp(); - } - // If this is a device admin, it can't be uninstalled or disabled. - // We do this here so the text of the button is still set correctly. - if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { - enabled = false; - } - - // We don't allow uninstalling DO/PO on *any* users, because if it's a system app, - // "uninstall" is actually "downgrade to the system version + disable", and "downgrade" - // will clear data on all users. - if (Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mPackageInfo.packageName)) { - enabled = false; - } - - // Don't allow uninstalling the device provisioning package. - if (Utils.isDeviceProvisioningPackage(getResources(), mAppEntry.info.packageName)) { - enabled = false; - } - - // If the uninstall intent is already queued, disable the uninstall button - if (mDpm.isUninstallInQueue(mPackageName)) { - enabled = false; - } - - // Home apps need special handling. Bundled ones we don't risk downgrading - // because that can interfere with home-key resolution. Furthermore, we - // can't allow uninstallation of the only home app, and we don't want to - // allow uninstallation of an explicitly preferred one -- the user can go - // to Home settings and pick a different one, after which we'll permit - // uninstallation of the now-not-default one. - if (enabled && mHomePackages.contains(mPackageInfo.packageName)) { - if (isBundled) { - enabled = false; - } else { - ArrayList homeActivities = new ArrayList(); - ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); - if (currentDefaultHome == null) { - // No preferred default, so permit uninstall only when - // there is more than one candidate - enabled = (mHomePackages.size() > 1); - } else { - // There is an explicit default home app -- forbid uninstall of - // that one, but permit it for installed-but-inactive ones. - enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName()); - } - } - } - - if (mAppsControlDisallowedBySystem) { - enabled = false; - } - - try { - final IWebViewUpdateService webviewUpdateService = - IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate")); - if (webviewUpdateService.isFallbackPackage(mAppEntry.info.packageName)) { - enabled = false; - } - } catch (RemoteException e) { - throw new RuntimeException(e); - } - - mActionButtons.setButton1Enabled(enabled); - if (enabled) { - // Register listener - mActionButtons.setButton1OnClickListener(v -> handleUninstallButtonClick()); - } - } - - @VisibleForTesting - boolean initUninstallButtonForUserApp() { - boolean enabled = true; - if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0 - && mUserManager.getUsers().size() >= 2) { - // When we have multiple users, there is a separate menu - // to uninstall for all users. - enabled = false; - } else if (AppUtils.isInstant(mPackageInfo.applicationInfo)) { - enabled = false; - mActionButtons.setButton1Visible(false); - } - mActionButtons.setButton1Text(R.string.uninstall_text).setButton1Positive(false); - return enabled; - } - /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); mFinishing = false; final Activity activity = getActivity(); - mApplicationFeatureProvider = FeatureFactory.getFactory(activity) - .getApplicationFeatureProvider(activity); mDpm = new DevicePolicyManagerWrapper( (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE)); mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); @@ -359,6 +223,9 @@ public class AppInfoDashboardFragment extends DashboardFragment final AppInstallerInfoPreferenceController appInstallerInfoPreferenceController = new AppInstallerInfoPreferenceController(context, this, packageName); controllers.add(appInstallerInfoPreferenceController); + mAppActionButtonPreferenceController = + new AppActionButtonPreferenceController(context, this, packageName); + controllers.add(mAppActionButtonPreferenceController); for (AbstractPreferenceController controller : controllers) { mCallbacks.add((Callback) controller); @@ -416,10 +283,6 @@ public class AppInfoDashboardFragment extends DashboardFragment } final Activity activity = getActivity(); mHeader = (LayoutPreference) findPreference(KEY_HEADER); - mActionButtons = ((ActionButtonPreference) findPreference(KEY_ACTION_BUTTONS)) - .setButton2Text(R.string.force_stop) - .setButton2Positive(false) - .setButton2Enabled(false); EntityHeaderController.newInstance(activity, this, mHeader.findViewById(R.id.entity_header)) .setRecyclerView(getListView(), getLifecycle()) .setPackageName(mPackageName) @@ -559,21 +422,6 @@ public class AppInfoDashboardFragment extends DashboardFragment return showIt; } - private boolean signaturesMatch(String pkg1, String pkg2) { - if (pkg1 != null && pkg2 != null) { - try { - final int match = mPm.checkSignatures(pkg1, pkg2); - if (match >= PackageManager.SIGNATURE_MATCH) { - return true; - } - } catch (Exception e) { - // e.g. named alternate package not found during lookup; - // this is an expected case sometimes - } - } - return false; - } - @VisibleForTesting boolean refreshUi() { retrieveAppEntry(); @@ -585,28 +433,8 @@ public class AppInfoDashboardFragment extends DashboardFragment return false; // onCreate must have failed, make sure to exit } - // Get list of "home" apps and trace through any meta-data references - final List homeActivities = new ArrayList(); - mPm.getHomeActivities(homeActivities); - mHomePackages.clear(); - for (int i = 0; i< homeActivities.size(); i++) { - final ResolveInfo ri = homeActivities.get(i); - final String activityPkg = ri.activityInfo.packageName; - mHomePackages.add(activityPkg); - // Also make sure to include anything proxying for the home app - final Bundle metadata = ri.activityInfo.metaData; - if (metadata != null) { - final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); - if (signaturesMatch(metaPkg, activityPkg)) { - mHomePackages.add(metaPkg); - } - } - } - - checkForceStop(); setAppLabelAndIcon(mPackageInfo); - initUninstallButtons(); // Update the preference summaries. final Activity context = getActivity(); @@ -714,41 +542,7 @@ public class AppInfoDashboardFragment extends DashboardFragment if (newEnt != null) { mAppEntry = newEnt; } - checkForceStop(); - } - - private void updateForceStopButton(boolean enabled) { - mActionButtons - .setButton2Enabled(mAppsControlDisallowedBySystem ? false : enabled) - .setButton2OnClickListener(mAppsControlDisallowedBySystem - ? null : v -> handleForceStopButtonClick()); - } - - @VisibleForTesting - void checkForceStop() { - if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { - // User can't force stop device admin. - Log.w(TAG, "User can't force stop device admin"); - updateForceStopButton(false); - } else if (AppUtils.isInstant(mPackageInfo.applicationInfo)) { - updateForceStopButton(false); - mActionButtons.setButton2Visible(false); - } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) { - // If the app isn't explicitly stopped, then always show the - // force stop button. - Log.w(TAG, "App is not explicitly stopped"); - updateForceStopButton(true); - } else { - final Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, - Uri.fromParts("package", mAppEntry.info.packageName, null)); - intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName }); - intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); - intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid)); - Log.d(TAG, "Sending broadcast to query restart status for " - + mAppEntry.info.packageName); - getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, - mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); - } + mAppActionButtonPreferenceController.checkForceStop(mAppEntry, mPackageInfo); } public static void startAppInfoFragment(Class fragment, int title, @@ -763,7 +557,7 @@ public class AppInfoDashboardFragment extends DashboardFragment SUB_INFO_FRAGMENT); } - private void handleUninstallButtonClick() { + void handleUninstallButtonClick() { if (mAppEntry == null) { setIntentAndFinish(true, true); return; @@ -811,7 +605,7 @@ public class AppInfoDashboardFragment extends DashboardFragment } } - private void handleForceStopButtonClick() { + void handleForceStopButtonClick() { if (mAppEntry == null) { setIntentAndFinish(true, true); return; @@ -877,16 +671,6 @@ public class AppInfoDashboardFragment extends DashboardFragment } } - private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final boolean enabled = getResultCode() != Activity.RESULT_CANCELED; - Log.d(TAG, "Got broadcast response: Restart status for " - + mAppEntry.info.packageName + " " + enabled); - updateForceStopButton(enabled); - } - }; - private String getPackageName() { if (mPackageName != null) { return mPackageName; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java new file mode 100644 index 00000000000..17b7a2299af --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppActionButtonPreferenceControllerTest.java @@ -0,0 +1,320 @@ +/* + * 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.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doNothing; +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.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.os.UserManager; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.widget.ActionButtonPreference; +import com.android.settings.widget.ActionButtonPreferenceTest; +import com.android.settings.wrapper.DevicePolicyManagerWrapper; +import com.android.settingslib.Utils; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.ApplicationsState; +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.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.util.ReflectionHelpers; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AppActionButtonPreferenceControllerTest { + + @Mock + private UserManager mUserManager; + @Mock + private DevicePolicyManagerWrapper mDevicePolicyManager; + @Mock + private AppInfoDashboardFragment mFragment; + + private Context mContext; + private AppActionButtonPreferenceController mController; + private FakeFeatureFactory mFeatureFactory; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mContext = spy(RuntimeEnvironment.application); + mController = spy(new AppActionButtonPreferenceController(mContext, mFragment, "Package1")); + mController.mActionButtons = ActionButtonPreferenceTest.createMock(); + ReflectionHelpers.setField(mController, "mUserManager", mUserManager); + ReflectionHelpers.setField(mController, "mDpm", mDevicePolicyManager); + ReflectionHelpers.setField(mController, "mApplicationFeatureProvider", + mFeatureFactory.applicationFeatureProvider); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + } + + @Test + public void displayPreference_shouldInitializeForceStopButton() { + final PreferenceScreen screen = mock(PreferenceScreen.class); + final ActionButtonPreference preference = spy(new ActionButtonPreference(mContext)); + when(screen.findPreference(mController.getPreferenceKey())).thenReturn(preference); + + mController.displayPreference(screen); + + verify(preference).setButton2Positive(false); + verify(preference).setButton2Text(R.string.force_stop); + verify(preference).setButton2Enabled(false); + } + + @Test + public void refreshUi_shouldRefreshButton() { + final PackageInfo packageInfo = mock(PackageInfo.class); + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + final ApplicationInfo info = new ApplicationInfo(); + appEntry.info = info; + doNothing().when(mController).checkForceStop(appEntry, packageInfo); + doNothing().when(mController).initUninstallButtons(appEntry, packageInfo); + when(mFragment.getAppEntry()).thenReturn(appEntry); + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + + mController.refreshUi(); + + verify(mController).checkForceStop(appEntry, packageInfo); + verify(mController).initUninstallButtons(appEntry, packageInfo); + } + + @Test + public void initUninstallButtonForUserApp_shouldSetNegativeButton() { + final ApplicationInfo info = new ApplicationInfo(); + info.flags = ApplicationInfo.FLAG_INSTALLED; + info.enabled = true; + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = info; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + + assertThat(mController.initUninstallButtonForUserApp()).isTrue(); + verify(mController.mActionButtons).setButton1Positive(false); + } + + // Tests that we don't show the uninstall button for instant apps" + @Test + public void initUninstallButtonForUserApp_instantApps_noUninstallButton() { + // Make this app appear to be instant. + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> true)); + final ApplicationInfo info = new ApplicationInfo(); + info.flags = ApplicationInfo.FLAG_INSTALLED; + info.enabled = true; + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.info = info; + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = info; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + + assertThat(mController.initUninstallButtonForUserApp()).isFalse(); + verify(mController.mActionButtons).setButton1Visible(false); + } + + @Test + public void initUninstallButtonForUserApp_notInstalledForCurrentUser_shouldDisableButton() { + final ApplicationInfo info = new ApplicationInfo(); + info.enabled = true; + final PackageInfo packageInfo = mock(PackageInfo.class); + packageInfo.applicationInfo = info; + when(mFragment.getPackageInfo()).thenReturn(packageInfo); + final int userID1 = 1; + final int userID2 = 2; + final List userInfos = new ArrayList<>(); + userInfos.add(new UserInfo(userID1, "User1", UserInfo.FLAG_PRIMARY)); + userInfos.add(new UserInfo(userID2, "User2", UserInfo.FLAG_GUEST)); + when(mUserManager.getUsers(true)).thenReturn(userInfos); + + assertThat(mController.initUninstallButtonForUserApp()).isFalse(); + } + + // Tests that we don't show the force stop button for instant apps (they aren't allowed to run + // when they aren't in the foreground). + @Test + public void checkForceStop_instantApps_shouldNotShowForceStop() { + // Make this app appear to be instant. + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> true)); + final PackageInfo packageInfo = mock(PackageInfo.class); + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + final ApplicationInfo info = new ApplicationInfo(); + appEntry.info = info; + + mController.checkForceStop(appEntry, packageInfo); + + verify(mController.mActionButtons).setButton2Visible(false); + } + + @Test + public void checkForceStop_hasActiveAdmin_shouldDisableForceStop() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + final String packageName = "Package1"; + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = packageName; + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + when(mDevicePolicyManager.packageHasActiveAdmins(packageName)).thenReturn(true); + + mController.checkForceStop(appEntry, packageInfo); + + verify(mController.mActionButtons).setButton2Enabled(false); + } + + @Test + public void checkForceStop_appRunning_shouldEnableForceStop() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + final PackageInfo packageInfo = mock(PackageInfo.class); + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + final ApplicationInfo info = new ApplicationInfo(); + appEntry.info = info; + + mController.checkForceStop(appEntry, packageInfo); + + verify(mController.mActionButtons).setButton2Enabled(true); + } + + @Test + public void checkForceStop_appStopped_shouldQueryPackageRestart() { + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + final PackageInfo packageInfo = mock(PackageInfo.class); + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + final ApplicationInfo info = new ApplicationInfo(); + appEntry.info = info; + info.flags = ApplicationInfo.FLAG_STOPPED; + info.packageName = "com.android.setting"; + + mController.checkForceStop(appEntry, packageInfo); + + verify(mContext).sendOrderedBroadcastAsUser(argThat(intent-> intent != null + && intent.getAction().equals(Intent.ACTION_QUERY_PACKAGE_RESTART)), + any(UserHandle.class), nullable(String.class), any(BroadcastReceiver.class), + nullable(Handler.class), anyInt(), nullable(String.class), nullable(Bundle.class)); + } + + @Test + public void handleDisableable_appIsHomeApp_buttonShouldNotWork() { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = "pkg"; + info.enabled = true; + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.info = info; + final HashSet homePackages = new HashSet<>(); + homePackages.add(info.packageName); + ReflectionHelpers.setField(mController, "mHomePackages", homePackages); + + assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isFalse(); + verify(mController.mActionButtons).setButton1Text(R.string.disable_text); + } + + @Test + @Config(shadows = ShadowUtils.class) + public void handleDisableable_appIsEnabled_buttonShouldWork() { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = "pkg"; + info.enabled = true; + info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.info = info; + when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()) + .thenReturn(new HashSet<>()); + + assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isTrue(); + verify(mController.mActionButtons).setButton1Text(R.string.disable_text); + } + + @Test + @Config(shadows = ShadowUtils.class) + public void handleDisableable_appIsDisabled_buttonShouldShowEnable() { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = "pkg"; + info.enabled = false; + info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.info = info; + when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()) + .thenReturn(new HashSet<>()); + + assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isTrue(); + verify(mController.mActionButtons).setButton1Text(R.string.enable_text); + verify(mController.mActionButtons).setButton1Positive(true); + } + + @Test + @Config(shadows = ShadowUtils.class) + public void handleDisableable_appIsEnabledAndInKeepEnabledWhitelist_buttonShouldNotWork() { + final ApplicationInfo info = new ApplicationInfo(); + info.packageName = "pkg"; + info.enabled = true; + info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.info = info; + final HashSet packages = new HashSet<>(); + packages.add(info.packageName); + when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()) + .thenReturn(packages); + + assertThat(mController.handleDisableable(appEntry, mock(PackageInfo.class))).isFalse(); + verify(mController.mActionButtons).setButton1Text(R.string.disable_text); + } + + @Implements(Utils.class) + public static class ShadowUtils { + @Implementation + public static boolean isSystemPackage(Resources resources, PackageManager pm, + PackageInfo pkg) { + return false; + } + } + +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java index b208253b3cd..87b82ad4d88 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java @@ -26,24 +26,18 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; -import android.content.res.Resources; import android.os.UserManager; -import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; -import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.widget.ActionButtonPreferenceTest; import com.android.settings.wrapper.DevicePolicyManagerWrapper; -import com.android.settingslib.Utils; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; @@ -56,12 +50,9 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @@ -73,20 +64,15 @@ public final class AppInfoDashboardFragmentTest { private static final String PACKAGE_NAME = "test_package_name"; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Context mContext; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private UserManager mUserManager; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) + @Mock private SettingsActivity mActivity; @Mock private DevicePolicyManagerWrapper mDevicePolicyManager; @Mock private PackageManager mPackageManager; - @Mock - private AppOpsManager mAppOpsManager; - private FakeFeatureFactory mFeatureFactory; private AppInfoDashboardFragment mFragment; private Context mShadowContext; @@ -94,14 +80,11 @@ public final class AppInfoDashboardFragmentTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mFeatureFactory = FakeFeatureFactory.setupForTest(); mShadowContext = RuntimeEnvironment.application; mFragment = spy(new AppInfoDashboardFragment()); doReturn(mActivity).when(mFragment).getActivity(); doReturn(mShadowContext).when(mFragment).getContext(); doReturn(mPackageManager).when(mActivity).getPackageManager(); - doReturn(mAppOpsManager).when(mActivity).getSystemService(Context.APP_OPS_SERVICE); - mFragment.mActionButtons = ActionButtonPreferenceTest.createMock(); // Default to not considering any apps to be instant (individual tests can override this). ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", @@ -198,48 +181,6 @@ public final class AppInfoDashboardFragmentTest { assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isFalse(); } - // Tests that we don't show the uninstall button for instant apps" - @Test - public void instantApps_noUninstallButton() { - // Make this app appear to be instant. - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - final ApplicationInfo info = new ApplicationInfo(); - info.flags = ApplicationInfo.FLAG_INSTALLED; - info.enabled = true; - final AppEntry appEntry = mock(AppEntry.class); - appEntry.info = info; - final PackageInfo packageInfo = mock(PackageInfo.class); - packageInfo.applicationInfo = info; - - ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); - ReflectionHelpers.setField(mFragment, "mAppEntry", appEntry); - ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); - - mFragment.initUninstallButtonForUserApp(); - verify(mFragment.mActionButtons).setButton1Visible(false); - } - - // Tests that we don't show the force stop button for instant apps (they aren't allowed to run - // when they aren't in the foreground). - @Test - public void instantApps_noForceStop() { - // Make this app appear to be instant. - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - final PackageInfo packageInfo = mock(PackageInfo.class); - final AppEntry appEntry = mock(AppEntry.class); - final ApplicationInfo info = new ApplicationInfo(); - appEntry.info = info; - - ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager); - ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); - ReflectionHelpers.setField(mFragment, "mAppEntry", appEntry); - - mFragment.checkForceStop(); - verify(mFragment.mActionButtons).setButton2Visible(false); - } - @Test public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() { doReturn(true).when(mFragment).refreshUi(); @@ -249,105 +190,6 @@ public final class AppInfoDashboardFragmentTest { verify(mActivity).invalidateOptionsMenu(); } - @Test - public void handleDisableable_appIsHomeApp_buttonShouldNotWork() { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = "pkg"; - info.enabled = true; - final AppEntry appEntry = mock(AppEntry.class); - appEntry.info = info; - final HashSet homePackages = new HashSet<>(); - homePackages.add(info.packageName); - - ReflectionHelpers.setField(mFragment, "mHomePackages", homePackages); - ReflectionHelpers.setField(mFragment, "mAppEntry", appEntry); - - assertThat(mFragment.handleDisableable()).isFalse(); - verify(mFragment.mActionButtons).setButton1Text(R.string.disable_text); - } - - @Test - @Config(shadows = ShadowUtils.class) - public void handleDisableable_appIsEnabled_buttonShouldWork() { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = "pkg"; - info.enabled = true; - info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - - final AppEntry appEntry = mock(AppEntry.class); - appEntry.info = info; - when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( - new HashSet<>()); - - ReflectionHelpers.setField(mFragment, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - ReflectionHelpers.setField(mFragment, "mAppEntry", appEntry); - - assertThat(mFragment.handleDisableable()).isTrue(); - verify(mFragment.mActionButtons).setButton1Text(R.string.disable_text); - } - - @Test - @Config(shadows = ShadowUtils.class) - public void handleDisableable_appIsDisabled_buttonShouldShowEnable() { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = "pkg"; - info.enabled = false; - info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - - final AppEntry appEntry = mock(AppEntry.class); - appEntry.info = info; - when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( - new HashSet<>()); - - ReflectionHelpers.setField(mFragment, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - ReflectionHelpers.setField(mFragment, "mAppEntry", appEntry); - - assertThat(mFragment.handleDisableable()).isTrue(); - verify(mFragment.mActionButtons).setButton1Text(R.string.enable_text); - verify(mFragment.mActionButtons).setButton1Positive(true); - } - - @Test - @Config(shadows = ShadowUtils.class) - public void handleDisableable_appIsEnabledAndInKeepEnabledWhitelist_buttonShouldNotWork() { - final ApplicationInfo info = new ApplicationInfo(); - info.packageName = "pkg"; - info.enabled = true; - info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - - final AppEntry appEntry = mock(AppEntry.class); - appEntry.info = info; - - final HashSet packages = new HashSet<>(); - packages.add(info.packageName); - when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( - packages); - - ReflectionHelpers.setField(mFragment, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - ReflectionHelpers.setField(mFragment, "mAppEntry", appEntry); - - assertThat(mFragment.handleDisableable()).isFalse(); - verify(mFragment.mActionButtons).setButton1Text(R.string.disable_text); - } - - @Test - public void initUninstallButtonForUserApp_shouldSetNegativeButton() { - final ApplicationInfo info = new ApplicationInfo(); - info.flags = ApplicationInfo.FLAG_INSTALLED; - info.enabled = true; - final PackageInfo packageInfo = mock(PackageInfo.class); - packageInfo.applicationInfo = info; - ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); - ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); - - mFragment.initUninstallButtonForUserApp(); - - verify(mFragment.mActionButtons).setButton1Positive(false); - } - @Test public void getNumberOfUserWithPackageInstalled_twoUsersInstalled_shouldReturnTwo() throws PackageManager.NameNotFoundException{ @@ -396,13 +238,4 @@ public final class AppInfoDashboardFragmentTest { assertThat(mFragment.getNumberOfUserWithPackageInstalled(packageName)).isEqualTo(1); } - - @Implements(Utils.class) - public static class ShadowUtils { - @Implementation - public static boolean isSystemPackage(Resources resources, PackageManager pm, - PackageInfo pkg) { - return false; - } - } }