Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a276b8d5 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "tip_new_page-pi-dev" into pi-dev

* changes:
  Hook up the new page to PowerUsageSummary
  Create new PowerUsageAdvanced page
  Change current PowerUsageAdvanced to legacy code
parents f178692b 094278e6
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@
        android:key="battery_graph"/>

    <PreferenceCategory
        android:key="battery_usage_list"
        android:title="@string/battery_detail_since_full_charge"/>
        android:key="app_list"
        android:title="@string/power_usage_list_summary"/>

</PreferenceScreen>
+31 −0
Original line number Diff line number Diff line
<?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:settings="http://schemas.android.com/apk/res-auto"
    android:key="power_usage_advanced_screen_legacy"
    android:title="@string/advanced_battery_title"
    settings:keywords="@string/keywords_battery">

    <com.android.settings.fuelgauge.BatteryHistoryPreference
        android:key="battery_graph_legacy"/>

    <PreferenceCategory
        android:key="battery_usage_list_legacy"
        android:title="@string/battery_detail_since_full_charge"/>

</PreferenceScreen>
+0 −4
Original line number Diff line number Diff line
@@ -66,8 +66,4 @@

    </PreferenceCategory>

    <PreferenceCategory
        android:key="app_list"
        android:title="@string/power_usage_list_summary"/>

</PreferenceScreen>
+11 −5
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
@@ -81,7 +82,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
    private Context mPrefContext;
    SparseArray<List<Anomaly>> mAnomalySparseArray;

    private Handler mHandler = new Handler() {
    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
@@ -149,7 +150,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro

    @Override
    public boolean isAvailable() {
        return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST);
        return true;
    }

    @Override
@@ -186,12 +187,17 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
        }
    }

    public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps,
            CharSequence timeSequence) {
    public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps) {
        if (!isAvailable()) {
            return;
        }

        mBatteryStatsHelper = statsHelper;
        final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(
                mBatteryStatsHelper, System.currentTimeMillis());
        final CharSequence timeSequence = StringUtil.formatRelativeTime(mContext,
                lastFullChargeTime,
                false);
        final int resId = showAllApps ? R.string.power_usage_list_summary_device
                : R.string.power_usage_list_summary;
        mAppListGroup.setTitle(TextUtils.expandTemplate(mContext.getText(resId), timeSequence));
+69 −368
Original line number Diff line number Diff line
@@ -13,134 +13,58 @@
 */
package com.android.settings.fuelgauge;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.fuelgauge.PowerUsageAdvanced.PowerUsageData.UsageType;
import com.android.settings.SettingsActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;

import com.android.settingslib.utils.StringUtil;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PowerUsageAdvanced extends PowerUsageBase {
    private static final String TAG = "AdvancedBatteryUsage";
    private static final String KEY_BATTERY_GRAPH = "battery_graph";
    private static final String KEY_BATTERY_USAGE_LIST = "battery_usage_list";
    private static final int STATUS_TYPE = BatteryStats.STATS_SINCE_CHARGED;

    private static final String KEY_APP_LIST = "app_list";
    private static final String KEY_SHOW_ALL_APPS = "show_all_apps";
    @VisibleForTesting
    final int[] mUsageTypes = {
            UsageType.WIFI,
            UsageType.CELL,
            UsageType.SYSTEM,
            UsageType.BLUETOOTH,
            UsageType.USER,
            UsageType.IDLE,
            UsageType.APP,
            UsageType.UNACCOUNTED,
            UsageType.OVERCOUNTED};
    static final int MENU_TOGGLE_APPS = Menu.FIRST + 1;

    @VisibleForTesting BatteryHistoryPreference mHistPref;
    @VisibleForTesting PreferenceGroup mUsageListGroup;
    @VisibleForTesting
    BatteryHistoryPreference mHistPref;
    private BatteryUtils mBatteryUtils;
    private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
    private PackageManager mPackageManager;
    private UserManager mUserManager;
    private Map<Integer, PowerUsageData> mBatteryDataMap;

    Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BatteryEntry.MSG_UPDATE_NAME_ICON:
                    final int dischargeAmount = mStatsHelper.getStats().getDischargeAmount(
                            STATUS_TYPE);
                    final double totalPower = mStatsHelper.getTotalPower();
                    final BatteryEntry entry = (BatteryEntry) msg.obj;
                    final int usageType = extractUsageType(entry.sipper);

                    PowerUsageData usageData = mBatteryDataMap.get(usageType);
                    Preference pref = findPreference(String.valueOf(usageType));
                    if (pref != null && usageData != null) {
                        updateUsageDataSummary(usageData, totalPower, dischargeAmount);
                        pref.setSummary(usageData.summary);
                    }
                    break;
                case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
                    Activity activity = getActivity();
                    if (activity != null) {
                        activity.reportFullyDrawn();
                    }
                    break;
            }
            super.handleMessage(msg);
        }
    };
    private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
    @VisibleForTesting
    boolean mShowAllApps = false;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        final Context context = getContext();

        mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
        mUsageListGroup = (PreferenceGroup) findPreference(KEY_BATTERY_USAGE_LIST);

        final Context context = getContext();
        mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
                .getPowerUsageFeatureProvider(context);
        mPackageManager = context.getPackageManager();
        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
        mBatteryUtils = BatteryUtils.getInstance(context);

        // init the summary so other preferences won't have unnecessary move
        updateHistPrefSummary(context);
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        BatteryEntry.stopRequestQueue();
        mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
        super.onPause();
        restoreSavedInstance(icicle);
    }

    @Override
@@ -166,24 +90,63 @@ public class PowerUsageAdvanced extends PowerUsageBase {
        return R.xml.power_usage_advanced;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        menu.add(Menu.NONE, MENU_TOGGLE_APPS, Menu.NONE,
                mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case MENU_TOGGLE_APPS:
                mShowAllApps = !mShowAllApps;
                item.setTitle(mShowAllApps ? R.string.hide_extra_apps : R.string.show_all_apps);
                mMetricsFeatureProvider.action(getContext(),
                        MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE,
                        mShowAllApps);
                restartBatteryStatsLoader();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @VisibleForTesting
    void restoreSavedInstance(Bundle savedInstance) {
        if (savedInstance != null) {
            mShowAllApps = savedInstance.getBoolean(KEY_SHOW_ALL_APPS, false);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(KEY_SHOW_ALL_APPS, mShowAllApps);
    }

    @Override
    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
        return null;
        final List<AbstractPreferenceController> controllers = new ArrayList<>();

        mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context,
                KEY_APP_LIST, getLifecycle(), (SettingsActivity) getActivity(), this);
        controllers.add(mBatteryAppListPreferenceController);

        return controllers;
    }

    @Override
    protected void refreshUi() {
        final long startTime = System.currentTimeMillis();
        final Context context = getContext();
        if (context == null) {
            return;
        }
        updatePreference(mHistPref);
        refreshPowerUsageDataList(mStatsHelper, mUsageListGroup);
        updateHistPrefSummary(context);

        BatteryEntry.startRequestQueue();
        BatteryUtils.logRuntime(TAG, "refreshUI", startTime);
        mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper, mShowAllApps);
    }

    private void updateHistPrefSummary(Context context) {
@@ -199,278 +162,6 @@ public class PowerUsageAdvanced extends PowerUsageBase {
        }
    }

    @VisibleForTesting
    void refreshPowerUsageDataList(BatteryStatsHelper statsHelper,
            PreferenceGroup preferenceGroup) {
        List<PowerUsageData> dataList = parsePowerUsageData(statsHelper);
        preferenceGroup.removeAll();
        for (int i = 0, size = dataList.size(); i < size; i++) {
            final PowerUsageData batteryData = dataList.get(i);
            if (shouldHideCategory(batteryData)) {
                continue;
            }
            final PowerGaugePreference pref = new PowerGaugePreference(getPrefContext());

            pref.setKey(String.valueOf(batteryData.usageType));
            pref.setTitle(batteryData.titleResId);
            pref.setSummary(batteryData.summary);
            pref.setPercent(batteryData.percentage);
            pref.setSelectable(false);
            preferenceGroup.addPreference(pref);
        }
    }

    @VisibleForTesting
    @UsageType
    int extractUsageType(BatterySipper sipper) {
        final DrainType drainType = sipper.drainType;
        final int uid = sipper.getUid();

        if (drainType == DrainType.WIFI) {
            return UsageType.WIFI;
        } else if (drainType == DrainType.BLUETOOTH) {
            return UsageType.BLUETOOTH;
        } else if (drainType == DrainType.IDLE) {
            return UsageType.IDLE;
        } else if (drainType == DrainType.USER) {
            return UsageType.USER;
        } else if (drainType == DrainType.CELL) {
            return UsageType.CELL;
        } else if (drainType == DrainType.UNACCOUNTED) {
            return UsageType.UNACCOUNTED;
        } else if (drainType == DrainType.OVERCOUNTED) {
            return UsageType.OVERCOUNTED;
        } else if (mPowerUsageFeatureProvider.isTypeSystem(sipper)
                || mPowerUsageFeatureProvider.isTypeService(sipper)) {
            return UsageType.SYSTEM;
        } else {
            return UsageType.APP;
        }
    }

    @VisibleForTesting
    boolean shouldHideCategory(PowerUsageData powerUsageData) {
        return powerUsageData.usageType == UsageType.UNACCOUNTED
                || powerUsageData.usageType == UsageType.OVERCOUNTED
                || (powerUsageData.usageType == UsageType.USER && isSingleNormalUser())
                || (powerUsageData.usageType == UsageType.CELL
                && !DataUsageUtils.hasMobileData(getContext()));
    }

    @VisibleForTesting
    boolean shouldShowBatterySipper(BatterySipper batterySipper) {
        return batterySipper.drainType != DrainType.SCREEN;
    }

    @VisibleForTesting
    List<PowerUsageData> parsePowerUsageData(BatteryStatsHelper statusHelper) {
        final List<BatterySipper> batterySippers = statusHelper.getUsageList();
        final Map<Integer, PowerUsageData> batteryDataMap = new HashMap<>();

        for (final @UsageType Integer type : mUsageTypes) {
            batteryDataMap.put(type, new PowerUsageData(type));
        }

        // Accumulate power usage based on usage type
        for (final BatterySipper sipper : batterySippers) {
            sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid());
            final PowerUsageData usageData = batteryDataMap.get(extractUsageType(sipper));
            usageData.totalPowerMah += sipper.totalPowerMah;
            if (sipper.drainType == DrainType.APP && sipper.usageTimeMs != 0) {
                sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
                        BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, STATUS_TYPE);
            }
            usageData.totalUsageTimeMs += sipper.usageTimeMs;
            if (shouldShowBatterySipper(sipper)) {
                usageData.usageList.add(sipper);
            }
        }

        final List<PowerUsageData> batteryDataList = new ArrayList<>(batteryDataMap.values());
        final int dischargeAmount = statusHelper.getStats().getDischargeAmount(STATUS_TYPE);
        final double totalPower = statusHelper.getTotalPower();
        final double hiddenPower = calculateHiddenPower(batteryDataList);
        for (final PowerUsageData usageData : batteryDataList) {
            usageData.percentage = mBatteryUtils.calculateBatteryPercent(usageData.totalPowerMah,
                    totalPower, hiddenPower, dischargeAmount);
            updateUsageDataSummary(usageData, totalPower, dischargeAmount);
        }

        Collections.sort(batteryDataList);

        mBatteryDataMap = batteryDataMap;
        return batteryDataList;
    }

    @VisibleForTesting
    double calculateHiddenPower(List<PowerUsageData> batteryDataList) {
        for (final PowerUsageData usageData : batteryDataList) {
            if (usageData.usageType == UsageType.UNACCOUNTED) {
                return usageData.totalPowerMah;
            }
        }

        return 0;
    }

    @VisibleForTesting
    void updateUsageDataSummary(PowerUsageData usageData, double totalPower, int dischargeAmount) {
        if (shouldHideSummary(usageData)) {
            return;
        }
        if (usageData.usageList.size() <= 1) {
            CharSequence timeSequence = StringUtil.formatElapsedTime(getContext(),
                    usageData.totalUsageTimeMs, false);
            usageData.summary = usageData.usageType == UsageType.IDLE ? timeSequence
                    : TextUtils.expandTemplate(getText(R.string.battery_used_for), timeSequence);
        } else {
            BatterySipper sipper = findBatterySipperWithMaxBatteryUsage(usageData.usageList);
            BatteryEntry batteryEntry = new BatteryEntry(getContext(), mHandler, mUserManager,
                    sipper);
            final double percentage = (sipper.totalPowerMah / totalPower) * dischargeAmount;
            usageData.summary = getString(R.string.battery_used_by,
                    Utils.formatPercentage(percentage, true), batteryEntry.name);
        }
    }

    @VisibleForTesting
    boolean shouldHideSummary(PowerUsageData powerUsageData) {
        @UsageType final int usageType = powerUsageData.usageType;

        return usageType == UsageType.CELL
                || usageType == UsageType.BLUETOOTH
                || usageType == UsageType.WIFI
                || usageType == UsageType.APP
                || usageType == UsageType.SYSTEM;
    }

    @VisibleForTesting
    BatterySipper findBatterySipperWithMaxBatteryUsage(List<BatterySipper> usageList) {
        BatterySipper sipper = usageList.get(0);
        for (int i = 1, size = usageList.size(); i < size; i++) {
            final BatterySipper comparedSipper = usageList.get(i);
            if (comparedSipper.totalPowerMah > sipper.totalPowerMah) {
                sipper = comparedSipper;
            }
        }

        return sipper;
    }

    @VisibleForTesting
    void setPackageManager(PackageManager packageManager) {
        mPackageManager = packageManager;
    }

    @VisibleForTesting
    void setPowerUsageFeatureProvider(PowerUsageFeatureProvider provider) {
        mPowerUsageFeatureProvider = provider;
    }
    @VisibleForTesting
    void setUserManager(UserManager userManager) {
        mUserManager = userManager;
    }
    @VisibleForTesting
    void setBatteryUtils(BatteryUtils batteryUtils) {
        mBatteryUtils = batteryUtils;
    }

    @VisibleForTesting
    boolean isSingleNormalUser() {
        int count = 0;
        for (UserInfo userInfo : mUserManager.getUsers()) {
            if (userInfo.isEnabled() && !userInfo.isManagedProfile()) {
                count++;
            }
        }

        return count == 1;
    }

    /**
     * Class that contains data used in {@link PowerGaugePreference}.
     */
    @VisibleForTesting
    static class PowerUsageData implements Comparable<PowerUsageData> {

        @Retention(RetentionPolicy.SOURCE)
        @IntDef({UsageType.APP,
                UsageType.WIFI,
                UsageType.CELL,
                UsageType.SYSTEM,
                UsageType.BLUETOOTH,
                UsageType.USER,
                UsageType.IDLE,
                UsageType.UNACCOUNTED,
                UsageType.OVERCOUNTED})
        public @interface UsageType {
            int APP = 0;
            int WIFI = 1;
            int CELL = 2;
            int SYSTEM = 3;
            int BLUETOOTH = 4;
            int USER = 5;
            int IDLE = 6;
            int UNACCOUNTED = 7;
            int OVERCOUNTED = 8;
        }

        @StringRes
        public int titleResId;
        public CharSequence summary;
        public double percentage;
        public double totalPowerMah;
        public long totalUsageTimeMs;
        @ColorInt
        public int iconColor;
        @UsageType
        public int usageType;
        public List<BatterySipper> usageList;

        public PowerUsageData(@UsageType int usageType) {
            this(usageType, 0);
        }

        public PowerUsageData(@UsageType int usageType, double totalPower) {
            this.usageType = usageType;
            totalPowerMah = 0;
            totalUsageTimeMs = 0;
            titleResId = getTitleResId(usageType);
            totalPowerMah = totalPower;
            usageList = new ArrayList<>();
        }

        private int getTitleResId(@UsageType int usageType) {
            switch (usageType) {
                case UsageType.WIFI:
                    return R.string.power_wifi;
                case UsageType.CELL:
                    return R.string.power_cell;
                case UsageType.SYSTEM:
                    return R.string.power_system;
                case UsageType.BLUETOOTH:
                    return R.string.power_bluetooth;
                case UsageType.USER:
                    return R.string.power_user;
                case UsageType.IDLE:
                    return R.string.power_idle;
                case UsageType.UNACCOUNTED:
                    return R.string.power_unaccounted;
                case UsageType.OVERCOUNTED:
                    return R.string.power_overcounted;
                case UsageType.APP:
                default:
                    return R.string.power_apps;
            }
        }

        @Override
        public int compareTo(@NonNull PowerUsageData powerUsageData) {
            final int diff = Double.compare(powerUsageData.totalPowerMah, totalPowerMah);
            return diff != 0 ? diff : usageType - powerUsageData.usageType;
        }
    }

    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
            new BaseSearchIndexProvider() {
                @Override
@@ -480,6 +171,16 @@ public class PowerUsageAdvanced extends PowerUsageBase {
                    sir.xmlResId = R.xml.power_usage_advanced;
                    return Arrays.asList(sir);
                }

                @Override
                public List<AbstractPreferenceController> createPreferenceControllers(
                        Context context) {
                    final List<AbstractPreferenceController> controllers = new ArrayList<>();
                    controllers.add(new BatteryAppListPreferenceController(context,
                            KEY_APP_LIST, null /* lifecycle */, null /* activity */,
                            null /* fragment */));
                    return controllers;
                }
            };

}
Loading