Merge "Convert install app detail to dashboard fragment."

This commit is contained in:
TreeHugger Robot
2017-12-01 02:16:26 +00:00
committed by Android (Google) Code Review
28 changed files with 2806 additions and 455 deletions

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:key="installed_app_detail_settings_screen"
app:initialExpandedChildrenCount="6">
<com.android.settings.applications.LayoutPreference
android:key="header_view"
android:layout="@layout/settings_entity_header"
android:selectable="false"
android:order="-10000"/>
<com.android.settings.applications.LayoutPreference
android:key="instant_app_buttons"
android:layout="@layout/instant_app_buttons"
android:selectable="false"
android:order="-9999"/>
<com.android.settings.widget.ActionButtonPreference
android:key="action_buttons"
android:order="-9998" />
<Preference
android:key="notification_settings"
android:title="@string/notifications_label"
android:selectable="true"/>
<com.android.settings.widget.FixedLineSummaryPreference
android:key="permission_settings"
android:title="@string/permissions_label"
android:summary="@string/summary_placeholder"
android:selectable="true"
app:summaryLineCount="1" />
<Preference
android:key="storage_settings"
android:title="@string/storage_settings"
android:summary="@string/summary_placeholder"
android:selectable="true"/>
<com.android.settings.applications.AppDomainsPreference
android:key="instant_app_launch_supported_domain_urls"
android:title="@string/app_launch_supported_domain_urls_title"
android:selectable="true" />
<Preference
android:key="data_settings"
android:title="@string/data_usage_summary_title"
android:summary="@string/summary_placeholder"
android:selectable="true"/>
<Preference
android:key="battery"
android:title="@string/power_usage_summary_title"
android:summary="@string/summary_placeholder"
android:selectable="true"/>
<Preference
android:key="preferred_settings"
android:title="@string/launch_by_default"
android:summary="@string/summary_placeholder"
android:selectable="true"/>
<Preference
android:key="memory"
android:title="@string/memory_settings_title"
android:summary="@string/summary_placeholder"
android:enabled="false"
android:selectable="true"/>
<Preference
android:key="app_version"
android:selectable="false"
android:order="9999"/>
</PreferenceScreen>

View File

@@ -15,6 +15,7 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto">
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/app_notifications_title">
</PreferenceScreen>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* 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
@@ -25,34 +25,22 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.Loader;
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.content.res.Resources;
import android.hardware.usb.IUsbManager;
import android.icu.text.ListFormatter;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkTemplate;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -64,8 +52,6 @@ import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen;
import android.text.BidiFormatter;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -75,13 +61,19 @@ import android.webkit.IWebViewUpdateService;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.DeviceAdminAdd;
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.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.defaultapps.DefaultBrowserPreferenceController;
import com.android.settings.applications.defaultapps.DefaultEmergencyPreferenceController;
import com.android.settings.applications.defaultapps.DefaultHomePreferenceController;
@@ -90,32 +82,17 @@ import com.android.settings.applications.defaultapps.DefaultSmsPreferenceControl
import com.android.settings.applications.instantapps.InstantAppButtonsController;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.datausage.AppDataUsage;
import com.android.settings.datausage.DataUsageList;
import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
import com.android.settings.fuelgauge.BatteryEntry;
import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.ActionButtonPreference;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.wrapper.DevicePolicyManagerWrapper;
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.PermissionsSummaryHelper;
import com.android.settingslib.applications.PermissionsSummaryHelper.PermissionsResultCallback;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.ChartDataLoader;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.wrapper.PackageManagerWrapper;
import java.lang.ref.WeakReference;
@@ -133,11 +110,10 @@ import java.util.Set;
* For non-system applications, there is no option to clear data. Instead there is an option to
* uninstall the application.
*/
public class AppInfoDashboardFragment extends SettingsPreferenceFragment
implements ApplicationsState.Callbacks, OnPreferenceClickListener,
LoaderCallbacks<AppStorageStats> {
public class AppInfoDashboardFragment extends DashboardFragment
implements ApplicationsState.Callbacks {
private static final String LOG_TAG = "AppInfoDashboardFragment";
private static final String TAG = "AppInfoDashboard";
// Menu identifiers
public static final int UNINSTALL_ALL_USERS_MENU = 1;
@@ -147,37 +123,27 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
public static final int REQUEST_UNINSTALL = 0;
private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
private static final int SUB_INFO_FRAGMENT = 1;
public static final int SUB_INFO_FRAGMENT = 1;
private static final int LOADER_CHART_DATA = 2;
private static final int LOADER_STORAGE = 3;
public static final int LOADER_CHART_DATA = 2;
public static final int LOADER_STORAGE = 3;
@VisibleForTesting
static final int LOADER_BATTERY = 4;
public 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 EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
private static final String KEY_HEADER = "header_view";
private static final String KEY_INSTANT_APP_BUTTONS = "instant_app_buttons";
private static final String KEY_ACTION_BUTTONS = "action_buttons";
private static final String KEY_NOTIFICATION = "notification_settings";
private static final String KEY_STORAGE = "storage_settings";
private static final String KEY_PERMISSION = "permission_settings";
private static final String KEY_DATA = "data_settings";
private static final String KEY_LAUNCH = "preferred_settings";
private static final String KEY_BATTERY = "battery";
private static final String KEY_MEMORY = "memory";
private static final String KEY_VERSION = "app_version";
private static final String KEY_INSTANT_APP_SUPPORTED_LINKS =
"instant_app_launch_supported_domain_urls";
// The following copied from AppInfoBase
public static final String ARG_PACKAGE_NAME = "package";
public static final String ARG_PACKAGE_UID = "uid";
protected static final String TAG = AppInfoBase.class.getSimpleName();
protected static final boolean localLOGV = false;
private EnforcedAdmin mAppsControlDisallowedAdmin;
@@ -191,7 +157,6 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
private int mUserId;
private String mPackageName;
private IUsbManager mUsbManager;
private DevicePolicyManagerWrapper mDpm;
private UserManager mUserManager;
private PackageManager mPm;
@@ -206,67 +171,23 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
private boolean mShowUninstalled;
private LayoutPreference mHeader;
private boolean mUpdatedSysApp = false;
private Preference mNotificationPreference;
private Preference mStoragePreference;
private Preference mPermissionsPreference;
private Preference mLaunchPreference;
private Preference mDataPreference;
private Preference mMemoryPreference;
private Preference mVersionPreference;
private AppDomainsPreference mInstantAppDomainsPreference;
private boolean mDisableAfterUninstall;
// Used for updating notification preference.
private final NotificationBackend mBackend = new NotificationBackend();
private ChartData mChartData;
private INetworkStatsSession mStatsSession;
private List<Callback> mCallbacks = new ArrayList<>();
@VisibleForTesting
ActionButtonPreference mActionButtons;
@VisibleForTesting
Preference mBatteryPreference;
@VisibleForTesting
BatterySipper mSipper;
@VisibleForTesting
BatteryStatsHelper mBatteryHelper;
@VisibleForTesting
BatteryUtils mBatteryUtils;
protected ProcStatsData mStatsManager;
protected ProcStatsPackageEntry mStats;
private InstantAppButtonsController mInstantAppButtonsController;
private AppStorageStats mLastResult;
private String mBatteryPercent;
@VisibleForTesting
final LoaderCallbacks<BatteryStatsHelper> mBatteryCallbacks =
new LoaderCallbacks<BatteryStatsHelper>() {
@Override
public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) {
return new BatteryStatsHelperLoader(getContext());
/**
* Callback to invoke when app info has been changed.
*/
public interface Callback {
void refreshUi();
}
@Override
public void onLoadFinished(Loader<BatteryStatsHelper> loader,
BatteryStatsHelper batteryHelper) {
mBatteryHelper = batteryHelper;
if (mPackageInfo != null) {
mSipper = findTargetSipper(batteryHelper, mPackageInfo.applicationInfo.uid);
if (getActivity() != null) {
updateBattery();
}
}
}
@Override
public void onLoaderReset(Loader<BatteryStatsHelper> loader) {
}
};
@VisibleForTesting
boolean handleDisableable() {
boolean disableable = false;
@@ -400,14 +321,10 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
final Activity activity = getActivity();
mApplicationFeatureProvider = FeatureFactory.getFactory(activity)
.getApplicationFeatureProvider(activity);
mState = ApplicationsState.getInstance(activity.getApplication());
mSession = mState.newSession(this, getLifecycle());
mDpm = new DevicePolicyManagerWrapper(
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE));
mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
mPm = activity.getPackageManager();
IBinder b = ServiceManager.getService(Context.USB_SERVICE);
mUsbManager = IUsbManager.Stub.asInterface(b);
retrieveAppEntry();
startListeningToPackageRemove();
@@ -417,21 +334,8 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
}
setHasOptionsMenu(true);
addPreferencesFromResource(R.xml.installed_app_details);
addDynamicPrefs();
if (Utils.isBandwidthControlEnabled()) {
INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
try {
mStatsSession = statsService.openSession();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
} else {
removePreference(KEY_DATA);
}
mBatteryUtils = BatteryUtils.getInstance(getContext());
}
@Override
@@ -454,31 +358,54 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
if (mFinishing) {
return;
}
AppItem app = new AppItem(mAppEntry.info.uid);
app.addUid(mAppEntry.info.uid);
if (mStatsSession != null) {
LoaderManager loaderManager = getLoaderManager();
loaderManager.restartLoader(LOADER_CHART_DATA,
ChartDataLoader.buildArgs(getTemplate(getContext()), app),
mDataCallbacks);
loaderManager.restartLoader(LOADER_STORAGE, Bundle.EMPTY, this);
}
restartBatteryStatsLoader();
if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext())) {
new MemoryUpdater().execute();
}
updateDynamicPrefs();
}
@VisibleForTesting
public void restartBatteryStatsLoader() {
getLoaderManager().restartLoader(LOADER_BATTERY, Bundle.EMPTY, mBatteryCallbacks);
@Override
protected int getPreferenceScreenResId() {
return R.xml.app_info_settings;
}
@Override
public void onPause() {
getLoaderManager().destroyLoader(LOADER_CHART_DATA);
super.onPause();
protected String getLogTag() {
return TAG;
}
@Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
final String packageName = getPackageName();
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final Lifecycle lifecycle = getLifecycle();
// The following are controllers for preferences that needs to refresh the preference state
// when app state changes.
controllers.add(new AppStoragePreferenceController(context, this, lifecycle));
controllers.add(new AppDataUsagePreferenceController(context, this, lifecycle));
controllers.add(new AppNotificationPreferenceController(context, this));
controllers.add(new AppOpenByDefaultPreferenceController(context, this));
controllers.add(new AppPermissionPreferenceController(context, this, packageName));
controllers.add(new AppVersionPreferenceController(context, this));
for (AbstractPreferenceController controller : controllers) {
mCallbacks.add((Callback) controller);
}
// The following are controllers for preferences that don't need to refresh the preference
// state when app state changes.
controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle));
controllers.add(new AppMemoryPreferenceController(context, this, lifecycle));
return controllers;
}
public ApplicationsState.AppEntry getAppEntry() {
if (mAppEntry == null) {
retrieveAppEntry();
}
return mAppEntry;
}
public PackageInfo getPackageInfo() {
return mPackageInfo;
}
public void onActivityCreated(Bundle savedInstanceState) {
@@ -501,43 +428,14 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
.styleActionBar(activity)
.bindHeaderButtons();
mNotificationPreference = findPreference(KEY_NOTIFICATION);
mNotificationPreference.setOnPreferenceClickListener(this);
mStoragePreference = findPreference(KEY_STORAGE);
mStoragePreference.setOnPreferenceClickListener(this);
mPermissionsPreference = findPreference(KEY_PERMISSION);
mPermissionsPreference.setOnPreferenceClickListener(this);
mDataPreference = findPreference(KEY_DATA);
if (mDataPreference != null) {
mDataPreference.setOnPreferenceClickListener(this);
}
mBatteryPreference = findPreference(KEY_BATTERY);
mBatteryPreference.setEnabled(false);
mBatteryPreference.setOnPreferenceClickListener(this);
mMemoryPreference = findPreference(KEY_MEMORY);
mMemoryPreference.setOnPreferenceClickListener(this);
mMemoryPreference.setVisible(
DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext()));
mVersionPreference = findPreference(KEY_VERSION);
mInstantAppDomainsPreference =
(AppDomainsPreference) findPreference(KEY_INSTANT_APP_SUPPORTED_LINKS);
mLaunchPreference = findPreference(KEY_LAUNCH);
if (mAppEntry != null && mAppEntry.info != null) {
if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0 ||
!mAppEntry.info.enabled) {
mLaunchPreference.setEnabled(false);
} else {
mLaunchPreference.setOnPreferenceClickListener(this);
}
} else {
mLaunchPreference.setEnabled(false);
}
}
@Override
public void onPackageSizeChanged(String packageName) {
if (!TextUtils.equals(packageName, mPackageName)) {
Log.d(LOG_TAG, "Package change irrelevant, skipping");
Log.d(TAG, "Package change irrelevant, skipping");
return;
}
refreshUi();
@@ -553,7 +451,7 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
boolean ensurePackageInfoAvailable(Activity activity) {
if (mPackageInfo == null) {
mFinishing = true;
Log.w(LOG_TAG, "Package info not available. Is this package already uninstalled?");
Log.w(TAG, "Package info not available. Is this package already uninstalled?");
activity.finishAndRemoveTask();
return false;
}
@@ -622,23 +520,6 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
}
}
@Override
public Loader<AppStorageStats> onCreateLoader(int id, Bundle args) {
Context context = getContext();
return new FetchPackageStorageAsyncLoader(
context, new StorageStatsSource(context), mAppEntry.info, UserHandle.of(mUserId));
}
@Override
public void onLoadFinished(Loader<AppStorageStats> loader, AppStorageStats result) {
mLastResult = result;
refreshUi();
}
@Override
public void onLoaderReset(Loader<AppStorageStats> loader) {
}
/**
* Utility method to hide and show specific preferences based on whether the app being displayed
* is an Instant App or an installed app.
@@ -652,7 +533,6 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
mInstantAppDomainsPreference.setTitles(handledDomains);
// Dummy values, unused in the implementation
mInstantAppDomainsPreference.setValues(new int[handledDomains.length]);
getPreferenceScreen().removePreference(mLaunchPreference);
} else {
getPreferenceScreen().removePreference(mInstantAppDomainsPreference);
}
@@ -672,8 +552,6 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
.setSummary(summary)
.setIsInstantApp(isInstantApp)
.done(activity, false /* rebindActions */);
mVersionPreference.setSummary(getString(R.string.version_text,
BidiFormatter.getInstance().unicodeWrap(pkgInfo.versionName)));
}
@VisibleForTesting
@@ -700,19 +578,6 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
return showIt;
}
@VisibleForTesting
BatterySipper findTargetSipper(BatteryStatsHelper batteryHelper, int uid) {
List<BatterySipper> usageList = batteryHelper.getUsageList();
for (int i = 0, size = usageList.size(); i < size; i++) {
BatterySipper sipper = usageList.get(i);
if (sipper.getUid() == uid) {
return sipper;
}
}
return null;
}
private boolean signaturesMatch(String pkg1, String pkg2) {
if (pkg1 != null && pkg2 != null) {
try {
@@ -764,17 +629,8 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
// Update the preference summaries.
Activity context = getActivity();
boolean isExternal = ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
mStoragePreference.setSummary(getStorageSummary(context, mLastResult, isExternal));
PermissionsSummaryHelper.getPermissionSummary(getContext(),
mPackageName, mPermissionCallback);
mLaunchPreference.setSummary(AppUtils.getLaunchByDefaultSummary(mAppEntry, mUsbManager,
mPm, context));
mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context,
mBackend));
if (mDataPreference != null) {
mDataPreference.setSummary(getDataSummary());
for (Callback callback : mCallbacks) {
callback.refreshUi();
}
if (!mInitialized) {
@@ -803,63 +659,6 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
return true;
}
@VisibleForTesting
void updateBattery() {
mBatteryPreference.setEnabled(true);
if (isBatteryStatsAvailable()) {
final int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount(
BatteryStats.STATS_SINCE_CHARGED);
final List<BatterySipper> usageList = new ArrayList<>(mBatteryHelper.getUsageList());
final double hiddenAmount = mBatteryUtils.removeHiddenBatterySippers(usageList);
final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent(
mSipper.totalPowerMah, mBatteryHelper.getTotalPower(), hiddenAmount,
dischargeAmount);
mBatteryPercent = Utils.formatPercentage(percentOfMax);
mBatteryPreference.setSummary(getString(R.string.battery_summary, mBatteryPercent));
} else {
mBatteryPreference.setSummary(getString(R.string.no_battery_summary));
}
}
private CharSequence getDataSummary() {
if (mChartData != null) {
long totalBytes = mChartData.detail.getTotalBytes();
if (totalBytes == 0) {
return getString(R.string.no_data_usage);
}
Context context = getActivity();
return getString(R.string.data_summary_format,
Formatter.formatFileSize(context, totalBytes),
DateUtils.formatDateTime(context, mChartData.detail.getStart(),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH));
}
return getString(R.string.computing_size);
}
@VisibleForTesting
static CharSequence getStorageSummary(
Context context, AppStorageStats stats, boolean isExternal) {
if (stats == null) {
return context.getText(R.string.computing_size);
} else {
CharSequence storageType = context.getString(isExternal
? R.string.storage_type_external
: R.string.storage_type_internal);
return context.getString(R.string.storage_summary_format,
getSize(context, stats), storageType.toString().toLowerCase());
}
}
@VisibleForTesting
boolean isBatteryStatsAvailable() {
return mBatteryHelper != null && mSipper != null;
}
private static CharSequence getSize(Context context, AppStorageStats stats) {
return Formatter.formatFileSize(context, stats.getTotalBytes());
}
protected AlertDialog createDialog(int id, int errorCode) {
switch (id) {
case DLG_DISABLE:
@@ -928,7 +727,7 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName);
ActivityManager am = (ActivityManager) getActivity().getSystemService(
Context.ACTIVITY_SERVICE);
Log.d(LOG_TAG, "Stopping package " + pkgName);
Log.d(TAG, "Stopping package " + pkgName);
am.forceStopPackage(pkgName);
int userId = UserHandle.getUserId(mAppEntry.info.uid);
mState.invalidatePackage(pkgName, userId);
@@ -950,7 +749,7 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
void checkForceStop() {
if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
// User can't force stop device admin.
Log.w(LOG_TAG, "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);
@@ -958,7 +757,7 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
} 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(LOG_TAG, "App is not explicitly stopped");
Log.w(TAG, "App is not explicitly stopped");
updateForceStopButton(true);
} else {
Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
@@ -966,25 +765,13 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
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(LOG_TAG, "Sending broadcast to query restart status for "
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);
}
}
private void startManagePermissionsActivity() {
// start new activity to manage app permissions
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName);
intent.putExtra(EXTRA_HIDE_INFO_BUTTON, true);
try {
getActivity().startActivityForResult(intent, SUB_INFO_FRAGMENT);
} catch (ActivityNotFoundException e) {
Log.w(LOG_TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS");
}
}
private void startAppInfoFragment(Class<?> fragment, int title) {
startAppInfoFragment(fragment, title, this, mAppEntry);
}
@@ -1070,38 +857,6 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
|| (mUserManager.isSplitSystemUser() && userCount == 2);
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference == mStoragePreference) {
startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
} else if (preference == mNotificationPreference) {
startAppInfoFragment(AppNotificationSettings.class, R.string.app_notifications_title);
} else if (preference == mPermissionsPreference) {
startManagePermissionsActivity();
} else if (preference == mLaunchPreference) {
startAppInfoFragment(AppLaunchSettings.class, R.string.launch_by_default);
} else if (preference == mMemoryPreference) {
ProcessStatsBase.launchMemoryDetail((SettingsActivity) getActivity(),
mStatsManager.getMemInfo(), mStats, false);
} else if (preference == mDataPreference) {
startAppInfoFragment(AppDataUsage.class, R.string.app_data_usage);
} else if (preference == mBatteryPreference) {
if (isBatteryStatsAvailable()) {
BatteryEntry entry = new BatteryEntry(getContext(), null, mUserManager, mSipper);
entry.defaultPackageName = mPackageName;
AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
this, mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry,
mBatteryPercent, null /* mAnomalies */);
} else {
AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
this, mPackageName);
}
} else {
return false;
}
return true;
}
private void addDynamicPrefs() {
if (UserManager.get(getContext()).isManagedProfile()) {
return;
@@ -1330,78 +1085,11 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
}
}
public static NetworkTemplate getTemplate(Context context) {
if (DataUsageList.hasReadyMobileRadio(context)) {
return NetworkTemplate.buildTemplateMobileWildcard();
}
if (DataUsageUtils.hasWifiRadio(context)) {
return NetworkTemplate.buildTemplateWifiWildcard();
}
return NetworkTemplate.buildTemplateEthernet();
}
public static CharSequence getNotificationSummary(AppEntry appEntry, Context context,
NotificationBackend backend) {
AppRow appRow = backend.loadAppRow(context, context.getPackageManager(), appEntry.info);
return getNotificationSummary(appRow, context);
}
public static CharSequence getNotificationSummary(AppRow appRow, Context context) {
// TODO: implement summary when it is known what it should say
return "";
}
private void onPackageRemoved() {
getActivity().finishActivity(SUB_INFO_FRAGMENT);
getActivity().finishAndRemoveTask();
}
private class MemoryUpdater extends AsyncTask<Void, Void, ProcStatsPackageEntry> {
@Override
protected ProcStatsPackageEntry doInBackground(Void... params) {
if (getActivity() == null) {
return null;
}
if (mPackageInfo == null) {
return null;
}
if (mStatsManager == null) {
mStatsManager = new ProcStatsData(getActivity(), false);
mStatsManager.setDuration(ProcessStatsBase.sDurations[0]);
}
mStatsManager.refreshStats(true);
for (ProcStatsPackageEntry pkgEntry : mStatsManager.getEntries()) {
for (ProcStatsEntry entry : pkgEntry.mEntries) {
if (entry.mUid == mPackageInfo.applicationInfo.uid) {
pkgEntry.updateMetrics();
return pkgEntry;
}
}
}
return null;
}
@Override
protected void onPostExecute(ProcStatsPackageEntry entry) {
if (getActivity() == null) {
return;
}
if (entry != null) {
mStats = entry;
mMemoryPreference.setEnabled(true);
double amount = Math.max(entry.mRunWeight, entry.mBgWeight)
* mStatsManager.getMemInfo().weightToRam;
mMemoryPreference.setSummary(getString(R.string.memory_use_summary,
Formatter.formatShortFileSize(getContext(), (long) amount)));
} else {
mMemoryPreference.setEnabled(false);
mMemoryPreference.setSummary(getString(R.string.no_memory_use_summary));
}
}
}
/**
* Elicit this class for testing. Test cannot be done in robolectric because it
* invokes the new API.
@@ -1453,76 +1141,22 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
}
}
private final LoaderCallbacks<ChartData> mDataCallbacks = new LoaderCallbacks<ChartData>() {
@Override
public Loader<ChartData> onCreateLoader(int id, Bundle args) {
return new ChartDataLoader(getActivity(), mStatsSession, args);
}
@Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
mChartData = data;
mDataPreference.setSummary(getDataSummary());
}
@Override
public void onLoaderReset(Loader<ChartData> loader) {
// Leave last result.
}
};
private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final boolean enabled = getResultCode() != Activity.RESULT_CANCELED;
Log.d(LOG_TAG, "Got broadcast response: Restart status for "
Log.d(TAG, "Got broadcast response: Restart status for "
+ mAppEntry.info.packageName + " " + enabled);
updateForceStopButton(enabled);
}
};
private final PermissionsResultCallback mPermissionCallback
= new PermissionsResultCallback() {
@Override
public void onPermissionSummaryResult(int standardGrantedPermissionCount,
int requestedPermissionCount, int additionalGrantedPermissionCount,
List<CharSequence> grantedGroupLabels) {
if (getActivity() == null) {
return;
private String getPackageName() {
if (mPackageName != null) {
return mPackageName;
}
final Resources res = getResources();
CharSequence summary = null;
if (requestedPermissionCount == 0) {
summary = res.getString(
R.string.runtime_permissions_summary_no_permissions_requested);
mPermissionsPreference.setOnPreferenceClickListener(null);
mPermissionsPreference.setEnabled(false);
} else {
final ArrayList<CharSequence> list = new ArrayList<>(grantedGroupLabels);
if (additionalGrantedPermissionCount > 0) {
// N additional permissions.
list.add(res.getQuantityString(
R.plurals.runtime_permissions_additional_count,
additionalGrantedPermissionCount, additionalGrantedPermissionCount));
}
if (list.size() == 0) {
summary = res.getString(
R.string.runtime_permissions_summary_no_permissions_granted);
} else {
summary = ListFormatter.getInstance().format(list);
}
mPermissionsPreference.setOnPreferenceClickListener(AppInfoDashboardFragment.this);
mPermissionsPreference.setEnabled(true);
}
mPermissionsPreference.setSummary(summary);
}
};
private String retrieveAppEntry() {
final Bundle args = getArguments();
mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
String mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
if (mPackageName == null) {
Intent intent = (args == null) ?
getActivity().getIntent() : (Intent) args.getParcelable("intent");
@@ -1530,12 +1164,25 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
mPackageName = intent.getData().getSchemeSpecificPart();
}
}
return mPackageName;
}
private void retrieveAppEntry() {
final Activity activity = getActivity();
if (activity == null) {
return;
}
if (mState == null) {
mState = ApplicationsState.getInstance(activity.getApplication());
mSession = mState.newSession(this, getLifecycle());
}
mUserId = UserHandle.myUserId();
mAppEntry = mState.getEntry(mPackageName, mUserId);
mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId());
if (mAppEntry != null) {
// Get application info again to refresh changed properties of application
try {
mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
mPackageInfo = activity.getPackageManager().getPackageInfo(
mAppEntry.info.packageName,
PackageManager.MATCH_DISABLED_COMPONENTS |
PackageManager.MATCH_ANY_USER |
PackageManager.GET_SIGNATURES |
@@ -1547,8 +1194,6 @@ public class AppInfoDashboardFragment extends SettingsPreferenceFragment
Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
mPackageInfo = null;
}
return mPackageName;
}
private void setIntentAndFinish(boolean finish, boolean appChanged) {

View File

@@ -372,6 +372,10 @@ public class ProcStatsData {
double totalScale;
long memTotalTime;
public double getWeightToRam() {
return weightToRam;
}
private MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem,
long memTotalTime) {
this.memTotalTime = memTotalTime;

View File

@@ -297,6 +297,10 @@ public final class ProcStatsEntry implements Parcelable {
}
}
public int getUid() {
return mUid;
}
public static final Parcelable.Creator<ProcStatsEntry> CREATOR
= new Parcelable.Creator<ProcStatsEntry>() {
public ProcStatsEntry createFromParcel(Parcel in) {

View File

@@ -175,4 +175,17 @@ public class ProcStatsPackageEntry implements Parcelable {
Utils.formatPercentage((int) (amount * 100)));
}
}
public double getRunWeight() {
return mRunWeight;
}
public double getBgWeight() {
return mBgWeight;
}
public ArrayList<ProcStatsEntry> getEntries() {
return mEntries;
}
}

View File

@@ -0,0 +1,186 @@
/*
* 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.LoaderManager;
import android.app.slice.Slice;
import android.content.Context;
import android.content.Loader;
import android.content.pm.PackageInfo;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import com.android.internal.os.BatterySipper;
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;
import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.ArrayList;
import java.util.List;
public class AppBatteryPreferenceController extends BasePreferenceController
implements LoaderManager.LoaderCallbacks<BatteryStatsHelper>,
LifecycleObserver, OnResume, OnPause {
private static final String KEY_BATTERY = "battery";
@VisibleForTesting
BatterySipper mSipper;
@VisibleForTesting
BatteryStatsHelper mBatteryHelper;
@VisibleForTesting
BatteryUtils mBatteryUtils;
private Preference mPreference;
private final AppInfoDashboardFragment mParent;
private String mBatteryPercent;
private final String mPackageName;
public AppBatteryPreferenceController(Context context, AppInfoDashboardFragment parent,
String packageName, Lifecycle lifecycle) {
super(context, KEY_BATTERY);
mParent = parent;
mBatteryUtils = BatteryUtils.getInstance(mContext);
mPackageName = packageName;
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public Slice getSettingSlice() {
return null;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
mPreference.setEnabled(false);
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!KEY_BATTERY.equals(preference.getKey())) {
return false;
}
if (isBatteryStatsAvailable()) {
final UserManager userManager =
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
final BatteryEntry entry = new BatteryEntry(mContext, null, userManager, mSipper);
entry.defaultPackageName = mPackageName;
AdvancedPowerUsageDetail.startBatteryDetailPage(
(SettingsActivity) mParent.getActivity(), mParent, mBatteryHelper,
BatteryStats.STATS_SINCE_CHARGED, entry, mBatteryPercent,
null /* mAnomalies */);
} else {
AdvancedPowerUsageDetail.startBatteryDetailPage(
(SettingsActivity) mParent.getActivity(), mParent, mPackageName);
}
return true;
}
@Override
public void onResume() {
mParent.getLoaderManager().restartLoader(
mParent.LOADER_BATTERY, Bundle.EMPTY, this);
}
@Override
public void onPause() {
mParent.getLoaderManager().destroyLoader(mParent.LOADER_BATTERY);
}
@Override
public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) {
return new BatteryStatsHelperLoader(mContext);
}
@Override
public void onLoadFinished(Loader<BatteryStatsHelper> loader,
BatteryStatsHelper batteryHelper) {
mBatteryHelper = batteryHelper;
final PackageInfo packageInfo = mParent.getPackageInfo();
if (packageInfo != null) {
mSipper = findTargetSipper(batteryHelper, packageInfo.applicationInfo.uid);
if (mParent.getActivity() != null) {
updateBattery();
}
}
}
@Override
public void onLoaderReset(Loader<BatteryStatsHelper> loader) {
}
@VisibleForTesting
void updateBattery() {
mPreference.setEnabled(true);
if (isBatteryStatsAvailable()) {
final int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount(
BatteryStats.STATS_SINCE_CHARGED);
final List<BatterySipper> usageList = new ArrayList<>(mBatteryHelper.getUsageList());
final double hiddenAmount = mBatteryUtils.removeHiddenBatterySippers(usageList);
final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent(
mSipper.totalPowerMah, mBatteryHelper.getTotalPower(), hiddenAmount,
dischargeAmount);
mBatteryPercent = Utils.formatPercentage(percentOfMax);
mPreference.setSummary(mContext.getString(R.string.battery_summary, mBatteryPercent));
} else {
mPreference.setSummary(mContext.getString(R.string.no_battery_summary));
}
}
@VisibleForTesting
boolean isBatteryStatsAvailable() {
return mBatteryHelper != null && mSipper != null;
}
@VisibleForTesting
BatterySipper findTargetSipper(BatteryStatsHelper batteryHelper, int uid) {
final List<BatterySipper> usageList = batteryHelper.getUsageList();
for (int i = 0, size = usageList.size(); i < size; i++) {
final BatterySipper sipper = usageList.get(i);
if (sipper.getUid() == uid) {
return sipper;
}
}
return null;
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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.LoaderManager;
import android.content.Context;
import android.content.Loader;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.text.format.DateUtils;
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;
import com.android.settingslib.AppItem;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.ChartDataLoader;
public class AppDataUsagePreferenceController extends AppInfoPreferenceControllerBase
implements LoaderManager.LoaderCallbacks<ChartData>, LifecycleObserver, OnResume, OnPause {
private static final String KEY_DATA = "data_settings";
private ChartData mChartData;
private INetworkStatsSession mStatsSession;
public AppDataUsagePreferenceController(Context context, AppInfoDashboardFragment parent,
Lifecycle lifecycle) {
super(context, parent, KEY_DATA);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public int getAvailabilityStatus() {
return isBandwidthControlEnabled() ? AVAILABLE : DISABLED_UNSUPPORTED;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (isAvailable()) {
final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
try {
mStatsSession = statsService.openSession();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void updateState(Preference preference) {
preference.setSummary(getDataSummary());
}
@Override
public void onResume() {
if (mStatsSession != null) {
final int uid = mParent.getAppEntry().info.uid;
final AppItem app = new AppItem(uid);
app.addUid(uid);
mParent.getLoaderManager().restartLoader(mParent.LOADER_CHART_DATA,
ChartDataLoader.buildArgs(getTemplate(mContext), app),
this);
}
}
@Override
public void onPause() {
mParent.getLoaderManager().destroyLoader(mParent.LOADER_CHART_DATA);
}
@Override
public Loader<ChartData> onCreateLoader(int id, Bundle args) {
return new ChartDataLoader(mContext, mStatsSession, args);
}
@Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
mChartData = data;
updateState(mPreference);
}
@Override
public void onLoaderReset(Loader<ChartData> loader) {
// Leave last result.
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return AppDataUsage.class;
}
private CharSequence getDataSummary() {
if (mChartData != null) {
final long totalBytes = mChartData.detail.getTotalBytes();
if (totalBytes == 0) {
return mContext.getString(R.string.no_data_usage);
}
return mContext.getString(R.string.data_summary_format,
Formatter.formatFileSize(mContext, totalBytes),
DateUtils.formatDateTime(mContext, mChartData.detail.getStart(),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH));
}
return mContext.getString(R.string.computing_size);
}
private static NetworkTemplate getTemplate(Context context) {
if (DataUsageList.hasReadyMobileRadio(context)) {
return NetworkTemplate.buildTemplateMobileWildcard();
}
if (DataUsageUtils.hasWifiRadio(context)) {
return NetworkTemplate.buildTemplateWifiWildcard();
}
return NetworkTemplate.buildTemplateEthernet();
}
@VisibleForTesting
boolean isBandwidthControlEnabled() {
return Utils.isBandwidthControlEnabled();
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.slice.Slice;
import android.content.Context;
import android.support.v7.preference.Preference;
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;
/*
* Abstract base controller for the app detail preferences that refresh the state when the app state
* changes and launch a specific detail fragment when the preference is clicked.
*/
public abstract class AppInfoPreferenceControllerBase extends BasePreferenceController
implements AppInfoDashboardFragment.Callback {
protected final AppInfoDashboardFragment mParent;
private final Class<? extends SettingsPreferenceFragment> mDetailFragmenClass;
protected Preference mPreference;
public AppInfoPreferenceControllerBase(Context context, AppInfoDashboardFragment parent,
String preferenceKey) {
super(context, preferenceKey);
mParent = parent;
mDetailFragmenClass = getDetailFragmentClass();
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public Slice getSettingSlice() {
return null;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (TextUtils.equals(preference.getKey(), mPreferenceKey) && mDetailFragmenClass != null) {
AppInfoDashboardFragment.startAppInfoFragment(
mDetailFragmenClass, -1, mParent, mParent.getAppEntry());
return true;
}
return false;
}
@Override
public void refreshUi() {
updateState(mPreference);
}
/**
* Gets the fragment class to be launched when the preference is clicked.
* @return the fragment to launch
*/
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return null;
}
}

View File

@@ -0,0 +1,141 @@
/*
* 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.slice.Slice;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.AsyncTask;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
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;
import com.android.settings.applications.ProcessStatsBase;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
public class AppMemoryPreferenceController extends BasePreferenceController
implements LifecycleObserver, OnResume {
private static final String KEY_MEMORY = "memory";
private Preference mPreference;
private final AppInfoDashboardFragment mParent;
private ProcStatsData mStatsManager;
private ProcStatsPackageEntry mStats;
private class MemoryUpdater extends AsyncTask<Void, Void, ProcStatsPackageEntry> {
@Override
protected ProcStatsPackageEntry doInBackground(Void... params) {
final Activity activity = mParent.getActivity();
if (activity == null) {
return null;
}
PackageInfo packageInfo = mParent.getPackageInfo();
if (packageInfo == null) {
return null;
}
if (mStatsManager == null) {
mStatsManager = new ProcStatsData(activity, false);
mStatsManager.setDuration(ProcessStatsBase.sDurations[0]);
}
mStatsManager.refreshStats(true);
for (ProcStatsPackageEntry pkgEntry : mStatsManager.getEntries()) {
for (ProcStatsEntry entry : pkgEntry.getEntries()) {
if (entry.getUid() == packageInfo.applicationInfo.uid) {
pkgEntry.updateMetrics();
return pkgEntry;
}
}
}
return null;
}
@Override
protected void onPostExecute(ProcStatsPackageEntry entry) {
if (mParent.getActivity() == null) {
return;
}
if (entry != null) {
mStats = entry;
mPreference.setEnabled(true);
double amount = Math.max(entry.getRunWeight(), entry.getBgWeight())
* mStatsManager.getMemInfo().getWeightToRam();
mPreference.setSummary(mContext.getString(R.string.memory_use_summary,
Formatter.formatShortFileSize(mContext, (long) amount)));
} else {
mPreference.setEnabled(false);
mPreference.setSummary(mContext.getString(R.string.no_memory_use_summary));
}
}
}
public AppMemoryPreferenceController(Context context, AppInfoDashboardFragment parent,
Lifecycle lifecycle) {
super(context, KEY_MEMORY);
mParent = parent;
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public int getAvailabilityStatus() {
return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
@Override
public Slice getSettingSlice() {
return null;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (KEY_MEMORY.equals(preference.getKey())) {
ProcessStatsBase.launchMemoryDetail((SettingsActivity) mParent.getActivity(),
mStatsManager.getMemInfo(), mStats, false);
return true;
}
return false;
}
@Override
public void onResume() {
if (isAvailable()) {
new MemoryUpdater().execute();
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.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;
public class AppNotificationPreferenceController extends AppInfoPreferenceControllerBase {
private static final String KEY_NOTIFICATION = "notification_settings";
// Used for updating notification preference.
private final NotificationBackend mBackend = new NotificationBackend();
public AppNotificationPreferenceController(Context context, AppInfoDashboardFragment parent) {
super(context, parent, KEY_NOTIFICATION);
}
@Override
public void updateState(Preference preference) {
preference.setSummary(getNotificationSummary(mParent.getAppEntry(), mContext, mBackend));
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return AppNotificationSettings.class;
}
private CharSequence getNotificationSummary(ApplicationsState.AppEntry appEntry,
Context context, NotificationBackend backend) {
NotificationBackend.AppRow appRow =
backend.loadAppRow(context, context.getPackageManager(), appEntry.info);
return getNotificationSummary(appRow, context);
}
public static CharSequence getNotificationSummary(NotificationBackend.AppRow appRow,
Context context) {
// TODO: implement summary when it is known what it should say
return "";
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
import android.os.ServiceManager;
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;
public class AppOpenByDefaultPreferenceController extends AppInfoPreferenceControllerBase {
private static final String KEY_LAUNCH = "preferred_settings";
private IUsbManager mUsbManager;
private PackageManager mPackageManager;
public AppOpenByDefaultPreferenceController(Context context, AppInfoDashboardFragment parent) {
super(context, parent, KEY_LAUNCH);
mUsbManager = IUsbManager.Stub.asInterface(ServiceManager.getService(Context.USB_SERVICE));
mPackageManager = context.getPackageManager();
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final ApplicationsState.AppEntry appEntry = mParent.getAppEntry();
if (appEntry == null || appEntry.info == null) {
mPreference.setEnabled(false);
} else if ((appEntry.info.flags& ApplicationInfo.FLAG_INSTALLED) == 0
|| !appEntry.info.enabled) {
mPreference.setEnabled(false);
}
}
@Override
public void updateState(Preference preference) {
final PackageInfo packageInfo = mParent.getPackageInfo();
if (packageInfo != null && !AppUtils.isInstant(packageInfo.applicationInfo)) {
preference.setVisible(true);
preference.setSummary(AppUtils.getLaunchByDefaultSummary(mParent.getAppEntry(),
mUsbManager, mPackageManager, mContext));
} else {
preference.setVisible(false);
}
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return AppLaunchSettings.class;
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.icu.text.ListFormatter;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settingslib.applications.PermissionsSummaryHelper;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
public class AppPermissionPreferenceController extends AppInfoPreferenceControllerBase {
private static final String TAG = "PermissionPrefControl";
private static final String KEY_PERMISSION = "permission_settings";
private static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
private final String mPackageName;
@VisibleForTesting
final PermissionsSummaryHelper.PermissionsResultCallback mPermissionCallback
= new PermissionsSummaryHelper.PermissionsResultCallback() {
@Override
public void onPermissionSummaryResult(int standardGrantedPermissionCount,
int requestedPermissionCount, int additionalGrantedPermissionCount,
List<CharSequence> grantedGroupLabels) {
if (mParent.getActivity() == null) {
return;
}
final Resources res = mContext.getResources();
CharSequence summary = null;
if (requestedPermissionCount == 0) {
summary = res.getString(
R.string.runtime_permissions_summary_no_permissions_requested);
mPreference.setEnabled(false);
} else {
final ArrayList<CharSequence> list = new ArrayList<>(grantedGroupLabels);
if (additionalGrantedPermissionCount > 0) {
// N additional permissions.
list.add(res.getQuantityString(
R.plurals.runtime_permissions_additional_count,
additionalGrantedPermissionCount, additionalGrantedPermissionCount));
}
if (list.size() == 0) {
summary = res.getString(
R.string.runtime_permissions_summary_no_permissions_granted);
} else {
summary = ListFormatter.getInstance().format(list);
}
mPreference.setEnabled(true);
}
mPreference.setSummary(summary);
}
};
public AppPermissionPreferenceController(Context context, AppInfoDashboardFragment parent,
String packageName) {
super(context, parent, KEY_PERMISSION);
mPackageName = packageName;
}
@Override
public void updateState(Preference preference) {
PermissionsSummaryHelper.getPermissionSummary(mContext, mPackageName, mPermissionCallback);
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (KEY_PERMISSION.equals(preference.getKey())) {
startManagePermissionsActivity();
return true;
}
return false;
}
private void startManagePermissionsActivity() {
// start new activity to manage app permissions
final Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mParent.getAppEntry().info.packageName);
intent.putExtra(EXTRA_HIDE_INFO_BUTTON, true);
try {
mParent.getActivity().startActivityForResult(intent, mParent.SUB_INFO_FRAGMENT);
} catch (ActivityNotFoundException e) {
Log.w(TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS");
}
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.LoaderManager;
import android.content.Context;
import android.content.Loader;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
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;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
public class AppStoragePreferenceController extends AppInfoPreferenceControllerBase
implements LoaderManager.LoaderCallbacks<StorageStatsSource.AppStorageStats>,
LifecycleObserver, OnResume, OnPause {
private static final String KEY_STORAGE = "storage_settings";
private StorageStatsSource.AppStorageStats mLastResult;
public AppStoragePreferenceController(Context context, AppInfoDashboardFragment parent,
Lifecycle lifecycle) {
super(context, parent, KEY_STORAGE);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public void updateState(Preference preference) {
final boolean isExternal =
(mParent.getAppEntry().info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
preference.setSummary(getStorageSummary(mLastResult, isExternal));
}
@Override
public void onResume() {
mParent.getLoaderManager().restartLoader(mParent.LOADER_STORAGE, Bundle.EMPTY, this);
}
@Override
public void onPause() {
mParent.getLoaderManager().destroyLoader(mParent.LOADER_STORAGE);
}
@Override
protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return AppStorageSettings.class;
}
@VisibleForTesting
CharSequence getStorageSummary(
StorageStatsSource.AppStorageStats stats, boolean isExternal) {
if (stats == null) {
return mContext.getText(R.string.computing_size);
}
final CharSequence storageType = mContext.getString(isExternal
? R.string.storage_type_external
: R.string.storage_type_internal);
return mContext.getString(R.string.storage_summary_format,
Formatter.formatFileSize(mContext, stats.getTotalBytes()),
storageType.toString().toLowerCase());
}
@Override
public Loader<StorageStatsSource.AppStorageStats> onCreateLoader(int id, Bundle args) {
return new FetchPackageStorageAsyncLoader(mContext, new StorageStatsSource(mContext),
mParent.getAppEntry().info, UserHandle.of(UserHandle.myUserId()));
}
@Override
public void onLoadFinished(Loader<StorageStatsSource.AppStorageStats> loader,
StorageStatsSource.AppStorageStats result) {
mLastResult = result;
updateState(mPreference);
}
@Override
public void onLoaderReset(Loader<StorageStatsSource.AppStorageStats> loader) {
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.content.Context;
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 {
private static final String KEY_VERSION = "app_version";
public AppVersionPreferenceController(Context context, AppInfoDashboardFragment parent) {
super(context, parent, KEY_VERSION);
}
@Override
public void updateState(Preference preference) {
preference.setSummary(mContext.getString(R.string.version_text,
BidiFormatter.getInstance().unicodeWrap(mParent.getPackageInfo().versionName)));
}
}

View File

@@ -70,7 +70,7 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl
*/
public static final int UNAVAILABLE_UNKNOWN = 4;
private final String mPreferenceKey;
protected final String mPreferenceKey;
public BasePreferenceController(Context context, String preferenceKey) {
super(context);

View File

@@ -1,3 +1,4 @@
com.android.settings.applications.AppInfoDashboardFragment
com.android.settings.bluetooth.DevicePickerFragment
com.android.settings.bluetooth.BluetoothDeviceDetailsFragment
com.android.settings.bluetooth.BluetoothPairingDetail

View File

@@ -46,7 +46,6 @@ com.android.settings.applications.RunningServices
com.android.settings.applications.ConfirmConvertToFbe
com.android.settings.deviceinfo.PublicVolumeSettings
com.android.settings.applications.InstalledAppDetails
com.android.settings.applications.AppInfoDashboardFragment
com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment
com.android.settings.print.PrintServiceSettingsFragment
com.android.settings.deviceinfo.PrivateVolumeSettings

View File

@@ -0,0 +1,459 @@
/*
* 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.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
import android.view.View;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.TestConfig;
import com.android.settings.applications.instantapps.InstantAppButtonsController;
import com.android.settings.applications.instantapps.InstantAppButtonsController.ShowDialogDelegate;
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_O,
shadows = AppInfoDashboardFragmentTest.ShadowUtils.class
)
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(mContext);
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 instantApps_buttonControllerHandlesDialog() {
InstantAppButtonsController mockController = mock(InstantAppButtonsController.class);
ReflectionHelpers.setField(
mAppDetail, "mInstantAppButtonsController", mockController);
// Make sure first that button controller is not called for supported dialog id
AlertDialog mockDialog = mock(AlertDialog.class);
when(mockController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP))
.thenReturn(mockDialog);
assertThat(mAppDetail.createDialog(InstantAppButtonsController.DLG_CLEAR_APP, 0))
.isEqualTo(mockDialog);
verify(mockController).createDialog(InstantAppButtonsController.DLG_CLEAR_APP);
}
// A helper class for testing the InstantAppButtonsController - it lets us look up the
// preference associated with a key for instant app buttons and get back a mock
// LayoutPreference (to avoid a null pointer exception).
public static class InstalledAppDetailsWithMockInstantButtons extends InstalledAppDetails {
@Mock
private LayoutPreference mInstantButtons;
public InstalledAppDetailsWithMockInstantButtons() {
super();
MockitoAnnotations.initMocks(this);
}
@Override
public Preference findPreference(CharSequence key) {
if (key == "instant_app_buttons") {
return mInstantButtons;
}
return super.findPreference(key);
}
}
@Test
public void instantApps_instantSpecificButtons() {
// Make this app appear to be instant.
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
(InstantAppDataProvider) (i -> true));
final PackageInfo packageInfo = mock(PackageInfo.class);
final InstalledAppDetailsWithMockInstantButtons
fragment = new InstalledAppDetailsWithMockInstantButtons();
ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo);
ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider",
mFeatureFactory.applicationFeatureProvider);
final InstantAppButtonsController buttonsController =
mock(InstantAppButtonsController.class);
when(buttonsController.setPackageName(nullable(String.class)))
.thenReturn(buttonsController);
when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController(
nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class)))
.thenReturn(buttonsController);
fragment.maybeAddInstantAppButtons();
verify(buttonsController).setPackageName(nullable(String.class));
verify(buttonsController).show();
}
@Test
public void instantApps_removeCorrectPref() {
PreferenceScreen mockPreferenceScreen = mock(PreferenceScreen.class);
PreferenceManager mockPreferenceManager = mock(PreferenceManager.class);
AppDomainsPreference mockAppDomainsPref = mock(AppDomainsPreference.class);
PackageInfo mockPackageInfo = mock(PackageInfo.class);
PackageManager mockPackageManager = mock(PackageManager.class);
ReflectionHelpers.setField(
mAppDetail, "mInstantAppDomainsPreference", mockAppDomainsPref);
ReflectionHelpers.setField(
mAppDetail, "mPreferenceManager", mockPreferenceManager);
ReflectionHelpers.setField(
mAppDetail, "mPackageInfo", mockPackageInfo);
ReflectionHelpers.setField(
mAppDetail, "mPm", mockPackageManager);
when(mockPreferenceManager.getPreferenceScreen()).thenReturn(mockPreferenceScreen);
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
(InstantAppDataProvider) (i -> false));
mAppDetail.prepareInstantAppPrefs();
// For the non instant case we remove the app domain pref, and leave the launch pref
verify(mockPreferenceScreen).removePreference(mockAppDomainsPref);
// For the instant app case we remove the launch preff, and leave the app domain pref
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
(InstantAppDataProvider) (i -> true));
mAppDetail.prepareInstantAppPrefs();
// Will be 1 still due to above call
verify(mockPreferenceScreen, times(1))
.removePreference(mockAppDomainsPref);
}
@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
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
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;
}
}
}

View File

@@ -0,0 +1,198 @@
/*
* 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.anyDouble;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.app.LoaderManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.BatteryStats;
import android.os.Bundle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
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;
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 java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppBatteryPreferenceControllerTest {
private static final int TARGET_UID = 111;
private static final int OTHER_UID = 222;
private static final double BATTERY_LEVEL = 60;
@Mock
private SettingsActivity mActivity;
@Mock
private BatteryUtils mBatteryUtils;
@Mock
private BatterySipper mBatterySipper;
@Mock
private BatterySipper mOtherBatterySipper;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private BatteryStats.Uid mUid;
@Mock
private PreferenceScreen mScreen;
@Mock
private PackageManager mPackageManager;
@Mock
private LoaderManager mLoaderManager;
private Context mContext;
private AppInfoDashboardFragment mFragment;
private AppBatteryPreferenceController mController;
private Preference mBatteryPreference;
@Before
public void setUp() throws NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
mFragment = spy(new AppInfoDashboardFragment());
mBatteryPreference = spy(new Preference(mContext));
mBatterySipper.drainType = BatterySipper.DrainType.IDLE;
mBatterySipper.uidObj = mUid;
doReturn(TARGET_UID).when(mBatterySipper).getUid();
doReturn(OTHER_UID).when(mOtherBatterySipper).getUid();
mController = spy(new AppBatteryPreferenceController(
mContext, mFragment, "package1", null /* lifecycle */));
mController.mBatteryUtils = mBatteryUtils;
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mBatteryPreference);
}
@Test
public void getAvailabilityStatus_shouldAlwaysReturnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void findTargetSipper_findCorrectSipper() {
final List<BatterySipper> usageList = new ArrayList<>();
usageList.add(mBatterySipper);
usageList.add(mOtherBatterySipper);
doReturn(usageList).when(mBatteryStatsHelper).getUsageList();
assertThat(mController.findTargetSipper(mBatteryStatsHelper, TARGET_UID)).isEqualTo(
mBatterySipper);
}
@Test
public void updateBattery_noBatteryStats_summaryNo() {
mController.displayPreference(mScreen);
mController.updateBattery();
assertThat(mBatteryPreference.getSummary()).isEqualTo(
"No battery use since last full charge");
}
@Test
public void updateBattery_hasBatteryStats_summaryPercent() {
mController.mBatteryHelper = mBatteryStatsHelper;
mController.mSipper = mBatterySipper;
doReturn(BATTERY_LEVEL).when(mBatteryUtils).calculateBatteryPercent(anyDouble(),
anyDouble(), anyDouble(), anyInt());
doReturn(new ArrayList<>()).when(mBatteryStatsHelper).getUsageList();
mController.displayPreference(mScreen);
mController.updateBattery();
assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use since last full charge");
}
@Test
public void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() {
mController.mBatteryHelper = mBatteryStatsHelper;
mController.mSipper = mBatterySipper;
assertThat(mController.isBatteryStatsAvailable()).isTrue();
}
@Test
public void isBatteryStatsAvailable_parametersNull_returnFalse() {
assertThat(mController.isBatteryStatsAvailable()).isFalse();
}
@Test
public void launchPowerUsageDetailFragment_shouldNotCrash() {
when(mActivity.getSystemService(Context.APP_OPS_SERVICE))
.thenReturn(mock(AppOpsManager.class));
when(mFragment.getActivity()).thenReturn(mActivity);
final String key = mController.getPreferenceKey();
when(mBatteryPreference.getKey()).thenReturn(key);
mController.mSipper = mBatterySipper;
mController.mBatteryHelper = mBatteryStatsHelper;
// Should not crash
mController.handlePreferenceTreeClick(mBatteryPreference);
}
@Test
public void onResume_shouldRestartBatteryStatsLoader() {
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
mController.onResume();
verify(mLoaderManager).restartLoader(AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY,
mController);
}
@Test
public void onPause_shouldDestroyBatteryStatsLoader() {
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
mController.onPause();
verify(mLoaderManager).destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY);
}
}

View File

@@ -0,0 +1,140 @@
/*
* 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.eq;
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.LoaderManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.net.ConnectivityManager;
import android.net.INetworkStatsSession;
import android.os.Bundle;
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.datausage.AppDataUsage;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
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.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppDataUsagePreferenceControllerTest {
@Mock
private LoaderManager mLoaderManager;
@Mock
private AppInfoDashboardFragment mFragment;
private Context mContext;
private AppDataUsagePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application.getApplicationContext());
mController = spy(
new AppDataUsagePreferenceController(mContext, mFragment, null /* lifecycle */));
}
@Test
public void getAvailabilityStatus_bandwidthControlEnabled_shouldReturnAvailable() {
doReturn(true).when(mController).isBandwidthControlEnabled();
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void getAvailabilityStatus_bandwidthControlDisabled_shouldReturnDisabled() {
doReturn(false).when(mController).isBandwidthControlEnabled();
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.DISABLED_UNSUPPORTED);
}
@Test
public void onResume_noSession_shouldNotRestartDataLoader() {
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
mController.onResume();
verify(mLoaderManager, never()).restartLoader(
AppInfoDashboardFragment.LOADER_CHART_DATA, Bundle.EMPTY, mController);
}
@Test
public void onResume_hasSession_shouldRestartDataLoader() {
final ConnectivityManager connectivityManager = mock(ConnectivityManager.class);
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
.thenReturn(connectivityManager);
when(connectivityManager.isNetworkSupported(anyInt())).thenReturn(true);
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
ReflectionHelpers.setField(mController, "mStatsSession", mock(INetworkStatsSession.class));
final AppEntry appEntry = mock(AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
mController.onResume();
verify(mLoaderManager).restartLoader(
eq(AppInfoDashboardFragment.LOADER_CHART_DATA), any(Bundle.class), eq(mController));
}
@Test
public void onPause_shouldDestroyDataLoader() {
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
mController.onPause();
verify(mLoaderManager).destroyLoader(AppInfoDashboardFragment.LOADER_CHART_DATA);
}
@Test
public void getDetailFragmentClass_shouldReturnAppDataUsage() {
assertThat(mController.getDetailFragmentClass()).isEqualTo(AppDataUsage.class);
}
@Test
public void updateState_shouldUpdatePreferenceSummary() {
final Preference preference = mock(Preference.class);
mController.updateState(preference);
verify(preference).setSummary(any());
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.R;
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;
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;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppInfoPreferenceControllerBaseTest {
@Mock
private SettingsActivity mActivity;
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
private TestPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new TestPreferenceController(mFragment);
final String key = mController.getPreferenceKey();
when(mScreen.findPreference(key)).thenReturn(mPreference);
when(mPreference.getKey()).thenReturn(key);
when(mFragment.getActivity()).thenReturn(mActivity);
}
@Test
public void getAvailabilityStatus_shouldReturnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void refreshUi_shouldUpdatePreference() {
mController.displayPreference(mScreen);
mController.refreshUi();
assertThat(mController.preferenceUpdated).isTrue();
}
@Test
public void handlePreferenceTreeClick_shouldStartDetailFragmentClass() {
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
mController.handlePreferenceTreeClick(mPreference);
verify(mActivity).startPreferencePanel(any(),
eq(mController.getDetailFragmentClass().getName()), any(), anyInt(), any(), any(),
anyInt());
}
private class TestPreferenceController extends AppInfoPreferenceControllerBase {
private boolean preferenceUpdated;
public TestPreferenceController(AppInfoDashboardFragment parent) {
super(RuntimeEnvironment.application, parent, "TestKey");
}
@Override
public void updateState(Preference preference) {
preferenceUpdated = true;
}
@Override
public Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
return AppNotificationSettings.class;
}
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.eq;
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.Context;
import android.provider.Settings;
import android.support.v7.preference.Preference;
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;
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.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppMemoryPreferenceControllerTest {
@Mock
private SettingsActivity mActivity;
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
private Context mContext;
private AppMemoryPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController =
spy(new AppMemoryPreferenceController(mContext, mFragment, null /* lifecycle */));
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
final String key = mController.getPreferenceKey();
when(mPreference.getKey()).thenReturn(key);
when(mFragment.getActivity()).thenReturn(mActivity);
}
@Test
public void getAvailabilityStatus_developmentSettingsEnabled_shouldReturnAvailable() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void getAvailabilityStatus_developmentSettingsDisabled_shouldReturnDisabled() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
assertThat(mController.getAvailabilityStatus())
.isEqualTo(mController.DISABLED_DEPENDENT_SETTING);
}
@Test
public void handlePreferenceTreeClick_shouldStartProcessStatsDetail() {
final ProcStatsData data = mock(ProcStatsData.class);
when(data.getMemInfo()).thenReturn(mock(ProcStatsData.MemInfo.class));
ReflectionHelpers.setField(mController, "mStatsManager", data);
mController.handlePreferenceTreeClick(mPreference);
verify(mActivity).startPreferencePanel(any(), eq(ProcessStatsDetail.class.getName()), any(),
eq(R.string.memory_usage), any(), any(), anyInt());
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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.Mockito.mock;
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.pm.ApplicationInfo;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.R;
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;
import com.android.settingslib.applications.ApplicationsState;
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.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppNotificationPreferenceControllerTest {
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
private Context mContext;
private AppNotificationPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController =
spy(new AppNotificationPreferenceController(mContext, mFragment));
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
final String key = mController.getPreferenceKey();
when(mPreference.getKey()).thenReturn(key);
}
@Test
public void getDetailFragmentClass_shouldReturnAppNotificationSettings() {
assertThat(mController.getDetailFragmentClass()).isEqualTo(AppNotificationSettings.class);
}
@Test
public void updateState_shouldSetSummary() {
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
ReflectionHelpers.setField(mController, "mBackend", new NotificationBackend());
mController.displayPreference(mScreen);
mController.updateState(mPreference);
verify(mPreference).setSummary(any());
}
}

View File

@@ -0,0 +1,166 @@
/*
* 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.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.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.R;
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;
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.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppOpenByDefaultPreferenceControllerTest {
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
private Context mContext;
private AppOpenByDefaultPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application.getApplicationContext();
mController = spy(new AppOpenByDefaultPreferenceController(mContext, mFragment));
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
}
@Test
public void getDetailFragmentClass_shouldReturnAppLaunchSettings() {
assertThat(mController.getDetailFragmentClass()).isEqualTo(AppLaunchSettings.class);
}
@Test
public void displayPreference_noAppEntry_shouldDisablePreference() {
mController.displayPreference(mScreen);
verify(mPreference).setEnabled(false);
}
@Test
public void displayPreference_noAppInfo_shouldDisablePreference() {
final AppEntry appEntry = mock(AppEntry.class);
when(mFragment.getAppEntry()).thenReturn(appEntry);
mController.displayPreference(mScreen);
verify(mPreference).setEnabled(false);
}
@Test
public void displayPreference_appNotInstalled_shouldDisablePreference() {
final AppEntry appEntry = mock(AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
mController.displayPreference(mScreen);
verify(mPreference).setEnabled(false);
}
@Test
public void displayPreference_appDisabled_shouldDisablePreference() {
final AppEntry appEntry = mock(AppEntry.class);
appEntry.info = new ApplicationInfo();
appEntry.info.flags &= ApplicationInfo.FLAG_INSTALLED;
appEntry.info.enabled = false;
when(mFragment.getAppEntry()).thenReturn(appEntry);
mController.displayPreference(mScreen);
verify(mPreference).setEnabled(false);
}
@Test
public void displayPreference_appEnabled_shouldNotDisablePreference() {
final AppEntry appEntry = mock(AppEntry.class);
appEntry.info = new ApplicationInfo();
appEntry.info.flags |= ApplicationInfo.FLAG_INSTALLED;
appEntry.info.enabled = true;
when(mFragment.getAppEntry()).thenReturn(appEntry);
mController.displayPreference(mScreen);
verify(mPreference, never()).setEnabled(false);
}
@Test
public void updateState_noPackageInfo_shouldNotShowPreference() {
mController.updateState(mPreference);
verify(mPreference).setVisible(false);
}
@Test
public void updateState_isInstantApp_shouldNotShowPreference() {
when(mFragment.getPackageInfo()).thenReturn(new PackageInfo());
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
(InstantAppDataProvider) (i -> true));
mController.updateState(mPreference);
verify(mPreference).setVisible(false);
}
@Test
public void updateState_notInstantApp_shouldShowPreferenceAndSetSummary() {
when(mFragment.getPackageInfo()).thenReturn(new PackageInfo());
final AppEntry appEntry = mock(AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
(InstantAppDataProvider) (i -> false));
mController.updateState(mPreference);
verify(mPreference).setVisible(true);
verify(mPreference).setSummary(any());
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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.Mockito.mock;
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.support.v7.preference.Preference;
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;
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 java.util.ArrayList;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppPermissionPreferenceControllerTest {
@Mock
private SettingsActivity mActivity;
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
private Context mContext;
private AppPermissionPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new AppPermissionPreferenceController(mContext, mFragment, "Package1");
when(mScreen.findPreference(any())).thenReturn(mPreference);
final String key = mController.getPreferenceKey();
when(mPreference.getKey()).thenReturn(key);
when(mFragment.getActivity()).thenReturn(mActivity);
}
@Test
public void getAvailabilityStatus_isAlwaysAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(mController.AVAILABLE);
}
@Test
public void onPermissionSummaryResult_noRequestedPermission_shouldDisablePreference() {
mController.displayPreference(mScreen);
mController.mPermissionCallback.onPermissionSummaryResult(
1, 0, 1, new ArrayList<CharSequence>());
verify(mPreference).setEnabled(false);
verify(mPreference).setSummary(mContext.getString(
R.string.runtime_permissions_summary_no_permissions_requested));
}
@Test
public void onPermissionSummaryResult_noGrantedPermission_shouldSetNoPermissionSummary() {
mController.displayPreference(mScreen);
mController.mPermissionCallback.onPermissionSummaryResult(
1, 5, 0, new ArrayList<CharSequence>());
verify(mPreference).setEnabled(true);
verify(mPreference).setSummary(mContext.getString(
R.string.runtime_permissions_summary_no_permissions_granted));
}
@Test
public void onPermissionSummaryResult_hasRuntimePermission_shouldSetPermissionAsSummary() {
mController.displayPreference(mScreen);
final String permission = "Storage";
final ArrayList<CharSequence> labels = new ArrayList<>();
labels.add(permission);
mController.mPermissionCallback.onPermissionSummaryResult(1, 5, 0, labels);
verify(mPreference).setEnabled(true);
verify(mPreference).setSummary(permission);
}
@Test
public void onPermissionSummaryResult_hasAdditionalPermission_shouldSetAdditionalSummary() {
mController.displayPreference(mScreen);
final String permission = "Storage";
final ArrayList<CharSequence> labels = new ArrayList<>();
labels.add(permission);
mController.mPermissionCallback.onPermissionSummaryResult(1, 5, 2, labels);
verify(mPreference).setEnabled(true);
verify(mPreference).setSummary("Storage and 2 additional permissions");
}
@Test
public void handlePreferenceTreeClick_shouldStartManagePermissionsActivity() {
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
mController.handlePreferenceTreeClick(mPreference);
verify(mActivity).startActivityForResult(argThat(intent-> intent != null &&
Intent.ACTION_MANAGE_APP_PERMISSIONS.equals(intent.getAction())), anyInt());
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.LoaderManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
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.applications.AppStorageSettings;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.StorageStatsSource;
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;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppStoragePreferenceControllerTest {
@Mock
private LoaderManager mLoaderManager;
@Mock
private AppInfoDashboardFragment mFragment;
private Context mContext;
private AppStoragePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application.getApplicationContext();
mController =
spy(new AppStoragePreferenceController(mContext, mFragment, null /* lifecycle */));
}
@Test
public void onResume_shouldRestartStorageLoader() {
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
mController.onResume();
verify(mLoaderManager).restartLoader(AppInfoDashboardFragment.LOADER_STORAGE, Bundle.EMPTY,
mController);
}
@Test
public void onPause_shouldDestroyStorageLoader() {
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
mController.onPause();
verify(mLoaderManager).destroyLoader(AppInfoDashboardFragment.LOADER_STORAGE);
}
@Test
public void getDetailFragmentClass_shouldReturnAppStorageSettings() {
assertThat(mController.getDetailFragmentClass()).isEqualTo(AppStorageSettings.class);
}
@Test
public void updateState_shouldUpdatePreferenceSummary() {
final AppEntry appEntry = mock(AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
Preference preference = mock(Preference.class);
mController.updateState(preference);
verify(preference).setSummary(any());
}
@Test
public void getStorageSummary_shouldWorkForExternal() {
final StorageStatsSource.AppStorageStats stats =
mock(StorageStatsSource.AppStorageStats.class);
when(stats.getTotalBytes()).thenReturn(1L);
assertThat(mController.getStorageSummary(stats, true))
.isEqualTo("1 B used in external storage");
}
@Test
public void getStorageSummary_shouldWorkForInternal() {
final StorageStatsSource.AppStorageStats stats =
mock(StorageStatsSource.AppStorageStats.class);
when(stats.getTotalBytes()).thenReturn(1L);
assertThat(mController.getStorageSummary(stats, false))
.isEqualTo("1 B used in internal storage");
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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 org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
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;
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;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
public class AppVersionPreferenceControllerTest {
@Mock
private AppInfoDashboardFragment mFragment;
@Mock
private Preference mPreference;
private Context mContext;
private AppVersionPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new AppVersionPreferenceController(mContext, mFragment);
}
@Test
public void updateState_shouldUpdatePreferenceSummary() {
final PackageInfo packageInfo = mock(PackageInfo.class);
packageInfo.versionName = "test1234";
when(mFragment.getPackageInfo()).thenReturn(packageInfo);
mController.updateState(mPreference);
verify(mPreference).setSummary("version test1234");
}
}