Merge "Impl app anomlay preference" into udc-qpr-dev am: c51a3da148 am: 428b124bea

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/24404174

Change-Id: I147d8496db00a770d8de45229813ae146e1e87cb
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Xinyi Mao
2023-09-01 09:54:53 +00:00
committed by Automerger Merge Worker
10 changed files with 220 additions and 27 deletions

View File

@@ -0,0 +1,18 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?android:attr/colorAccent"/>
</selector>

View File

@@ -0,0 +1,18 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/palette_list_color_yellow"/>
</selector>

View File

@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:attr/colorAccent"
android:fillColor="@color/color_accent_selector"
android:pathData="M7,20h4c0,1.1 -0.9,2 -2,2S7,21.1 7,20zM5,19h8v-2H5V19zM16.5,9.5c0,3.82 -2.66,5.86 -3.77,6.5H5.27C4.16,15.36 1.5,13.32 1.5,9.5C1.5,5.36 4.86,2 9,2S16.5,5.36 16.5,9.5zM14.5,9.5C14.5,6.47 12.03,4 9,4S3.5,6.47 3.5,9.5c0,2.47 1.49,3.89 2.35,4.5h6.3C13.01,13.39 14.5,11.97 14.5,9.5zM21.37,7.37L20,8l1.37,0.63L22,10l0.63,-1.37L24,8l-1.37,-0.63L22,6L21.37,7.37zM19,6l0.94,-2.06L22,3l-2.06,-0.94L19,0l-0.94,2.06L16,3l2.06,0.94L19,6z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/color_battery_anomaly_yellow_selector"
android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
</vector>

View File

@@ -61,7 +61,7 @@
android:text="@string/battery_tips_card_action_button"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"
app:strokeColor="?android:attr/colorAccent"
app:strokeColor="@color/color_accent_selector"
app:strokeWidth="1dp" />
</LinearLayout>
</LinearLayout>

View File

@@ -1409,6 +1409,17 @@
<integer-array name="network_mode_3g_deprecated_carrier_id" translatable="false">
</integer-array>
<!-- The following 2 arrays are for battery tips card. Please keep them the same size. -->
<string-array name="battery_tips_card_icons" translatable="false">
<item>ic_battery_tips_lightbulb</item>
<item>ic_battery_tips_warning_icon</item>
</string-array>
<string-array name="battery_tips_card_colors" translatable="false">
<item>color_accent_selector</item>
<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>

View File

@@ -23,6 +23,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -51,6 +52,8 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
private String mAnomalyEventId;
private PowerAnomalyKey mPowerAnomalyKey;
private int mIconResourceId = 0;
private int mMainButtonStrokeColorResourceId = 0;
@VisibleForTesting
CharSequence mMainButtonLabel;
@@ -73,6 +76,26 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
mPowerAnomalyKey = null;
}
/**
* Sets the icon in tips card.
*/
public void setIconResourceId(int resourceId) {
if (mIconResourceId != resourceId) {
mIconResourceId = resourceId;
notifyChanged();
}
}
/**
* Sets the stroke color of main button in tips card.
*/
public void setMainButtonStrokeColorResourceId(int resourceId) {
if (mMainButtonStrokeColorResourceId != resourceId) {
mMainButtonStrokeColorResourceId = resourceId;
notifyChanged();
}
}
/**
* Sets the anomaly event id which is used in metrics.
*/
@@ -159,9 +182,15 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
MaterialButton mainButton = (MaterialButton) view.findViewById(R.id.main_button);
mainButton.setOnClickListener(this);
mainButton.setText(mMainButtonLabel);
if (mMainButtonStrokeColorResourceId != 0) {
mainButton.setStrokeColorResource(mMainButtonStrokeColorResourceId);
}
MaterialButton dismissButton = (MaterialButton) view.findViewById(R.id.dismiss_button);
dismissButton.setOnClickListener(this);
dismissButton.setText(mDismissButtonLabel);
if (mIconResourceId != 0) {
((ImageView) view.findViewById(R.id.icon)).setImageResource(mIconResourceId);
}
if (!mPowerUsageFeatureProvider.isBatteryTipsFeedbackEnabled()) {
return;

View File

@@ -77,23 +77,29 @@ public class BatteryTipsController extends BasePreferenceController {
return null;
}
private String getStringFromResource(int resourceId, int resourceIndex) {
if (resourceId < 0) {
return null;
}
final String[] stringArray = mContext.getResources().getStringArray(resourceId);
return (resourceIndex >= 0 && resourceIndex < stringArray.length)
? stringArray[resourceIndex] : null;
}
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);
if (!TextUtils.isEmpty(string) || resourceId < 0) {
return string;
}
String[] stringArray = mContext.getResources().getStringArray(resourceId);
if (resourceIndex >= 0 && resourceIndex < stringArray.length) {
string = stringArray[resourceIndex];
}
return string;
return (!TextUtils.isEmpty(string) || resourceId < 0) ? string
: getStringFromResource(resourceId, resourceIndex);
}
@VisibleForTesting
@@ -107,6 +113,13 @@ public class BatteryTipsController extends BasePreferenceController {
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 PowerAnomalyKey powerAnomalyKey = powerAnomalyEvent.hasKey()
? powerAnomalyEvent.getKey() : null;
@@ -133,10 +146,12 @@ public class BatteryTipsController extends BasePreferenceController {
String preferenceHighlightKey = getInfo(powerAnomalyEvent,
WarningBannerInfo::getMainButtonSourceHighlightKey, null);
// Updated card preference and main button fragment launcher
// Update card preference and main button fragment launcher
mCardPreference.setAnomalyEventId(powerAnomalyEvent.getEventId());
mCardPreference.setPowerAnomalyKey(powerAnomalyKey);
mCardPreference.setTitle(titleString);
mCardPreference.setIconResourceId(iconResId);
mCardPreference.setMainButtonStrokeColorResourceId(colorResId);
mCardPreference.setMainButtonLabel(mainBtnString);
mCardPreference.setDismissButtonLabel(dismissBtnString);
mCardPreference.setMainButtonLauncherInfo(

View File

@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.os.LocaleList;
import com.android.settings.R;
import com.android.settings.testutils.BatteryTestUtils;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -83,6 +84,9 @@ public final class BatteryTipsControllerTest {
// Check pre-defined string
verify(mBatteryTipsCardPreference).setTitle(
"Turn on adaptive brightness to extend battery life");
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId(
R.color.color_accent_selector);
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
// Check proto info
@@ -103,6 +107,9 @@ public final class BatteryTipsControllerTest {
verify(mBatteryTipsCardPreference).setAnomalyEventId("ScreenTimeoutAnomaly");
verify(mBatteryTipsCardPreference).setTitle("Reduce screen timeout to extend battery life");
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId(
R.color.color_accent_selector);
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
@@ -129,6 +136,9 @@ public final class BatteryTipsControllerTest {
verify(mBatteryTipsCardPreference).setAnomalyEventId("ScreenTimeoutAnomaly");
verify(mBatteryTipsCardPreference).setTitle(testTitle);
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId(
R.color.color_accent_selector);
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
@@ -138,4 +148,27 @@ public final class BatteryTipsControllerTest {
verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "ScreenTimeoutAnomaly");
}
@Test
public void handleBatteryTipsCardUpdated_appAnomaly_showAnomaly() {
PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
mBatteryTipsController.handleBatteryTipsCardUpdated(event);
verify(mBatteryTipsCardPreference).setAnomalyEventId("AppAnomaly");
verify(mBatteryTipsCardPreference).setTitle(
"Chrome used more battery than usual in foreground");
verify(mBatteryTipsCardPreference).setIconResourceId(
R.drawable.ic_battery_tips_warning_icon);
verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId(
R.color.color_battery_anomaly_yellow_selector);
verify(mBatteryTipsCardPreference).setMainButtonLabel("Check");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
null, null, null);
verify(mBatteryTipsCardPreference).setVisible(true);
verify(mFeatureFactory.metricsFeatureProvider).action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "AppAnomaly");
}
}

View File

@@ -39,6 +39,7 @@ import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyKey;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyType;
import com.android.settings.fuelgauge.batteryusage.WarningBannerInfo;
import com.android.settings.fuelgauge.batteryusage.WarningItemInfo;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
@@ -70,14 +71,18 @@ public class BatteryTestUtils {
BatteryManager.BATTERY_STATUS_DISCHARGING);
}
/** Sets the work profile mode. */
/**
* Sets the work profile mode.
*/
public static void setWorkProfile(Context context) {
final UserManager userManager = context.getSystemService(UserManager.class);
Shadows.shadowOf(userManager).setManagedProfile(true);
Shadows.shadowOf(userManager).setIsSystemUser(false);
}
/** Creates and sets up the in-memory {@link BatteryStateDatabase}. */
/**
* Creates and sets up the in-memory {@link BatteryStateDatabase}.
*/
public static BatteryStateDatabase setUpBatteryStateDatabase(Context context) {
final BatteryStateDatabase inMemoryDatabase =
Room.inMemoryDatabaseBuilder(context, BatteryStateDatabase.class)
@@ -87,21 +92,27 @@ public class BatteryTestUtils {
return inMemoryDatabase;
}
/** Inserts a fake data into the database for testing. */
/**
* Inserts a fake data into the database for testing.
*/
public static void insertDataToBatteryStateTable(
Context context, long timestamp, String packageName) {
insertDataToBatteryStateTable(
context, timestamp, packageName, /*multiple=*/ false, /*isFullChargeStart=*/ false);
}
/** Inserts a fake data into the database for testing. */
/**
* Inserts a fake data into the database for testing.
*/
public static void insertDataToBatteryStateTable(
Context context, long timestamp, String packageName, boolean isFullChargeStart) {
insertDataToBatteryStateTable(
context, timestamp, packageName, /*multiple=*/ false, isFullChargeStart);
}
/** Inserts a fake data into the database for testing. */
/**
* Inserts a fake data into the database for testing.
*/
public static void insertDataToBatteryStateTable(
Context context, long timestamp, String packageName, boolean multiple,
boolean isFullChargeStart) {
@@ -151,14 +162,18 @@ public class BatteryTestUtils {
}
}
/** Inserts a fake data into the database for testing. */
/**
* Inserts a fake data into the database for testing.
*/
public static void insertDataToAppUsageEventTable(
Context context, long userId, long timestamp, String packageName) {
insertDataToAppUsageEventTable(
context, userId, timestamp, packageName, /*multiple=*/ false);
}
/** Inserts a fake data into the database for testing. */
/**
* Inserts a fake data into the database for testing.
*/
public static void insertDataToAppUsageEventTable(
Context context, long userId, long timestamp, String packageName, boolean multiple) {
final AppUsageEventEntity entity =
@@ -179,7 +194,9 @@ public class BatteryTestUtils {
}
}
/** Gets customized battery changed intent. */
/**
* Gets customized battery changed intent.
*/
public static Intent getCustomBatteryIntent(int plugged, int level, int scale, int status) {
Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_PLUGGED, plugged);
@@ -190,7 +207,9 @@ public class BatteryTestUtils {
return intent;
}
/** Configures the incompatible charger environment. */
/**
* Configures the incompatible charger environment.
*/
public static void setupIncompatibleEvent(
UsbPort mockUsbPort, UsbManager mockUsbManager, UsbPortStatus mockUsbPortStatus) {
final List<UsbPort> usbPorts = new ArrayList<>();
@@ -203,12 +222,16 @@ public class BatteryTestUtils {
.thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_OTHER});
}
/** Create an empty power anomaly event list proto. */
/**
* Create an empty power anomaly event list proto.
*/
public static PowerAnomalyEventList createEmptyPowerAnomalyEventList() {
return PowerAnomalyEventList.getDefaultInstance();
}
/** Create an non-empty power anomaly event list proto. */
/**
* Create an non-empty power anomaly event list proto.
*/
public static PowerAnomalyEventList createNonEmptyPowerAnomalyEventList() {
return PowerAnomalyEventList.newBuilder()
.addPowerAnomalyEvents(0, createAdaptiveBrightnessAnomalyEvent())
@@ -216,7 +239,9 @@ public class BatteryTestUtils {
.build();
}
/** Create a power anomaly event proto of adaptive brightness. */
/**
* Create a power anomaly event proto of adaptive brightness.
*/
public static PowerAnomalyEvent createAdaptiveBrightnessAnomalyEvent() {
return PowerAnomalyEvent.newBuilder()
.setEventId("BrightnessAnomaly")
@@ -231,7 +256,9 @@ public class BatteryTestUtils {
.build();
}
/** Create a power anomaly event proto of screen timeout. */
/**
* Create a power anomaly event proto of screen timeout.
*/
public static PowerAnomalyEvent createScreenTimeoutAnomalyEvent() {
return PowerAnomalyEvent.newBuilder()
.setEventId("ScreenTimeoutAnomaly")
@@ -245,4 +272,21 @@ public class BatteryTestUtils {
.build())
.build();
}
/**
* Create a power anomaly event proto of app anomaly.
*/
public static PowerAnomalyEvent createAppAnomalyEvent() {
return PowerAnomalyEvent.newBuilder()
.setEventId("AppAnomaly")
.setType(PowerAnomalyType.TYPE_APPS_ITEM)
.setKey(PowerAnomalyKey.KEY_APP)
.setScore(2.0f)
.setWarningItemInfo(WarningItemInfo.newBuilder()
.setTitleString("Chrome used more battery than usual in foreground")
.setMainButtonString("Check")
.setCancelButtonString("Got it")
.build())
.build();
}
}