From 56ba1c09d9c6f41319a076e5ea734232f50724b0 Mon Sep 17 00:00:00 2001 From: mxyyiyi Date: Wed, 30 Aug 2023 17:38:32 +0800 Subject: [PATCH] App anomaly tips on PowerUsage App list Screenshots: [in bg - banner] https://screenshot.googleplex.com/MzLC6LfX93TkkYf [in bg - hints] https://screenshot.googleplex.com/9JLXNsRiVG8arAU [in fg - banner] https://screenshot.googleplex.com/9oYbwUkeeLbQX2t [in fg - hints] https://screenshot.googleplex.com/53DTTUCUnf8rsoE [apps anomaly highlight hint + settings anomaly banner] https://screenshot.googleplex.com/8NdS2VMrSzwv2DM Bug: 291689643 Bug: 291689623 Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bfd0f5859b6e5ffef4727ee562009f2050de7a58) Change-Id: Ic02db49cb3794ef134759d9dcec5f5ef32454a95 --- res/drawable/battery_hints_chip_bg.xml | 22 ++ res/drawable/battery_hints_chip_bg_ripple.xml | 21 ++ res/layout/anomaly_app_item_preference.xml | 67 +++++ res/values/arrays.xml | 37 ++- res/values/dimens.xml | 1 + res/values/strings.xml | 39 +++ .../AnomalyAppItemPreference.java | 60 +++++ .../batteryusage/AnomalyEventWrapper.java | 231 ++++++++++++++++++ .../BatteryChartPreferenceController.java | 16 +- .../BatteryTipsCardPreference.java | 2 - .../batteryusage/BatteryTipsController.java | 136 +++-------- .../BatteryUsageBreakdownController.java | 45 +++- .../batteryusage/PowerUsageAdvanced.java | 122 ++++++--- .../protos/power_anomaly_event.proto | 13 +- .../batteryusage/AnomalyEventWrapperTest.java | 63 +++++ .../BatteryTipsCardPreferenceTest.java | 46 +++- .../BatteryTipsControllerTest.java | 33 +-- .../BatteryUsageBreakdownControllerTest.java | 28 +-- .../batteryusage/ConvertUtilsTest.java | 1 + .../batteryusage/PowerUsageAdvancedTest.java | 127 ++++++---- .../settings/testutils/BatteryTestUtils.java | 9 +- 21 files changed, 856 insertions(+), 263 deletions(-) create mode 100644 res/drawable/battery_hints_chip_bg.xml create mode 100644 res/drawable/battery_hints_chip_bg_ripple.xml create mode 100644 res/layout/anomaly_app_item_preference.xml create mode 100644 src/com/android/settings/fuelgauge/batteryusage/AnomalyAppItemPreference.java create mode 100644 src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapperTest.java diff --git a/res/drawable/battery_hints_chip_bg.xml b/res/drawable/battery_hints_chip_bg.xml new file mode 100644 index 00000000000..e7d1d0fd178 --- /dev/null +++ b/res/drawable/battery_hints_chip_bg.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/res/drawable/battery_hints_chip_bg_ripple.xml b/res/drawable/battery_hints_chip_bg_ripple.xml new file mode 100644 index 00000000000..a8bd0b37042 --- /dev/null +++ b/res/drawable/battery_hints_chip_bg_ripple.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/res/layout/anomaly_app_item_preference.xml b/res/layout/anomaly_app_item_preference.xml new file mode 100644 index 00000000000..0a198496f1e --- /dev/null +++ b/res/layout/anomaly_app_item_preference.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 1723d177797..f45495433fc 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -1420,20 +1420,49 @@ color_battery_anomaly_yellow_selector - - - Turn on adaptive brightness to extend battery life - Reduce screen timeout to extend battery life + + + battery_tips_settings_summary_brightness + battery_tips_settings_summary_screen_timeout + battery_tips_apps_summary_always_high + battery_tips_apps_summary_higher_than_usual + battery_tips_apps_summary_always_high_in_background + battery_tips_apps_summary_higher_than_usual_in_background + battery_tips_apps_summary_always_high_in_foreground + battery_tips_apps_summary_higher_than_usual_in_foreground @string/battery_tips_card_action_button @string/battery_tips_card_action_button + @string/battery_tips_card_action_button_check + @string/battery_tips_card_action_button_check + @string/battery_tips_card_action_button_check + @string/battery_tips_card_action_button_check + @string/battery_tips_card_action_button_check + @string/battery_tips_card_action_button_check @string/battery_tips_card_dismiss_button @string/battery_tips_card_dismiss_button + @string/battery_tips_card_dismiss_button + @string/battery_tips_card_dismiss_button + @string/battery_tips_card_dismiss_button + @string/battery_tips_card_dismiss_button + @string/battery_tips_card_dismiss_button + @string/battery_tips_card_dismiss_button + + + + + + @string/battery_app_item_hint + @string/battery_app_item_hint + @string/battery_app_item_hint_in_bg + @string/battery_app_item_hint_in_bg + @string/battery_app_item_hint_in_fg + @string/battery_app_item_hint_in_fg diff --git a/res/values/dimens.xml b/res/values/dimens.xml index a8ad434459b..205e2a3d310 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -403,6 +403,7 @@ 4dp 24dp + 8dp 174dp diff --git a/res/values/strings.xml b/res/values/strings.xml index e4b68d032e1..eb8cfe7be2c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9821,12 +9821,51 @@ View Settings + + Check + Got it Is this message helpful? + + Battery tips warning icon + + + Turn on adaptive brightness to extend battery life + + + Reduce screen timeout to extend battery life + + + %1$s used more battery + + + %1$s used more battery than usual + + + %1$s used more battery while in the background + + + %1$s used more battery than usual while in the background + + + %1$s used more battery while in the foreground + + + %1$s used more battery than usual while in the foreground + + + High battery usage + + + High battery usage in the background + + + High battery usage in the foreground + Unrestricted diff --git a/src/com/android/settings/fuelgauge/batteryusage/AnomalyAppItemPreference.java b/src/com/android/settings/fuelgauge/batteryusage/AnomalyAppItemPreference.java new file mode 100644 index 00000000000..2f139ecaabc --- /dev/null +++ b/src/com/android/settings/fuelgauge/batteryusage/AnomalyAppItemPreference.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge.batteryusage; + +import android.content.Context; +import android.text.TextUtils; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.preference.PreferenceViewHolder; + +import com.android.settings.R; + +class AnomalyAppItemPreference extends PowerGaugePreference { + + private static final String TAG = "AnomalyAppItemPreference"; + + private CharSequence mAnomalyHintText; + + AnomalyAppItemPreference(Context context) { + super(context, /* attrs */ null); + setLayoutResource(R.layout.anomaly_app_item_preference); + } + + void setAnomalyHint(CharSequence anomalyHintText) { + if (!TextUtils.equals(mAnomalyHintText, anomalyHintText)) { + mAnomalyHintText = anomalyHintText; + notifyChanged(); + } + } + + @Override + public void onBindViewHolder(PreferenceViewHolder viewHolder) { + super.onBindViewHolder(viewHolder); + final LinearLayout warningChipView = + (LinearLayout) viewHolder.findViewById(R.id.warning_chip); + + if (!TextUtils.isEmpty(mAnomalyHintText)) { + ((TextView) warningChipView.findViewById(R.id.warning_info)).setText(mAnomalyHintText); + warningChipView.setVisibility(View.VISIBLE); + } else { + warningChipView.setVisibility(View.GONE); + } + } +} diff --git a/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java new file mode 100644 index 00000000000..d5354900b05 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapper.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge.batteryusage; + +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Pair; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.core.SubSettingLauncher; + +import java.util.function.Function; + +final class AnomalyEventWrapper { + private static final String TAG = "AnomalyEventWrapper"; + + private final Context mContext; + private final PowerAnomalyEvent mPowerAnomalyEvent; + + private final int mCardStyleId; + private final int mResourceIndex; + + private SubSettingLauncher mSubSettingLauncher = null; + private Pair mHighlightSlotPair = null; + private BatteryDiffEntry mRelatedBatteryDiffEntry = null; + + AnomalyEventWrapper(Context context, PowerAnomalyEvent powerAnomalyEvent) { + mContext = context; + mPowerAnomalyEvent = powerAnomalyEvent; + // Set basic battery tips card info + mCardStyleId = mPowerAnomalyEvent.getType().getNumber(); + mResourceIndex = mPowerAnomalyEvent.getKey().getNumber(); + } + + private T getInfo(Function warningBannerInfoSupplier, + Function warningItemInfoSupplier) { + if (warningBannerInfoSupplier != null && mPowerAnomalyEvent.hasWarningBannerInfo()) { + return warningBannerInfoSupplier.apply(mPowerAnomalyEvent.getWarningBannerInfo()); + } else if (warningItemInfoSupplier != null && mPowerAnomalyEvent.hasWarningItemInfo()) { + return warningItemInfoSupplier.apply(mPowerAnomalyEvent.getWarningItemInfo()); + } + return null; + } + + private int getResourceId(int resourceId, int resourceIndex, String defType) { + final String key = getStringFromArrayResource(resourceId, resourceIndex); + return TextUtils.isEmpty(key) ? 0 + : mContext.getResources().getIdentifier(key, defType, mContext.getPackageName()); + } + + private String getString(Function warningBannerInfoSupplier, + Function warningItemInfoSupplier, + int resourceId, int resourceIndex) { + final String string = getInfo(warningBannerInfoSupplier, warningItemInfoSupplier); + return (!TextUtils.isEmpty(string) || resourceId <= 0) ? string + : getStringFromArrayResource(resourceId, resourceIndex); + } + + private String getStringFromArrayResource(int resourceId, int resourceIndex) { + if (resourceId <= 0 || resourceIndex < 0) { + return null; + } + final String[] stringArray = mContext.getResources().getStringArray(resourceId); + return (resourceIndex >= 0 && resourceIndex < stringArray.length) + ? stringArray[resourceIndex] : null; + } + + void setRelatedBatteryDiffEntry(BatteryDiffEntry batteryDiffEntry) { + mRelatedBatteryDiffEntry = batteryDiffEntry; + } + + String getEventId() { + return mPowerAnomalyEvent.hasEventId() ? mPowerAnomalyEvent.getEventId() : null; + } + + int getIconResId() { + return getResourceId(R.array.battery_tips_card_icons, mCardStyleId, "drawable"); + } + + int getColorResId() { + return getResourceId(R.array.battery_tips_card_colors, mCardStyleId, "color"); + } + + String getTitleString() { + final String protoTitleString = getInfo(WarningBannerInfo::getTitleString, + WarningItemInfo::getTitleString); + if (!TextUtils.isEmpty(protoTitleString)) { + return protoTitleString; + } + final int titleFormatResId = getResourceId(R.array.power_anomaly_title_ids, + mResourceIndex, "string"); + if (mPowerAnomalyEvent.hasWarningBannerInfo()) { + return mContext.getString(titleFormatResId); + } else if (mPowerAnomalyEvent.hasWarningItemInfo() && mRelatedBatteryDiffEntry != null) { + final String appLabel = mRelatedBatteryDiffEntry.getAppLabel(); + return mContext.getString(titleFormatResId, appLabel); + } + return null; + } + + String getMainBtnString() { + return getString(WarningBannerInfo::getMainButtonString, + WarningItemInfo::getMainButtonString, + R.array.power_anomaly_main_btn_strings, mResourceIndex); + } + + String getDismissBtnString() { + return getString(WarningBannerInfo::getCancelButtonString, + WarningItemInfo::getCancelButtonString, + R.array.power_anomaly_dismiss_btn_strings, mResourceIndex); + } + + String getAnomalyHintString() { + return getStringFromArrayResource(R.array.power_anomaly_hint_messages, mResourceIndex); + } + + String getDismissRecordKey() { + return mPowerAnomalyEvent.getDismissRecordKey(); + } + + boolean hasAnomalyEntryKey() { + return getAnomalyEntryKey() != null; + } + + String getAnomalyEntryKey() { + return mPowerAnomalyEvent.hasWarningItemInfo() + && mPowerAnomalyEvent.getWarningItemInfo().hasItemKey() + ? mPowerAnomalyEvent.getWarningItemInfo().getItemKey() : null; + } + + boolean hasSubSettingLauncher() { + if (mSubSettingLauncher == null) { + mSubSettingLauncher = getSubSettingLauncher(); + } + return mSubSettingLauncher != null; + } + + SubSettingLauncher getSubSettingLauncher() { + if (mSubSettingLauncher != null) { + return mSubSettingLauncher; + } + final String destinationClassName = getInfo( + WarningBannerInfo::getMainButtonDestination, null); + if (!TextUtils.isEmpty(destinationClassName)) { + final Integer sourceMetricsCategory = getInfo( + WarningBannerInfo::getMainButtonSourceMetricsCategory, null); + final String preferenceHighlightKey = getInfo( + WarningBannerInfo::getMainButtonSourceHighlightKey, null); + Bundle arguments = Bundle.EMPTY; + if (!TextUtils.isEmpty(preferenceHighlightKey)) { + arguments = new Bundle(1); + arguments.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, + preferenceHighlightKey); + } + mSubSettingLauncher = new SubSettingLauncher(mContext) + .setDestination(destinationClassName) + .setSourceMetricsCategory(sourceMetricsCategory) + .setArguments(arguments); + } + return mSubSettingLauncher; + } + + boolean hasHighlightSlotPair(BatteryLevelData batteryLevelData) { + if (mHighlightSlotPair == null) { + mHighlightSlotPair = getHighlightSlotPair(batteryLevelData); + } + return mHighlightSlotPair != null; + } + + Pair getHighlightSlotPair(BatteryLevelData batteryLevelData) { + if (mHighlightSlotPair != null) { + return mHighlightSlotPair; + } + if (!mPowerAnomalyEvent.hasWarningItemInfo()) { + return null; + } + final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo(); + final Long startTimestamp = warningItemInfo.hasStartTimestamp() + ? warningItemInfo.getStartTimestamp() : null; + final Long endTimestamp = warningItemInfo.hasEndTimestamp() + ? warningItemInfo.getEndTimestamp() : null; + if (startTimestamp != null && endTimestamp != null) { + mHighlightSlotPair = batteryLevelData + .getIndexByTimestamps(startTimestamp, endTimestamp); + if (mHighlightSlotPair.first == BatteryChartViewModel.SELECTED_INDEX_INVALID + || mHighlightSlotPair.second == BatteryChartViewModel.SELECTED_INDEX_INVALID) { + // Drop invalid mHighlightSlotPair index + mHighlightSlotPair = null; + } + } + return mHighlightSlotPair; + } + + boolean updateTipsCardPreference(BatteryTipsCardPreference preference) { + final String titleString = getTitleString(); + if (TextUtils.isEmpty(titleString)) { + return false; + } + preference.setTitle(titleString); + preference.setIconResourceId(getIconResId()); + preference.setMainButtonStrokeColorResourceId(getColorResId()); + preference.setMainButtonLabel(getMainBtnString()); + preference.setDismissButtonLabel(getDismissBtnString()); + return true; + } + + boolean launchSubSetting() { + if (!hasSubSettingLauncher()) { + return false; + } + // Navigate to sub setting page + mSubSettingLauncher.launch(); + return true; + } +} diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index 75b7c08de8d..1ae3bef5159 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -221,14 +221,20 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll refreshUi(); } + boolean isHighlightSlotFocused() { + return (mDailyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID + && mDailyHighlightSlotIndex == mDailyChartIndex + && mHourlyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID + && mHourlyHighlightSlotIndex == mHourlyChartIndex); + } + void onHighlightSlotIndexUpdate(int dailyHighlightSlotIndex, int hourlyHighlightSlotIndex) { - if (mDailyHighlightSlotIndex == dailyHighlightSlotIndex - && mHourlyHighlightSlotIndex == hourlyHighlightSlotIndex) { - return; - } mDailyHighlightSlotIndex = dailyHighlightSlotIndex; mHourlyHighlightSlotIndex = hourlyHighlightSlotIndex; refreshUi(); + if (mOnSelectedIndexUpdatedListener != null) { + mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated(); + } } void selectHighlightSlotIndex() { @@ -405,7 +411,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll final String slotInformation = getSlotInformation(); return slotInformation == null ? mPrefContext.getString( - R.string.battery_usage_breakdown_title_since_last_full_charge) + R.string.battery_usage_breakdown_title_since_last_full_charge) : mPrefContext.getString( R.string.battery_usage_breakdown_title_for_slot, slotInformation); } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java index e5cff2007c6..2c799fada57 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java @@ -120,12 +120,10 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic public void onClick(View view) { final int viewId = view.getId(); if (viewId == R.id.main_button || viewId == R.id.tips_card) { - setVisible(false); if (mOnConfirmListener != null) { mOnConfirmListener.onConfirm(); } } else if (viewId == R.id.dismiss_button) { - setVisible(false); if (mOnRejectListener != null) { mOnRejectListener.onReject(); } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java index b3a3508bdd1..44b24214d04 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java @@ -18,21 +18,15 @@ package com.android.settings.fuelgauge.batteryusage; import android.app.settings.SettingsEnums; import android.content.Context; -import android.os.Bundle; import android.text.TextUtils; import androidx.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; -import com.android.settings.R; -import com.android.settings.SettingsActivity; import com.android.settings.core.BasePreferenceController; -import com.android.settings.core.SubSettingLauncher; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import java.util.function.Function; - /** Controls the update for battery tips card */ public class BatteryTipsController extends BasePreferenceController { @@ -59,6 +53,10 @@ public class BatteryTipsController extends BasePreferenceController { @VisibleForTesting BatteryTipsCardPreference mCardPreference; + @VisibleForTesting + AnomalyEventWrapper mAnomalyEventWrapper = null; + @VisibleForTesting + Boolean mIsAcceptable = false; public BatteryTipsController(Context context) { super(context, ROOT_PREFERENCE_KEY); @@ -85,132 +83,56 @@ public class BatteryTipsController extends BasePreferenceController { mOnAnomalyRejectListener = listener; } - private T getInfo(PowerAnomalyEvent powerAnomalyEvent, - Function warningBannerInfoSupplier, - Function warningItemInfoSupplier) { - if (warningBannerInfoSupplier != null && powerAnomalyEvent.hasWarningBannerInfo()) { - return warningBannerInfoSupplier.apply(powerAnomalyEvent.getWarningBannerInfo()); - } else if (warningItemInfoSupplier != null && powerAnomalyEvent.hasWarningItemInfo()) { - return warningItemInfoSupplier.apply(powerAnomalyEvent.getWarningItemInfo()); + void acceptTipsCard() { + if (mAnomalyEventWrapper == null || !mIsAcceptable) { + return; } - return null; - } - - private String getStringFromResource(int resourceId, int resourceIndex) { - if (resourceId < 0) { - return null; + // For anomaly events with same record key, dismissed until next time full charged. + final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey(); + if (!TextUtils.isEmpty(dismissRecordKey)) { + DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey); } - final String[] stringArray = mContext.getResources().getStringArray(resourceId); - return (resourceIndex >= 0 && resourceIndex < stringArray.length) - ? stringArray[resourceIndex] : null; + mCardPreference.setVisible(false); + mMetricsFeatureProvider.action( + mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, + mAnomalyEventWrapper.getEventId()); } - private int getResourceId(int resourceId, int resourceIndex, String defType) { - final String key = getStringFromResource(resourceId, resourceIndex); - return TextUtils.isEmpty(key) ? 0 - : mContext.getResources().getIdentifier(key, defType, mContext.getPackageName()); - } - - private String getString(PowerAnomalyEvent powerAnomalyEvent, - Function warningBannerInfoSupplier, - Function warningItemInfoSupplier, - int resourceId, int resourceIndex) { - String string = - getInfo(powerAnomalyEvent, warningBannerInfoSupplier, warningItemInfoSupplier); - return (!TextUtils.isEmpty(string) || resourceId < 0) ? string - : getStringFromResource(resourceId, resourceIndex); - } - - /** Generate a key string of current anomaly to record as dismissed in sharedPreferences. */ - public static String getDismissRecordKey(PowerAnomalyEvent event) { - if (!event.hasKey()) { - return null; - } - switch (event.getKey()){ - case KEY_APP: - return event.hasWarningItemInfo() - && event.getWarningItemInfo().hasDismissRecordKey() - ? event.getWarningItemInfo().getDismissRecordKey() : null; - default: - return event.getKey().name(); - } - } - - void handleBatteryTipsCardUpdated(PowerAnomalyEvent powerAnomalyEvent) { - if (powerAnomalyEvent == null) { + void handleBatteryTipsCardUpdated( + AnomalyEventWrapper anomalyEventWrapper, boolean isAcceptable) { + mAnomalyEventWrapper = anomalyEventWrapper; + mIsAcceptable = isAcceptable; + if (mAnomalyEventWrapper == null) { mCardPreference.setVisible(false); return; } - // Get card icon and color styles - final int cardStyleId = powerAnomalyEvent.getType().getNumber(); - final int iconResId = getResourceId( - R.array.battery_tips_card_icons, cardStyleId, "drawable"); - final int colorResId = getResourceId( - R.array.battery_tips_card_colors, cardStyleId, "color"); - // Get card preference strings and navigate fragment info - final String eventId = powerAnomalyEvent.hasEventId() - ? powerAnomalyEvent.getEventId() : null; - final PowerAnomalyKey powerAnomalyKey = powerAnomalyEvent.hasKey() - ? powerAnomalyEvent.getKey() : null; - final int resourceIndex = powerAnomalyKey != null ? powerAnomalyKey.getNumber() : -1; + final String eventId = mAnomalyEventWrapper.getEventId(); - final String titleString = getString(powerAnomalyEvent, WarningBannerInfo::getTitleString, - WarningItemInfo::getTitleString, R.array.power_anomaly_titles, resourceIndex); - if (titleString.isEmpty()) { + // Update card & buttons preference + if (!mAnomalyEventWrapper.updateTipsCardPreference(mCardPreference)) { mCardPreference.setVisible(false); return; } - final String mainBtnString = getString(powerAnomalyEvent, - WarningBannerInfo::getMainButtonString, WarningItemInfo::getMainButtonString, - R.array.power_anomaly_main_btn_strings, resourceIndex); - final String dismissBtnString = getString(powerAnomalyEvent, - WarningBannerInfo::getCancelButtonString, WarningItemInfo::getCancelButtonString, - R.array.power_anomaly_dismiss_btn_strings, resourceIndex); - - final String destinationClassName = getInfo(powerAnomalyEvent, - WarningBannerInfo::getMainButtonDestination, null); - final Integer sourceMetricsCategory = getInfo(powerAnomalyEvent, - WarningBannerInfo::getMainButtonSourceMetricsCategory, null); - final String preferenceHighlightKey = getInfo(powerAnomalyEvent, - WarningBannerInfo::getMainButtonSourceHighlightKey, null); - - // Update card preference and main button fragment launcher - mCardPreference.setTitle(titleString); - mCardPreference.setIconResourceId(iconResId); - mCardPreference.setMainButtonStrokeColorResourceId(colorResId); - mCardPreference.setMainButtonLabel(mainBtnString); - mCardPreference.setDismissButtonLabel(dismissBtnString); - // Set battery tips card listener mCardPreference.setOnConfirmListener(() -> { + mCardPreference.setVisible(false); if (mOnAnomalyConfirmListener != null) { mOnAnomalyConfirmListener.onAnomalyConfirm(); - } else if (!TextUtils.isEmpty(destinationClassName)) { - // Navigate to sub setting page - Bundle arguments = Bundle.EMPTY; - if (!TextUtils.isEmpty(preferenceHighlightKey)) { - arguments = new Bundle(1); - arguments.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, - preferenceHighlightKey); - } - new SubSettingLauncher(mContext) - .setDestination(destinationClassName) - .setSourceMetricsCategory(sourceMetricsCategory) - .setArguments(arguments) - .launch(); + } else if (mAnomalyEventWrapper.launchSubSetting()) { + mMetricsFeatureProvider.action( + mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId); } - mMetricsFeatureProvider.action( - mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId); }); mCardPreference.setOnRejectListener(() -> { + mCardPreference.setVisible(false); if (mOnAnomalyRejectListener != null) { mOnAnomalyRejectListener.onAnomalyReject(); } // For anomaly events with same record key, dismissed until next time full charged. - final String dismissRecordKey = getDismissRecordKey(powerAnomalyEvent); + final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey(); if (!TextUtils.isEmpty(dismissRecordKey)) { DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey); } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java index d51485a87d1..4db4d3be8bd 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; /** Controller for battery usage breakdown preference group. */ @@ -93,6 +94,14 @@ public class BatteryUsageBreakdownController extends BasePreferenceController BatteryDiffData mBatteryDiffData; @VisibleForTesting String mPercentLessThanThresholdText; + @VisibleForTesting + boolean mIsHighlightSlot; + @VisibleForTesting + String mAnomalyEventId; + @VisibleForTesting + String mAnomalyEntryKey; + @VisibleForTesting + String mAnomalyHintString; public BatteryUsageBreakdownController( Context context, Lifecycle lifecycle, SettingsActivity activity, @@ -137,6 +146,12 @@ public class BatteryUsageBreakdownController extends BasePreferenceController return false; } + private String getActionKey(String packageName) { + final String actionKey = TextUtils.isEmpty(packageName) + ? PACKAGE_NAME_NONE : packageName; + return mAnomalyEventId == null ? actionKey : actionKey + "|" + mAnomalyEventId; + } + @Override public boolean handlePreferenceTreeClick(Preference preference) { if (!(preference instanceof PowerGaugePreference)) { @@ -151,7 +166,7 @@ public class BatteryUsageBreakdownController extends BasePreferenceController ? SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM : SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM, /* pageId */ SettingsEnums.OPEN_BATTERY_USAGE, - TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName, + getActionKey(packageName), (int) Math.round(diffEntry.getPercentage())); Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s", diffEntry.getAppLabel(), diffEntry.getKey(), packageName)); @@ -211,9 +226,23 @@ public class BatteryUsageBreakdownController extends BasePreferenceController * used when showing the footer. */ void handleBatteryUsageUpdated( - BatteryDiffData slotUsageData, String slotTimestamp, boolean isAllUsageDataEmpty) { + BatteryDiffData slotUsageData, String slotTimestamp, + boolean isAllUsageDataEmpty, boolean isHighlightSlot, + Optional optionalAnomalyEventWrapper) { mBatteryDiffData = slotUsageData; mSlotTimestamp = slotTimestamp; + mIsHighlightSlot = isHighlightSlot; + + if (optionalAnomalyEventWrapper != null) { + final AnomalyEventWrapper anomalyEventWrapper = + optionalAnomalyEventWrapper.orElse(null); + mAnomalyEventId = anomalyEventWrapper != null + ? anomalyEventWrapper.getEventId() : null; + mAnomalyEntryKey = anomalyEventWrapper != null + ? anomalyEventWrapper.getAnomalyEntryKey() : null; + mAnomalyHintString = anomalyEventWrapper != null + ? anomalyEventWrapper.getAnomalyHintString() : null; + } showCategoryTitle(slotTimestamp); showSpinnerAndAppList(); @@ -278,15 +307,15 @@ public class BatteryUsageBreakdownController extends BasePreferenceController continue; } final String prefKey = entry.getKey(); - PowerGaugePreference pref = mAppListPreferenceGroup.findPreference(prefKey); + AnomalyAppItemPreference pref = mAppListPreferenceGroup.findPreference(prefKey); if (pref != null) { isAdded = true; } else { - pref = (PowerGaugePreference) mPreferenceCache.get(prefKey); + pref = (AnomalyAppItemPreference) mPreferenceCache.get(prefKey); } - // Creates new innstance if cached preference is not found. + // Creates new instance if cached preference is not found. if (pref == null) { - pref = new PowerGaugePreference(mPrefContext); + pref = new AnomalyAppItemPreference(mPrefContext); pref.setKey(prefKey); mPreferenceCache.put(prefKey, pref); } @@ -294,6 +323,10 @@ public class BatteryUsageBreakdownController extends BasePreferenceController pref.setTitle(appLabel); pref.setOrder(prefIndex); pref.setSingleLineTitle(true); + // Updates App item preference style + pref.setAnomalyHint(mIsHighlightSlot && mAnomalyEntryKey != null + && mAnomalyEntryKey.equals(entry.getKey()) + ? mAnomalyHintString : null); // Sets the BatteryDiffEntry to preference for launching detailed page. pref.setBatteryDiffEntry(entry); pref.setSelectable(entry.validForRestriction()); diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java index 283b742a2a9..fb83302164b 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java +++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java @@ -52,6 +52,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.function.Predicate; /** Advanced power usage. */ @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) @@ -92,9 +93,9 @@ public class PowerUsageAdvanced extends PowerUsageBase { @VisibleForTesting BatteryUsageBreakdownController mBatteryUsageBreakdownController; @VisibleForTesting - PowerAnomalyEvent mPowerAnomalyEvent; - @VisibleForTesting Optional mBatteryLevelData; + @VisibleForTesting + Optional mHighlightEventWrapper; @Override public void onCreate(Bundle icicle) { @@ -188,7 +189,7 @@ public class PowerUsageAdvanced extends PowerUsageBase { mIsChartDataLoaded = true; mBatteryLevelData = null; mBatteryUsageMap = null; - mPowerAnomalyEvent = null; + mHighlightEventWrapper = null; restartLoader(LoaderIndex.BATTERY_LEVEL_DATA_LOADER, bundle, mBatteryLevelDataLoaderCallbacks); } @@ -239,8 +240,13 @@ public class PowerUsageAdvanced extends PowerUsageBase { mScreenOnTimeController.handleSceenOnTimeUpdated( slotUsageData.getScreenOnTime(), slotInformation); } + // Hide card tips if the related highlight slot was clicked. + if (isAppsAnomalyEventFocused()) { + mBatteryTipsController.acceptTipsCard(); + } mBatteryUsageBreakdownController.handleBatteryUsageUpdated( - slotUsageData, slotInformation, isBatteryUsageMapNullOrEmpty()); + slotUsageData, slotInformation, isBatteryUsageMapNullOrEmpty(), + isAppsAnomalyEventFocused(), mHighlightEventWrapper); Log.d(TAG, String.format("Battery usage list shows in %d millis", System.currentTimeMillis() - mResumeTimestamp)); } @@ -261,49 +267,95 @@ public class PowerUsageAdvanced extends PowerUsageBase { return; } Log.d(TAG, "anomalyEventList = " + anomalyEventList); - final PowerAnomalyEvent displayEvent = - getHighestScoreAnomalyEvent(getContext(), anomalyEventList); - onDisplayAnomalyEventUpdated(displayEvent); + + final Set dismissedPowerAnomalyKeys = + DatabaseUtils.getDismissedPowerAnomalyKeys(getContext()); + Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys); + + // Choose an app anomaly event with highest score to show highlight slot + final PowerAnomalyEvent highlightEvent = + getAnomalyEvent(anomalyEventList, PowerAnomalyEvent::hasWarningItemInfo); + // Choose an event never dismissed to show as card. + // If the slot is already highlighted, the tips card should be the corresponding app + // or settings anomaly event. + final PowerAnomalyEvent tipsCardEvent = + getAnomalyEvent(anomalyEventList, + event -> !dismissedPowerAnomalyKeys.contains(event.getDismissRecordKey()) + && (event.equals(highlightEvent) || !event.hasWarningItemInfo())); + onDisplayAnomalyEventUpdated(tipsCardEvent, highlightEvent); } @VisibleForTesting - void onDisplayAnomalyEventUpdated(PowerAnomalyEvent event) { - mPowerAnomalyEvent = event; + void onDisplayAnomalyEventUpdated( + PowerAnomalyEvent tipsCardEvent, PowerAnomalyEvent highlightEvent) { if (mBatteryTipsController == null || mBatteryChartPreferenceController == null || mBatteryUsageBreakdownController == null) { return; } + final boolean isSameAnomalyEvent = (tipsCardEvent == highlightEvent); // Update battery tips card preference & behaviour mBatteryTipsController.setOnAnomalyConfirmListener(null); mBatteryTipsController.setOnAnomalyRejectListener(null); - mBatteryTipsController.handleBatteryTipsCardUpdated(mPowerAnomalyEvent); + final AnomalyEventWrapper tipsCardEventWrapper = (tipsCardEvent == null) ? null : + new AnomalyEventWrapper(getContext(), tipsCardEvent); + if (tipsCardEventWrapper != null) { + tipsCardEventWrapper.setRelatedBatteryDiffEntry( + findRelatedBatteryDiffEntry(tipsCardEventWrapper)); + } + mBatteryTipsController.handleBatteryTipsCardUpdated( + tipsCardEventWrapper, isSameAnomalyEvent); // Update highlight slot effect in battery chart view Pair highlightSlotIndexPair = Pair.create( BatteryChartViewModel.SELECTED_INDEX_INVALID, BatteryChartViewModel.SELECTED_INDEX_INVALID); - if (mPowerAnomalyEvent != null && mPowerAnomalyEvent.hasWarningItemInfo()) { - final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo(); - final Long startTimestamp = warningItemInfo.hasStartTimestamp() - ? warningItemInfo.getStartTimestamp() : null; - final Long endTimestamp = warningItemInfo.hasEndTimestamp() - ? warningItemInfo.getEndTimestamp() : null; - if (startTimestamp != null && endTimestamp != null) { - highlightSlotIndexPair = mBatteryLevelData.map(levelData -> - levelData.getIndexByTimestamps(startTimestamp, endTimestamp)) - .orElse(highlightSlotIndexPair); - mBatteryTipsController.setOnAnomalyConfirmListener( - mBatteryChartPreferenceController::selectHighlightSlotIndex); - mBatteryTipsController.setOnAnomalyRejectListener( - () -> onDisplayAnomalyEventUpdated(null)); + mHighlightEventWrapper = Optional.ofNullable(isSameAnomalyEvent ? tipsCardEventWrapper : + ((highlightEvent != null) + ? new AnomalyEventWrapper(getContext(), highlightEvent) : null)); + if (mBatteryLevelData != null && mBatteryLevelData.isPresent() + && mHighlightEventWrapper.isPresent() + && mHighlightEventWrapper.get().hasHighlightSlotPair(mBatteryLevelData.get())) { + highlightSlotIndexPair = mHighlightEventWrapper.get() + .getHighlightSlotPair(mBatteryLevelData.get()); + if (isSameAnomalyEvent) { + // For main button, focus on highlight slot when clicked + mBatteryTipsController.setOnAnomalyConfirmListener(() -> { + mBatteryChartPreferenceController.selectHighlightSlotIndex(); + mBatteryTipsController.acceptTipsCard(); + }); } } mBatteryChartPreferenceController.onHighlightSlotIndexUpdate( highlightSlotIndexPair.first, highlightSlotIndexPair.second); } + @VisibleForTesting + BatteryDiffEntry findRelatedBatteryDiffEntry(AnomalyEventWrapper eventWrapper) { + if (eventWrapper == null + || mBatteryLevelData == null || mBatteryLevelData.isEmpty() + || !eventWrapper.hasHighlightSlotPair(mBatteryLevelData.get()) + || !eventWrapper.hasAnomalyEntryKey() + || mBatteryUsageMap == null) { + return null; + } + final Pair highlightSlotIndexPair = + eventWrapper.getHighlightSlotPair(mBatteryLevelData.get()); + final BatteryDiffData relatedDiffData = mBatteryUsageMap + .get(highlightSlotIndexPair.first).get(highlightSlotIndexPair.second); + final String anomalyEntryKey = eventWrapper.getAnomalyEntryKey(); + if (relatedDiffData == null || anomalyEntryKey == null) { + return null; + } + for (BatteryDiffEntry entry : relatedDiffData.getAppDiffEntryList()) { + if (anomalyEntryKey.equals(entry.getKey())) { + return entry; + } + } + return null; + } + private void setBatteryChartPreferenceController() { if (mHistPref != null && mBatteryChartPreferenceController != null) { mHistPref.setChartPreferenceController(mBatteryChartPreferenceController); @@ -318,6 +370,11 @@ public class PowerUsageAdvanced extends PowerUsageBase { && allBatteryDiffData.getSystemDiffEntryList().isEmpty()); } + private boolean isAppsAnomalyEventFocused() { + return mBatteryChartPreferenceController != null + && mBatteryChartPreferenceController.isHighlightSlotFocused(); + } + private void logScreenUsageTime() { final BatteryDiffData allBatteryDiffData = getAllBatteryDiffData(mBatteryUsageMap); if (allBatteryDiffData == null) { @@ -338,25 +395,22 @@ public class PowerUsageAdvanced extends PowerUsageBase { } @VisibleForTesting - static PowerAnomalyEvent getHighestScoreAnomalyEvent( - Context context, PowerAnomalyEventList anomalyEventList) { + static PowerAnomalyEvent getAnomalyEvent( + PowerAnomalyEventList anomalyEventList, Predicate predicate) { if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) { return null; } - final Set dismissedPowerAnomalyKeys = - DatabaseUtils.getDismissedPowerAnomalyKeys(context); - Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys); - final PowerAnomalyEvent highestScoreEvent = anomalyEventList.getPowerAnomalyEventsList() + final PowerAnomalyEvent filterAnomalyEvent = anomalyEventList.getPowerAnomalyEventsList() .stream() - .filter(event -> !dismissedPowerAnomalyKeys.contains( - BatteryTipsController.getDismissRecordKey(event))) + .filter(predicate) .max(Comparator.comparing(PowerAnomalyEvent::getScore)) .orElse(null); - Log.d(TAG, "highestScoreAnomalyEvent = " + highestScoreEvent); - return highestScoreEvent; + Log.d(TAG, "filterAnomalyEvent = " + filterAnomalyEvent); + return filterAnomalyEvent; } + private static BatteryDiffData getAllBatteryDiffData( Map> batteryUsageMap) { return batteryUsageMap == null ? null : batteryUsageMap diff --git a/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto index 99df215f5aa..caa9c352cc3 100644 --- a/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto +++ b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto @@ -18,6 +18,7 @@ message PowerAnomalyEvent { WarningBannerInfo warning_banner_info = 6; WarningItemInfo warning_item_info = 7; } + optional string dismiss_record_key = 8; } // NOTE: Please DO NOT delete enum items or change enum values. Use [deprecated = true] instead. @@ -32,11 +33,16 @@ enum PowerAnomalyType{ // NOTE: Please DO NOT delete enum items or change enum values. Use [deprecated = true] instead. // The enum value will be used to decide pre-defined title and button labels. // -// Next id: 3 +// Next id: 8 enum PowerAnomalyKey{ KEY_BRIGHTNESS = 0; KEY_SCREEN_TIMEOUT = 1; - KEY_APP = 2; + KEY_APP_TOTAL_ALWAYS_HIGH = 2; + KEY_APP_TOTAL_HIGHER_THAN_USUAL = 3; + KEY_APP_BACKGROUND_ALWAYS_HIGH = 4; + KEY_APP_BACKGROUND_HIGHER_THAN_USUAL = 5; + KEY_APP_FOREGROUND_ALWAYS_HIGH = 6; + KEY_APP_FOREGROUND_HIGHER_THAN_USUAL = 7; } message WarningBannerInfo { @@ -60,6 +66,5 @@ message WarningItemInfo { optional string description_string = 5; optional string main_button_string = 6; optional string cancel_button_string = 7; - optional string dismiss_record_key = 8; - optional string item_key = 9; + optional string item_key = 8; } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapperTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapperTest.java new file mode 100644 index 00000000000..60e0af09309 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AnomalyEventWrapperTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 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.spy; + +import android.content.Context; + +import com.android.settings.testutils.BatteryTestUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.TimeZone; + +@RunWith(RobolectricTestRunner.class) +public class AnomalyEventWrapperTest { + private AnomalyEventWrapper mAnomalyEventWrapper; + private Context mContext; + + @Before + public void setUp() { + TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); + mContext = spy(RuntimeEnvironment.application); + } + + @Test + public void getDismissRecordKey_returnExpectedResult() { + mAnomalyEventWrapper = new AnomalyEventWrapper(mContext, + BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent()); + assertThat(mAnomalyEventWrapper.getDismissRecordKey()) + .isEqualTo("KEY_BRIGHTNESS"); + + mAnomalyEventWrapper = new AnomalyEventWrapper(mContext, + BatteryTestUtils.createScreenTimeoutAnomalyEvent()); + assertThat(mAnomalyEventWrapper.getDismissRecordKey()) + .isEqualTo("KEY_SCREEN_TIMEOUT"); + + mAnomalyEventWrapper = new AnomalyEventWrapper(mContext, + BatteryTestUtils.createAppAnomalyEvent()); + assertThat(mAnomalyEventWrapper.getDismissRecordKey()) + .isEqualTo("KEY_APP_1"); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java index 630ff45bf2f..63cb1b3c94b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -58,13 +59,14 @@ public final class BatteryTipsCardPreferenceTest { private BatteryTipsCardPreference mBatteryTipsCardPreference; private PowerUsageAdvanced mPowerUsageAdvanced; private BatteryTipsController mBatteryTipsController; + private BatteryChartPreferenceController mBatteryChartPreferenceController; @Mock private View mFakeView; @Mock - private BatteryChartPreferenceController mBatteryChartPreferenceController; - @Mock private BatteryUsageBreakdownController mBatteryUsageBreakdownController; + @Mock + private BatteryDiffEntry mFakeEntry; @Before public void setUp() { @@ -73,8 +75,13 @@ public final class BatteryTipsCardPreferenceTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /*attrs=*/ null); mBatteryTipsController = new BatteryTipsController(mContext); + mBatteryChartPreferenceController = + spy(new BatteryChartPreferenceController(mContext, null, null)); + mBatteryChartPreferenceController.mPrefContext = mContext; mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference; - mPowerUsageAdvanced = new PowerUsageAdvanced(); + + mPowerUsageAdvanced = spy(new PowerUsageAdvanced()); + doReturn(mContext).when(mPowerUsageAdvanced).getContext(); mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController; mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController; mPowerUsageAdvanced.mBatteryUsageBreakdownController = mBatteryUsageBreakdownController; @@ -82,6 +89,7 @@ public final class BatteryTipsCardPreferenceTest { 1694354400000L, 1, // 2023-09-10 22:00:00 1694361600000L, 2, // 2023-09-11 00:00:00 1694368800000L, 3))); // 2023-09-11 02:00:00 + doReturn("TestEntriesKey").when(mFakeEntry).getKey(); } @Test @@ -99,7 +107,8 @@ public final class BatteryTipsCardPreferenceTest { when(mFakeView.getId()).thenReturn(R.id.main_button); doNothing().when(mContext).startActivity(captor.capture()); - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(adaptiveBrightnessAnomaly); + mPowerUsageAdvanced.onDisplayAnomalyEventUpdated( + adaptiveBrightnessAnomaly, adaptiveBrightnessAnomaly); mBatteryTipsCardPreference.onClick(mFakeView); assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); @@ -109,25 +118,30 @@ public final class BatteryTipsCardPreferenceTest { .isEqualTo(DisplaySettings.class.getName()); assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, -1)) .isEqualTo(SettingsEnums.DISPLAY); + verify(mFeatureFactory.metricsFeatureProvider).action( + mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "BrightnessAnomaly"); verify(mFeatureFactory.metricsFeatureProvider).action( mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "BrightnessAnomaly"); } @Test - public void onClick_dismissBtn_cardDismissAndLogged() { + public void onClick_dismissBtnOfSettingsAnomaly_cardDismissAndLogged() { final PowerAnomalyEvent screenTimeoutAnomaly = BatteryTestUtils.createScreenTimeoutAnomalyEvent(); DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFakeView.getId()).thenReturn(R.id.dismiss_button); - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(screenTimeoutAnomaly); + mPowerUsageAdvanced.onDisplayAnomalyEventUpdated( + screenTimeoutAnomaly, screenTimeoutAnomaly); mBatteryTipsCardPreference.onClick(mFakeView); assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)).hasSize(1); assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)) .contains(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name()); + verify(mFeatureFactory.metricsFeatureProvider).action( + mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "ScreenTimeoutAnomaly"); verify(mFeatureFactory.metricsFeatureProvider).action( mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "ScreenTimeoutAnomaly"); } @@ -137,30 +151,40 @@ public final class BatteryTipsCardPreferenceTest { final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent(); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFakeView.getId()).thenReturn(R.id.main_button); + doNothing().when(mBatteryChartPreferenceController).selectHighlightSlotIndex(); + when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry); - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly); + mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly); mBatteryTipsCardPreference.onClick(mFakeView); assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); verify(mContext, never()).startActivity(any(Intent.class)); + verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate( + eq(1), eq(0)); verify(mBatteryChartPreferenceController).selectHighlightSlotIndex(); + verify(mFeatureFactory.metricsFeatureProvider).action( + mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "AppAnomaly"); verify(mFeatureFactory.metricsFeatureProvider).action( mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "AppAnomaly"); } @Test - public void onClick_dismissBtnOfAppsAnomaly_removeHighlightSlotIndex() { + public void onClick_dismissBtnOfAppsAnomaly_keepHighlightSlotIndex() { final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent(); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFakeView.getId()).thenReturn(R.id.dismiss_button); + when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry); - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly); + mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly); mBatteryTipsCardPreference.onClick(mFakeView); assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); + verify(mContext, never()).startActivity(any(Intent.class)); verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate( - eq(BatteryChartViewModel.SELECTED_INDEX_INVALID), - eq(BatteryChartViewModel.SELECTED_INDEX_INVALID)); + eq(1), eq(0)); + verify(mBatteryChartPreferenceController, never()).selectHighlightSlotIndex(); + verify(mFeatureFactory.metricsFeatureProvider).action( + mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "AppAnomaly"); verify(mFeatureFactory.metricsFeatureProvider).action( mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "AppAnomaly"); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java index 913c00a1fb6..b8afe984950 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java @@ -16,8 +16,6 @@ package com.android.settings.fuelgauge.batteryusage; -import static com.google.common.truth.Truth.assertThat; - import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -70,30 +68,18 @@ public final class BatteryTipsControllerTest { @Test public void handleBatteryTipsCardUpdated_null_hidePreference() { - mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null); + mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null, false); verify(mBatteryTipsCardPreference).setVisible(false); } - @Test - public void getDismissRecordKey_returnExpectedResult() { - assertThat(BatteryTipsController.getDismissRecordKey( - BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent())) - .isEqualTo("KEY_BRIGHTNESS"); - assertThat(BatteryTipsController.getDismissRecordKey( - BatteryTestUtils.createScreenTimeoutAnomalyEvent())) - .isEqualTo("KEY_SCREEN_TIMEOUT"); - assertThat(BatteryTipsController.getDismissRecordKey( - BatteryTestUtils.createAppAnomalyEvent())) - .isEqualTo("KEY_APP_1"); - } - @Test public void handleBatteryTipsCardUpdated_adaptiveBrightnessAnomaly_showAnomaly() { PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - mBatteryTipsController.handleBatteryTipsCardUpdated(event); + mBatteryTipsController.handleBatteryTipsCardUpdated( + new AnomalyEventWrapper(mContext, event), false); // Check pre-defined string verify(mBatteryTipsCardPreference).setTitle( @@ -114,7 +100,8 @@ public final class BatteryTipsControllerTest { PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent(); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - mBatteryTipsController.handleBatteryTipsCardUpdated(event); + mBatteryTipsController.handleBatteryTipsCardUpdated( + new AnomalyEventWrapper(mContext, event), false); verify(mBatteryTipsCardPreference).setTitle("Reduce screen timeout to extend battery life"); verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb); @@ -139,7 +126,8 @@ public final class BatteryTipsControllerTest { .build(); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - mBatteryTipsController.handleBatteryTipsCardUpdated(event); + mBatteryTipsController.handleBatteryTipsCardUpdated( + new AnomalyEventWrapper(mContext, event), false); verify(mBatteryTipsCardPreference).setTitle(testTitle); verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb); @@ -157,10 +145,13 @@ public final class BatteryTipsControllerTest { PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent(); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); - mBatteryTipsController.handleBatteryTipsCardUpdated(event); + AnomalyEventWrapper eventWrapper = new AnomalyEventWrapper(mContext, event); + eventWrapper.setRelatedBatteryDiffEntry( + new BatteryDiffEntry(mContext, "", "Chrome", 0)); + mBatteryTipsController.handleBatteryTipsCardUpdated(eventWrapper, false); verify(mBatteryTipsCardPreference).setTitle( - "Chrome used more battery than usual in foreground"); + "Chrome used more battery than usual"); verify(mBatteryTipsCardPreference).setIconResourceId( R.drawable.ic_battery_tips_warning_icon); verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId( diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java index d89c06b1150..a721ad47f15 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java @@ -69,7 +69,7 @@ public final class BatteryUsageBreakdownControllerTest { @Mock private BatteryHistEntry mBatteryHistEntry; @Mock - private PowerGaugePreference mPowerGaugePreference; + private AnomalyAppItemPreference mAnomalyAppItemPreference; private Context mContext; private FakeFeatureFactory mFeatureFactory; @@ -123,13 +123,14 @@ public final class BatteryUsageBreakdownControllerTest { BatteryDiffEntry.sResourceCache.put( "fakeBatteryDiffEntryKey", new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1)); + doReturn(mAnomalyAppItemPreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY); } @Test public void onDestroy_clearPreferenceCacheAndPreferenceGroupRemoveAll() { // Ensures the testing environment is correct. mBatteryUsageBreakdownController.mPreferenceCache.put( - PREF_KEY, mPowerGaugePreference); + PREF_KEY, mAnomalyAppItemPreference); assertThat(mBatteryUsageBreakdownController.mPreferenceCache).hasSize(1); mBatteryUsageBreakdownController.onDestroy(); @@ -178,7 +179,6 @@ public final class BatteryUsageBreakdownControllerTest { doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel(); doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey(); - doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY); mBatteryUsageBreakdownController.addAllPreferences(); @@ -188,27 +188,25 @@ public final class BatteryUsageBreakdownControllerTest { @Test public void removeAndCacheAllUnusedPreferences_removePref_buildCacheAndRemoveAllPreference() { doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); - doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0); + doReturn(mAnomalyAppItemPreference).when(mAppListPreferenceGroup).getPreference(0); doReturn(PREF_KEY2).when(mBatteryHistEntry).getKey(); - doReturn(PREF_KEY).when(mPowerGaugePreference).getKey(); - doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY); + doReturn(PREF_KEY).when(mAnomalyAppItemPreference).getKey(); // Ensures the testing data is correct. assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty(); mBatteryUsageBreakdownController.removeAndCacheAllUnusedPreferences(); assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY)) - .isEqualTo(mPowerGaugePreference); - verify(mAppListPreferenceGroup).removePreference(mPowerGaugePreference); + .isEqualTo(mAnomalyAppItemPreference); + verify(mAppListPreferenceGroup).removePreference(mAnomalyAppItemPreference); } @Test public void removeAndCacheAllUnusedPreferences_keepPref_KeepAllPreference() { doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); - doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0); + doReturn(mAnomalyAppItemPreference).when(mAppListPreferenceGroup).getPreference(0); doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey(); - doReturn(PREF_KEY).when(mPowerGaugePreference).getKey(); - doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY); + doReturn(PREF_KEY).when(mAnomalyAppItemPreference).getKey(); // Ensures the testing data is correct. assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty(); @@ -232,10 +230,10 @@ public final class BatteryUsageBreakdownControllerTest { @Test public void handlePreferenceTreeClick_forAppEntry_returnTrue() { mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY; - doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); + doReturn(mBatteryDiffEntry).when(mAnomalyAppItemPreference).getBatteryDiffEntry(); assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick( - mPowerGaugePreference)).isTrue(); + mAnomalyAppItemPreference)).isTrue(); verify(mMetricsFeatureProvider) .action( SettingsEnums.OPEN_BATTERY_USAGE, @@ -248,10 +246,10 @@ public final class BatteryUsageBreakdownControllerTest { @Test public void handlePreferenceTreeClick_forSystemEntry_returnTrue() { mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_UID_BATTERY; - doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); + doReturn(mBatteryDiffEntry).when(mAnomalyAppItemPreference).getBatteryDiffEntry(); assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick( - mPowerGaugePreference)).isTrue(); + mAnomalyAppItemPreference)).isTrue(); verify(mMetricsFeatureProvider) .action( SettingsEnums.OPEN_BATTERY_USAGE, 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 f06dc634005..cd594d32eb4 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java @@ -72,6 +72,7 @@ public final class ConvertUtilsTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); mContext = spy(RuntimeEnvironment.application); ConvertUtils.sUsageSource = ConvertUtils.EMPTY_USAGE_SOURCE; when(mContext.getPackageManager()).thenReturn(mMockPackageManager); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java index 953c2d4ef39..9753bd28e1c 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java @@ -20,8 +20,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; @@ -41,7 +41,9 @@ import org.robolectric.annotation.Config; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.TimeZone; +import java.util.function.Predicate; @RunWith(RobolectricTestRunner.class) @Config(shadows = ShadowDashboardFragment.class) @@ -50,6 +52,9 @@ public final class PowerUsageAdvancedTest { private Context mContext; private PowerUsageAdvanced mPowerUsageAdvanced; + private Predicate mCardFilterPredicate; + private Predicate mSlotFilterPredicate; + @Mock private BatteryTipsController mBatteryTipsController; @Mock @@ -65,7 +70,7 @@ public final class PowerUsageAdvancedTest { TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); mContext = spy(RuntimeEnvironment.application); - mPowerUsageAdvanced = new PowerUsageAdvanced(); + mPowerUsageAdvanced = spy(new PowerUsageAdvanced()); mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController; mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController; mPowerUsageAdvanced.mScreenOnTimeController = mScreenOnTimeController; @@ -74,43 +79,63 @@ public final class PowerUsageAdvancedTest { 1694354400000L, 1, // 2023-09-10 22:00:00 1694361600000L, 2, // 2023-09-11 00:00:00 1694368800000L, 3))); // 2023-09-11 02:00:00 + doReturn(mContext).when(mPowerUsageAdvanced).getContext(); + mSlotFilterPredicate = PowerAnomalyEvent::hasWarningItemInfo; } @Test - public void getHighestScoreAnomalyEvent_withEmptyOrNullList_getNull() { - assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, null)).isNull(); - assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent( - mContext, BatteryTestUtils.createEmptyPowerAnomalyEventList())).isNull(); + public void getFilterAnomalyEvent_withEmptyOrNullList_getNull() { + prepareCardFilterPredicate(null); + assertThat(PowerUsageAdvanced + .getAnomalyEvent(null, mCardFilterPredicate)).isNull(); + assertThat(PowerUsageAdvanced + .getAnomalyEvent(null, mSlotFilterPredicate)).isNull(); + assertThat(PowerUsageAdvanced.getAnomalyEvent( + BatteryTestUtils.createEmptyPowerAnomalyEventList(), mCardFilterPredicate)) + .isNull(); + assertThat(PowerUsageAdvanced.getAnomalyEvent( + BatteryTestUtils.createEmptyPowerAnomalyEventList(), mSlotFilterPredicate)) + .isNull(); } @Test - public void getHighestScoreAnomalyEvent_withoutDismissed_getHighestScoreEvent() { + public void getFilterAnomalyEvent_withoutDismissed_getHighestScoreEvent() { final PowerAnomalyEventList powerAnomalyEventList = BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); - final PowerAnomalyEvent highestScoreEvent = - PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList); + final PowerAnomalyEvent slotEvent = + PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList, + mSlotFilterPredicate); + prepareCardFilterPredicate(slotEvent); + final PowerAnomalyEvent cardEvent = + PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList, + mCardFilterPredicate); - assertThat(highestScoreEvent) - .isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent()); + assertThat(cardEvent).isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent()); + assertThat(slotEvent).isNull(); } @Test - public void getHighestScoreAnomalyEvent_withBrightnessDismissed_getScreenTimeout() { + public void getFilterAnomalyEvent_withBrightnessDismissed_getScreenTimeout() { final PowerAnomalyEventList powerAnomalyEventList = BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, PowerAnomalyKey.KEY_BRIGHTNESS.name()); - final PowerAnomalyEvent highestScoreEvent = - PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList); + final PowerAnomalyEvent slotEvent = + PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList, + mSlotFilterPredicate); + prepareCardFilterPredicate(slotEvent); + final PowerAnomalyEvent cardEvent = + PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList, + mCardFilterPredicate); - assertThat(highestScoreEvent) - .isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent()); + assertThat(cardEvent).isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent()); + assertThat(slotEvent).isNull(); } @Test - public void getHighestScoreAnomalyEvent_withAllDismissed_getNull() { + public void getFilterAnomalyEvent_withAllDismissed_getNull() { final PowerAnomalyEventList powerAnomalyEventList = BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); @@ -118,20 +143,26 @@ public final class PowerUsageAdvancedTest { DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, key.name()); } - final PowerAnomalyEvent highestScoreEvent = - PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList); + final PowerAnomalyEvent slotEvent = + PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList, + mSlotFilterPredicate); + prepareCardFilterPredicate(slotEvent); + final PowerAnomalyEvent cardEvent = + PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList, + mCardFilterPredicate); - assertThat(highestScoreEvent).isNull(); + assertThat(cardEvent).isNull(); + assertThat(slotEvent).isNull(); } @Test public void onDisplayAnomalyEventUpdated_withSettingsAnomalyEvent_skipHighlightSlotEffect() { final PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(); - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event); + mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event, event); - assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isEqualTo(event); - verify(mBatteryTipsController).handleBatteryTipsCardUpdated(eq(event)); + assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId()) + .isEqualTo(event.getEventId()); verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyConfirmListener(isNull()); verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyRejectListener(isNull()); verify(mPowerUsageAdvanced.mBatteryChartPreferenceController).onHighlightSlotIndexUpdate( @@ -140,46 +171,44 @@ public final class PowerUsageAdvancedTest { } @Test - public void onDisplayAnomalyEventUpdated_withAppAnomalyEvent_setHighlightSlotEffect() { + public void onDisplayAnomalyEventUpdated_onlyAppAnomalyEvent_setHighlightSlotEffect() { final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent(); - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event); + mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event, event); - assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isEqualTo(event); - verify(mBatteryTipsController).handleBatteryTipsCardUpdated(eq(event)); + assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId()) + .isEqualTo(event.getEventId()); verify(mBatteryTipsController).setOnAnomalyConfirmListener(isNull()); verify(mBatteryTipsController).setOnAnomalyRejectListener(isNull()); - - assertThat(event.getWarningItemInfo().hasStartTimestamp()).isTrue(); - assertThat(event.getWarningItemInfo().hasEndTimestamp()).isTrue(); assertThat(mPowerUsageAdvanced.mBatteryLevelData.get().getIndexByTimestamps( event.getWarningItemInfo().getStartTimestamp(), event.getWarningItemInfo().getEndTimestamp() )).isEqualTo(Pair.create(1, 0)); verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0)); verify(mBatteryTipsController).setOnAnomalyConfirmListener(notNull()); - verify(mBatteryTipsController).setOnAnomalyRejectListener(notNull()); } @Test - public void onDisplayAnomalyEventUpdated_withNull_removeHighlightSlotEffect() { - final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent(); + public void onDisplayAnomalyEventUpdated_withSettingsCardAndAppsSlotEvent_showExpected() { + final PowerAnomalyEvent settingsEvent = + BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(); + final PowerAnomalyEvent appsEvent = + BatteryTestUtils.createAppAnomalyEvent(); - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event); - mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(null); + mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(settingsEvent, appsEvent); - assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isNull(); - verify(mBatteryTipsController, times(2)) - .setOnAnomalyConfirmListener(isNull()); - verify(mBatteryTipsController, times(2)) - .setOnAnomalyRejectListener(isNull()); - verify(mBatteryTipsController).setOnAnomalyConfirmListener(notNull()); - verify(mBatteryTipsController).setOnAnomalyRejectListener(notNull()); - - verify(mBatteryChartPreferenceController) - .onHighlightSlotIndexUpdate(eq(1), eq(0)); - verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate( - eq(BatteryChartViewModel.SELECTED_INDEX_INVALID), - eq(BatteryChartViewModel.SELECTED_INDEX_INVALID)); + assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId()) + .isEqualTo(appsEvent.getEventId()); + verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0)); + verify(mBatteryTipsController).setOnAnomalyConfirmListener(isNull()); + verify(mBatteryTipsController).setOnAnomalyRejectListener(isNull()); } -} + + private void prepareCardFilterPredicate(PowerAnomalyEvent slotEvent) { + final Set dismissedPowerAnomalyKeys = + DatabaseUtils.getDismissedPowerAnomalyKeys(mContext); + mCardFilterPredicate = event -> !dismissedPowerAnomalyKeys.contains( + event.getDismissRecordKey()) + && (event.equals(slotEvent) || !event.hasWarningItemInfo()); + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java index 10355605747..e98ea1b0278 100644 --- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java @@ -247,6 +247,7 @@ public class BatteryTestUtils { .setEventId("BrightnessAnomaly") .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER) .setKey(PowerAnomalyKey.KEY_BRIGHTNESS) + .setDismissRecordKey(PowerAnomalyKey.KEY_BRIGHTNESS.name()) .setScore(1.2f) .setWarningBannerInfo(WarningBannerInfo.newBuilder() .setMainButtonDestination(DisplaySettings.class.getName()) @@ -264,6 +265,7 @@ public class BatteryTestUtils { .setEventId("ScreenTimeoutAnomaly") .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER) .setKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT) + .setDismissRecordKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name()) .setScore(1.1f) .setWarningBannerInfo(WarningBannerInfo.newBuilder() .setMainButtonDestination(ScreenTimeoutSettings.class.getName()) @@ -280,15 +282,12 @@ public class BatteryTestUtils { return PowerAnomalyEvent.newBuilder() .setEventId("AppAnomaly") .setType(PowerAnomalyType.TYPE_APPS_ITEM) - .setKey(PowerAnomalyKey.KEY_APP) + .setKey(PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL) + .setDismissRecordKey("KEY_APP_1") .setScore(2.0f) .setWarningItemInfo(WarningItemInfo.newBuilder() - .setDismissRecordKey("KEY_APP_1") .setStartTimestamp(1694361600000L) // 2023-09-11 00:00:00 .setEndTimestamp(1694368800000L) // 2023-09-11 02:00:00 - .setTitleString("Chrome used more battery than usual in foreground") - .setMainButtonString("Check") - .setCancelButtonString("Got it") .build()) .build(); }