From 284e049cc1072dd77082c10d11637d06d1033ff6 Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Fri, 15 Jul 2022 15:54:47 +0800 Subject: [PATCH] Switch Battery Usage Chart from V1 to V2. Test: manual Bug: 236101166 Change-Id: I9142c0d4e00dea3771777ba9aedeab07b635fa1a --- res/layout/battery_chart_graph.xml | 14 +- res/values/dimens.xml | 1 - .../AppBatteryPreferenceController.java | 4 +- .../BatteryChartPreferenceController.java | 487 ++++++------ .../BatteryChartPreferenceControllerV2.java | 701 ------------------ .../batteryusage/BatteryChartView.java | 368 ++++----- .../batteryusage/BatteryChartViewModel.java | 2 +- .../batteryusage/BatteryChartViewV2.java | 671 ----------------- .../batteryusage/BatteryHistoryLoader.java | 2 +- .../BatteryHistoryPreference.java | 16 +- .../fuelgauge/batteryusage/ConvertUtils.java | 2 +- .../BatteryChartPreferenceControllerTest.java | 438 ++++++----- ...atteryChartPreferenceControllerV2Test.java | 684 ----------------- .../batteryusage/BatteryChartViewTest.java | 86 ++- .../batteryusage/BatteryChartViewV2Test.java | 245 ------ .../BatteryHistoryLoaderTest.java | 2 +- .../batteryusage/ConvertUtilsTest.java | 15 +- 17 files changed, 720 insertions(+), 3018 deletions(-) delete mode 100644 src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java delete mode 100644 src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java delete mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2Test.java delete mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java diff --git a/res/layout/battery_chart_graph.xml b/res/layout/battery_chart_graph.xml index 024f37cbf70..8b05b5191e8 100644 --- a/res/layout/battery_chart_graph.xml +++ b/res/layout/battery_chart_graph.xml @@ -32,10 +32,20 @@ android:text="@string/battery_usage_chart_graph_hint_last_full_charge" /> + + 5dp 1dp 2dp - 16dp 174dp diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java index 0b0dbfdee62..732163b6a36 100644 --- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java @@ -40,7 +40,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settings.fuelgauge.AdvancedPowerUsageDetail; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; -import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceControllerV2; +import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController; import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.fuelgauge.batteryusage.BatteryUsageStatsLoader; @@ -179,7 +179,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController return null; } final BatteryDiffEntry entry = - BatteryChartPreferenceControllerV2.getAppBatteryUsageData( + BatteryChartPreferenceController.getAppBatteryUsageData( mContext, mPackageName, mUserId); Log.d(TAG, "loadBatteryDiffEntries():\n" + entry); return entry; diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index d363308740a..a399b7d4420 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -20,7 +20,6 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -28,7 +27,9 @@ import android.text.TextUtils; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.Log; +import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; @@ -53,8 +54,6 @@ import com.android.settingslib.utils.StringUtil; import com.android.settingslib.widget.FooterPreference; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -62,27 +61,23 @@ import java.util.Map; /** Controls the update for chart graph and the list items. */ public class BatteryChartPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy, - OnSaveInstanceState, BatteryChartView.OnSelectListener, OnResume, - ExpandDividerPreference.OnExpandListener { + OnSaveInstanceState, OnResume, ExpandDividerPreference.OnExpandListener { private static final String TAG = "BatteryChartPreferenceController"; private static final String KEY_FOOTER_PREF = "battery_graph_footer"; private static final String PACKAGE_NAME_NONE = "none"; - /** Desired battery history size for timestamp slots. */ - public static final int DESIRED_HISTORY_SIZE = 25; - private static final int CHART_LEVEL_ARRAY_SIZE = 13; - private static final int CHART_KEY_ARRAY_SIZE = DESIRED_HISTORY_SIZE; private static final long VALID_USAGE_TIME_DURATION = DateUtils.HOUR_IN_MILLIS * 2; private static final long VALID_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3; // Keys for bundle instance to restore configurations. private static final String KEY_EXPAND_SYSTEM_INFO = "expand_system_info"; - private static final String KEY_CURRENT_TIME_SLOT = "current_time_slot"; + private static final String KEY_DAILY_CHART_INDEX = "daily_chart_index"; + private static final String KEY_HOURLY_CHART_INDEX = "hourly_chart_index"; private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED; @VisibleForTesting - Map> mBatteryIndexedMap; + Map> mBatteryUsageMap; @VisibleForTesting Context mPrefContext; @@ -91,28 +86,34 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll @VisibleForTesting PreferenceGroup mAppListPrefGroup; @VisibleForTesting - BatteryChartView mBatteryChartView; - @VisibleForTesting ExpandDividerPreference mExpandDividerPreference; - @VisibleForTesting boolean mIsExpanded = false; - @VisibleForTesting - int[] mBatteryHistoryLevels; - @VisibleForTesting - long[] mBatteryHistoryKeys; - @VisibleForTesting - int mTrapezoidIndex = BatteryChartView.SELECTED_INDEX_INVALID; - private boolean mIs24HourFormat = false; + @VisibleForTesting + BatteryChartView mDailyChartView; + @VisibleForTesting + BatteryChartView mHourlyChartView; + + @VisibleForTesting + int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; + @VisibleForTesting + int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; + + private boolean mIs24HourFormat; private boolean mIsFooterPrefAdded = false; private PreferenceScreen mPreferenceScreen; private FooterPreference mFooterPreference; + // Daily view model only saves abbreviated day of week texts (e.g. MON). This field saves the + // full day of week texts (e.g. Monday), which is used in category title and battery detail + // page. + private List mDailyTimestampFullTexts; + private BatteryChartViewModel mDailyViewModel; + private List mHourlyViewModels; private final String mPreferenceKey; private final SettingsActivity mActivity; private final InstrumentedPreferenceFragment mFragment; - private final CharSequence[] mNotAllowShowEntryPackages; private final CharSequence[] mNotAllowShowSummaryPackages; private final MetricsFeatureProvider mMetricsFeatureProvider; private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -120,8 +121,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll // Preference cache to avoid create new instance each time. @VisibleForTesting final Map mPreferenceCache = new HashMap<>(); - @VisibleForTesting - final List mSystemEntries = new ArrayList<>(); public BatteryChartPreferenceController( Context context, String preferenceKey, @@ -134,10 +133,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mIs24HourFormat = DateFormat.is24HourFormat(context); mMetricsFeatureProvider = FeatureFactory.getFactory(mContext).getMetricsFeatureProvider(); - mNotAllowShowEntryPackages = - FeatureFactory.getFactory(context) - .getPowerUsageFeatureProvider(context) - .getHideApplicationEntries(context); mNotAllowShowSummaryPackages = FeatureFactory.getFactory(context) .getPowerUsageFeatureProvider(context) @@ -152,12 +147,14 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll if (savedInstanceState == null) { return; } - mTrapezoidIndex = - savedInstanceState.getInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex); + mDailyChartIndex = + savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex); + mHourlyChartIndex = + savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex); mIsExpanded = savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded); - Log.d(TAG, String.format("onCreate() slotIndex=%d isExpanded=%b", - mTrapezoidIndex, mIsExpanded)); + Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d isExpanded=%b", + mDailyChartIndex, mHourlyChartIndex, mIsExpanded)); } @Override @@ -179,10 +176,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll if (savedInstance == null) { return; } - savedInstance.putInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex); + savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex); + savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex); savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded); - Log.d(TAG, String.format("onSaveInstanceState() slotIndex=%d isExpanded=%b", - mTrapezoidIndex, mIsExpanded)); + Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d isExpanded=%b", + mDailyChartIndex, mHourlyChartIndex, mIsExpanded)); } @Override @@ -204,8 +202,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mPrefContext = screen.getContext(); mAppListPrefGroup = screen.findPreference(mPreferenceKey); mAppListPrefGroup.setOrderingAsAdded(false); - mAppListPrefGroup.setTitle( - mPrefContext.getString(R.string.battery_app_usage_for_past_24)); + mAppListPrefGroup.setTitle(mPrefContext.getString(R.string.battery_app_usage)); mFooterPreference = screen.findPreference(KEY_FOOTER_PREF); // Removes footer first until usage data is loaded to avoid flashing. if (mFooterPreference != null) { @@ -249,17 +246,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll return true; } - @Override - public void onSelect(int trapezoidIndex) { - Log.d(TAG, "onChartSelect:" + trapezoidIndex); - refreshUi(trapezoidIndex, /*isForce=*/ false); - mMetricsFeatureProvider.action( - mPrefContext, - trapezoidIndex == BatteryChartView.SELECTED_INDEX_ALL - ? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL - : SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT); - } - @Override public void onExpand(boolean isExpanded) { mIsExpanded = isExpanded; @@ -272,81 +258,119 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll void setBatteryHistoryMap( final Map> batteryHistoryMap) { - // Resets all battery history data relative variables. - if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) { - mBatteryIndexedMap = null; - mBatteryHistoryKeys = null; - mBatteryHistoryLevels = null; - addFooterPreferenceIfNeeded(false); + Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null" + : ("size=" + batteryHistoryMap.size()))); + final BatteryLevelData batteryLevelData = + DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap, + batteryUsageMap -> { + mBatteryUsageMap = batteryUsageMap; + refreshUi(); + }); + Log.d(TAG, "getBatteryLevelData: " + batteryLevelData); + if (batteryLevelData == null) { + mDailyTimestampFullTexts = null; + mDailyViewModel = null; + mHourlyViewModels = null; + refreshUi(); return; } - mBatteryHistoryKeys = getBatteryHistoryKeys(batteryHistoryMap); - mBatteryHistoryLevels = new int[CHART_LEVEL_ARRAY_SIZE]; - for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) { - final long timestamp = mBatteryHistoryKeys[index * 2]; - final Map entryMap = batteryHistoryMap.get(timestamp); - if (entryMap == null || entryMap.isEmpty()) { - Log.e(TAG, "abnormal entry list in the timestamp:" - + ConvertUtils.utcToLocalTime(mPrefContext, timestamp)); - continue; + mDailyTimestampFullTexts = generateTimestampDayOfWeekTexts( + mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(), + /* isAbbreviation= */ false); + mDailyViewModel = new BatteryChartViewModel( + batteryLevelData.getDailyBatteryLevels().getLevels(), + generateTimestampDayOfWeekTexts( + mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(), + /* isAbbreviation= */ true), + BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS); + mHourlyViewModels = new ArrayList<>(); + for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay : + batteryLevelData.getHourlyBatteryLevelsPerDay()) { + mHourlyViewModels.add(new BatteryChartViewModel( + hourlyBatteryLevelsPerDay.getLevels(), + generateTimestampHourTexts( + mContext, hourlyBatteryLevelsPerDay.getTimestamps()), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); + } + refreshUi(); + } + + void setBatteryChartView(@NonNull final BatteryChartView dailyChartView, + @NonNull final BatteryChartView hourlyChartView) { + if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) { + mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView)); + } + } + + private void setBatteryChartViewInner(@NonNull final BatteryChartView dailyChartView, + @NonNull final BatteryChartView hourlyChartView) { + mDailyChartView = dailyChartView; + mDailyChartView.setOnSelectListener(trapezoidIndex -> { + if (mDailyChartIndex == trapezoidIndex) { + return; } - // Averages the battery level in each time slot to avoid corner conditions. - float batteryLevelCounter = 0; - for (BatteryHistEntry entry : entryMap.values()) { - batteryLevelCounter += entry.mBatteryLevel; + Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex); + mDailyChartIndex = trapezoidIndex; + mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; + refreshUi(); + // TODO: Change to log daily data. + }); + mHourlyChartView = hourlyChartView; + mHourlyChartView.setOnSelectListener(trapezoidIndex -> { + if (mHourlyChartIndex == trapezoidIndex) { + return; } - mBatteryHistoryLevels[index] = - Math.round(batteryLevelCounter / entryMap.size()); - } - forceRefreshUi(); - Log.d(TAG, String.format( - "setBatteryHistoryMap() size=%d key=%s\nlevels=%s", - batteryHistoryMap.size(), - ConvertUtils.utcToLocalTime(mPrefContext, - mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1]), - Arrays.toString(mBatteryHistoryLevels))); - - // Loads item icon and label in the background. - new LoadAllItemsInfoTask(batteryHistoryMap).execute(); - } - - void setBatteryChartView(final BatteryChartView batteryChartView) { - if (mBatteryChartView != batteryChartView) { - mHandler.post(() -> setBatteryChartViewInner(batteryChartView)); - } - } - - private void setBatteryChartViewInner(final BatteryChartView batteryChartView) { - mBatteryChartView = batteryChartView; - mBatteryChartView.setOnSelectListener(this); - forceRefreshUi(); - } - - private void forceRefreshUi() { - final int refreshIndex = - mTrapezoidIndex == BatteryChartView.SELECTED_INDEX_INVALID - ? BatteryChartView.SELECTED_INDEX_ALL - : mTrapezoidIndex; - if (mBatteryChartView != null) { - mBatteryChartView.setLevels(mBatteryHistoryLevels); - mBatteryChartView.setSelectedIndex(refreshIndex); - setTimestampLabel(); - } - refreshUi(refreshIndex, /*isForce=*/ true); + Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex); + mHourlyChartIndex = trapezoidIndex; + refreshUi(); + mMetricsFeatureProvider.action( + mPrefContext, + trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL + ? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL + : SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT); + }); + refreshUi(); } @VisibleForTesting - boolean refreshUi(int trapezoidIndex, boolean isForce) { - // Invalid refresh condition. - if (mBatteryIndexedMap == null - || mBatteryChartView == null - || (mTrapezoidIndex == trapezoidIndex && !isForce)) { + boolean refreshUi() { + if (mDailyChartView == null || mHourlyChartView == null) { + // Chart views are not initialized. + return false; + } + if (mDailyViewModel == null || mHourlyViewModels == null) { + // Fail to get battery level data, show an empty hourly chart view. + mDailyChartView.setVisibility(View.GONE); + mHourlyChartView.setVisibility(View.VISIBLE); + mHourlyChartView.setViewModel(null); + addFooterPreferenceIfNeeded(false); + return false; + } + if (mBatteryUsageMap == null) { + // Battery usage data is not ready, wait for data ready to refresh UI. return false; } - Log.d(TAG, String.format("refreshUi: index=%d size=%d isForce:%b", - trapezoidIndex, mBatteryIndexedMap.size(), isForce)); - mTrapezoidIndex = trapezoidIndex; + if (isBatteryLevelDataInOneDay()) { + // Only 1 day data, hide the daily chart view. + mDailyChartView.setVisibility(View.GONE); + mDailyChartIndex = 0; + } else { + mDailyChartView.setVisibility(View.VISIBLE); + mDailyViewModel.setSelectedIndex(mDailyChartIndex); + mDailyChartView.setViewModel(mDailyViewModel); + } + + if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) { + // Multiple days are selected, hide the hourly chart view. + mHourlyChartView.setVisibility(View.GONE); + } else { + mHourlyChartView.setVisibility(View.VISIBLE); + final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex); + hourlyViewModel.setSelectedIndex(mHourlyChartIndex); + mHourlyChartView.setViewModel(hourlyViewModel); + } + mHandler.post(() -> { final long start = System.currentTimeMillis(); removeAndCacheAllPrefs(); @@ -359,43 +383,22 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } private void addAllPreferences() { - final List entries = - mBatteryIndexedMap.get(Integer.valueOf(mTrapezoidIndex)); - addFooterPreferenceIfNeeded(entries != null && !entries.isEmpty()); - if (entries == null) { - Log.w(TAG, "cannot find BatteryDiffEntry for:" + mTrapezoidIndex); + 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; } - // Separates data into two groups and sort them individually. - final List appEntries = new ArrayList<>(); - mSystemEntries.clear(); - entries.forEach(entry -> { - final String packageName = entry.getPackageName(); - if (!isValidToShowEntry(packageName)) { - Log.w(TAG, "ignore showing item:" + packageName); - return; - } - if (entry.isSystemEntry()) { - mSystemEntries.add(entry); - } else { - appEntries.add(entry); - } - // Validates the usage time if users click a specific slot. - if (mTrapezoidIndex >= 0) { - validateUsageTime(entry); - } - }); - Collections.sort(appEntries, BatteryDiffEntry.COMPARATOR); - Collections.sort(mSystemEntries, BatteryDiffEntry.COMPARATOR); - Log.d(TAG, String.format("addAllPreferences() app=%d system=%d", - appEntries.size(), mSystemEntries.size())); - // Adds app entries to the list if it is not empty. - if (!appEntries.isEmpty()) { - addPreferenceToScreen(appEntries); + if (!batteryDiffData.getAppDiffEntryList().isEmpty()) { + addPreferenceToScreen(batteryDiffData.getAppDiffEntryList()); } // Adds the expabable divider if we have system entries data. - if (!mSystemEntries.isEmpty()) { + if (!batteryDiffData.getSystemDiffEntryList().isEmpty()) { if (mExpandDividerPreference == null) { mExpandDividerPreference = new ExpandDividerPreference(mPrefContext); mExpandDividerPreference.setOnExpandListener(this); @@ -469,11 +472,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } private void refreshExpandUi() { + final List systemEntries = mBatteryUsageMap.get(mDailyChartIndex).get( + mHourlyChartIndex).getSystemDiffEntryList(); if (mIsExpanded) { - addPreferenceToScreen(mSystemEntries); + addPreferenceToScreen(systemEntries); } else { // Removes and recycles all system entries to hide all of them. - for (BatteryDiffEntry entry : mSystemEntries) { + for (BatteryDiffEntry entry : systemEntries) { final String prefKey = entry.mBatteryHistEntry.getKey(); final Preference pref = mAppListPrefGroup.findPreference(prefKey); if (pref != null) { @@ -499,11 +504,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } 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_for_past_24) - : mPrefContext.getString(R.string.battery_system_usage_for_past_24); + ? 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) @@ -511,17 +517,33 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } } - private String getSlotInformation() { - if (mTrapezoidIndex < 0) { + @VisibleForTesting + String getSlotInformation() { + if (mDailyTimestampFullTexts == null || mDailyViewModel == null + || mHourlyViewModels == null) { + // No data return null; } - final String fromHour = ConvertUtils.utcToLocalTimeHour(mPrefContext, - mBatteryHistoryKeys[mTrapezoidIndex * 2], mIs24HourFormat); - final String toHour = ConvertUtils.utcToLocalTimeHour(mPrefContext, - mBatteryHistoryKeys[(mTrapezoidIndex + 1) * 2], mIs24HourFormat); - return mIs24HourFormat - ? String.format("%s–%s", fromHour, toHour) - : String.format("%s – %s", fromHour, toHour); + if (isAllSelected()) { + return null; + } + + final String selectedDayText = mDailyTimestampFullTexts.get(mDailyChartIndex); + if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) { + return selectedDayText; + } + + final String fromHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get( + mHourlyChartIndex); + final String toHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get( + mHourlyChartIndex + 1); + final String selectedHourText = + String.format("%s%s%s", fromHourText, mIs24HourFormat ? "-" : " - ", toHourText); + if (isBatteryLevelDataInOneDay()) { + return selectedHourText; + } + + return String.format("%s %s", selectedDayText, selectedHourText); } @VisibleForTesting @@ -575,22 +597,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll @VisibleForTesting boolean isValidToShowSummary(String packageName) { - return !contains(packageName, mNotAllowShowSummaryPackages); - } - - @VisibleForTesting - boolean isValidToShowEntry(String packageName) { - return !contains(packageName, mNotAllowShowEntryPackages); - } - - @VisibleForTesting - void setTimestampLabel() { - if (mBatteryChartView == null || mBatteryHistoryKeys == null) { - return; - } - final long latestTimestamp = - mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1]; - mBatteryChartView.setLatestTimestamp(latestTimestamp); + return !DataProcessor.contains(packageName, mNotAllowShowSummaryPackages); } private void addFooterPreferenceIfNeeded(boolean containAppItems) { @@ -605,60 +612,65 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference)); } - private static boolean contains(String target, CharSequence[] packageNames) { - if (target != null && packageNames != null) { - for (CharSequence packageName : packageNames) { - if (TextUtils.equals(target, packageName)) { - return true; - } - } - } - return false; + private boolean isBatteryLevelDataInOneDay() { + return mHourlyViewModels != null && mHourlyViewModels.size() == 1; } - @VisibleForTesting - static boolean validateUsageTime(BatteryDiffEntry entry) { - final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs; - final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs; - final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs; - if (foregroundUsageTimeInMs > VALID_USAGE_TIME_DURATION - || backgroundUsageTimeInMs > VALID_USAGE_TIME_DURATION - || totalUsageTimeInMs > VALID_USAGE_TIME_DURATION) { - Log.e(TAG, "validateUsageTime() fail for\n" + entry); - return false; + private boolean isAllSelected() { + return (isBatteryLevelDataInOneDay() + || mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) + && mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL; + } + + private static List generateTimestampDayOfWeekTexts(@NonNull final Context context, + @NonNull final List timestamps, final boolean isAbbreviation) { + final ArrayList texts = new ArrayList<>(); + for (Long timestamp : timestamps) { + texts.add(ConvertUtils.utcToLocalTimeDayOfWeek(context, timestamp, isAbbreviation)); } - return true; + return texts; + } + + private static List generateTimestampHourTexts( + @NonNull final Context context, @NonNull final List timestamps) { + final boolean is24HourFormat = DateFormat.is24HourFormat(context); + final ArrayList texts = new ArrayList<>(); + for (Long timestamp : timestamps) { + texts.add(ConvertUtils.utcToLocalTimeHour(context, timestamp, is24HourFormat)); + } + return texts; } /** Used for {@link AppBatteryPreferenceController}. */ - public static List getBatteryLast24HrUsageData(Context context) { + public static List getAppBatteryUsageData(Context context) { final long start = System.currentTimeMillis(); final Map> batteryHistoryMap = FeatureFactory.getFactory(context) .getPowerUsageFeatureProvider(context) - .getBatteryHistory(context); + .getBatteryHistorySinceLastFullCharge(context); if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) { return null; } - Log.d(TAG, String.format("getBatteryLast24HrData() size=%d time=&d/ms", + Log.d(TAG, String.format("getBatterySinceLastFullChargeUsageData() size=%d time=%d/ms", batteryHistoryMap.size(), (System.currentTimeMillis() - start))); - final Map> batteryIndexedMap = - ConvertUtils.getIndexedUsageMap( - context, - /*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1, - getBatteryHistoryKeys(batteryHistoryMap), - batteryHistoryMap, - /*purgeLowPercentageAndFakeData=*/ true); - return batteryIndexedMap.get(BatteryChartView.SELECTED_INDEX_ALL); + + final Map> batteryUsageData = + DataProcessor.getBatteryUsageData(context, batteryHistoryMap); + return batteryUsageData == null + ? null + : batteryUsageData + .get(BatteryChartViewModel.SELECTED_INDEX_ALL) + .get(BatteryChartViewModel.SELECTED_INDEX_ALL) + .getAppDiffEntryList(); } /** Used for {@link AppBatteryPreferenceController}. */ - public static BatteryDiffEntry getBatteryLast24HrUsageData( + public static BatteryDiffEntry getAppBatteryUsageData( Context context, String packageName, int userId) { if (packageName == null) { return null; } - final List entries = getBatteryLast24HrUsageData(context); + final List entries = getAppBatteryUsageData(context); if (entries == null) { return null; } @@ -673,65 +685,4 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } return null; } - - private static long[] getBatteryHistoryKeys( - final Map> batteryHistoryMap) { - final List batteryHistoryKeyList = - new ArrayList<>(batteryHistoryMap.keySet()); - Collections.sort(batteryHistoryKeyList); - final long[] batteryHistoryKeys = new long[CHART_KEY_ARRAY_SIZE]; - for (int index = 0; index < CHART_KEY_ARRAY_SIZE; index++) { - batteryHistoryKeys[index] = batteryHistoryKeyList.get(index); - } - return batteryHistoryKeys; - } - - // Loads all items icon and label in the background. - private final class LoadAllItemsInfoTask - extends AsyncTask>> { - - private long[] mBatteryHistoryKeysCache; - private Map> mBatteryHistoryMap; - - private LoadAllItemsInfoTask( - Map> batteryHistoryMap) { - this.mBatteryHistoryMap = batteryHistoryMap; - this.mBatteryHistoryKeysCache = mBatteryHistoryKeys; - } - - @Override - protected Map> doInBackground(Void... voids) { - if (mPrefContext == null || mBatteryHistoryKeysCache == null) { - return null; - } - final long startTime = System.currentTimeMillis(); - final Map> indexedUsageMap = - ConvertUtils.getIndexedUsageMap( - mPrefContext, /*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1, - mBatteryHistoryKeysCache, mBatteryHistoryMap, - /*purgeLowPercentageAndFakeData=*/ true); - // Pre-loads each BatteryDiffEntry relative icon and label for all slots. - for (List entries : indexedUsageMap.values()) { - entries.forEach(entry -> entry.loadLabelAndIcon()); - } - Log.d(TAG, String.format("execute LoadAllItemsInfoTask in %d/ms", - (System.currentTimeMillis() - startTime))); - return indexedUsageMap; - } - - @Override - protected void onPostExecute( - Map> indexedUsageMap) { - mBatteryHistoryMap = null; - mBatteryHistoryKeysCache = null; - if (indexedUsageMap == null) { - return; - } - // Posts results back to main thread to refresh UI. - mHandler.post(() -> { - mBatteryIndexedMap = indexedUsageMap; - forceRefreshUi(); - }); - } - } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java deleted file mode 100644 index e743e22215a..00000000000 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java +++ /dev/null @@ -1,701 +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.app.settings.SettingsEnums; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.util.Log; -import android.view.View; -import android.widget.LinearLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; -import androidx.preference.PreferenceGroup; -import androidx.preference.PreferenceScreen; - -import com.android.settings.R; -import com.android.settings.SettingsActivity; -import com.android.settings.core.InstrumentedPreferenceFragment; -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.settingslib.core.AbstractPreferenceController; -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.OnCreate; -import com.android.settingslib.core.lifecycle.events.OnDestroy; -import com.android.settingslib.core.lifecycle.events.OnResume; -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.HashMap; -import java.util.List; -import java.util.Map; - -/** Controls the update for chart graph and the list items. */ -public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceController - implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy, - OnSaveInstanceState, OnResume, ExpandDividerPreference.OnExpandListener { - private static final String TAG = "BatteryChartPreferenceControllerV2"; - private static final String KEY_FOOTER_PREF = "battery_graph_footer"; - private static final String PACKAGE_NAME_NONE = "none"; - - private static final int TWENTY_FOUR_HOURS_TIME_SLOT_SIZE = 12; - private static final long VALID_USAGE_TIME_DURATION = DateUtils.HOUR_IN_MILLIS * 2; - private static final long VALID_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3; - - // 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_HOURLY_CHART_INDEX = "hourly_chart_index"; - - private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED; - - @VisibleForTesting - Map> mBatteryUsageMap; - - @VisibleForTesting - Context mPrefContext; - @VisibleForTesting - BatteryUtils mBatteryUtils; - @VisibleForTesting - PreferenceGroup mAppListPrefGroup; - @VisibleForTesting - ExpandDividerPreference mExpandDividerPreference; - @VisibleForTesting - boolean mIsExpanded = false; - - @VisibleForTesting - BatteryChartViewV2 mDailyChartView; - @VisibleForTesting - BatteryChartViewV2 mHourlyChartView; - - @VisibleForTesting - int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; - @VisibleForTesting - int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; - - private boolean mIs24HourFormat; - private boolean mIsFooterPrefAdded = false; - private PreferenceScreen mPreferenceScreen; - private FooterPreference mFooterPreference; - // Daily view model only saves abbreviated day of week texts (e.g. MON). This field saves the - // full day of week texts (e.g. Monday), which is used in category title and battery detail - // page. - private List mDailyTimestampFullTexts; - private BatteryChartViewModel mDailyViewModel; - private List mHourlyViewModels; - - private final String mPreferenceKey; - private final SettingsActivity mActivity; - private final InstrumentedPreferenceFragment mFragment; - private final CharSequence[] mNotAllowShowSummaryPackages; - private final MetricsFeatureProvider mMetricsFeatureProvider; - private final Handler mHandler = new Handler(Looper.getMainLooper()); - - // Preference cache to avoid create new instance each time. - @VisibleForTesting - final Map mPreferenceCache = new HashMap<>(); - - public BatteryChartPreferenceControllerV2( - Context context, String preferenceKey, - Lifecycle lifecycle, SettingsActivity activity, - InstrumentedPreferenceFragment fragment) { - super(context); - mActivity = activity; - mFragment = fragment; - mPreferenceKey = preferenceKey; - mIs24HourFormat = DateFormat.is24HourFormat(context); - mMetricsFeatureProvider = - FeatureFactory.getFactory(mContext).getMetricsFeatureProvider(); - mNotAllowShowSummaryPackages = - FeatureFactory.getFactory(context) - .getPowerUsageFeatureProvider(context) - .getHideApplicationSummary(context); - if (lifecycle != null) { - lifecycle.addObserver(this); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - if (savedInstanceState == null) { - return; - } - mDailyChartIndex = - savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex); - mHourlyChartIndex = - savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex); - mIsExpanded = - savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded); - Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d isExpanded=%b", - mDailyChartIndex, mHourlyChartIndex, mIsExpanded)); - } - - @Override - public void onResume() { - final int currentUiMode = - mContext.getResources().getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_MASK; - if (sUiMode != currentUiMode) { - sUiMode = currentUiMode; - BatteryDiffEntry.clearCache(); - Log.d(TAG, "clear icon and label cache since uiMode is changed"); - } - mIs24HourFormat = DateFormat.is24HourFormat(mContext); - mMetricsFeatureProvider.action(mPrefContext, SettingsEnums.OPEN_BATTERY_USAGE); - } - - @Override - public void onSaveInstanceState(Bundle savedInstance) { - if (savedInstance == null) { - return; - } - savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex); - savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex); - savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded); - Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d isExpanded=%b", - mDailyChartIndex, mHourlyChartIndex, mIsExpanded)); - } - - @Override - public void onDestroy() { - if (mActivity.isChangingConfigurations()) { - BatteryDiffEntry.clearCache(); - } - mHandler.removeCallbacksAndMessages(/*token=*/ null); - mPreferenceCache.clear(); - if (mAppListPrefGroup != null) { - mAppListPrefGroup.removeAll(); - } - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - mPreferenceScreen = screen; - mPrefContext = screen.getContext(); - mAppListPrefGroup = screen.findPreference(mPreferenceKey); - mAppListPrefGroup.setOrderingAsAdded(false); - mAppListPrefGroup.setTitle(mPrefContext.getString(R.string.battery_app_usage)); - mFooterPreference = screen.findPreference(KEY_FOOTER_PREF); - // Removes footer first until usage data is loaded to avoid flashing. - if (mFooterPreference != null) { - screen.removePreference(mFooterPreference); - } - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public String getPreferenceKey() { - return mPreferenceKey; - } - - @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(), - isValidToShowSummary(packageName), getSlotInformation()); - return true; - } - - @Override - public void onExpand(boolean isExpanded) { - mIsExpanded = isExpanded; - mMetricsFeatureProvider.action( - mPrefContext, - SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM, - isExpanded); - refreshExpandUi(); - } - - void setBatteryHistoryMap( - final Map> batteryHistoryMap) { - Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null" - : ("size=" + batteryHistoryMap.size()))); - final BatteryLevelData batteryLevelData = - DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap, - batteryUsageMap -> { - mBatteryUsageMap = batteryUsageMap; - refreshUi(); - }); - Log.d(TAG, "getBatteryLevelData: " + batteryLevelData); - if (batteryLevelData == null) { - mDailyTimestampFullTexts = null; - mDailyViewModel = null; - mHourlyViewModels = null; - refreshUi(); - return; - } - mDailyTimestampFullTexts = generateTimestampDayOfWeekTexts( - mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(), - /* isAbbreviation= */ false); - mDailyViewModel = new BatteryChartViewModel( - batteryLevelData.getDailyBatteryLevels().getLevels(), - generateTimestampDayOfWeekTexts( - mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(), - /* isAbbreviation= */ true), - BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS); - mHourlyViewModels = new ArrayList<>(); - for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay : - batteryLevelData.getHourlyBatteryLevelsPerDay()) { - mHourlyViewModels.add(new BatteryChartViewModel( - hourlyBatteryLevelsPerDay.getLevels(), - generateTimestampHourTexts( - mContext, hourlyBatteryLevelsPerDay.getTimestamps()), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); - } - refreshUi(); - } - - void setBatteryChartView(@NonNull final BatteryChartViewV2 dailyChartView, - @NonNull final BatteryChartViewV2 hourlyChartView) { - if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) { - mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView)); - } - } - - private void setBatteryChartViewInner(@NonNull final BatteryChartViewV2 dailyChartView, - @NonNull final BatteryChartViewV2 hourlyChartView) { - mDailyChartView = dailyChartView; - mDailyChartView.setOnSelectListener(trapezoidIndex -> { - if (mDailyChartIndex == trapezoidIndex) { - return; - } - Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex); - mDailyChartIndex = trapezoidIndex; - mHourlyChartIndex = BatteryChartView.SELECTED_INDEX_ALL; - refreshUi(); - // TODO: Change to log daily data. - }); - mHourlyChartView = hourlyChartView; - mHourlyChartView.setOnSelectListener(trapezoidIndex -> { - if (mHourlyChartIndex == trapezoidIndex) { - return; - } - Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex); - mHourlyChartIndex = trapezoidIndex; - refreshUi(); - mMetricsFeatureProvider.action( - mPrefContext, - trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL - ? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL - : SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT); - }); - refreshUi(); - } - - @VisibleForTesting - boolean refreshUi() { - if (mDailyChartView == null || mHourlyChartView == null) { - // Chart views are not initialized. - return false; - } - if (mDailyViewModel == null || mHourlyViewModels == null) { - // Fail to get battery level data, show an empty hourly chart view. - mDailyChartView.setVisibility(View.GONE); - mHourlyChartView.setVisibility(View.VISIBLE); - mHourlyChartView.setViewModel(null); - addFooterPreferenceIfNeeded(false); - return false; - } - if (mBatteryUsageMap == null) { - // Battery usage data is not ready, wait for data ready to refresh UI. - return false; - } - - if (isBatteryLevelDataInOneDay()) { - // Only 1 day data, hide the daily chart view. - mDailyChartView.setVisibility(View.GONE); - mDailyChartIndex = 0; - } else { - mDailyChartView.setVisibility(View.VISIBLE); - mDailyViewModel.setSelectedIndex(mDailyChartIndex); - mDailyChartView.setViewModel(mDailyViewModel); - } - - if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) { - // Multiple days are selected, hide the hourly chart view. - mHourlyChartView.setVisibility(View.GONE); - updateMarginBetweenCharts(false); - } else { - mHourlyChartView.setVisibility(View.VISIBLE); - final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex); - hourlyViewModel.setSelectedIndex(mHourlyChartIndex); - mHourlyChartView.setViewModel(hourlyViewModel); - - updateMarginBetweenCharts(true); - } - - mHandler.post(() -> { - final long start = System.currentTimeMillis(); - removeAndCacheAllPrefs(); - addAllPreferences(); - refreshCategoryTitle(); - Log.d(TAG, String.format("refreshUi is finished in %d/ms", - (System.currentTimeMillis() - start))); - }); - return true; - } - - private void updateMarginBetweenCharts(boolean addMargin) { - final LinearLayout.LayoutParams layoutParams = - (LinearLayout.LayoutParams) mDailyChartView.getLayoutParams(); - layoutParams.bottomMargin = addMargin ? Math.round( - mContext.getResources().getDimension(R.dimen.chartview_two_charts_margin)) : 0; - mDailyChartView.setLayoutParams(layoutParams); - } - - 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 expabable 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 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); - } - 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 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 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 - String getSlotInformation() { - if (mDailyTimestampFullTexts == null || mDailyViewModel == null - || mHourlyViewModels == null) { - // No data - return null; - } - if (isAllSelected()) { - return null; - } - - final String selectedDayText = mDailyTimestampFullTexts.get(mDailyChartIndex); - if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) { - return selectedDayText; - } - - final String fromHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get( - mHourlyChartIndex); - final String toHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get( - mHourlyChartIndex + 1); - final String selectedHourText = - String.format("%s%s%s", fromHourText, mIs24HourFormat ? "-" : " - ", toHourText); - if (isBatteryLevelDataInOneDay()) { - return 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; - // Checks whether the package is allowed to show summary or not. - if (!isValidToShowSummary(entry.getPackageName())) { - preference.setSummary(null); - return; - } - 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); - } - - @VisibleForTesting - boolean isValidToShowSummary(String packageName) { - return !DataProcessor.contains(packageName, mNotAllowShowSummaryPackages); - } - - 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() { - return mHourlyViewModels != null && mHourlyViewModels.size() == 1; - } - - private boolean isAllSelected() { - return (isBatteryLevelDataInOneDay() - || mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) - && mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL; - } - - private static List generateTimestampDayOfWeekTexts(@NonNull final Context context, - @NonNull final List timestamps, final boolean isAbbreviation) { - final ArrayList texts = new ArrayList<>(); - for (Long timestamp : timestamps) { - texts.add(ConvertUtils.utcToLocalTimeDayOfWeek(context, timestamp, isAbbreviation)); - } - return texts; - } - - private static List generateTimestampHourTexts( - @NonNull final Context context, @NonNull final List timestamps) { - final boolean is24HourFormat = DateFormat.is24HourFormat(context); - final ArrayList texts = new ArrayList<>(); - for (Long timestamp : timestamps) { - texts.add(ConvertUtils.utcToLocalTimeHour(context, timestamp, is24HourFormat)); - } - return texts; - } - - /** Used for {@link AppBatteryPreferenceController}. */ - public static List getAppBatteryUsageData(Context context) { - final long start = System.currentTimeMillis(); - final Map> batteryHistoryMap = - FeatureFactory.getFactory(context) - .getPowerUsageFeatureProvider(context) - .getBatteryHistorySinceLastFullCharge(context); - if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) { - return null; - } - Log.d(TAG, String.format("getBatterySinceLastFullChargeUsageData() size=%d time=%d/ms", - batteryHistoryMap.size(), (System.currentTimeMillis() - start))); - - final Map> batteryUsageData = - DataProcessor.getBatteryUsageData(context, batteryHistoryMap); - return batteryUsageData == null - ? null - : batteryUsageData - .get(BatteryChartViewModel.SELECTED_INDEX_ALL) - .get(BatteryChartViewModel.SELECTED_INDEX_ALL) - .getAppDiffEntryList(); - } - - /** Used for {@link AppBatteryPreferenceController}. */ - public static BatteryDiffEntry getAppBatteryUsageData( - Context context, String packageName, int userId) { - if (packageName == null) { - return null; - } - final List entries = getAppBatteryUsageData(context); - if (entries == null) { - return null; - } - for (BatteryDiffEntry entry : entries) { - final BatteryHistEntry batteryHistEntry = entry.mBatteryHistEntry; - if (batteryHistEntry != null - && batteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY - && batteryHistEntry.mUserId == userId - && packageName.equals(entry.getPackageName())) { - return entry; - } - } - return null; - } -} diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java index 427388befcd..ddfbbc200dd 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java @@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batteryusage; import static com.android.settings.Utils.formatPercentage; import static java.lang.Math.round; +import static java.util.Objects.requireNonNull; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; @@ -29,8 +30,6 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.os.Handler; -import android.text.format.DateFormat; -import android.text.format.DateUtils; import android.util.AttributeSet; import android.util.Log; import android.view.HapticFeedbackConstants; @@ -39,6 +38,7 @@ import android.view.View; import android.view.accessibility.AccessibilityManager; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.appcompat.widget.AppCompatImageView; @@ -46,7 +46,7 @@ import com.android.settings.R; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.Utils; -import java.time.Clock; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -58,36 +58,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private static final List ACCESSIBILITY_SERVICE_NAMES = Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService"); - private static final int DEFAULT_TRAPEZOID_COUNT = 12; - private static final int DEFAULT_TIMESTAMP_COUNT = 4; - private static final int TIMESTAMP_GAPS_COUNT = DEFAULT_TIMESTAMP_COUNT - 1; private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5"); private static final long UPDATE_STATE_DELAYED_TIME = 500L; - /** Selects all trapezoid shapes. */ - public static final int SELECTED_INDEX_ALL = -1; - public static final int SELECTED_INDEX_INVALID = -2; - /** A callback listener for selected group index is updated. */ public interface OnSelectListener { /** The callback function for selected group index is updated. */ void onSelect(int trapezoidIndex); } + private BatteryChartViewModel mViewModel; + private int mDividerWidth; private int mDividerHeight; - private int mTrapezoidCount; private float mTrapezoidVOffset; private float mTrapezoidHOffset; private boolean mIsSlotsClickabled; private String[] mPercentages = getPercentages(); @VisibleForTesting - int mHoveredIndex = SELECTED_INDEX_INVALID; - @VisibleForTesting - int mSelectedIndex = SELECTED_INDEX_INVALID; - @VisibleForTesting - String[] mTimestamps; + int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // Colors for drawing the trapezoid shape and dividers. private int mTrapezoidColor; @@ -98,25 +88,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private final Rect mIndent = new Rect(); private final Rect[] mPercentageBounds = new Rect[]{new Rect(), new Rect(), new Rect()}; - // For drawing the timestamp information. - private final Rect[] mTimestampsBounds = - new Rect[]{new Rect(), new Rect(), new Rect(), new Rect()}; + // For drawing the axis label information. + private final List mAxisLabelsBounds = new ArrayList<>(); + @VisibleForTesting Handler mHandler = new Handler(); @VisibleForTesting final Runnable mUpdateClickableStateRun = () -> updateClickableState(); - private int[] mLevels; private Paint mTextPaint; private Paint mDividerPaint; private Paint mTrapezoidPaint; @VisibleForTesting Paint mTrapezoidCurvePaint = null; - private TrapezoidSlot[] mTrapezoidSlots; + @VisibleForTesting + TrapezoidSlot[] mTrapezoidSlots; // Records the location to calculate selected index. - private float mTouchUpEventX = Float.MIN_VALUE; + @VisibleForTesting + float mTouchUpEventX = Float.MIN_VALUE; private BatteryChartView.OnSelectListener mOnSelectListener; public BatteryChartView(Context context) { @@ -128,57 +119,25 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick initializeColors(context); // Registers the click event listener. setOnClickListener(this); - setSelectedIndex(SELECTED_INDEX_ALL); - setTrapezoidCount(DEFAULT_TRAPEZOID_COUNT); setClickable(false); - setLatestTimestamp(0); + requestLayout(); } - /** Sets the total trapezoid count for drawing. */ - public void setTrapezoidCount(int trapezoidCount) { - Log.i(TAG, "trapezoidCount:" + trapezoidCount); - mTrapezoidCount = trapezoidCount; - mTrapezoidSlots = new TrapezoidSlot[trapezoidCount]; - // Allocates the trapezoid slot array. - for (int index = 0; index < trapezoidCount; index++) { - mTrapezoidSlots[index] = new TrapezoidSlot(); - } - invalidate(); - } - - /** Sets all levels value to draw the trapezoid shape */ - public void setLevels(int[] levels) { - Log.d(TAG, "setLevels() " + (levels == null ? "null" : levels.length)); - if (levels == null) { - mLevels = null; - return; - } - // We should provide trapezoid count + 1 data to draw all trapezoids. - mLevels = levels.length == mTrapezoidCount + 1 ? levels : null; - setClickable(false); - invalidate(); - if (mLevels == null) { - return; - } - // Sets the chart is clickable if there is at least one valid item in it. - for (int index = 0; index < mLevels.length - 1; index++) { - if (mLevels[index] != 0 && mLevels[index + 1] != 0) { - setClickable(true); - break; - } - } - } - - /** Sets the selected group index to draw highlight effect. */ - public void setSelectedIndex(int index) { - if (mSelectedIndex != index) { - mSelectedIndex = index; + /** Sets the data model of this view. */ + public void setViewModel(BatteryChartViewModel viewModel) { + if (viewModel == null) { + mViewModel = null; invalidate(); - // Callbacks to the listener if we have. - if (mOnSelectListener != null) { - mOnSelectListener.onSelect(mSelectedIndex); - } + return; } + + Log.d(TAG, String.format("setViewModel(): size: %d, selectedIndex: %d.", + viewModel.size(), viewModel.selectedIndex())); + mViewModel = viewModel; + initializeAxisLabelsBounds(); + initializeTrapezoidSlots(viewModel.size() - 1); + setClickable(hasAnyValidTrapezoid(viewModel)); + requestLayout(); } /** Sets the callback to monitor the selected group index. */ @@ -195,29 +154,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick } else { mTextPaint = null; } - setVisibility(View.VISIBLE); - requestLayout(); - } - - /** Sets the latest timestamp for drawing into x-axis information. */ - public void setLatestTimestamp(long latestTimestamp) { - if (latestTimestamp == 0) { - latestTimestamp = Clock.systemUTC().millis(); - } - if (mTimestamps == null) { - mTimestamps = new String[DEFAULT_TIMESTAMP_COUNT]; - } - final long timeSlotOffset = - DateUtils.HOUR_IN_MILLIS * (/*total 24 hours*/ 24 / TIMESTAMP_GAPS_COUNT); - final boolean is24HourFormat = DateFormat.is24HourFormat(getContext()); - for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { - mTimestamps[index] = - ConvertUtils.utcToLocalTimeHour( - getContext(), - latestTimestamp - (TIMESTAMP_GAPS_COUNT - index) - * timeSlotOffset, - is24HourFormat); - } requestLayout(); } @@ -226,6 +162,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Measures text bounds and updates indent configuration. if (mTextPaint != null) { + mTextPaint.setTextAlign(Paint.Align.LEFT); for (int index = 0; index < mPercentages.length; index++) { mTextPaint.getTextBounds( mPercentages[index], 0, mPercentages[index].length(), @@ -235,15 +172,14 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick mIndent.top = mPercentageBounds[0].height(); mIndent.right = mPercentageBounds[0].width() + mTextPadding; - if (mTimestamps != null) { - int maxHeight = 0; - for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { - mTextPaint.getTextBounds( - mTimestamps[index], 0, mTimestamps[index].length(), - mTimestampsBounds[index]); - maxHeight = Math.max(maxHeight, mTimestampsBounds[index].height()); + if (mViewModel != null) { + int maxTop = 0; + for (int index = 0; index < mViewModel.size(); index++) { + final String text = mViewModel.texts().get(index); + mTextPaint.getTextBounds(text, 0, text.length(), mAxisLabelsBounds.get(index)); + maxTop = Math.max(maxTop, -mAxisLabelsBounds.get(index).top); } - mIndent.bottom = maxHeight + round(mTextPadding * 1.5f); + mIndent.bottom = maxTop + round(mTextPadding * 2f); } Log.d(TAG, "setIndent:" + mPercentageBounds[0]); } else { @@ -254,7 +190,12 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick @Override public void draw(Canvas canvas) { super.draw(canvas); + // Before mLevels initialized, the count of trapezoids is unknown. Only draws the + // horizontal percentages and dividers. drawHorizontalDividers(canvas); + if (mViewModel == null) { + return; + } drawVerticalDividers(canvas); drawTrapezoids(canvas); } @@ -294,7 +235,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick public void onHoverChanged(boolean hovered) { super.onHoverChanged(hovered); if (!hovered) { - mHoveredIndex = SELECTED_INDEX_INVALID; // reset + mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset invalidate(); } } @@ -307,15 +248,15 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick } final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX); // Ignores the click event if the level is zero. - if (trapezoidIndex == SELECTED_INDEX_INVALID - || !isValidToDraw(trapezoidIndex)) { + if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID + || !isValidToDraw(mViewModel, trapezoidIndex)) { return; } - // Selects all if users click the same trapezoid item two times. - if (trapezoidIndex == mSelectedIndex) { - setSelectedIndex(SELECTED_INDEX_ALL); - } else { - setSelectedIndex(trapezoidIndex); + if (mOnSelectListener != null) { + // Selects all if users click the same trapezoid item two times. + mOnSelectListener.onSelect( + trapezoidIndex == mViewModel.selectedIndex() + ? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex); } view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK); } @@ -364,8 +305,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2); } else if (mIsSlotsClickabled) { mTrapezoidCurvePaint = null; - // Sets levels again to force update the click state. - setLevels(mLevels); + // Sets view model again to force update the click state. + setViewModel(mViewModel); } invalidate(); } @@ -380,6 +321,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick super.setClickable(clickable); } + private void initializeTrapezoidSlots(int count) { + mTrapezoidSlots = new TrapezoidSlot[count]; + for (int index = 0; index < mTrapezoidSlots.length; index++) { + mTrapezoidSlots[index] = new TrapezoidSlot(); + } + } + private void initializeColors(Context context) { setBackgroundColor(Color.TRANSPARENT); mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context); @@ -434,10 +382,10 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private void drawPercentage(Canvas canvas, int index, float offsetY) { if (mTextPaint != null) { + mTextPaint.setTextAlign(Paint.Align.RIGHT); canvas.drawText( mPercentages[index], - getWidth() - mPercentageBounds[index].width() - - mPercentageBounds[index].left, + getWidth(), offsetY + mPercentageBounds[index].height() * .5f, mTextPaint); } @@ -445,9 +393,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private void drawVerticalDividers(Canvas canvas) { final int width = getWidth() - mIndent.right; - final int dividerCount = mTrapezoidCount + 1; + final int dividerCount = mTrapezoidSlots.length + 1; final float dividerSpace = dividerCount * mDividerWidth; - final float unitWidth = (width - dividerSpace) / (float) mTrapezoidCount; + final float unitWidth = (width - dividerSpace) / (float) mTrapezoidSlots.length; final float bottomY = getHeight() - mIndent.bottom; final float startY = bottomY - mDividerHeight; final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f; @@ -463,53 +411,119 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick } startX = nextX; } - // Draws the timestamp slot information. - if (mTimestamps != null) { - final float[] xOffsets = new float[DEFAULT_TIMESTAMP_COUNT]; - final float baselineX = mDividerWidth * .5f; - final float offsetX = mDividerWidth + unitWidth; - final int slotBarOffset = (/*total 12 bars*/ 12) / TIMESTAMP_GAPS_COUNT; - for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { - xOffsets[index] = baselineX + index * offsetX * slotBarOffset; + // Draws the axis label slot information. + if (mViewModel != null) { + final float baselineY = getHeight() - mTextPadding * 1.5f; + Rect[] axisLabelDisplayAreas; + switch (mViewModel.axisLabelPosition()) { + case CENTER_OF_TRAPEZOIDS: + axisLabelDisplayAreas = getAxisLabelDisplayAreas( + /* size= */ mViewModel.size() - 1, + /* baselineX= */ mDividerWidth + unitWidth * .5f, + /* offsetX= */ mDividerWidth + unitWidth, + baselineY, + /* shiftFirstAndLast= */ false); + break; + case BETWEEN_TRAPEZOIDS: + default: + axisLabelDisplayAreas = getAxisLabelDisplayAreas( + /* size= */ mViewModel.size(), + /* baselineX= */ mDividerWidth * .5f, + /* offsetX= */ mDividerWidth + unitWidth, + baselineY, + /* shiftFirstAndLast= */ true); + break; } - drawTimestamp(canvas, xOffsets); + drawAxisLabels(canvas, axisLabelDisplayAreas, baselineY); } } - private void drawTimestamp(Canvas canvas, float[] xOffsets) { - // Draws the 1st timestamp info. - canvas.drawText( - mTimestamps[0], - xOffsets[0] - mTimestampsBounds[0].left, - getTimestampY(0), mTextPaint); - final int latestIndex = DEFAULT_TIMESTAMP_COUNT - 1; - // Draws the last timestamp info. - canvas.drawText( - mTimestamps[latestIndex], - xOffsets[latestIndex] - mTimestampsBounds[latestIndex].width() - - mTimestampsBounds[latestIndex].left, - getTimestampY(latestIndex), mTextPaint); - // Draws the rest of timestamp info since it is located in the center. - for (int index = 1; index <= DEFAULT_TIMESTAMP_COUNT - 2; index++) { - canvas.drawText( - mTimestamps[index], - xOffsets[index] - - (mTimestampsBounds[index].width() - mTimestampsBounds[index].left) - * .5f, - getTimestampY(index), mTextPaint); + /** Gets all the axis label texts displaying area positions if they are shown. */ + private Rect[] getAxisLabelDisplayAreas(final int size, final float baselineX, + final float offsetX, final float baselineY, final boolean shiftFirstAndLast) { + final Rect[] result = new Rect[size]; + for (int index = 0; index < result.length; index++) { + final float width = mAxisLabelsBounds.get(index).width(); + float middle = baselineX + index * offsetX; + if (shiftFirstAndLast) { + if (index == 0) { + middle += width * .5f; + } + if (index == size - 1) { + middle -= width * .5f; + } + } + final float left = middle - width * .5f; + final float right = left + width; + final float top = baselineY + mAxisLabelsBounds.get(index).top; + final float bottom = top + mAxisLabelsBounds.get(index).height(); + result[index] = new Rect(round(left), round(top), round(right), round(bottom)); + } + return result; + } + /** + * Pairly draws axis labels from left and right side to middle. If the pair of labels have + * any overlap, skips that pair of labels. + */ + private void drawAxisLabels(Canvas canvas, final Rect[] displayAreas, final float baselineY) { + int forwardCheckLine = Integer.MIN_VALUE; + int backwardCheckLine = Integer.MAX_VALUE; + Rect middleDisplayArea = null; + for (int forwardIndex = 0, backwordIndex = displayAreas.length - 1; + forwardIndex <= backwordIndex; forwardIndex++, backwordIndex--) { + final Rect forwardDisplayArea = displayAreas[forwardIndex]; + final Rect backwardDisplayArea = displayAreas[backwordIndex]; + if (forwardDisplayArea.left < forwardCheckLine + || backwardDisplayArea.right > backwardCheckLine) { + // Overlapped at left or right, skip the pair of labels + continue; + } + if (middleDisplayArea != null && ( + forwardDisplayArea.right + mTextPadding > middleDisplayArea.left + || backwardDisplayArea.left - mTextPadding < middleDisplayArea.right)) { + // Overlapped with the middle label. + continue; + } + if (forwardIndex != backwordIndex + && forwardDisplayArea.right + mTextPadding > backwardDisplayArea.left) { + // Overlapped in the middle, skip the pair of labels + continue; + } + + drawAxisLabelText(canvas, forwardIndex, forwardDisplayArea, baselineY); + drawAxisLabelText(canvas, backwordIndex, backwardDisplayArea, baselineY); + + forwardCheckLine = forwardDisplayArea.right + mTextPadding; + backwardCheckLine = backwardDisplayArea.left - mTextPadding; + + // If the number of labels is odd, draw the middle label first + if (forwardIndex == 0 && backwordIndex % 2 == 0) { + final int middleIndex = backwordIndex / 2; + middleDisplayArea = displayAreas[middleIndex]; + if (middleDisplayArea.left < forwardCheckLine + || middleDisplayArea.right > backwardCheckLine) { + // Overlapped at left or right, skip the pair of labels + continue; + } + drawAxisLabelText(canvas, middleIndex, middleDisplayArea, baselineY); + } } } - private int getTimestampY(int index) { - return getHeight() - mTimestampsBounds[index].height() - + (mTimestampsBounds[index].height() + mTimestampsBounds[index].top) - + round(mTextPadding * 1.5f); + private void drawAxisLabelText( + Canvas canvas, final int index, final Rect displayArea, final float baselineY) { + mTextPaint.setTextAlign(Paint.Align.CENTER); + canvas.drawText( + mViewModel.texts().get(index), + displayArea.centerX(), + baselineY, + mTextPaint); } private void drawTrapezoids(Canvas canvas) { // Ignores invalid trapezoid data. - if (mLevels == null) { + if (mViewModel == null) { return; } final float trapezoidBottom = @@ -520,9 +534,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick // Draws all trapezoid shapes into the canvas. final Path trapezoidPath = new Path(); Path trapezoidCurvePath = null; - for (int index = 0; index < mTrapezoidCount; index++) { + for (int index = 0; index < mTrapezoidSlots.length; index++) { // Not draws the trapezoid for corner or not initialization cases. - if (!isValidToDraw(index)) { + if (!isValidToDraw(mViewModel, index)) { if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) { canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint); trapezoidCurvePath = null; @@ -530,17 +544,18 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick continue; } // Configures the trapezoid paint color. - final int trapezoidColor = - !mIsSlotsClickabled - ? mTrapezoidColor - : mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL - ? mTrapezoidSolidColor : mTrapezoidColor; + final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index + || mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL) + ? mTrapezoidSolidColor : mTrapezoidColor; final boolean isHoverState = - mIsSlotsClickabled && mHoveredIndex == index && isValidToDraw(mHoveredIndex); + mIsSlotsClickabled && mHoveredIndex == index + && isValidToDraw(mViewModel, mHoveredIndex); mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor); - final float leftTop = round(trapezoidBottom - mLevels[index] * unitHeight); - final float rightTop = round(trapezoidBottom - mLevels[index + 1] * unitHeight); + final float leftTop = round( + trapezoidBottom - requireNonNull(mViewModel.levels().get(index)) * unitHeight); + final float rightTop = round(trapezoidBottom + - requireNonNull(mViewModel.levels().get(index + 1)) * unitHeight); trapezoidPath.reset(); trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom); trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop); @@ -579,15 +594,37 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick return index; } } - return SELECTED_INDEX_INVALID; + return BatteryChartViewModel.SELECTED_INDEX_INVALID; } - private boolean isValidToDraw(int trapezoidIndex) { - return mLevels != null + private void initializeAxisLabelsBounds() { + mAxisLabelsBounds.clear(); + for (int i = 0; i < mViewModel.size(); i++) { + mAxisLabelsBounds.add(new Rect()); + } + } + + private static boolean isTrapezoidValid( + @NonNull BatteryChartViewModel viewModel, int trapezoidIndex) { + return viewModel.levels().get(trapezoidIndex) != null + && viewModel.levels().get(trapezoidIndex + 1) != null; + } + + private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) { + return viewModel != null && trapezoidIndex >= 0 - && trapezoidIndex < mLevels.length - 1 - && mLevels[trapezoidIndex] != 0 - && mLevels[trapezoidIndex + 1] != 0; + && trapezoidIndex < viewModel.size() - 1 + && isTrapezoidValid(viewModel, trapezoidIndex); + } + + private static boolean hasAnyValidTrapezoid(@NonNull BatteryChartViewModel viewModel) { + // Sets the chart is clickable if there is at least one valid item in it. + for (int trapezoidIndex = 0; trapezoidIndex < viewModel.size() - 1; trapezoidIndex++) { + if (isTrapezoidValid(viewModel, trapezoidIndex)) { + return true; + } + } + return false; } private static String[] getPercentages() { @@ -621,7 +658,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick } // A container class for each trapezoid left and right location. - private static final class TrapezoidSlot { + @VisibleForTesting + static final class TrapezoidSlot { public float mLeft; public float mRight; diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java index 82a41c4a26e..ac01bfd645b 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java @@ -23,7 +23,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; -/** The view model of {@code BatteryChartViewV2} */ +/** The view model of {@code BatteryChartView} */ class BatteryChartViewModel { private static final String TAG = "BatteryChartViewModel"; diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java deleted file mode 100644 index 64f650603e8..00000000000 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java +++ /dev/null @@ -1,671 +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.android.settings.Utils.formatPercentage; - -import static java.lang.Math.round; -import static java.util.Objects.requireNonNull; - -import android.accessibilityservice.AccessibilityServiceInfo; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.CornerPathEffect; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Rect; -import android.os.Handler; -import android.util.AttributeSet; -import android.util.Log; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityManager; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.appcompat.widget.AppCompatImageView; - -import com.android.settings.R; -import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.Utils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -/** A widget component to draw chart graph. */ -public class BatteryChartViewV2 extends AppCompatImageView implements View.OnClickListener, - AccessibilityManager.AccessibilityStateChangeListener { - private static final String TAG = "BatteryChartViewV2"; - private static final List ACCESSIBILITY_SERVICE_NAMES = - Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService"); - - private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5"); - private static final long UPDATE_STATE_DELAYED_TIME = 500L; - - /** A callback listener for selected group index is updated. */ - public interface OnSelectListener { - /** The callback function for selected group index is updated. */ - void onSelect(int trapezoidIndex); - } - - private BatteryChartViewModel mViewModel; - - private int mDividerWidth; - private int mDividerHeight; - private float mTrapezoidVOffset; - private float mTrapezoidHOffset; - private boolean mIsSlotsClickabled; - private String[] mPercentages = getPercentages(); - - @VisibleForTesting - int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; - - // Colors for drawing the trapezoid shape and dividers. - private int mTrapezoidColor; - private int mTrapezoidSolidColor; - private int mTrapezoidHoverColor; - // For drawing the percentage information. - private int mTextPadding; - private final Rect mIndent = new Rect(); - private final Rect[] mPercentageBounds = - new Rect[]{new Rect(), new Rect(), new Rect()}; - // For drawing the axis label information. - private final List mAxisLabelsBounds = new ArrayList<>(); - - - @VisibleForTesting - Handler mHandler = new Handler(); - @VisibleForTesting - final Runnable mUpdateClickableStateRun = () -> updateClickableState(); - - private Paint mTextPaint; - private Paint mDividerPaint; - private Paint mTrapezoidPaint; - - @VisibleForTesting - Paint mTrapezoidCurvePaint = null; - @VisibleForTesting - TrapezoidSlot[] mTrapezoidSlots; - // Records the location to calculate selected index. - @VisibleForTesting - float mTouchUpEventX = Float.MIN_VALUE; - private BatteryChartViewV2.OnSelectListener mOnSelectListener; - - public BatteryChartViewV2(Context context) { - super(context, null); - } - - public BatteryChartViewV2(Context context, AttributeSet attrs) { - super(context, attrs); - initializeColors(context); - // Registers the click event listener. - setOnClickListener(this); - setClickable(false); - requestLayout(); - } - - /** Sets the data model of this view. */ - public void setViewModel(BatteryChartViewModel viewModel) { - if (viewModel == null) { - mViewModel = null; - invalidate(); - return; - } - - Log.d(TAG, String.format("setViewModel(): size: %d, selectedIndex: %d.", - viewModel.size(), viewModel.selectedIndex())); - mViewModel = viewModel; - initializeAxisLabelsBounds(); - initializeTrapezoidSlots(viewModel.size() - 1); - setClickable(hasAnyValidTrapezoid(viewModel)); - requestLayout(); - } - - /** Sets the callback to monitor the selected group index. */ - public void setOnSelectListener(BatteryChartViewV2.OnSelectListener listener) { - mOnSelectListener = listener; - } - - /** Sets the companion {@link TextView} for percentage information. */ - public void setCompanionTextView(TextView textView) { - if (textView != null) { - // Pre-draws the view first to load style atttributions into paint. - textView.draw(new Canvas()); - mTextPaint = textView.getPaint(); - } else { - mTextPaint = null; - } - requestLayout(); - } - - @Override - public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - // Measures text bounds and updates indent configuration. - if (mTextPaint != null) { - mTextPaint.setTextAlign(Paint.Align.LEFT); - for (int index = 0; index < mPercentages.length; index++) { - mTextPaint.getTextBounds( - mPercentages[index], 0, mPercentages[index].length(), - mPercentageBounds[index]); - } - // Updates the indent configurations. - mIndent.top = mPercentageBounds[0].height(); - mIndent.right = mPercentageBounds[0].width() + mTextPadding; - - if (mViewModel != null) { - int maxTop = 0; - for (int index = 0; index < mViewModel.size(); index++) { - final String text = mViewModel.texts().get(index); - mTextPaint.getTextBounds(text, 0, text.length(), mAxisLabelsBounds.get(index)); - maxTop = Math.max(maxTop, -mAxisLabelsBounds.get(index).top); - } - mIndent.bottom = maxTop + round(mTextPadding * 2f); - } - Log.d(TAG, "setIndent:" + mPercentageBounds[0]); - } else { - mIndent.set(0, 0, 0, 0); - } - } - - @Override - public void draw(Canvas canvas) { - super.draw(canvas); - // Before mLevels initialized, the count of trapezoids is unknown. Only draws the - // horizontal percentages and dividers. - drawHorizontalDividers(canvas); - if (mViewModel == null) { - return; - } - drawVerticalDividers(canvas); - drawTrapezoids(canvas); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - // Caches the location to calculate selected trapezoid index. - final int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_UP: - mTouchUpEventX = event.getX(); - break; - case MotionEvent.ACTION_CANCEL: - mTouchUpEventX = Float.MIN_VALUE; // reset - break; - } - return super.onTouchEvent(event); - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - final int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_HOVER_ENTER: - case MotionEvent.ACTION_HOVER_MOVE: - final int trapezoidIndex = getTrapezoidIndex(event.getX()); - if (mHoveredIndex != trapezoidIndex) { - mHoveredIndex = trapezoidIndex; - invalidate(); - } - break; - } - return super.onHoverEvent(event); - } - - @Override - public void onHoverChanged(boolean hovered) { - super.onHoverChanged(hovered); - if (!hovered) { - mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset - invalidate(); - } - } - - @Override - public void onClick(View view) { - if (mTouchUpEventX == Float.MIN_VALUE) { - Log.w(TAG, "invalid motion event for onClick() callback"); - return; - } - final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX); - // Ignores the click event if the level is zero. - if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID - || !isValidToDraw(mViewModel, trapezoidIndex)) { - return; - } - if (mOnSelectListener != null) { - // Selects all if users click the same trapezoid item two times. - mOnSelectListener.onSelect( - trapezoidIndex == mViewModel.selectedIndex() - ? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex); - } - view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - updateClickableState(); - mContext.getSystemService(AccessibilityManager.class) - .addAccessibilityStateChangeListener(/*listener=*/ this); - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mContext.getSystemService(AccessibilityManager.class) - .removeAccessibilityStateChangeListener(/*listener=*/ this); - mHandler.removeCallbacks(mUpdateClickableStateRun); - } - - @Override - public void onAccessibilityStateChanged(boolean enabled) { - Log.d(TAG, "onAccessibilityStateChanged:" + enabled); - mHandler.removeCallbacks(mUpdateClickableStateRun); - // We should delay it a while since accessibility manager will spend - // some times to bind with new enabled accessibility services. - mHandler.postDelayed( - mUpdateClickableStateRun, UPDATE_STATE_DELAYED_TIME); - } - - private void updateClickableState() { - final Context context = mContext; - mIsSlotsClickabled = - FeatureFactory.getFactory(context) - .getPowerUsageFeatureProvider(context) - .isChartGraphSlotsEnabled(context) - && !isAccessibilityEnabled(context); - Log.d(TAG, "isChartGraphSlotsEnabled:" + mIsSlotsClickabled); - setClickable(isClickable()); - // Initializes the trapezoid curve paint for non-clickable case. - if (!mIsSlotsClickabled && mTrapezoidCurvePaint == null) { - mTrapezoidCurvePaint = new Paint(); - mTrapezoidCurvePaint.setAntiAlias(true); - mTrapezoidCurvePaint.setColor(mTrapezoidSolidColor); - mTrapezoidCurvePaint.setStyle(Paint.Style.STROKE); - mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2); - } else if (mIsSlotsClickabled) { - mTrapezoidCurvePaint = null; - // Sets view model again to force update the click state. - setViewModel(mViewModel); - } - invalidate(); - } - - @Override - public void setClickable(boolean clickable) { - super.setClickable(mIsSlotsClickabled && clickable); - } - - @VisibleForTesting - void setClickableForce(boolean clickable) { - super.setClickable(clickable); - } - - private void initializeTrapezoidSlots(int count) { - mTrapezoidSlots = new TrapezoidSlot[count]; - for (int index = 0; index < mTrapezoidSlots.length; index++) { - mTrapezoidSlots[index] = new TrapezoidSlot(); - } - } - - private void initializeColors(Context context) { - setBackgroundColor(Color.TRANSPARENT); - mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context); - mTrapezoidColor = Utils.getDisabled(context, mTrapezoidSolidColor); - mTrapezoidHoverColor = Utils.getColorAttrDefaultColor(context, - com.android.internal.R.attr.colorAccentSecondaryVariant); - // Initializes the divider line paint. - final Resources resources = getContext().getResources(); - mDividerWidth = resources.getDimensionPixelSize(R.dimen.chartview_divider_width); - mDividerHeight = resources.getDimensionPixelSize(R.dimen.chartview_divider_height); - mDividerPaint = new Paint(); - mDividerPaint.setAntiAlias(true); - mDividerPaint.setColor(DIVIDER_COLOR); - mDividerPaint.setStyle(Paint.Style.STROKE); - mDividerPaint.setStrokeWidth(mDividerWidth); - Log.i(TAG, "mDividerWidth:" + mDividerWidth); - Log.i(TAG, "mDividerHeight:" + mDividerHeight); - // Initializes the trapezoid paint. - mTrapezoidHOffset = resources.getDimension(R.dimen.chartview_trapezoid_margin_start); - mTrapezoidVOffset = resources.getDimension(R.dimen.chartview_trapezoid_margin_bottom); - mTrapezoidPaint = new Paint(); - mTrapezoidPaint.setAntiAlias(true); - mTrapezoidPaint.setColor(mTrapezoidSolidColor); - mTrapezoidPaint.setStyle(Paint.Style.FILL); - mTrapezoidPaint.setPathEffect( - new CornerPathEffect( - resources.getDimensionPixelSize(R.dimen.chartview_trapezoid_radius))); - // Initializes for drawing text information. - mTextPadding = resources.getDimensionPixelSize(R.dimen.chartview_text_padding); - } - - private void drawHorizontalDividers(Canvas canvas) { - final int width = getWidth() - mIndent.right; - final int height = getHeight() - mIndent.top - mIndent.bottom; - // Draws the top divider line for 100% curve. - float offsetY = mIndent.top + mDividerWidth * .5f; - canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint); - drawPercentage(canvas, /*index=*/ 0, offsetY); - - // Draws the center divider line for 50% curve. - final float availableSpace = - height - mDividerWidth * 2 - mTrapezoidVOffset - mDividerHeight; - offsetY = mIndent.top + mDividerWidth + availableSpace * .5f; - canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint); - drawPercentage(canvas, /*index=*/ 1, offsetY); - - // Draws the bottom divider line for 0% curve. - offsetY = mIndent.top + (height - mDividerHeight - mDividerWidth * .5f); - canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint); - drawPercentage(canvas, /*index=*/ 2, offsetY); - } - - private void drawPercentage(Canvas canvas, int index, float offsetY) { - if (mTextPaint != null) { - mTextPaint.setTextAlign(Paint.Align.RIGHT); - canvas.drawText( - mPercentages[index], - getWidth(), - offsetY + mPercentageBounds[index].height() * .5f, - mTextPaint); - } - } - - private void drawVerticalDividers(Canvas canvas) { - final int width = getWidth() - mIndent.right; - final int dividerCount = mTrapezoidSlots.length + 1; - final float dividerSpace = dividerCount * mDividerWidth; - final float unitWidth = (width - dividerSpace) / (float) mTrapezoidSlots.length; - final float bottomY = getHeight() - mIndent.bottom; - final float startY = bottomY - mDividerHeight; - final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f; - // Draws each vertical dividers. - float startX = mDividerWidth * .5f; - for (int index = 0; index < dividerCount; index++) { - canvas.drawLine(startX, startY, startX, bottomY, mDividerPaint); - final float nextX = startX + mDividerWidth + unitWidth; - // Updates the trapezoid slots for drawing. - if (index < mTrapezoidSlots.length) { - mTrapezoidSlots[index].mLeft = round(startX + trapezoidSlotOffset); - mTrapezoidSlots[index].mRight = round(nextX - trapezoidSlotOffset); - } - startX = nextX; - } - // Draws the axis label slot information. - if (mViewModel != null) { - final float baselineY = getHeight() - mTextPadding * 1.5f; - Rect[] axisLabelDisplayAreas; - switch (mViewModel.axisLabelPosition()) { - case CENTER_OF_TRAPEZOIDS: - axisLabelDisplayAreas = getAxisLabelDisplayAreas( - /* size= */ mViewModel.size() - 1, - /* baselineX= */ mDividerWidth + unitWidth * .5f, - /* offsetX= */ mDividerWidth + unitWidth, - baselineY, - /* shiftFirstAndLast= */ false); - break; - case BETWEEN_TRAPEZOIDS: - default: - axisLabelDisplayAreas = getAxisLabelDisplayAreas( - /* size= */ mViewModel.size(), - /* baselineX= */ mDividerWidth * .5f, - /* offsetX= */ mDividerWidth + unitWidth, - baselineY, - /* shiftFirstAndLast= */ true); - break; - } - drawAxisLabels(canvas, axisLabelDisplayAreas, baselineY); - } - } - - /** Gets all the axis label texts displaying area positions if they are shown. */ - private Rect[] getAxisLabelDisplayAreas(final int size, final float baselineX, - final float offsetX, final float baselineY, final boolean shiftFirstAndLast) { - final Rect[] result = new Rect[size]; - for (int index = 0; index < result.length; index++) { - final float width = mAxisLabelsBounds.get(index).width(); - float middle = baselineX + index * offsetX; - if (shiftFirstAndLast) { - if (index == 0) { - middle += width * .5f; - } - if (index == size - 1) { - middle -= width * .5f; - } - } - final float left = middle - width * .5f; - final float right = left + width; - final float top = baselineY + mAxisLabelsBounds.get(index).top; - final float bottom = top + mAxisLabelsBounds.get(index).height(); - result[index] = new Rect(round(left), round(top), round(right), round(bottom)); - } - return result; - } - - /** - * Pairly draws axis labels from left and right side to middle. If the pair of labels have - * any overlap, skips that pair of labels. - */ - private void drawAxisLabels(Canvas canvas, final Rect[] displayAreas, final float baselineY) { - int forwardCheckLine = Integer.MIN_VALUE; - int backwardCheckLine = Integer.MAX_VALUE; - Rect middleDisplayArea = null; - for (int forwardIndex = 0, backwordIndex = displayAreas.length - 1; - forwardIndex <= backwordIndex; forwardIndex++, backwordIndex--) { - final Rect forwardDisplayArea = displayAreas[forwardIndex]; - final Rect backwardDisplayArea = displayAreas[backwordIndex]; - if (forwardDisplayArea.left < forwardCheckLine - || backwardDisplayArea.right > backwardCheckLine) { - // Overlapped at left or right, skip the pair of labels - continue; - } - if (middleDisplayArea != null && ( - forwardDisplayArea.right + mTextPadding > middleDisplayArea.left - || backwardDisplayArea.left - mTextPadding < middleDisplayArea.right)) { - // Overlapped with the middle label. - continue; - } - if (forwardIndex != backwordIndex - && forwardDisplayArea.right + mTextPadding > backwardDisplayArea.left) { - // Overlapped in the middle, skip the pair of labels - continue; - } - - drawAxisLabelText(canvas, forwardIndex, forwardDisplayArea, baselineY); - drawAxisLabelText(canvas, backwordIndex, backwardDisplayArea, baselineY); - - forwardCheckLine = forwardDisplayArea.right + mTextPadding; - backwardCheckLine = backwardDisplayArea.left - mTextPadding; - - // If the number of labels is odd, draw the middle label first - if (forwardIndex == 0 && backwordIndex % 2 == 0) { - final int middleIndex = backwordIndex / 2; - middleDisplayArea = displayAreas[middleIndex]; - if (middleDisplayArea.left < forwardCheckLine - || middleDisplayArea.right > backwardCheckLine) { - // Overlapped at left or right, skip the pair of labels - continue; - } - drawAxisLabelText(canvas, middleIndex, middleDisplayArea, baselineY); - } - } - } - - private void drawAxisLabelText( - Canvas canvas, final int index, final Rect displayArea, final float baselineY) { - mTextPaint.setTextAlign(Paint.Align.CENTER); - canvas.drawText( - mViewModel.texts().get(index), - displayArea.centerX(), - baselineY, - mTextPaint); - } - - private void drawTrapezoids(Canvas canvas) { - // Ignores invalid trapezoid data. - if (mViewModel == null) { - return; - } - final float trapezoidBottom = - getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth - - mTrapezoidVOffset; - final float availableSpace = trapezoidBottom - mDividerWidth * .5f - mIndent.top; - final float unitHeight = availableSpace / 100f; - // Draws all trapezoid shapes into the canvas. - final Path trapezoidPath = new Path(); - Path trapezoidCurvePath = null; - for (int index = 0; index < mTrapezoidSlots.length; index++) { - // Not draws the trapezoid for corner or not initialization cases. - if (!isValidToDraw(mViewModel, index)) { - if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) { - canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint); - trapezoidCurvePath = null; - } - continue; - } - // Configures the trapezoid paint color. - final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index - || mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL) - ? mTrapezoidSolidColor : mTrapezoidColor; - final boolean isHoverState = - mIsSlotsClickabled && mHoveredIndex == index - && isValidToDraw(mViewModel, mHoveredIndex); - mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor); - - final float leftTop = round( - trapezoidBottom - requireNonNull(mViewModel.levels().get(index)) * unitHeight); - final float rightTop = round(trapezoidBottom - - requireNonNull(mViewModel.levels().get(index + 1)) * unitHeight); - trapezoidPath.reset(); - trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom); - trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop); - trapezoidPath.lineTo(mTrapezoidSlots[index].mRight, rightTop); - trapezoidPath.lineTo(mTrapezoidSlots[index].mRight, trapezoidBottom); - // A tricky way to make the trapezoid shape drawing the rounded corner. - trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, trapezoidBottom); - trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop); - // Draws the trapezoid shape into canvas. - canvas.drawPath(trapezoidPath, mTrapezoidPaint); - - // Generates path for non-clickable trapezoid curve. - if (mTrapezoidCurvePaint != null) { - if (trapezoidCurvePath == null) { - trapezoidCurvePath = new Path(); - trapezoidCurvePath.moveTo(mTrapezoidSlots[index].mLeft, leftTop); - } else { - trapezoidCurvePath.lineTo(mTrapezoidSlots[index].mLeft, leftTop); - } - trapezoidCurvePath.lineTo(mTrapezoidSlots[index].mRight, rightTop); - } - } - // Draws the trapezoid curve for non-clickable case. - if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) { - canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint); - trapezoidCurvePath = null; - } - } - - // Searches the corresponding trapezoid index from x location. - private int getTrapezoidIndex(float x) { - for (int index = 0; index < mTrapezoidSlots.length; index++) { - final TrapezoidSlot slot = mTrapezoidSlots[index]; - if (x >= slot.mLeft - mTrapezoidHOffset - && x <= slot.mRight + mTrapezoidHOffset) { - return index; - } - } - return BatteryChartViewModel.SELECTED_INDEX_INVALID; - } - - private void initializeAxisLabelsBounds() { - mAxisLabelsBounds.clear(); - for (int i = 0; i < mViewModel.size(); i++) { - mAxisLabelsBounds.add(new Rect()); - } - } - - private static boolean isTrapezoidValid( - @NonNull BatteryChartViewModel viewModel, int trapezoidIndex) { - return viewModel.levels().get(trapezoidIndex) != null - && viewModel.levels().get(trapezoidIndex + 1) != null; - } - - private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) { - return viewModel != null - && trapezoidIndex >= 0 - && trapezoidIndex < viewModel.size() - 1 - && isTrapezoidValid(viewModel, trapezoidIndex); - } - - private static boolean hasAnyValidTrapezoid(@NonNull BatteryChartViewModel viewModel) { - // Sets the chart is clickable if there is at least one valid item in it. - for (int trapezoidIndex = 0; trapezoidIndex < viewModel.size() - 1; trapezoidIndex++) { - if (isTrapezoidValid(viewModel, trapezoidIndex)) { - return true; - } - } - return false; - } - - private static String[] getPercentages() { - return new String[]{ - formatPercentage(/*percentage=*/ 100, /*round=*/ true), - formatPercentage(/*percentage=*/ 50, /*round=*/ true), - formatPercentage(/*percentage=*/ 0, /*round=*/ true)}; - } - - @VisibleForTesting - static boolean isAccessibilityEnabled(Context context) { - final AccessibilityManager accessibilityManager = - context.getSystemService(AccessibilityManager.class); - if (!accessibilityManager.isEnabled()) { - return false; - } - final List serviceInfoList = - accessibilityManager.getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_SPOKEN - | AccessibilityServiceInfo.FEEDBACK_GENERIC); - for (AccessibilityServiceInfo info : serviceInfoList) { - for (String serviceName : ACCESSIBILITY_SERVICE_NAMES) { - final String serviceId = info.getId(); - if (serviceId != null && serviceId.contains(serviceName)) { - Log.d(TAG, "acccessibilityEnabled:" + serviceId); - return true; - } - } - } - return false; - } - - // A container class for each trapezoid left and right location. - @VisibleForTesting - static final class TrapezoidSlot { - public float mLeft; - public float mRight; - - @Override - public String toString() { - return String.format(Locale.US, "TrapezoidSlot[%f,%f]", mLeft, mRight); - } - } -} diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoader.java index 34606a5a583..83b26150d39 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoader.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoader.java @@ -43,6 +43,6 @@ public class BatteryHistoryLoader public Map> loadInBackground() { final PowerUsageFeatureProvider powerUsageFeatureProvider = FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(mContext); - return powerUsageFeatureProvider.getBatteryHistory(mContext); + return powerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext); } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreference.java index e125d17d6a3..71fd26ce95c 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreference.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreference.java @@ -49,7 +49,8 @@ public class BatteryHistoryPreference extends Preference { private TextView mSummaryView; private CharSequence mSummaryContent; - private BatteryChartView mBatteryChartView; + private BatteryChartView mDailyChartView; + private BatteryChartView mHourlyChartView; private BatteryChartPreferenceController mChartPreferenceController; public BatteryHistoryPreference(Context context, AttributeSet attrs) { @@ -92,8 +93,8 @@ public class BatteryHistoryPreference extends Preference { void setChartPreferenceController(BatteryChartPreferenceController controller) { mChartPreferenceController = controller; - if (mBatteryChartView != null) { - mChartPreferenceController.setBatteryChartView(mBatteryChartView); + if (mDailyChartView != null && mHourlyChartView != null) { + mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView); } } @@ -105,11 +106,14 @@ public class BatteryHistoryPreference extends Preference { return; } if (mIsChartGraphEnabled) { - mBatteryChartView = (BatteryChartView) view.findViewById(R.id.battery_chart); - mBatteryChartView.setCompanionTextView( + mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart); + mDailyChartView.setCompanionTextView( + (TextView) view.findViewById(R.id.companion_text)); + mHourlyChartView = (BatteryChartView) view.findViewById(R.id.hourly_battery_chart); + mHourlyChartView.setCompanionTextView( (TextView) view.findViewById(R.id.companion_text)); if (mChartPreferenceController != null) { - mChartPreferenceController.setBatteryChartView(mBatteryChartView); + mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView); } } else { final TextView chargeView = (TextView) view.findViewById(R.id.charge); diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java index 8d2013ce793..f04658d83a4 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java @@ -276,7 +276,7 @@ public final class ConvertUtils { diffEntry.setTotalConsumePower(totalConsumePower); } } - insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap); + insert24HoursData(BatteryChartViewModel.SELECTED_INDEX_ALL, resultMap); resolveMultiUsersData(context, resultMap); if (purgeLowPercentageAndFakeData) { purgeLowPercentageAndFakeData(context, resultMap); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java index 4a28e64693e..016287e2f87 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java @@ -19,9 +19,10 @@ 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.anyLong; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -33,6 +34,8 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.LocaleList; import android.text.format.DateUtils; +import android.view.View; +import android.widget.LinearLayout; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; @@ -56,15 +59,15 @@ import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.TimeZone; @RunWith(RobolectricTestRunner.class) public final class BatteryChartPreferenceControllerTest { private static final String PREF_KEY = "pref_key"; private static final String PREF_SUMMARY = "fake preference summary"; - private static final int DESIRED_HISTORY_SIZE = - BatteryChartPreferenceController.DESIRED_HISTORY_SIZE; @Mock private InstrumentedPreferenceFragment mFragment; @@ -77,11 +80,15 @@ public final class BatteryChartPreferenceControllerTest { @Mock private BatteryHistEntry mBatteryHistEntry; @Mock - private BatteryChartView mBatteryChartView; + private BatteryChartView mDailyChartView; + @Mock + private BatteryChartView mHourlyChartView; @Mock private PowerGaugePreference mPowerGaugePreference; @Mock private BatteryUtils mBatteryUtils; + @Mock + private LinearLayout.LayoutParams mLayoutParams; private Context mContext; private FakeFeatureFactory mFeatureFactory; @@ -94,6 +101,7 @@ public final class BatteryChartPreferenceControllerTest { 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); @@ -106,10 +114,12 @@ public final class BatteryChartPreferenceControllerTest { doReturn(new String[]{"com.android.gms.persistent"}) .when(mFeatureFactory.powerUsageFeatureProvider) .getHideApplicationEntries(mContext); + doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.mPrefContext = mContext; mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup; - mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartView; + mBatteryChartPreferenceController.mDailyChartView = mDailyChartView; + mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView; mBatteryDiffEntry = new BatteryDiffEntry( mContext, /*foregroundUsageTimeInMs=*/ 1, @@ -121,12 +131,10 @@ public final class BatteryChartPreferenceControllerTest { BatteryDiffEntry.sResourceCache.put( "fakeBatteryDiffEntryKey", new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1)); - mBatteryChartPreferenceController.setBatteryHistoryMap( - createBatteryHistoryMap()); } @Test - public void testOnDestroy_activityIsChanging_clearBatteryEntryCache() { + public void onDestroy_activityIsChanging_clearBatteryEntryCache() { doReturn(true).when(mSettingsActivity).isChangingConfigurations(); // Ensures the testing environment is correct. assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); @@ -136,7 +144,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testOnDestroy_activityIsNotChanging_notClearBatteryEntryCache() { + public void onDestroy_activityIsNotChanging_notClearBatteryEntryCache() { doReturn(false).when(mSettingsActivity).isChangingConfigurations(); // Ensures the testing environment is correct. assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); @@ -146,7 +154,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testOnDestroy_clearPreferenceCache() { + public void onDestroy_clearPreferenceCache() { // Ensures the testing environment is correct. mBatteryChartPreferenceController.mPreferenceCache.put( PREF_KEY, mPowerGaugePreference); @@ -158,113 +166,135 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testOnDestroy_removeAllPreferenceFromPreferenceGroup() { + public void onDestroy_removeAllPreferenceFromPreferenceGroup() { mBatteryChartPreferenceController.onDestroy(); verify(mAppListGroup).removeAll(); } @Test - public void testSetBatteryHistoryMap_createExpectedKeysAndLevels() { - mBatteryChartPreferenceController.setBatteryHistoryMap( - createBatteryHistoryMap()); + public void setBatteryChartViewModel_6Hours() { + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); - // Verifies the created battery keys array. - for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) { - assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index]) - // These values is are calculated by hand from createBatteryHistoryMap(). - .isEqualTo(index + 1); - } - // Verifies the created battery levels array. - for (int index = 0; index < 13; index++) { - assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index]) - // These values is are calculated by hand from createBatteryHistoryMap(). - .isEqualTo(100 - index * 2); - } - assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13); + verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE); + verify(mHourlyChartView, atLeastOnce()).setVisibility(View.VISIBLE); + verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( + List.of(100, 97, 95), + List.of("8 am", "10 am", "12 pm"), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); } @Test - public void testSetBatteryHistoryMap_largeSize_createExpectedKeysAndLevels() { - mBatteryChartPreferenceController.setBatteryHistoryMap( - createBatteryHistoryMap()); + public void setBatteryChartViewModel_60Hours() { + BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel( + List.of(100, 83, 59, 41), + List.of("Sat", "Sun", "Mon", "Mon"), + BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS); - // Verifies the created battery keys array. - for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) { - assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index]) - // These values is are calculated by hand from createBatteryHistoryMap(). - .isEqualTo(index + 1); - } - // Verifies the created battery levels array. - for (int index = 0; index < 13; index++) { - assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index]) - // These values is are calculated by hand from createBatteryHistoryMap(). - .isEqualTo(100 - index * 2); - } - assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13); + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); + + verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE); + verify(mHourlyChartView, atLeastOnce()).setVisibility(View.GONE); + verify(mDailyChartView).setViewModel(expectedDailyViewModel); + + reset(mDailyChartView); + reset(mHourlyChartView); + doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); + mBatteryChartPreferenceController.mDailyChartIndex = 0; + mBatteryChartPreferenceController.refreshUi(); + verify(mDailyChartView).setVisibility(View.VISIBLE); + verify(mHourlyChartView).setVisibility(View.VISIBLE); + + expectedDailyViewModel.setSelectedIndex(0); + verify(mDailyChartView).setViewModel(expectedDailyViewModel); + verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( + List.of(100, 97, 95, 93, 91, 89, 87, 85, 83), + List.of("8 am", "10 am", "12 pm", "2 pm", "4 pm", "6 pm", "8 pm", "10 pm", + "12 am"), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); + + reset(mDailyChartView); + reset(mHourlyChartView); + doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); + mBatteryChartPreferenceController.mDailyChartIndex = 1; + mBatteryChartPreferenceController.mHourlyChartIndex = 6; + mBatteryChartPreferenceController.refreshUi(); + verify(mDailyChartView).setVisibility(View.VISIBLE); + verify(mHourlyChartView).setVisibility(View.VISIBLE); + expectedDailyViewModel.setSelectedIndex(1); + verify(mDailyChartView).setViewModel(expectedDailyViewModel); + BatteryChartViewModel expectedHourlyViewModel = new BatteryChartViewModel( + List.of(83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59), + List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm", + "4 pm", "6 pm", "8 pm", "10 pm", "12 am"), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS); + expectedHourlyViewModel.setSelectedIndex(6); + verify(mHourlyChartView).setViewModel(expectedHourlyViewModel); + + reset(mDailyChartView); + reset(mHourlyChartView); + doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); + mBatteryChartPreferenceController.mDailyChartIndex = 2; + mBatteryChartPreferenceController.mHourlyChartIndex = + BatteryChartViewModel.SELECTED_INDEX_ALL; + mBatteryChartPreferenceController.refreshUi(); + verify(mDailyChartView).setVisibility(View.VISIBLE); + verify(mHourlyChartView).setVisibility(View.VISIBLE); + expectedDailyViewModel.setSelectedIndex(2); + verify(mDailyChartView).setViewModel(expectedDailyViewModel); + verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( + List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41), + List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm", + "4 pm", "6 pm"), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); } @Test - public void testRefreshUi_batteryIndexedMapIsNull_ignoreRefresh() { + public void refreshUi_normalCase_returnTrue() { + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); + assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue(); + } + + @Test + public void refreshUi_batteryIndexedMapIsNull_ignoreRefresh() { mBatteryChartPreferenceController.setBatteryHistoryMap(null); - assertThat(mBatteryChartPreferenceController.refreshUi( - /*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse(); + assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse(); } @Test - public void testRefreshUi_batteryChartViewIsNull_ignoreRefresh() { - mBatteryChartPreferenceController.mBatteryChartView = null; - assertThat(mBatteryChartPreferenceController.refreshUi( - /*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse(); + public void refreshUi_dailyChartViewIsNull_ignoreRefresh() { + mBatteryChartPreferenceController.mDailyChartView = null; + assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse(); } @Test - public void testRefreshUi_trapezoidIndexIsNotChanged_ignoreRefresh() { - final int trapezoidIndex = 1; - mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex; - assertThat(mBatteryChartPreferenceController.refreshUi( - trapezoidIndex, /*isForce=*/ false)).isFalse(); + public void refreshUi_hourlyChartViewIsNull_ignoreRefresh() { + mBatteryChartPreferenceController.mHourlyChartView = null; + assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse(); } @Test - public void testRefreshUi_forceUpdate_refreshUi() { - final int trapezoidIndex = 1; - mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex; - assertThat(mBatteryChartPreferenceController.refreshUi( - trapezoidIndex, /*isForce=*/ true)).isTrue(); - } - - @Test - public void testForceRefreshUi_updateTrapezoidIndexIntoSelectAll() { - mBatteryChartPreferenceController.mTrapezoidIndex = - BatteryChartView.SELECTED_INDEX_INVALID; - mBatteryChartPreferenceController.setBatteryHistoryMap( - createBatteryHistoryMap()); - - assertThat(mBatteryChartPreferenceController.mTrapezoidIndex) - .isEqualTo(BatteryChartView.SELECTED_INDEX_ALL); - } - - @Test - public void testRemoveAndCacheAllPrefs_emptyContent_ignoreRemoveAll() { - final int trapezoidIndex = 1; + public void removeAndCacheAllPrefs_emptyContent_ignoreRemoveAll() { + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); + mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap(); doReturn(0).when(mAppListGroup).getPreferenceCount(); - mBatteryChartPreferenceController.refreshUi( - trapezoidIndex, /*isForce=*/ true); + mBatteryChartPreferenceController.refreshUi(); verify(mAppListGroup, never()).removeAll(); } @Test - public void testRemoveAndCacheAllPrefs_buildCacheAndRemoveAllPreference() { - final int trapezoidIndex = 1; + 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( - trapezoidIndex, /*isForce=*/ true); + mBatteryChartPreferenceController.refreshUi(); assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY)) .isEqualTo(mPowerGaugePreference); @@ -272,14 +302,14 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testAddPreferenceToScreen_emptyContent_ignoreAddPreference() { + public void addPreferenceToScreen_emptyContent_ignoreAddPreference() { mBatteryChartPreferenceController.addPreferenceToScreen( new ArrayList()); verify(mAppListGroup, never()).addPreference(any()); } @Test - public void testAddPreferenceToScreen_addPreferenceIntoScreen() { + public void addPreferenceToScreen_addPreferenceIntoScreen() { final String appLabel = "fake app label"; doReturn(1).when(mAppListGroup).getPreferenceCount(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); @@ -308,7 +338,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testAddPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() { + public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() { final String appLabel = "fake app label"; doReturn(1).when(mAppListGroup).getPreferenceCount(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); @@ -323,7 +353,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testHandlePreferenceTreeiClick_notPowerGaugePreference_returnFalse() { + public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() { assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup)) .isFalse(); @@ -334,7 +364,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testHandlePreferenceTreeClick_forAppEntry_returnTrue() { + public void handlePreferenceTreeClick_forAppEntry_returnTrue() { doReturn(false).when(mBatteryHistEntry).isAppEntry(); doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); @@ -350,7 +380,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testHandlePreferenceTreeClick_forSystemEntry_returnTrue() { + public void handlePreferenceTreeClick_forSystemEntry_returnTrue() { mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils; doReturn(true).when(mBatteryHistEntry).isAppEntry(); doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); @@ -367,7 +397,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testSetPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() { + public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() { final PowerGaugePreference pref = new PowerGaugePreference(mContext); pref.setSummary(PREF_SUMMARY); @@ -379,7 +409,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testSetPreferenceSummary_setBackgroundUsageTimeOnly() { + public void setPreferenceSummary_setBackgroundUsageTimeOnly() { final PowerGaugePreference pref = new PowerGaugePreference(mContext); pref.setSummary(PREF_SUMMARY); @@ -391,7 +421,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testSetPreferenceSummary_setTotalUsageTimeLessThanAMinute() { + public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() { final PowerGaugePreference pref = new PowerGaugePreference(mContext); pref.setSummary(PREF_SUMMARY); @@ -403,7 +433,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testSetPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() { + public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() { final PowerGaugePreference pref = new PowerGaugePreference(mContext); pref.setSummary(PREF_SUMMARY); @@ -416,7 +446,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testSetPreferenceSummary_setTotalAndBackgroundUsageTime() { + public void setPreferenceSummary_setTotalAndBackgroundUsageTime() { final PowerGaugePreference pref = new PowerGaugePreference(mContext); pref.setSummary(PREF_SUMMARY); @@ -428,7 +458,7 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testSetPreferenceSummary_notAllowShownPackage_setSummayAsNull() { + public void setPreferenceSummary_notAllowShownPackage_setSummayAsNull() { final PowerGaugePreference pref = new PowerGaugePreference(mContext); pref.setSummary(PREF_SUMMARY); final BatteryDiffEntry batteryDiffEntry = @@ -443,36 +473,9 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testValidateUsageTime_returnTrueIfBatteryDiffEntryIsValid() { - assertThat(BatteryChartPreferenceController.validateUsageTime( - createBatteryDiffEntry( - /*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS, - /*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS))) - .isTrue(); - } - - @Test - public void testValidateUsageTime_foregroundTimeExceedThreshold_returnFalse() { - assertThat(BatteryChartPreferenceController.validateUsageTime( - createBatteryDiffEntry( - /*foregroundUsageTimeInMs=*/ DateUtils.HOUR_IN_MILLIS * 3, - /*backgroundUsageTimeInMs=*/ 0))) - .isFalse(); - } - - @Test - public void testValidateUsageTime_backgroundTimeExceedThreshold_returnFalse() { - assertThat(BatteryChartPreferenceController.validateUsageTime( - createBatteryDiffEntry( - /*foregroundUsageTimeInMs=*/ 0, - /*backgroundUsageTimeInMs=*/ DateUtils.HOUR_IN_MILLIS * 3))) - .isFalse(); - } - - @Test - public void testOnExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() { + public void onExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() { doReturn(1).when(mAppListGroup).getPreferenceCount(); - mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry); + mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap(); doReturn("label").when(mBatteryDiffEntry).getAppLabel(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); doReturn(PREF_KEY).when(mBatteryHistEntry).getKey(); @@ -491,10 +494,10 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testOnExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() { + public void onExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() { doReturn(PREF_KEY).when(mBatteryHistEntry).getKey(); doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY); - mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry); + mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap(); // Verifies the cache is empty first. assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty(); @@ -511,57 +514,17 @@ public final class BatteryChartPreferenceControllerTest { } @Test - public void testOnSelect_selectSpecificTimeSlot_logMetric() { - mBatteryChartPreferenceController.onSelect(1 /*slot index*/); - - verify(mMetricsFeatureProvider) - .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT); - } - - @Test - public void testOnSelect_selectAll_logMetric() { - mBatteryChartPreferenceController.onSelect( - BatteryChartView.SELECTED_INDEX_ALL /*slot index*/); - - verify(mMetricsFeatureProvider) - .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL); - } - - @Test - public void testRefreshCategoryTitle_setHourIntoBothTitleTextView() { - mBatteryChartPreferenceController = createController(); - setUpBatteryHistoryKeys(); - mBatteryChartPreferenceController.mAppListPrefGroup = - spy(new PreferenceCategory(mContext)); - mBatteryChartPreferenceController.mExpandDividerPreference = - spy(new ExpandDividerPreference(mContext)); - // Simulates select the first slot. - mBatteryChartPreferenceController.mTrapezoidIndex = 0; - - mBatteryChartPreferenceController.refreshCategoryTitle(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - // Verifies the title in the preference group. - verify(mBatteryChartPreferenceController.mAppListPrefGroup) - .setTitle(captor.capture()); - assertThat(captor.getValue()).isNotEqualTo("App usage for past 24 hr"); - // Verifies the title in the expandable divider. - captor = ArgumentCaptor.forClass(String.class); - verify(mBatteryChartPreferenceController.mExpandDividerPreference) - .setTitle(captor.capture()); - assertThat(captor.getValue()).isNotEqualTo("System usage for past 24 hr"); - } - - @Test - public void testRefreshCategoryTitle_setLast24HrIntoBothTitleTextView() { + public void refreshCategoryTitle_setLastFullChargeIntoBothTitleTextView() { mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.mAppListPrefGroup = spy(new PreferenceCategory(mContext)); mBatteryChartPreferenceController.mExpandDividerPreference = spy(new ExpandDividerPreference(mContext)); // Simulates select all condition. - mBatteryChartPreferenceController.mTrapezoidIndex = - BatteryChartView.SELECTED_INDEX_ALL; + mBatteryChartPreferenceController.mDailyChartIndex = + BatteryChartViewModel.SELECTED_INDEX_ALL; + mBatteryChartPreferenceController.mHourlyChartIndex = + BatteryChartViewModel.SELECTED_INDEX_ALL; mBatteryChartPreferenceController.refreshCategoryTitle(); @@ -570,76 +533,93 @@ public final class BatteryChartPreferenceControllerTest { verify(mBatteryChartPreferenceController.mAppListPrefGroup) .setTitle(captor.capture()); assertThat(captor.getValue()) - .isEqualTo("App usage for past 24 hr"); + .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 for past 24 hr"); + .isEqualTo("System usage since last full charge"); } @Test - public void testSetTimestampLabel_nullBatteryHistoryKeys_ignore() { - mBatteryChartPreferenceController = createController(); - mBatteryChartPreferenceController.mBatteryHistoryKeys = null; - mBatteryChartPreferenceController.mBatteryChartView = - spy(new BatteryChartView(mContext)); - mBatteryChartPreferenceController.setTimestampLabel(); + public void selectedSlotText_selectAllDaysAllHours_returnNull() { + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); + mBatteryChartPreferenceController.mDailyChartIndex = + BatteryChartViewModel.SELECTED_INDEX_ALL; + mBatteryChartPreferenceController.mHourlyChartIndex = + BatteryChartViewModel.SELECTED_INDEX_ALL; - verify(mBatteryChartPreferenceController.mBatteryChartView, never()) - .setLatestTimestamp(anyLong()); + assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null); } @Test - public void testSetTimestampLabel_setExpectedTimestampData() { - mBatteryChartPreferenceController = createController(); - mBatteryChartPreferenceController.mBatteryChartView = - spy(new BatteryChartView(mContext)); - setUpBatteryHistoryKeys(); + public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() { + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); + mBatteryChartPreferenceController.mDailyChartIndex = 0; + mBatteryChartPreferenceController.mHourlyChartIndex = + BatteryChartViewModel.SELECTED_INDEX_ALL; - mBatteryChartPreferenceController.setTimestampLabel(); - - verify(mBatteryChartPreferenceController.mBatteryChartView) - .setLatestTimestamp(1619247636826L); + assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null); } @Test - public void testSetTimestampLabel_withoutValidTimestamp_setExpectedTimestampData() { - mBatteryChartPreferenceController = createController(); - mBatteryChartPreferenceController.mBatteryChartView = - spy(new BatteryChartView(mContext)); - mBatteryChartPreferenceController.mBatteryHistoryKeys = new long[]{0L}; + public void selectedSlotText_selectADayAllHours_onlyDayText() { + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); + mBatteryChartPreferenceController.mDailyChartIndex = 1; + mBatteryChartPreferenceController.mHourlyChartIndex = + BatteryChartViewModel.SELECTED_INDEX_ALL; - mBatteryChartPreferenceController.setTimestampLabel(); - - verify(mBatteryChartPreferenceController.mBatteryChartView) - .setLatestTimestamp(anyLong()); + assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("Sunday"); } @Test - public void testOnSaveInstanceState_restoreSelectedIndexAndExpandState() { - final int expectedIndex = 1; + public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() { + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); + mBatteryChartPreferenceController.mDailyChartIndex = 0; + mBatteryChartPreferenceController.mHourlyChartIndex = 1; + + assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo( + "10 am - 12 pm"); + } + + @Test + public void selectedSlotText_SelectADayAnHour_dayAndHourText() { + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); + mBatteryChartPreferenceController.mDailyChartIndex = 1; + mBatteryChartPreferenceController.mHourlyChartIndex = 8; + + assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo( + "Sunday 4 pm - 6 pm"); + } + + @Test + public void onSaveInstanceState_restoreSelectedIndexAndExpandState() { + final int expectedDailyIndex = 1; + final int expectedHourlyIndex = 2; final boolean isExpanded = true; final Bundle bundle = new Bundle(); - mBatteryChartPreferenceController.mTrapezoidIndex = expectedIndex; + mBatteryChartPreferenceController.mDailyChartIndex = expectedDailyIndex; + mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex; mBatteryChartPreferenceController.mIsExpanded = isExpanded; mBatteryChartPreferenceController.onSaveInstanceState(bundle); // Replaces the original controller with other values. - mBatteryChartPreferenceController.mTrapezoidIndex = -1; + mBatteryChartPreferenceController.mDailyChartIndex = -1; + mBatteryChartPreferenceController.mHourlyChartIndex = -1; mBatteryChartPreferenceController.mIsExpanded = false; mBatteryChartPreferenceController.onCreate(bundle); - mBatteryChartPreferenceController.setBatteryHistoryMap( - createBatteryHistoryMap()); + mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25)); - assertThat(mBatteryChartPreferenceController.mTrapezoidIndex) - .isEqualTo(expectedIndex); + assertThat(mBatteryChartPreferenceController.mDailyChartIndex) + .isEqualTo(expectedDailyIndex); + assertThat(mBatteryChartPreferenceController.mHourlyChartIndex) + .isEqualTo(expectedHourlyIndex); assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue(); } @Test - public void testIsValidToShowSummary_returnExpectedResult() { + public void isValidToShowSummary_returnExpectedResult() { assertThat(mBatteryChartPreferenceController .isValidToShowSummary("com.google.android.apps.scone")) .isTrue(); @@ -650,31 +630,42 @@ public final class BatteryChartPreferenceControllerTest { .isFalse(); } - @Test - public void testIsValidToShowEntry_returnExpectedResult() { - assertThat(mBatteryChartPreferenceController - .isValidToShowEntry("com.google.android.apps.scone")) - .isTrue(); - - // Verifies the items which are defined in the array list. - assertThat(mBatteryChartPreferenceController - .isValidToShowEntry("com.android.gms.persistent")) - .isFalse(); + private static Long generateTimestamp(int index) { + // "2021-04-23 07:00:00 UTC" + index hours + return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS; } - private static Map> createBatteryHistoryMap() { + private static Map> createBatteryHistoryMap( + int numOfHours) { final Map> batteryHistoryMap = new HashMap<>(); - for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) { + for (int index = 0; index < numOfHours; index++) { final ContentValues values = new ContentValues(); values.put("batteryLevel", Integer.valueOf(100 - index)); + values.put("consumePower", Integer.valueOf(100 - index)); final BatteryHistEntry entry = new BatteryHistEntry(values); final Map entryMap = new HashMap<>(); entryMap.put("fake_entry_key" + index, entry); - batteryHistoryMap.put(Long.valueOf(index + 1), entryMap); + batteryHistoryMap.put(generateTimestamp(index), entryMap); } return batteryHistoryMap; } + private Map> 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( @@ -682,13 +673,6 @@ public final class BatteryChartPreferenceControllerTest { /*consumePower=*/ 0, mBatteryHistEntry); } - private void setUpBatteryHistoryKeys() { - mBatteryChartPreferenceController.mBatteryHistoryKeys = - new long[]{1619196786769L, 0L, 1619247636826L}; - ConvertUtils.utcToLocalTimeHour( - mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); - } - private BatteryChartPreferenceController createController() { final BatteryChartPreferenceController controller = new BatteryChartPreferenceController( diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2Test.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2Test.java deleted file mode 100644 index e738e6534f6..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2Test.java +++ /dev/null @@ -1,684 +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.any; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.app.settings.SettingsEnums; -import android.content.ContentValues; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.LocaleList; -import android.text.format.DateUtils; -import android.view.View; -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.core.InstrumentedPreferenceFragment; -import com.android.settings.fuelgauge.BatteryUtils; -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.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -@RunWith(RobolectricTestRunner.class) -public final class BatteryChartPreferenceControllerV2Test { - 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 mAppListGroup; - @Mock - private Drawable mDrawable; - @Mock - private BatteryHistEntry mBatteryHistEntry; - @Mock - private BatteryChartViewV2 mDailyChartView; - @Mock - private BatteryChartViewV2 mHourlyChartView; - @Mock - private PowerGaugePreference mPowerGaugePreference; - @Mock - private BatteryUtils mBatteryUtils; - @Mock - private LinearLayout.LayoutParams mLayoutParams; - - private Context mContext; - private FakeFeatureFactory mFeatureFactory; - private BatteryDiffEntry mBatteryDiffEntry; - private MetricsFeatureProvider mMetricsFeatureProvider; - private BatteryChartPreferenceControllerV2 mBatteryChartPreferenceController; - - @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.googlequicksearchbox"}) - .when(mFeatureFactory.powerUsageFeatureProvider) - .getHideApplicationSummary(mContext); - doReturn(new String[]{"com.android.gms.persistent"}) - .when(mFeatureFactory.powerUsageFeatureProvider) - .getHideApplicationEntries(mContext); - doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); - mBatteryChartPreferenceController = createController(); - mBatteryChartPreferenceController.mPrefContext = mContext; - mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup; - mBatteryChartPreferenceController.mDailyChartView = mDailyChartView; - mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView; - mBatteryDiffEntry = new BatteryDiffEntry( - mContext, - /*foregroundUsageTimeInMs=*/ 1, - /*backgroundUsageTimeInMs=*/ 2, - /*consumePower=*/ 3, - mBatteryHistEntry); - mBatteryDiffEntry = spy(mBatteryDiffEntry); - // Adds fake testing data. - BatteryDiffEntry.sResourceCache.put( - "fakeBatteryDiffEntryKey", - new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1)); - } - - @Test - public void onDestroy_activityIsChanging_clearBatteryEntryCache() { - doReturn(true).when(mSettingsActivity).isChangingConfigurations(); - // Ensures the testing environment is correct. - assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); - - mBatteryChartPreferenceController.onDestroy(); - assertThat(BatteryDiffEntry.sResourceCache).isEmpty(); - } - - @Test - public void onDestroy_activityIsNotChanging_notClearBatteryEntryCache() { - doReturn(false).when(mSettingsActivity).isChangingConfigurations(); - // Ensures the testing environment is correct. - assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); - - mBatteryChartPreferenceController.onDestroy(); - 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 - public void setBatteryChartViewModel_6Hours() { - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); - - verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE); - verify(mHourlyChartView, atLeastOnce()).setVisibility(View.VISIBLE); - verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( - List.of(100, 97, 95), - List.of("8 am", "10 am", "12 pm"), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); - } - - @Test - public void setBatteryChartViewModel_60Hours() { - BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel( - List.of(100, 83, 59, 41), - List.of("Sat", "Sun", "Mon", "Mon"), - BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS); - - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); - - verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE); - verify(mHourlyChartView, atLeastOnce()).setVisibility(View.GONE); - verify(mDailyChartView).setViewModel(expectedDailyViewModel); - - reset(mDailyChartView); - reset(mHourlyChartView); - doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); - mBatteryChartPreferenceController.mDailyChartIndex = 0; - mBatteryChartPreferenceController.refreshUi(); - verify(mDailyChartView).setVisibility(View.VISIBLE); - verify(mHourlyChartView).setVisibility(View.VISIBLE); - - expectedDailyViewModel.setSelectedIndex(0); - verify(mDailyChartView).setViewModel(expectedDailyViewModel); - verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( - List.of(100, 97, 95, 93, 91, 89, 87, 85, 83), - List.of("8 am", "10 am", "12 pm", "2 pm", "4 pm", "6 pm", "8 pm", "10 pm", - "12 am"), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); - - reset(mDailyChartView); - reset(mHourlyChartView); - doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); - mBatteryChartPreferenceController.mDailyChartIndex = 1; - mBatteryChartPreferenceController.mHourlyChartIndex = 6; - mBatteryChartPreferenceController.refreshUi(); - verify(mDailyChartView).setVisibility(View.VISIBLE); - verify(mHourlyChartView).setVisibility(View.VISIBLE); - expectedDailyViewModel.setSelectedIndex(1); - verify(mDailyChartView).setViewModel(expectedDailyViewModel); - BatteryChartViewModel expectedHourlyViewModel = new BatteryChartViewModel( - List.of(83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59), - List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm", - "4 pm", "6 pm", "8 pm", "10 pm", "12 am"), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS); - expectedHourlyViewModel.setSelectedIndex(6); - verify(mHourlyChartView).setViewModel(expectedHourlyViewModel); - - reset(mDailyChartView); - reset(mHourlyChartView); - doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); - mBatteryChartPreferenceController.mDailyChartIndex = 2; - mBatteryChartPreferenceController.mHourlyChartIndex = - BatteryChartViewModel.SELECTED_INDEX_ALL; - mBatteryChartPreferenceController.refreshUi(); - verify(mDailyChartView).setVisibility(View.VISIBLE); - verify(mHourlyChartView).setVisibility(View.VISIBLE); - expectedDailyViewModel.setSelectedIndex(2); - verify(mDailyChartView).setViewModel(expectedDailyViewModel); - verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( - List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41), - List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm", - "4 pm", "6 pm"), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); - } - - @Test - public void refreshUi_normalCase_returnTrue() { - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); - assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue(); - } - - @Test - public void refreshUi_batteryIndexedMapIsNull_ignoreRefresh() { - mBatteryChartPreferenceController.setBatteryHistoryMap(null); - assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse(); - } - - @Test - public void refreshUi_dailyChartViewIsNull_ignoreRefresh() { - mBatteryChartPreferenceController.mDailyChartView = null; - assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse(); - } - - @Test - public void refreshUi_hourlyChartViewIsNull_ignoreRefresh() { - mBatteryChartPreferenceController.mHourlyChartView = null; - 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()); - 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 setPreferenceSummary_notAllowShownPackage_setSummayAsNull() { - final PowerGaugePreference pref = new PowerGaugePreference(mContext); - pref.setSummary(PREF_SUMMARY); - final BatteryDiffEntry batteryDiffEntry = - spy(createBatteryDiffEntry( - /*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS, - /*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS)); - doReturn("com.android.googlequicksearchbox").when(batteryDiffEntry) - .getPackageName(); - - mBatteryChartPreferenceController.setPreferenceSummary(pref, batteryDiffEntry); - assertThat(pref.getSummary()).isNull(); - } - - @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 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 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 - public void selectedSlotText_selectAllDaysAllHours_returnNull() { - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); - mBatteryChartPreferenceController.mDailyChartIndex = - BatteryChartViewModel.SELECTED_INDEX_ALL; - mBatteryChartPreferenceController.mHourlyChartIndex = - BatteryChartViewModel.SELECTED_INDEX_ALL; - - assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null); - } - - @Test - public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() { - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); - mBatteryChartPreferenceController.mDailyChartIndex = 0; - mBatteryChartPreferenceController.mHourlyChartIndex = - BatteryChartViewModel.SELECTED_INDEX_ALL; - - assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null); - } - - @Test - public void selectedSlotText_selectADayAllHours_onlyDayText() { - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); - mBatteryChartPreferenceController.mDailyChartIndex = 1; - mBatteryChartPreferenceController.mHourlyChartIndex = - BatteryChartViewModel.SELECTED_INDEX_ALL; - - assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("Sunday"); - } - - @Test - public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() { - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); - mBatteryChartPreferenceController.mDailyChartIndex = 0; - mBatteryChartPreferenceController.mHourlyChartIndex = 1; - - assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo( - "10 am - 12 pm"); - } - - @Test - public void selectedSlotText_SelectADayAnHour_dayAndHourText() { - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); - mBatteryChartPreferenceController.mDailyChartIndex = 1; - mBatteryChartPreferenceController.mHourlyChartIndex = 8; - - assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo( - "Sunday 4 pm - 6 pm"); - } - - @Test - public void onSaveInstanceState_restoreSelectedIndexAndExpandState() { - final int expectedDailyIndex = 1; - final int expectedHourlyIndex = 2; - final boolean isExpanded = true; - final Bundle bundle = new Bundle(); - mBatteryChartPreferenceController.mDailyChartIndex = expectedDailyIndex; - mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex; - mBatteryChartPreferenceController.mIsExpanded = isExpanded; - mBatteryChartPreferenceController.onSaveInstanceState(bundle); - // Replaces the original controller with other values. - mBatteryChartPreferenceController.mDailyChartIndex = -1; - mBatteryChartPreferenceController.mHourlyChartIndex = -1; - mBatteryChartPreferenceController.mIsExpanded = false; - - mBatteryChartPreferenceController.onCreate(bundle); - mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25)); - - assertThat(mBatteryChartPreferenceController.mDailyChartIndex) - .isEqualTo(expectedDailyIndex); - assertThat(mBatteryChartPreferenceController.mHourlyChartIndex) - .isEqualTo(expectedHourlyIndex); - assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue(); - } - - @Test - public void isValidToShowSummary_returnExpectedResult() { - assertThat(mBatteryChartPreferenceController - .isValidToShowSummary("com.google.android.apps.scone")) - .isTrue(); - - // Verifies the item which is defined in the array list. - assertThat(mBatteryChartPreferenceController - .isValidToShowSummary("com.android.googlequicksearchbox")) - .isFalse(); - } - - private static Long generateTimestamp(int index) { - // "2021-04-23 07:00:00 UTC" + index hours - return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS; - } - - private static Map> createBatteryHistoryMap( - int numOfHours) { - final Map> batteryHistoryMap = new HashMap<>(); - for (int index = 0; index < numOfHours; index++) { - final ContentValues values = new ContentValues(); - values.put("batteryLevel", Integer.valueOf(100 - index)); - values.put("consumePower", Integer.valueOf(100 - index)); - final BatteryHistEntry entry = new BatteryHistEntry(values); - final Map entryMap = new HashMap<>(); - entryMap.put("fake_entry_key" + index, entry); - batteryHistoryMap.put(generateTimestamp(index), entryMap); - } - return batteryHistoryMap; - } - - private Map> 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, mBatteryHistEntry); - } - - private BatteryChartPreferenceControllerV2 createController() { - final BatteryChartPreferenceControllerV2 controller = - new BatteryChartPreferenceControllerV2( - mContext, "app_list", /*lifecycle=*/ null, - mSettingsActivity, mFragment); - controller.mPrefContext = mContext; - return controller; - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java index a2d8ca95c07..8a430875614 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; import android.os.LocaleList; +import android.view.View; import android.view.accessibility.AccessibilityManager; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; @@ -41,6 +42,7 @@ import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Locale; @RunWith(RobolectricTestRunner.class) @@ -55,6 +57,8 @@ public final class BatteryChartViewTest { private AccessibilityServiceInfo mMockAccessibilityServiceInfo; @Mock private AccessibilityManager mMockAccessibilityManager; + @Mock + private View mMockView; @Before public void setUp() { @@ -74,13 +78,13 @@ public final class BatteryChartViewTest { } @Test - public void testIsAccessibilityEnabled_disable_returnFalse() { + public void isAccessibilityEnabled_disable_returnFalse() { doReturn(false).when(mMockAccessibilityManager).isEnabled(); assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse(); } @Test - public void testIsAccessibilityEnabled_emptyInfo_returnFalse() { + public void isAccessibilityEnabled_emptyInfo_returnFalse() { doReturn(true).when(mMockAccessibilityManager).isEnabled(); doReturn(new ArrayList()) .when(mMockAccessibilityManager) @@ -90,68 +94,70 @@ public final class BatteryChartViewTest { } @Test - public void testIsAccessibilityEnabled_validServiceId_returnTrue() { + public void isAccessibilityEnabled_validServiceId_returnTrue() { doReturn(true).when(mMockAccessibilityManager).isEnabled(); assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isTrue(); } @Test - public void testSetSelectedIndex_invokesCallback() { + public void onClick_invokesCallback() { + final int originalSelectedIndex = 2; + BatteryChartViewModel batteryChartViewModel = new BatteryChartViewModel( + List.of(90, 80, 70, 60), List.of("", "", "", ""), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS); + batteryChartViewModel.setSelectedIndex(originalSelectedIndex); + mBatteryChartView.setViewModel(batteryChartViewModel); + for (int i = 0; i < mBatteryChartView.mTrapezoidSlots.length; i++) { + mBatteryChartView.mTrapezoidSlots[i] = new BatteryChartView.TrapezoidSlot(); + mBatteryChartView.mTrapezoidSlots[i].mLeft = i; + mBatteryChartView.mTrapezoidSlots[i].mRight = i + 0.5f; + } final int[] selectedIndex = new int[1]; - final int expectedIndex = 2; - mBatteryChartView.mSelectedIndex = 1; mBatteryChartView.setOnSelectListener( trapezoidIndex -> { selectedIndex[0] = trapezoidIndex; }); - mBatteryChartView.setSelectedIndex(expectedIndex); + // Verify onClick() a different index 1. + mBatteryChartView.mTouchUpEventX = 1; + selectedIndex[0] = Integer.MIN_VALUE; + mBatteryChartView.onClick(mMockView); + assertThat(selectedIndex[0]).isEqualTo(1); - assertThat(mBatteryChartView.mSelectedIndex) - .isEqualTo(expectedIndex); - assertThat(selectedIndex[0]).isEqualTo(expectedIndex); + // Verify onClick() the same index 2. + mBatteryChartView.mTouchUpEventX = 2; + selectedIndex[0] = Integer.MIN_VALUE; + mBatteryChartView.onClick(mMockView); + assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewModel.SELECTED_INDEX_ALL); } @Test - public void testSetSelectedIndex_sameIndex_notInvokesCallback() { - final int[] selectedIndex = new int[1]; - final int expectedIndex = 1; - mBatteryChartView.mSelectedIndex = expectedIndex; - mBatteryChartView.setOnSelectListener( - trapezoidIndex -> { - selectedIndex[0] = trapezoidIndex; - }); - - mBatteryChartView.setSelectedIndex(expectedIndex); - - assertThat(selectedIndex[0]).isNotEqualTo(expectedIndex); - } - - @Test - public void testClickable_isChartGraphSlotsEnabledIsFalse_notClickable() { + public void clickable_isChartGraphSlotsEnabledIsFalse_notClickable() { mBatteryChartView.setClickableForce(true); when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) .thenReturn(false); mBatteryChartView.onAttachedToWindow(); + assertThat(mBatteryChartView.isClickable()).isFalse(); assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); } @Test - public void testClickable_accessibilityIsDisabled_clickable() { + public void clickable_accessibilityIsDisabled_clickable() { mBatteryChartView.setClickableForce(true); when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) .thenReturn(true); doReturn(false).when(mMockAccessibilityManager).isEnabled(); mBatteryChartView.onAttachedToWindow(); + assertThat(mBatteryChartView.isClickable()).isTrue(); assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); } @Test - public void testClickable_accessibilityIsEnabledWithoutValidId_clickable() { + public void clickable_accessibilityIsEnabledWithoutValidId_clickable() { mBatteryChartView.setClickableForce(true); when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) .thenReturn(true); @@ -161,30 +167,34 @@ public final class BatteryChartViewTest { .getEnabledAccessibilityServiceList(anyInt()); mBatteryChartView.onAttachedToWindow(); + assertThat(mBatteryChartView.isClickable()).isTrue(); assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); } @Test - public void testClickable_accessibilityIsEnabledWithValidId_notClickable() { + public void clickable_accessibilityIsEnabledWithValidId_notClickable() { mBatteryChartView.setClickableForce(true); when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) .thenReturn(true); doReturn(true).when(mMockAccessibilityManager).isEnabled(); mBatteryChartView.onAttachedToWindow(); + assertThat(mBatteryChartView.isClickable()).isFalse(); assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); } @Test - public void testClickable_restoreFromNonClickableState() { - final int[] levels = new int[13]; - for (int index = 0; index < levels.length; index++) { - levels[index] = index + 1; + public void clickable_restoreFromNonClickableState() { + final List levels = new ArrayList(); + final List texts = new ArrayList(); + for (int index = 0; index < 13; index++) { + levels.add(index + 1); + texts.add(""); } - mBatteryChartView.setTrapezoidCount(12); - mBatteryChartView.setLevels(levels); + mBatteryChartView.setViewModel(new BatteryChartViewModel(levels, texts, + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); mBatteryChartView.setClickableForce(true); when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) .thenReturn(true); @@ -201,14 +211,14 @@ public final class BatteryChartViewTest { } @Test - public void testOnAttachedToWindow_addAccessibilityStateChangeListener() { + public void onAttachedToWindow_addAccessibilityStateChangeListener() { mBatteryChartView.onAttachedToWindow(); verify(mMockAccessibilityManager) .addAccessibilityStateChangeListener(mBatteryChartView); } @Test - public void testOnDetachedFromWindow_removeAccessibilityStateChangeListener() { + public void onDetachedFromWindow_removeAccessibilityStateChangeListener() { mBatteryChartView.onAttachedToWindow(); mBatteryChartView.mHandler.postDelayed( mBatteryChartView.mUpdateClickableStateRun, 1000); @@ -223,7 +233,7 @@ public final class BatteryChartViewTest { } @Test - public void testOnAccessibilityStateChanged_postUpdateStateRunnable() { + public void onAccessibilityStateChanged_postUpdateStateRunnable() { mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler); mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java deleted file mode 100644 index 10e62d69dfb..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java +++ /dev/null @@ -1,245 +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.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.accessibilityservice.AccessibilityServiceInfo; -import android.content.Context; -import android.os.LocaleList; -import android.view.View; -import android.view.accessibility.AccessibilityManager; - -import com.android.settings.fuelgauge.PowerUsageFeatureProvider; -import com.android.settings.testutils.FakeFeatureFactory; - -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.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -@RunWith(RobolectricTestRunner.class) -public final class BatteryChartViewV2Test { - - private Context mContext; - private BatteryChartViewV2 mBatteryChartView; - private FakeFeatureFactory mFeatureFactory; - private PowerUsageFeatureProvider mPowerUsageFeatureProvider; - - @Mock - private AccessibilityServiceInfo mMockAccessibilityServiceInfo; - @Mock - private AccessibilityManager mMockAccessibilityManager; - @Mock - private View mMockView; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mFeatureFactory = FakeFeatureFactory.setupForTest(); - mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider; - mContext = spy(RuntimeEnvironment.application); - mContext.getResources().getConfiguration().setLocales( - new LocaleList(new Locale("en_US"))); - mBatteryChartView = new BatteryChartViewV2(mContext); - doReturn(mMockAccessibilityManager).when(mContext) - .getSystemService(AccessibilityManager.class); - doReturn("TalkBackService").when(mMockAccessibilityServiceInfo).getId(); - doReturn(Arrays.asList(mMockAccessibilityServiceInfo)) - .when(mMockAccessibilityManager) - .getEnabledAccessibilityServiceList(anyInt()); - } - - @Test - public void isAccessibilityEnabled_disable_returnFalse() { - doReturn(false).when(mMockAccessibilityManager).isEnabled(); - assertThat(BatteryChartViewV2.isAccessibilityEnabled(mContext)).isFalse(); - } - - @Test - public void isAccessibilityEnabled_emptyInfo_returnFalse() { - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - doReturn(new ArrayList()) - .when(mMockAccessibilityManager) - .getEnabledAccessibilityServiceList(anyInt()); - - assertThat(BatteryChartViewV2.isAccessibilityEnabled(mContext)).isFalse(); - } - - @Test - public void isAccessibilityEnabled_validServiceId_returnTrue() { - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - assertThat(BatteryChartViewV2.isAccessibilityEnabled(mContext)).isTrue(); - } - - @Test - public void onClick_invokesCallback() { - final int originalSelectedIndex = 2; - BatteryChartViewModel batteryChartViewModel = new BatteryChartViewModel( - List.of(90, 80, 70, 60), List.of("", "", "", ""), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS); - batteryChartViewModel.setSelectedIndex(originalSelectedIndex); - mBatteryChartView.setViewModel(batteryChartViewModel); - for (int i = 0; i < mBatteryChartView.mTrapezoidSlots.length; i++) { - mBatteryChartView.mTrapezoidSlots[i] = new BatteryChartViewV2.TrapezoidSlot(); - mBatteryChartView.mTrapezoidSlots[i].mLeft = i; - mBatteryChartView.mTrapezoidSlots[i].mRight = i + 0.5f; - } - final int[] selectedIndex = new int[1]; - mBatteryChartView.setOnSelectListener( - trapezoidIndex -> { - selectedIndex[0] = trapezoidIndex; - }); - - // Verify onClick() a different index 1. - mBatteryChartView.mTouchUpEventX = 1; - selectedIndex[0] = Integer.MIN_VALUE; - mBatteryChartView.onClick(mMockView); - assertThat(selectedIndex[0]).isEqualTo(1); - - // Verify onClick() the same index 2. - mBatteryChartView.mTouchUpEventX = 2; - selectedIndex[0] = Integer.MIN_VALUE; - mBatteryChartView.onClick(mMockView); - assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewModel.SELECTED_INDEX_ALL); - } - - @Test - public void clickable_isChartGraphSlotsEnabledIsFalse_notClickable() { - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(false); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isFalse(); - assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); - } - - @Test - public void clickable_accessibilityIsDisabled_clickable() { - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(true); - doReturn(false).when(mMockAccessibilityManager).isEnabled(); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isTrue(); - assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); - } - - @Test - public void clickable_accessibilityIsEnabledWithoutValidId_clickable() { - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(true); - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - doReturn(new ArrayList()) - .when(mMockAccessibilityManager) - .getEnabledAccessibilityServiceList(anyInt()); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isTrue(); - assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); - } - - @Test - public void clickable_accessibilityIsEnabledWithValidId_notClickable() { - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(true); - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isFalse(); - assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); - } - - @Test - public void clickable_restoreFromNonClickableState() { - final List levels = new ArrayList(); - final List texts = new ArrayList(); - for (int index = 0; index < 13; index++) { - levels.add(index + 1); - texts.add(""); - } - mBatteryChartView.setViewModel(new BatteryChartViewModel(levels, texts, - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(true); - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - mBatteryChartView.onAttachedToWindow(); - // Ensures the testing environment is correct. - assertThat(mBatteryChartView.isClickable()).isFalse(); - // Turns off accessibility service. - doReturn(false).when(mMockAccessibilityManager).isEnabled(); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isTrue(); - } - - @Test - public void onAttachedToWindow_addAccessibilityStateChangeListener() { - mBatteryChartView.onAttachedToWindow(); - verify(mMockAccessibilityManager) - .addAccessibilityStateChangeListener(mBatteryChartView); - } - - @Test - public void onDetachedFromWindow_removeAccessibilityStateChangeListener() { - mBatteryChartView.onAttachedToWindow(); - mBatteryChartView.mHandler.postDelayed( - mBatteryChartView.mUpdateClickableStateRun, 1000); - - mBatteryChartView.onDetachedFromWindow(); - - verify(mMockAccessibilityManager) - .removeAccessibilityStateChangeListener(mBatteryChartView); - assertThat(mBatteryChartView.mHandler.hasCallbacks( - mBatteryChartView.mUpdateClickableStateRun)) - .isFalse(); - } - - @Test - public void onAccessibilityStateChanged_postUpdateStateRunnable() { - mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler); - mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true); - - verify(mBatteryChartView.mHandler) - .removeCallbacks(mBatteryChartView.mUpdateClickableStateRun); - verify(mBatteryChartView.mHandler) - .postDelayed(mBatteryChartView.mUpdateClickableStateRun, 500L); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoaderTest.java index 98a44de81b8..57178578169 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoaderTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoaderTest.java @@ -53,7 +53,7 @@ public final class BatteryHistoryLoaderTest { public void testLoadIBackground_returnsMapFromPowerFeatureProvider() { final Map> batteryHistoryMap = new HashMap<>(); doReturn(batteryHistoryMap).when(mFeatureFactory.powerUsageFeatureProvider) - .getBatteryHistory(mContext); + .getBatteryHistorySinceLastFullCharge(mContext); assertThat(mBatteryHistoryLoader.loadInBackground()) .isSameInstanceAs(batteryHistoryMap); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java index c1f981539c1..c9bac030ac8 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java @@ -26,6 +26,7 @@ import android.os.BatteryManager; import android.os.BatteryUsageStats; import android.os.LocaleList; import android.os.UserHandle; +import android.text.format.DateUtils; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; @@ -39,8 +40,8 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -173,7 +174,8 @@ public final class ConvertUtilsTest { public void getIndexedUsageMap_returnsExpectedResult() { // Creates the fake testing data. final int timeSlotSize = 2; - final long[] batteryHistoryKeys = new long[]{101L, 102L, 103L, 104L, 105L}; + final long[] batteryHistoryKeys = new long[]{generateTimestamp(0), generateTimestamp(1), + generateTimestamp(2), generateTimestamp(3), generateTimestamp(4)}; final Map> batteryHistoryMap = new HashMap<>(); final BatteryHistEntry fakeEntry = createBatteryHistEntry( @@ -270,11 +272,11 @@ public final class ConvertUtilsTest { for (int index = 0; index < remainingSize; index++) { batteryHistoryMap.put(105L + index + 1, new HashMap<>()); } - when(mPowerUsageFeatureProvider.getBatteryHistory(mContext)) + when(mPowerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext)) .thenReturn(batteryHistoryMap); final List batteryDiffEntryList = - BatteryChartPreferenceController.getBatteryLast24HrUsageData(mContext); + BatteryChartPreferenceController.getAppBatteryUsageData(mContext); assertThat(batteryDiffEntryList).isNotEmpty(); final BatteryDiffEntry resultEntry = batteryDiffEntryList.get(0); @@ -472,4 +474,9 @@ public final class ConvertUtilsTest { assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs); assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs); } + + private static Long generateTimestamp(int index) { + // "2021-04-23 07:00:00 UTC" + index hours + return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS; + } }