Create Controller for app buttons

These two buttons(uninstall + forcestop) are used in both battery page
and app page, we should move the logic for these two buttons into one
place.

This cl creates the AppButtonsPreferenceController for the above
purpose. This cl only copies the logic to controller but hasn't make
InstalledAppDetails use this controller.

Since DialogFragment could not use function in controller directly,
the controller expose DialogListener and all the fragments must
implement this interface. Then they can delegate the method call
to controller directly.

The following cl will:
1. Make the InstalledAppDetails be compatible to controller
2. Make the InstalledAppDetails use this controller.

Bug: 35810915
Test: RunSettingsRoboTests
Change-Id: Ie2aa8064bcec3003233896c18be772825b12930a
(cherry picked from commit 82d07983b4)
This commit is contained in:
jackqdyulei
2017-03-15 15:13:10 -07:00
committed by Lei Yu
parent a48cb8d978
commit 31b8de1e21
7 changed files with 1299 additions and 17 deletions

View File

@@ -129,4 +129,25 @@ public interface DevicePolicyManagerWrapper {
* @see android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts
*/
List<String> getOwnerInstalledCaCerts(@NonNull UserHandle user);
/**
* Calls {@code DevicePolicyManager.isDeviceOwnerAppOnAnyUser()}.
*
* @see android.app.admin.DevicePolicyManager#isDeviceOwnerAppOnAnyUser
*/
boolean isDeviceOwnerAppOnAnyUser(String packageName);
/**
* Calls {@code DevicePolicyManager.packageHasActiveAdmins()}.
*
* @see android.app.admin.DevicePolicyManager#packageHasActiveAdmins
*/
boolean packageHasActiveAdmins(String packageName);
/**
* Calls {@code DevicePolicyManager.isUninstallInQueue()}.
*
* @see android.app.admin.DevicePolicyManager#isUninstallInQueue
*/
boolean isUninstallInQueue(String packageName);
}

View File

@@ -101,4 +101,19 @@ public class DevicePolicyManagerWrapperImpl implements DevicePolicyManagerWrappe
public List<String> getOwnerInstalledCaCerts(@NonNull UserHandle user) {
return mDpm.getOwnerInstalledCaCerts(user);
}
@Override
public boolean isDeviceOwnerAppOnAnyUser(String packageName) {
return mDpm.isDeviceOwnerAppOnAnyUser(packageName);
}
@Override
public boolean packageHasActiveAdmins(String packageName) {
return mDpm.packageHasActiveAdmins(packageName);
}
@Override
public boolean isUninstallInQueue(String packageName) {
return mDpm.isUninstallInQueue(packageName);
}
}

View File

@@ -16,11 +16,19 @@
package com.android.settings.fuelgauge;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.Preference;
@@ -36,6 +44,8 @@ import com.android.settings.Utils;
import com.android.settings.applications.AppHeaderController;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.PreferenceController;
import com.android.settings.enterprise.DevicePolicyManagerWrapper;
import com.android.settings.enterprise.DevicePolicyManagerWrapperImpl;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState;
@@ -50,7 +60,8 @@ import java.util.List;
*
* This fragment will replace {@link PowerUsageDetail}
*/
public class AdvancedPowerUsageDetail extends PowerUsageBase {
public class AdvancedPowerUsageDetail extends PowerUsageBase implements
ButtonActionDialogFragment.AppButtonsDialogListener {
public static final String TAG = "AdvancedPowerUsageDetail";
public static final String EXTRA_UID = "extra_uid";
@@ -67,6 +78,9 @@ public class AdvancedPowerUsageDetail extends PowerUsageBase {
private static final String KEY_PREF_POWER_USAGE = "app_power_usage";
private static final String KEY_PREF_HEADER = "header_view";
private static final int REQUEST_UNINSTALL = 0;
private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
@VisibleForTesting
LayoutPreference mHeaderPreference;
@VisibleForTesting
@@ -77,6 +91,11 @@ public class AdvancedPowerUsageDetail extends PowerUsageBase {
private Preference mForegroundPreference;
private Preference mBackgroundPreference;
private Preference mPowerUsagePreference;
private AppButtonsPreferenceController mAppButtonsPreferenceController;
private DevicePolicyManagerWrapper mDpm;
private UserManager mUserManager;
private PackageManager mPackageManager;
public static void startBatteryDetailPage(SettingsActivity caller, PreferenceFragment fragment,
BatteryStatsHelper helper, int which, BatteryEntry entry, String usagePercent) {
@@ -112,6 +131,17 @@ public class AdvancedPowerUsageDetail extends PowerUsageBase {
R.string.battery_details_title, null, new UserHandle(UserHandle.myUserId()));
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mState = ApplicationsState.getInstance(getActivity().getApplication());
mDpm = new DevicePolicyManagerWrapperImpl(
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE));
mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
mPackageManager = activity.getPackageManager();
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -120,7 +150,6 @@ public class AdvancedPowerUsageDetail extends PowerUsageBase {
mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
mPowerUsagePreference = findPreference(KEY_PREF_POWER_USAGE);
mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
mState = ApplicationsState.getInstance(getActivity().getApplication());
final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME);
if (packageName != null) {
@@ -160,7 +189,13 @@ public class AdvancedPowerUsageDetail extends PowerUsageBase {
if (mAppEntry == null) {
controller.setLabel(bundle.getString(EXTRA_LABEL));
controller.setIcon(getContext().getDrawable(bundle.getInt(EXTRA_ICON_ID)));
final int iconId = bundle.getInt(EXTRA_ICON_ID, 0);
if (iconId == 0) {
controller.setIcon(context.getPackageManager().getDefaultActivityIcon());
} else {
controller.setIcon(context.getDrawable(bundle.getInt(EXTRA_ICON_ID)));
}
} else {
mState.ensureIcon(mAppEntry);
controller.setLabel(mAppEntry);
@@ -196,9 +231,26 @@ public class AdvancedPowerUsageDetail extends PowerUsageBase {
controllers.add(new BackgroundActivityPreferenceController(context, uid));
controllers.add(new BatteryOptimizationPreferenceController(
(SettingsActivity) getActivity(), this));
controllers.add(
new AppButtonsPreferenceController(getActivity(), getLifecycle(), packageName));
mAppButtonsPreferenceController = new AppButtonsPreferenceController(
(SettingsActivity) getActivity(), this, getLifecycle(), packageName, mState, mDpm,
mUserManager, mPackageManager, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
controllers.add(mAppButtonsPreferenceController);
return controllers;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mAppButtonsPreferenceController != null) {
mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data);
}
}
@Override
public void handleDialogClick(int id) {
if (mAppButtonsPreferenceController != null) {
mAppButtonsPreferenceController.handleDialogClick(id);
}
}
}

View File

@@ -17,42 +17,139 @@
package com.android.settings.fuelgauge;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Fragment;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
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.v7.preference.PreferenceScreen;
import android.util.Log;
import android.view.View;
import android.webkit.IWebViewUpdateService;
import android.widget.Button;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.DeviceAdminAdd;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.core.lifecycle.LifecycleObserver;
import com.android.settings.core.lifecycle.events.OnDestroy;
import com.android.settings.core.lifecycle.events.OnPause;
import com.android.settings.core.lifecycle.events.OnResume;
import com.android.settings.enterprise.DevicePolicyManagerWrapper;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.applications.ApplicationsState;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
* Controller to control the uninstall button and forcestop button
* Controller to control the uninstall button and forcestop button. All fragments that use
* this controller should implement {@link ButtonActionDialogFragment.AppButtonsDialogListener} and
* handle {@link Fragment#onActivityResult(int, int, Intent)}
*
* An easy way to handle them is to delegate them to {@link #handleDialogClick(int)} and
* {@link #handleActivityResult(int, int, Intent)} in this controller.
*/
//TODO(b/35810915): refine the button logic and make InstalledAppDetails use this controller
//TODO(b/35810915): add test for this file
//TODO(b/35810915): Make InstalledAppDetails use this controller
public class AppButtonsPreferenceController extends PreferenceController implements
LifecycleObserver, OnResume {
LifecycleObserver, OnResume, OnPause, OnDestroy, View.OnClickListener,
ApplicationsState.Callbacks {
public static final String APP_CHG = "chg";
private static final String TAG = "AppButtonsPrefCtl";
private static final String KEY_ACTION_BUTTONS = "action_buttons";
private static final boolean LOCAL_LOGV = false;
@VisibleForTesting
final HashSet<String> mHomePackages = new HashSet<>();
@VisibleForTesting
ApplicationsState mState;
@VisibleForTesting
ApplicationsState.AppEntry mAppEntry;
@VisibleForTesting
PackageInfo mPackageInfo;
@VisibleForTesting
Button mForceStopButton;
@VisibleForTesting
Button mUninstallButton;
@VisibleForTesting
boolean mDisableAfterUninstall = false;
private final int mRequestUninstall;
private final int mRequestRemoveDeviceAdmin;
private ApplicationsState.Session mSession;
private DevicePolicyManagerWrapper mDpm;
private UserManager mUserManager;
private PackageManager mPm;
private SettingsActivity mActivity;
private Fragment mFragment;
private RestrictedLockUtils.EnforcedAdmin mAppsControlDisallowedAdmin;
private MetricsFeatureProvider mMetricsFeatureProvider;
private ApplicationsState.AppEntry mAppEntry;
private LayoutPreference mButtonsPref;
private Button mForceStopButton;
private Button mUninstallButton;
private String mPackageName;
private int mUserId;
private boolean mUpdatedSysApp = false;
private boolean mListeningToPackageRemove = false;
private boolean mFinishing = false;
private boolean mAppsControlDisallowedBySystem;
public AppButtonsPreferenceController(Activity activity, Lifecycle lifecycle,
String packageName) {
public AppButtonsPreferenceController(SettingsActivity activity, Fragment fragment,
Lifecycle lifecycle, String packageName, ApplicationsState state,
DevicePolicyManagerWrapper dpm, UserManager userManager,
PackageManager packageManager, int requestUninstall, int requestRemoveDeviceAdmin) {
super(activity);
if (!(fragment instanceof ButtonActionDialogFragment.AppButtonsDialogListener)) {
throw new IllegalArgumentException(
"Fragment should implement AppButtonsDialogListener");
}
mMetricsFeatureProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider();
mState = state;
mSession = mState.newSession(this);
mDpm = dpm;
mUserManager = userManager;
mPm = packageManager;
mPackageName = packageName;
mActivity = activity;
mFragment = fragment;
mUserId = UserHandle.myUserId();
mRequestUninstall = requestUninstall;
mRequestRemoveDeviceAdmin = requestRemoveDeviceAdmin;
lifecycle.addObserver(this);
ApplicationsState state = ApplicationsState.getInstance(activity.getApplication());
if (packageName != null) {
mAppEntry = state.getEntry(packageName, UserHandle.myUserId());
mAppEntry = mState.getEntry(packageName, mUserId);
} else {
mFinishing = true;
}
}
@@ -72,6 +169,7 @@ public class AppButtonsPreferenceController extends PreferenceController impleme
mForceStopButton = (Button) mButtonsPref.findViewById(R.id.right_button);
mForceStopButton.setText(R.string.force_stop);
mForceStopButton.setEnabled(false);
}
}
@@ -82,6 +180,524 @@ public class AppButtonsPreferenceController extends PreferenceController impleme
@Override
public void onResume() {
//TODO(b/35810915): check and update the status of buttons
mSession.resume();
if (isAvailable() && !mFinishing) {
mAppsControlDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(mActivity,
UserManager.DISALLOW_APPS_CONTROL, mUserId);
mAppsControlDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(mActivity,
UserManager.DISALLOW_APPS_CONTROL, mUserId);
if (!refreshUi()) {
setIntentAndFinish(true);
}
}
}
@Override
public void onPause() {
mSession.pause();
}
@Override
public void onDestroy() {
stopListeningToPackageRemove();
mSession.release();
}
@Override
public void onClick(View v) {
final String packageName = mAppEntry.info.packageName;
final int id = v.getId();
if (id == R.id.left_button) {
// Uninstall
if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
stopListeningToPackageRemove();
Intent uninstallDaIntent = new Intent(mActivity, DeviceAdminAdd.class);
uninstallDaIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
packageName);
mMetricsFeatureProvider.action(mActivity,
MetricsProto.MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN);
mFragment.startActivityForResult(uninstallDaIntent, mRequestRemoveDeviceAdmin);
return;
}
RestrictedLockUtils.EnforcedAdmin admin =
RestrictedLockUtils.checkIfUninstallBlocked(mActivity,
packageName, mUserId);
boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem ||
RestrictedLockUtils.hasBaseUserRestriction(mActivity, packageName, mUserId);
if (admin != null && !uninstallBlockedBySystem) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mActivity, admin);
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
// If the system app has an update and this is the only user on the device,
// then offer to downgrade the app, otherwise only offer to disable the
// app for this user.
if (mUpdatedSysApp && isSingleUser()) {
showDialogInner(ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE);
} else {
showDialogInner(ButtonActionDialogFragment.DialogType.DISABLE);
}
} else {
mMetricsFeatureProvider.action(
mActivity,
mAppEntry.info.enabled
? MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP
: MetricsProto.MetricsEvent.ACTION_SETTINGS_ENABLE_APP);
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT));
}
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
uninstallPkg(packageName, true, false);
} else {
uninstallPkg(packageName, false, false);
}
} else if (id == R.id.right_button) {
// force stop
if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
mActivity, mAppsControlDisallowedAdmin);
} else {
showDialogInner(ButtonActionDialogFragment.DialogType.FORCE_STOP);
}
}
}
public void handleActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == mRequestUninstall) {
if (mDisableAfterUninstall) {
mDisableAfterUninstall = false;
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER));
}
refreshAndFinishIfPossible();
} else if (requestCode == mRequestRemoveDeviceAdmin) {
refreshAndFinishIfPossible();
}
}
public void handleDialogClick(int id) {
switch (id) {
case ButtonActionDialogFragment.DialogType.DISABLE:
mMetricsFeatureProvider.action(mActivity,
MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER));
break;
case ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE:
mMetricsFeatureProvider.action(mActivity,
MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
uninstallPkg(mAppEntry.info.packageName, false, true);
break;
case ButtonActionDialogFragment.DialogType.FORCE_STOP:
forceStopPackage(mAppEntry.info.packageName);
break;
}
}
@Override
public void onRunningStateChanged(boolean running) {
}
@Override
public void onPackageListChanged() {
refreshUi();
}
@Override
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
}
@Override
public void onPackageIconChanged() {
}
@Override
public void onPackageSizeChanged(String packageName) {
}
@Override
public void onAllSizesComputed() {
}
@Override
public void onLauncherInfoChanged() {
}
@Override
public void onLoadEntriesCompleted() {
}
@VisibleForTesting
void retrieveAppEntry() {
mAppEntry = mState.getEntry(mPackageName, mUserId);
if (mAppEntry != null) {
try {
mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
PackageManager.MATCH_DISABLED_COMPONENTS |
PackageManager.MATCH_ANY_USER |
PackageManager.GET_SIGNATURES |
PackageManager.GET_PERMISSIONS);
mPackageName = mAppEntry.info.packageName;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
mPackageInfo = null;
}
} else {
mPackageInfo = null;
}
}
@VisibleForTesting
void updateUninstallButton() {
final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
boolean enabled = true;
if (isBundled) {
enabled = handleDisableable(mUninstallButton);
} else {
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;
}
}
// 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 (isProfileOrDeviceOwner(mPackageInfo.packageName)) {
enabled = false;
}
// Don't allow uninstalling the device provisioning package.
if (Utils.isDeviceProvisioningPackage(mContext.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<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
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;
}
if (isFallbackPackage(mAppEntry.info.packageName)) {
enabled = false;
}
mUninstallButton.setEnabled(enabled);
if (enabled) {
// Register listener
mUninstallButton.setOnClickListener(this);
}
}
/**
* Finish this fragment and return data if possible
*/
private void setIntentAndFinish(boolean appChanged) {
if (LOCAL_LOGV) {
Log.i(TAG, "appChanged=" + appChanged);
}
Intent intent = new Intent();
intent.putExtra(APP_CHG, appChanged);
mActivity.finishPreferencePanel(mFragment, Activity.RESULT_OK, intent);
mFinishing = true;
}
private void refreshAndFinishIfPossible() {
if (!refreshUi()) {
setIntentAndFinish(true);
} else {
startListeningToPackageRemove();
}
}
@VisibleForTesting
boolean isFallbackPackage(String packageName) {
try {
IWebViewUpdateService webviewUpdateService =
IWebViewUpdateService.Stub.asInterface(
ServiceManager.getService("webviewupdate"));
if (webviewUpdateService.isFallbackPackage(packageName)) {
return true;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
return false;
}
@VisibleForTesting
void updateForceStopButton() {
if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
// User can't force stop device admin.
Log.w(TAG, "User can't force stop device admin");
updateForceStopButtonInner(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");
updateForceStopButtonInner(true);
} else {
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);
mActivity.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
}
}
@VisibleForTesting
void updateForceStopButtonInner(boolean enabled) {
if (mAppsControlDisallowedBySystem) {
mForceStopButton.setEnabled(false);
} else {
mForceStopButton.setEnabled(enabled);
mForceStopButton.setOnClickListener(this);
}
}
@VisibleForTesting
void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
stopListeningToPackageRemove();
// Create new intent to launch Uninstaller activity
Uri packageUri = Uri.parse("package:" + packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
mMetricsFeatureProvider.action(
mActivity, MetricsProto.MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP);
mFragment.startActivityForResult(uninstallIntent, mRequestUninstall);
mDisableAfterUninstall = andDisable;
}
@VisibleForTesting
void forceStopPackage(String pkgName) {
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext,
MetricsProto.MetricsEvent.ACTION_APP_FORCE_STOP, pkgName);
ActivityManager am = (ActivityManager) mActivity.getSystemService(
Context.ACTIVITY_SERVICE);
Log.d(TAG, "Stopping package " + pkgName);
am.forceStopPackage(pkgName);
int userId = UserHandle.getUserId(mAppEntry.info.uid);
mState.invalidatePackage(pkgName, userId);
ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId);
if (newEnt != null) {
mAppEntry = newEnt;
}
updateForceStopButton();
}
@VisibleForTesting
boolean handleDisableable(Button button) {
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)
|| isSystemPackage(mActivity.getResources(), mPm, mPackageInfo)) {
// Disable button for core system applications.
button.setText(R.string.disable_text);
} else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
button.setText(R.string.disable_text);
disableable = true;
} else {
button.setText(R.string.enable_text);
disableable = true;
}
return disableable;
}
@VisibleForTesting
boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo packageInfo) {
return Utils.isSystemPackage(resources, pm, packageInfo);
}
private boolean isDisabledUntilUsed() {
return mAppEntry.info.enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
}
private void showDialogInner(@ButtonActionDialogFragment.DialogType int id) {
ButtonActionDialogFragment newFragment = ButtonActionDialogFragment.newInstance(id);
newFragment.setTargetFragment(mFragment, 0);
newFragment.show(mActivity.getFragmentManager(), "dialog " + id);
}
/** Returns whether there is only one user on this device, not including the system-only user */
private boolean isSingleUser() {
final int userCount = mUserManager.getUserCount();
return userCount == 1
|| (mUserManager.isSplitSystemUser() && userCount == 2);
}
/** Returns if the supplied package is device owner or profile owner of at least one user */
private boolean isProfileOrDeviceOwner(String packageName) {
List<UserInfo> userInfos = mUserManager.getUsers();
if (mDpm.isDeviceOwnerAppOnAnyUser(packageName)) {
return true;
}
for (int i = 0, size = userInfos.size(); i < size; i++) {
ComponentName cn = mDpm.getProfileOwnerAsUser(userInfos.get(i).id);
if (cn != null && cn.getPackageName().equals(packageName)) {
return true;
}
}
return false;
}
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);
updateForceStopButtonInner(enabled);
}
};
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;
}
private boolean refreshUi() {
retrieveAppEntry();
if (mAppEntry == null || mPackageInfo == null) {
return false;
}
// Get list of "home" apps and trace through any meta-data references
List<ResolveInfo> homeActivities = new ArrayList<>();
mPm.getHomeActivities(homeActivities);
mHomePackages.clear();
for (int i = 0, size = homeActivities.size(); i < size; i++) {
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);
}
}
}
updateUninstallButton();
updateForceStopButton();
return true;
}
private void startListeningToPackageRemove() {
if (mListeningToPackageRemove) {
return;
}
mListeningToPackageRemove = true;
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
mActivity.registerReceiver(mPackageRemovedReceiver, filter);
}
private void stopListeningToPackageRemove() {
if (!mListeningToPackageRemove) {
return;
}
mListeningToPackageRemove = false;
mActivity.unregisterReceiver(mPackageRemovedReceiver);
}
/**
* Changes the status of disable/enable for a package
*/
private class DisableChangerRunnable implements Runnable {
final PackageManager mPm;
final String mPackageName;
final int mState;
public DisableChangerRunnable(PackageManager pm, String packageName, int state) {
mPm = pm;
mPackageName = packageName;
mState = state;
}
@Override
public void run() {
mPm.setApplicationEnabledSetting(mPackageName, mState, 0);
}
}
/**
* Receiver to listen to the remove action for packages
*/
private final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String packageName = intent.getData().getSchemeSpecificPart();
if (!mFinishing && mAppEntry.info.packageName.equals(packageName)) {
mActivity.finishAndRemoveTask();
}
}
};
}

View File

@@ -0,0 +1,104 @@
package com.android.settings.fuelgauge;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.annotation.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Fragment to show the dialog for uninstall or forcestop. This fragment uses function in
* target fragment to handle the dialog button click.
*/
public class ButtonActionDialogFragment extends InstrumentedDialogFragment implements
DialogInterface.OnClickListener {
/**
* Interface to handle the dialog click
*/
interface AppButtonsDialogListener {
void handleDialogClick(int type);
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({
DialogType.DISABLE,
DialogType.SPECIAL_DISABLE,
DialogType.FORCE_STOP
})
public @interface DialogType {
int DISABLE = 0;
int SPECIAL_DISABLE = 1;
int FORCE_STOP = 2;
}
private static final String ARG_ID = "id";
@VisibleForTesting
int mId;
public static ButtonActionDialogFragment newInstance(@DialogType int id) {
ButtonActionDialogFragment dialogFragment = new ButtonActionDialogFragment();
Bundle args = new Bundle(1);
args.putInt(ARG_ID, id);
dialogFragment.setArguments(args);
return dialogFragment;
}
@Override
public int getMetricsCategory() {
//TODO(35810915): update the metrics label because for now this fragment will be shown
// in two screens
return MetricsProto.MetricsEvent.DIALOG_APP_INFO_ACTION;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Bundle bundle = getArguments();
mId = bundle.getInt(ARG_ID);
Dialog dialog = createDialog(mId);
if (dialog == null) {
throw new IllegalArgumentException("unknown id " + mId);
}
return dialog;
}
@Override
public void onClick(DialogInterface dialog, int which) {
final AppButtonsDialogListener lsn =
(AppButtonsDialogListener) getTargetFragment();
lsn.handleDialogClick(mId);
}
private AlertDialog createDialog(int id) {
final Context context = getContext();
switch (id) {
case DialogType.DISABLE:
case DialogType.SPECIAL_DISABLE:
return new AlertDialog.Builder(context)
.setMessage(R.string.app_disable_dlg_text)
.setPositiveButton(R.string.app_disable_dlg_positive, this)
.setNegativeButton(R.string.dlg_cancel, null)
.create();
case DialogType.FORCE_STOP:
return new AlertDialog.Builder(context)
.setTitle(R.string.force_stop_dlg_title)
.setMessage(R.string.force_stop_dlg_text)
.setPositiveButton(R.string.dlg_ok, this)
.setNegativeButton(R.string.dlg_cancel, null)
.create();
}
return null;
}
}

View File

@@ -0,0 +1,330 @@
/*
* 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.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
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.ActivityManager;
import android.app.Application;
import android.app.Fragment;
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.os.UserManager;
import android.widget.Button;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.TestConfig;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.enterprise.DevicePolicyManagerWrapper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class AppButtonsPreferenceControllerTest {
private static final String PACKAGE_NAME = "com.android.settings";
private static final String RESOURCE_STRING = "string";
private static final boolean ALL_USERS = false;
private static final boolean DISABLE_AFTER_INSTALL = true;
private static final int REQUEST_UNINSTALL = 0;
private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private SettingsActivity mSettingsActivity;
@Mock
private TestFragment mFragment;
@Mock
private Lifecycle mLifecycle;
@Mock
private ApplicationsState mState;
@Mock
private ApplicationsState.AppEntry mAppEntry;
@Mock
private ApplicationInfo mAppInfo;
@Mock
private PackageManager mPackageManger;
@Mock
private DevicePolicyManagerWrapper mDpm;
@Mock
private ActivityManager mAm;
@Mock
private UserManager mUserManager;
@Mock
private Application mApplication;
@Mock
private PackageInfo mPackageInfo;
@Mock
private Button mUninstallButton;
@Mock
private Button mForceStopButton;
private Intent mUninstallIntent;
private AppButtonsPreferenceController mController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest(mSettingsActivity);
doReturn(mUserManager).when(mSettingsActivity).getSystemService(Context.USER_SERVICE);
doReturn(mPackageManger).when(mSettingsActivity).getPackageManager();
doReturn(mAm).when(mSettingsActivity).getSystemService(Context.ACTIVITY_SERVICE);
doReturn(mAppEntry).when(mState).getEntry(anyString(), anyInt());
doReturn(mApplication).when(mSettingsActivity).getApplication();
when(mSettingsActivity.getResources().getString(anyInt())).thenReturn(RESOURCE_STRING);
mController = spy(new AppButtonsPreferenceController(mSettingsActivity, mFragment,
mLifecycle, PACKAGE_NAME, mState, mDpm, mUserManager, mPackageManger,
REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN));
doReturn(false).when(mController).isFallbackPackage(anyString());
mAppEntry.info = mAppInfo;
mAppInfo.packageName = PACKAGE_NAME;
mAppInfo.flags = 0;
mPackageInfo.packageName = PACKAGE_NAME;
mPackageInfo.applicationInfo = mAppInfo;
mController.mUninstallButton = mUninstallButton;
mController.mForceStopButton = mForceStopButton;
mController.mPackageInfo = mPackageInfo;
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
Answer<Void> callable = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
mUninstallIntent = captor.getValue();
return null;
}
};
doAnswer(callable).when(mFragment).startActivityForResult(captor.capture(), anyInt());
}
@Test
public void testRetrieveAppEntry_hasAppEntry_notNull()
throws PackageManager.NameNotFoundException {
doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
mController.retrieveAppEntry();
assertThat(mController.mAppEntry).isNotNull();
assertThat(mController.mPackageInfo).isNotNull();
}
@Test
public void testRetrieveAppEntry_noAppEntry_null() throws PackageManager.NameNotFoundException {
doReturn(null).when(mState).getEntry(eq(PACKAGE_NAME), anyInt());
doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
mController.retrieveAppEntry();
assertThat(mController.mAppEntry).isNull();
assertThat(mController.mPackageInfo).isNull();
}
@Test
public void testRetrieveAppEntry_throwException_null() throws
PackageManager.NameNotFoundException {
doReturn(mAppEntry).when(mState).getEntry(anyString(), anyInt());
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManger).getPackageInfo(
anyString(), anyInt());
mController.retrieveAppEntry();
assertThat(mController.mAppEntry).isNotNull();
assertThat(mController.mPackageInfo).isNull();
}
@Test
public void testUpdateUninstallButton_isSystemApp_handleAsDisableableButton() {
doReturn(false).when(mController).handleDisableable(any());
mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
mController.updateUninstallButton();
verify(mController).handleDisableable(any());
verify(mUninstallButton).setEnabled(false);
}
@Test
public void testUpdateUninstallButton_isDeviceAdminApp_setButtonDisable() {
doReturn(true).when(mController).handleDisableable(any());
mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
doReturn(true).when(mDpm).packageHasActiveAdmins(anyString());
mController.updateUninstallButton();
verify(mController).handleDisableable(any());
verify(mUninstallButton).setEnabled(false);
}
@Test
public void testUpdateUninstallButton_isProfileOrDeviceOwner_setButtonDisable() {
doReturn(true).when(mDpm).isDeviceOwnerAppOnAnyUser(anyString());
mController.updateUninstallButton();
verify(mUninstallButton).setEnabled(false);
}
@Test
public void testUpdateUninstallButton_isDeviceProvisioningApp_setButtonDisable() {
doReturn(true).when(mDpm).isDeviceOwnerAppOnAnyUser(anyString());
when(mSettingsActivity.getResources().getString(anyInt())).thenReturn(PACKAGE_NAME);
mController.updateUninstallButton();
verify(mUninstallButton).setEnabled(false);
}
@Test
public void testUpdateUninstallButton_isUninstallInQueue_setButtonDisable() {
doReturn(true).when(mDpm).isUninstallInQueue(any());
mController.updateUninstallButton();
verify(mUninstallButton).setEnabled(false);
}
@Test
public void testUpdateUninstallButton_isHomeAppAndBundled_setButtonDisable() {
mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
mController.mHomePackages.add(PACKAGE_NAME);
mController.updateUninstallButton();
verify(mUninstallButton).setEnabled(false);
}
@Test
public void testUpdateForceStopButton_HasActiveAdmins_setButtonDisable() {
doReturn(true).when(mDpm).packageHasActiveAdmins(anyString());
mController.updateForceStopButton();
verify(mController).updateForceStopButtonInner(false);
}
@Test
public void testUpdateForceStopButton_AppNotStopped_setButtonEnable() {
mController.updateForceStopButton();
verify(mController).updateForceStopButtonInner(true);
}
@Test
public void testUninstallPkg_intentSent() {
mController.uninstallPkg(PACKAGE_NAME, ALL_USERS, DISABLE_AFTER_INSTALL);
verify(mFragment).startActivityForResult(any(), eq(REQUEST_UNINSTALL));
assertThat(
mUninstallIntent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, true))
.isEqualTo(ALL_USERS);
assertThat(mUninstallIntent.getAction()).isEqualTo(Intent.ACTION_UNINSTALL_PACKAGE);
assertThat(mController.mDisableAfterUninstall).isEqualTo(DISABLE_AFTER_INSTALL);
}
@Test
public void testForceStopPackage_methodInvokedAndUpdated() {
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
doReturn(appEntry).when(mState).getEntry(anyString(), anyInt());
doNothing().when(mController).updateForceStopButton();
mController.forceStopPackage(PACKAGE_NAME);
verify(mAm).forceStopPackage(PACKAGE_NAME);
assertThat(mController.mAppEntry).isSameAs(appEntry);
verify(mController).updateForceStopButton();
}
@Test
public void testHandleDisableable_isHomeApp_notControllable() {
mController.mHomePackages.add(PACKAGE_NAME);
final boolean controllable = mController.handleDisableable(mUninstallButton);
verify(mUninstallButton).setText(R.string.disable_text);
assertThat(controllable).isFalse();
}
@Test
public void testHandleDisableable_isAppEnabled_controllable() {
mAppEntry.info.enabled = true;
mAppEntry.info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
doReturn(false).when(mController).isSystemPackage(any(), any(), any());
final boolean controllable = mController.handleDisableable(mUninstallButton);
verify(mUninstallButton).setText(R.string.disable_text);
assertThat(controllable).isTrue();
}
@Test
public void testHandleDisableable_isAppDisabled_controllable() {
mAppEntry.info.enabled = false;
mAppEntry.info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
doReturn(false).when(mController).isSystemPackage(any(), any(), any());
final boolean controllable = mController.handleDisableable(mUninstallButton);
verify(mUninstallButton).setText(R.string.enable_text);
assertThat(controllable).isTrue();
}
/**
* The test fragment which implements
* {@link ButtonActionDialogFragment.AppButtonsDialogListener}
*/
private static class TestFragment extends Fragment implements
ButtonActionDialogFragment.AppButtonsDialogListener {
@Override
public void handleDialogClick(int type) {
// Do nothing
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import android.app.AlertDialog;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
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.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowDialog;
import org.robolectric.util.FragmentTestUtil;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = {
ShadowEventLogWriter.class
})
public class ButtonActionDialogFragmentTest {
private static final int FORCE_STOP_ID = ButtonActionDialogFragment.DialogType.FORCE_STOP;
private static final int DISABLE_ID = ButtonActionDialogFragment.DialogType.DISABLE;
private static final int SPECIAL_DISABLE_ID =
ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE;
@Mock
private TestFragment mTargetFragment;
private ButtonActionDialogFragment mFragment;
private Context mShadowContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowContext = RuntimeEnvironment.application;
mFragment = spy(ButtonActionDialogFragment.newInstance(FORCE_STOP_ID));
doReturn(mShadowContext).when(mFragment).getContext();
mFragment.setTargetFragment(mTargetFragment, 0);
}
@Test
public void testOnClick_handleToTargetFragment() {
mFragment.onClick(null, 0);
verify(mTargetFragment).handleDialogClick(anyInt());
}
@Test
public void testOnCreateDialog_forceStopDialog() {
ButtonActionDialogFragment fragment = ButtonActionDialogFragment.newInstance(FORCE_STOP_ID);
FragmentTestUtil.startFragment(fragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
mShadowContext.getString(R.string.force_stop_dlg_text));
assertThat(shadowDialog.getTitle()).isEqualTo(
mShadowContext.getString(R.string.force_stop_dlg_title));
assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
mShadowContext.getString(R.string.dlg_ok));
assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
mShadowContext.getString(R.string.dlg_cancel));
}
@Test
public void testOnCreateDialog_disableDialog() {
ButtonActionDialogFragment fragment = ButtonActionDialogFragment.newInstance(DISABLE_ID);
FragmentTestUtil.startFragment(fragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
mShadowContext.getString(R.string.app_disable_dlg_text));
assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
mShadowContext.getString(R.string.app_disable_dlg_positive));
assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
mShadowContext.getString(R.string.dlg_cancel));
}
@Test
public void testOnCreateDialog_specialDisableDialog() {
ButtonActionDialogFragment fragment = ButtonActionDialogFragment.newInstance(
SPECIAL_DISABLE_ID);
FragmentTestUtil.startFragment(fragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
mShadowContext.getString(R.string.app_disable_dlg_text));
assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
mShadowContext.getString(R.string.app_disable_dlg_positive));
assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
mShadowContext.getString(R.string.dlg_cancel));
}
/**
* Test fragment that used as the target fragment, it must implement the
* {@link com.android.settings.fuelgauge.ButtonActionDialogFragment.AppButtonsDialogListener}
*/
public static class TestFragment extends Fragment implements
ButtonActionDialogFragment.AppButtonsDialogListener {
@Override
public void handleDialogClick(int type) {
// do nothing
}
}
}