1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.fuelgauge.batteryusage;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.provider.Settings;
22 import android.text.TextUtils;
23 import android.util.Log;
24 import android.util.Pair;
25 
26 import androidx.annotation.Nullable;
27 
28 import com.android.settings.R;
29 import com.android.settings.SettingsActivity;
30 import com.android.settings.core.SubSettingLauncher;
31 
32 import java.util.function.Function;
33 
34 final class AnomalyEventWrapper {
35     private static final String TAG = "AnomalyEventWrapper";
36 
37     private final Context mContext;
38     private final PowerAnomalyEvent mPowerAnomalyEvent;
39 
40     private final int mCardStyleId;
41     private final int mResourceIndex;
42 
43     private SubSettingLauncher mSubSettingLauncher = null;
44     private Pair<Integer, Integer> mHighlightSlotPair = null;
45     private BatteryDiffEntry mRelatedBatteryDiffEntry = null;
46 
AnomalyEventWrapper(Context context, PowerAnomalyEvent powerAnomalyEvent)47     AnomalyEventWrapper(Context context, PowerAnomalyEvent powerAnomalyEvent) {
48         mContext = context;
49         mPowerAnomalyEvent = powerAnomalyEvent;
50         // Set basic battery tips card info
51         mCardStyleId = mPowerAnomalyEvent.getType().getNumber();
52         mResourceIndex = mPowerAnomalyEvent.getKey().getNumber();
53     }
54 
getInfo( @ullable Function<WarningBannerInfo, T> warningBannerInfoSupplier, @Nullable Function<WarningItemInfo, T> warningItemInfoSupplier)55     private <T> T getInfo(
56             @Nullable Function<WarningBannerInfo, T> warningBannerInfoSupplier,
57             @Nullable Function<WarningItemInfo, T> warningItemInfoSupplier) {
58         if (warningBannerInfoSupplier != null && mPowerAnomalyEvent.hasWarningBannerInfo()) {
59             return warningBannerInfoSupplier.apply(mPowerAnomalyEvent.getWarningBannerInfo());
60         } else if (warningItemInfoSupplier != null && mPowerAnomalyEvent.hasWarningItemInfo()) {
61             return warningItemInfoSupplier.apply(mPowerAnomalyEvent.getWarningItemInfo());
62         }
63         return null;
64     }
65 
getResourceId(int resourceId, int resourceIndex, String defType)66     private int getResourceId(int resourceId, int resourceIndex, String defType) {
67         final String key = getStringFromArrayResource(resourceId, resourceIndex);
68         return TextUtils.isEmpty(key)
69                 ? 0
70                 : mContext.getResources().getIdentifier(key, defType, mContext.getPackageName());
71     }
72 
getString( Function<WarningBannerInfo, String> warningBannerInfoSupplier, Function<WarningItemInfo, String> warningItemInfoSupplier, int resourceId, int resourceIndex)73     private String getString(
74             Function<WarningBannerInfo, String> warningBannerInfoSupplier,
75             Function<WarningItemInfo, String> warningItemInfoSupplier,
76             int resourceId,
77             int resourceIndex) {
78         final String string = getInfo(warningBannerInfoSupplier, warningItemInfoSupplier);
79         return (!TextUtils.isEmpty(string) || resourceId <= 0)
80                 ? string
81                 : getStringFromArrayResource(resourceId, resourceIndex);
82     }
83 
getStringFromArrayResource(int resourceId, int resourceIndex)84     private String getStringFromArrayResource(int resourceId, int resourceIndex) {
85         if (resourceId <= 0 || resourceIndex < 0) {
86             return null;
87         }
88         final String[] stringArray = mContext.getResources().getStringArray(resourceId);
89         return (resourceIndex >= 0 && resourceIndex < stringArray.length)
90                 ? stringArray[resourceIndex]
91                 : null;
92     }
93 
setRelatedBatteryDiffEntry(BatteryDiffEntry batteryDiffEntry)94     void setRelatedBatteryDiffEntry(BatteryDiffEntry batteryDiffEntry) {
95         mRelatedBatteryDiffEntry = batteryDiffEntry;
96     }
97 
getAnomalyKeyNumber()98     int getAnomalyKeyNumber() {
99         return mPowerAnomalyEvent.getKey().getNumber();
100     }
101 
getEventId()102     String getEventId() {
103         return mPowerAnomalyEvent.hasEventId() ? mPowerAnomalyEvent.getEventId() : null;
104     }
105 
getIconResId()106     int getIconResId() {
107         return getResourceId(R.array.battery_tips_card_icons, mCardStyleId, "drawable");
108     }
109 
getColorResId()110     int getColorResId() {
111         return getResourceId(R.array.battery_tips_card_colors, mCardStyleId, "color");
112     }
113 
getTitleString()114     String getTitleString() {
115         final String titleStringFromProto =
116                 getInfo(WarningBannerInfo::getTitleString, WarningItemInfo::getTitleString);
117         if (!TextUtils.isEmpty(titleStringFromProto)) {
118             return titleStringFromProto;
119         }
120         final int titleFormatResId =
121                 getResourceId(R.array.power_anomaly_title_ids, mResourceIndex, "string");
122         if (mPowerAnomalyEvent.hasWarningBannerInfo()) {
123             return mContext.getString(titleFormatResId);
124         } else if (mPowerAnomalyEvent.hasWarningItemInfo() && mRelatedBatteryDiffEntry != null) {
125             final String appLabel = mRelatedBatteryDiffEntry.getAppLabel();
126             return mContext.getString(titleFormatResId, appLabel);
127         }
128         return null;
129     }
130 
getMainBtnString()131     String getMainBtnString() {
132         return getString(
133                 WarningBannerInfo::getMainButtonString,
134                 WarningItemInfo::getMainButtonString,
135                 R.array.power_anomaly_main_btn_strings,
136                 mResourceIndex);
137     }
138 
getDismissBtnString()139     String getDismissBtnString() {
140         return getString(
141                 WarningBannerInfo::getCancelButtonString,
142                 WarningItemInfo::getCancelButtonString,
143                 R.array.power_anomaly_dismiss_btn_strings,
144                 mResourceIndex);
145     }
146 
getAnomalyHintString()147     String getAnomalyHintString() {
148         final String anomalyHintStringFromProto =
149                 getInfo(null, WarningItemInfo::getWarningInfoString);
150         return TextUtils.isEmpty(anomalyHintStringFromProto)
151                 ? getStringFromArrayResource(R.array.power_anomaly_hint_messages, mResourceIndex)
152                 : anomalyHintStringFromProto;
153     }
154 
getAnomalyHintPrefKey()155     String getAnomalyHintPrefKey() {
156         return getInfo(null, WarningItemInfo::getAnomalyHintPrefKey);
157     }
158 
getDismissRecordKey()159     String getDismissRecordKey() {
160         return mPowerAnomalyEvent.getDismissRecordKey();
161     }
162 
hasAnomalyEntryKey()163     boolean hasAnomalyEntryKey() {
164         return getAnomalyEntryKey() != null;
165     }
166 
getAnomalyEntryKey()167     String getAnomalyEntryKey() {
168         return mPowerAnomalyEvent.hasWarningItemInfo()
169                         && mPowerAnomalyEvent.getWarningItemInfo().hasItemKey()
170                 ? mPowerAnomalyEvent.getWarningItemInfo().getItemKey()
171                 : null;
172     }
173 
hasSubSettingLauncher()174     boolean hasSubSettingLauncher() {
175         if (mSubSettingLauncher == null) {
176             mSubSettingLauncher = getSubSettingLauncher();
177         }
178         return mSubSettingLauncher != null;
179     }
180 
getSubSettingLauncher()181     SubSettingLauncher getSubSettingLauncher() {
182         if (mSubSettingLauncher != null) {
183             return mSubSettingLauncher;
184         }
185         final String destinationClassName =
186                 getInfo(WarningBannerInfo::getMainButtonDestination, null);
187         if (!TextUtils.isEmpty(destinationClassName)) {
188             final Integer sourceMetricsCategory =
189                     getInfo(WarningBannerInfo::getMainButtonSourceMetricsCategory, null);
190             final String preferenceHighlightKey =
191                     getInfo(WarningBannerInfo::getMainButtonSourceHighlightKey, null);
192             Bundle arguments = Bundle.EMPTY;
193             if (!TextUtils.isEmpty(preferenceHighlightKey)) {
194                 arguments = new Bundle(1);
195                 arguments.putString(
196                         SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, preferenceHighlightKey);
197             }
198             mSubSettingLauncher =
199                     new SubSettingLauncher(mContext)
200                             .setDestination(destinationClassName)
201                             .setSourceMetricsCategory(sourceMetricsCategory)
202                             .setArguments(arguments);
203         }
204         return mSubSettingLauncher;
205     }
206 
hasHighlightSlotPair(BatteryLevelData batteryLevelData)207     boolean hasHighlightSlotPair(BatteryLevelData batteryLevelData) {
208         if (mHighlightSlotPair == null) {
209             mHighlightSlotPair = getHighlightSlotPair(batteryLevelData);
210         }
211         return mHighlightSlotPair != null;
212     }
213 
getHighlightSlotPair(BatteryLevelData batteryLevelData)214     Pair<Integer, Integer> getHighlightSlotPair(BatteryLevelData batteryLevelData) {
215         if (mHighlightSlotPair != null) {
216             return mHighlightSlotPair;
217         }
218         if (!mPowerAnomalyEvent.hasWarningItemInfo()) {
219             return null;
220         }
221         final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo();
222         final Long startTimestamp =
223                 warningItemInfo.hasStartTimestamp() ? warningItemInfo.getStartTimestamp() : null;
224         final Long endTimestamp =
225                 warningItemInfo.hasEndTimestamp() ? warningItemInfo.getEndTimestamp() : null;
226         if (startTimestamp != null && endTimestamp != null) {
227             mHighlightSlotPair =
228                     batteryLevelData.getIndexByTimestamps(startTimestamp, endTimestamp);
229             if (mHighlightSlotPair.first == BatteryChartViewModel.SELECTED_INDEX_INVALID
230                     || mHighlightSlotPair.second == BatteryChartViewModel.SELECTED_INDEX_INVALID) {
231                 // Drop invalid mHighlightSlotPair index
232                 mHighlightSlotPair = null;
233             }
234         }
235         return mHighlightSlotPair;
236     }
237 
updateTipsCardPreference(BatteryTipsCardPreference preference)238     boolean updateTipsCardPreference(BatteryTipsCardPreference preference) {
239         final String titleString = getTitleString();
240         if (TextUtils.isEmpty(titleString)) {
241             return false;
242         }
243         preference.setTitle(titleString);
244         preference.setIconResourceId(getIconResId());
245         preference.setButtonColorResourceId(getColorResId());
246         preference.setMainButtonLabel(getMainBtnString());
247         preference.setDismissButtonLabel(getDismissBtnString());
248         return true;
249     }
250 
launchSubSetting()251     boolean launchSubSetting() {
252         if (!hasSubSettingLauncher()) {
253             return false;
254         }
255         // Navigate to sub setting page
256         mSubSettingLauncher.launch();
257         return true;
258     }
259 
updateSystemSettingsIfAvailable()260     boolean updateSystemSettingsIfAvailable() {
261         final String settingsName =
262                 getInfo(WarningBannerInfo::getMainButtonConfigSettingsName, null);
263         final Integer settingsValue =
264                 getInfo(WarningBannerInfo::getMainButtonConfigSettingsValue, null);
265         if (TextUtils.isEmpty(settingsName) || settingsValue == null) {
266             Log.d(TAG, "Failed to update settings due to invalid key or value");
267             return false;
268         }
269 
270         try {
271             Settings.System.putInt(mContext.getContentResolver(), settingsName, settingsValue);
272             Log.d(
273                     TAG,
274                     String.format(
275                             "Update settings name=%s to value=%d", settingsName, settingsValue));
276             return true;
277         } catch (SecurityException e) {
278             Log.w(
279                     TAG,
280                     String.format(
281                             "Failed to update settings name=%s to value=%d",
282                             settingsName, settingsValue),
283                     e);
284             return false;
285         }
286     }
287 }
288