Add method to disable "disable" button in installed app UI

OEMs can now force grey out the "disable" button for important apps on
device.

Change-Id: Ic075a07ad12592bd60238c7b1c9ab84932c8db3c
Fix: 38250742
Test: make RunSettingsRoboTests
This commit is contained in:
Fan Zhang
2017-05-31 10:42:19 -07:00
parent 63b013ea60
commit a45f3d2f33
6 changed files with 129 additions and 33 deletions

View File

@@ -42,6 +42,7 @@ import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -62,6 +63,7 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment
protected EnforcedAdmin mAppsControlDisallowedAdmin;
protected boolean mAppsControlDisallowedBySystem;
protected ApplicationFeatureProvider mApplicationFeatureProvider;
protected ApplicationsState mState;
protected ApplicationsState.Session mSession;
protected ApplicationsState.AppEntry mAppEntry;
@@ -84,13 +86,14 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFinishing = false;
mState = ApplicationsState.getInstance(getActivity().getApplication());
final Activity activity = getActivity();
mApplicationFeatureProvider = FeatureFactory.getFactory(activity)
.getApplicationFeatureProvider(activity);
mState = ApplicationsState.getInstance(activity.getApplication());
mSession = mState.newSession(this);
Context context = getActivity();
mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mPm = context.getPackageManager();
mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
mPm = activity.getPackageManager();
IBinder b = ServiceManager.getService(Context.USB_SERVICE);
mUsbManager = IUsbManager.Stub.asInterface(b);

View File

@@ -24,6 +24,7 @@ import android.view.View;
import com.android.settings.applications.instantapps.InstantAppButtonsController;
import java.util.List;
import java.util.Set;
public interface ApplicationFeatureProvider {
@@ -86,6 +87,11 @@ public interface ApplicationFeatureProvider {
*/
List<UserAppInfo> findPersistentPreferredActivities(@UserIdInt int userId, Intent[] intents);
/**
* Returns a list of package names that should be kept enabled.
*/
Set<String> getKeepEnabledPackages();
/**
* Callback that receives the number of packages installed on the device.
*/

View File

@@ -129,6 +129,11 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide
return preferredActivities;
}
@Override
public Set<String> getKeepEnabledPackages() {
return new ArraySet<>();
}
private static class CurrentUserAndManagedProfilePolicyInstalledAppCounter
extends InstalledAppCounter {
private NumberOfAppsCallback mCallback;

View File

@@ -91,7 +91,6 @@ import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils;
@@ -228,7 +227,8 @@ public class InstalledAppDetails extends AppInfoBase
}
};
private boolean handleDisableable(Button button) {
@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
@@ -239,7 +239,8 @@ public class InstalledAppDetails extends AppInfoBase
button.setText(R.string.disable_text);
} else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
button.setText(R.string.disable_text);
disableable = true;
disableable = !mApplicationFeatureProvider.getKeepEnabledPackages()
.contains(mAppEntry.info.packageName);
} else {
button.setText(R.string.enable_text);
disableable = true;
@@ -1214,9 +1215,7 @@ public class InstalledAppDetails extends AppInfoBase
void maybeAddInstantAppButtons() {
if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
LayoutPreference buttons = (LayoutPreference) findPreference(KEY_INSTANT_APP_BUTTONS);
final Activity activity = getActivity();
mInstantAppButtonsController = FeatureFactory.getFactory(activity)
.getApplicationFeatureProvider(activity)
mInstantAppButtonsController = mApplicationFeatureProvider
.newInstantAppButtonsController(this,
buttons.findViewById(R.id.instant_app_button_container),
id -> showDialogInner(id, 0))

View File

@@ -245,6 +245,12 @@ public final class ApplicationFeatureProviderImplTest {
.isEqualTo(expectedManagedUserActivities);
}
@Test
public void getKeepEnabledPackages_shouldContainNothing() {
assertThat(mProvider.getKeepEnabledPackages())
.isEmpty();
}
private void setUpUsersAndInstalledApps() {
when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),

View File

@@ -17,19 +17,6 @@
package com.android.settings.applications;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
@@ -38,6 +25,7 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.BatteryStats;
import android.os.UserManager;
import android.support.v7.preference.Preference;
@@ -56,6 +44,7 @@ import com.android.settings.applications.instantapps.InstantAppButtonsController
import com.android.settings.applications.instantapps.InstantAppButtonsController.ShowDialogDelegate;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.Utils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
@@ -69,11 +58,26 @@ 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -87,8 +91,6 @@ public final class InstalledAppDetailsTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
ApplicationFeatureProvider mApplicationFeatureProvider;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private UserManager mUserManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -107,15 +109,17 @@ public final class InstalledAppDetailsTest {
private PackageManager mPackageManager;
@Mock
private BatteryUtils mBatteryUtils;
private FakeFeatureFactory mFeatureFactory;
private InstalledAppDetails mAppDetail;
private Context mShadowContext;
private Preference mBatteryPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFactory = FakeFeatureFactory.setupForTest(mContext);
mShadowContext = RuntimeEnvironment.application;
mAppDetail = spy(new InstalledAppDetails());
mAppDetail.mBatteryUtils = mBatteryUtils;
@@ -347,16 +351,14 @@ public final class InstalledAppDetailsTest {
final InstalledAppDetailsWithMockInstantButtons
fragment = new InstalledAppDetailsWithMockInstantButtons();
ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo);
ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider",
mFeatureFactory.applicationFeatureProvider);
final InstantAppButtonsController buttonsController =
mock(InstantAppButtonsController.class);
when(buttonsController.setPackageName(nullable(String.class)))
.thenReturn(buttonsController);
FakeFeatureFactory.setupForTest(mContext);
FakeFeatureFactory factory =
(FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
when(factory.applicationFeatureProvider.newInstantAppButtonsController(
when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController(
nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class)))
.thenReturn(buttonsController);
@@ -461,4 +463,79 @@ public final class InstalledAppDetailsTest {
public void isBatteryStatsAvailable_parametersNull_returnFalse() {
assertThat(mAppDetail.isBatteryStatsAvailable()).isFalse();
}
@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<String> homePackages = new HashSet<>();
homePackages.add(info.packageName);
ReflectionHelpers.setField(mAppDetail, "mHomePackages", homePackages);
ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
final Button button = mock(Button.class);
assertThat(mAppDetail.handleDisableable(button)).isFalse();
verify(button).setText(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(mAppDetail, "mApplicationFeatureProvider",
mFeatureFactory.applicationFeatureProvider);
ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
final Button button = mock(Button.class);
assertThat(mAppDetail.handleDisableable(button)).isTrue();
verify(button).setText(R.string.disable_text);
}
@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<String> packages = new HashSet<>();
packages.add(info.packageName);
when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn(
packages);
ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider",
mFeatureFactory.applicationFeatureProvider);
ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
final Button button = mock(Button.class);
assertThat(mAppDetail.handleDisableable(button)).isFalse();
verify(button).setText(R.string.disable_text);
}
@Implements(Utils.class)
public static class ShadowUtils {
@Implementation
public static boolean isSystemPackage(Resources resources, PackageManager pm,
PackageInfo pkg) {
return false;
}
}
}