Add high power whitelist for apps

- Strings not final!
 - New UX for power usage details (more preferency)
 - Add high power apps list shows on/off and screen to
   change (when possible)
 - Link from power usage summary to high power list
 - Link from advanced apps to high power list

Bug: 19991702
Change-Id: I97c927ed82d3b89041e4429b427508545763d66c
This commit is contained in:
Jason Monk
2015-04-29 12:46:42 -04:00
parent a283e6e325
commit 1eb54eb2ff
29 changed files with 952 additions and 597 deletions

View File

@@ -26,6 +26,7 @@ import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.ApplicationsState.AppEntry;
import com.android.settings.applications.ApplicationsState.Session;
import com.android.settings.fuelgauge.PowerWhitelistBackend;
import com.android.settingslib.applications.PermissionsInfo;
import java.util.ArrayList;
@@ -38,11 +39,13 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
private static final String KEY_APP_PERM = "manage_perms";
private static final String KEY_APP_DOMAIN_URLS = "domain_urls";
private static final String KEY_HIGH_POWER_APPS = "high_power_apps";
private ApplicationsState mApplicationsState;
private Session mSession;
private Preference mAppPermsPreference;
private Preference mAppDomainURLsPreference;
private Preference mHighPowerPreference;
private PermissionsInfo mPermissionsInfo;
@Override
@@ -55,6 +58,7 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
mAppPermsPreference = findPreference(KEY_APP_PERM);
mAppDomainURLsPreference = findPreference(KEY_APP_DOMAIN_URLS);
mHighPowerPreference = findPreference(KEY_HIGH_POWER_APPS);
updateUI();
}
@@ -70,6 +74,10 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
String summary = getResources().getQuantityString(
R.plurals.domain_urls_apps_summary, countAppWithDomainURLs, countAppWithDomainURLs);
mAppDomainURLsPreference.setSummary(summary);
int highPowerCount = PowerWhitelistBackend.getInstance().getWhitelistSize();
mHighPowerPreference.setSummary(getResources().getQuantityString(R.plurals.high_power_count,
highPowerCount, highPowerCount));
}
@Override

View File

@@ -20,6 +20,7 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -36,6 +37,7 @@ import android.util.Log;
import com.android.settings.InstrumentedPreferenceFragment;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.ApplicationsState.AppEntry;
import java.util.ArrayList;
@@ -194,6 +196,17 @@ public abstract class AppInfoBase extends InstrumentedPreferenceFragment
refreshUi();
}
public static void startAppInfoFragment(Class<? extends AppInfoBase> fragment, int titleRes,
String pkg, int uid, Fragment source, int request) {
Bundle args = new Bundle();
args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg);
Intent intent = Utils.onBuildStartFragmentIntent(source.getActivity(), fragment.getName(),
args, null, titleRes, null, false);
source.getActivity().startActivityForResultAsUser(intent, request,
new UserHandle(UserHandle.getUserId(uid)));
}
public class MyAlertDialogFragment extends DialogFragment {
public MyAlertDialogFragment(int id, int errorCode) {
Bundle args = new Bundle();

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2015 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 com.android.settings.applications.ApplicationsState.AppEntry;
import com.android.settings.applications.ApplicationsState.AppFilter;
import com.android.settings.fuelgauge.PowerWhitelistBackend;
import java.util.ArrayList;
/**
* Connects data from the PowerWhitelistBackend to ApplicationsState.
*/
public class AppStatePowerBridge extends AppStateBaseBridge {
private final PowerWhitelistBackend mBackend = PowerWhitelistBackend.getInstance();
public AppStatePowerBridge(ApplicationsState appState, Callback callback) {
super(appState, callback);
}
@Override
protected void loadAllExtraInfo() {
ArrayList<AppEntry> apps = mAppSession.getAllApps();
final int N = apps.size();
for (int i = 0; i < N; i++) {
AppEntry app = apps.get(i);
app.extraInfo = mBackend.isWhitelisted(app.info.packageName)
? Boolean.TRUE : Boolean.FALSE;
}
}
@Override
protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
app.extraInfo = mBackend.isWhitelisted(pkg) ? Boolean.TRUE : Boolean.FALSE;
}
public static class HighPowerState {
public boolean isHighPower;
public boolean isSystemHighPower;
}
public static final AppFilter FILTER_POWER_WHITELISTED = new AppFilter() {
@Override
public void init() {
}
@Override
public boolean filterApp(AppEntry info) {
return info.extraInfo == Boolean.TRUE;
}
};
public static final AppFilter FILTER_POWER_NOT_WHITELISTED = new AppFilter() {
@Override
public void init() {
}
@Override
public boolean filterApp(AppEntry info) {
return info.extraInfo == Boolean.FALSE;
}
};
}

View File

@@ -38,6 +38,7 @@ import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -57,12 +58,16 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.DataUsageSummary;
import com.android.settings.DataUsageSummary.AppItem;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.ApplicationsState.AppEntry;
import com.android.settings.fuelgauge.BatteryEntry;
import com.android.settings.fuelgauge.PowerUsageDetail;
import com.android.settings.net.ChartData;
import com.android.settings.net.ChartDataLoader;
import com.android.settings.notification.NotificationBackend;
@@ -108,6 +113,7 @@ public class InstalledAppDetails extends AppInfoBase
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 final HashSet<String> mHomePackages = new HashSet<String>();
@@ -131,6 +137,11 @@ public class InstalledAppDetails extends AppInfoBase
private ChartData mChartData;
private INetworkStatsSession mStatsSession;
private Preference mBatteryPreference;
private BatteryStatsHelper mBatteryHelper;
private BatterySipper mSipper;
private boolean handleDisableable(Button button) {
boolean disableable = false;
// Try to prevent the user from bricking their phone
@@ -221,6 +232,7 @@ public class InstalledAppDetails extends AppInfoBase
} catch (RemoteException e) {
throw new RuntimeException(e);
}
mBatteryHelper = new BatteryStatsHelper(getActivity(), true);
}
@Override
@@ -236,6 +248,7 @@ public class InstalledAppDetails extends AppInfoBase
getLoaderManager().restartLoader(LOADER_CHART_DATA,
ChartDataLoader.buildArgs(NetworkTemplate.buildTemplateMobileWildcard(), app),
mDataCallbacks);
new BatteryUpdater().execute();
}
@Override
@@ -263,6 +276,9 @@ public class InstalledAppDetails extends AppInfoBase
mPermissionsPreference.setOnPreferenceClickListener(this);
mDataPreference = findPreference(KEY_DATA);
mDataPreference.setOnPreferenceClickListener(this);
mBatteryPreference = findPreference(KEY_BATTERY);
mBatteryPreference.setEnabled(false);
mBatteryPreference.setOnPreferenceClickListener(this);
mLaunchPreference = findPreference(KEY_LAUNCH);
if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
@@ -434,6 +450,8 @@ public class InstalledAppDetails extends AppInfoBase
mBackend));
mDataPreference.setSummary(getDataSummary());
updateBattery();
if (!mInitialized) {
// First time init: are we displaying an uninstalled app?
mInitialized = true;
@@ -459,6 +477,20 @@ public class InstalledAppDetails extends AppInfoBase
return true;
}
private void updateBattery() {
if (mSipper != null) {
mBatteryPreference.setEnabled(true);
int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount(
BatteryStats.STATS_SINCE_CHARGED);
final int percentOfMax = (int) ((mSipper.totalPowerMah)
/ mBatteryHelper.getTotalPower() * dischargeAmount + .5f);
mBatteryPreference.setSummary(getString(R.string.battery_summary, percentOfMax));
} else {
mBatteryPreference.setEnabled(false);
mBatteryPreference.setSummary(getString(R.string.no_battery_summary));
}
}
private CharSequence getDataSummary() {
if (mChartData != null) {
long totalBytes = mChartData.detail.getTotalBytes();
@@ -656,6 +688,10 @@ public class InstalledAppDetails extends AppInfoBase
SettingsActivity sa = (SettingsActivity) getActivity();
sa.startPreferencePanel(DataUsageSummary.class.getName(), args, -1,
getString(R.string.app_data_usage), this, SUB_INFO_FRAGMENT);
} else if (preference == mBatteryPreference) {
BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper);
PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, true);
} else {
return false;
}
@@ -700,7 +736,31 @@ public class InstalledAppDetails extends AppInfoBase
}
}
static class DisableChanger extends AsyncTask<Object, Object, Object> {
private class BatteryUpdater extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
mBatteryHelper.create((Bundle) null);
mBatteryHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
mUserManager.getUserProfiles());
List<BatterySipper> usageList = mBatteryHelper.getUsageList();
final int N = usageList.size();
for (int i = 0; i < N; i++) {
BatterySipper sipper = usageList.get(i);
if (sipper.getUid() == mPackageInfo.applicationInfo.uid) {
mSipper = sipper;
break;
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
refreshUi();
}
}
private static class DisableChanger extends AsyncTask<Object, Object, Object> {
final PackageManager mPm;
final WeakReference<InstalledAppDetails> mActivity;
final ApplicationInfo mInfo;

View File

@@ -45,7 +45,9 @@ public class LayoutPreference extends Preference {
.inflate(layoutResource, null, false);
final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details);
Utils.forceCustomPadding(allDetails, true /* additive padding */);
if (allDetails != null) {
Utils.forceCustomPadding(allDetails, true /* additive padding */);
}
mRootView = view;
setShouldDisableView(false);
}

View File

@@ -55,6 +55,7 @@ import com.android.settings.InstrumentedFragment;
import com.android.settings.R;
import com.android.settings.Settings.AllApplicationsActivity;
import com.android.settings.Settings.DomainsURLsAppListActivity;
import com.android.settings.Settings.HighPowerApplicationsActivity;
import com.android.settings.Settings.NotificationAppListActivity;
import com.android.settings.Settings.StorageUseActivity;
import com.android.settings.Settings.UsageAccessSettingsActivity;
@@ -65,6 +66,7 @@ import com.android.settings.applications.ApplicationsState.AppEntry;
import com.android.settings.applications.ApplicationsState.AppFilter;
import com.android.settings.applications.ApplicationsState.CompoundFilter;
import com.android.settings.applications.ApplicationsState.VolumeFilter;
import com.android.settings.fuelgauge.HighPowerDetail;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.NotificationBackend.AppRow;
@@ -117,6 +119,8 @@ public class ManageApplications extends InstrumentedFragment
public static final int FILTER_APPS_WORK = 8;
public static final int FILTER_APPS_WITH_DOMAIN_URLS = 9;
public static final int FILTER_APPS_USAGE_ACCESS = 10;
public static final int FILTER_APPS_POWER_WHITELIST = 11;
public static final int FILTER_APPS_POWER_NO_WHITELIST = 12;
// This is the string labels for the filter modes above, the order must be kept in sync.
public static final int[] FILTER_LABELS = new int[] {
@@ -131,6 +135,8 @@ public class ManageApplications extends InstrumentedFragment
R.string.filter_work_apps, // Work
R.string.filter_with_domain_urls_apps, // Domain URLs
R.string.filter_all_apps, // Usage access screen, never displayed
R.string.high_power_on, // High power whitelist, on
R.string.high_power_off, // High power whitelist, off
};
// This is the actual mapping to filters from FILTER_ constants above, the order must
// be kept in sync.
@@ -146,6 +152,8 @@ public class ManageApplications extends InstrumentedFragment
ApplicationsState.FILTER_WORK, // Work
ApplicationsState.FILTER_WITH_DOMAIN_URLS, // Apps with Domain URLs
AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
AppStatePowerBridge.FILTER_POWER_WHITELISTED, // High power whitelist, on
AppStatePowerBridge.FILTER_POWER_NOT_WHITELISTED, // High power whitelist, off
};
// sort order
@@ -180,11 +188,12 @@ public class ManageApplications extends InstrumentedFragment
private Menu mOptionsMenu;
public static final int LIST_TYPE_MAIN = 0;
public static final int LIST_TYPE_MAIN = 0;
public static final int LIST_TYPE_NOTIFICATION = 1;
public static final int LIST_TYPE_DOMAINS_URLS = 2;
public static final int LIST_TYPE_STORAGE = 3;
public static final int LIST_TYPE_STORAGE = 3;
public static final int LIST_TYPE_USAGE_ACCESS = 4;
public static final int LIST_TYPE_HIGH_POWER = 5;
private View mRootView;
@@ -228,6 +237,8 @@ public class ManageApplications extends InstrumentedFragment
} else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
mListType = LIST_TYPE_USAGE_ACCESS;
getActivity().getActionBar().setTitle(R.string.usage_access_title);
} else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
mListType = LIST_TYPE_HIGH_POWER;
} else {
mListType = LIST_TYPE_MAIN;
}
@@ -310,6 +321,9 @@ public class ManageApplications extends InstrumentedFragment
mFilterAdapter.enableFilter(FILTER_APPS_SENSITIVE);
mFilterAdapter.enableFilter(FILTER_APPS_NO_PEEKING);
}
if (mListType == LIST_TYPE_HIGH_POWER) {
mFilterAdapter.enableFilter(FILTER_APPS_POWER_NO_WHITELIST);
}
if (mListType == LIST_TYPE_STORAGE) {
mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid));
}
@@ -325,12 +339,12 @@ public class ManageApplications extends InstrumentedFragment
private int getDefaultFilter() {
switch (mListType) {
case LIST_TYPE_MAIN:
return FILTER_APPS_ALL;
case LIST_TYPE_DOMAINS_URLS:
return FILTER_APPS_WITH_DOMAIN_URLS;
case LIST_TYPE_USAGE_ACCESS:
return FILTER_APPS_USAGE_ACCESS;
case LIST_TYPE_HIGH_POWER:
return FILTER_APPS_POWER_WHITELIST;
default:
return FILTER_APPS_ALL;
}
@@ -349,6 +363,8 @@ public class ManageApplications extends InstrumentedFragment
return InstrumentedFragment.VIEW_CATEGORY_STORAGE_APPS;
case LIST_TYPE_USAGE_ACCESS:
return MetricsLogger.USAGE_ACCESS;
case LIST_TYPE_HIGH_POWER:
return InstrumentedFragment.VIEW_CATEGORY_HIGH_POWER_APPS;
default:
return MetricsLogger.VIEW_UNKNOWN;
}
@@ -426,6 +442,9 @@ public class ManageApplications extends InstrumentedFragment
case LIST_TYPE_STORAGE:
startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
break;
case LIST_TYPE_HIGH_POWER:
startAppInfoFragment(HighPowerDetail.class, R.string.high_power);
break;
// TODO: Figure out if there is a way where we can spin up the profile's settings
// process ahead of time, to avoid a long load of data when user clicks on a managed app.
// Maybe when they load the list of apps that contains managed profile apps.
@@ -436,13 +455,8 @@ public class ManageApplications extends InstrumentedFragment
}
private void startAppInfoFragment(Class<? extends AppInfoBase> fragment, int titleRes) {
Bundle args = new Bundle();
args.putString(AppInfoBase.ARG_PACKAGE_NAME, mCurrentPkgName);
Intent intent = Utils.onBuildStartFragmentIntent(getActivity(), fragment.getName(), args,
null, titleRes, null, false);
getActivity().startActivityForResultAsUser(intent, INSTALLED_APP_DETAILS,
new UserHandle(UserHandle.getUserId(mCurrentUid)));
AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this,
INSTALLED_APP_DETAILS);
}
@Override
@@ -685,6 +699,8 @@ public class ManageApplications extends InstrumentedFragment
mState, this, manageApplications.mNotifBackend);
} else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
mExtraInfoBridge = new AppStatePowerBridge(mState, this);
} else {
mExtraInfoBridge = null;
}
@@ -994,6 +1010,10 @@ public class ManageApplications extends InstrumentedFragment
}
break;
case LIST_TYPE_HIGH_POWER:
holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
break;
default:
holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
break;