diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6ccb8d1b08d..0418d51dfc0 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3312,13 +3312,13 @@ - + android:name=".slices.SliceBroadcastReceiver" > diff --git a/res/values/strings.xml b/res/values/strings.xml index 45aadf38d12..686cb724122 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6728,14 +6728,14 @@ No sound - - No sound (Total Silence) + + Total Silence No sound except %1$s - - No sound except alarms, media and system feedback (Alarms only) + + No sound except alarms and media Turn on automatically @@ -7315,8 +7315,11 @@ Alarms - - Media and system feedback + + Media + + + Includes system feedback Reminders @@ -7336,6 +7339,9 @@ If the same person calls a second time within a %d minute period + + Custom + Automatically turn on diff --git a/res/xml/zen_mode_behavior_settings.xml b/res/xml/zen_mode_behavior_settings.xml index c0849daaf48..6aeebe69a9b 100644 --- a/res/xml/zen_mode_behavior_settings.xml +++ b/res/xml/zen_mode_behavior_settings.xml @@ -34,7 +34,8 @@ + android:title="@string/zen_mode_media_system_other" + android:summary="@string/zen_mode_media_system_other_secondary_text"/> installedAdmin = findAdminWithPackageName(packageName); + if (!installedAdmin.isPresent()) { Log.w(TAG, "No component specified in " + action); finish(); return; } + who = installedAdmin.get(); + mUninstalling = true; } if (action != null && action.equals(DevicePolicyManager.ACTION_SET_PROFILE_OWNER)) { @@ -692,6 +689,18 @@ public class DeviceAdminAdd extends Activity { return info != null ? info.isManagedProfile() : false; } + /** + * @return an {@link Optional} containing the admin with a given package name, if it exists, + * or {@link Optional#empty()} otherwise. + */ + private Optional findAdminWithPackageName(String packageName) { + List admins = mDPM.getActiveAdmins(); + if (admins == null) { + return Optional.empty(); + } + return admins.stream().filter(i -> i.getPackageName().equals(packageName)).findAny(); + } + private boolean isAdminUninstallable() { // System apps can't be uninstalled. return !mDeviceAdmin.getActivityInfo().applicationInfo.isSystemApp(); diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 2098bd6bf48..f78459f6980 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -75,6 +75,7 @@ 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.appinfo.AppInfoDashboardFragment; import com.android.settings.applications.appinfo.DrawOverlayDetails; import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.PictureInPictureDetails; diff --git a/src/com/android/settings/applications/InstalledAppDetailsTop.java b/src/com/android/settings/applications/InstalledAppDetailsTop.java index 174a86a2dfc..8090de03ea2 100644 --- a/src/com/android/settings/applications/InstalledAppDetailsTop.java +++ b/src/com/android/settings/applications/InstalledAppDetailsTop.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.util.FeatureFlagUtils; import com.android.settings.SettingsActivity; +import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.core.FeatureFlags; public class InstalledAppDetailsTop extends SettingsActivity { diff --git a/src/com/android/settings/applications/RecentAppsPreferenceController.java b/src/com/android/settings/applications/RecentAppsPreferenceController.java index c613a7b166e..ee954acf250 100644 --- a/src/com/android/settings/applications/RecentAppsPreferenceController.java +++ b/src/com/android/settings/applications/RecentAppsPreferenceController.java @@ -40,6 +40,7 @@ import android.util.Log; import com.android.settings.R; import com.android.settings.Utils; +import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.core.FeatureFlags; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.widget.AppPreference; 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/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java index 017afe75479..ffe2bf313f9 100644 --- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java @@ -32,7 +32,6 @@ import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.core.BasePreferenceController; import com.android.settings.fuelgauge.AdvancedPowerUsageDetail; import com.android.settings.fuelgauge.BatteryEntry; diff --git a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java index 61f3e46ca8e..669bc5a3a9e 100644 --- a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java @@ -34,7 +34,6 @@ import android.text.format.Formatter; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.datausage.AppDataUsage; import com.android.settings.datausage.DataUsageList; import com.android.settings.datausage.DataUsageUtils; diff --git a/src/com/android/settings/applications/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java similarity index 64% rename from src/com/android/settings/applications/AppInfoDashboardFragment.java rename to src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 1e24df900fc..57e2b0c1f84 100755 --- a/src/com/android/settings/applications/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -14,7 +14,7 @@ * under the License. */ -package com.android.settings.applications; +package com.android.settings.applications.appinfo; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; @@ -23,10 +23,8 @@ import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; -import android.app.Fragment; 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; @@ -35,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; @@ -51,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; @@ -59,32 +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.appinfo.AppBatteryPreferenceController; -import com.android.settings.applications.appinfo.AppDataUsagePreferenceController; -import com.android.settings.applications.appinfo.AppInstallerInfoPreferenceController; -import com.android.settings.applications.appinfo.AppInstallerPreferenceCategoryController; -import com.android.settings.applications.appinfo.AppMemoryPreferenceController; -import com.android.settings.applications.appinfo.AppNotificationPreferenceController; -import com.android.settings.applications.appinfo.AppOpenByDefaultPreferenceController; -import com.android.settings.applications.appinfo.AppPermissionPreferenceController; -import com.android.settings.applications.appinfo.AppStoragePreferenceController; -import com.android.settings.applications.appinfo.AppVersionPreferenceController; -import com.android.settings.applications.appinfo.DefaultBrowserShortcutPreferenceController; -import com.android.settings.applications.appinfo.DefaultEmergencyShortcutPreferenceController; -import com.android.settings.applications.appinfo.DefaultHomeShortcutPreferenceController; -import com.android.settings.applications.appinfo.DefaultPhoneShortcutPreferenceController; -import com.android.settings.applications.appinfo.DefaultSmsShortcutPreferenceController; -import com.android.settings.applications.appinfo.DrawOverlayDetailPreferenceController; -import com.android.settings.applications.appinfo.ExternalSourceDetailPreferenceController; -import com.android.settings.applications.appinfo.InstantAppButtonsPreferenceController; -import com.android.settings.applications.appinfo.InstantAppDomainsPreferenceController; -import com.android.settings.applications.appinfo.PictureInPictureDetailPreferenceController; -import com.android.settings.applications.appinfo.WriteSystemSettingsPreferenceController; +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; @@ -98,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; /** @@ -116,38 +87,37 @@ public class AppInfoDashboardFragment extends DashboardFragment private static final String TAG = "AppInfoDashboard"; // Menu identifiers - public static final int UNINSTALL_ALL_USERS_MENU = 1; - public static final int UNINSTALL_UPDATES = 2; + private static final int UNINSTALL_ALL_USERS_MENU = 1; + private static final int UNINSTALL_UPDATES = 2; // Result code identifiers - public static final int REQUEST_UNINSTALL = 0; + @VisibleForTesting + static final int REQUEST_UNINSTALL = 0; private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; - public static final int SUB_INFO_FRAGMENT = 1; + static final int SUB_INFO_FRAGMENT = 1; - public static final int LOADER_CHART_DATA = 2; - public static final int LOADER_STORAGE = 3; - @VisibleForTesting - public static final int LOADER_BATTERY = 4; + static final int LOADER_CHART_DATA = 2; + static final int LOADER_STORAGE = 3; + static final int LOADER_BATTERY = 4; // Dialog identifiers used in showDialog private static final int DLG_BASE = 0; 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"; public static final String ARG_PACKAGE_UID = "uid"; - protected static final boolean localLOGV = false; - private static final String KEY_ADVANCED_APP_INFO_CATEGORY = "advanced_app_info"; + private static final boolean localLOGV = false; private EnforcedAdmin mAppsControlDisallowedAdmin; private boolean mAppsControlDisallowedBySystem; - private ApplicationFeatureProvider mApplicationFeatureProvider; private ApplicationsState mState; private ApplicationsState.Session mSession; private ApplicationsState.AppEntry mAppEntry; @@ -163,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; @@ -173,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. @@ -185,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 { - 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); @@ -379,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); @@ -414,20 +261,21 @@ public class AppInfoDashboardFragment extends DashboardFragment return controllers; } - public ApplicationsState.AppEntry getAppEntry() { + ApplicationsState.AppEntry getAppEntry() { if (mAppEntry == null) { retrieveAppEntry(); } return mAppEntry; } - public PackageInfo getPackageInfo() { + PackageInfo getPackageInfo() { if (mAppEntry == null) { retrieveAppEntry(); } return mPackageInfo; } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (mFinishing) { @@ -435,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) @@ -492,7 +336,7 @@ public class AppInfoDashboardFragment extends DashboardFragment } menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; - MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); + final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); uninstallUpdatesItem.setVisible(mUpdatedSysApp && !mAppsControlDisallowedBySystem); if (uninstallUpdatesItem.isVisible()) { RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(), @@ -525,7 +369,7 @@ public class AppInfoDashboardFragment extends DashboardFragment mDisableAfterUninstall = false; new DisableChanger(this, mAppEntry.info, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) - .execute((Object)null); + .execute((Object) null); } // continue with following operations case REQUEST_REMOVE_DEVICE_ADMIN: @@ -569,7 +413,7 @@ public class AppInfoDashboardFragment extends DashboardFragment showIt = false; } else if (mUserManager.getUsers().size() < 2) { showIt = false; - } else if (PackageUtil.countPackageInUsers(mPm, mUserManager, mPackageName) < 2 + } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { showIt = false; } else if (AppUtils.isInstant(appEntry.info)) { @@ -578,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(); @@ -604,31 +433,11 @@ 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 - List homeActivities = new ArrayList(); - mPm.getHomeActivities(homeActivities); - mHomePackages.clear(); - for (int i = 0; i< homeActivities.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); - } - } - } - - checkForceStop(); setAppLabelAndIcon(mPackageInfo); - initUninstallButtons(); // Update the preference summaries. - Activity context = getActivity(); + final Activity context = getActivity(); for (Callback callback : mCallbacks) { callback.refreshUi(); } @@ -641,7 +450,7 @@ public class AppInfoDashboardFragment extends DashboardFragment // All other times: if the app no longer exists then we want // to go away. try { - ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo( + final ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo( mAppEntry.info.packageName, PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_ANY_USER); @@ -712,8 +521,8 @@ public class AppInfoDashboardFragment extends DashboardFragment private 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); + final Uri packageURI = Uri.parse("package:"+packageName); + final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); mMetricsFeatureProvider.action( getContext(), MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP); @@ -723,66 +532,32 @@ public class AppInfoDashboardFragment extends DashboardFragment private void forceStopPackage(String pkgName) { mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName); - ActivityManager am = (ActivityManager) getActivity().getSystemService( + final ActivityManager am = (ActivityManager) getActivity().getSystemService( Context.ACTIVITY_SERVICE); Log.d(TAG, "Stopping package " + pkgName); am.forceStopPackage(pkgName); - int userId = UserHandle.getUserId(mAppEntry.info.uid); + final int userId = UserHandle.getUserId(mAppEntry.info.uid); mState.invalidatePackage(pkgName, userId); - AppEntry newEnt = mState.getEntry(pkgName, userId); + final AppEntry newEnt = mState.getEntry(pkgName, userId); 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 { - 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, SettingsPreferenceFragment caller, AppEntry appEntry) { // start new fragment to display extended information - Bundle args = new Bundle(); + final Bundle args = new Bundle(); args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName); args.putInt(ARG_PACKAGE_UID, appEntry.info.uid); - SettingsActivity sa = (SettingsActivity) caller.getActivity(); + final SettingsActivity sa = (SettingsActivity) caller.getActivity(); sa.startPreferencePanel(caller, fragment.getName(), args, title, null, caller, SUB_INFO_FRAGMENT); } - private void handleUninstallButtonClick() { + void handleUninstallButtonClick() { if (mAppEntry == null) { setIntentAndFinish(true, true); return; @@ -790,8 +565,8 @@ public class AppInfoDashboardFragment extends DashboardFragment final String packageName = mAppEntry.info.packageName; if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { stopListeningToPackageRemove(); - Activity activity = getActivity(); - Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class); + final Activity activity = getActivity(); + final Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class); uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, mPackageName); mMetricsFeatureProvider.action( @@ -799,9 +574,9 @@ public class AppInfoDashboardFragment extends DashboardFragment activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN); return; } - EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(), + final EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(), packageName, mUserId); - boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem || + final boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem || RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId); if (admin != null && !uninstallBlockedBySystem) { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin); @@ -830,7 +605,7 @@ public class AppInfoDashboardFragment extends DashboardFragment } } - private void handleForceStopButtonClick() { + void handleForceStopButtonClick() { if (mAppEntry == null) { setIntentAndFinish(true, true); return; @@ -847,8 +622,7 @@ public class AppInfoDashboardFragment extends DashboardFragment /** 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); + return userCount == 1 || (mUserManager.isSplitSystemUser() && userCount == 2); } private void onPackageRemoved() { @@ -856,35 +630,25 @@ public class AppInfoDashboardFragment extends DashboardFragment getActivity().finishAndRemoveTask(); } - /** - * Elicit this class for testing. Test cannot be done in robolectric because it - * invokes the new API. - */ @VisibleForTesting - public static class PackageUtil { - /** - * Count how many users in device have installed package {@paramref packageName} - */ - public static int countPackageInUsers(PackageManager packageManager, UserManager - userManager, String packageName) { - final List userInfos = userManager.getUsers(true); - int count = 0; + int getNumberOfUserWithPackageInstalled(String packageName) { + final List userInfos = mUserManager.getUsers(true); + int count = 0; - for (final UserInfo userInfo : userInfos) { - try { - // Use this API to check whether user has this package - final ApplicationInfo info = packageManager.getApplicationInfoAsUser( - packageName, PackageManager.GET_META_DATA, userInfo.id); - if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { - count++; - } - } catch(NameNotFoundException e) { - Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id); + for (final UserInfo userInfo : userInfos) { + try { + // Use this API to check whether user has this package + final ApplicationInfo info = mPm.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userInfo.id); + if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { + count++; } + } catch(NameNotFoundException e) { + Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id); } - - return count; } + + return count; } private static class DisableChanger extends AsyncTask { @@ -907,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; @@ -924,7 +678,7 @@ public class AppInfoDashboardFragment extends DashboardFragment final Bundle args = getArguments(); mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; if (mPackageName == null) { - Intent intent = (args == null) ? + final Intent intent = (args == null) ? getActivity().getIntent() : (Intent) args.getParcelable("intent"); if (intent != null) { mPackageName = intent.getData().getSchemeSpecificPart(); @@ -964,16 +718,15 @@ public class AppInfoDashboardFragment extends DashboardFragment private void setIntentAndFinish(boolean finish, boolean appChanged) { if (localLOGV) Log.i(TAG, "appChanged="+appChanged); - Intent intent = new Intent(); + final Intent intent = new Intent(); intent.putExtra(ManageApplications.APP_CHG, appChanged); - SettingsActivity sa = (SettingsActivity)getActivity(); + final SettingsActivity sa = (SettingsActivity)getActivity(); sa.finishPreferencePanel(this, Activity.RESULT_OK, intent); mFinishing = true; } - public void showDialogInner(int id, int moveErrorCode) { - DialogFragment newFragment = - MyAlertDialogFragment.newInstance(id, moveErrorCode); + void showDialogInner(int id, int moveErrorCode) { + final DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); newFragment.setTargetFragment(this, 0); newFragment.show(getFragmentManager(), "dialog " + id); } @@ -1015,24 +768,6 @@ public class AppInfoDashboardFragment extends DashboardFragment } } - public static void startAppInfoFragment(Class fragment, int titleRes, - String pkg, int uid, Fragment source, int request, int sourceMetricsCategory) { - startAppInfoFragment(fragment, titleRes, pkg, uid, source.getActivity(), request, - sourceMetricsCategory); - } - - public static void startAppInfoFragment(Class fragment, int titleRes, - String pkg, int uid, Activity source, int request, int sourceMetricsCategory) { - Bundle args = new Bundle(); - args.putString(ARG_PACKAGE_NAME, pkg); - args.putInt(ARG_PACKAGE_UID, uid); - - Intent intent = Utils.onBuildStartFragmentIntent(source, fragment.getName(), - args, null, titleRes, null, false, sourceMetricsCategory); - source.startActivityForResultAsUser(intent, request, - new UserHandle(UserHandle.getUserId(uid))); - } - public static class MyAlertDialogFragment extends InstrumentedDialogFragment { private static final String ARG_ID = "id"; @@ -1044,10 +779,10 @@ public class AppInfoDashboardFragment extends DashboardFragment @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - int id = getArguments().getInt(ARG_ID); - int errorCode = getArguments().getInt("moveError"); - Dialog dialog = ((AppInfoDashboardFragment) getTargetFragment()) - .createDialog(id, errorCode); + final int id = getArguments().getInt(ARG_ID); + final int errorCode = getArguments().getInt("moveError"); + final Dialog dialog = + ((AppInfoDashboardFragment) getTargetFragment()).createDialog(id, errorCode); if (dialog == null) { throw new IllegalArgumentException("unknown id " + id); } @@ -1055,8 +790,8 @@ public class AppInfoDashboardFragment extends DashboardFragment } public static MyAlertDialogFragment newInstance(int id, int errorCode) { - MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment(); - Bundle args = new Bundle(); + final MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment(); + final Bundle args = new Bundle(); args.putInt(ARG_ID, id); args.putInt("moveError", errorCode); dialogFragment.setArguments(args); @@ -1085,7 +820,7 @@ public class AppInfoDashboardFragment extends DashboardFragment private final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - String packageName = intent.getData().getSchemeSpecificPart(); + final String packageName = intent.getData().getSchemeSpecificPart(); if (!mFinishing && (mAppEntry == null || mAppEntry.info == null || TextUtils.equals(mAppEntry.info.packageName, packageName))) { onPackageRemoved(); diff --git a/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java b/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java index eac0a0c94c5..105a01e38f4 100644 --- a/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java +++ b/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java @@ -22,7 +22,6 @@ import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.core.BasePreferenceController; /* diff --git a/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java index 2449004b525..1fdc690d8d1 100644 --- a/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java @@ -23,7 +23,6 @@ import android.support.v7.preference.Preference; import com.android.settings.R; import com.android.settings.Utils; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.AppStoreUtil; import com.android.settingslib.applications.AppUtils; diff --git a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java index 3943041b2fd..7b497a9c8e8 100644 --- a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java @@ -26,7 +26,6 @@ import android.text.format.Formatter; import com.android.settings.R; import com.android.settings.SettingsActivity; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.ProcStatsData; import com.android.settings.applications.ProcStatsEntry; import com.android.settings.applications.ProcStatsPackageEntry; diff --git a/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java b/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java index 7eef370ecc1..1f1950418b3 100644 --- a/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java @@ -20,7 +20,6 @@ import android.content.Context; import android.support.v7.preference.Preference; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.notification.AppNotificationSettings; import com.android.settings.notification.NotificationBackend; import com.android.settingslib.applications.ApplicationsState; diff --git a/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java b/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java index a56e3fb6712..3f20381dbb5 100644 --- a/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java @@ -26,7 +26,6 @@ import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.AppLaunchSettings; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; diff --git a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java index 815e8d854c2..b844f785d90 100644 --- a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java @@ -26,7 +26,6 @@ import android.support.v7.preference.Preference; import android.util.Log; import com.android.settings.R; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settingslib.applications.PermissionsSummaryHelper; import java.util.ArrayList; diff --git a/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java b/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java index d737288c62b..86383cb0678 100644 --- a/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java @@ -28,7 +28,6 @@ import android.text.format.Formatter; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.AppStorageSettings; import com.android.settings.applications.FetchPackageStorageAsyncLoader; import com.android.settingslib.applications.StorageStatsSource; diff --git a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java index 82719f7f006..0cfeb008a3b 100644 --- a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java @@ -21,7 +21,6 @@ import android.support.v7.preference.Preference; import android.text.BidiFormatter; import com.android.settings.R; -import com.android.settings.applications.AppInfoDashboardFragment; public class AppVersionPreferenceController extends AppInfoPreferenceControllerBase { diff --git a/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceController.java b/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceController.java index 314d7995bf7..37a9edfff05 100644 --- a/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceController.java @@ -25,7 +25,6 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.applications.AppInfoDashboardFragment; public class DrawOverlayDetailPreferenceController extends AppInfoPreferenceControllerBase { diff --git a/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceController.java b/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceController.java index 4ac67ed77a5..6fb6dc356c9 100644 --- a/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceController.java @@ -22,7 +22,6 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.AppStateInstallAppsBridge; public class ExternalSourceDetailPreferenceController extends AppInfoPreferenceControllerBase { diff --git a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java index 04000666d0c..87e5fdb359d 100644 --- a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java +++ b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java @@ -107,6 +107,9 @@ public class ExternalSourcesDetails extends AppInfoWithHeader @Override protected boolean refreshUi() { + if (mPackageInfo == null || mPackageInfo.applicationInfo == null) { + return false; + } if (mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, UserHandle.of(UserHandle.myUserId()))) { mSwitchPref.setChecked(false); diff --git a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java index e35fa76b85f..b9fe0039cf2 100644 --- a/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceController.java @@ -22,7 +22,6 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.PreferenceScreen; import com.android.settings.R; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.instantapps.InstantAppButtonsController; diff --git a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java index 1d2229127da..d89c538d764 100644 --- a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java @@ -22,7 +22,6 @@ import android.support.v7.preference.Preference; import com.android.settings.Utils; import com.android.settings.applications.AppDomainsPreference; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settingslib.applications.AppUtils; import java.util.Set; diff --git a/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceController.java b/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceController.java index aea6baef922..18736832158 100644 --- a/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceController.java @@ -26,7 +26,6 @@ import android.support.v7.preference.Preference; import android.util.Log; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.applications.AppInfoDashboardFragment; public class PictureInPictureDetailPreferenceController extends AppInfoPreferenceControllerBase { diff --git a/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceController.java b/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceController.java index 55b181ab7b4..2a88d2f6c9d 100644 --- a/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceController.java @@ -25,7 +25,6 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.applications.AppInfoDashboardFragment; public class WriteSystemSettingsPreferenceController extends AppInfoPreferenceControllerBase { diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 067e167a92f..7371294e124 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -93,7 +93,7 @@ import com.android.settings.applications.InstalledAppCounter; import com.android.settings.applications.InstalledAppDetails; import com.android.settings.applications.NotificationApps; import com.android.settings.applications.UsageAccessDetails; -import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.applications.appinfo.DrawOverlayDetails; import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.WriteSettingsDetails; diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index acb20d55c4f..3b38df64a7f 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -47,7 +47,7 @@ import com.android.settings.applications.ProcessStatsSummary; import com.android.settings.applications.ProcessStatsUi; import com.android.settings.applications.UsageAccessDetails; import com.android.settings.applications.VrListenerSettings; -import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.applications.appinfo.DrawOverlayDetails; import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.PictureInPictureDetails; diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccess.java b/src/com/android/settings/datausage/UnrestrictedDataAccess.java index 5b55ada19c6..e8a7bbfa2f9 100644 --- a/src/com/android/settings/datausage/UnrestrictedDataAccess.java +++ b/src/com/android/settings/datausage/UnrestrictedDataAccess.java @@ -32,7 +32,7 @@ import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.applications.AppStateBaseBridge; import com.android.settings.applications.InstalledAppDetails; -import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.core.FeatureFlags; import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState; import com.android.settings.overlay.FeatureFactory; diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java index 15ca87bd859..35b8bd1576a 100644 --- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java +++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java @@ -68,6 +68,8 @@ public class SimStatusDialogController implements LifecycleObserver, OnResume, O @VisibleForTesting final static int SERVICE_STATE_VALUE_ID = R.id.service_state_value; @VisibleForTesting + final static int SIGNAL_STRENGTH_LABEL_ID = R.id.signal_strength_label; + @VisibleForTesting final static int SIGNAL_STRENGTH_VALUE_ID = R.id.signal_strength_value; @VisibleForTesting final static int CELLULAR_NETWORK_TYPE_VALUE_ID = R.id.network_type_value; @@ -262,6 +264,21 @@ public class SimStatusDialogController implements LifecycleObserver, OnResume, O } private void updateSignalStrength(SignalStrength signalStrength) { + final int subscriptionId = mSubscriptionInfo.getSubscriptionId(); + final PersistableBundle carrierConfig = + mCarrierConfigManager.getConfigForSubId(subscriptionId); + // by default we show the signal strength + boolean showSignalStrength = true; + if (carrierConfig != null) { + showSignalStrength = carrierConfig.getBoolean( + CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL); + } + if (!showSignalStrength) { + mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_LABEL_ID); + mDialog.removeSettingFromScreen(SIGNAL_STRENGTH_VALUE_ID); + return; + } + final int state = getCurrentServiceState().getState(); if ((ServiceState.STATE_OUT_OF_SERVICE == state) || @@ -327,9 +344,14 @@ public class SimStatusDialogController implements LifecycleObserver, OnResume, O private void updateIccidNumber() { final int subscriptionId = mSubscriptionInfo.getSubscriptionId(); - final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subscriptionId); - final boolean showIccId = carrierConfig.getBoolean( - CarrierConfigManager.KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL); + final PersistableBundle carrierConfig = + mCarrierConfigManager.getConfigForSubId(subscriptionId); + // do not show iccid by default + boolean showIccId = false; + if (carrierConfig != null) { + showIccId = carrierConfig.getBoolean( + CarrierConfigManager.KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL); + } if (!showIccId) { mDialog.removeSettingFromScreen(ICCID_INFO_LABEL_ID); mDialog.removeSettingFromScreen(ICCID_INFO_VALUE_ID); diff --git a/src/com/android/settings/location/RecentLocationRequestPreferenceController.java b/src/com/android/settings/location/RecentLocationRequestPreferenceController.java index 8c4fa5798ef..461f6e36a75 100644 --- a/src/com/android/settings/location/RecentLocationRequestPreferenceController.java +++ b/src/com/android/settings/location/RecentLocationRequestPreferenceController.java @@ -25,7 +25,7 @@ import android.util.FeatureFlagUtils; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.applications.InstalledAppDetails; -import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.core.FeatureFlags; import com.android.settings.widget.AppPreference; import com.android.settingslib.core.lifecycle.Lifecycle; diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java index f8408fcd273..1ee20d30633 100644 --- a/src/com/android/settings/notification/ZenModeSettings.java +++ b/src/com/android/settings/notification/ZenModeSettings.java @@ -97,23 +97,21 @@ public class ZenModeSettings extends ZenModeSettingsBase { enabledCategories = getEnabledCategories(policy); } + // no sound categories can bypass dnd int numCategories = enabledCategories.size(); if (numCategories == 0) { - return mContext.getString(R.string.zen_mode_behavior_no_sound); + return mContext.getString(R.string.zen_mode_behavior_total_silence); } - String s = enabledCategories.get(0).toLowerCase(); - for (int i = 1; i < numCategories; i++) { - if (i == numCategories - 1) { - s = mContext.getString(R.string.join_many_items_last, - s, enabledCategories.get(i).toLowerCase()); - } else { - s = mContext.getString(R.string.join_many_items_middle, - s, enabledCategories.get(i).toLowerCase()); - } + // only alarms and media/system can bypass dnd + if (numCategories == 2 && + isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_ALARMS) && + isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER)) { + return mContext.getString(R.string.zen_mode_behavior_alarms_only); } - return mContext.getString(R.string.zen_mode_behavior_no_sound_except, s); + // custom + return mContext.getString(R.string.zen_mode_behavior_summary_custom); } String getAutomaticRulesSummary() { diff --git a/src/com/android/settings/search/DatabaseIndexingUtils.java b/src/com/android/settings/search/DatabaseIndexingUtils.java index 207d09fdb37..94ec65096e3 100644 --- a/src/com/android/settings/search/DatabaseIndexingUtils.java +++ b/src/com/android/settings/search/DatabaseIndexingUtils.java @@ -43,7 +43,7 @@ public class DatabaseIndexingUtils { private static final String TAG = "IndexingUtil"; - private static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER = + public static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER = "SEARCH_INDEX_DATA_PROVIDER"; /** diff --git a/src/com/android/settings/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java similarity index 67% rename from src/com/android/settings/SettingsSliceProvider.java rename to src/com/android/settings/slices/SettingsSliceProvider.java index 845dacddca3..22035d2195b 100644 --- a/src/com/android/settings/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -14,28 +14,31 @@ * limitations under the License */ -package com.android.settings; +package com.android.settings.slices; import android.app.PendingIntent; -import android.app.slice.Slice; -import android.app.slice.SliceProvider; + import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.net.Uri; -import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import com.android.settings.R; + +import androidx.app.slice.Slice; +import androidx.app.slice.SliceProvider; +import androidx.app.slice.builders.ListBuilder; + public class SettingsSliceProvider extends SliceProvider { public static final String SLICE_AUTHORITY = "com.android.settings.slices"; public static final String PATH_WIFI = "wifi"; public static final String ACTION_WIFI_CHANGED = "com.android.settings.slice.action.WIFI_CHANGED"; - // TODO -- Associate slice URI with search result instead of separate hardcoded thing - public static final String[] WIFI_SEARCH_TERMS = {"wi-fi", "wifi", "internet"}; + // TODO -- Associate slice URI with search result instead of separate hardcoded thing public static Uri getUri(String path) { return new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) @@ -44,7 +47,7 @@ public class SettingsSliceProvider extends SliceProvider { } @Override - public boolean onCreate() { + public boolean onCreateSliceProvider() { return true; } @@ -53,15 +56,15 @@ public class SettingsSliceProvider extends SliceProvider { String path = sliceUri.getPath(); switch (path) { case "/" + PATH_WIFI: - return createWifi(sliceUri); - + return createWifiSlice(sliceUri); } throw new IllegalArgumentException("Unrecognized slice uri: " + sliceUri); } - private Slice createWifi(Uri uri) { + + // TODO (b/70622039) remove this when the proper wifi slice is enabled. + private Slice createWifiSlice(Uri sliceUri) { // Get wifi state - String[] toggleHints; WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); int wifiState = wifiManager.getWifiState(); boolean wifiEnabled = false; @@ -74,7 +77,6 @@ public class SettingsSliceProvider extends SliceProvider { case WifiManager.WIFI_STATE_ENABLED: case WifiManager.WIFI_STATE_ENABLING: state = wifiManager.getConnectionInfo().getSSID(); - WifiInfo.removeDoubleQuotes(state); wifiEnabled = true; break; case WifiManager.WIFI_STATE_UNKNOWN: @@ -82,28 +84,17 @@ public class SettingsSliceProvider extends SliceProvider { state = ""; // just don't show anything? break; } - if (wifiEnabled) { - toggleHints = new String[] {Slice.HINT_TOGGLE, Slice.HINT_SELECTED}; - } else { - toggleHints = new String[] {Slice.HINT_TOGGLE}; - } - // Construct the slice - Slice.Builder b = new Slice.Builder(uri); - b.addSubSlice(new Slice.Builder(b) - .addAction(getIntent("android.settings.WIFI_SETTINGS"), - new Slice.Builder(b) - .addText(getContext().getString(R.string.wifi_settings), null) - .addText(state, null) - .addIcon(Icon.createWithResource(getContext(), - R.drawable.ic_settings_wireless), null, Slice.HINT_HIDDEN) - .addHints(Slice.HINT_TITLE) - .build()) - .addAction(getBroadcastIntent(ACTION_WIFI_CHANGED), - new Slice.Builder(b) - .addHints(toggleHints) - .build()) - .build()); - return b.build(); + + boolean finalWifiEnabled = wifiEnabled; + return new ListBuilder(sliceUri) + .setColor(R.color.material_blue_500) + .add(b -> b + .setTitle(getContext().getString(R.string.wifi_settings)) + .setTitleItem(Icon.createWithResource(getContext(), R.drawable.wifi_signal)) + .setSubtitle(state) + .addToggle(getBroadcastIntent(ACTION_WIFI_CHANGED), finalWifiEnabled) + .setContentIntent(getIntent(Intent.ACTION_MAIN))) + .build(); } private PendingIntent getIntent(String action) { diff --git a/src/com/android/settings/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java similarity index 85% rename from src/com/android/settings/SliceBroadcastReceiver.java rename to src/com/android/settings/slices/SliceBroadcastReceiver.java index f43e3a39206..b6f2ab945ca 100644 --- a/src/com/android/settings/SliceBroadcastReceiver.java +++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java @@ -14,9 +14,9 @@ * limitations under the License */ -package com.android.settings; +package com.android.settings.slices; -import static com.android.settings.SettingsSliceProvider.ACTION_WIFI_CHANGED; +import static com.android.settings.slices.SettingsSliceProvider.ACTION_WIFI_CHANGED; import android.app.slice.Slice; import android.content.BroadcastReceiver; @@ -42,8 +42,8 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { // Wait a bit for wifi to update (TODO: is there a better way to do this?) Handler h = new Handler(); h.postDelayed(() -> { - Uri uri = SettingsSliceProvider.getUri(SettingsSliceProvider.PATH_WIFI); - context.getContentResolver().notifyChange(uri, null); + Uri uri = SettingsSliceProvider.getUri(SettingsSliceProvider.PATH_WIFI); + context.getContentResolver().notifyChange(uri, null); }, 1000); break; } diff --git a/src/com/android/settings/slices/SliceData.java b/src/com/android/settings/slices/SliceData.java new file mode 100644 index 00000000000..528f23c9133 --- /dev/null +++ b/src/com/android/settings/slices/SliceData.java @@ -0,0 +1,188 @@ +/* + * 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.slices; + +import android.net.Uri; +import android.text.TextUtils; + +/** + * TODO (b/67996923) Add SlicesIndexingManager + * Data class representing a slice stored by {@link SlicesIndexingManager}. + * Note that {@link #key} is treated as a primary key for this class and determines equality. + */ +public class SliceData { + + private final String key; + + private final String title; + + private final String summary; + + private final String screenTitle; + + private final int iconResource; + + private final String fragmentClassName; + + private final Uri uri; + + private final String preferenceController; + + public String getKey() { + return key; + } + + public String getTitle() { + return title; + } + + public String getSummary() { + return summary; + } + + public String getScreenTitle() { + return screenTitle; + } + + public int getIconResource() { + return iconResource; + } + + public String getFragmentClassName() { + return fragmentClassName; + } + + public Uri getUri() { + return uri; + } + + public String getPreferenceController() { + return preferenceController; + } + + private SliceData(Builder builder) { + key = builder.mKey; + title = builder.mTitle; + summary = builder.mSummary; + screenTitle = builder.mScreenTitle; + iconResource = builder.mIconResource; + fragmentClassName = builder.mFragmentClassName; + uri = builder.mUri; + preferenceController = builder.mPrefControllerClassName; + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SliceData)) { + return false; + } + SliceData newObject = (SliceData) obj; + return TextUtils.equals(key, newObject.key); + } + + static class Builder { + private String mKey; + + private String mTitle; + + private String mSummary; + + private String mScreenTitle; + + private int mIconResource; + + private String mFragmentClassName; + + private Uri mUri; + + private String mPrefControllerClassName; + + public Builder setKey(String key) { + mKey = key; + return this; + } + + public Builder setTitle(String title) { + mTitle = title; + return this; + } + + public Builder setSummary(String summary) { + mSummary = summary; + return this; + } + + public Builder setScreenTitle(String screenTitle) { + mScreenTitle = screenTitle; + return this; + } + + public Builder setIcon(int iconResource) { + mIconResource = iconResource; + return this; + } + + public Builder setPreferenceControllerClassName(String controllerClassName) { + mPrefControllerClassName = controllerClassName; + return this; + } + + public Builder setFragmentName(String fragmentClassName) { + mFragmentClassName = fragmentClassName; + return this; + } + + public Builder setUri(Uri uri) { + mUri = uri; + return this; + } + + public SliceData build() { + if (TextUtils.isEmpty(mKey)) { + throw new IllegalStateException("Key cannot be empty"); + } + + if (TextUtils.isEmpty(mTitle)) { + throw new IllegalStateException("Title cannot be empty"); + } + + if (TextUtils.isEmpty(mFragmentClassName)) { + throw new IllegalStateException("Fragment Name cannot be empty"); + } + + if (TextUtils.isEmpty(mPrefControllerClassName)) { + throw new IllegalStateException("Preference Controller cannot be empty"); + } + + if (mUri == null) { + throw new IllegalStateException("Uri cannot be null"); + } + + return new SliceData(this); + } + + public String getKey() { + return mKey; + } + } + +} \ No newline at end of file diff --git a/src/com/android/settings/slices/SlicesDatabaseHelper.java b/src/com/android/settings/slices/SlicesDatabaseHelper.java new file mode 100644 index 00000000000..a74fc812d61 --- /dev/null +++ b/src/com/android/settings/slices/SlicesDatabaseHelper.java @@ -0,0 +1,122 @@ +package com.android.settings.slices; + +import android.content.Context; + +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Defines the schema for the Slices database. + */ +public class SlicesDatabaseHelper extends SQLiteOpenHelper { + + private static final String TAG = "SlicesDatabaseHelper"; + + private static final String DATABASE_NAME = "slices_index.db"; + private static final String SHARED_PREFS_TAG = "slices_shared_prefs"; + + private static final int DATABASE_VERSION = 1; + + public interface Tables { + String TABLE_SLICES_INDEX = "slices_index"; + } + + public interface IndexColumns { + /** + * Primary key of the DB. Preference key from preference controllers. + */ + String KEY = "key"; + + /** + * Title of the Setting. + */ + String TITLE = "title"; + + /** + * Summary / Subtitle for the setting. + */ + String SUBTITLE = "subtitle"; + + /** + * Title of the Setting screen on which the Setting lives. + */ + String SCREENTITLE = "screentitle"; + + /** + * Resource ID for the icon of the setting. Should be 0 for no icon. + */ + String ICON_RESOURCE = "icon"; + + /** + * Classname of the fragment name of the page that hosts the setting. + */ + String FRAGMENT = "fragment"; + + /** + * Class name of the controller backing the setting. Must be a + * {@link com.android.settings.core.BasePreferenceController}. + */ + String CONTROLLER = "controller"; + } + + private static final String CREATE_SLICES_TABLE = + "CREATE VIRTUAL TABLE " + Tables.TABLE_SLICES_INDEX + " USING fts4" + + "(" + + IndexColumns.KEY + + ", " + + IndexColumns.TITLE + + ", " + + IndexColumns.SUBTITLE + + ", " + + IndexColumns.SCREENTITLE + + ", " + + IndexColumns.ICON_RESOURCE + + ", " + + IndexColumns.FRAGMENT + + ", " + + IndexColumns.CONTROLLER + + ");"; + + private final Context mContext; + + public SlicesDatabaseHelper(Context context) { + super(context, DATABASE_NAME, null /* CursorFactor */, DATABASE_VERSION); + mContext = context; + } + + @Override + public void onCreate(SQLiteDatabase db) { + createDatabases(db); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion < DATABASE_VERSION) { + Log.d(TAG, "Reconstructing DB from " + oldVersion + "to " + newVersion); + reconstruct(db); + } + } + + @VisibleForTesting + void reconstruct(SQLiteDatabase db) { + mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE) + .edit() + .clear() + .commit(); + dropTables(db); + createDatabases(db); + } + + private void createDatabases(SQLiteDatabase db) { + db.execSQL(CREATE_SLICES_TABLE); + Log.d(TAG, "Created databases"); + } + + + private void dropTables(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SLICES_INDEX); + } +} \ No newline at end of file diff --git a/src/com/android/settings/widget/EntityHeaderController.java b/src/com/android/settings/widget/EntityHeaderController.java index 5fa75862898..0d07e67f809 100644 --- a/src/com/android/settings/widget/EntityHeaderController.java +++ b/src/com/android/settings/widget/EntityHeaderController.java @@ -45,7 +45,7 @@ import com.android.settings.Utils; import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.InstalledAppDetails; import com.android.settings.applications.LayoutPreference; -import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.core.FeatureFlags; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.applications.ApplicationsState; diff --git a/tests/robotests/Android.mk b/tests/robotests/Android.mk index 97e5e04f875..727188488a1 100644 --- a/tests/robotests/Android.mk +++ b/tests/robotests/Android.mk @@ -14,7 +14,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_JAVA_LIBRARIES := \ junit \ - platform-robolectric-3.4.2-prebuilt \ + platform-robolectric-3.5.1-prebuilt \ telephony-common LOCAL_INSTRUMENTATION_FOR := Settings @@ -42,4 +42,4 @@ LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src LOCAL_ROBOTEST_TIMEOUT := 36000 -include prebuilts/misc/common/robolectric/3.4.2/run_robotests.mk +include prebuilts/misc/common/robolectric/3.5.1/run_robotests.mk diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider index 6d3ec9a3c96..ebcea43a42a 100644 --- a/tests/robotests/assets/grandfather_not_implementing_index_provider +++ b/tests/robotests/assets/grandfather_not_implementing_index_provider @@ -1,4 +1,4 @@ -com.android.settings.applications.AppInfoDashboardFragment +com.android.settings.applications.appinfo.AppInfoDashboardFragment com.android.settings.bluetooth.DevicePickerFragment com.android.settings.bluetooth.BluetoothDeviceDetailsFragment com.android.settings.bluetooth.BluetoothPairingDetail diff --git a/tests/robotests/src/com/android/settings/HelpTrampolineTest.java b/tests/robotests/src/com/android/settings/HelpTrampolineTest.java index e10b878d31a..a6bcf03c7fc 100644 --- a/tests/robotests/src/com/android/settings/HelpTrampolineTest.java +++ b/tests/robotests/src/com/android/settings/HelpTrampolineTest.java @@ -50,7 +50,7 @@ public class HelpTrampolineTest { final Intent intent = new Intent().setClassName( RuntimeEnvironment.application.getPackageName(), HelpTrampoline.class.getName()); - Robolectric.buildActivity(HelpTrampoline.class).withIntent(intent).create().get(); + Robolectric.buildActivity(HelpTrampoline.class, intent).create().get(); assertThat(ShadowHelpUtils.isGetHelpIntentCalled()).isFalse(); } @@ -60,8 +60,8 @@ public class HelpTrampolineTest { final Intent intent = new Intent().setClassName( RuntimeEnvironment.application.getPackageName(), HelpTrampoline.class.getName()) .putExtra(Intent.EXTRA_TEXT, "help_url_upgrading"); - final ShadowActivity shadow = shadowOf(Robolectric.buildActivity(HelpTrampoline.class) - .withIntent(intent).create().get()); + final ShadowActivity shadow = + shadowOf(Robolectric.buildActivity(HelpTrampoline.class, intent).create().get()); final Intent launchedIntent = shadow.getNextStartedActivity(); assertThat(ShadowHelpUtils.isGetHelpIntentCalled()).isTrue(); diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java deleted file mode 100644 index e742549e720..00000000000 --- a/tests/robotests/src/com/android/settings/applications/AppInfoDashboardFragmentTest.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * 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; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.nullable; -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.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.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; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -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.HashSet; - -@RunWith(SettingsRobolectricTestRunner.class) -@Config( - manifest = TestConfig.MANIFEST_PATH, - sdk = TestConfig.SDK_VERSION -) -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) - private SettingsActivity mActivity; - @Mock - private DevicePolicyManagerWrapper mDevicePolicyManager; - @Mock - private PackageManager mPackageManager; - @Mock - private AppOpsManager mAppOpsManager; - - private FakeFeatureFactory mFeatureFactory; - private AppInfoDashboardFragment mAppDetail; - private Context mShadowContext; - - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mFeatureFactory = FakeFeatureFactory.setupForTest(); - mShadowContext = RuntimeEnvironment.application; - mAppDetail = spy(new AppInfoDashboardFragment()); - doReturn(mActivity).when(mAppDetail).getActivity(); - doReturn(mShadowContext).when(mAppDetail).getContext(); - doReturn(mPackageManager).when(mActivity).getPackageManager(); - doReturn(mAppOpsManager).when(mActivity).getSystemService(Context.APP_OPS_SERVICE); - mAppDetail.mActionButtons = ActionButtonPreferenceTest.createMock(); - - // Default to not considering any apps to be instant (individual tests can override this). - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> false)); - } - - @Test - public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() { - when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); - when(mUserManager.getUsers().size()).thenReturn(2); - ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); - ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); - final ApplicationInfo info = new ApplicationInfo(); - info.enabled = true; - final AppEntry appEntry = mock(AppEntry.class); - appEntry.info = info; - final PackageInfo packageInfo = mock(PackageInfo.class); - ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); - - assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isTrue(); - } - - @Test - public void shouldShowUninstallForAll_installForSelfOnly_shouldReturnFalse() { - when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); - when(mUserManager.getUsers().size()).thenReturn(2); - ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); - ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); - 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); - ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); - - assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isFalse(); - } - - @Test - public void launchFragment_hasNoPackageInfo_shouldFinish() { - ReflectionHelpers.setField(mAppDetail, "mPackageInfo", null); - - assertThat(mAppDetail.ensurePackageInfoAvailable(mActivity)).isFalse(); - verify(mActivity).finishAndRemoveTask(); - } - - @Test - public void launchFragment_hasPackageInfo_shouldReturnTrue() { - final PackageInfo packageInfo = mock(PackageInfo.class); - ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); - - assertThat(mAppDetail.ensurePackageInfoAvailable(mActivity)).isTrue(); - verify(mActivity, never()).finishAndRemoveTask(); - } - - @Test - public void packageSizeChange_isOtherPackage_shouldNotRefreshUi() { - ReflectionHelpers.setField(mAppDetail, "mPackageName", PACKAGE_NAME); - mAppDetail.onPackageSizeChanged("Not_" + PACKAGE_NAME); - - verify(mAppDetail, never()).refreshUi(); - } - - @Test - public void packageSizeChange_isOwnPackage_shouldRefreshUi() { - doReturn(Boolean.TRUE).when(mAppDetail).refreshUi(); - ReflectionHelpers.setField(mAppDetail, "mPackageName", PACKAGE_NAME); - - mAppDetail.onPackageSizeChanged(PACKAGE_NAME); - - verify(mAppDetail).refreshUi(); - } - - // Tests that we don't show the "uninstall for all users" button for instant apps. - @Test - public void instantApps_noUninstallForAllButton() { - // Make this app appear to be instant. - ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", - (InstantAppDataProvider) (i -> true)); - when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); - when(mUserManager.getUsers().size()).thenReturn(2); - - final ApplicationInfo info = new ApplicationInfo(); - info.enabled = true; - final AppEntry appEntry = mock(AppEntry.class); - appEntry.info = info; - final PackageInfo packageInfo = mock(PackageInfo.class); - - ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); - ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); - ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); - - assertThat(mAppDetail.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(mAppDetail, "mUserManager", mUserManager); - ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); - ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); - - mAppDetail.initUninstallButtonForUserApp(); - verify(mAppDetail.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(mAppDetail, "mDpm", mDevicePolicyManager); - ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); - ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); - - mAppDetail.checkForceStop(); - verify(mAppDetail.mActionButtons).setButton2Visible(false); - } - - @Test - public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() { - doReturn(true).when(mAppDetail).refreshUi(); - - mAppDetail.onActivityResult(InstalledAppDetails.REQUEST_UNINSTALL, 0, mock(Intent.class)); - - 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(mAppDetail, "mHomePackages", homePackages); - ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); - - assertThat(mAppDetail.handleDisableable()).isFalse(); - verify(mAppDetail.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(mAppDetail, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); - - assertThat(mAppDetail.handleDisableable()).isTrue(); - verify(mAppDetail.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(mAppDetail, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); - - assertThat(mAppDetail.handleDisableable()).isTrue(); - verify(mAppDetail.mActionButtons).setButton1Text(R.string.enable_text); - verify(mAppDetail.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(mAppDetail, "mApplicationFeatureProvider", - mFeatureFactory.applicationFeatureProvider); - ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); - - assertThat(mAppDetail.handleDisableable()).isFalse(); - verify(mAppDetail.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(mAppDetail, "mUserManager", mUserManager); - ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); - - mAppDetail.initUninstallButtonForUserApp(); - - verify(mAppDetail.mActionButtons).setButton1Positive(false); - } - - @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/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/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java index 3516445f120..91833f562cf 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java @@ -40,7 +40,6 @@ import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.testutils.SettingsRobolectricTestRunner; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java index b02e01e30fe..76160eeba77 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java @@ -37,7 +37,6 @@ import android.os.Bundle; import android.support.v7.preference.Preference; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.datausage.AppDataUsage; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState.AppEntry; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java new file mode 100644 index 00000000000..87b82ad4d88 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoDashboardFragmentTest.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.os.UserManager; + +import com.android.settings.SettingsActivity; +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.wrapper.DevicePolicyManagerWrapper; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.ApplicationsState.AppEntry; +import com.android.settingslib.applications.instantapps.InstantAppDataProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config( + manifest = TestConfig.MANIFEST_PATH, + sdk = TestConfig.SDK_VERSION +) +public final class AppInfoDashboardFragmentTest { + + private static final String PACKAGE_NAME = "test_package_name"; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private UserManager mUserManager; + @Mock + private SettingsActivity mActivity; + @Mock + private DevicePolicyManagerWrapper mDevicePolicyManager; + @Mock + private PackageManager mPackageManager; + + private AppInfoDashboardFragment mFragment; + private Context mShadowContext; + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mShadowContext = RuntimeEnvironment.application; + mFragment = spy(new AppInfoDashboardFragment()); + doReturn(mActivity).when(mFragment).getActivity(); + doReturn(mShadowContext).when(mFragment).getContext(); + doReturn(mPackageManager).when(mActivity).getPackageManager(); + + // Default to not considering any apps to be instant (individual tests can override this). + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> false)); + } + + @Test + public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() { + when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); + when(mUserManager.getUsers().size()).thenReturn(2); + ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager); + ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); + final ApplicationInfo info = new ApplicationInfo(); + info.enabled = true; + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = info; + final PackageInfo packageInfo = mock(PackageInfo.class); + ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); + + assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isTrue(); + } + + @Test + public void shouldShowUninstallForAll_installForSelfOnly_shouldReturnFalse() { + when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); + when(mUserManager.getUsers().size()).thenReturn(2); + ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager); + ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); + 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); + ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); + + assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isFalse(); + } + + @Test + public void launchFragment_hasNoPackageInfo_shouldFinish() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", null); + + assertThat(mFragment.ensurePackageInfoAvailable(mActivity)).isFalse(); + verify(mActivity).finishAndRemoveTask(); + } + + @Test + public void launchFragment_hasPackageInfo_shouldReturnTrue() { + final PackageInfo packageInfo = mock(PackageInfo.class); + ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); + + assertThat(mFragment.ensurePackageInfoAvailable(mActivity)).isTrue(); + verify(mActivity, never()).finishAndRemoveTask(); + } + + @Test + public void packageSizeChange_isOtherPackage_shouldNotRefreshUi() { + ReflectionHelpers.setField(mFragment, "mPackageName", PACKAGE_NAME); + mFragment.onPackageSizeChanged("Not_" + PACKAGE_NAME); + + verify(mFragment, never()).refreshUi(); + } + + @Test + public void packageSizeChange_isOwnPackage_shouldRefreshUi() { + doReturn(Boolean.TRUE).when(mFragment).refreshUi(); + ReflectionHelpers.setField(mFragment, "mPackageName", PACKAGE_NAME); + + mFragment.onPackageSizeChanged(PACKAGE_NAME); + + verify(mFragment).refreshUi(); + } + + // Tests that we don't show the "uninstall for all users" button for instant apps. + @Test + public void instantApps_noUninstallForAllButton() { + // Make this app appear to be instant. + ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", + (InstantAppDataProvider) (i -> true)); + when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); + when(mUserManager.getUsers().size()).thenReturn(2); + + final ApplicationInfo info = new ApplicationInfo(); + info.enabled = true; + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = info; + final PackageInfo packageInfo = mock(PackageInfo.class); + + ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager); + ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); + ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); + + assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isFalse(); + } + + @Test + public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() { + doReturn(true).when(mFragment).refreshUi(); + + mFragment.onActivityResult(mFragment.REQUEST_UNINSTALL, 0, mock(Intent.class)); + + verify(mActivity).invalidateOptionsMenu(); + } + + @Test + public void getNumberOfUserWithPackageInstalled_twoUsersInstalled_shouldReturnTwo() + throws PackageManager.NameNotFoundException{ + final String packageName = "Package1"; + 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, "yue", UserInfo.FLAG_GUEST)); + when(mUserManager.getUsers(true)).thenReturn(userInfos); + ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); + final ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.flags = ApplicationInfo.FLAG_INSTALLED; + when(mPackageManager.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userID1)) + .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userID2)) + .thenReturn(appInfo); + ReflectionHelpers.setField(mFragment, "mPm", mPackageManager); + + assertThat(mFragment.getNumberOfUserWithPackageInstalled(packageName)).isEqualTo(2); + } + + @Test + public void getNumberOfUserWithPackageInstalled_oneUserInstalled_shouldReturnOne() + throws PackageManager.NameNotFoundException{ + final String packageName = "Package1"; + 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, "yue", UserInfo.FLAG_GUEST)); + when(mUserManager.getUsers(true)).thenReturn(userInfos); + ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); + final ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.flags = ApplicationInfo.FLAG_INSTALLED; + when(mPackageManager.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userID1)) + .thenReturn(appInfo); + when(mPackageManager.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userID2)) + .thenThrow(new PackageManager.NameNotFoundException()); + ReflectionHelpers.setField(mFragment, "mPm", mPackageManager); + + assertThat(mFragment.getNumberOfUserWithPackageInstalled(packageName)).isEqualTo(1); + + } +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBaseTest.java index 25dcab31ca5..51b6ddf06d6 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBaseTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBaseTest.java @@ -32,7 +32,6 @@ import android.support.v7.preference.PreferenceScreen; import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.notification.AppNotificationSettings; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java index ffbc8f55954..d8d11bc5c27 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java @@ -39,7 +39,6 @@ import android.os.UserManager; import android.support.v7.preference.Preference; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java index d74e301004c..47844c59b0a 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppMemoryPreferenceControllerTest.java @@ -34,7 +34,6 @@ import android.support.v7.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.ProcStatsData; import com.android.settings.applications.ProcessStatsDetail; import com.android.settings.testutils.SettingsRobolectricTestRunner; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java index 482f33cf903..0b747a8f662 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java @@ -30,7 +30,6 @@ import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.notification.AppNotificationSettings; import com.android.settings.notification.NotificationBackend; import com.android.settings.testutils.SettingsRobolectricTestRunner; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java index b708232ed1d..c5003cc9290 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java @@ -32,7 +32,6 @@ import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.AppLaunchSettings; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.AppUtils; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java index f9f8d98da81..f0b415cb9fc 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppPermissionPreferenceControllerTest.java @@ -34,7 +34,6 @@ import android.support.v7.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java index 729914acc9c..c0695178424 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java @@ -32,7 +32,6 @@ import android.os.Bundle; import android.support.v7.preference.Preference; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.AppStorageSettings; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.ApplicationsState.AppEntry; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java index 74184897f0d..d6ecf3e120c 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java @@ -25,7 +25,6 @@ import android.content.pm.PackageInfo; import android.support.v7.preference.Preference; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java index 358e50d2ed9..e44fdfb2070 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBaseTest.java @@ -31,7 +31,6 @@ import android.support.v7.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.DefaultAppSettings; import com.android.settings.testutils.SettingsRobolectricTestRunner; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceControllerTest.java index a7468b5a69d..18a29e3cd3f 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/DrawOverlayDetailPreferenceControllerTest.java @@ -32,7 +32,6 @@ import android.os.UserManager; import android.support.v7.preference.Preference; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceControllerTest.java index d500be901b2..7e542f70920 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourceDetailPreferenceControllerTest.java @@ -28,7 +28,6 @@ import android.os.UserManager; import android.support.v7.preference.Preference; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java new file mode 100644 index 00000000000..ce38a562bdc --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settings.applications.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.os.UserManager; + +import com.android.settings.TestConfig; +import com.android.settings.applications.AppStateInstallAppsBridge; +import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.RestrictedSwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ExternalSourcesDetailsTest { + + @Mock + private UserManager mUserManager; + @Mock + private RestrictedSwitchPreference mSwitchPref; + @Mock + private PackageInfo mPackageInfo; + + private ExternalSourcesDetails mFragment; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mFragment = new ExternalSourcesDetails(); + ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); + ReflectionHelpers.setField(mFragment, "mSwitchPref", mSwitchPref); + } + + @Test + public void refreshUi_noPackageInfo_shouldReturnFalseAndNoCrash() { + mFragment.refreshUi(); + + assertThat(mFragment.refreshUi()).isFalse(); + // should not crash + } + + @Test + public void refreshUi_noApplicationInfo_shouldReturnFalseAndNoCrash() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); + + mFragment.refreshUi(); + + assertThat(mFragment.refreshUi()).isFalse(); + // should not crash + } + + @Test + public void refreshUi_hasApplicationInfo_shouldReturnTrue() { + ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo); + mPackageInfo.applicationInfo = new ApplicationInfo(); + final AppStateInstallAppsBridge appBridge = mock(AppStateInstallAppsBridge.class); + ReflectionHelpers.setField(mFragment, "mAppBridge", appBridge); + when(appBridge.createInstallAppsStateFor(nullable(String.class), anyInt())) + .thenReturn(mock(InstallAppsState.class)); + + mFragment.refreshUi(); + + assertThat(mFragment.refreshUi()).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java index 121659538a4..eb8a082ce57 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java @@ -34,7 +34,6 @@ import android.support.v7.preference.PreferenceScreen; import android.view.View; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.applications.LayoutPreference; import com.android.settings.applications.instantapps.InstantAppButtonsController; import com.android.settings.testutils.FakeFeatureFactory; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java index f1776e88e7d..bb0b42a07a2 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceControllerTest.java @@ -18,8 +18,6 @@ package com.android.settings.applications.appinfo; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -34,7 +32,6 @@ import android.util.ArraySet; import com.android.settings.TestConfig; import com.android.settings.applications.AppDomainsPreference; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceControllerTest.java index 7d8116899ca..cf37b362f2b 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/PictureInPictureDetailPreferenceControllerTest.java @@ -29,7 +29,6 @@ import android.support.v7.preference.Preference; import com.android.settings.R; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceControllerTest.java index fabcbb20439..08133f01a26 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/WriteSystemSettingsPreferenceControllerTest.java @@ -32,7 +32,6 @@ import android.os.UserManager; import android.support.v7.preference.Preference; import com.android.settings.TestConfig; -import com.android.settings.applications.AppInfoDashboardFragment; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; diff --git a/tests/robotests/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java index fd4816233f2..2f896ac9f27 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java @@ -34,11 +34,14 @@ import static com.android.settings.deviceinfo.simstatus.SimStatusDialogControlle .ROAMING_INFO_VALUE_ID; import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController .SERVICE_STATE_VALUE_ID; +import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController + .SIGNAL_STRENGTH_LABEL_ID; import static com.android.settings.deviceinfo.simstatus.SimStatusDialogController .SIGNAL_STRENGTH_VALUE_ID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -157,6 +160,9 @@ public class SimStatusDialogControllerTest { @Test public void initialize_updateDataStateWithPowerOff_shouldUpdateSettingAndResetSignalStrength() { when(mServiceState.getState()).thenReturn(ServiceState.STATE_POWER_OFF); + when(mPersistableBundle.getBoolean( + CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL)).thenReturn( + true); mController.initialize(); @@ -172,6 +178,9 @@ public class SimStatusDialogControllerTest { final int signalAsu = 50; doReturn(signalDbm).when(mController).getDbm(mSignalStrength); doReturn(signalAsu).when(mController).getAsuLevel(mSignalStrength); + when(mPersistableBundle.getBoolean( + CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL)).thenReturn( + true); mController.initialize(); @@ -225,6 +234,30 @@ public class SimStatusDialogControllerTest { verify(mDialog).removeSettingFromScreen(ICCID_INFO_VALUE_ID); } + @Test + public void initialize_doNotShowSignalStrength_shouldRemoveSignalStrengthSetting() { + when(mPersistableBundle.getBoolean( + CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL)).thenReturn( + false); + + mController.initialize(); + + verify(mDialog).removeSettingFromScreen(SIGNAL_STRENGTH_LABEL_ID); + verify(mDialog).removeSettingFromScreen(SIGNAL_STRENGTH_VALUE_ID); + } + + @Test + public void initialize_showSignalStrengthAndIccId_shouldShowSignalStrengthAndIccIdSetting() { + // getConfigForSubId is nullable, so make sure the default behavior is correct + when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null); + + mController.initialize(); + + verify(mDialog).setText(eq(SIGNAL_STRENGTH_VALUE_ID), any()); + verify(mDialog).removeSettingFromScreen(ICCID_INFO_LABEL_ID); + verify(mDialog).removeSettingFromScreen(ICCID_INFO_VALUE_ID); + } + @Test public void initialize_showIccid_shouldSetIccidToSetting() { final String iccid = "12351351231241"; diff --git a/tests/robotests/src/com/android/settings/location/RecentLocationRequestPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/RecentLocationRequestPreferenceControllerTest.java index 5d7cca4c894..a1268d01399 100644 --- a/tests/robotests/src/com/android/settings/location/RecentLocationRequestPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/location/RecentLocationRequestPreferenceControllerTest.java @@ -41,7 +41,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.TestConfig; import com.android.settings.applications.InstalledAppDetails; -import com.android.settings.applications.AppInfoDashboardFragment; +import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.core.FeatureFlags; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.widget.AppPreference; diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java index fa2c6b9a2e0..80233d8ac4d 100644 --- a/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java +++ b/tests/robotests/src/com/android/settings/notification/ZenModeSettingsTest.java @@ -18,6 +18,7 @@ package com.android.settings.notification; import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import android.app.NotificationManager; @@ -53,7 +54,7 @@ public class ZenModeSettingsTest { } @Test - public void testGetBehaviorSettingSummary_sameOrderAsTargetPage() { + public void testGetBehaviorSettingSummary_customBehavior() { NotificationManager.Policy policy = new NotificationManager.Policy( NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS | NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS @@ -63,18 +64,31 @@ public class ZenModeSettingsTest { final String result = mBuilder.getBehaviorSettingSummary(policy, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); - String alarms = mContext.getString(R.string.zen_mode_alarms).toLowerCase(); - String reminders = mContext.getString(R.string.zen_mode_reminders).toLowerCase(); - String events = mContext.getString(R.string.zen_mode_events).toLowerCase(); - String media = mContext.getString(R.string.zen_mode_media_system_other).toLowerCase(); + String custom = mContext.getString(R.string.zen_mode_behavior_summary_custom); + assertEquals(custom, result); + } - assertThat(result).contains(alarms); - assertThat(result).contains(reminders); - assertThat(result).contains(events); - assertThat(result).contains(media); - assertTrue(result.indexOf(alarms) < result.indexOf(media) - && result.indexOf(media) < result.indexOf(reminders) - && result.indexOf(reminders) < result.indexOf(events)); + @Test + public void testGetBehaviorSettingSummary_totalSilence() { + NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0); + final String result = mBuilder.getBehaviorSettingSummary(policy, + Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); + + String totalSilence = mContext.getString(R.string.zen_mode_behavior_total_silence); + assertEquals(totalSilence, result); + } + + @Test + public void testGetBehaviorSettingSummary_alarmsAndMedia() { + NotificationManager.Policy policy = new NotificationManager.Policy( + NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS + | NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, + 0, 0); + final String result = mBuilder.getBehaviorSettingSummary(policy, + Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); + + String alarmsAndMedia = mContext.getString(R.string.zen_mode_behavior_alarms_only); + assertEquals(alarmsAndMedia, result); } @Test diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java index 20a05e3b2dc..8628678930c 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockSettingsHelperTest.java @@ -131,9 +131,7 @@ public class ChooseLockSettingsHelperTest { public void testLaunchConfirmationActivity_internal_shouldPropagateTheme() { Intent intent = new Intent() .putExtra(WizardManagerHelper.EXTRA_THEME, WizardManagerHelper.THEME_GLIF_V2); - Activity activity = Robolectric.buildActivity(Activity.class) - .withIntent(intent) - .get(); + Activity activity = Robolectric.buildActivity(Activity.class, intent).get(); ChooseLockSettingsHelper helper = getChooseLockSettingsHelper(activity); helper.launchConfirmationActivity(123, "test title", true, 0 /* userId */); diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java index a8372d93d26..f84f9a2240d 100644 --- a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java +++ b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java @@ -43,13 +43,13 @@ public class SearchIndexProviderCodeInspector extends CodeInspector { "SettingsPreferenceFragment should implement Indexable, but these do not:\n"; private static final String NOT_CONTAINING_PROVIDER_OBJECT_ERROR = "Indexable should have public field " - + DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + + DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + " but these are not:\n"; private static final String NOT_SHARING_PREF_CONTROLLERS_BETWEEN_FRAG_AND_PROVIDER = "DashboardFragment should share pref controllers with its SearchIndexProvider, but " + " these are not: \n"; private static final String NOT_IN_INDEXABLE_PROVIDER_REGISTRY = - "Class containing " + DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "Class containing " + DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + " must be added to " + SearchIndexableResources.class.getName() + " but these are not: \n"; private static final String NOT_PROVIDING_VALID_RESOURCE_ERROR = @@ -173,7 +173,7 @@ public class SearchIndexProviderCodeInspector extends CodeInspector { private boolean hasSearchIndexProvider(Class clazz) { try { final Field f = clazz.getField( - DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER); + DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER); return f != null; } catch (NoClassDefFoundError e) { // Cannot find class def, ignore diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java new file mode 100644 index 00000000000..0e4accabe5e --- /dev/null +++ b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java @@ -0,0 +1,269 @@ +/* + * 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.slices; + + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SliceDataTest { + + private final String KEY = "KEY"; + private final String TITLE = "title"; + private final String SUMMARY = "summary"; + private final String SCREEN_TITLE = "screen title"; + private final String FRAGMENT_NAME = "fragment name"; + private final int ICON = 1234; // I declare a thumb war + private final Uri URI = Uri.parse("content://com.android.settings.slices/test"); + private final String PREF_CONTROLLER = "com.android.settings.slices.tester"; + + @Test + public void testBuilder_buildsMatchingObject() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData data = builder.build(); + + assertThat(data.getKey()).isEqualTo(KEY); + assertThat(data.getTitle()).isEqualTo(TITLE); + assertThat(data.getSummary()).isEqualTo(SUMMARY); + assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE); + assertThat(data.getIconResource()).isEqualTo(ICON); + assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); + assertThat(data.getUri()).isEqualTo(URI); + assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noKey_throwsIllegalStateException() { + new SliceData.Builder() + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noTitle_throwsIllegalStateException() { + new SliceData.Builder() + .setKey(KEY) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noFragment_throwsIllegalStateException() { + new SliceData.Builder() + .setKey(KEY) + .setFragmentName(FRAGMENT_NAME) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noUri_throwsIllegalStateException() { + new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setPreferenceControllerClassName(PREF_CONTROLLER) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noPrefController_throwsIllegalStateException() { + new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setUri(URI) + .setFragmentName(FRAGMENT_NAME) + .build(); + } + + @Test + public void testBuilder_noSubtitle_buildsMatchingObject() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData data = builder.build(); + + assertThat(data.getKey()).isEqualTo(KEY); + assertThat(data.getTitle()).isEqualTo(TITLE); + assertThat(data.getSummary()).isNull(); + assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE); + assertThat(data.getIconResource()).isEqualTo(ICON); + assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); + assertThat(data.getUri()).isEqualTo(URI); + assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); + } + + @Test + public void testBuilder_noScreenTitle_buildsMatchingObject() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData data = builder.build(); + + assertThat(data.getKey()).isEqualTo(KEY); + assertThat(data.getTitle()).isEqualTo(TITLE); + assertThat(data.getSummary()).isEqualTo(SUMMARY); + assertThat(data.getScreenTitle()).isNull(); + assertThat(data.getIconResource()).isEqualTo(ICON); + assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); + assertThat(data.getUri()).isEqualTo(URI); + assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); + } + + @Test + public void testBuilder_noIcon_buildsMatchingObject() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData data = builder.build(); + + assertThat(data.getKey()).isEqualTo(KEY); + assertThat(data.getTitle()).isEqualTo(TITLE); + assertThat(data.getSummary()).isEqualTo(SUMMARY); + assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE); + assertThat(data.getIconResource()).isEqualTo(0); + assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); + assertThat(data.getUri()).isEqualTo(URI); + assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); + } + + @Test + public void testEquality_identicalObjects() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData dataOne = builder.build(); + SliceData dataTwo = builder.build(); + + assertThat(dataOne.hashCode()).isEqualTo(dataTwo.hashCode()); + assertThat(dataOne).isEqualTo(dataTwo); + } + + @Test + public void testEquality_matchingKey_EqualObjects() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData dataOne = builder.build(); + + builder.setTitle(TITLE + " diff") + .setSummary(SUMMARY + " diff") + .setScreenTitle(SCREEN_TITLE + " diff") + .setIcon(ICON + 1) + .setFragmentName(FRAGMENT_NAME + " diff") + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER + " diff"); + + SliceData dataTwo = builder.build(); + + assertThat(dataOne.hashCode()).isEqualTo(dataTwo.hashCode()); + assertThat(dataOne).isEqualTo(dataTwo); + } + + @Test + public void testEquality_differentKey_differentObjects() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData dataOne = builder.build(); + + builder.setKey("not key"); + SliceData dataTwo = builder.build(); + + assertThat(dataOne.hashCode()).isNotEqualTo(dataTwo.hashCode()); + assertThat(dataOne).isNotEqualTo(dataTwo); + } +} diff --git a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java new file mode 100644 index 00000000000..a4020dd3d31 --- /dev/null +++ b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java @@ -0,0 +1,87 @@ +package com.android.settings.slices; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import com.android.settings.TestConfig; +import com.android.settings.slices.SlicesDatabaseHelper.IndexColumns; +import com.android.settings.testutils.DatabaseTestUtils; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SlicesDatabaseHelperTest { + + private Context mContext; + private SlicesDatabaseHelper mSlicesDatabaseHelper; + private SQLiteDatabase mDatabase; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mSlicesDatabaseHelper = new SlicesDatabaseHelper(mContext); + mDatabase = mSlicesDatabaseHelper.getWritableDatabase(); + } + + @After + public void cleanUp() { + DatabaseTestUtils.clearDb(mContext); + } + + @Test + public void testDatabaseSchema() { + Cursor cursor = mDatabase.rawQuery("SELECT * FROM slices_index", null); + String[] columnNames = cursor.getColumnNames(); + + String[] expectedNames = new String[]{ + IndexColumns.KEY, + IndexColumns.TITLE, + IndexColumns.SUBTITLE, + IndexColumns.SCREENTITLE, + IndexColumns.ICON_RESOURCE, + IndexColumns.FRAGMENT, + IndexColumns.CONTROLLER + }; + + assertThat(columnNames).isEqualTo(expectedNames); + } + + @Test + public void testUpgrade_dropsOldData() { + ContentValues dummyValues = getDummyRow(); + + mDatabase.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, dummyValues); + Cursor baseline = mDatabase.rawQuery("SELECT * FROM slices_index", null); + assertThat(baseline.getCount()).isEqualTo(1); + + mSlicesDatabaseHelper.onUpgrade(mDatabase, 0, 1); + + Cursor newCursor = mDatabase.rawQuery("SELECT * FROM slices_index", null); + assertThat(newCursor.getCount()).isEqualTo(0); + } + + private ContentValues getDummyRow() { + ContentValues values; + + values = new ContentValues(); + values.put(IndexColumns.KEY, "key"); + values.put(IndexColumns.TITLE, "title"); + values.put(IndexColumns.SUBTITLE, "subtitle"); + values.put(IndexColumns.ICON_RESOURCE, 99); + values.put(IndexColumns.FRAGMENT, "fragmentClassName"); + values.put(IndexColumns.CONTROLLER, "preferenceController"); + + return values; + } +} diff --git a/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java b/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java index 2e8bac0f36c..7c374e9e92d 100644 --- a/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java +++ b/tests/robotests/src/com/android/settings/testutils/SettingsRobolectricTestRunner.java @@ -62,8 +62,8 @@ public class SettingsRobolectricTestRunner extends RobolectricTestRunner { // By adding any resources from libraries we need the AndroidManifest, we can access // them from within the parallel universe's resource loader. - final AndroidManifest manifest = new AndroidManifest(Fs.fileFromPath(manifestPath), - Fs.fileFromPath(resDir), Fs.fileFromPath(assetsDir)) { + return new AndroidManifest(Fs.fileFromPath(manifestPath), Fs.fileFromPath(resDir), + Fs.fileFromPath(assetsDir), "com.android.settings") { @Override public List getIncludedResourcePaths() { List paths = super.getIncludedResourcePaths(); @@ -71,10 +71,6 @@ public class SettingsRobolectricTestRunner extends RobolectricTestRunner { return paths; } }; - - // Set the package name to the renamed one - manifest.setPackageName("com.android.settings"); - return manifest; } public static void getIncludedResourcePaths(String packageName, List paths) { diff --git a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java index f7e956b3205..22d4bf6e04e 100644 --- a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java +++ b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java @@ -37,7 +37,6 @@ import android.os.UserManager; import android.provider.Settings; import android.support.test.InstrumentationRegistry; import android.support.test.filters.LargeTest; -import android.support.test.filters.Suppress; import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.BySelector; @@ -46,7 +45,7 @@ import android.support.test.uiautomator.Direction; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.Until; -import android.widget.ListView; +import android.support.v7.widget.RecyclerView; import android.widget.Switch; import android.widget.TextView; @@ -57,7 +56,6 @@ import org.junit.runner.RunWith; import java.util.List; -@Suppress @RunWith(AndroidJUnit4.class) @LargeTest public class ExternalSourcesSettingsTest { @@ -124,7 +122,8 @@ public class ExternalSourcesSettingsTest { final String testAppLabel = getApplicationLabel(mPackageName); mContext.startActivity(createManageExternalSourcesListIntent()); - final BySelector preferenceListSelector = By.clazz(ListView.class).res("android:id/list"); + final BySelector preferenceListSelector = + By.clazz(RecyclerView.class).res("com.android.settings:id/apps_list"); final UiObject2 preferenceList = mUiDevice.wait(Until.findObject(preferenceListSelector), START_ACTIVITY_TIMEOUT); assertNotNull("App list not shown", preferenceList); diff --git a/tests/unit/src/com/android/settings/applications/PackageUtilTest.java b/tests/unit/src/com/android/settings/applications/PackageUtilTest.java index 1c064ae8897..0e3c402c70e 100644 --- a/tests/unit/src/com/android/settings/applications/PackageUtilTest.java +++ b/tests/unit/src/com/android/settings/applications/PackageUtilTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.when; @RunWith(AndroidJUnit4.class) @SmallTest +@Deprecated public class PackageUtilTest { private static final String ALL_USERS_APP_NAME = "com.google.allusers.app"; private static final String ONE_USER_APP_NAME = "com.google.oneuser.app"; diff --git a/tests/unit/src/com/android/settings/applications/SpecialAppAccessSettingsTest.java b/tests/unit/src/com/android/settings/applications/SpecialAppAccessSettingsTest.java index 4d92cf98a06..4165d06df17 100644 --- a/tests/unit/src/com/android/settings/applications/SpecialAppAccessSettingsTest.java +++ b/tests/unit/src/com/android/settings/applications/SpecialAppAccessSettingsTest.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.support.test.filters.SmallTest; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject; +import android.support.test.uiautomator.UiObjectNotFoundException; import android.support.test.uiautomator.UiScrollable; import android.support.test.uiautomator.UiSelector; import android.test.InstrumentationTestCase; @@ -79,9 +80,15 @@ public class SpecialAppAccessSettingsTest extends InstrumentationTestCase { final String titleSpecialApps = mTargetContext.getResources().getString( R.string.special_access); - final UiScrollable settings = new UiScrollable( - new UiSelector().packageName(mTargetContext.getPackageName()).scrollable(true)); - settings.scrollTextIntoView(titleSpecialApps); + try { + // scollbar may or may not be present, depending on how many recents app are there. If + // the page is scrollable, scroll to the bottom to show the special app access settings. + final UiScrollable settings = new UiScrollable( + new UiSelector().packageName(mTargetContext.getPackageName()).scrollable(true)); + settings.scrollTextIntoView(titleSpecialApps); + } catch (UiObjectNotFoundException e) { + // ignore + } mDevice.findObject(new UiSelector().text(titleSpecialApps)).click(); } diff --git a/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java b/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java index bb12efacf09..2accbf2aa0c 100644 --- a/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java +++ b/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java @@ -36,9 +36,9 @@ import java.util.List; import java.util.Map; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.compat.ArgumentMatcher; public class PreferenceListTest extends AndroidTestCase { private static final String TAG = "PreferenceListTest"; @@ -135,13 +135,10 @@ public class PreferenceListTest extends AndroidTestCase { /* lockdownVpnKey */ null); updater.run(); - final ArgumentMatcher equalsFake = new ArgumentMatcher() { - @Override - public boolean matchesObject(final Object arg) { - if (arg == vpnProfile) return true; - if (arg == null) return false; - return TextUtils.equals(((VpnProfile) arg).key, vpnProfile.key); - } + final ArgumentMatcher equalsFake = arg -> { + if (arg == vpnProfile) return true; + if (arg == null) return false; + return TextUtils.equals(arg.key, vpnProfile.key); }; // The VPN profile should have been used to create a preference and set up at laest once