Snap for 4502278 from 7498b79569 to pi-release
Change-Id: I1232ecc6e88859779b545e82a72b9fb192b857c0
This commit is contained in:
@@ -3312,13 +3312,13 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider android:name=".SettingsSliceProvider"
|
||||
<provider android:name=".slices.SettingsSliceProvider"
|
||||
android:authorities="com.android.settings.slices"
|
||||
android:exported="true">
|
||||
</provider>
|
||||
|
||||
<receiver
|
||||
android:name=".SliceBroadcastReceiver" >
|
||||
android:name=".slices.SliceBroadcastReceiver" >
|
||||
<intent-filter>
|
||||
<action android:name="com.android.settings.slice.action.WIFI_CHANGED"/>
|
||||
</intent-filter>
|
||||
|
||||
@@ -6728,14 +6728,14 @@
|
||||
<!-- Do not disturb: Subtitle for DND behavior indicating no sound will get past DND. [CHAR LIMIT=30] -->
|
||||
<string name="zen_mode_behavior_no_sound">No sound</string>
|
||||
|
||||
<!-- Do not disturb: Subtitle for DND behavior indicating no sound will get past DND due to user and/or API-invoked Total Silence mode. [CHAR LIMIT=40] -->
|
||||
<string name="zen_mode_behavior_total_silence">No sound (Total Silence)</string>
|
||||
<!-- Do not disturb: Subtitle for DND behavior indicating no sound will get past DND. [CHAR LIMIT=40] -->
|
||||
<string name="zen_mode_behavior_total_silence">Total Silence</string>
|
||||
|
||||
<!-- Do not disturb: Used before specifying which sounds can bypass DND (ie: No sound except alarms and reminders). [CHAR LIMIT=40] -->
|
||||
<string name="zen_mode_behavior_no_sound_except">No sound except <xliff:g id="categories" example="alarms, media and system feedback">%1$s</xliff:g></string>
|
||||
|
||||
<!-- Do not disturb: Specifies sounds that can bypass DND in user and/or API-invoked Alarms Only mode. [CHAR LIMIT=100] -->
|
||||
<string name="zen_mode_behavior_alarms_only">No sound except alarms, media and system feedback (Alarms only)</string>
|
||||
<!-- Do not disturb: Specifies alarms and media can bypass DND. [CHAR LIMIT=100] -->
|
||||
<string name="zen_mode_behavior_alarms_only">No sound except alarms and media</string>
|
||||
|
||||
<!-- Do not disturb: Title for the zen mode automation option in Settings. [CHAR LIMIT=40] -->
|
||||
<string name="zen_mode_automation_settings_title">Turn on automatically</string>
|
||||
@@ -7315,8 +7315,11 @@
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Alarms option -->
|
||||
<string name="zen_mode_alarms">Alarms</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Media and system sounds option -->
|
||||
<string name="zen_mode_media_system_other">Media and system feedback</string>
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Media option -->
|
||||
<string name="zen_mode_media_system_other">Media</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Media secondary text explaining sounds include system feedback such as system tapping sounds, haptic feedback, etc. -->
|
||||
<string name="zen_mode_media_system_other_secondary_text">Includes system feedback</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings: Reminders option -->
|
||||
<string name="zen_mode_reminders">Reminders</string>
|
||||
@@ -7336,6 +7339,9 @@
|
||||
<!-- [CHAR LIMIT=200] Zen mode settings: Repeat callers option summary -->
|
||||
<string name="zen_mode_repeat_callers_summary">If the same person calls a second time within a <xliff:g id="minutes">%d</xliff:g> minute period</string>
|
||||
|
||||
<!-- [CHAR LIMIT=50] Zen mode settings dnd beahvior description: when a user has chosen custom sounds to bypass DND -->
|
||||
<string name="zen_mode_behavior_summary_custom">Custom</string>
|
||||
|
||||
<!-- [CHAR LIMIT=20] Zen mode settings: When option -->
|
||||
<string name="zen_mode_when">Automatically turn on</string>
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
<!-- Media -->
|
||||
<SwitchPreference
|
||||
android:key="zen_mode_media"
|
||||
android:title="@string/zen_mode_media_system_other"/>
|
||||
android:title="@string/zen_mode_media_system_other"
|
||||
android:summary="@string/zen_mode_media_system_other_secondary_text"/>
|
||||
|
||||
<!-- Reminders -->
|
||||
<SwitchPreference
|
||||
|
||||
@@ -69,6 +69,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class DeviceAdminAdd extends Activity {
|
||||
static final String TAG = "DeviceAdminAdd";
|
||||
@@ -145,18 +146,14 @@ public class DeviceAdminAdd extends Activity {
|
||||
DevicePolicyManager.EXTRA_DEVICE_ADMIN);
|
||||
if (who == null) {
|
||||
String packageName = getIntent().getStringExtra(EXTRA_DEVICE_ADMIN_PACKAGE_NAME);
|
||||
for (ComponentName component : mDPM.getActiveAdmins()) {
|
||||
if (component.getPackageName().equals(packageName)) {
|
||||
who = component;
|
||||
mUninstalling = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (who == null) {
|
||||
Optional<ComponentName> 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<ComponentName> findAdminWithPackageName(String packageName) {
|
||||
List<ComponentName> 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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<String> 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<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
|
||||
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<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
|
||||
ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities);
|
||||
if (currentDefaultHome == null) {
|
||||
// No preferred default, so permit uninstall only when
|
||||
// there is more than one candidate
|
||||
enabled = (mHomePackages.size() > 1);
|
||||
} else {
|
||||
// There is an explicit default home app -- forbid uninstall of
|
||||
// that one, but permit it for installed-but-inactive ones.
|
||||
enabled = !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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<String> mHomePackages = new HashSet<>();
|
||||
|
||||
private boolean mInitialized;
|
||||
private boolean mShowUninstalled;
|
||||
private LayoutPreference mHeader;
|
||||
@@ -173,10 +141,8 @@ public class AppInfoDashboardFragment extends DashboardFragment
|
||||
|
||||
private List<Callback> 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<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
|
||||
ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities);
|
||||
if (currentDefaultHome == null) {
|
||||
// No preferred default, so permit uninstall only when
|
||||
// there is more than one candidate
|
||||
enabled = (mHomePackages.size() > 1);
|
||||
} else {
|
||||
// There is an explicit default home app -- forbid uninstall of
|
||||
// that one, but permit it for installed-but-inactive ones.
|
||||
enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mAppsControlDisallowedBySystem) {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
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<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
|
||||
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<UserInfo> userInfos = userManager.getUsers(true);
|
||||
int count = 0;
|
||||
int getNumberOfUserWithPackageInstalled(String packageName) {
|
||||
final List<UserInfo> 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<Object, Object, Object> {
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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";
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
}
|
||||
188
src/com/android/settings/slices/SliceData.java
Normal file
188
src/com/android/settings/slices/SliceData.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
122
src/com/android/settings/slices/SlicesDatabaseHelper.java
Normal file
122
src/com/android/settings/slices/SlicesDatabaseHelper.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<String> 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<String> packages = new HashSet<>();
|
||||
packages.add(info.packageName);
|
||||
when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn(
|
||||
packages);
|
||||
|
||||
ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider",
|
||||
mFeatureFactory.applicationFeatureProvider);
|
||||
ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<UserInfo> 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<String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<UserInfo> 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<UserInfo> 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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<ResourcePath> getIncludedResourcePaths() {
|
||||
List<ResourcePath> 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<ResourcePath> paths) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<VpnProfile> equalsFake = new ArgumentMatcher<VpnProfile>() {
|
||||
@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<VpnProfile> 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
|
||||
|
||||
Reference in New Issue
Block a user