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:
@@ -42,6 +42,7 @@ import com.android.settings.SettingsActivity;
|
|||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.RestrictedLockUtils;
|
import com.android.settingslib.RestrictedLockUtils;
|
||||||
import com.android.settingslib.applications.ApplicationsState;
|
import com.android.settingslib.applications.ApplicationsState;
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||||
@@ -62,6 +63,7 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment
|
|||||||
protected EnforcedAdmin mAppsControlDisallowedAdmin;
|
protected EnforcedAdmin mAppsControlDisallowedAdmin;
|
||||||
protected boolean mAppsControlDisallowedBySystem;
|
protected boolean mAppsControlDisallowedBySystem;
|
||||||
|
|
||||||
|
protected ApplicationFeatureProvider mApplicationFeatureProvider;
|
||||||
protected ApplicationsState mState;
|
protected ApplicationsState mState;
|
||||||
protected ApplicationsState.Session mSession;
|
protected ApplicationsState.Session mSession;
|
||||||
protected ApplicationsState.AppEntry mAppEntry;
|
protected ApplicationsState.AppEntry mAppEntry;
|
||||||
@@ -84,13 +86,14 @@ public abstract class AppInfoBase extends SettingsPreferenceFragment
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mFinishing = false;
|
mFinishing = false;
|
||||||
|
final Activity activity = getActivity();
|
||||||
mState = ApplicationsState.getInstance(getActivity().getApplication());
|
mApplicationFeatureProvider = FeatureFactory.getFactory(activity)
|
||||||
|
.getApplicationFeatureProvider(activity);
|
||||||
|
mState = ApplicationsState.getInstance(activity.getApplication());
|
||||||
mSession = mState.newSession(this);
|
mSession = mState.newSession(this);
|
||||||
Context context = getActivity();
|
mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||||
mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
|
||||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
mPm = activity.getPackageManager();
|
||||||
mPm = context.getPackageManager();
|
|
||||||
IBinder b = ServiceManager.getService(Context.USB_SERVICE);
|
IBinder b = ServiceManager.getService(Context.USB_SERVICE);
|
||||||
mUsbManager = IUsbManager.Stub.asInterface(b);
|
mUsbManager = IUsbManager.Stub.asInterface(b);
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ import android.view.View;
|
|||||||
import com.android.settings.applications.instantapps.InstantAppButtonsController;
|
import com.android.settings.applications.instantapps.InstantAppButtonsController;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public interface ApplicationFeatureProvider {
|
public interface ApplicationFeatureProvider {
|
||||||
|
|
||||||
@@ -86,6 +87,11 @@ public interface ApplicationFeatureProvider {
|
|||||||
*/
|
*/
|
||||||
List<UserAppInfo> findPersistentPreferredActivities(@UserIdInt int userId, Intent[] intents);
|
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.
|
* Callback that receives the number of packages installed on the device.
|
||||||
*/
|
*/
|
||||||
|
@@ -129,6 +129,11 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide
|
|||||||
return preferredActivities;
|
return preferredActivities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getKeepEnabledPackages() {
|
||||||
|
return new ArraySet<>();
|
||||||
|
}
|
||||||
|
|
||||||
private static class CurrentUserAndManagedProfilePolicyInstalledAppCounter
|
private static class CurrentUserAndManagedProfilePolicyInstalledAppCounter
|
||||||
extends InstalledAppCounter {
|
extends InstalledAppCounter {
|
||||||
private NumberOfAppsCallback mCallback;
|
private NumberOfAppsCallback mCallback;
|
||||||
|
@@ -91,7 +91,6 @@ import com.android.settings.fuelgauge.BatteryUtils;
|
|||||||
import com.android.settings.notification.AppNotificationSettings;
|
import com.android.settings.notification.AppNotificationSettings;
|
||||||
import com.android.settings.notification.NotificationBackend;
|
import com.android.settings.notification.NotificationBackend;
|
||||||
import com.android.settings.notification.NotificationBackend.AppRow;
|
import com.android.settings.notification.NotificationBackend.AppRow;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settings.widget.EntityHeaderController;
|
import com.android.settings.widget.EntityHeaderController;
|
||||||
import com.android.settingslib.AppItem;
|
import com.android.settingslib.AppItem;
|
||||||
import com.android.settingslib.RestrictedLockUtils;
|
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;
|
boolean disableable = false;
|
||||||
// Try to prevent the user from bricking their phone
|
// Try to prevent the user from bricking their phone
|
||||||
// by not allowing disabling of apps signed with the
|
// by not allowing disabling of apps signed with the
|
||||||
@@ -239,7 +239,8 @@ public class InstalledAppDetails extends AppInfoBase
|
|||||||
button.setText(R.string.disable_text);
|
button.setText(R.string.disable_text);
|
||||||
} else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
|
} else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
|
||||||
button.setText(R.string.disable_text);
|
button.setText(R.string.disable_text);
|
||||||
disableable = true;
|
disableable = !mApplicationFeatureProvider.getKeepEnabledPackages()
|
||||||
|
.contains(mAppEntry.info.packageName);
|
||||||
} else {
|
} else {
|
||||||
button.setText(R.string.enable_text);
|
button.setText(R.string.enable_text);
|
||||||
disableable = true;
|
disableable = true;
|
||||||
@@ -1214,9 +1215,7 @@ public class InstalledAppDetails extends AppInfoBase
|
|||||||
void maybeAddInstantAppButtons() {
|
void maybeAddInstantAppButtons() {
|
||||||
if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
|
if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
|
||||||
LayoutPreference buttons = (LayoutPreference) findPreference(KEY_INSTANT_APP_BUTTONS);
|
LayoutPreference buttons = (LayoutPreference) findPreference(KEY_INSTANT_APP_BUTTONS);
|
||||||
final Activity activity = getActivity();
|
mInstantAppButtonsController = mApplicationFeatureProvider
|
||||||
mInstantAppButtonsController = FeatureFactory.getFactory(activity)
|
|
||||||
.getApplicationFeatureProvider(activity)
|
|
||||||
.newInstantAppButtonsController(this,
|
.newInstantAppButtonsController(this,
|
||||||
buttons.findViewById(R.id.instant_app_button_container),
|
buttons.findViewById(R.id.instant_app_button_container),
|
||||||
id -> showDialogInner(id, 0))
|
id -> showDialogInner(id, 0))
|
||||||
|
@@ -245,6 +245,12 @@ public final class ApplicationFeatureProviderImplTest {
|
|||||||
.isEqualTo(expectedManagedUserActivities);
|
.isEqualTo(expectedManagedUserActivities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getKeepEnabledPackages_shouldContainNothing() {
|
||||||
|
assertThat(mProvider.getKeepEnabledPackages())
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
private void setUpUsersAndInstalledApps() {
|
private void setUpUsersAndInstalledApps() {
|
||||||
when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
|
when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList(
|
||||||
new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
|
new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
|
||||||
|
@@ -17,19 +17,6 @@
|
|||||||
package com.android.settings.applications;
|
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.AlertDialog;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
@@ -38,6 +25,7 @@ import android.content.Intent;
|
|||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.os.BatteryStats;
|
import android.os.BatteryStats;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.support.v7.preference.Preference;
|
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.applications.instantapps.InstantAppButtonsController.ShowDialogDelegate;
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
|
import com.android.settingslib.Utils;
|
||||||
import com.android.settingslib.applications.AppUtils;
|
import com.android.settingslib.applications.AppUtils;
|
||||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||||
import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
|
import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
|
||||||
@@ -69,11 +58,26 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.annotation.Implementation;
|
||||||
|
import org.robolectric.annotation.Implements;
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
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)
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
@@ -87,8 +91,6 @@ public final class InstalledAppDetailsTest {
|
|||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
@Mock
|
|
||||||
ApplicationFeatureProvider mApplicationFeatureProvider;
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private UserManager mUserManager;
|
private UserManager mUserManager;
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
@@ -107,15 +109,17 @@ public final class InstalledAppDetailsTest {
|
|||||||
private PackageManager mPackageManager;
|
private PackageManager mPackageManager;
|
||||||
@Mock
|
@Mock
|
||||||
private BatteryUtils mBatteryUtils;
|
private BatteryUtils mBatteryUtils;
|
||||||
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
|
|
||||||
private InstalledAppDetails mAppDetail;
|
private InstalledAppDetails mAppDetail;
|
||||||
private Context mShadowContext;
|
private Context mShadowContext;
|
||||||
private Preference mBatteryPreference;
|
private Preference mBatteryPreference;
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mFeatureFactory = FakeFeatureFactory.setupForTest(mContext);
|
||||||
mShadowContext = RuntimeEnvironment.application;
|
mShadowContext = RuntimeEnvironment.application;
|
||||||
mAppDetail = spy(new InstalledAppDetails());
|
mAppDetail = spy(new InstalledAppDetails());
|
||||||
mAppDetail.mBatteryUtils = mBatteryUtils;
|
mAppDetail.mBatteryUtils = mBatteryUtils;
|
||||||
@@ -347,16 +351,14 @@ public final class InstalledAppDetailsTest {
|
|||||||
final InstalledAppDetailsWithMockInstantButtons
|
final InstalledAppDetailsWithMockInstantButtons
|
||||||
fragment = new InstalledAppDetailsWithMockInstantButtons();
|
fragment = new InstalledAppDetailsWithMockInstantButtons();
|
||||||
ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo);
|
ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo);
|
||||||
|
ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider",
|
||||||
|
mFeatureFactory.applicationFeatureProvider);
|
||||||
|
|
||||||
final InstantAppButtonsController buttonsController =
|
final InstantAppButtonsController buttonsController =
|
||||||
mock(InstantAppButtonsController.class);
|
mock(InstantAppButtonsController.class);
|
||||||
when(buttonsController.setPackageName(nullable(String.class)))
|
when(buttonsController.setPackageName(nullable(String.class)))
|
||||||
.thenReturn(buttonsController);
|
.thenReturn(buttonsController);
|
||||||
|
when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController(
|
||||||
FakeFeatureFactory.setupForTest(mContext);
|
|
||||||
FakeFeatureFactory factory =
|
|
||||||
(FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
|
|
||||||
when(factory.applicationFeatureProvider.newInstantAppButtonsController(
|
|
||||||
nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class)))
|
nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class)))
|
||||||
.thenReturn(buttonsController);
|
.thenReturn(buttonsController);
|
||||||
|
|
||||||
@@ -461,4 +463,79 @@ public final class InstalledAppDetailsTest {
|
|||||||
public void isBatteryStatsAvailable_parametersNull_returnFalse() {
|
public void isBatteryStatsAvailable_parametersNull_returnFalse() {
|
||||||
assertThat(mAppDetail.isBatteryStatsAvailable()).isFalse();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user