Merge "Revamp the battery usage details page."

This commit is contained in:
Lei Yu
2017-03-25 06:17:52 +00:00
committed by Android (Google) Code Review
18 changed files with 992 additions and 40 deletions

View File

@@ -74,6 +74,7 @@ import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.RawContacts;
import android.provider.Settings;
import android.service.persistentdata.PersistentDataBlockManager;
import android.support.annotation.StringRes;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceManager;
@@ -1241,6 +1242,17 @@ public final class Utils extends com.android.settingslib.Utils {
return isVolumeValid(volume) ? volume : null;
}
/**
* Return the resource id to represent the install status for an app
*/
@StringRes
public static int getInstallationStatus(ApplicationInfo info) {
if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
return R.string.not_installed;
}
return info.enabled ? R.string.installed : R.string.disabled;
}
private static boolean isVolumeValid(VolumeInfo volume) {
return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
&& volume.isMountedReadable();

View File

@@ -546,7 +546,7 @@ public class InstalledAppDetails extends AppInfoBase
.newAppHeaderController(this, appSnippet)
.setLabel(mAppEntry)
.setIcon(mAppEntry)
.setSummary(getString(getInstallationStatus(mAppEntry.info)))
.setSummary(getString(Utils.getInstallationStatus(mAppEntry.info)))
.setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
.done(false /* rebindActions */);
mVersionPreference.setSummary(getString(R.string.version_text, pkgInfo.versionName));
@@ -574,14 +574,6 @@ public class InstalledAppDetails extends AppInfoBase
return showIt;
}
@VisibleForTesting
int getInstallationStatus(ApplicationInfo info) {
if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
return R.string.not_installed;
}
return info.enabled ? R.string.installed : R.string.disabled;
}
private boolean signaturesMatch(String pkg1, String pkg2) {
if (pkg1 != null && pkg2 != null) {
try {

View File

@@ -0,0 +1,204 @@
/*
* 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.fuelgauge;
import android.content.Context;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.Preference;
import android.view.View;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.AppHeaderController;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.PreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState;
import java.util.ArrayList;
import java.util.List;
/**
* Power usage detail fragment for each app, this fragment contains
*
* 1. Detail battery usage information for app(i.e. usage time, usage amount)
* 2. Battery related controls for app(i.e uninstall, force stop)
*
* This fragment will replace {@link PowerUsageDetail}
*/
public class AdvancedPowerUsageDetail extends PowerUsageBase {
public static final String TAG = "AdvancedPowerUsageDetail";
public static final String EXTRA_UID = "extra_uid";
public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
public static final String EXTRA_FOREGROUND_TIME = "extra_foreground_time";
public static final String EXTRA_BACKGROUND_TIME = "extra_background_time";
public static final String EXTRA_LABEL = "extra_label";
public static final String EXTRA_ICON_ID = "extra_icon_id";
public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent";
public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount";
private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
private static final String KEY_PREF_BACKGROUND = "app_usage_background";
private static final String KEY_PREF_POWER_USAGE = "app_power_usage";
private static final String KEY_PREF_HEADER = "header_view";
@VisibleForTesting
LayoutPreference mHeaderPreference;
@VisibleForTesting
ApplicationsState mState;
@VisibleForTesting
ApplicationsState.AppEntry mAppEntry;
private Preference mForegroundPreference;
private Preference mBackgroundPreference;
private Preference mPowerUsagePreference;
public static void startBatteryDetailPage(SettingsActivity caller, PreferenceFragment fragment,
BatteryStatsHelper helper, int which, BatteryEntry entry, String usagePercent) {
// Initialize mStats if necessary.
helper.getStats();
final Bundle args = new Bundle();
final BatterySipper sipper = entry.sipper;
final BatteryStats.Uid uid = sipper.uidObj;
final long backgroundTimeMs = BatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.BACKGROUND, uid, which);
final long foregroundTimeMs = BatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, uid, which);
if (ArrayUtils.isEmpty(sipper.mPackages)) {
// populate data for system app
args.putString(EXTRA_LABEL, entry.getLabel());
args.putInt(EXTRA_ICON_ID, entry.iconId);
args.putString(EXTRA_PACKAGE_NAME, null);
} else {
// populate data for normal app
args.putString(EXTRA_PACKAGE_NAME, sipper.mPackages[0]);
}
args.putInt(EXTRA_UID, sipper.getUid());
args.putLong(EXTRA_BACKGROUND_TIME, backgroundTimeMs);
args.putLong(EXTRA_FOREGROUND_TIME, foregroundTimeMs);
args.putString(EXTRA_POWER_USAGE_PERCENT, usagePercent);
args.putInt(EXTRA_POWER_USAGE_AMOUNT, (int) sipper.totalPowerMah);
caller.startPreferencePanelAsUser(fragment, AdvancedPowerUsageDetail.class.getName(), args,
R.string.details_title, null, new UserHandle(UserHandle.myUserId()));
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
mPowerUsagePreference = findPreference(KEY_PREF_POWER_USAGE);
mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
mState = ApplicationsState.getInstance(getActivity().getApplication());
final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME);
if (packageName != null) {
mAppEntry = mState.getEntry(packageName, UserHandle.myUserId());
}
}
@Override
public void onResume() {
super.onResume();
initHeader();
final Bundle bundle = getArguments();
final Context context = getContext();
final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
final String usagePercent = bundle.getString(EXTRA_POWER_USAGE_PERCENT);
final int powerMah = bundle.getInt(EXTRA_POWER_USAGE_AMOUNT);
mForegroundPreference.setSummary(Utils.formatElapsedTime(context, foregroundTimeMs, false));
mBackgroundPreference.setSummary(Utils.formatElapsedTime(context, backgroundTimeMs, false));
mPowerUsagePreference.setSummary(
getString(R.string.battery_detail_power_percentage, usagePercent, powerMah));
}
@VisibleForTesting
void initHeader() {
final View appSnippet = mHeaderPreference.findViewById(R.id.app_snippet);
final Context context = getContext();
final Bundle bundle = getArguments();
AppHeaderController controller = FeatureFactory.getFactory(context)
.getApplicationFeatureProvider(context)
.newAppHeaderController(this, appSnippet)
.setButtonActions(AppHeaderController.ActionType.ACTION_NONE,
AppHeaderController.ActionType.ACTION_NONE);
if (mAppEntry == null) {
controller.setLabel(bundle.getString(EXTRA_LABEL));
controller.setIcon(getContext().getDrawable(bundle.getInt(EXTRA_ICON_ID)));
} else {
mState.ensureIcon(mAppEntry);
controller.setLabel(mAppEntry);
controller.setIcon(mAppEntry);
controller.setSummary(getString(Utils.getInstallationStatus(mAppEntry.info)));
}
controller.done(true /* rebindActions */);
}
@Override
public int getMetricsCategory() {
return MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.power_usage_detail_ia;
}
@Override
protected List<PreferenceController> getPreferenceControllers(Context context) {
final List<PreferenceController> controllers = new ArrayList<>();
final Bundle bundle = getArguments();
final int uid = bundle.getInt(EXTRA_UID, 0);
final String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
controllers.add(new BackgroundActivityPreferenceController(context, uid));
controllers.add(new BatteryOptimizationPreferenceController(
(SettingsActivity) getActivity(), this));
controllers.add(
new AppButtonsPreferenceController(getActivity(), getLifecycle(), packageName));
return controllers;
}
}

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.fuelgauge;
import android.app.Activity;
import android.os.UserHandle;
import android.support.v7.preference.PreferenceScreen;
import android.view.View;
import android.widget.Button;
import com.android.settings.R;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.core.lifecycle.LifecycleObserver;
import com.android.settings.core.lifecycle.events.OnResume;
import com.android.settingslib.applications.ApplicationsState;
/**
* Controller to control the uninstall button and forcestop button
*/
//TODO(b/35810915): refine the button logic and make InstalledAppDetails use this controller
//TODO(b/35810915): add test for this file
public class AppButtonsPreferenceController extends PreferenceController implements
LifecycleObserver, OnResume {
private static final String KEY_ACTION_BUTTONS = "action_buttons";
private ApplicationsState.AppEntry mAppEntry;
private LayoutPreference mButtonsPref;
private Button mForceStopButton;
private Button mUninstallButton;
public AppButtonsPreferenceController(Activity activity, Lifecycle lifecycle,
String packageName) {
super(activity);
lifecycle.addObserver(this);
ApplicationsState state = ApplicationsState.getInstance(activity.getApplication());
if (packageName != null) {
mAppEntry = state.getEntry(packageName, UserHandle.myUserId());
}
}
@Override
public boolean isAvailable() {
return mAppEntry != null;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (isAvailable()) {
mButtonsPref = (LayoutPreference) screen.findPreference(KEY_ACTION_BUTTONS);
mUninstallButton = (Button) mButtonsPref.findViewById(R.id.left_button);
mUninstallButton.setText(R.string.uninstall_text);
mForceStopButton = (Button) mButtonsPref.findViewById(R.id.right_button);
mForceStopButton.setText(R.string.force_stop);
}
}
@Override
public String getPreferenceKey() {
return KEY_ACTION_BUTTONS;
}
@Override
public void onResume() {
//TODO(b/35810915): check and update the status of buttons
}
}

View File

@@ -23,6 +23,8 @@ import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.core.PreferenceController;
/**
@@ -56,8 +58,12 @@ public class BackgroundActivityPreferenceController extends PreferenceController
if (mode == AppOpsManager.MODE_ERRORED) {
preference.setEnabled(false);
preference.setSummary(R.string.background_activity_summary_disabled);
} else {
((SwitchPreference) preference).setChecked(mode != AppOpsManager.MODE_IGNORED);
final boolean checked = mode != AppOpsManager.MODE_IGNORED;
((SwitchPreference) preference).setChecked(checked);
preference.setSummary(checked ? R.string.background_activity_summary_on
: R.string.background_activity_summary_off);
}
}

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.fuelgauge;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.SettingsActivity;
import com.android.settings.applications.ManageApplications;
import com.android.settings.core.PreferenceController;
/**
* Controller that jumps to high power optimization fragment
*/
public class BatteryOptimizationPreferenceController extends PreferenceController {
private static final String KEY_BACKGROUND_ACTIVITY = "battery_optimization";
private Fragment mFragment;
private SettingsActivity mSettingsActivity;
public BatteryOptimizationPreferenceController(SettingsActivity settingsActivity,
Fragment fragment) {
super(settingsActivity);
mFragment = fragment;
mSettingsActivity = settingsActivity;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return KEY_BACKGROUND_ACTIVITY;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) {
return false;
}
Bundle args = new Bundle(1);
args.putString(ManageApplications.EXTRA_CLASSNAME,
Settings.HighPowerApplicationsActivity.class.getName());
mSettingsActivity.startPreferencePanel(mFragment, ManageApplications.class.getName(), args,
R.string.high_power_apps, null, null, 0);
return true;
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.fuelgauge;
import android.annotation.IntDef;
import android.os.BatteryStats;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Utils for battery operation
*/
public class BatteryUtils {
@Retention(RetentionPolicy.SOURCE)
@IntDef({StatusType.FOREGROUND,
StatusType.BACKGROUND,
StatusType.ALL
})
public @interface StatusType {
int FOREGROUND = 0;
int BACKGROUND = 1;
int ALL = 2;
}
public static long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid,
int which) {
if (uid == null) {
return 0;
}
switch (type) {
case StatusType.FOREGROUND:
return getProcessForegroundTimeMs(uid, which);
case StatusType.BACKGROUND:
return getProcessBackgroundTimeMs(uid, which);
case StatusType.ALL:
return getProcessForegroundTimeMs(uid, which)
+ getProcessBackgroundTimeMs(uid, which);
}
return 0;
}
private static long getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which) {
final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
final long timeUs = uid.getProcessStateTime(
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, rawRealTimeUs, which);
return convertUsToMs(timeUs);
}
private static long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) {
final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP,
BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING,
BatteryStats.Uid.PROCESS_STATE_FOREGROUND};
long timeUs = 0;
for (int type : foregroundTypes) {
timeUs += uid.getProcessStateTime(type, rawRealTimeUs, which);
}
return convertUsToMs(timeUs);
}
private static long convertUsToMs(long timeUs) {
return timeUs / 1000;
}
private static long convertMsToUs(long timeMs) {
return timeMs * 1000;
}
}

View File

@@ -63,6 +63,10 @@ public class PowerGaugePreference extends TintablePreference {
notifyChanged();
}
public String getPercent() {
return mProgress.toString();
}
BatteryEntry getInfo() {
return mInfo;
}

View File

@@ -69,6 +69,7 @@ import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
// TODO(b/35810915): Delete this page once ag/1971493 is done.
public class PowerUsageDetail extends PowerUsageBase implements Button.OnClickListener {
// Note: Must match the sequence of the DrainType

View File

@@ -165,8 +165,8 @@ public class PowerUsageSummary extends PowerUsageBase {
}
PowerGaugePreference pgp = (PowerGaugePreference) preference;
BatteryEntry entry = pgp.getInfo();
PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), this,
mStatsHelper, mStatsType, entry, true, true);
AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
this, mStatsHelper, mStatsType, entry, pgp.getPercent());
return super.onPreferenceTreeClick(preference);
}