Support App and System tabs for battery usage breakdown.
design_doc: go/usage-frontend-dd screen_record: https://drive.google.com/file/d/1I8dnoMf7y9KUg0eVeqLJpHqGGOtaX6_0/view?usp=share_link&resourcekey=0-Mte0-LKN1LULRssg9t7zGg Bug b/260786962 is also fixed in this cl. Bug: 258123381 Bug: 260786962 Fix: 258123381 Fix: 260786962 Test: manual Change-Id: Ifd337331d02dc10c3234c594b2e142bcd459c00f
This commit is contained in:
@@ -1,46 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2021 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/expand_title"
|
|
||||||
android:layout_width="0px"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:paddingEnd="4dp"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
style="@style/PreferenceCategoryTitleTextStyle"/>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/expand_icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:src="@drawable/ic_settings_expand_more"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
37
res/layout/preference_tab.xml
Normal file
37
res/layout/preference_tab.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:theme="@style/Theme.TabTheme"
|
||||||
|
android:id="@+id/tab_container"
|
||||||
|
android:clipToPadding="true"
|
||||||
|
android:clipChildren="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tabs"
|
||||||
|
style="@style/SettingsLibTabsStyle" />
|
||||||
|
|
||||||
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
android:id="@+id/view_pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@@ -4993,6 +4993,14 @@
|
|||||||
<string name="daily_battery_usage_chart">Daily battery usage chart</string>
|
<string name="daily_battery_usage_chart">Daily battery usage chart</string>
|
||||||
<!-- [CHAR_LIMIT=NONE] Accessibility content description for hourly battery chart view. -->
|
<!-- [CHAR_LIMIT=NONE] Accessibility content description for hourly battery chart view. -->
|
||||||
<string name="hourly_battery_usage_chart">Hourly battery usage chart</string>
|
<string name="hourly_battery_usage_chart">Hourly battery usage chart</string>
|
||||||
|
<!-- [CHAR_LIMIT=NONE] Battery usage breakdown title since last full charge -->
|
||||||
|
<string name="battery_usage_breakdown_title_since_last_full_charge">Usage proportional breakdown since last full charge</string>
|
||||||
|
<!-- [CHAR_LIMIT=NONE] Battery usage breakdown title for a selected slot -->
|
||||||
|
<string name="battery_usage_breakdown_title_for_slot">Usage proportional breakdown for <xliff:g id="slot">%s</xliff:g></string>
|
||||||
|
<!-- [CHAR_LIMIT=NONE] The tab title in the battery usage breakdown. -->
|
||||||
|
<string name="battery_usage_app_tab">App</string>
|
||||||
|
<!-- [CHAR_LIMIT=NONE] The tab title in the battery usage breakdown. -->
|
||||||
|
<string name="battery_usage_system_tab">System</string>
|
||||||
<!-- Process Stats strings -->
|
<!-- Process Stats strings -->
|
||||||
<skip />
|
<skip />
|
||||||
|
|
||||||
|
@@ -22,14 +22,30 @@
|
|||||||
settings:keywords="@string/keywords_battery_usage">
|
settings:keywords="@string/keywords_battery_usage">
|
||||||
|
|
||||||
<com.android.settings.fuelgauge.batteryusage.BatteryHistoryPreference
|
<com.android.settings.fuelgauge.batteryusage.BatteryHistoryPreference
|
||||||
android:key="battery_graph"/>
|
android:key="battery_chart"
|
||||||
|
settings:controller=
|
||||||
|
"com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController" />
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="app_list"/>
|
android:key="battery_usage_breakdown"
|
||||||
|
settings:controller=
|
||||||
|
"com.android.settings.fuelgauge.batteryusage.BatteryUsageBreakdownController"
|
||||||
|
settings:isPreferenceVisible="false">
|
||||||
|
|
||||||
<com.android.settingslib.widget.FooterPreference
|
<com.android.settings.fuelgauge.batteryusage.TabPreference
|
||||||
android:key="battery_graph_footer"
|
android:key="battery_usage_tab"
|
||||||
android:title="@string/battery_usage_screen_footer"
|
settings:isPreferenceVisible="false" />
|
||||||
android:selectable="false"
|
|
||||||
settings:searchable="false"/>
|
<PreferenceCategory
|
||||||
|
android:key="app_list"
|
||||||
|
settings:isPreferenceVisible="false" />
|
||||||
|
|
||||||
|
<com.android.settingslib.widget.FooterPreference
|
||||||
|
android:key="battery_usage_footer"
|
||||||
|
android:selectable="false"
|
||||||
|
android:title="@string/battery_usage_screen_footer"
|
||||||
|
settings:isPreferenceVisible="false"
|
||||||
|
settings:searchable="false" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
@@ -21,30 +21,22 @@ import android.animation.AnimatorListenerAdapter;
|
|||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceGroup;
|
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
|
||||||
import com.android.settings.core.PreferenceControllerMixin;
|
import com.android.settings.core.PreferenceControllerMixin;
|
||||||
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
@@ -54,73 +46,68 @@ import com.android.settingslib.core.lifecycle.events.OnCreate;
|
|||||||
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
||||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
||||||
import com.android.settingslib.utils.StringUtil;
|
|
||||||
import com.android.settingslib.widget.FooterPreference;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/** Controls the update for chart graph and the list items. */
|
/** Controls the update for chart graph and the list items. */
|
||||||
public class BatteryChartPreferenceController extends AbstractPreferenceController
|
public class BatteryChartPreferenceController extends AbstractPreferenceController
|
||||||
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
|
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
|
||||||
OnSaveInstanceState, OnResume, ExpandDividerPreference.OnExpandListener {
|
OnSaveInstanceState, OnResume {
|
||||||
private static final String TAG = "BatteryChartPreferenceController";
|
private static final String TAG = "BatteryChartPreferenceController";
|
||||||
private static final String KEY_FOOTER_PREF = "battery_graph_footer";
|
private static final String PREFERENCE_KEY = "battery_chart";
|
||||||
private static final String PACKAGE_NAME_NONE = "none";
|
|
||||||
private static final int ENABLED_ICON_ALPHA = 255;
|
|
||||||
private static final int DISABLED_ICON_ALPHA = 255 / 3;
|
|
||||||
|
|
||||||
private static final long FADE_IN_ANIMATION_DURATION = 400L;
|
private static final long FADE_IN_ANIMATION_DURATION = 400L;
|
||||||
private static final long FADE_OUT_ANIMATION_DURATION = 200L;
|
private static final long FADE_OUT_ANIMATION_DURATION = 200L;
|
||||||
|
|
||||||
// Keys for bundle instance to restore configurations.
|
// Keys for bundle instance to restore configurations.
|
||||||
private static final String KEY_EXPAND_SYSTEM_INFO = "expand_system_info";
|
|
||||||
private static final String KEY_DAILY_CHART_INDEX = "daily_chart_index";
|
private static final String KEY_DAILY_CHART_INDEX = "daily_chart_index";
|
||||||
private static final String KEY_HOURLY_CHART_INDEX = "hourly_chart_index";
|
private static final String KEY_HOURLY_CHART_INDEX = "hourly_chart_index";
|
||||||
|
|
||||||
private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
|
private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
|
||||||
|
|
||||||
@VisibleForTesting
|
/**
|
||||||
Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
|
* A callback listener for battery usage is updated.
|
||||||
|
* This happens when battery usage data is ready or the selected index is changed.
|
||||||
|
*/
|
||||||
|
public interface OnBatteryUsageUpdatedListener {
|
||||||
|
/**
|
||||||
|
* The callback function for battery usage is updated.
|
||||||
|
* @param slotUsageData The battery usage diff data for the selected slot. This is used in
|
||||||
|
* the app list.
|
||||||
|
* @param slotTimestamp The selected slot timestamp information. This is used in the battery
|
||||||
|
* usage breakdown category.
|
||||||
|
* @param isAllUsageDataEmpty Whether all the battery usage data is null or empty. This is
|
||||||
|
* used when showing the footer.
|
||||||
|
*/
|
||||||
|
void onBatteryUsageUpdated(
|
||||||
|
BatteryDiffData slotUsageData, String slotTimestamp, boolean isAllUsageDataEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Context mPrefContext;
|
Context mPrefContext;
|
||||||
@VisibleForTesting
|
|
||||||
BatteryUtils mBatteryUtils;
|
|
||||||
@VisibleForTesting
|
|
||||||
PreferenceGroup mAppListPrefGroup;
|
|
||||||
@VisibleForTesting
|
|
||||||
ExpandDividerPreference mExpandDividerPreference;
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean mIsExpanded = false;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
BatteryChartView mDailyChartView;
|
BatteryChartView mDailyChartView;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
BatteryChartView mHourlyChartView;
|
BatteryChartView mHourlyChartView;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
@VisibleForTesting
|
||||||
|
Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
|
||||||
|
|
||||||
private boolean mIs24HourFormat;
|
private boolean mIs24HourFormat;
|
||||||
private boolean mIsFooterPrefAdded = false;
|
|
||||||
private boolean mHourlyChartVisible = true;
|
private boolean mHourlyChartVisible = true;
|
||||||
private View mBatteryChartViewGroup;
|
private View mBatteryChartViewGroup;
|
||||||
private View mCategoryTitleView;
|
|
||||||
private PreferenceScreen mPreferenceScreen;
|
|
||||||
private FooterPreference mFooterPreference;
|
|
||||||
private TextView mChartSummaryTextView;
|
private TextView mChartSummaryTextView;
|
||||||
private BatteryChartViewModel mDailyViewModel;
|
private BatteryChartViewModel mDailyViewModel;
|
||||||
private List<BatteryChartViewModel> mHourlyViewModels;
|
private List<BatteryChartViewModel> mHourlyViewModels;
|
||||||
|
private OnBatteryUsageUpdatedListener mOnBatteryUsageUpdatedListener;
|
||||||
|
|
||||||
private final String mPreferenceKey;
|
|
||||||
private final SettingsActivity mActivity;
|
private final SettingsActivity mActivity;
|
||||||
private final InstrumentedPreferenceFragment mFragment;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
|
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
|
||||||
@@ -135,18 +122,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
final HourlyChartLabelTextGenerator mHourlyChartLabelTextGenerator =
|
final HourlyChartLabelTextGenerator mHourlyChartLabelTextGenerator =
|
||||||
new HourlyChartLabelTextGenerator();
|
new HourlyChartLabelTextGenerator();
|
||||||
|
|
||||||
// Preference cache to avoid create new instance each time.
|
|
||||||
@VisibleForTesting
|
|
||||||
final Map<String, Preference> mPreferenceCache = new HashMap<>();
|
|
||||||
|
|
||||||
public BatteryChartPreferenceController(
|
public BatteryChartPreferenceController(
|
||||||
Context context, String preferenceKey,
|
Context context, Lifecycle lifecycle, SettingsActivity activity) {
|
||||||
Lifecycle lifecycle, SettingsActivity activity,
|
|
||||||
InstrumentedPreferenceFragment fragment) {
|
|
||||||
super(context);
|
super(context);
|
||||||
mActivity = activity;
|
mActivity = activity;
|
||||||
mFragment = fragment;
|
|
||||||
mPreferenceKey = preferenceKey;
|
|
||||||
mIs24HourFormat = DateFormat.is24HourFormat(context);
|
mIs24HourFormat = DateFormat.is24HourFormat(context);
|
||||||
mMetricsFeatureProvider =
|
mMetricsFeatureProvider =
|
||||||
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||||
@@ -164,10 +143,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
|
savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
|
||||||
mHourlyChartIndex =
|
mHourlyChartIndex =
|
||||||
savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
|
savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
|
||||||
mIsExpanded =
|
Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d",
|
||||||
savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
|
mDailyChartIndex, mHourlyChartIndex));
|
||||||
Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
|
|
||||||
mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,9 +168,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
}
|
}
|
||||||
savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
|
savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
|
||||||
savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
|
savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
|
||||||
savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
|
Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d",
|
||||||
Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
|
mDailyChartIndex, mHourlyChartIndex));
|
||||||
mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -202,25 +178,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
BatteryDiffEntry.clearCache();
|
BatteryDiffEntry.clearCache();
|
||||||
}
|
}
|
||||||
mHandler.removeCallbacksAndMessages(/*token=*/ null);
|
mHandler.removeCallbacksAndMessages(/*token=*/ null);
|
||||||
mPreferenceCache.clear();
|
|
||||||
if (mAppListPrefGroup != null) {
|
|
||||||
mAppListPrefGroup.removeAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void displayPreference(PreferenceScreen screen) {
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
super.displayPreference(screen);
|
super.displayPreference(screen);
|
||||||
mPreferenceScreen = screen;
|
|
||||||
mPrefContext = screen.getContext();
|
mPrefContext = screen.getContext();
|
||||||
mAppListPrefGroup = screen.findPreference(mPreferenceKey);
|
|
||||||
mAppListPrefGroup.setOrderingAsAdded(false);
|
|
||||||
mAppListPrefGroup.setTitle("");
|
|
||||||
mFooterPreference = screen.findPreference(KEY_FOOTER_PREF);
|
|
||||||
// Removes footer first until usage data is loaded to avoid flashing.
|
|
||||||
if (mFooterPreference != null) {
|
|
||||||
screen.removePreference(mFooterPreference);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -230,42 +193,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPreferenceKey() {
|
public String getPreferenceKey() {
|
||||||
return mPreferenceKey;
|
return PREFERENCE_KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
void setOnBatteryUsageUpdatedListener(OnBatteryUsageUpdatedListener listener) {
|
||||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
mOnBatteryUsageUpdatedListener = listener;
|
||||||
if (!(preference instanceof PowerGaugePreference)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
|
|
||||||
final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
|
|
||||||
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
|
|
||||||
final String packageName = histEntry.mPackageName;
|
|
||||||
final boolean isAppEntry = histEntry.isAppEntry();
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
/* attribution */ SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
/* action */ isAppEntry
|
|
||||||
? SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM
|
|
||||||
: SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
|
|
||||||
/* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName,
|
|
||||||
(int) Math.round(diffEntry.getPercentOfTotal()));
|
|
||||||
Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
|
|
||||||
diffEntry.getAppLabel(), histEntry.getKey(), histEntry.mPackageName));
|
|
||||||
AdvancedPowerUsageDetail.startBatteryDetailPage(
|
|
||||||
mActivity, mFragment, diffEntry, powerPref.getPercent(), getSlotInformation());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExpand(boolean isExpanded) {
|
|
||||||
mIsExpanded = isExpanded;
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
mPrefContext,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
|
|
||||||
isExpanded);
|
|
||||||
refreshExpandUi();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBatteryHistoryMap(
|
void setBatteryHistoryMap(
|
||||||
@@ -339,7 +271,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
mDailyChartIndex = trapezoidIndex;
|
mDailyChartIndex = trapezoidIndex;
|
||||||
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
refreshUi();
|
refreshUi();
|
||||||
requestAccessibilityFocusForCategoryTitle(mDailyChartView);
|
|
||||||
mMetricsFeatureProvider.action(
|
mMetricsFeatureProvider.action(
|
||||||
mPrefContext,
|
mPrefContext,
|
||||||
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
|
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
|
||||||
@@ -355,7 +286,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex);
|
Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex);
|
||||||
mHourlyChartIndex = trapezoidIndex;
|
mHourlyChartIndex = trapezoidIndex;
|
||||||
refreshUi();
|
refreshUi();
|
||||||
requestAccessibilityFocusForCategoryTitle(mHourlyChartView);
|
|
||||||
mMetricsFeatureProvider.action(
|
mMetricsFeatureProvider.action(
|
||||||
mPrefContext,
|
mPrefContext,
|
||||||
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
|
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
|
||||||
@@ -385,14 +315,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mHandler.post(() -> {
|
|
||||||
final long start = System.currentTimeMillis();
|
if (mOnBatteryUsageUpdatedListener != null) {
|
||||||
removeAndCacheAllPrefs();
|
final BatteryDiffData slotUsageData =
|
||||||
addAllPreferences();
|
mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
|
||||||
refreshCategoryTitle();
|
mOnBatteryUsageUpdatedListener.onBatteryUsageUpdated(
|
||||||
Log.d(TAG, String.format("refreshUi is finished in %d/ms",
|
slotUsageData, getSlotInformation(), isBatteryUsageMapNullOrEmpty());
|
||||||
(System.currentTimeMillis() - start)));
|
}
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,9 +343,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
mDailyChartView.setVisibility(View.GONE);
|
mDailyChartView.setVisibility(View.GONE);
|
||||||
mHourlyChartView.setVisibility(View.VISIBLE);
|
mHourlyChartView.setVisibility(View.VISIBLE);
|
||||||
mHourlyChartView.setViewModel(null);
|
mHourlyChartView.setViewModel(null);
|
||||||
removeAndCacheAllPrefs();
|
|
||||||
addFooterPreferenceIfNeeded(false);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -452,154 +378,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAllPreferences() {
|
|
||||||
final BatteryDiffData batteryDiffData =
|
|
||||||
mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
|
|
||||||
addFooterPreferenceIfNeeded(batteryDiffData != null
|
|
||||||
&& (!batteryDiffData.getAppDiffEntryList().isEmpty()
|
|
||||||
|| !batteryDiffData.getSystemDiffEntryList().isEmpty()));
|
|
||||||
if (batteryDiffData == null) {
|
|
||||||
Log.w(TAG, "cannot find BatteryDiffEntry for daily_index: " + mDailyChartIndex
|
|
||||||
+ " hourly_index: " + mHourlyChartIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Adds app entries to the list if it is not empty.
|
|
||||||
if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
|
|
||||||
addPreferenceToScreen(batteryDiffData.getAppDiffEntryList());
|
|
||||||
}
|
|
||||||
// Adds the expandable divider if we have system entries data.
|
|
||||||
if (!batteryDiffData.getSystemDiffEntryList().isEmpty()) {
|
|
||||||
if (mExpandDividerPreference == null) {
|
|
||||||
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
|
|
||||||
mExpandDividerPreference.setOnExpandListener(this);
|
|
||||||
mExpandDividerPreference.setIsExpanded(mIsExpanded);
|
|
||||||
}
|
|
||||||
mExpandDividerPreference.setOrder(
|
|
||||||
mAppListPrefGroup.getPreferenceCount());
|
|
||||||
mAppListPrefGroup.addPreference(mExpandDividerPreference);
|
|
||||||
}
|
|
||||||
refreshExpandUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void addPreferenceToScreen(List<BatteryDiffEntry> entries) {
|
|
||||||
if (mAppListPrefGroup == null || entries.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int prefIndex = mAppListPrefGroup.getPreferenceCount();
|
|
||||||
for (BatteryDiffEntry entry : entries) {
|
|
||||||
boolean isAdded = false;
|
|
||||||
final String appLabel = entry.getAppLabel();
|
|
||||||
final Drawable appIcon = entry.getAppIcon();
|
|
||||||
if (TextUtils.isEmpty(appLabel) || appIcon == null) {
|
|
||||||
Log.w(TAG, "cannot find app resource for:" + entry.getPackageName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String prefKey = entry.mBatteryHistEntry.getKey();
|
|
||||||
PowerGaugePreference pref = mAppListPrefGroup.findPreference(prefKey);
|
|
||||||
if (pref != null) {
|
|
||||||
isAdded = true;
|
|
||||||
Log.w(TAG, "preference should be removed for:" + entry.getPackageName());
|
|
||||||
} else {
|
|
||||||
pref = (PowerGaugePreference) mPreferenceCache.get(prefKey);
|
|
||||||
}
|
|
||||||
// Creates new innstance if cached preference is not found.
|
|
||||||
if (pref == null) {
|
|
||||||
pref = new PowerGaugePreference(mPrefContext);
|
|
||||||
pref.setKey(prefKey);
|
|
||||||
mPreferenceCache.put(prefKey, pref);
|
|
||||||
}
|
|
||||||
pref.setIcon(appIcon);
|
|
||||||
pref.setTitle(appLabel);
|
|
||||||
pref.setOrder(prefIndex);
|
|
||||||
pref.setPercent(entry.getPercentOfTotal());
|
|
||||||
pref.setSingleLineTitle(true);
|
|
||||||
// Sets the BatteryDiffEntry to preference for launching detailed page.
|
|
||||||
pref.setBatteryDiffEntry(entry);
|
|
||||||
pref.setEnabled(entry.validForRestriction());
|
|
||||||
setPreferenceSummary(pref, entry);
|
|
||||||
if (!isAdded) {
|
|
||||||
mAppListPrefGroup.addPreference(pref);
|
|
||||||
}
|
|
||||||
appIcon.setAlpha(pref.isEnabled() ? ENABLED_ICON_ALPHA : DISABLED_ICON_ALPHA);
|
|
||||||
prefIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeAndCacheAllPrefs() {
|
|
||||||
if (mAppListPrefGroup == null
|
|
||||||
|| mAppListPrefGroup.getPreferenceCount() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final int prefsCount = mAppListPrefGroup.getPreferenceCount();
|
|
||||||
for (int index = 0; index < prefsCount; index++) {
|
|
||||||
final Preference pref = mAppListPrefGroup.getPreference(index);
|
|
||||||
if (TextUtils.isEmpty(pref.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mPreferenceCache.put(pref.getKey(), pref);
|
|
||||||
}
|
|
||||||
mAppListPrefGroup.removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshExpandUi() {
|
|
||||||
final List<BatteryDiffEntry> systemEntries = mBatteryUsageMap.get(mDailyChartIndex).get(
|
|
||||||
mHourlyChartIndex).getSystemDiffEntryList();
|
|
||||||
if (mIsExpanded) {
|
|
||||||
addPreferenceToScreen(systemEntries);
|
|
||||||
} else {
|
|
||||||
// Removes and recycles all system entries to hide all of them.
|
|
||||||
for (BatteryDiffEntry entry : systemEntries) {
|
|
||||||
final String prefKey = entry.mBatteryHistEntry.getKey();
|
|
||||||
final Preference pref = mAppListPrefGroup.findPreference(prefKey);
|
|
||||||
if (pref != null) {
|
|
||||||
mAppListPrefGroup.removePreference(pref);
|
|
||||||
mPreferenceCache.put(pref.getKey(), pref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void refreshCategoryTitle() {
|
|
||||||
final String slotInformation = getSlotInformation();
|
|
||||||
Log.d(TAG, String.format("refreshCategoryTitle:%s", slotInformation));
|
|
||||||
if (mAppListPrefGroup != null) {
|
|
||||||
mAppListPrefGroup.setTitle(
|
|
||||||
getSlotInformation(/*isApp=*/ true, slotInformation));
|
|
||||||
}
|
|
||||||
if (mExpandDividerPreference != null) {
|
|
||||||
mExpandDividerPreference.setTitle(
|
|
||||||
getSlotInformation(/*isApp=*/ false, slotInformation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requestAccessibilityFocusForCategoryTitle(View view) {
|
|
||||||
if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mCategoryTitleView == null) {
|
|
||||||
mCategoryTitleView = view.getRootView().findViewById(com.android.internal.R.id.title);
|
|
||||||
}
|
|
||||||
if (mCategoryTitleView != null) {
|
|
||||||
mCategoryTitleView.requestAccessibilityFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSlotInformation(boolean isApp, String slotInformation) {
|
|
||||||
// TODO: Updates the right slot information from daily and hourly chart selection.
|
|
||||||
// Null means we show all information without a specific time slot.
|
|
||||||
if (slotInformation == null) {
|
|
||||||
return isApp
|
|
||||||
? mPrefContext.getString(R.string.battery_app_usage)
|
|
||||||
: mPrefContext.getString(R.string.battery_system_usage);
|
|
||||||
} else {
|
|
||||||
return isApp
|
|
||||||
? mPrefContext.getString(R.string.battery_app_usage_for, slotInformation)
|
|
||||||
: mPrefContext.getString(R.string.battery_system_usage_for, slotInformation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
String getSlotInformation() {
|
String getSlotInformation() {
|
||||||
if (mDailyViewModel == null || mHourlyViewModels == null) {
|
if (mDailyViewModel == null || mHourlyViewModels == null) {
|
||||||
@@ -624,50 +402,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
return String.format("%s %s", selectedDayText, selectedHourText);
|
return String.format("%s %s", selectedDayText, selectedHourText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void setPreferenceSummary(
|
|
||||||
PowerGaugePreference preference, BatteryDiffEntry entry) {
|
|
||||||
final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;
|
|
||||||
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
|
|
||||||
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
|
|
||||||
String usageTimeSummary = null;
|
|
||||||
// Not shows summary for some system components without usage time.
|
|
||||||
if (totalUsageTimeInMs == 0) {
|
|
||||||
preference.setSummary(null);
|
|
||||||
// Shows background summary only if we don't have foreground usage time.
|
|
||||||
} else if (foregroundUsageTimeInMs == 0 && backgroundUsageTimeInMs != 0) {
|
|
||||||
usageTimeSummary = buildUsageTimeInfo(backgroundUsageTimeInMs, true);
|
|
||||||
// Shows total usage summary only if total usage time is small.
|
|
||||||
} else if (totalUsageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
|
|
||||||
usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
|
|
||||||
} else {
|
|
||||||
usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
|
|
||||||
// Shows background usage time if it is larger than a minute.
|
|
||||||
if (backgroundUsageTimeInMs > 0) {
|
|
||||||
usageTimeSummary +=
|
|
||||||
"\n" + buildUsageTimeInfo(backgroundUsageTimeInMs, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
preference.setSummary(usageTimeSummary);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildUsageTimeInfo(long usageTimeInMs, boolean isBackground) {
|
|
||||||
if (usageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
|
|
||||||
return mPrefContext.getString(
|
|
||||||
isBackground
|
|
||||||
? R.string.battery_usage_background_less_than_one_minute
|
|
||||||
: R.string.battery_usage_total_less_than_one_minute);
|
|
||||||
}
|
|
||||||
final CharSequence timeSequence =
|
|
||||||
StringUtil.formatElapsedTime(mPrefContext, usageTimeInMs,
|
|
||||||
/*withSeconds=*/ false, /*collapseTimeUnit=*/ false);
|
|
||||||
final int resourceId =
|
|
||||||
isBackground
|
|
||||||
? R.string.battery_usage_for_background_time
|
|
||||||
: R.string.battery_usage_for_total_time;
|
|
||||||
return mPrefContext.getString(resourceId, timeSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateBatteryChartViewGroup() {
|
private void animateBatteryChartViewGroup() {
|
||||||
if (mBatteryChartViewGroup != null && mBatteryChartViewGroup.getAlpha() == 0) {
|
if (mBatteryChartViewGroup != null && mBatteryChartViewGroup.getAlpha() == 0) {
|
||||||
mBatteryChartViewGroup.animate().alpha(1f).setDuration(FADE_IN_ANIMATION_DURATION)
|
mBatteryChartViewGroup.animate().alpha(1f).setDuration(FADE_IN_ANIMATION_DURATION)
|
||||||
@@ -725,18 +459,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFooterPreferenceIfNeeded(boolean containAppItems) {
|
|
||||||
if (mIsFooterPrefAdded || mFooterPreference == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIsFooterPrefAdded = true;
|
|
||||||
mFooterPreference.setTitle(mPrefContext.getString(
|
|
||||||
containAppItems
|
|
||||||
? R.string.battery_usage_screen_footer
|
|
||||||
: R.string.battery_usage_screen_footer_empty));
|
|
||||||
mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBatteryLevelDataInOneDay() {
|
private boolean isBatteryLevelDataInOneDay() {
|
||||||
return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
|
return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
|
||||||
}
|
}
|
||||||
@@ -747,6 +469,19 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
&& mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
|
&& mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isBatteryUsageMapNullOrEmpty() {
|
||||||
|
if (mBatteryUsageMap == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
BatteryDiffData allBatteryDiffData = mBatteryUsageMap
|
||||||
|
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||||
|
.get(BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||||
|
// If all data is null or empty, each slot must be null or empty.
|
||||||
|
return allBatteryDiffData == null
|
||||||
|
|| (allBatteryDiffData.getAppDiffEntryList().isEmpty()
|
||||||
|
&& allBatteryDiffData.getSystemDiffEntryList().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static int getTotalHours(final BatteryLevelData batteryLevelData) {
|
static int getTotalHours(final BatteryLevelData batteryLevelData) {
|
||||||
if (batteryLevelData == null) {
|
if (batteryLevelData == null) {
|
||||||
|
@@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.batteryusage;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceCategory;
|
||||||
|
import androidx.preference.PreferenceGroup;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.core.BasePreferenceController;
|
||||||
|
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||||
|
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
|
||||||
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||||
|
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
||||||
|
import com.android.settingslib.utils.StringUtil;
|
||||||
|
import com.android.settingslib.widget.FooterPreference;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Controller for battery usage breakdown preference group. */
|
||||||
|
public class BatteryUsageBreakdownController extends BasePreferenceController
|
||||||
|
implements LifecycleObserver, OnDestroy {
|
||||||
|
private static final String TAG = "BatteryUsageBreakdownController";
|
||||||
|
private static final String ROOT_PREFERENCE_KEY = "battery_usage_breakdown";
|
||||||
|
private static final String FOOTER_PREFERENCE_KEY = "battery_usage_footer";
|
||||||
|
private static final String TAB_PREFERENCE_KEY = "battery_usage_tab";
|
||||||
|
private static final String APP_LIST_PREFERENCE_KEY = "app_list";
|
||||||
|
private static final String PACKAGE_NAME_NONE = "none";
|
||||||
|
private static final int ENABLED_ICON_ALPHA = 255;
|
||||||
|
private static final int DISABLED_ICON_ALPHA = 255 / 3;
|
||||||
|
|
||||||
|
private final SettingsActivity mActivity;
|
||||||
|
private final InstrumentedPreferenceFragment mFragment;
|
||||||
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
final Map<String, Preference> mPreferenceCache = new HashMap<>();
|
||||||
|
|
||||||
|
private int mTabPosition;
|
||||||
|
private String mSlotTimestamp;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Context mPrefContext;
|
||||||
|
@VisibleForTesting
|
||||||
|
PreferenceCategory mRootPreference;
|
||||||
|
@VisibleForTesting
|
||||||
|
TabPreference mTabPreference;
|
||||||
|
@VisibleForTesting
|
||||||
|
PreferenceGroup mAppListPreferenceGroup;
|
||||||
|
@VisibleForTesting
|
||||||
|
FooterPreference mFooterPreference;
|
||||||
|
@VisibleForTesting
|
||||||
|
BatteryDiffData mBatteryDiffData;
|
||||||
|
|
||||||
|
public BatteryUsageBreakdownController(
|
||||||
|
Context context, Lifecycle lifecycle, SettingsActivity activity,
|
||||||
|
InstrumentedPreferenceFragment fragment) {
|
||||||
|
super(context, ROOT_PREFERENCE_KEY);
|
||||||
|
mActivity = activity;
|
||||||
|
mFragment = fragment;
|
||||||
|
mMetricsFeatureProvider =
|
||||||
|
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
|
||||||
|
if (lifecycle != null) {
|
||||||
|
lifecycle.addObserver(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
mHandler.removeCallbacksAndMessages(/*token=*/ null);
|
||||||
|
mPreferenceCache.clear();
|
||||||
|
mAppListPreferenceGroup.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
return AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSliceable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||||
|
if (!(preference instanceof PowerGaugePreference)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
|
||||||
|
final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
|
||||||
|
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
|
||||||
|
final String packageName = histEntry.mPackageName;
|
||||||
|
final boolean isAppEntry = histEntry.isAppEntry();
|
||||||
|
mMetricsFeatureProvider.action(
|
||||||
|
/* attribution */ SettingsEnums.OPEN_BATTERY_USAGE,
|
||||||
|
/* action */ isAppEntry
|
||||||
|
? SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM
|
||||||
|
: SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
|
||||||
|
/* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
|
||||||
|
TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName,
|
||||||
|
(int) Math.round(diffEntry.getPercentOfTotal()));
|
||||||
|
Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
|
||||||
|
diffEntry.getAppLabel(), histEntry.getKey(), histEntry.mPackageName));
|
||||||
|
AdvancedPowerUsageDetail.startBatteryDetailPage(
|
||||||
|
mActivity, mFragment, diffEntry, powerPref.getPercent(), mSlotTimestamp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
mPrefContext = screen.getContext();
|
||||||
|
mRootPreference = screen.findPreference(ROOT_PREFERENCE_KEY);
|
||||||
|
mTabPreference = screen.findPreference(TAB_PREFERENCE_KEY);
|
||||||
|
mAppListPreferenceGroup = screen.findPreference(APP_LIST_PREFERENCE_KEY);
|
||||||
|
mFooterPreference = screen.findPreference(FOOTER_PREFERENCE_KEY);
|
||||||
|
|
||||||
|
mAppListPreferenceGroup.setOrderingAsAdded(false);
|
||||||
|
mTabPreference.initializeTabs(mFragment, new String[]{
|
||||||
|
mPrefContext.getString(R.string.battery_usage_app_tab),
|
||||||
|
mPrefContext.getString(R.string.battery_usage_system_tab)
|
||||||
|
});
|
||||||
|
mTabPreference.setOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
super.onPageSelected(position);
|
||||||
|
mTabPosition = position;
|
||||||
|
mHandler.post(() -> {
|
||||||
|
removeAndCacheAllPreferences();
|
||||||
|
addAllPreferences();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates UI when the battery usage is updated.
|
||||||
|
* @param slotUsageData The battery usage diff data for the selected slot. This is used in
|
||||||
|
* the app list.
|
||||||
|
* @param slotTimestamp The selected slot timestamp information. This is used in the battery
|
||||||
|
* usage breakdown category.
|
||||||
|
* @param isAllUsageDataEmpty Whether all the battery usage data is null or empty. This is
|
||||||
|
* used when showing the footer.
|
||||||
|
*/
|
||||||
|
void handleBatteryUsageUpdated(
|
||||||
|
BatteryDiffData slotUsageData, String slotTimestamp, boolean isAllUsageDataEmpty) {
|
||||||
|
mBatteryDiffData = slotUsageData;
|
||||||
|
mSlotTimestamp = slotTimestamp;
|
||||||
|
|
||||||
|
showCategoryTitle(slotTimestamp);
|
||||||
|
showTabAndAppList();
|
||||||
|
showFooterPreference(isAllUsageDataEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: request accessibility focus on category title when slot selection updated.
|
||||||
|
private void showCategoryTitle(String slotTimestamp) {
|
||||||
|
mRootPreference.setTitle(slotTimestamp == null
|
||||||
|
? mPrefContext.getString(
|
||||||
|
R.string.battery_usage_breakdown_title_since_last_full_charge)
|
||||||
|
: mPrefContext.getString(
|
||||||
|
R.string.battery_usage_breakdown_title_for_slot, slotTimestamp));
|
||||||
|
mRootPreference.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFooterPreference(boolean isAllBatteryUsageEmpty) {
|
||||||
|
mFooterPreference.setTitle(mPrefContext.getString(
|
||||||
|
isAllBatteryUsageEmpty
|
||||||
|
? R.string.battery_usage_screen_footer_empty
|
||||||
|
: R.string.battery_usage_screen_footer));
|
||||||
|
mFooterPreference.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showTabAndAppList() {
|
||||||
|
removeAndCacheAllPreferences();
|
||||||
|
if (mBatteryDiffData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mTabPreference.setVisible(true);
|
||||||
|
mAppListPreferenceGroup.setVisible(true);
|
||||||
|
mHandler.post(() -> {
|
||||||
|
addAllPreferences();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void addAllPreferences() {
|
||||||
|
if (mBatteryDiffData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final long start = System.currentTimeMillis();
|
||||||
|
final List<BatteryDiffEntry> entries = mTabPosition == 0
|
||||||
|
? mBatteryDiffData.getAppDiffEntryList()
|
||||||
|
: mBatteryDiffData.getSystemDiffEntryList();
|
||||||
|
int prefIndex = mAppListPreferenceGroup.getPreferenceCount();
|
||||||
|
for (BatteryDiffEntry entry : entries) {
|
||||||
|
boolean isAdded = false;
|
||||||
|
final String appLabel = entry.getAppLabel();
|
||||||
|
final Drawable appIcon = entry.getAppIcon();
|
||||||
|
if (TextUtils.isEmpty(appLabel) || appIcon == null) {
|
||||||
|
Log.w(TAG, "cannot find app resource for:" + entry.getPackageName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String prefKey = entry.mBatteryHistEntry.getKey();
|
||||||
|
PowerGaugePreference pref = mAppListPreferenceGroup.findPreference(prefKey);
|
||||||
|
if (pref != null) {
|
||||||
|
isAdded = true;
|
||||||
|
Log.w(TAG, "preference should be removed for:" + entry.getPackageName());
|
||||||
|
} else {
|
||||||
|
pref = (PowerGaugePreference) mPreferenceCache.get(prefKey);
|
||||||
|
}
|
||||||
|
// Creates new innstance if cached preference is not found.
|
||||||
|
if (pref == null) {
|
||||||
|
pref = new PowerGaugePreference(mPrefContext);
|
||||||
|
pref.setKey(prefKey);
|
||||||
|
mPreferenceCache.put(prefKey, pref);
|
||||||
|
}
|
||||||
|
pref.setIcon(appIcon);
|
||||||
|
pref.setTitle(appLabel);
|
||||||
|
pref.setOrder(prefIndex);
|
||||||
|
pref.setPercent(entry.getPercentOfTotal());
|
||||||
|
pref.setSingleLineTitle(true);
|
||||||
|
// Sets the BatteryDiffEntry to preference for launching detailed page.
|
||||||
|
pref.setBatteryDiffEntry(entry);
|
||||||
|
pref.setEnabled(entry.validForRestriction());
|
||||||
|
setPreferenceSummary(pref, entry);
|
||||||
|
if (!isAdded) {
|
||||||
|
mAppListPreferenceGroup.addPreference(pref);
|
||||||
|
}
|
||||||
|
appIcon.setAlpha(pref.isEnabled() ? ENABLED_ICON_ALPHA : DISABLED_ICON_ALPHA);
|
||||||
|
prefIndex++;
|
||||||
|
}
|
||||||
|
Log.d(TAG, String.format("addAllPreferences() is finished in %d/ms",
|
||||||
|
(System.currentTimeMillis() - start)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void removeAndCacheAllPreferences() {
|
||||||
|
final int prefsCount = mAppListPreferenceGroup.getPreferenceCount();
|
||||||
|
for (int index = 0; index < prefsCount; index++) {
|
||||||
|
final Preference pref = mAppListPreferenceGroup.getPreference(index);
|
||||||
|
if (TextUtils.isEmpty(pref.getKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mPreferenceCache.put(pref.getKey(), pref);
|
||||||
|
}
|
||||||
|
mAppListPreferenceGroup.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setPreferenceSummary(
|
||||||
|
PowerGaugePreference preference, BatteryDiffEntry entry) {
|
||||||
|
final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;
|
||||||
|
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
|
||||||
|
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
|
||||||
|
String usageTimeSummary = null;
|
||||||
|
// Not shows summary for some system components without usage time.
|
||||||
|
if (totalUsageTimeInMs == 0) {
|
||||||
|
preference.setSummary(null);
|
||||||
|
// Shows background summary only if we don't have foreground usage time.
|
||||||
|
} else if (foregroundUsageTimeInMs == 0 && backgroundUsageTimeInMs != 0) {
|
||||||
|
usageTimeSummary = buildUsageTimeInfo(backgroundUsageTimeInMs, true);
|
||||||
|
// Shows total usage summary only if total usage time is small.
|
||||||
|
} else if (totalUsageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
|
||||||
|
usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
|
||||||
|
} else {
|
||||||
|
usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
|
||||||
|
// Shows background usage time if it is larger than a minute.
|
||||||
|
if (backgroundUsageTimeInMs > 0) {
|
||||||
|
usageTimeSummary +=
|
||||||
|
"\n" + buildUsageTimeInfo(backgroundUsageTimeInMs, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preference.setSummary(usageTimeSummary);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildUsageTimeInfo(long usageTimeInMs, boolean isBackground) {
|
||||||
|
if (usageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
|
||||||
|
return mPrefContext.getString(
|
||||||
|
isBackground
|
||||||
|
? R.string.battery_usage_background_less_than_one_minute
|
||||||
|
: R.string.battery_usage_total_less_than_one_minute);
|
||||||
|
}
|
||||||
|
final CharSequence timeSequence =
|
||||||
|
StringUtil.formatElapsedTime(mPrefContext, (double) usageTimeInMs,
|
||||||
|
/*withSeconds=*/ false, /*collapseTimeUnit=*/ false);
|
||||||
|
final int resourceId =
|
||||||
|
isBackground
|
||||||
|
? R.string.battery_usage_for_background_time
|
||||||
|
: R.string.battery_usage_for_total_time;
|
||||||
|
return mPrefContext.getString(resourceId, timeSequence);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,100 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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.batteryusage;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceViewHolder;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
/** A preference for expandable section divider. */
|
|
||||||
public class ExpandDividerPreference extends Preference {
|
|
||||||
private static final String TAG = "ExpandDividerPreference";
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String PREFERENCE_KEY = "expandable_divider";
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
TextView mTextView;
|
|
||||||
@VisibleForTesting
|
|
||||||
ImageView mImageView;
|
|
||||||
private OnExpandListener mOnExpandListener;
|
|
||||||
|
|
||||||
private boolean mIsExpanded = false;
|
|
||||||
private String mTitleContent = null;
|
|
||||||
|
|
||||||
/** A callback listener for expand state is changed by users. */
|
|
||||||
public interface OnExpandListener {
|
|
||||||
/** Callback function for expand state is changed by users. */
|
|
||||||
void onExpand(boolean isExpanded);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExpandDividerPreference(Context context) {
|
|
||||||
this(context, /*attrs=*/ null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExpandDividerPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
setLayoutResource(R.layout.preference_expand_divider);
|
|
||||||
setKey(PREFERENCE_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(PreferenceViewHolder view) {
|
|
||||||
super.onBindViewHolder(view);
|
|
||||||
mTextView = (TextView) view.findViewById(R.id.expand_title);
|
|
||||||
mImageView = (ImageView) view.findViewById(R.id.expand_icon);
|
|
||||||
refreshState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick() {
|
|
||||||
setIsExpanded(!mIsExpanded);
|
|
||||||
if (mOnExpandListener != null) {
|
|
||||||
mOnExpandListener.onExpand(mIsExpanded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTitle(final String titleContent) {
|
|
||||||
mTitleContent = titleContent;
|
|
||||||
refreshState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setIsExpanded(boolean isExpanded) {
|
|
||||||
mIsExpanded = isExpanded;
|
|
||||||
refreshState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOnExpandListener(OnExpandListener listener) {
|
|
||||||
mOnExpandListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshState() {
|
|
||||||
if (mImageView != null) {
|
|
||||||
mImageView.setImageResource(mIsExpanded
|
|
||||||
? R.drawable.ic_settings_expand_less
|
|
||||||
: R.drawable.ic_settings_expand_more);
|
|
||||||
}
|
|
||||||
if (mTextView != null) {
|
|
||||||
mTextView.setText(mTitleContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -50,8 +50,7 @@ import java.util.Map;
|
|||||||
public class PowerUsageAdvanced extends PowerUsageBase {
|
public class PowerUsageAdvanced extends PowerUsageBase {
|
||||||
private static final String TAG = "AdvancedBatteryUsage";
|
private static final String TAG = "AdvancedBatteryUsage";
|
||||||
private static final String KEY_REFRESH_TYPE = "refresh_type";
|
private static final String KEY_REFRESH_TYPE = "refresh_type";
|
||||||
private static final String KEY_BATTERY_GRAPH = "battery_graph";
|
private static final String KEY_BATTERY_CHART = "battery_chart";
|
||||||
private static final String KEY_APP_LIST = "app_list";
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
BatteryHistoryPreference mHistPref;
|
BatteryHistoryPreference mHistPref;
|
||||||
@@ -81,7 +80,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
|||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
refreshFeatureFlag(context);
|
refreshFeatureFlag(context);
|
||||||
mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
|
mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_CHART);
|
||||||
setBatteryChartPreferenceController();
|
setBatteryChartPreferenceController();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,9 +133,17 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
|||||||
refreshFeatureFlag(context);
|
refreshFeatureFlag(context);
|
||||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||||
mBatteryChartPreferenceController =
|
mBatteryChartPreferenceController =
|
||||||
new BatteryChartPreferenceController(context, KEY_APP_LIST,
|
new BatteryChartPreferenceController(
|
||||||
getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
|
context, getSettingsLifecycle(), (SettingsActivity) getActivity());
|
||||||
|
BatteryUsageBreakdownController batteryUsageBreakdownController =
|
||||||
|
new BatteryUsageBreakdownController(
|
||||||
|
context, getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
|
||||||
|
|
||||||
|
mBatteryChartPreferenceController.setOnBatteryUsageUpdatedListener(
|
||||||
|
batteryUsageBreakdownController::handleBatteryUsageUpdated);
|
||||||
|
|
||||||
controllers.add(mBatteryChartPreferenceController);
|
controllers.add(mBatteryChartPreferenceController);
|
||||||
|
controllers.add(batteryUsageBreakdownController);
|
||||||
setBatteryChartPreferenceController();
|
setBatteryChartPreferenceController();
|
||||||
return controllers;
|
return controllers;
|
||||||
}
|
}
|
||||||
@@ -196,8 +203,10 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
|||||||
public List<AbstractPreferenceController> createPreferenceControllers(
|
public List<AbstractPreferenceController> createPreferenceControllers(
|
||||||
Context context) {
|
Context context) {
|
||||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||||
controllers.add(new BatteryChartPreferenceController(context,
|
controllers.add(new BatteryChartPreferenceController(
|
||||||
KEY_APP_LIST, null /* lifecycle */, null /* activity */,
|
context, null /* lifecycle */, null /* activity */));
|
||||||
|
controllers.add(new BatteryUsageBreakdownController(
|
||||||
|
context, null /* lifecycle */, null /* activity */,
|
||||||
null /* fragment */));
|
null /* fragment */));
|
||||||
return controllers;
|
return controllers;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.batteryusage;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||||
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator;
|
||||||
|
|
||||||
|
/** A preference which contains a tab selection. */
|
||||||
|
public class TabPreference extends Preference {
|
||||||
|
private static final String TAG = "TabPreference";
|
||||||
|
|
||||||
|
private Fragment mRootFragment;
|
||||||
|
private ViewPager2 mViewPager;
|
||||||
|
private ViewPager2.OnPageChangeCallback mOnPageChangeCallback;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
String[] mTabTitles;
|
||||||
|
@VisibleForTesting
|
||||||
|
int mSavedTabPosition;
|
||||||
|
@VisibleForTesting
|
||||||
|
TabLayout mTabLayout;
|
||||||
|
|
||||||
|
public TabPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setLayoutResource(R.layout.preference_tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeTabs(Fragment rootFragment, String[] tabTitles) {
|
||||||
|
mRootFragment = rootFragment;
|
||||||
|
mTabTitles = tabTitles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback) {
|
||||||
|
mOnPageChangeCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder view) {
|
||||||
|
super.onBindViewHolder(view);
|
||||||
|
if (mViewPager != null && mTabLayout != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mViewPager = (ViewPager2) view.findViewById(R.id.view_pager);
|
||||||
|
mViewPager.setAdapter(new FragmentAdapter(mRootFragment, mTabTitles.length));
|
||||||
|
mViewPager.setUserInputEnabled(false);
|
||||||
|
if (mOnPageChangeCallback != null) {
|
||||||
|
mViewPager.registerOnPageChangeCallback(mOnPageChangeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
mTabLayout = (TabLayout) view.findViewById(R.id.tabs);
|
||||||
|
new TabLayoutMediator(
|
||||||
|
mTabLayout, mViewPager, /* autoRefresh= */ true, /* smoothScroll= */ false,
|
||||||
|
(tab, position) -> tab.setText(mTabTitles[position])).attach();
|
||||||
|
mTabLayout.getTabAt(mSavedTabPosition).select();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetached() {
|
||||||
|
super.onDetached();
|
||||||
|
if (mOnPageChangeCallback != null) {
|
||||||
|
mViewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Parcelable onSaveInstanceState() {
|
||||||
|
Log.d(TAG, "onSaveInstanceState() tabPosition=" + mTabLayout.getSelectedTabPosition());
|
||||||
|
return new SavedState(super.onSaveInstanceState(), mTabLayout.getSelectedTabPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Parcelable state) {
|
||||||
|
if (state == null || !state.getClass().equals(SavedState.class)) {
|
||||||
|
super.onRestoreInstanceState(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SavedState savedState = (SavedState) state;
|
||||||
|
super.onRestoreInstanceState(savedState.getSuperState());
|
||||||
|
mSavedTabPosition = savedState.getTabPosition();
|
||||||
|
Log.d(TAG, "onRestoreInstanceState() tabPosition=" + savedState.getTabPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class SavedState extends BaseSavedState {
|
||||||
|
private int mTabPosition;
|
||||||
|
|
||||||
|
SavedState(Parcelable superState, int tabPosition) {
|
||||||
|
super(superState);
|
||||||
|
mTabPosition = tabPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTabPosition() {
|
||||||
|
return mTabPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FragmentAdapter extends FragmentStateAdapter {
|
||||||
|
private final int mItemCount;
|
||||||
|
private final Fragment[] mItemFragments;
|
||||||
|
|
||||||
|
FragmentAdapter(@NonNull Fragment rootFragment, int itemCount) {
|
||||||
|
super(rootFragment);
|
||||||
|
mItemCount = itemCount;
|
||||||
|
mItemFragments = new Fragment[mItemCount];
|
||||||
|
for (int i = 0; i < mItemCount; i++) {
|
||||||
|
// Empty tab pages.
|
||||||
|
mItemFragments[i] = new Fragment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Fragment createFragment(int position) {
|
||||||
|
return mItemFragments[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mItemCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -24,16 +24,13 @@ import static org.mockito.Mockito.any;
|
|||||||
import static org.mockito.Mockito.atLeast;
|
import static org.mockito.Mockito.atLeast;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.reset;
|
import static org.mockito.Mockito.reset;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.LocaleList;
|
import android.os.LocaleList;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
@@ -41,27 +38,17 @@ import android.view.View;
|
|||||||
import android.view.ViewPropertyAnimator;
|
import android.view.ViewPropertyAnimator;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceCategory;
|
|
||||||
import androidx.preference.PreferenceGroup;
|
|
||||||
|
|
||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -70,18 +57,9 @@ import java.util.TimeZone;
|
|||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
public final class BatteryChartPreferenceControllerTest {
|
public final class BatteryChartPreferenceControllerTest {
|
||||||
private static final String PREF_KEY = "pref_key";
|
|
||||||
private static final String PREF_SUMMARY = "fake preference summary";
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private InstrumentedPreferenceFragment mFragment;
|
|
||||||
@Mock
|
@Mock
|
||||||
private SettingsActivity mSettingsActivity;
|
private SettingsActivity mSettingsActivity;
|
||||||
@Mock
|
@Mock
|
||||||
private PreferenceGroup mAppListGroup;
|
|
||||||
@Mock
|
|
||||||
private Drawable mDrawable;
|
|
||||||
@Mock
|
|
||||||
private BatteryHistEntry mBatteryHistEntry;
|
private BatteryHistEntry mBatteryHistEntry;
|
||||||
@Mock
|
@Mock
|
||||||
private BatteryChartView mDailyChartView;
|
private BatteryChartView mDailyChartView;
|
||||||
@@ -90,16 +68,11 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private ViewPropertyAnimator mViewPropertyAnimator;
|
private ViewPropertyAnimator mViewPropertyAnimator;
|
||||||
@Mock
|
@Mock
|
||||||
private PowerGaugePreference mPowerGaugePreference;
|
|
||||||
@Mock
|
|
||||||
private BatteryUtils mBatteryUtils;
|
|
||||||
@Mock
|
|
||||||
private LinearLayout.LayoutParams mLayoutParams;
|
private LinearLayout.LayoutParams mLayoutParams;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
private BatteryDiffEntry mBatteryDiffEntry;
|
private BatteryDiffEntry mBatteryDiffEntry;
|
||||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private BatteryChartPreferenceController mBatteryChartPreferenceController;
|
private BatteryChartPreferenceController mBatteryChartPreferenceController;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -109,7 +82,6 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
|
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
|
||||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
final Resources resources = spy(mContext.getResources());
|
final Resources resources = spy(mContext.getResources());
|
||||||
resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US")));
|
resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US")));
|
||||||
@@ -121,7 +93,6 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
setupHourlyChartViewAnimationMock();
|
setupHourlyChartViewAnimationMock();
|
||||||
mBatteryChartPreferenceController = createController();
|
mBatteryChartPreferenceController = createController();
|
||||||
mBatteryChartPreferenceController.mPrefContext = mContext;
|
mBatteryChartPreferenceController.mPrefContext = mContext;
|
||||||
mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup;
|
|
||||||
mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
|
mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
|
||||||
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
|
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
|
||||||
mBatteryDiffEntry = new BatteryDiffEntry(
|
mBatteryDiffEntry = new BatteryDiffEntry(
|
||||||
@@ -161,24 +132,6 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
assertThat(BatteryDiffEntry.sResourceCache).isNotEmpty();
|
assertThat(BatteryDiffEntry.sResourceCache).isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onDestroy_clearPreferenceCache() {
|
|
||||||
// Ensures the testing environment is correct.
|
|
||||||
mBatteryChartPreferenceController.mPreferenceCache.put(
|
|
||||||
PREF_KEY, mPowerGaugePreference);
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).hasSize(1);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onDestroy();
|
|
||||||
// Verifies the result after onDestroy.
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onDestroy_removeAllPreferenceFromPreferenceGroup() {
|
|
||||||
mBatteryChartPreferenceController.onDestroy();
|
|
||||||
verify(mAppListGroup).removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setBatteryChartViewModel_6Hours() {
|
public void setBatteryChartViewModel_6Hours() {
|
||||||
reset(mDailyChartView);
|
reset(mDailyChartView);
|
||||||
@@ -313,9 +266,9 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void refreshUi_batteryIndexedMapIsNull_ignoreRefresh() {
|
public void refreshUi_batteryIndexedMapIsNull_returnTrue() {
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(null);
|
mBatteryChartPreferenceController.setBatteryHistoryMap(null);
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -330,261 +283,6 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
|
||||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
|
||||||
doReturn(0).when(mAppListGroup).getPreferenceCount();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshUi();
|
|
||||||
verify(mAppListGroup, never()).removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
|
||||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0);
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
|
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
// Ensures the testing data is correct.
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshUi();
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY))
|
|
||||||
.isEqualTo(mPowerGaugePreference);
|
|
||||||
verify(mAppListGroup).removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addPreferenceToScreen_emptyContent_ignoreAddPreference() {
|
|
||||||
mBatteryChartPreferenceController.addPreferenceToScreen(
|
|
||||||
new ArrayList<BatteryDiffEntry>());
|
|
||||||
verify(mAppListGroup, never()).addPreference(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addPreferenceToScreen_addPreferenceIntoScreen() {
|
|
||||||
final String appLabel = "fake app label";
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
|
||||||
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
doReturn(null).when(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
doReturn(false).when(mBatteryDiffEntry).validForRestriction();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.addPreferenceToScreen(
|
|
||||||
Arrays.asList(mBatteryDiffEntry));
|
|
||||||
|
|
||||||
// Verifies the preference cache.
|
|
||||||
final PowerGaugePreference pref =
|
|
||||||
(PowerGaugePreference) mBatteryChartPreferenceController.mPreferenceCache
|
|
||||||
.get(PREF_KEY);
|
|
||||||
assertThat(pref).isNotNull();
|
|
||||||
// Verifies the added preference configuration.
|
|
||||||
verify(mAppListGroup).addPreference(pref);
|
|
||||||
assertThat(pref.getKey()).isEqualTo(PREF_KEY);
|
|
||||||
assertThat(pref.getTitle()).isEqualTo(appLabel);
|
|
||||||
assertThat(pref.getIcon()).isEqualTo(mDrawable);
|
|
||||||
assertThat(pref.getOrder()).isEqualTo(1);
|
|
||||||
assertThat(pref.getBatteryDiffEntry()).isSameInstanceAs(mBatteryDiffEntry);
|
|
||||||
assertThat(pref.isSingleLineTitle()).isTrue();
|
|
||||||
assertThat(pref.isEnabled()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
|
|
||||||
final String appLabel = "fake app label";
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
|
||||||
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.addPreferenceToScreen(
|
|
||||||
Arrays.asList(mBatteryDiffEntry));
|
|
||||||
|
|
||||||
verify(mAppListGroup, never()).addPreference(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() {
|
|
||||||
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup))
|
|
||||||
.isFalse();
|
|
||||||
|
|
||||||
verify(mMetricsFeatureProvider, never())
|
|
||||||
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM);
|
|
||||||
verify(mMetricsFeatureProvider, never())
|
|
||||||
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
|
|
||||||
doReturn(false).when(mBatteryHistEntry).isAppEntry();
|
|
||||||
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(
|
|
||||||
mPowerGaugePreference)).isTrue();
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(
|
|
||||||
SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
|
|
||||||
SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
/* package name */ "none",
|
|
||||||
/* percentage of total */ 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
|
|
||||||
mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils;
|
|
||||||
doReturn(true).when(mBatteryHistEntry).isAppEntry();
|
|
||||||
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(
|
|
||||||
mPowerGaugePreference)).isTrue();
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(
|
|
||||||
SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
|
|
||||||
SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
/* package name */ "none",
|
|
||||||
/* percentage of total */ 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ 0,
|
|
||||||
/*backgroundUsageTimeInMs=*/ 0));
|
|
||||||
assertThat(pref.getSummary()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setBackgroundUsageTimeOnly() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ 0,
|
|
||||||
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
|
|
||||||
assertThat(pref.getSummary()).isEqualTo("Background: 1 min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ 100,
|
|
||||||
/*backgroundUsageTimeInMs=*/ 200));
|
|
||||||
assertThat(pref.getSummary()).isEqualTo("Total: less than a min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
/*backgroundUsageTimeInMs=*/ 200));
|
|
||||||
assertThat(pref.getSummary())
|
|
||||||
.isEqualTo("Total: 1 min\nBackground: less than a min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setTotalAndBackgroundUsageTime() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
|
|
||||||
assertThat(pref.getSummary()).isEqualTo("Total: 2 min\nBackground: 1 min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() {
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
|
||||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
|
||||||
doReturn("label").when(mBatteryDiffEntry).getAppLabel();
|
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onExpand(/*isExpanded=*/ true);
|
|
||||||
|
|
||||||
final ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
|
|
||||||
verify(mAppListGroup).addPreference(captor.capture());
|
|
||||||
// Verifies the added preference.
|
|
||||||
assertThat(captor.getValue().getKey()).isEqualTo(PREF_KEY);
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(
|
|
||||||
mContext,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
|
|
||||||
true /*isExpanded*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() {
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
|
||||||
// Verifies the cache is empty first.
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onExpand(/*isExpanded=*/ false);
|
|
||||||
|
|
||||||
verify(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
verify(mAppListGroup).removePreference(mPowerGaugePreference);
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).hasSize(1);
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(
|
|
||||||
mContext,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
|
|
||||||
false /*isExpanded*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void refreshCategoryTitle_setLastFullChargeIntoBothTitleTextView() {
|
|
||||||
mBatteryChartPreferenceController = createController();
|
|
||||||
mBatteryChartPreferenceController.mAppListPrefGroup =
|
|
||||||
spy(new PreferenceCategory(mContext));
|
|
||||||
mBatteryChartPreferenceController.mExpandDividerPreference =
|
|
||||||
spy(new ExpandDividerPreference(mContext));
|
|
||||||
// Simulates select all condition.
|
|
||||||
mBatteryChartPreferenceController.mDailyChartIndex =
|
|
||||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
|
||||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshCategoryTitle();
|
|
||||||
|
|
||||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
|
||||||
// Verifies the title in the preference group.
|
|
||||||
verify(mBatteryChartPreferenceController.mAppListPrefGroup)
|
|
||||||
.setTitle(captor.capture());
|
|
||||||
assertThat(captor.getValue())
|
|
||||||
.isEqualTo("App usage since last full charge");
|
|
||||||
// Verifies the title in the expandable divider.
|
|
||||||
captor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(mBatteryChartPreferenceController.mExpandDividerPreference)
|
|
||||||
.setTitle(captor.capture());
|
|
||||||
assertThat(captor.getValue())
|
|
||||||
.isEqualTo("System usage since last full charge");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void selectedSlotText_selectAllDaysAllHours_returnNull() {
|
public void selectedSlotText_selectAllDaysAllHours_returnNull() {
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
||||||
@@ -640,16 +338,13 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
public void onSaveInstanceState_restoreSelectedIndexAndExpandState() {
|
public void onSaveInstanceState_restoreSelectedIndexAndExpandState() {
|
||||||
final int expectedDailyIndex = 1;
|
final int expectedDailyIndex = 1;
|
||||||
final int expectedHourlyIndex = 2;
|
final int expectedHourlyIndex = 2;
|
||||||
final boolean isExpanded = true;
|
|
||||||
final Bundle bundle = new Bundle();
|
final Bundle bundle = new Bundle();
|
||||||
mBatteryChartPreferenceController.mDailyChartIndex = expectedDailyIndex;
|
mBatteryChartPreferenceController.mDailyChartIndex = expectedDailyIndex;
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex;
|
mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex;
|
||||||
mBatteryChartPreferenceController.mIsExpanded = isExpanded;
|
|
||||||
mBatteryChartPreferenceController.onSaveInstanceState(bundle);
|
mBatteryChartPreferenceController.onSaveInstanceState(bundle);
|
||||||
// Replaces the original controller with other values.
|
// Replaces the original controller with other values.
|
||||||
mBatteryChartPreferenceController.mDailyChartIndex = -1;
|
mBatteryChartPreferenceController.mDailyChartIndex = -1;
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex = -1;
|
mBatteryChartPreferenceController.mHourlyChartIndex = -1;
|
||||||
mBatteryChartPreferenceController.mIsExpanded = false;
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onCreate(bundle);
|
mBatteryChartPreferenceController.onCreate(bundle);
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25));
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25));
|
||||||
@@ -658,7 +353,6 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
.isEqualTo(expectedDailyIndex);
|
.isEqualTo(expectedDailyIndex);
|
||||||
assertThat(mBatteryChartPreferenceController.mHourlyChartIndex)
|
assertThat(mBatteryChartPreferenceController.mHourlyChartIndex)
|
||||||
.isEqualTo(expectedHourlyIndex);
|
.isEqualTo(expectedHourlyIndex);
|
||||||
assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -704,36 +398,10 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
return batteryHistoryMap;
|
return batteryHistoryMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Integer, Map<Integer, BatteryDiffData>> createBatteryUsageMap() {
|
|
||||||
final int selectedAll = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
return Map.of(
|
|
||||||
selectedAll, Map.of(
|
|
||||||
selectedAll, new BatteryDiffData(
|
|
||||||
Arrays.asList(mBatteryDiffEntry),
|
|
||||||
Arrays.asList(mBatteryDiffEntry))),
|
|
||||||
0, Map.of(
|
|
||||||
selectedAll, new BatteryDiffData(
|
|
||||||
Arrays.asList(mBatteryDiffEntry),
|
|
||||||
Arrays.asList(mBatteryDiffEntry)),
|
|
||||||
0, new BatteryDiffData(
|
|
||||||
Arrays.asList(mBatteryDiffEntry),
|
|
||||||
Arrays.asList(mBatteryDiffEntry))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private BatteryDiffEntry createBatteryDiffEntry(
|
|
||||||
long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
|
|
||||||
return new BatteryDiffEntry(
|
|
||||||
mContext, foregroundUsageTimeInMs, backgroundUsageTimeInMs, /*consumePower=*/ 0,
|
|
||||||
/*foregroundUsageConsumePower=*/ 0, /*foregroundServiceUsageConsumePower=*/ 0,
|
|
||||||
/*backgroundUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0,
|
|
||||||
mBatteryHistEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BatteryChartPreferenceController createController() {
|
private BatteryChartPreferenceController createController() {
|
||||||
final BatteryChartPreferenceController controller =
|
final BatteryChartPreferenceController controller =
|
||||||
new BatteryChartPreferenceController(
|
new BatteryChartPreferenceController(
|
||||||
mContext, "app_list", /*lifecycle=*/ null,
|
mContext, /*lifecycle=*/ null, mSettingsActivity);
|
||||||
mSettingsActivity, mFragment);
|
|
||||||
controller.mPrefContext = mContext;
|
controller.mPrefContext = mContext;
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,310 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.batteryusage;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.LocaleList;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceGroup;
|
||||||
|
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public final class BatteryUsageBreakdownControllerTest {
|
||||||
|
private static final String PREF_KEY = "pref_key";
|
||||||
|
private static final String PREF_SUMMARY = "fake preference summary";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private InstrumentedPreferenceFragment mFragment;
|
||||||
|
@Mock
|
||||||
|
private SettingsActivity mSettingsActivity;
|
||||||
|
@Mock
|
||||||
|
private PreferenceGroup mAppListPreferenceGroup;
|
||||||
|
@Mock
|
||||||
|
private Drawable mDrawable;
|
||||||
|
@Mock
|
||||||
|
private BatteryHistEntry mBatteryHistEntry;
|
||||||
|
@Mock
|
||||||
|
private PowerGaugePreference mPowerGaugePreference;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
|
private BatteryDiffEntry mBatteryDiffEntry;
|
||||||
|
private MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
|
private BatteryUsageBreakdownController mBatteryUsageBreakdownController;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
Locale.setDefault(new Locale("en_US"));
|
||||||
|
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
|
||||||
|
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||||
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
|
mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
|
||||||
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
|
final Resources resources = spy(mContext.getResources());
|
||||||
|
resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US")));
|
||||||
|
doReturn(resources).when(mContext).getResources();
|
||||||
|
doReturn(new String[]{"com.android.gms.persistent"})
|
||||||
|
.when(mFeatureFactory.powerUsageFeatureProvider)
|
||||||
|
.getHideApplicationEntries(mContext);
|
||||||
|
mBatteryUsageBreakdownController = createController();
|
||||||
|
mBatteryUsageBreakdownController.mAppListPreferenceGroup = mAppListPreferenceGroup;
|
||||||
|
mBatteryDiffEntry = new BatteryDiffEntry(
|
||||||
|
mContext,
|
||||||
|
/*foregroundUsageTimeInMs=*/ 1,
|
||||||
|
/*backgroundUsageTimeInMs=*/ 2,
|
||||||
|
/*consumePower=*/ 3,
|
||||||
|
/*foregroundUsageConsumePower=*/ 0,
|
||||||
|
/*foregroundServiceUsageConsumePower=*/ 1,
|
||||||
|
/*backgroundUsageConsumePower=*/ 2,
|
||||||
|
/*cachedUsageConsumePower=*/ 0,
|
||||||
|
mBatteryHistEntry);
|
||||||
|
mBatteryDiffEntry = spy(mBatteryDiffEntry);
|
||||||
|
mBatteryUsageBreakdownController.mBatteryDiffData =
|
||||||
|
new BatteryDiffData(Arrays.asList(mBatteryDiffEntry), Arrays.asList());
|
||||||
|
// Adds fake testing data.
|
||||||
|
BatteryDiffEntry.sResourceCache.put(
|
||||||
|
"fakeBatteryDiffEntryKey",
|
||||||
|
new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDestroy_clearPreferenceCacheAndPreferenceGroupRemoveAll() {
|
||||||
|
// Ensures the testing environment is correct.
|
||||||
|
mBatteryUsageBreakdownController.mPreferenceCache.put(
|
||||||
|
PREF_KEY, mPowerGaugePreference);
|
||||||
|
assertThat(mBatteryUsageBreakdownController.mPreferenceCache).hasSize(1);
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.onDestroy();
|
||||||
|
|
||||||
|
assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDestroy_removeAllPreferenceFromPreferenceGroup() {
|
||||||
|
mBatteryUsageBreakdownController.onDestroy();
|
||||||
|
verify(mAppListPreferenceGroup).removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addAllPreferences_addAllPreferences() {
|
||||||
|
final String appLabel = "fake app label";
|
||||||
|
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
|
||||||
|
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
||||||
|
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
|
||||||
|
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||||
|
doReturn(null).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
|
||||||
|
doReturn(false).when(mBatteryDiffEntry).validForRestriction();
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.addAllPreferences();
|
||||||
|
|
||||||
|
// Verifies the preference cache.
|
||||||
|
final PowerGaugePreference pref =
|
||||||
|
(PowerGaugePreference) mBatteryUsageBreakdownController.mPreferenceCache
|
||||||
|
.get(PREF_KEY);
|
||||||
|
assertThat(pref).isNotNull();
|
||||||
|
// Verifies the added preference configuration.
|
||||||
|
verify(mAppListPreferenceGroup).addPreference(pref);
|
||||||
|
assertThat(pref.getKey()).isEqualTo(PREF_KEY);
|
||||||
|
assertThat(pref.getTitle().toString()).isEqualTo(appLabel);
|
||||||
|
assertThat(pref.getIcon()).isEqualTo(mDrawable);
|
||||||
|
assertThat(pref.getOrder()).isEqualTo(1);
|
||||||
|
assertThat(pref.getBatteryDiffEntry()).isSameInstanceAs(mBatteryDiffEntry);
|
||||||
|
assertThat(pref.isSingleLineTitle()).isTrue();
|
||||||
|
assertThat(pref.isEnabled()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
|
||||||
|
final String appLabel = "fake app label";
|
||||||
|
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
|
||||||
|
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
||||||
|
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
|
||||||
|
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||||
|
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.addAllPreferences();
|
||||||
|
|
||||||
|
verify(mAppListPreferenceGroup, never()).addPreference(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void removeAndCacheAllPreferences_buildCacheAndRemoveAllPreference() {
|
||||||
|
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
|
||||||
|
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
|
||||||
|
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||||
|
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
|
||||||
|
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
|
||||||
|
// Ensures the testing data is correct.
|
||||||
|
assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.removeAndCacheAllPreferences();
|
||||||
|
|
||||||
|
assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY))
|
||||||
|
.isEqualTo(mPowerGaugePreference);
|
||||||
|
verify(mAppListPreferenceGroup).removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() {
|
||||||
|
assertThat(mBatteryUsageBreakdownController
|
||||||
|
.handlePreferenceTreeClick(mAppListPreferenceGroup)).isFalse();
|
||||||
|
|
||||||
|
verify(mMetricsFeatureProvider, never())
|
||||||
|
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM);
|
||||||
|
verify(mMetricsFeatureProvider, never())
|
||||||
|
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
|
||||||
|
doReturn(false).when(mBatteryHistEntry).isAppEntry();
|
||||||
|
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
||||||
|
|
||||||
|
assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
|
||||||
|
mPowerGaugePreference)).isTrue();
|
||||||
|
verify(mMetricsFeatureProvider)
|
||||||
|
.action(
|
||||||
|
SettingsEnums.OPEN_BATTERY_USAGE,
|
||||||
|
SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
|
||||||
|
SettingsEnums.OPEN_BATTERY_USAGE,
|
||||||
|
/* package name */ "none",
|
||||||
|
/* percentage of total */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
|
||||||
|
doReturn(true).when(mBatteryHistEntry).isAppEntry();
|
||||||
|
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
||||||
|
|
||||||
|
assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
|
||||||
|
mPowerGaugePreference)).isTrue();
|
||||||
|
verify(mMetricsFeatureProvider)
|
||||||
|
.action(
|
||||||
|
SettingsEnums.OPEN_BATTERY_USAGE,
|
||||||
|
SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
|
||||||
|
SettingsEnums.OPEN_BATTERY_USAGE,
|
||||||
|
/* package name */ "none",
|
||||||
|
/* percentage of total */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
|
||||||
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.setPreferenceSummary(
|
||||||
|
pref, createBatteryDiffEntry(
|
||||||
|
/*foregroundUsageTimeInMs=*/ 0,
|
||||||
|
/*backgroundUsageTimeInMs=*/ 0));
|
||||||
|
assertThat(pref.getSummary()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setPreferenceSummary_setBackgroundUsageTimeOnly() {
|
||||||
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.setPreferenceSummary(
|
||||||
|
pref, createBatteryDiffEntry(
|
||||||
|
/*foregroundUsageTimeInMs=*/ 0,
|
||||||
|
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
|
||||||
|
assertThat(pref.getSummary().toString()).isEqualTo("Background: 1 min");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
|
||||||
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.setPreferenceSummary(
|
||||||
|
pref, createBatteryDiffEntry(
|
||||||
|
/*foregroundUsageTimeInMs=*/ 100,
|
||||||
|
/*backgroundUsageTimeInMs=*/ 200));
|
||||||
|
assertThat(pref.getSummary().toString()).isEqualTo("Total: less than a min");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
|
||||||
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.setPreferenceSummary(
|
||||||
|
pref, createBatteryDiffEntry(
|
||||||
|
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
|
||||||
|
/*backgroundUsageTimeInMs=*/ 200));
|
||||||
|
assertThat(pref.getSummary().toString())
|
||||||
|
.isEqualTo("Total: 1 min\nBackground: less than a min");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setPreferenceSummary_setTotalAndBackgroundUsageTime() {
|
||||||
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
|
mBatteryUsageBreakdownController.setPreferenceSummary(
|
||||||
|
pref, createBatteryDiffEntry(
|
||||||
|
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
|
||||||
|
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
|
||||||
|
assertThat(pref.getSummary().toString()).isEqualTo("Total: 2 min\nBackground: 1 min");
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatteryDiffEntry createBatteryDiffEntry(
|
||||||
|
long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
|
||||||
|
return new BatteryDiffEntry(
|
||||||
|
mContext, foregroundUsageTimeInMs, backgroundUsageTimeInMs, /*consumePower=*/ 0,
|
||||||
|
/*foregroundUsageConsumePower=*/ 0, /*foregroundServiceUsageConsumePower=*/ 0,
|
||||||
|
/*backgroundUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0,
|
||||||
|
mBatteryHistEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BatteryUsageBreakdownController createController() {
|
||||||
|
final BatteryUsageBreakdownController controller =
|
||||||
|
new BatteryUsageBreakdownController(
|
||||||
|
mContext, /*lifecycle=*/ null, mSettingsActivity, mFragment);
|
||||||
|
controller.mPrefContext = mContext;
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,106 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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.batteryusage;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
|
||||||
public final class ExpandDividerPreferenceTest {
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private ExpandDividerPreference mExpandDividerPreference;
|
|
||||||
|
|
||||||
private ImageView mImageView;
|
|
||||||
private TextView mTextView;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
|
||||||
mImageView = spy(new ImageView(mContext));
|
|
||||||
mTextView = spy(new TextView(mContext));
|
|
||||||
mExpandDividerPreference = new ExpandDividerPreference(mContext);
|
|
||||||
doReturn(R.id.expand_title).when(mTextView).getId();
|
|
||||||
doReturn(R.id.expand_icon).when(mImageView).getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testConstructor_returnExpectedResult() {
|
|
||||||
assertThat(mExpandDividerPreference.getKey())
|
|
||||||
.isEqualTo(ExpandDividerPreference.PREFERENCE_KEY);
|
|
||||||
assertThat(mExpandDividerPreference.getLayoutResource())
|
|
||||||
.isEqualTo(R.layout.preference_expand_divider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetTitle_setTitleContentIntoTextView() {
|
|
||||||
final String titleContent = "title content";
|
|
||||||
mExpandDividerPreference.mTextView = mTextView;
|
|
||||||
|
|
||||||
mExpandDividerPreference.setTitle(titleContent);
|
|
||||||
|
|
||||||
verify(mTextView).setText(titleContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnClick_switchExpandStateAndInvokeCallback() {
|
|
||||||
final boolean[] isExpandedArray = new boolean[]{false};
|
|
||||||
mExpandDividerPreference.mImageView = mImageView;
|
|
||||||
mExpandDividerPreference.setOnExpandListener(
|
|
||||||
isExpanded -> isExpandedArray[0] = isExpanded);
|
|
||||||
|
|
||||||
// Click the item first time from false -> true.
|
|
||||||
mExpandDividerPreference.onClick();
|
|
||||||
// Verifies the first time click result.
|
|
||||||
verify(mImageView).setImageResource(R.drawable.ic_settings_expand_less);
|
|
||||||
assertThat(isExpandedArray[0]).isTrue();
|
|
||||||
|
|
||||||
// Clicks the item second time from true -> false.
|
|
||||||
mExpandDividerPreference.onClick();
|
|
||||||
// Verifies the second time click result.
|
|
||||||
verify(mImageView).setImageResource(R.drawable.ic_settings_expand_more);
|
|
||||||
assertThat(isExpandedArray[0]).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetIsExpanded_updateStateButNotInvokeCallback() {
|
|
||||||
final boolean[] isExpandedArray = new boolean[]{false};
|
|
||||||
mExpandDividerPreference.mImageView = mImageView;
|
|
||||||
mExpandDividerPreference.setOnExpandListener(
|
|
||||||
isExpanded -> isExpandedArray[0] = isExpanded);
|
|
||||||
|
|
||||||
mExpandDividerPreference.setIsExpanded(true);
|
|
||||||
|
|
||||||
verify(mImageView).setImageResource(R.drawable.ic_settings_expand_less);
|
|
||||||
assertThat(isExpandedArray[0]).isFalse();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.batteryusage;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public final class TabPreferenceTest {
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private TabPreference mTabPreference;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Fragment mMockFragment;
|
||||||
|
@Mock
|
||||||
|
private TabLayout mMockTabLayout;
|
||||||
|
|
||||||
|
private final String[] mTabTitles = new String[]{"tab1", "tab2"};
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
|
mTabPreference = new TabPreference(mContext, /*attrs=*/ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructor_returnExpectedResult() {
|
||||||
|
assertThat(mTabPreference.getLayoutResource()).isEqualTo(R.layout.preference_tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void initializeTabs_returnExpectedResult() {
|
||||||
|
mTabPreference.initializeTabs(mMockFragment, mTabTitles);
|
||||||
|
assertThat(mTabPreference.mTabTitles).isEqualTo(mTabTitles);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onSaveInstanceState_returnExpectedResult() {
|
||||||
|
doReturn(1).when(mMockTabLayout).getSelectedTabPosition();
|
||||||
|
mTabPreference.mTabLayout = mMockTabLayout;
|
||||||
|
TabPreference.SavedState savedState =
|
||||||
|
(TabPreference.SavedState) mTabPreference.onSaveInstanceState();
|
||||||
|
assertThat(savedState.getTabPosition()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onRestoreInstanceState_returnExpectedResult() {
|
||||||
|
TabPreference.SavedState savedState =
|
||||||
|
new TabPreference.SavedState(Preference.BaseSavedState.EMPTY_STATE, 2);
|
||||||
|
mTabPreference.onRestoreInstanceState(savedState);
|
||||||
|
assertThat(mTabPreference.mSavedTabPosition).isEqualTo(2);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user