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
This commit is contained in:
mxyyiyi
2023-08-30 17:38:32 +08:00
committed by Xinyi Mao
parent 71235aa403
commit 56ba1c09d9
21 changed files with 856 additions and 263 deletions

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/settingslib_dialog_background" />
<corners android:radius="@dimen/battery_hints_chip_corner_radius" />
</shape>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:drawable="@drawable/battery_hints_chip_bg"/>
</ripple>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/preference_app"/>
<LinearLayout
android:id="@+id/warning_chip"
android:visibility="gone"
android:clickable="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<Space
android:layout_width="@dimen/secondary_app_icon_size"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:layout_marginStart="16dp"
android:background="@drawable/battery_hints_chip_bg_ripple">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center_vertical|start"
android:contentDescription="@string/battery_hints_warning_icon_a11y"
android:src="@drawable/ic_battery_tips_warning_icon" />
<TextView
android:id="@+id/warning_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
android:layout_gravity="center_vertical|start"
android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -1420,20 +1420,49 @@
<item>color_battery_anomaly_yellow_selector</item> <item>color_battery_anomaly_yellow_selector</item>
</string-array> </string-array>
<!-- The following 3 arrays are for power anomaly tips card. Please keep them the same size. --> <!-- The following 4 arrays are for power anomaly tips card. Please keep them the same size. -->
<string-array name="power_anomaly_titles"> <string-array name="power_anomaly_title_ids" translatable="false">
<item>Turn on adaptive brightness to extend battery life</item> <item>battery_tips_settings_summary_brightness</item>
<item>Reduce screen timeout to extend battery life</item> <item>battery_tips_settings_summary_screen_timeout</item>
<item>battery_tips_apps_summary_always_high</item>
<item>battery_tips_apps_summary_higher_than_usual</item>
<item>battery_tips_apps_summary_always_high_in_background</item>
<item>battery_tips_apps_summary_higher_than_usual_in_background</item>
<item>battery_tips_apps_summary_always_high_in_foreground</item>
<item>battery_tips_apps_summary_higher_than_usual_in_foreground</item>
</string-array> </string-array>
<string-array name="power_anomaly_main_btn_strings" translatable="false"> <string-array name="power_anomaly_main_btn_strings" translatable="false">
<item>@string/battery_tips_card_action_button</item> <item>@string/battery_tips_card_action_button</item>
<item>@string/battery_tips_card_action_button</item> <item>@string/battery_tips_card_action_button</item>
<item>@string/battery_tips_card_action_button_check</item>
<item>@string/battery_tips_card_action_button_check</item>
<item>@string/battery_tips_card_action_button_check</item>
<item>@string/battery_tips_card_action_button_check</item>
<item>@string/battery_tips_card_action_button_check</item>
<item>@string/battery_tips_card_action_button_check</item>
</string-array> </string-array>
<string-array name="power_anomaly_dismiss_btn_strings" translatable="false"> <string-array name="power_anomaly_dismiss_btn_strings" translatable="false">
<item>@string/battery_tips_card_dismiss_button</item> <item>@string/battery_tips_card_dismiss_button</item>
<item>@string/battery_tips_card_dismiss_button</item> <item>@string/battery_tips_card_dismiss_button</item>
<item>@string/battery_tips_card_dismiss_button</item>
<item>@string/battery_tips_card_dismiss_button</item>
<item>@string/battery_tips_card_dismiss_button</item>
<item>@string/battery_tips_card_dismiss_button</item>
<item>@string/battery_tips_card_dismiss_button</item>
<item>@string/battery_tips_card_dismiss_button</item>
</string-array>
<string-array name="power_anomaly_hint_messages" translatable="false">
<item></item>
<item></item>
<item>@string/battery_app_item_hint</item>
<item>@string/battery_app_item_hint</item>
<item>@string/battery_app_item_hint_in_bg</item>
<item>@string/battery_app_item_hint_in_bg</item>
<item>@string/battery_app_item_hint_in_fg</item>
<item>@string/battery_app_item_hint_in_fg</item>
</string-array> </string-array>
<!-- A list of not supporting Terms of Address. [DO NOT TRANSLATE] --> <!-- A list of not supporting Terms of Address. [DO NOT TRANSLATE] -->

View File

@@ -403,6 +403,7 @@
<!-- Battery tips card view component --> <!-- Battery tips card view component -->
<dimen name="battery_tips_card_corner_radius_small">4dp</dimen> <dimen name="battery_tips_card_corner_radius_small">4dp</dimen>
<dimen name="battery_tips_card_corner_radius_normal">24dp</dimen> <dimen name="battery_tips_card_corner_radius_normal">24dp</dimen>
<dimen name="battery_hints_chip_corner_radius">8dp</dimen>
<!-- Dimensions for Dream settings cards --> <!-- Dimensions for Dream settings cards -->
<dimen name="dream_item_min_column_width">174dp</dimen> <dimen name="dream_item_min_column_width">174dp</dimen>

View File

@@ -9821,12 +9821,51 @@
<!-- Label of action button in battery tips card [CHAR LIMIT=50] --> <!-- Label of action button in battery tips card [CHAR LIMIT=50] -->
<string name="battery_tips_card_action_button">View Settings</string> <string name="battery_tips_card_action_button">View Settings</string>
<!-- Label of action button in battery tips card [CHAR LIMIT=50] -->
<string name="battery_tips_card_action_button_check">Check</string>
<!-- Label of dismiss button in battery tips card [CHAR LIMIT=50] --> <!-- Label of dismiss button in battery tips card [CHAR LIMIT=50] -->
<string name="battery_tips_card_dismiss_button">Got it</string> <string name="battery_tips_card_dismiss_button">Got it</string>
<!-- Feedback card message in battery tips card [CHAR LIMIT=NONE] --> <!-- Feedback card message in battery tips card [CHAR LIMIT=NONE] -->
<string name="battery_tips_card_feedback_info">Is this message helpful?</string> <string name="battery_tips_card_feedback_info">Is this message helpful?</string>
<!-- Content description for battery hints warning icon of app anomaly [CHAR LIMIT=NONE] -->
<string name="battery_hints_warning_icon_a11y">Battery tips warning icon</string>
<!-- Summary of settings anomaly for adaptive brightness [CHAR LIMIT=NONE] -->
<string name="battery_tips_settings_summary_brightness">Turn on adaptive brightness to extend battery life</string>
<!-- Summary of settings anomaly for screen timeout [CHAR LIMIT=NONE] -->
<string name="battery_tips_settings_summary_screen_timeout">Reduce screen timeout to extend battery life</string>
<!-- Summary of apps anomaly for always high [CHAR LIMIT=NONE] -->
<string name="battery_tips_apps_summary_always_high"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery</string>
<!-- Summary of apps anomaly for higher than usual [CHAR LIMIT=NONE] -->
<string name="battery_tips_apps_summary_higher_than_usual"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery than usual</string>
<!-- Summary of apps anomaly for always high in background [CHAR LIMIT=NONE] -->
<string name="battery_tips_apps_summary_always_high_in_background"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery while in the background</string>
<!-- Summary of apps anomaly for higher than usual in background [CHAR LIMIT=NONE] -->
<string name="battery_tips_apps_summary_higher_than_usual_in_background"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery than usual while in the background</string>
<!-- Summary of apps anomaly for always high in foreground [CHAR LIMIT=NONE] -->
<string name="battery_tips_apps_summary_always_high_in_foreground"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery while in the foreground</string>
<!-- Summary of apps anomaly for higher than usual in foreground [CHAR LIMIT=NONE] -->
<string name="battery_tips_apps_summary_higher_than_usual_in_foreground"><xliff:g id="app_label" example="Pokemon Go">%1$s</xliff:g> used more battery than usual while in the foreground</string>
<!-- Label of hint for apps anomaly in battery usage [CHAR LIMIT=NONE] -->
<string name="battery_app_item_hint">High battery usage</string>
<!-- Label of hint for apps background anomaly in battery usage [CHAR LIMIT=NONE] -->
<string name="battery_app_item_hint_in_bg">High battery usage in the background</string>
<!-- Label of hint for apps foreground anomaly in battery usage [CHAR LIMIT=NONE] -->
<string name="battery_app_item_hint_in_fg">High battery usage in the foreground</string>
<!-- Filter title for battery unrestricted[CHAR_LIMIT=50]--> <!-- Filter title for battery unrestricted[CHAR_LIMIT=50]-->
<string name="filter_battery_unrestricted_title">Unrestricted</string> <string name="filter_battery_unrestricted_title">Unrestricted</string>

View File

@@ -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);
}
}
}

View File

@@ -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<Integer, Integer> 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> T getInfo(Function<WarningBannerInfo, T> warningBannerInfoSupplier,
Function<WarningItemInfo, T> 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<WarningBannerInfo, String> warningBannerInfoSupplier,
Function<WarningItemInfo, String> 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<Integer, Integer> 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;
}
}

View File

@@ -221,14 +221,20 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
refreshUi(); refreshUi();
} }
boolean isHighlightSlotFocused() {
return (mDailyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID
&& mDailyHighlightSlotIndex == mDailyChartIndex
&& mHourlyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID
&& mHourlyHighlightSlotIndex == mHourlyChartIndex);
}
void onHighlightSlotIndexUpdate(int dailyHighlightSlotIndex, int hourlyHighlightSlotIndex) { void onHighlightSlotIndexUpdate(int dailyHighlightSlotIndex, int hourlyHighlightSlotIndex) {
if (mDailyHighlightSlotIndex == dailyHighlightSlotIndex
&& mHourlyHighlightSlotIndex == hourlyHighlightSlotIndex) {
return;
}
mDailyHighlightSlotIndex = dailyHighlightSlotIndex; mDailyHighlightSlotIndex = dailyHighlightSlotIndex;
mHourlyHighlightSlotIndex = hourlyHighlightSlotIndex; mHourlyHighlightSlotIndex = hourlyHighlightSlotIndex;
refreshUi(); refreshUi();
if (mOnSelectedIndexUpdatedListener != null) {
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
}
} }
void selectHighlightSlotIndex() { void selectHighlightSlotIndex() {
@@ -405,7 +411,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
final String slotInformation = getSlotInformation(); final String slotInformation = getSlotInformation();
return slotInformation == null return slotInformation == null
? mPrefContext.getString( ? mPrefContext.getString(
R.string.battery_usage_breakdown_title_since_last_full_charge) R.string.battery_usage_breakdown_title_since_last_full_charge)
: mPrefContext.getString( : mPrefContext.getString(
R.string.battery_usage_breakdown_title_for_slot, slotInformation); R.string.battery_usage_breakdown_title_for_slot, slotInformation);
} }

View File

@@ -120,12 +120,10 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
public void onClick(View view) { public void onClick(View view) {
final int viewId = view.getId(); final int viewId = view.getId();
if (viewId == R.id.main_button || viewId == R.id.tips_card) { if (viewId == R.id.main_button || viewId == R.id.tips_card) {
setVisible(false);
if (mOnConfirmListener != null) { if (mOnConfirmListener != null) {
mOnConfirmListener.onConfirm(); mOnConfirmListener.onConfirm();
} }
} else if (viewId == R.id.dismiss_button) { } else if (viewId == R.id.dismiss_button) {
setVisible(false);
if (mOnRejectListener != null) { if (mOnRejectListener != null) {
mOnRejectListener.onReject(); mOnRejectListener.onReject();
} }

View File

@@ -18,21 +18,15 @@ package com.android.settings.fuelgauge.batteryusage;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting; 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.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import java.util.function.Function;
/** Controls the update for battery tips card */ /** Controls the update for battery tips card */
public class BatteryTipsController extends BasePreferenceController { public class BatteryTipsController extends BasePreferenceController {
@@ -59,6 +53,10 @@ public class BatteryTipsController extends BasePreferenceController {
@VisibleForTesting @VisibleForTesting
BatteryTipsCardPreference mCardPreference; BatteryTipsCardPreference mCardPreference;
@VisibleForTesting
AnomalyEventWrapper mAnomalyEventWrapper = null;
@VisibleForTesting
Boolean mIsAcceptable = false;
public BatteryTipsController(Context context) { public BatteryTipsController(Context context) {
super(context, ROOT_PREFERENCE_KEY); super(context, ROOT_PREFERENCE_KEY);
@@ -85,132 +83,56 @@ public class BatteryTipsController extends BasePreferenceController {
mOnAnomalyRejectListener = listener; mOnAnomalyRejectListener = listener;
} }
private <T> T getInfo(PowerAnomalyEvent powerAnomalyEvent, void acceptTipsCard() {
Function<WarningBannerInfo, T> warningBannerInfoSupplier, if (mAnomalyEventWrapper == null || !mIsAcceptable) {
Function<WarningItemInfo, T> warningItemInfoSupplier) { return;
if (warningBannerInfoSupplier != null && powerAnomalyEvent.hasWarningBannerInfo()) {
return warningBannerInfoSupplier.apply(powerAnomalyEvent.getWarningBannerInfo());
} else if (warningItemInfoSupplier != null && powerAnomalyEvent.hasWarningItemInfo()) {
return warningItemInfoSupplier.apply(powerAnomalyEvent.getWarningItemInfo());
} }
return null; // For anomaly events with same record key, dismissed until next time full charged.
} final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey();
if (!TextUtils.isEmpty(dismissRecordKey)) {
private String getStringFromResource(int resourceId, int resourceIndex) { DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey);
if (resourceId < 0) {
return null;
} }
final String[] stringArray = mContext.getResources().getStringArray(resourceId); mCardPreference.setVisible(false);
return (resourceIndex >= 0 && resourceIndex < stringArray.length) mMetricsFeatureProvider.action(
? stringArray[resourceIndex] : null; mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT,
mAnomalyEventWrapper.getEventId());
} }
private int getResourceId(int resourceId, int resourceIndex, String defType) { void handleBatteryTipsCardUpdated(
final String key = getStringFromResource(resourceId, resourceIndex); AnomalyEventWrapper anomalyEventWrapper, boolean isAcceptable) {
return TextUtils.isEmpty(key) ? 0 mAnomalyEventWrapper = anomalyEventWrapper;
: mContext.getResources().getIdentifier(key, defType, mContext.getPackageName()); mIsAcceptable = isAcceptable;
} if (mAnomalyEventWrapper == null) {
private String getString(PowerAnomalyEvent powerAnomalyEvent,
Function<WarningBannerInfo, String> warningBannerInfoSupplier,
Function<WarningItemInfo, String> 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) {
mCardPreference.setVisible(false); mCardPreference.setVisible(false);
return; 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 // Get card preference strings and navigate fragment info
final String eventId = powerAnomalyEvent.hasEventId() final String eventId = mAnomalyEventWrapper.getEventId();
? powerAnomalyEvent.getEventId() : null;
final PowerAnomalyKey powerAnomalyKey = powerAnomalyEvent.hasKey()
? powerAnomalyEvent.getKey() : null;
final int resourceIndex = powerAnomalyKey != null ? powerAnomalyKey.getNumber() : -1;
final String titleString = getString(powerAnomalyEvent, WarningBannerInfo::getTitleString, // Update card & buttons preference
WarningItemInfo::getTitleString, R.array.power_anomaly_titles, resourceIndex); if (!mAnomalyEventWrapper.updateTipsCardPreference(mCardPreference)) {
if (titleString.isEmpty()) {
mCardPreference.setVisible(false); mCardPreference.setVisible(false);
return; 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 // Set battery tips card listener
mCardPreference.setOnConfirmListener(() -> { mCardPreference.setOnConfirmListener(() -> {
mCardPreference.setVisible(false);
if (mOnAnomalyConfirmListener != null) { if (mOnAnomalyConfirmListener != null) {
mOnAnomalyConfirmListener.onAnomalyConfirm(); mOnAnomalyConfirmListener.onAnomalyConfirm();
} else if (!TextUtils.isEmpty(destinationClassName)) { } else if (mAnomalyEventWrapper.launchSubSetting()) {
// Navigate to sub setting page mMetricsFeatureProvider.action(
Bundle arguments = Bundle.EMPTY; mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId);
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();
} }
mMetricsFeatureProvider.action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId);
}); });
mCardPreference.setOnRejectListener(() -> { mCardPreference.setOnRejectListener(() -> {
mCardPreference.setVisible(false);
if (mOnAnomalyRejectListener != null) { if (mOnAnomalyRejectListener != null) {
mOnAnomalyRejectListener.onAnomalyReject(); mOnAnomalyRejectListener.onAnomalyReject();
} }
// For anomaly events with same record key, dismissed until next time full charged. // 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)) { if (!TextUtils.isEmpty(dismissRecordKey)) {
DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey); DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey);
} }

View File

@@ -53,6 +53,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
/** Controller for battery usage breakdown preference group. */ /** Controller for battery usage breakdown preference group. */
@@ -93,6 +94,14 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
BatteryDiffData mBatteryDiffData; BatteryDiffData mBatteryDiffData;
@VisibleForTesting @VisibleForTesting
String mPercentLessThanThresholdText; String mPercentLessThanThresholdText;
@VisibleForTesting
boolean mIsHighlightSlot;
@VisibleForTesting
String mAnomalyEventId;
@VisibleForTesting
String mAnomalyEntryKey;
@VisibleForTesting
String mAnomalyHintString;
public BatteryUsageBreakdownController( public BatteryUsageBreakdownController(
Context context, Lifecycle lifecycle, SettingsActivity activity, Context context, Lifecycle lifecycle, SettingsActivity activity,
@@ -137,6 +146,12 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
return false; return false;
} }
private String getActionKey(String packageName) {
final String actionKey = TextUtils.isEmpty(packageName)
? PACKAGE_NAME_NONE : packageName;
return mAnomalyEventId == null ? actionKey : actionKey + "|" + mAnomalyEventId;
}
@Override @Override
public boolean handlePreferenceTreeClick(Preference preference) { public boolean handlePreferenceTreeClick(Preference preference) {
if (!(preference instanceof PowerGaugePreference)) { if (!(preference instanceof PowerGaugePreference)) {
@@ -151,7 +166,7 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
? SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM ? SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM
: SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM, : SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
/* pageId */ SettingsEnums.OPEN_BATTERY_USAGE, /* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName, getActionKey(packageName),
(int) Math.round(diffEntry.getPercentage())); (int) Math.round(diffEntry.getPercentage()));
Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s", Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
diffEntry.getAppLabel(), diffEntry.getKey(), packageName)); diffEntry.getAppLabel(), diffEntry.getKey(), packageName));
@@ -211,9 +226,23 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
* used when showing the footer. * used when showing the footer.
*/ */
void handleBatteryUsageUpdated( void handleBatteryUsageUpdated(
BatteryDiffData slotUsageData, String slotTimestamp, boolean isAllUsageDataEmpty) { BatteryDiffData slotUsageData, String slotTimestamp,
boolean isAllUsageDataEmpty, boolean isHighlightSlot,
Optional<AnomalyEventWrapper> optionalAnomalyEventWrapper) {
mBatteryDiffData = slotUsageData; mBatteryDiffData = slotUsageData;
mSlotTimestamp = slotTimestamp; 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); showCategoryTitle(slotTimestamp);
showSpinnerAndAppList(); showSpinnerAndAppList();
@@ -278,15 +307,15 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
continue; continue;
} }
final String prefKey = entry.getKey(); final String prefKey = entry.getKey();
PowerGaugePreference pref = mAppListPreferenceGroup.findPreference(prefKey); AnomalyAppItemPreference pref = mAppListPreferenceGroup.findPreference(prefKey);
if (pref != null) { if (pref != null) {
isAdded = true; isAdded = true;
} else { } 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) { if (pref == null) {
pref = new PowerGaugePreference(mPrefContext); pref = new AnomalyAppItemPreference(mPrefContext);
pref.setKey(prefKey); pref.setKey(prefKey);
mPreferenceCache.put(prefKey, pref); mPreferenceCache.put(prefKey, pref);
} }
@@ -294,6 +323,10 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
pref.setTitle(appLabel); pref.setTitle(appLabel);
pref.setOrder(prefIndex); pref.setOrder(prefIndex);
pref.setSingleLineTitle(true); 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. // Sets the BatteryDiffEntry to preference for launching detailed page.
pref.setBatteryDiffEntry(entry); pref.setBatteryDiffEntry(entry);
pref.setSelectable(entry.validForRestriction()); pref.setSelectable(entry.validForRestriction());

View File

@@ -52,6 +52,7 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Predicate;
/** Advanced power usage. */ /** Advanced power usage. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -92,9 +93,9 @@ public class PowerUsageAdvanced extends PowerUsageBase {
@VisibleForTesting @VisibleForTesting
BatteryUsageBreakdownController mBatteryUsageBreakdownController; BatteryUsageBreakdownController mBatteryUsageBreakdownController;
@VisibleForTesting @VisibleForTesting
PowerAnomalyEvent mPowerAnomalyEvent;
@VisibleForTesting
Optional<BatteryLevelData> mBatteryLevelData; Optional<BatteryLevelData> mBatteryLevelData;
@VisibleForTesting
Optional<AnomalyEventWrapper> mHighlightEventWrapper;
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
@@ -188,7 +189,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
mIsChartDataLoaded = true; mIsChartDataLoaded = true;
mBatteryLevelData = null; mBatteryLevelData = null;
mBatteryUsageMap = null; mBatteryUsageMap = null;
mPowerAnomalyEvent = null; mHighlightEventWrapper = null;
restartLoader(LoaderIndex.BATTERY_LEVEL_DATA_LOADER, bundle, restartLoader(LoaderIndex.BATTERY_LEVEL_DATA_LOADER, bundle,
mBatteryLevelDataLoaderCallbacks); mBatteryLevelDataLoaderCallbacks);
} }
@@ -239,8 +240,13 @@ public class PowerUsageAdvanced extends PowerUsageBase {
mScreenOnTimeController.handleSceenOnTimeUpdated( mScreenOnTimeController.handleSceenOnTimeUpdated(
slotUsageData.getScreenOnTime(), slotInformation); slotUsageData.getScreenOnTime(), slotInformation);
} }
// Hide card tips if the related highlight slot was clicked.
if (isAppsAnomalyEventFocused()) {
mBatteryTipsController.acceptTipsCard();
}
mBatteryUsageBreakdownController.handleBatteryUsageUpdated( mBatteryUsageBreakdownController.handleBatteryUsageUpdated(
slotUsageData, slotInformation, isBatteryUsageMapNullOrEmpty()); slotUsageData, slotInformation, isBatteryUsageMapNullOrEmpty(),
isAppsAnomalyEventFocused(), mHighlightEventWrapper);
Log.d(TAG, String.format("Battery usage list shows in %d millis", Log.d(TAG, String.format("Battery usage list shows in %d millis",
System.currentTimeMillis() - mResumeTimestamp)); System.currentTimeMillis() - mResumeTimestamp));
} }
@@ -261,49 +267,95 @@ public class PowerUsageAdvanced extends PowerUsageBase {
return; return;
} }
Log.d(TAG, "anomalyEventList = " + anomalyEventList); Log.d(TAG, "anomalyEventList = " + anomalyEventList);
final PowerAnomalyEvent displayEvent =
getHighestScoreAnomalyEvent(getContext(), anomalyEventList); final Set<String> dismissedPowerAnomalyKeys =
onDisplayAnomalyEventUpdated(displayEvent); 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 @VisibleForTesting
void onDisplayAnomalyEventUpdated(PowerAnomalyEvent event) { void onDisplayAnomalyEventUpdated(
mPowerAnomalyEvent = event; PowerAnomalyEvent tipsCardEvent, PowerAnomalyEvent highlightEvent) {
if (mBatteryTipsController == null if (mBatteryTipsController == null
|| mBatteryChartPreferenceController == null || mBatteryChartPreferenceController == null
|| mBatteryUsageBreakdownController == null) { || mBatteryUsageBreakdownController == null) {
return; return;
} }
final boolean isSameAnomalyEvent = (tipsCardEvent == highlightEvent);
// Update battery tips card preference & behaviour // Update battery tips card preference & behaviour
mBatteryTipsController.setOnAnomalyConfirmListener(null); mBatteryTipsController.setOnAnomalyConfirmListener(null);
mBatteryTipsController.setOnAnomalyRejectListener(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 // Update highlight slot effect in battery chart view
Pair<Integer, Integer> highlightSlotIndexPair = Pair.create( Pair<Integer, Integer> highlightSlotIndexPair = Pair.create(
BatteryChartViewModel.SELECTED_INDEX_INVALID, BatteryChartViewModel.SELECTED_INDEX_INVALID,
BatteryChartViewModel.SELECTED_INDEX_INVALID); BatteryChartViewModel.SELECTED_INDEX_INVALID);
if (mPowerAnomalyEvent != null && mPowerAnomalyEvent.hasWarningItemInfo()) { mHighlightEventWrapper = Optional.ofNullable(isSameAnomalyEvent ? tipsCardEventWrapper :
final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo(); ((highlightEvent != null)
final Long startTimestamp = warningItemInfo.hasStartTimestamp() ? new AnomalyEventWrapper(getContext(), highlightEvent) : null));
? warningItemInfo.getStartTimestamp() : null; if (mBatteryLevelData != null && mBatteryLevelData.isPresent()
final Long endTimestamp = warningItemInfo.hasEndTimestamp() && mHighlightEventWrapper.isPresent()
? warningItemInfo.getEndTimestamp() : null; && mHighlightEventWrapper.get().hasHighlightSlotPair(mBatteryLevelData.get())) {
if (startTimestamp != null && endTimestamp != null) { highlightSlotIndexPair = mHighlightEventWrapper.get()
highlightSlotIndexPair = mBatteryLevelData.map(levelData -> .getHighlightSlotPair(mBatteryLevelData.get());
levelData.getIndexByTimestamps(startTimestamp, endTimestamp)) if (isSameAnomalyEvent) {
.orElse(highlightSlotIndexPair); // For main button, focus on highlight slot when clicked
mBatteryTipsController.setOnAnomalyConfirmListener( mBatteryTipsController.setOnAnomalyConfirmListener(() -> {
mBatteryChartPreferenceController::selectHighlightSlotIndex); mBatteryChartPreferenceController.selectHighlightSlotIndex();
mBatteryTipsController.setOnAnomalyRejectListener( mBatteryTipsController.acceptTipsCard();
() -> onDisplayAnomalyEventUpdated(null)); });
} }
} }
mBatteryChartPreferenceController.onHighlightSlotIndexUpdate( mBatteryChartPreferenceController.onHighlightSlotIndexUpdate(
highlightSlotIndexPair.first, highlightSlotIndexPair.second); 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<Integer, Integer> 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() { private void setBatteryChartPreferenceController() {
if (mHistPref != null && mBatteryChartPreferenceController != null) { if (mHistPref != null && mBatteryChartPreferenceController != null) {
mHistPref.setChartPreferenceController(mBatteryChartPreferenceController); mHistPref.setChartPreferenceController(mBatteryChartPreferenceController);
@@ -318,6 +370,11 @@ public class PowerUsageAdvanced extends PowerUsageBase {
&& allBatteryDiffData.getSystemDiffEntryList().isEmpty()); && allBatteryDiffData.getSystemDiffEntryList().isEmpty());
} }
private boolean isAppsAnomalyEventFocused() {
return mBatteryChartPreferenceController != null
&& mBatteryChartPreferenceController.isHighlightSlotFocused();
}
private void logScreenUsageTime() { private void logScreenUsageTime() {
final BatteryDiffData allBatteryDiffData = getAllBatteryDiffData(mBatteryUsageMap); final BatteryDiffData allBatteryDiffData = getAllBatteryDiffData(mBatteryUsageMap);
if (allBatteryDiffData == null) { if (allBatteryDiffData == null) {
@@ -338,25 +395,22 @@ public class PowerUsageAdvanced extends PowerUsageBase {
} }
@VisibleForTesting @VisibleForTesting
static PowerAnomalyEvent getHighestScoreAnomalyEvent( static PowerAnomalyEvent getAnomalyEvent(
Context context, PowerAnomalyEventList anomalyEventList) { PowerAnomalyEventList anomalyEventList, Predicate<PowerAnomalyEvent> predicate) {
if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) { if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) {
return null; return null;
} }
final Set<String> dismissedPowerAnomalyKeys =
DatabaseUtils.getDismissedPowerAnomalyKeys(context);
Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys);
final PowerAnomalyEvent highestScoreEvent = anomalyEventList.getPowerAnomalyEventsList() final PowerAnomalyEvent filterAnomalyEvent = anomalyEventList.getPowerAnomalyEventsList()
.stream() .stream()
.filter(event -> !dismissedPowerAnomalyKeys.contains( .filter(predicate)
BatteryTipsController.getDismissRecordKey(event)))
.max(Comparator.comparing(PowerAnomalyEvent::getScore)) .max(Comparator.comparing(PowerAnomalyEvent::getScore))
.orElse(null); .orElse(null);
Log.d(TAG, "highestScoreAnomalyEvent = " + highestScoreEvent); Log.d(TAG, "filterAnomalyEvent = " + filterAnomalyEvent);
return highestScoreEvent; return filterAnomalyEvent;
} }
private static BatteryDiffData getAllBatteryDiffData( private static BatteryDiffData getAllBatteryDiffData(
Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) { Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
return batteryUsageMap == null ? null : batteryUsageMap return batteryUsageMap == null ? null : batteryUsageMap

View File

@@ -18,6 +18,7 @@ message PowerAnomalyEvent {
WarningBannerInfo warning_banner_info = 6; WarningBannerInfo warning_banner_info = 6;
WarningItemInfo warning_item_info = 7; 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. // 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. // 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. // The enum value will be used to decide pre-defined title and button labels.
// //
// Next id: 3 // Next id: 8
enum PowerAnomalyKey{ enum PowerAnomalyKey{
KEY_BRIGHTNESS = 0; KEY_BRIGHTNESS = 0;
KEY_SCREEN_TIMEOUT = 1; 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 { message WarningBannerInfo {
@@ -60,6 +66,5 @@ message WarningItemInfo {
optional string description_string = 5; optional string description_string = 5;
optional string main_button_string = 6; optional string main_button_string = 6;
optional string cancel_button_string = 7; optional string cancel_button_string = 7;
optional string dismiss_record_key = 8; optional string item_key = 8;
optional string item_key = 9;
} }

View File

@@ -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");
}
}

View File

@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -58,13 +59,14 @@ public final class BatteryTipsCardPreferenceTest {
private BatteryTipsCardPreference mBatteryTipsCardPreference; private BatteryTipsCardPreference mBatteryTipsCardPreference;
private PowerUsageAdvanced mPowerUsageAdvanced; private PowerUsageAdvanced mPowerUsageAdvanced;
private BatteryTipsController mBatteryTipsController; private BatteryTipsController mBatteryTipsController;
private BatteryChartPreferenceController mBatteryChartPreferenceController;
@Mock @Mock
private View mFakeView; private View mFakeView;
@Mock @Mock
private BatteryChartPreferenceController mBatteryChartPreferenceController;
@Mock
private BatteryUsageBreakdownController mBatteryUsageBreakdownController; private BatteryUsageBreakdownController mBatteryUsageBreakdownController;
@Mock
private BatteryDiffEntry mFakeEntry;
@Before @Before
public void setUp() { public void setUp() {
@@ -73,8 +75,13 @@ public final class BatteryTipsCardPreferenceTest {
mFeatureFactory = FakeFeatureFactory.setupForTest(); mFeatureFactory = FakeFeatureFactory.setupForTest();
mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /*attrs=*/ null); mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /*attrs=*/ null);
mBatteryTipsController = new BatteryTipsController(mContext); mBatteryTipsController = new BatteryTipsController(mContext);
mBatteryChartPreferenceController =
spy(new BatteryChartPreferenceController(mContext, null, null));
mBatteryChartPreferenceController.mPrefContext = mContext;
mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference; mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference;
mPowerUsageAdvanced = new PowerUsageAdvanced();
mPowerUsageAdvanced = spy(new PowerUsageAdvanced());
doReturn(mContext).when(mPowerUsageAdvanced).getContext();
mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController; mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController;
mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController; mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController;
mPowerUsageAdvanced.mBatteryUsageBreakdownController = mBatteryUsageBreakdownController; mPowerUsageAdvanced.mBatteryUsageBreakdownController = mBatteryUsageBreakdownController;
@@ -82,6 +89,7 @@ public final class BatteryTipsCardPreferenceTest {
1694354400000L, 1, // 2023-09-10 22:00:00 1694354400000L, 1, // 2023-09-10 22:00:00
1694361600000L, 2, // 2023-09-11 00:00:00 1694361600000L, 2, // 2023-09-11 00:00:00
1694368800000L, 3))); // 2023-09-11 02:00:00 1694368800000L, 3))); // 2023-09-11 02:00:00
doReturn("TestEntriesKey").when(mFakeEntry).getKey();
} }
@Test @Test
@@ -99,7 +107,8 @@ public final class BatteryTipsCardPreferenceTest {
when(mFakeView.getId()).thenReturn(R.id.main_button); when(mFakeView.getId()).thenReturn(R.id.main_button);
doNothing().when(mContext).startActivity(captor.capture()); doNothing().when(mContext).startActivity(captor.capture());
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(adaptiveBrightnessAnomaly); mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(
adaptiveBrightnessAnomaly, adaptiveBrightnessAnomaly);
mBatteryTipsCardPreference.onClick(mFakeView); mBatteryTipsCardPreference.onClick(mFakeView);
assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
@@ -109,25 +118,30 @@ public final class BatteryTipsCardPreferenceTest {
.isEqualTo(DisplaySettings.class.getName()); .isEqualTo(DisplaySettings.class.getName());
assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, -1)) assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, -1))
.isEqualTo(SettingsEnums.DISPLAY); .isEqualTo(SettingsEnums.DISPLAY);
verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "BrightnessAnomaly");
verify(mFeatureFactory.metricsFeatureProvider).action( verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "BrightnessAnomaly"); mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "BrightnessAnomaly");
} }
@Test @Test
public void onClick_dismissBtn_cardDismissAndLogged() { public void onClick_dismissBtnOfSettingsAnomaly_cardDismissAndLogged() {
final PowerAnomalyEvent screenTimeoutAnomaly = final PowerAnomalyEvent screenTimeoutAnomaly =
BatteryTestUtils.createScreenTimeoutAnomalyEvent(); BatteryTestUtils.createScreenTimeoutAnomalyEvent();
DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
when(mFakeView.getId()).thenReturn(R.id.dismiss_button); when(mFakeView.getId()).thenReturn(R.id.dismiss_button);
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(screenTimeoutAnomaly); mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(
screenTimeoutAnomaly, screenTimeoutAnomaly);
mBatteryTipsCardPreference.onClick(mFakeView); mBatteryTipsCardPreference.onClick(mFakeView);
assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)).hasSize(1); assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)).hasSize(1);
assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)) assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext))
.contains(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name()); .contains(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name());
verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "ScreenTimeoutAnomaly");
verify(mFeatureFactory.metricsFeatureProvider).action( verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "ScreenTimeoutAnomaly"); mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "ScreenTimeoutAnomaly");
} }
@@ -137,30 +151,40 @@ public final class BatteryTipsCardPreferenceTest {
final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent(); final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent();
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
when(mFakeView.getId()).thenReturn(R.id.main_button); 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); mBatteryTipsCardPreference.onClick(mFakeView);
assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
verify(mContext, never()).startActivity(any(Intent.class)); verify(mContext, never()).startActivity(any(Intent.class));
verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
eq(1), eq(0));
verify(mBatteryChartPreferenceController).selectHighlightSlotIndex(); verify(mBatteryChartPreferenceController).selectHighlightSlotIndex();
verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "AppAnomaly");
verify(mFeatureFactory.metricsFeatureProvider).action( verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "AppAnomaly"); mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "AppAnomaly");
} }
@Test @Test
public void onClick_dismissBtnOfAppsAnomaly_removeHighlightSlotIndex() { public void onClick_dismissBtnOfAppsAnomaly_keepHighlightSlotIndex() {
final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent(); final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent();
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
when(mFakeView.getId()).thenReturn(R.id.dismiss_button); when(mFakeView.getId()).thenReturn(R.id.dismiss_button);
when(mPowerUsageAdvanced.findRelatedBatteryDiffEntry(any())).thenReturn(mFakeEntry);
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly); mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly, appsAnomaly);
mBatteryTipsCardPreference.onClick(mFakeView); mBatteryTipsCardPreference.onClick(mFakeView);
assertThat(mBatteryTipsCardPreference.isVisible()).isFalse(); assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
verify(mContext, never()).startActivity(any(Intent.class));
verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate( verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
eq(BatteryChartViewModel.SELECTED_INDEX_INVALID), eq(1), eq(0));
eq(BatteryChartViewModel.SELECTED_INDEX_INVALID)); verify(mBatteryChartPreferenceController, never()).selectHighlightSlotIndex();
verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "AppAnomaly");
verify(mFeatureFactory.metricsFeatureProvider).action( verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "AppAnomaly"); mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "AppAnomaly");
} }

View File

@@ -16,8 +16,6 @@
package com.android.settings.fuelgauge.batteryusage; 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.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -70,30 +68,18 @@ public final class BatteryTipsControllerTest {
@Test @Test
public void handleBatteryTipsCardUpdated_null_hidePreference() { public void handleBatteryTipsCardUpdated_null_hidePreference() {
mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null); mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null, false);
verify(mBatteryTipsCardPreference).setVisible(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 @Test
public void handleBatteryTipsCardUpdated_adaptiveBrightnessAnomaly_showAnomaly() { public void handleBatteryTipsCardUpdated_adaptiveBrightnessAnomaly_showAnomaly() {
PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(); PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
mBatteryTipsController.handleBatteryTipsCardUpdated(event); mBatteryTipsController.handleBatteryTipsCardUpdated(
new AnomalyEventWrapper(mContext, event), false);
// Check pre-defined string // Check pre-defined string
verify(mBatteryTipsCardPreference).setTitle( verify(mBatteryTipsCardPreference).setTitle(
@@ -114,7 +100,8 @@ public final class BatteryTipsControllerTest {
PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent(); PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent();
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); 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).setTitle("Reduce screen timeout to extend battery life");
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb); verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
@@ -139,7 +126,8 @@ public final class BatteryTipsControllerTest {
.build(); .build();
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
mBatteryTipsController.handleBatteryTipsCardUpdated(event); mBatteryTipsController.handleBatteryTipsCardUpdated(
new AnomalyEventWrapper(mContext, event), false);
verify(mBatteryTipsCardPreference).setTitle(testTitle); verify(mBatteryTipsCardPreference).setTitle(testTitle);
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb); verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
@@ -157,10 +145,13 @@ public final class BatteryTipsControllerTest {
PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent(); PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); 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( verify(mBatteryTipsCardPreference).setTitle(
"Chrome used more battery than usual in foreground"); "Chrome used more battery than usual");
verify(mBatteryTipsCardPreference).setIconResourceId( verify(mBatteryTipsCardPreference).setIconResourceId(
R.drawable.ic_battery_tips_warning_icon); R.drawable.ic_battery_tips_warning_icon);
verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId( verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId(

View File

@@ -69,7 +69,7 @@ public final class BatteryUsageBreakdownControllerTest {
@Mock @Mock
private BatteryHistEntry mBatteryHistEntry; private BatteryHistEntry mBatteryHistEntry;
@Mock @Mock
private PowerGaugePreference mPowerGaugePreference; private AnomalyAppItemPreference mAnomalyAppItemPreference;
private Context mContext; private Context mContext;
private FakeFeatureFactory mFeatureFactory; private FakeFeatureFactory mFeatureFactory;
@@ -123,13 +123,14 @@ public final class BatteryUsageBreakdownControllerTest {
BatteryDiffEntry.sResourceCache.put( BatteryDiffEntry.sResourceCache.put(
"fakeBatteryDiffEntryKey", "fakeBatteryDiffEntryKey",
new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1)); new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
doReturn(mAnomalyAppItemPreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
} }
@Test @Test
public void onDestroy_clearPreferenceCacheAndPreferenceGroupRemoveAll() { public void onDestroy_clearPreferenceCacheAndPreferenceGroupRemoveAll() {
// Ensures the testing environment is correct. // Ensures the testing environment is correct.
mBatteryUsageBreakdownController.mPreferenceCache.put( mBatteryUsageBreakdownController.mPreferenceCache.put(
PREF_KEY, mPowerGaugePreference); PREF_KEY, mAnomalyAppItemPreference);
assertThat(mBatteryUsageBreakdownController.mPreferenceCache).hasSize(1); assertThat(mBatteryUsageBreakdownController.mPreferenceCache).hasSize(1);
mBatteryUsageBreakdownController.onDestroy(); mBatteryUsageBreakdownController.onDestroy();
@@ -178,7 +179,6 @@ public final class BatteryUsageBreakdownControllerTest {
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel(); doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey(); doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
mBatteryUsageBreakdownController.addAllPreferences(); mBatteryUsageBreakdownController.addAllPreferences();
@@ -188,27 +188,25 @@ public final class BatteryUsageBreakdownControllerTest {
@Test @Test
public void removeAndCacheAllUnusedPreferences_removePref_buildCacheAndRemoveAllPreference() { public void removeAndCacheAllUnusedPreferences_removePref_buildCacheAndRemoveAllPreference() {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); 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_KEY2).when(mBatteryHistEntry).getKey();
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey(); doReturn(PREF_KEY).when(mAnomalyAppItemPreference).getKey();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
// Ensures the testing data is correct. // Ensures the testing data is correct.
assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty(); assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
mBatteryUsageBreakdownController.removeAndCacheAllUnusedPreferences(); mBatteryUsageBreakdownController.removeAndCacheAllUnusedPreferences();
assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY)) assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY))
.isEqualTo(mPowerGaugePreference); .isEqualTo(mAnomalyAppItemPreference);
verify(mAppListPreferenceGroup).removePreference(mPowerGaugePreference); verify(mAppListPreferenceGroup).removePreference(mAnomalyAppItemPreference);
} }
@Test @Test
public void removeAndCacheAllUnusedPreferences_keepPref_KeepAllPreference() { public void removeAndCacheAllUnusedPreferences_keepPref_KeepAllPreference() {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); 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(mBatteryDiffEntry).getKey();
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey(); doReturn(PREF_KEY).when(mAnomalyAppItemPreference).getKey();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
// Ensures the testing data is correct. // Ensures the testing data is correct.
assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty(); assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
@@ -232,10 +230,10 @@ public final class BatteryUsageBreakdownControllerTest {
@Test @Test
public void handlePreferenceTreeClick_forAppEntry_returnTrue() { public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY; mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); doReturn(mBatteryDiffEntry).when(mAnomalyAppItemPreference).getBatteryDiffEntry();
assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick( assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
mPowerGaugePreference)).isTrue(); mAnomalyAppItemPreference)).isTrue();
verify(mMetricsFeatureProvider) verify(mMetricsFeatureProvider)
.action( .action(
SettingsEnums.OPEN_BATTERY_USAGE, SettingsEnums.OPEN_BATTERY_USAGE,
@@ -248,10 +246,10 @@ public final class BatteryUsageBreakdownControllerTest {
@Test @Test
public void handlePreferenceTreeClick_forSystemEntry_returnTrue() { public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_UID_BATTERY; mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_UID_BATTERY;
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); doReturn(mBatteryDiffEntry).when(mAnomalyAppItemPreference).getBatteryDiffEntry();
assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick( assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
mPowerGaugePreference)).isTrue(); mAnomalyAppItemPreference)).isTrue();
verify(mMetricsFeatureProvider) verify(mMetricsFeatureProvider)
.action( .action(
SettingsEnums.OPEN_BATTERY_USAGE, SettingsEnums.OPEN_BATTERY_USAGE,

View File

@@ -72,6 +72,7 @@ public final class ConvertUtilsTest {
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
ConvertUtils.sUsageSource = ConvertUtils.EMPTY_USAGE_SOURCE; ConvertUtils.sUsageSource = ConvertUtils.EMPTY_USAGE_SOURCE;
when(mContext.getPackageManager()).thenReturn(mMockPackageManager); when(mContext.getPackageManager()).thenReturn(mMockPackageManager);

View File

@@ -20,8 +20,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.content.Context; import android.content.Context;
@@ -41,7 +41,9 @@ import org.robolectric.annotation.Config;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.function.Predicate;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowDashboardFragment.class) @Config(shadows = ShadowDashboardFragment.class)
@@ -50,6 +52,9 @@ public final class PowerUsageAdvancedTest {
private Context mContext; private Context mContext;
private PowerUsageAdvanced mPowerUsageAdvanced; private PowerUsageAdvanced mPowerUsageAdvanced;
private Predicate<PowerAnomalyEvent> mCardFilterPredicate;
private Predicate<PowerAnomalyEvent> mSlotFilterPredicate;
@Mock @Mock
private BatteryTipsController mBatteryTipsController; private BatteryTipsController mBatteryTipsController;
@Mock @Mock
@@ -65,7 +70,7 @@ public final class PowerUsageAdvancedTest {
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mPowerUsageAdvanced = new PowerUsageAdvanced(); mPowerUsageAdvanced = spy(new PowerUsageAdvanced());
mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController; mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController;
mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController; mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController;
mPowerUsageAdvanced.mScreenOnTimeController = mScreenOnTimeController; mPowerUsageAdvanced.mScreenOnTimeController = mScreenOnTimeController;
@@ -74,43 +79,63 @@ public final class PowerUsageAdvancedTest {
1694354400000L, 1, // 2023-09-10 22:00:00 1694354400000L, 1, // 2023-09-10 22:00:00
1694361600000L, 2, // 2023-09-11 00:00:00 1694361600000L, 2, // 2023-09-11 00:00:00
1694368800000L, 3))); // 2023-09-11 02:00:00 1694368800000L, 3))); // 2023-09-11 02:00:00
doReturn(mContext).when(mPowerUsageAdvanced).getContext();
mSlotFilterPredicate = PowerAnomalyEvent::hasWarningItemInfo;
} }
@Test @Test
public void getHighestScoreAnomalyEvent_withEmptyOrNullList_getNull() { public void getFilterAnomalyEvent_withEmptyOrNullList_getNull() {
assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, null)).isNull(); prepareCardFilterPredicate(null);
assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent( assertThat(PowerUsageAdvanced
mContext, BatteryTestUtils.createEmptyPowerAnomalyEventList())).isNull(); .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 @Test
public void getHighestScoreAnomalyEvent_withoutDismissed_getHighestScoreEvent() { public void getFilterAnomalyEvent_withoutDismissed_getHighestScoreEvent() {
final PowerAnomalyEventList powerAnomalyEventList = final PowerAnomalyEventList powerAnomalyEventList =
BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
final PowerAnomalyEvent highestScoreEvent = final PowerAnomalyEvent slotEvent =
PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList); PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
mSlotFilterPredicate);
prepareCardFilterPredicate(slotEvent);
final PowerAnomalyEvent cardEvent =
PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
mCardFilterPredicate);
assertThat(highestScoreEvent) assertThat(cardEvent).isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent());
.isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent()); assertThat(slotEvent).isNull();
} }
@Test @Test
public void getHighestScoreAnomalyEvent_withBrightnessDismissed_getScreenTimeout() { public void getFilterAnomalyEvent_withBrightnessDismissed_getScreenTimeout() {
final PowerAnomalyEventList powerAnomalyEventList = final PowerAnomalyEventList powerAnomalyEventList =
BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, PowerAnomalyKey.KEY_BRIGHTNESS.name()); DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, PowerAnomalyKey.KEY_BRIGHTNESS.name());
final PowerAnomalyEvent highestScoreEvent = final PowerAnomalyEvent slotEvent =
PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList); PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
mSlotFilterPredicate);
prepareCardFilterPredicate(slotEvent);
final PowerAnomalyEvent cardEvent =
PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
mCardFilterPredicate);
assertThat(highestScoreEvent) assertThat(cardEvent).isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent());
.isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent()); assertThat(slotEvent).isNull();
} }
@Test @Test
public void getHighestScoreAnomalyEvent_withAllDismissed_getNull() { public void getFilterAnomalyEvent_withAllDismissed_getNull() {
final PowerAnomalyEventList powerAnomalyEventList = final PowerAnomalyEventList powerAnomalyEventList =
BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
@@ -118,20 +143,26 @@ public final class PowerUsageAdvancedTest {
DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, key.name()); DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, key.name());
} }
final PowerAnomalyEvent highestScoreEvent = final PowerAnomalyEvent slotEvent =
PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList); PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
mSlotFilterPredicate);
prepareCardFilterPredicate(slotEvent);
final PowerAnomalyEvent cardEvent =
PowerUsageAdvanced.getAnomalyEvent(powerAnomalyEventList,
mCardFilterPredicate);
assertThat(highestScoreEvent).isNull(); assertThat(cardEvent).isNull();
assertThat(slotEvent).isNull();
} }
@Test @Test
public void onDisplayAnomalyEventUpdated_withSettingsAnomalyEvent_skipHighlightSlotEffect() { public void onDisplayAnomalyEventUpdated_withSettingsAnomalyEvent_skipHighlightSlotEffect() {
final PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent(); final PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event); mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event, event);
assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isEqualTo(event); assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId())
verify(mBatteryTipsController).handleBatteryTipsCardUpdated(eq(event)); .isEqualTo(event.getEventId());
verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyConfirmListener(isNull()); verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyConfirmListener(isNull());
verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyRejectListener(isNull()); verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyRejectListener(isNull());
verify(mPowerUsageAdvanced.mBatteryChartPreferenceController).onHighlightSlotIndexUpdate( verify(mPowerUsageAdvanced.mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
@@ -140,46 +171,44 @@ public final class PowerUsageAdvancedTest {
} }
@Test @Test
public void onDisplayAnomalyEventUpdated_withAppAnomalyEvent_setHighlightSlotEffect() { public void onDisplayAnomalyEventUpdated_onlyAppAnomalyEvent_setHighlightSlotEffect() {
final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent(); final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event); mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event, event);
assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isEqualTo(event); assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId())
verify(mBatteryTipsController).handleBatteryTipsCardUpdated(eq(event)); .isEqualTo(event.getEventId());
verify(mBatteryTipsController).setOnAnomalyConfirmListener(isNull()); verify(mBatteryTipsController).setOnAnomalyConfirmListener(isNull());
verify(mBatteryTipsController).setOnAnomalyRejectListener(isNull()); verify(mBatteryTipsController).setOnAnomalyRejectListener(isNull());
assertThat(event.getWarningItemInfo().hasStartTimestamp()).isTrue();
assertThat(event.getWarningItemInfo().hasEndTimestamp()).isTrue();
assertThat(mPowerUsageAdvanced.mBatteryLevelData.get().getIndexByTimestamps( assertThat(mPowerUsageAdvanced.mBatteryLevelData.get().getIndexByTimestamps(
event.getWarningItemInfo().getStartTimestamp(), event.getWarningItemInfo().getStartTimestamp(),
event.getWarningItemInfo().getEndTimestamp() event.getWarningItemInfo().getEndTimestamp()
)).isEqualTo(Pair.create(1, 0)); )).isEqualTo(Pair.create(1, 0));
verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0)); verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0));
verify(mBatteryTipsController).setOnAnomalyConfirmListener(notNull()); verify(mBatteryTipsController).setOnAnomalyConfirmListener(notNull());
verify(mBatteryTipsController).setOnAnomalyRejectListener(notNull());
} }
@Test @Test
public void onDisplayAnomalyEventUpdated_withNull_removeHighlightSlotEffect() { public void onDisplayAnomalyEventUpdated_withSettingsCardAndAppsSlotEvent_showExpected() {
final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent(); final PowerAnomalyEvent settingsEvent =
BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
final PowerAnomalyEvent appsEvent =
BatteryTestUtils.createAppAnomalyEvent();
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event); mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(settingsEvent, appsEvent);
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(null);
assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isNull(); assertThat(mPowerUsageAdvanced.mHighlightEventWrapper.get().getEventId())
verify(mBatteryTipsController, times(2)) .isEqualTo(appsEvent.getEventId());
.setOnAnomalyConfirmListener(isNull()); verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0));
verify(mBatteryTipsController, times(2)) verify(mBatteryTipsController).setOnAnomalyConfirmListener(isNull());
.setOnAnomalyRejectListener(isNull()); verify(mBatteryTipsController).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));
} }
}
private void prepareCardFilterPredicate(PowerAnomalyEvent slotEvent) {
final Set<String> dismissedPowerAnomalyKeys =
DatabaseUtils.getDismissedPowerAnomalyKeys(mContext);
mCardFilterPredicate = event -> !dismissedPowerAnomalyKeys.contains(
event.getDismissRecordKey())
&& (event.equals(slotEvent) || !event.hasWarningItemInfo());
}
}

View File

@@ -247,6 +247,7 @@ public class BatteryTestUtils {
.setEventId("BrightnessAnomaly") .setEventId("BrightnessAnomaly")
.setType(PowerAnomalyType.TYPE_SETTINGS_BANNER) .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER)
.setKey(PowerAnomalyKey.KEY_BRIGHTNESS) .setKey(PowerAnomalyKey.KEY_BRIGHTNESS)
.setDismissRecordKey(PowerAnomalyKey.KEY_BRIGHTNESS.name())
.setScore(1.2f) .setScore(1.2f)
.setWarningBannerInfo(WarningBannerInfo.newBuilder() .setWarningBannerInfo(WarningBannerInfo.newBuilder()
.setMainButtonDestination(DisplaySettings.class.getName()) .setMainButtonDestination(DisplaySettings.class.getName())
@@ -264,6 +265,7 @@ public class BatteryTestUtils {
.setEventId("ScreenTimeoutAnomaly") .setEventId("ScreenTimeoutAnomaly")
.setType(PowerAnomalyType.TYPE_SETTINGS_BANNER) .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER)
.setKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT) .setKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT)
.setDismissRecordKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name())
.setScore(1.1f) .setScore(1.1f)
.setWarningBannerInfo(WarningBannerInfo.newBuilder() .setWarningBannerInfo(WarningBannerInfo.newBuilder()
.setMainButtonDestination(ScreenTimeoutSettings.class.getName()) .setMainButtonDestination(ScreenTimeoutSettings.class.getName())
@@ -280,15 +282,12 @@ public class BatteryTestUtils {
return PowerAnomalyEvent.newBuilder() return PowerAnomalyEvent.newBuilder()
.setEventId("AppAnomaly") .setEventId("AppAnomaly")
.setType(PowerAnomalyType.TYPE_APPS_ITEM) .setType(PowerAnomalyType.TYPE_APPS_ITEM)
.setKey(PowerAnomalyKey.KEY_APP) .setKey(PowerAnomalyKey.KEY_APP_TOTAL_HIGHER_THAN_USUAL)
.setDismissRecordKey("KEY_APP_1")
.setScore(2.0f) .setScore(2.0f)
.setWarningItemInfo(WarningItemInfo.newBuilder() .setWarningItemInfo(WarningItemInfo.newBuilder()
.setDismissRecordKey("KEY_APP_1")
.setStartTimestamp(1694361600000L) // 2023-09-11 00:00:00 .setStartTimestamp(1694361600000L) // 2023-09-11 00:00:00
.setEndTimestamp(1694368800000L) // 2023-09-11 02: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())
.build(); .build();
} }