Snap for 10649958 from a277f78789 to udc-qpr1-release

Change-Id: I657cd0bd60b31d38d62e11583eb2538dcc535efa
This commit is contained in:
Android Build Coastguard Worker
2023-08-12 03:19:25 +00:00
21 changed files with 501 additions and 158 deletions

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_tips_all_rounded_bg"/>
</ripple>

View File

@@ -12,36 +12,16 @@
android:id="@+id/tips_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/battery_tips_all_rounded_bg"
android:background="@drawable/battery_tips_all_rounded_bg_ripple"
android:orientation="vertical"
android:padding="24dp">
<LinearLayout
android:layout_width="match_parent"
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:src="@drawable/ic_battery_tips_lightbulb" />
<Space
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<ImageButton
android:id="@+id/dismiss_button"
style="@style/Banner.Dismiss.SettingsLib"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="0dp"
android:src="@drawable/ic_battery_tips_close_icon" />
</LinearLayout>
android:layout_gravity="center_vertical|start"
android:src="@drawable/ic_battery_tips_lightbulb" />
<TextView
android:id="@+id/title"
@@ -53,30 +33,40 @@
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/summary"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="8dp"
android:gravity="start"
android:maxLines="10"
android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" />
android:gravity="end">
<com.google.android.material.button.MaterialButton
android:id="@+id/action_button"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="8dp"
android:text="@string/battery_tips_card_action_button"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"
android:textStyle="bold"
app:strokeColor="?android:attr/colorAccent"
app:strokeWidth="1dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/dismiss_button"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:paddingHorizontal="16dp"
android:layout_marginEnd="8dp"
android:text="@string/battery_tips_card_dismiss_button"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/colorAccent"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/main_button"
style="@style/Widget.Material3.Button.OutlinedButton"
android:paddingHorizontal="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="@string/battery_tips_card_action_button"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"
android:textStyle="bold"
app:strokeColor="?android:attr/colorAccent"
app:strokeWidth="1dp" />
</LinearLayout>
</LinearLayout>
<Space

View File

@@ -1409,4 +1409,25 @@
<integer-array name="network_mode_3g_deprecated_carrier_id" translatable="false">
</integer-array>
<!-- The following 4 arrays are for power anomaly tips card. Please keep them the same size. -->
<string-array name="power_anomaly_keys" translatable="false">
<item>adaptive_brightness</item>
<item>screen_timeout</item>
</string-array>
<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>
</string-array>
<string-array name="power_anomaly_main_btn_strings">
<item>@string/battery_tips_card_action_button</item>
<item>@string/battery_tips_card_action_button</item>
</string-array>
<string-array name="power_anomaly_dismiss_btn_strings">
<item>@string/battery_tips_card_dismiss_button</item>
<item>@string/battery_tips_card_dismiss_button</item>
</string-array>
</resources>

View File

@@ -9651,23 +9651,14 @@
<string name="app_battery_usage_summary">Set battery usage for apps</string>
<!-- Label of action button in battery tips card [CHAR LIMIT=NONE] -->
<string name="battery_tips_card_action_button" translatable="false">Optimize</string>
<string name="battery_tips_card_action_button" translatable="false">View Settings</string>
<!-- Label of dismiss button in battery tips card [CHAR LIMIT=NONE] -->
<string name="battery_tips_card_dismiss_button" translatable="false">Got it</string>
<!-- Feedback card message in battery tips card [CHAR LIMIT=NONE] -->
<string name="battery_tips_card_feedback_info" translatable="false">Is this message helpful?</string>
<!-- Title of battery tips: adaptive brightness [CHAR LIMIT=NONE] -->
<string name="battery_tips_adaptive_brightness_title" translatable="false">Turn on adaptive brightness to extend battery life</string>
<!-- Summary of battery tips: adaptive brightness [CHAR LIMIT=NONE] -->
<string name="battery_tips_adaptive_brightness_summary" translatable="false">It will help reduce your daily battery drain by 10%</string>
<!-- Title of battery tips: reduce screen timeout [CHAR LIMIT=NONE] -->
<string name="battery_tips_screen_timeout_title" translatable="false">Reduce screen timeout to extend battery life</string>
<!-- Summary of battery tips: reduce screen timeout [CHAR LIMIT=NONE] -->
<string name="battery_tips_screen_timeout_summary" translatable="false">It will help reduce your daily battery drain by 10%</string>
<!-- Filter title for battery unrestricted[CHAR_LIMIT=50]-->
<string name="filter_battery_unrestricted_title">Unrestricted</string>

View File

@@ -22,6 +22,7 @@ import android.os.Bundle;
import android.util.ArrayMap;
import android.util.SparseIntArray;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
import com.android.settingslib.fuelgauge.Estimate;
import java.util.List;
@@ -147,7 +148,7 @@ public interface PowerUsageFeatureProvider {
/**
* Returns {@link Bundle} for settings anomaly detection result
*/
Bundle detectSettingsAnomaly(Context context, double displayDrain);
PowerAnomalyEventList detectSettingsAnomaly(Context context, double displayDrain);
/**
* Gets an intent for one time bypass charge limited to resume charging.

View File

@@ -21,13 +21,13 @@ import static com.android.settings.Utils.SYSTEMUI_PACKAGE_NAME;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Process;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseIntArray;
import com.android.internal.util.ArrayUtils;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
import com.android.settingslib.fuelgauge.Estimate;
import java.util.ArrayList;
@@ -82,7 +82,7 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
@Override
public boolean isBatteryTipsFeedbackEnabled() {
return true;
return false;
}
@Override
@@ -175,7 +175,7 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
public void insertSettingsData(Context context, double displayDrain) {}
@Override
public Bundle detectSettingsAnomaly(Context context, double displayDrain) {
public PowerAnomalyEventList detectSettingsAnomaly(Context context, double displayDrain) {
return null;
}

View File

@@ -105,10 +105,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
public interface OnBatteryTipsUpdatedListener {
/**
* The callback function for the battery tips card is updated.
* @param title the title of the battery tip card
* @param summary the summary of the battery tip card
* @param powerAnomalyEvent the power anomaly event with highest score
*/
void onBatteryTipsUpdated(String title, String summary);
void onBatteryTipsUpdated(PowerAnomalyEvent powerAnomalyEvent);
}
@@ -365,7 +364,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
slotUsageData, getSlotInformation(), isBatteryUsageMapNullOrEmpty());
if (mOnBatteryTipsUpdatedListener != null) {
mOnBatteryTipsUpdatedListener.onBatteryTipsUpdated(null, null);
// TODO: replace with a selected powerAnomalyEvent with highest score
mOnBatteryTipsUpdatedListener.onBatteryTipsUpdated(null);
}
}
return true;

View File

@@ -17,15 +17,19 @@
package com.android.settings.fuelgauge.batteryusage;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -40,12 +44,14 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
private final PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private MaterialButton mActionButton;
private ImageButton mDismissButton;
private ImageButton mThumbUpButton;
private ImageButton mThumbDownButton;
private CharSequence mTitle;
private CharSequence mSummary;
@VisibleForTesting
CharSequence mMainButtonLabel;
@VisibleForTesting
CharSequence mDismissButtonLabel;
@VisibleForTesting
String mDestinationComponentName;
@VisibleForTesting
int mSourceMetricsCategory;
public BatteryTipsCardPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -55,34 +61,65 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
.getPowerUsageFeatureProvider(context);
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
notifyChanged();
/**
* Update the label of main button in tips card.
*/
public void setMainButtonLabel(CharSequence label) {
if (!TextUtils.equals(mMainButtonLabel, label)) {
mMainButtonLabel = label;
notifyChanged();
}
}
@Override
public void setSummary(CharSequence summary) {
mSummary = summary;
notifyChanged();
/**
* Update the label of dismiss button in tips card.
*/
public void setDismissButtonLabel(CharSequence label) {
if (!TextUtils.equals(mDismissButtonLabel, label)) {
mDismissButtonLabel = label;
notifyChanged();
}
}
/**
* Update the info of target fragment launched by main button.
*/
public void setMainButtonLauncherInfo(final String destinationClassName,
final Integer sourceMetricsCategory) {
mDestinationComponentName = destinationClassName;
mSourceMetricsCategory = sourceMetricsCategory;
}
@Override
public void onClick(View view) {
// TODO: replace with the settings anomaly obtained from detectSettingsAnomaly();
final int viewId = view.getId();
if (viewId == R.id.main_button || viewId == R.id.tips_card) {
if (TextUtils.isEmpty(mDestinationComponentName)) {
return;
}
new SubSettingLauncher(getContext())
.setDestination(mDestinationComponentName)
.setSourceMetricsCategory(mSourceMetricsCategory)
.launch();
} else if (viewId == R.id.dismiss_button) {
setVisible(false);
}
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
((TextView) view.findViewById(R.id.title)).setText(mTitle);
((TextView) view.findViewById(R.id.summary)).setText(mSummary);
((TextView) view.findViewById(R.id.title)).setText(getTitle());
mActionButton = (MaterialButton) view.findViewById(R.id.action_button);
mActionButton.setOnClickListener(this);
mDismissButton = (ImageButton) view.findViewById(R.id.dismiss_button);
mDismissButton.setOnClickListener(this);
LinearLayout tipsCard = (LinearLayout) view.findViewById(R.id.tips_card);
tipsCard.setOnClickListener(this);
MaterialButton mainButton = (MaterialButton) view.findViewById(R.id.main_button);
mainButton.setOnClickListener(this);
mainButton.setText(mMainButtonLabel);
MaterialButton dismissButton = (MaterialButton) view.findViewById(R.id.dismiss_button);
dismissButton.setOnClickListener(this);
dismissButton.setText(mDismissButtonLabel);
if (!mPowerUsageFeatureProvider.isBatteryTipsFeedbackEnabled()) {
return;
@@ -91,9 +128,9 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
.setBackgroundResource(R.drawable.battery_tips_half_rounded_top_bg);
view.findViewById(R.id.feedback_card).setVisibility(View.VISIBLE);
mThumbUpButton = (ImageButton) view.findViewById(R.id.thumb_up);
mThumbUpButton.setOnClickListener(this);
mThumbDownButton = (ImageButton) view.findViewById(R.id.thumb_down);
mThumbDownButton.setOnClickListener(this);
ImageButton thumbUpButton = (ImageButton) view.findViewById(R.id.thumb_up);
thumbUpButton.setOnClickListener(this);
ImageButton thumbDownButton = (ImageButton) view.findViewById(R.id.thumb_down);
thumbDownButton.setOnClickListener(this);
}
}

View File

@@ -17,13 +17,18 @@
package com.android.settings.fuelgauge.batteryusage;
import android.content.Context;
import android.text.TextUtils;
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import java.util.function.Function;
/** Controls the update for battery tips card */
public class BatteryTipsController extends BasePreferenceController {
@@ -31,15 +36,23 @@ public class BatteryTipsController extends BasePreferenceController {
private static final String ROOT_PREFERENCE_KEY = "battery_tips_category";
private static final String CARD_PREFERENCE_KEY = "battery_tips_card";
private final PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private final String[] mPowerAnomalyKeys;
private Context mPrefContext;
private BatteryTipsCardPreference mCardPreference;
@VisibleForTesting
BatteryTipsCardPreference mCardPreference;
@VisibleForTesting
PowerUsageFeatureProvider mPowerUsageFeatureProvider;
public BatteryTipsController(Context context) {
super(context, ROOT_PREFERENCE_KEY);
mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context);
mPowerAnomalyKeys = context.getResources().getStringArray(R.array.power_anomaly_keys);
}
private boolean isTipsCardVisible() {
// TODO: compared with the timestamp of last user dismiss action in sharedPreference.
return mPowerUsageFeatureProvider.isBatteryTipsEnabled();
}
@Override
@@ -50,28 +63,89 @@ public class BatteryTipsController extends BasePreferenceController {
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPrefContext = screen.getContext();
mCardPreference = screen.findPreference(CARD_PREFERENCE_KEY);
}
/**
* Update the card visibility and contents.
* @param title a string not extend 2 lines.
* @param summary a string not extend 10 lines.
*/
// TODO: replace parameters with SettingsAnomaly Data Proto
public void handleBatteryTipsCardUpdated(String title, String summary) {
if (!mPowerUsageFeatureProvider.isBatteryTipsEnabled()) {
mCardPreference.setVisible(false);
return;
@VisibleForTesting
int getPowerAnomalyEventIndex(String powerAnomalyKey) {
for (int index = 0; index < mPowerAnomalyKeys.length; index++) {
if (mPowerAnomalyKeys[index].equals(powerAnomalyKey)) {
return index;
}
}
if (title == null || summary == null) {
mCardPreference.setVisible(false);
return;
}
mCardPreference.setTitle(title);
mCardPreference.setSummary(summary);
mCardPreference.setVisible(true);
return -1;
}
private <T> T getInfo(PowerAnomalyEvent powerAnomalyEvent,
Function<WarningBannerInfo, T> warningBannerInfoSupplier,
Function<WarningItemInfo, T> warningItemInfoSupplier) {
if (powerAnomalyEvent.hasWarningBannerInfo() && warningBannerInfoSupplier != null) {
return warningBannerInfoSupplier.apply(powerAnomalyEvent.getWarningBannerInfo());
} else if (powerAnomalyEvent.hasWarningItemInfo() && warningItemInfoSupplier != null) {
return warningItemInfoSupplier.apply(powerAnomalyEvent.getWarningItemInfo());
}
return null;
}
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;
}
if (resourceIndex >= 0) {
string = mContext.getResources().getStringArray(resourceId)[resourceIndex];
}
return string;
}
@VisibleForTesting
void handleBatteryTipsCardUpdated(PowerAnomalyEvent powerAnomalyEvent) {
if (!isTipsCardVisible()) {
mCardPreference.setVisible(false);
return;
}
if (powerAnomalyEvent == null) {
mCardPreference.setVisible(false);
return;
}
// Get card preference strings and navigate fragment info
final int index = getPowerAnomalyEventIndex(powerAnomalyEvent.getKey());
String titleString = getString(powerAnomalyEvent, WarningBannerInfo::getTitleString,
WarningItemInfo::getTitleString, R.array.power_anomaly_titles, index);
if (titleString.isEmpty()) {
mCardPreference.setVisible(false);
return;
}
String mainBtnString = getString(powerAnomalyEvent,
WarningBannerInfo::getMainButtonString, WarningItemInfo::getMainButtonString,
R.array.power_anomaly_main_btn_strings, index);
String dismissBtnString = getString(powerAnomalyEvent,
WarningBannerInfo::getCancelButtonString, WarningItemInfo::getCancelButtonString,
R.array.power_anomaly_dismiss_btn_strings, index);
String destinationClassName = getString(powerAnomalyEvent,
WarningBannerInfo::getMainButtonDestination,
WarningItemInfo::getMainButtonDestination,
-1, -1);
Integer sourceMetricsCategory = getInfo(powerAnomalyEvent,
WarningBannerInfo::getMainButtonSourceMetricsCategory,
WarningItemInfo::getMainButtonSourceMetricsCategory);
// Updated card preference and main button fragment launcher
mCardPreference.setTitle(titleString);
mCardPreference.setMainButtonLabel(mainBtnString);
mCardPreference.setDismissButtonLabel(dismissBtnString);
mCardPreference.setMainButtonLauncherInfo(destinationClassName, sourceMetricsCategory);
mCardPreference.setVisible(true);
}
}

View File

@@ -17,6 +17,7 @@ package com.android.settings.fuelgauge.batteryusage;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManager;
import android.content.ContentValues;
@@ -185,7 +186,8 @@ public final class ConvertUtils {
/** Converts to {@link AppUsageEvent} from {@link Event} */
@Nullable
public static AppUsageEvent convertToAppUsageEvent(
Context context, final Event event, final long userId) {
Context context, IUsageStatsManager usageStatsManager, final Event event,
final long userId) {
final String packageName = event.getPackageName();
if (packageName == null) {
// See b/190609174: Event package names should never be null, but sometimes they are.
@@ -210,7 +212,8 @@ public final class ConvertUtils {
}
final String effectivePackageName =
getEffectivePackageName(context, packageName, taskRootPackageName);
getEffectivePackageName(
context, usageStatsManager, packageName, taskRootPackageName);
try {
final long uid = context
.getPackageManager()
@@ -326,8 +329,9 @@ public final class ConvertUtils {
*/
@VisibleForTesting
static String getEffectivePackageName(
Context context, final String packageName, final String taskRootPackageName) {
final int usageSource = getUsageSource(context);
Context context, IUsageStatsManager usageStatsManager, final String packageName,
final String taskRootPackageName) {
final int usageSource = getUsageSource(context, usageStatsManager);
switch (usageSource) {
case UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY:
return !TextUtils.isEmpty(taskRootPackageName)
@@ -372,9 +376,9 @@ public final class ConvertUtils {
}
}
private static int getUsageSource(Context context) {
private static int getUsageSource(Context context, IUsageStatsManager usageStatsManager) {
if (sUsageSource == EMPTY_USAGE_SOURCE) {
sUsageSource = DatabaseUtils.getUsageSource(context);
sUsageSource = DatabaseUtils.getUsageSource(context, usageStatsManager);
}
return sUsageSource;
}

View File

@@ -32,6 +32,7 @@ import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
import android.os.UserHandle;
@@ -108,6 +109,11 @@ public final class DataProcessor {
@VisibleForTesting
static Set<String> sTestSystemAppsPackageNames;
@VisibleForTesting
static IUsageStatsManager sUsageStatsManager =
IUsageStatsManager.Stub.asInterface(
ServiceManager.getService(Context.USAGE_STATS_SERVICE));
public static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER =
"CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER";
@@ -338,7 +344,8 @@ public final class DataProcessor {
break;
}
final AppUsageEvent appUsageEvent =
ConvertUtils.convertToAppUsageEvent(context, event, userId);
ConvertUtils.convertToAppUsageEvent(
context, sUsageStatsManager, event, userId);
if (appUsageEvent != null) {
numEventsFetched++;
appUsageEventList.add(appUsageEvent);
@@ -694,6 +701,7 @@ public final class DataProcessor {
final long eventUserId = firstEvent.getUserId();
final String packageName = getEffectivePackageName(
context,
sUsageStatsManager,
firstEvent.getPackageName(),
firstEvent.getTaskRootPackageName());
usageEvents.addAll(deviceEvents);
@@ -966,7 +974,7 @@ public final class DataProcessor {
final long startTime = DatabaseUtils.getAppUsageStartTimestampOfUser(
context, userID, earliestTimestamp);
return loadAppUsageEventsForUserFromService(
DatabaseUtils.sUsageStatsManager, startTime, now, userID, callingPackage);
sUsageStatsManager, startTime, now, userID, callingPackage);
}
@Nullable

View File

@@ -31,7 +31,6 @@ import android.os.BatteryUsageStats;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserManager;
import android.util.Log;
@@ -117,11 +116,6 @@ public final class DatabaseUtils {
@VisibleForTesting
static Supplier<Cursor> sFakeSupplier;
@VisibleForTesting
static IUsageStatsManager sUsageStatsManager =
IUsageStatsManager.Stub.asInterface(
ServiceManager.getService(Context.USAGE_STATS_SERVICE));
private DatabaseUtils() {
}
@@ -488,7 +482,7 @@ public final class DatabaseUtils {
*
* @see UsageStatsManager#getUsageSource()
*/
static int getUsageSource(Context context) {
static int getUsageSource(Context context, IUsageStatsManager usageStatsManager) {
final SharedPreferences sharedPreferences = getSharedPreferences(context);
if (sharedPreferences != null && sharedPreferences.contains(KEY_LAST_USAGE_SOURCE)) {
return sharedPreferences
@@ -497,7 +491,7 @@ public final class DatabaseUtils {
int usageSource = ConvertUtils.DEFAULT_USAGE_SOURCE;
try {
usageSource = sUsageStatsManager.getUsageSource();
usageSource = usageStatsManager.getUsageSource();
} catch (RemoteException e) {
Log.e(TAG, "Failed to getUsageSource", e);
}

View File

@@ -4,14 +4,19 @@ option java_multiple_files = true;
option java_package = "com.android.settings.fuelgauge.batteryusage";
option java_outer_classname = "PowerAnomalyEventProto";
message PowerAnomalyEventList {
repeated PowerAnomalyEvent power_anomaly_events = 1;
}
message PowerAnomalyEvent {
optional int64 timestamp = 1;
optional string type = 2; // e.g. settings, apps
optional string key = 3; // e.g. brightness, significant_increase
optional float score = 4;
optional string event_id = 1;
optional int64 timestamp = 2;
optional string type = 3; // e.g. settings, apps
optional string key = 4; // e.g. brightness, significant_increase
optional float score = 5;
oneof info {
WarningBannerInfo warning_banner_info = 5;
WarningItemInfo warning_item_info = 6;
WarningBannerInfo warning_banner_info = 6;
WarningItemInfo warning_item_info = 7;
}
}
@@ -19,9 +24,11 @@ message WarningBannerInfo {
optional string title_string = 1;
optional string description_string = 2;
optional string main_button_string = 3;
optional string main_button_action = 4;
optional string cancel_button_string = 5;
optional string cancel_button_action = 6;
// Used in the SubSettingLauncher.setDestination().
optional string main_button_destination = 4;
// Used in the SubSettingLauncher.setSourceMetricsCategory().
optional int32 main_button_source_metrics_category = 5;
optional string cancel_button_string = 6;
}
message WarningItemInfo {
@@ -31,7 +38,9 @@ message WarningItemInfo {
optional string title_string = 4;
optional string description_string = 5;
optional string main_button_string = 6;
optional string main_button_action = 7;
optional string cancel_button_string = 8;
optional string cancel_button_action = 9;
// Used in the SubSettingLauncher.setDestination().
optional string main_button_destination = 7;
// Used in the SubSettingLauncher.setSourceMetricsCategory().
optional int32 main_button_source_metrics_category = 8;
optional string cancel_button_string = 9;
}

View File

@@ -73,8 +73,8 @@ public class PowerUsageFeatureProviderImplTest {
}
@Test
public void testIsBatteryTipsFeedbackEnabled_returnTrue() {
assertThat(mPowerFeatureProvider.isBatteryTipsFeedbackEnabled()).isTrue();
public void testIsBatteryTipsFeedbackEnabled_returnFalse() {
assertThat(mPowerFeatureProvider.isBatteryTipsFeedbackEnabled()).isFalse();
}
@Test
public void testGetBatteryUsageListConsumePowerThreshold_return0() {

View File

@@ -35,11 +35,14 @@ public final class BatteryTipsCardPreferenceTest {
private Context mContext;
private BatteryTipsCardPreference mBatteryTipsCardPreference;
private BatteryTipsController mBatteryTipsController;
@Before
public void setUp() {
mContext = spy(RuntimeEnvironment.application);
mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /*attrs=*/ null);
mBatteryTipsController = new BatteryTipsController(mContext);
mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference;
}
@Test

View File

@@ -0,0 +1,149 @@
/*
* 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.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.os.LocaleList;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.testutils.BatteryTestUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Locale;
import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class)
public final class BatteryTipsControllerTest {
private Context mContext;
private BatteryTipsController mBatteryTipsController;
@Mock
private BatteryTipsCardPreference mBatteryTipsCardPreference;
@Mock
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Locale.setDefault(new Locale("en_US"));
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
mContext = spy(RuntimeEnvironment.application);
final Resources resources = spy(mContext.getResources());
resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US")));
doReturn(resources).when(mContext).getResources();
mBatteryTipsController = new BatteryTipsController(mContext);
mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference;
mBatteryTipsController.mPowerUsageFeatureProvider = mPowerUsageFeatureProvider;
}
@Test
public void parsePowerAnomalyKey_preDefinedKeys_returnTrue() {
final String[] keys = {"adaptive_brightness", "screen_timeout"};
for (int index = 0; index < keys.length; index++) {
assertThat(mBatteryTipsController.getPowerAnomalyEventIndex(keys[index]))
.isEqualTo(index);
}
}
@Test
public void parsePowerAnomalyKey_unknownKey_returnTrue() {
final String key = "unknown_key_for_test";
assertThat(mBatteryTipsController.getPowerAnomalyEventIndex(key)).isEqualTo(-1);
}
@Test
public void handleBatteryTipsCardUpdated_null_hidePreference() {
mBatteryTipsController.handleBatteryTipsCardUpdated(/* powerAnomalyEvents= */ null);
verify(mBatteryTipsCardPreference).setVisible(false);
}
@Test
public void handleBatteryTipsCardUpdated_adaptiveBrightnessAnomaly_showAnomaly() {
PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
when(mPowerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
mBatteryTipsController.handleBatteryTipsCardUpdated(event);
// Check pre-defined string
verify(mBatteryTipsCardPreference).setTitle(
"Turn on adaptive brightness to extend battery life");
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
// Check proto info
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
"com.android.settings.display.AutoBrightnessSettings",
1381);
verify(mBatteryTipsCardPreference).setVisible(true);
}
@Test
public void handleBatteryTipsCardUpdated_screenTimeoutAnomaly_showAnomaly() {
PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent();
when(mPowerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
mBatteryTipsController.handleBatteryTipsCardUpdated(event);
verify(mBatteryTipsCardPreference).setTitle("Reduce screen timeout to extend battery life");
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
"com.android.settings.display.ScreenTimeoutSettings",
1852);
verify(mBatteryTipsCardPreference).setVisible(true);
}
@Test
public void handleBatteryTipsCardUpdated_screenTimeoutAnomalyHasTitle_showAnomaly() {
PowerAnomalyEvent event = BatteryTestUtils.createScreenTimeoutAnomalyEvent();
String testTitle = "TestTitle";
event = event.toBuilder()
.setWarningBannerInfo(
event.getWarningBannerInfo().toBuilder()
.setTitleString(testTitle)
.build())
.build();
when(mPowerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
mBatteryTipsController.handleBatteryTipsCardUpdated(event);
verify(mBatteryTipsCardPreference).setTitle(testTitle);
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
"com.android.settings.display.ScreenTimeoutSettings",
1852);
verify(mBatteryTipsCardPreference).setVisible(true);
}
}

View File

@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.content.ContentValues;
@@ -61,6 +62,8 @@ public final class ConvertUtilsTest {
private BatteryUsageStats mBatteryUsageStats;
@Mock
private BatteryEntry mMockBatteryEntry;
@Mock
private IUsageStatsManager mUsageStatsManager;
@Before
public void setUp() {
@@ -68,6 +71,7 @@ public final class ConvertUtilsTest {
mContext = spy(RuntimeEnvironment.application);
ConvertUtils.sUsageSource = ConvertUtils.EMPTY_USAGE_SOURCE;
when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
DataProcessor.sUsageStatsManager = mUsageStatsManager;
}
@Test
@@ -299,7 +303,7 @@ public final class ConvertUtilsTest {
final long userId = 2;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
mContext, event, userId);
mContext, mUsageStatsManager, event, userId);
assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.ACTIVITY_RESUMED);
assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
@@ -320,7 +324,7 @@ public final class ConvertUtilsTest {
final long userId = 1;
final AppUsageEvent appUsageEvent =
ConvertUtils.convertToAppUsageEvent(mContext, event, userId);
ConvertUtils.convertToAppUsageEvent(mContext, mUsageStatsManager, event, userId);
assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.DEVICE_SHUTDOWN);
assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
@@ -336,7 +340,8 @@ public final class ConvertUtilsTest {
event.mPackage = null;
final AppUsageEvent appUsageEvent =
ConvertUtils.convertToAppUsageEvent(mContext, event, /*userId=*/ 0);
ConvertUtils.convertToAppUsageEvent(
mContext, mUsageStatsManager, event, /*userId=*/ 0);
assertThat(appUsageEvent).isNull();
}
@@ -352,7 +357,7 @@ public final class ConvertUtilsTest {
final long userId = 1;
final AppUsageEvent appUsageEvent =
ConvertUtils.convertToAppUsageEvent(mContext, event, userId);
ConvertUtils.convertToAppUsageEvent(mContext, mUsageStatsManager, event, userId);
assertThat(appUsageEvent).isNull();
}
@@ -453,7 +458,7 @@ public final class ConvertUtilsTest {
final String taskRootPackageName = "com.android.settings2";
assertThat(ConvertUtils.getEffectivePackageName(
mContext, packageName, taskRootPackageName))
mContext, mUsageStatsManager, packageName, taskRootPackageName))
.isEqualTo(packageName);
}
@@ -463,7 +468,7 @@ public final class ConvertUtilsTest {
final String taskRootPackageName = "com.android.settings2";
assertThat(ConvertUtils.getEffectivePackageName(
mContext, packageName, taskRootPackageName))
mContext, mUsageStatsManager, packageName, taskRootPackageName))
.isEqualTo(packageName);
}
@@ -474,7 +479,7 @@ public final class ConvertUtilsTest {
final String taskRootPackageName = "com.android.settings2";
assertThat(ConvertUtils.getEffectivePackageName(
mContext, packageName, taskRootPackageName))
mContext, mUsageStatsManager, packageName, taskRootPackageName))
.isEqualTo(taskRootPackageName);
}
@@ -484,10 +489,10 @@ public final class ConvertUtilsTest {
final String packageName = "com.android.settings1";
assertThat(ConvertUtils.getEffectivePackageName(
mContext, packageName, /*taskRootPackageName=*/ null))
mContext, mUsageStatsManager, packageName, /*taskRootPackageName=*/ null))
.isEqualTo(packageName);
assertThat(ConvertUtils.getEffectivePackageName(
mContext, packageName, /*taskRootPackageName=*/ ""))
mContext, mUsageStatsManager, packageName, /*taskRootPackageName=*/ ""))
.isEqualTo(packageName);
}
}

View File

@@ -72,7 +72,7 @@ public final class DataProcessManagerTest {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
DataProcessor.sUsageStatsManager = mUsageStatsManager;
doReturn(mContext).when(mContext).getApplicationContext();
doReturn(mUserManager)
.when(mContext)

View File

@@ -93,7 +93,7 @@ public final class DataProcessorTest {
mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
DataProcessor.sTestSystemAppsPackageNames = Set.of();
DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
DataProcessor.sUsageStatsManager = mUsageStatsManager;
doReturn(mIntent).when(mContext).registerReceiver(
isA(BroadcastReceiver.class), isA(IntentFilter.class));
doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt());

View File

@@ -85,7 +85,7 @@ public final class DatabaseUtilsTest {
doReturn(mPackageManager).when(mMockContext).getPackageManager();
doReturn(mPackageManager).when(mContext).getPackageManager();
DatabaseUtils.getSharedPreferences(mContext).edit().clear().apply();
DatabaseUtils.sUsageStatsManager = mUsageStatsManager;
DataProcessor.sUsageStatsManager = mUsageStatsManager;
}
@Test
@@ -466,7 +466,7 @@ public final class DatabaseUtilsTest {
.putInt(DatabaseUtils.KEY_LAST_USAGE_SOURCE, USAGE_SOURCE_TASK_ROOT_ACTIVITY)
.apply();
assertThat(DatabaseUtils.getUsageSource(mContext))
assertThat(DatabaseUtils.getUsageSource(mContext, mUsageStatsManager))
.isEqualTo(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
}
@@ -474,7 +474,7 @@ public final class DatabaseUtilsTest {
public void getUsageSource_notHasData_writeLoadedData() throws RemoteException {
when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
assertThat(DatabaseUtils.getUsageSource(mContext))
assertThat(DatabaseUtils.getUsageSource(mContext, mUsageStatsManager))
.isEqualTo(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
assertThat(
DatabaseUtils
@@ -487,7 +487,7 @@ public final class DatabaseUtilsTest {
public void getUsageSource_throwException_writeDefaultData() throws RemoteException {
when(mUsageStatsManager.getUsageSource()).thenThrow(new RemoteException());
assertThat(DatabaseUtils.getUsageSource(mContext))
assertThat(DatabaseUtils.getUsageSource(mContext, mUsageStatsManager))
.isEqualTo(USAGE_SOURCE_CURRENT_ACTIVITY);
assertThat(
DatabaseUtils

View File

@@ -18,6 +18,7 @@ package com.android.settings.testutils;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbManager;
@@ -25,11 +26,17 @@ import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
import android.os.BatteryManager;
import android.os.UserManager;
import androidx.room.Room;
import com.android.settings.display.AutoBrightnessSettings;
import com.android.settings.display.ScreenTimeoutSettings;
import com.android.settings.fuelgauge.batteryusage.BatteryInformation;
import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
import com.android.settings.fuelgauge.batteryusage.DeviceBatteryState;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEvent;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
import com.android.settings.fuelgauge.batteryusage.WarningBannerInfo;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
@@ -193,4 +200,33 @@ public class BatteryTestUtils {
when(mockUsbPortStatus.getComplianceWarnings())
.thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_OTHER});
}
/** Create an empty power anomaly event list proto. */
public static PowerAnomalyEventList createEmptyPowerAnomalyEventList() {
return PowerAnomalyEventList.getDefaultInstance();
}
/** Create a power anomaly event proto of adaptive brightness. */
public static PowerAnomalyEvent createAdaptiveBrightnessAnomalyEvent() {
return PowerAnomalyEvent.newBuilder()
.setType("settings banner")
.setKey("adaptive_brightness")
.setWarningBannerInfo(WarningBannerInfo.newBuilder()
.setMainButtonDestination(AutoBrightnessSettings.class.getName())
.setMainButtonSourceMetricsCategory(SettingsEnums.SETTINGS_AUTO_BRIGHTNESS)
.build())
.build();
}
/** Create a power anomaly event proto of screen timeout. */
public static PowerAnomalyEvent createScreenTimeoutAnomalyEvent() {
return PowerAnomalyEvent.newBuilder()
.setType("settings banner")
.setKey("screen_timeout")
.setWarningBannerInfo(WarningBannerInfo.newBuilder()
.setMainButtonDestination(ScreenTimeoutSettings.class.getName())
.setMainButtonSourceMetricsCategory(SettingsEnums.SCREEN_TIMEOUT)
.build())
.build();
}
}