Snap for 10832001 from 7c1b1fc934 to udc-qpr1-release

Change-Id: Ic61c84245a98aed84c1462f819c5634fdd6b1c39
This commit is contained in:
Android Build Coastguard Worker
2023-09-19 23:20:08 +00:00
29 changed files with 1026 additions and 332 deletions

View File

@@ -659,6 +659,7 @@
<activity android:name="Settings$FaceSettingsActivity"
android:label="@string/security_settings_face_preference_title"
android:exported="true"
android:theme="@style/Theme.Settings.NoActionBar"
android:icon="@drawable/ic_face_header">
<intent-filter>
<action android:name="android.settings.FACE_SETTINGS" />
@@ -673,6 +674,7 @@
<activity android:name="Settings$FaceSettingsInternalActivity"
android:label="@string/security_settings_face_preference_title"
android:exported="false"
android:theme="@style/Theme.Settings.NoActionBar"
android:icon="@drawable/ic_face_header"
android:taskAffinity="com.android.settings.root">
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"

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>
</string-array>
<!-- The following 3 arrays are for power anomaly tips card. Please keep them the same size. -->
<string-array name="power_anomaly_titles">
<item>Turn on adaptive brightness to extend battery life</item>
<item>Reduce screen timeout to extend battery life</item>
<!-- The following 4 arrays are for power anomaly tips card. Please keep them the same size. -->
<string-array name="power_anomaly_title_ids" translatable="false">
<item>battery_tips_settings_summary_brightness</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 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_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 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>
</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>
</resources>

View File

@@ -382,6 +382,7 @@
<!-- Battery tips card view component -->
<dimen name="battery_tips_card_corner_radius_small">4dp</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 -->
<dimen name="dream_item_min_column_width">174dp</dimen>

View File

@@ -7252,7 +7252,7 @@
<string name="vibrate_when_ringing_option_ramping_ringer">Vibrate first then ring gradually</string>
<!-- Sound: Title for the option enabling spatializer effect. [CHAR LIMIT=30] -->
<string name="spatial_audio_title">Spatial audio</string>
<string name="spatial_audio_title">Spatial Audio</string>
<!-- Sound: Other sounds: Title for the option enabling touch sounds for dial pad tones. [CHAR LIMIT=30] -->
<string name="dial_pad_tones_title">Dial pad tones</string>
@@ -7300,7 +7300,11 @@
<string name="live_caption_summary">Automatically caption media</string>
<!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
<string name="spatial_audio_speaker">Phone speaker</string>
<string name="spatial_audio_speaker" product="default">Phone speakers</string>
<!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
<string name="spatial_audio_speaker" product="tablet">Tablet speakers</string>
<!-- Output device type for the phone speaker that is available for spatializer effect. [CHAR LIMIT=NONE]-->
<string name="spatial_audio_speaker" product="device">Device speakers</string>
<!-- Output device type for the wired headphones that is available for spatializer effect. [CHAR LIMIT=NONE]-->
<string name="spatial_audio_wired_headphones">Wired headphones</string>
@@ -9688,12 +9692,51 @@
<!-- Label of action button in battery tips card [CHAR LIMIT=50] -->
<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] -->
<string name="battery_tips_card_dismiss_button">Got it</string>
<!-- Feedback card message in battery tips card [CHAR LIMIT=NONE] -->
<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]-->
<string name="filter_battery_unrestricted_title">Unrestricted</string>

View File

@@ -69,7 +69,9 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
private static final String ENABLE_DUAL_MODE_AUDIO =
"persist.bluetooth.enable_dual_mode_audio";
private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
private static final boolean LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE = true;
private static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY =
"persist.bluetooth.leaudio.toggle_visible";
private LocalBluetoothManager mManager;
private LocalBluetoothProfileManager mProfileManager;
@@ -464,15 +466,13 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
private void updateLeAudioConfig() {
mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true);
boolean isLeDeviceDetailEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
boolean isLeAudioToggleVisible = SystemProperties.getBoolean(
LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE);
boolean isLeEnabledByDefault = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
mIsLeAudioToggleEnabled = isLeDeviceDetailEnabled || isLeEnabledByDefault;
mIsLeAudioToggleEnabled = isLeAudioToggleVisible || isLeEnabledByDefault;
Log.d(TAG, "BT_LE_AUDIO_CONTACT_SHARING_ENABLED:" + mIsLeContactSharingEnabled
+ ", BT_LE_AUDIO_DEVICE_DETAIL_ENABLED:" + isLeDeviceDetailEnabled
+ ", LE_AUDIO_TOGGLE_VISIBLE_PROPERTY:" + isLeAudioToggleVisible
+ ", CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT:" + isLeEnabledByDefault);
}

View File

@@ -42,9 +42,4 @@ public class SettingsUIDeviceConfig {
* {@code true} whether or not event_log for generic actions is enabled. Default is true.
*/
public static final String GENERIC_EVENT_LOGGING_ENABLED = "event_logging_enabled";
/**
* {@code true} whether to show LE Audio toggle in device detail page. Default is false.
*/
public static final String BT_LE_AUDIO_DEVICE_DETAIL_ENABLED =
"bt_le_audio_device_detail_enabled";
}

View File

@@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
import androidx.annotation.VisibleForTesting;
@@ -27,7 +28,6 @@ import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
/**
@@ -40,9 +40,12 @@ public class BluetoothLeAudioDeviceDetailsPreferenceController
private static final String PREFERENCE_KEY = "bluetooth_show_leaudio_device_details";
private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
private static final boolean LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE = true;
private static final boolean LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE = true;
static int sLeAudioSupportedStateCache = BluetoothStatusCodes.ERROR_UNKNOWN;
static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY =
"persist.bluetooth.leaudio.toggle_visible";
@VisibleForTesting
BluetoothAdapter mBluetoothAdapter;
@@ -73,10 +76,7 @@ public class BluetoothLeAudioDeviceDetailsPreferenceController
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean isEnabled = (Boolean) newValue;
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
isEnabled ? "true" : "false", LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, Boolean.toString(isEnabled));
return true;
}
@@ -86,25 +86,13 @@ public class BluetoothLeAudioDeviceDetailsPreferenceController
return;
}
final boolean leAudioDeviceDetailEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED,
LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
final boolean isLeAudioToggleVisible = SystemProperties.getBoolean(
LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE);
final boolean leAudioEnabledByDefault = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_BLUETOOTH, CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
mPreference.setEnabled(!leAudioEnabledByDefault);
((SwitchPreference) mPreference).setChecked(leAudioDeviceDetailEnabled
((SwitchPreference) mPreference).setChecked(isLeAudioToggleVisible
|| leAudioEnabledByDefault);
}
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
// Reset the toggle to null when the developer option is disabled
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "null",
LE_AUDIO_DEVICE_DETAIL_DEFAULT_VALUE);
}
}

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

View File

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

View File

@@ -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> T getInfo(PowerAnomalyEvent powerAnomalyEvent,
Function<WarningBannerInfo, T> warningBannerInfoSupplier,
Function<WarningItemInfo, T> 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<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) {
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);
}

View File

@@ -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<AnomalyEventWrapper> 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());

View File

@@ -28,6 +28,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
import com.android.settings.overlay.FeatureFactory;
import java.util.List;
import java.util.Map;
@@ -138,6 +139,8 @@ public final class BatteryUsageDataLoader {
// No app usage data or battery diff data at this time.
loadAppUsageData(context);
preprocessBatteryUsageSlots(context);
FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context)
.detectSettingsAnomaly(context, /* displayDrain= */ 0);
}
Log.d(TAG, String.format(
"loadUsageDataSafely() in %d/ms", System.currentTimeMillis() - start));

View File

@@ -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<BatteryLevelData> mBatteryLevelData;
@VisibleForTesting
Optional<AnomalyEventWrapper> 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));
}
@@ -262,49 +268,95 @@ public class PowerUsageAdvanced extends PowerUsageBase {
return;
}
Log.d(TAG, "anomalyEventList = " + anomalyEventList);
final PowerAnomalyEvent displayEvent =
getHighestScoreAnomalyEvent(getContext(), anomalyEventList);
onDisplayAnomalyEventUpdated(displayEvent);
final Set<String> 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<Integer, Integer> 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<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() {
if (mHistPref != null && mBatteryChartPreferenceController != null) {
mHistPref.setChartPreferenceController(mBatteryChartPreferenceController);
@@ -319,6 +371,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) {
@@ -339,25 +396,22 @@ public class PowerUsageAdvanced extends PowerUsageBase {
}
@VisibleForTesting
static PowerAnomalyEvent getHighestScoreAnomalyEvent(
Context context, PowerAnomalyEventList anomalyEventList) {
static PowerAnomalyEvent getAnomalyEvent(
PowerAnomalyEventList anomalyEventList, Predicate<PowerAnomalyEvent> predicate) {
if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) {
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()
.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<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
return batteryUsageMap == null ? null : batteryUsageMap

View File

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

View File

@@ -18,7 +18,6 @@ package com.android.settings.network;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
import android.annotation.Nullable;
@@ -56,6 +55,8 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -66,6 +67,9 @@ public class SubscriptionUtil {
static final String SUB_ID = "sub_id";
@VisibleForTesting
static final String KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME = "unique_subscription_displayName";
private static final String REGEX_DISPLAY_NAME_PREFIXES = "^";
private static final String REGEX_DISPLAY_NAME_SUFFIXES = "\\s[0-9]+";
private static List<SubscriptionInfo> sAvailableResultsForTesting;
private static List<SubscriptionInfo> sActiveResultsForTesting;
@@ -281,8 +285,8 @@ public class SubscriptionUtil {
String displayName = i.getDisplayName().toString();
info.originalName =
TextUtils.equals(displayName, PROFILE_GENERIC_DISPLAY_NAME)
? context.getResources().getString(R.string.sim_card)
: displayName.trim();
? context.getResources().getString(R.string.sim_card)
: displayName.trim();
return info;
});
@@ -298,12 +302,17 @@ public class SubscriptionUtil {
// If a display name is duplicate, append the final 4 digits of the phone number.
// Creates a mapping of Subscription id to original display name + phone number display name
final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
int infoSubId = info.subscriptionInfo.getSubscriptionId();
String cachedDisplayName = getDisplayNameFromSharedPreference(
context, info.subscriptionInfo.getSubscriptionId());
if (!TextUtils.isEmpty(cachedDisplayName)) {
Log.d(TAG, "use cached display name : " + cachedDisplayName);
context, infoSubId);
if (isValidCachedDisplayName(cachedDisplayName, info.originalName.toString())) {
Log.d(TAG, "use cached display name : for subId : " + infoSubId
+ "cached display name : " + cachedDisplayName);
info.uniqueName = cachedDisplayName;
return info;
} else {
Log.d(TAG, "remove cached display name : " + infoSubId);
removeItemFromDisplayNameSharedPreference(context, infoSubId);
}
if (duplicateOriginalNames.contains(info.originalName)) {
@@ -320,9 +329,8 @@ public class SubscriptionUtil {
} else {
info.uniqueName = info.originalName + " " + lastFourDigits;
Log.d(TAG, "Cache display name [" + info.uniqueName + "] for sub id "
+ info.subscriptionInfo.getSubscriptionId());
saveDisplayNameToSharedPreference(
context, info.subscriptionInfo.getSubscriptionId(), info.uniqueName);
+ infoSubId);
saveDisplayNameToSharedPreference(context, infoSubId, info.uniqueName);
}
} else {
info.uniqueName = info.originalName;
@@ -404,10 +412,27 @@ public class SubscriptionUtil {
.apply();
}
private static void removeItemFromDisplayNameSharedPreference(Context context, int subId) {
getDisplayNameSharedPreferenceEditor(context)
.remove(SUB_ID + subId)
.commit();
}
private static String getDisplayNameFromSharedPreference(Context context, int subid) {
return getDisplayNameSharedPreferences(context).getString(SUB_ID + subid, "");
}
@VisibleForTesting
static boolean isValidCachedDisplayName(String cachedDisplayName, String originalName) {
if (TextUtils.isEmpty(cachedDisplayName) || TextUtils.isEmpty(originalName)) {
return false;
}
String regex = REGEX_DISPLAY_NAME_PREFIXES + originalName + REGEX_DISPLAY_NAME_SUFFIXES;
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(cachedDisplayName);
return matcher.matches();
}
public static String getDisplayName(SubscriptionInfo info) {
final CharSequence name = info.getDisplayName();
if (name != null) {

View File

@@ -16,6 +16,9 @@
package com.android.settings.development;
import static com.android.settings.development.BluetoothLeAudioDeviceDetailsPreferenceController
.LE_AUDIO_TOGGLE_VISIBLE_PROPERTY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
@@ -25,12 +28,11 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.provider.DeviceConfig;
import android.os.SystemProperties;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
import org.junit.After;
@@ -77,9 +79,8 @@ public class BluetoothLeAudioDeviceDetailsPreferenceControllerTest {
public void onPreferenceChanged_settingEnabled_shouldTurnOnLeAudioDeviceDetailSetting() {
mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
mController.onPreferenceChange(mPreference, true /* new value */);
final boolean isEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false);
final boolean isEnabled = SystemProperties.getBoolean(
LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, false);
assertThat(isEnabled).isTrue();
}
@@ -88,9 +89,8 @@ public class BluetoothLeAudioDeviceDetailsPreferenceControllerTest {
public void onPreferenceChanged_settingDisabled_shouldTurnOffLeAudioDeviceDetailSetting() {
mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
mController.onPreferenceChange(mPreference, false /* new value */);
final boolean isEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, false);
final boolean isEnabled = SystemProperties.getBoolean(
LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, true);
assertThat(isEnabled).isFalse();
}
@@ -98,18 +98,15 @@ public class BluetoothLeAudioDeviceDetailsPreferenceControllerTest {
@Test
public void updateState_settingEnabled_preferenceShouldBeChecked() {
mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "true", false);
SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, "true");
mController.updateState(mPreference);
verify(mPreference).setChecked(true);
}
@Test
public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
mController.sLeAudioSupportedStateCache = BluetoothStatusCodes.FEATURE_SUPPORTED;
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_DEVICE_DETAIL_ENABLED, "false", false);
SystemProperties.set(LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, "false");
mController.updateState(mPreference);
verify(mPreference).setChecked(false);

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

View File

@@ -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(

View File

@@ -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,

View File

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

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.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<PowerAnomalyEvent> mCardFilterPredicate;
private Predicate<PowerAnomalyEvent> 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<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")
.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();
}

View File

@@ -18,9 +18,7 @@ package com.android.settings.network;
import static com.android.settings.network.SubscriptionUtil.KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME;
import static com.android.settings.network.SubscriptionUtil.SUB_ID;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
@@ -185,7 +183,7 @@ public class SubscriptionUtilTest {
@Ignore
@Test
public void getUniqueDisplayNames_identicalCarriers_fourDigitsUsed() {
// Both subscriptoins have the same display name.
// Both subscriptions have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -215,7 +213,7 @@ public class SubscriptionUtilTest {
@Ignore
@Test
public void getUniqueDisplayNames_identicalCarriersAfterTrim_fourDigitsUsed() {
// Both subscriptoins have the same display name.
// Both subscriptions have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -244,8 +242,8 @@ public class SubscriptionUtilTest {
@Ignore
@Test
public void getUniqueDisplayNames_phoneNumberBlocked_subscriptoinIdFallback() {
// Both subscriptoins have the same display name.
public void getUniqueDisplayNames_phoneNumberBlocked_subscriptionIdFallback() {
// Both subscriptions have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -273,9 +271,9 @@ public class SubscriptionUtilTest {
@Ignore
@Test
public void getUniqueDisplayNames_phoneNumberIdentical_subscriptoinIdFallback() {
public void getUniqueDisplayNames_phoneNumberIdentical_subscriptionIdFallback() {
// TODO have three here from the same carrier
// Both subscriptoins have the same display name.
// Both subscriptions have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
final SubscriptionInfo info3 = mock(SubscriptionInfo.class);
@@ -464,8 +462,8 @@ public class SubscriptionUtilTest {
SharedPreferences sp = mock(SharedPreferences.class);
when(mContext.getSharedPreferences(
KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE)).thenReturn(sp);
when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + "6789");
when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + "4321");
when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + " 6789");
when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + " 4321");
final CharSequence nameOfSub1 =
@@ -475,8 +473,41 @@ public class SubscriptionUtilTest {
assertThat(nameOfSub1).isNotNull();
assertThat(nameOfSub2).isNotNull();
assertEquals(CARRIER_1 + "6789", nameOfSub1.toString());
assertEquals(CARRIER_1 + "4321", nameOfSub2.toString());
assertEquals(CARRIER_1 + " 6789", nameOfSub1.toString());
assertEquals(CARRIER_1 + " 4321", nameOfSub2.toString());
}
@Test
public void getUniqueDisplayName_hasRecordAndNameIsChanged_doesNotUseRecordBeTheResult() {
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
when(info2.getSubscriptionId()).thenReturn(SUBID_2);
when(info1.getDisplayName()).thenReturn(CARRIER_1);
when(info2.getDisplayName()).thenReturn(CARRIER_2);
when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(
Arrays.asList(info1, info2));
SharedPreferences sp = mock(SharedPreferences.class);
SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class);
when(mContext.getSharedPreferences(
KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE)).thenReturn(sp);
when(sp.edit()).thenReturn(editor);
when(editor.remove(anyString())).thenReturn(editor);
when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + " 6789");
when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + " 4321");
final CharSequence nameOfSub1 =
SubscriptionUtil.getUniqueSubscriptionDisplayName(info1, mContext);
final CharSequence nameOfSub2 =
SubscriptionUtil.getUniqueSubscriptionDisplayName(info2, mContext);
assertThat(nameOfSub1).isNotNull();
assertThat(nameOfSub2).isNotNull();
assertEquals(CARRIER_1 + " 6789", nameOfSub1.toString());
assertEquals(CARRIER_2.toString(), nameOfSub2.toString());
}
@Test
@@ -501,4 +532,60 @@ public class SubscriptionUtilTest {
assertTrue(SubscriptionUtil.isSimHardwareVisible(mContext));
}
@Test
public void isValidCachedDisplayName_matchesRule1_returnTrue() {
String originalName = "originalName";
String cacheString = "originalName 1234";
assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isTrue();
}
@Test
public void isValidCachedDisplayName_matchesRule2_returnTrue() {
String originalName = "original Name";
String cacheString = originalName + " " + 1234;
assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isTrue();
}
@Test
public void isValidCachedDisplayName_nameIsEmpty1_returnFalse() {
String originalName = "original Name";
String cacheString = "";
assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
}
@Test
public void isValidCachedDisplayName_nameIsEmpty2_returnFalse() {
String originalName = "";
String cacheString = "originalName 1234";
assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
}
@Test
public void isValidCachedDisplayName_nameIsDifferent_returnFalse() {
String originalName = "original Name";
String cacheString = "originalName 1234";
assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
}
@Test
public void isValidCachedDisplayName_noNumber_returnFalse() {
String originalName = "original Name";
String cacheString = originalName;
assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
}
@Test
public void isValidCachedDisplayName_noSpace_returnFalse() {
String originalName = "original Name";
String cacheString = originalName;
assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
}
}