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.graphics.drawable.Drawable; 21 import android.os.Bundle; 22 import android.provider.Settings; 23 import android.text.TextUtils; 24 import android.util.Log; 25 import android.util.Pair; 26 27 import androidx.annotation.Nullable; 28 29 import com.android.settings.R; 30 import com.android.settings.SettingsActivity; 31 import com.android.settings.core.SubSettingLauncher; 32 import com.android.settingslib.widget.BannerMessagePreference; 33 import com.android.settingslib.widget.BannerMessagePreference.AttentionLevel; 34 35 import java.util.function.Function; 36 37 class AnomalyEventWrapper { 38 private static final String TAG = "AnomalyEventWrapper"; 39 40 private final Context mContext; 41 private final PowerAnomalyEvent mPowerAnomalyEvent; 42 private final AttentionLevel mAttentionLevel; 43 44 private final int mCardStyleId; 45 private final int mResourceIndex; 46 47 private SubSettingLauncher mSubSettingLauncher = null; 48 private Pair<Integer, Integer> mHighlightSlotPair = null; 49 private BatteryDiffEntry mRelatedBatteryDiffEntry = null; 50 AnomalyEventWrapper(Context context, PowerAnomalyEvent powerAnomalyEvent)51 AnomalyEventWrapper(Context context, PowerAnomalyEvent powerAnomalyEvent) { 52 mContext = context; 53 mPowerAnomalyEvent = powerAnomalyEvent; 54 // Set basic battery tips card info 55 mCardStyleId = mPowerAnomalyEvent.getType().getNumber(); 56 mResourceIndex = mPowerAnomalyEvent.getKey().getNumber(); 57 mAttentionLevel = mCardStyleId == 0 ? AttentionLevel.NORMAL : AttentionLevel.MEDIUM; 58 } 59 getInfo( @ullable Function<WarningBannerInfo, T> warningBannerInfoSupplier, @Nullable Function<WarningItemInfo, T> warningItemInfoSupplier)60 private <T> T getInfo( 61 @Nullable Function<WarningBannerInfo, T> warningBannerInfoSupplier, 62 @Nullable Function<WarningItemInfo, T> warningItemInfoSupplier) { 63 if (warningBannerInfoSupplier != null && mPowerAnomalyEvent.hasWarningBannerInfo()) { 64 return warningBannerInfoSupplier.apply(mPowerAnomalyEvent.getWarningBannerInfo()); 65 } else if (warningItemInfoSupplier != null && mPowerAnomalyEvent.hasWarningItemInfo()) { 66 return warningItemInfoSupplier.apply(mPowerAnomalyEvent.getWarningItemInfo()); 67 } 68 return null; 69 } 70 getResourceId(int resourceId, int resourceIndex, String defType)71 private int getResourceId(int resourceId, int resourceIndex, String defType) { 72 final String key = getStringFromArrayResource(resourceId, resourceIndex); 73 return TextUtils.isEmpty(key) 74 ? 0 75 : mContext.getResources().getIdentifier(key, defType, mContext.getPackageName()); 76 } 77 getString( Function<WarningBannerInfo, String> warningBannerInfoSupplier, Function<WarningItemInfo, String> warningItemInfoSupplier, int resourceId, int resourceIndex)78 private String getString( 79 Function<WarningBannerInfo, String> warningBannerInfoSupplier, 80 Function<WarningItemInfo, String> warningItemInfoSupplier, 81 int resourceId, 82 int resourceIndex) { 83 final String string = getInfo(warningBannerInfoSupplier, warningItemInfoSupplier); 84 return (!TextUtils.isEmpty(string) || resourceId <= 0) 85 ? string 86 : getStringFromArrayResource(resourceId, resourceIndex); 87 } 88 getStringFromArrayResource(int resourceId, int resourceIndex)89 private String getStringFromArrayResource(int resourceId, int resourceIndex) { 90 if (resourceId <= 0 || resourceIndex < 0) { 91 return null; 92 } 93 final String[] stringArray = mContext.getResources().getStringArray(resourceId); 94 return (resourceIndex >= 0 && resourceIndex < stringArray.length) 95 ? stringArray[resourceIndex] 96 : null; 97 } 98 setRelatedBatteryDiffEntry(BatteryDiffEntry batteryDiffEntry)99 void setRelatedBatteryDiffEntry(BatteryDiffEntry batteryDiffEntry) { 100 mRelatedBatteryDiffEntry = batteryDiffEntry; 101 } 102 getAnomalyKeyNumber()103 int getAnomalyKeyNumber() { 104 return mPowerAnomalyEvent.getKey().getNumber(); 105 } 106 getEventId()107 String getEventId() { 108 return mPowerAnomalyEvent.hasEventId() ? mPowerAnomalyEvent.getEventId() : null; 109 } 110 getIconDrawable()111 Drawable getIconDrawable() { 112 final int iconResId = 113 getResourceId(R.array.battery_tips_card_icons, mCardStyleId, "drawable"); 114 Drawable drawable = mContext.getDrawable(iconResId); 115 if (drawable != null && mAttentionLevel != AttentionLevel.NORMAL) { 116 drawable.setTint(mContext.getColor(mAttentionLevel.getButtonBackgroundColorResId())); 117 } 118 return drawable; 119 } 120 getTitleString()121 String getTitleString() { 122 final String titleStringFromProto = 123 getInfo(WarningBannerInfo::getTitleString, WarningItemInfo::getTitleString); 124 if (!TextUtils.isEmpty(titleStringFromProto)) { 125 return titleStringFromProto; 126 } 127 final int titleFormatResId = 128 getResourceId(R.array.power_anomaly_title_ids, mResourceIndex, "string"); 129 if (mPowerAnomalyEvent.hasWarningBannerInfo()) { 130 return mContext.getString(titleFormatResId); 131 } else if (mPowerAnomalyEvent.hasWarningItemInfo() && mRelatedBatteryDiffEntry != null) { 132 final String appLabel = mRelatedBatteryDiffEntry.getAppLabel(); 133 return mContext.getString(titleFormatResId, appLabel); 134 } 135 return null; 136 } 137 getMainBtnString()138 String getMainBtnString() { 139 return getString( 140 WarningBannerInfo::getMainButtonString, 141 WarningItemInfo::getMainButtonString, 142 R.array.power_anomaly_main_btn_strings, 143 mResourceIndex); 144 } 145 getDismissBtnString()146 String getDismissBtnString() { 147 return getString( 148 WarningBannerInfo::getCancelButtonString, 149 WarningItemInfo::getCancelButtonString, 150 R.array.power_anomaly_dismiss_btn_strings, 151 mResourceIndex); 152 } 153 getAnomalyHintString()154 String getAnomalyHintString() { 155 final String anomalyHintStringFromProto = 156 getInfo(null, WarningItemInfo::getWarningInfoString); 157 return TextUtils.isEmpty(anomalyHintStringFromProto) 158 ? getStringFromArrayResource(R.array.power_anomaly_hint_messages, mResourceIndex) 159 : anomalyHintStringFromProto; 160 } 161 getAnomalyHintPrefKey()162 String getAnomalyHintPrefKey() { 163 return getInfo(null, WarningItemInfo::getAnomalyHintPrefKey); 164 } 165 getDismissRecordKey()166 String getDismissRecordKey() { 167 return mPowerAnomalyEvent.getDismissRecordKey(); 168 } 169 hasAnomalyEntryKey()170 boolean hasAnomalyEntryKey() { 171 return getAnomalyEntryKey() != null; 172 } 173 getAnomalyEntryKey()174 String getAnomalyEntryKey() { 175 return mPowerAnomalyEvent.hasWarningItemInfo() 176 && mPowerAnomalyEvent.getWarningItemInfo().hasItemKey() 177 ? mPowerAnomalyEvent.getWarningItemInfo().getItemKey() 178 : null; 179 } 180 hasSubSettingLauncher()181 boolean hasSubSettingLauncher() { 182 if (mSubSettingLauncher == null) { 183 mSubSettingLauncher = getSubSettingLauncher(); 184 } 185 return mSubSettingLauncher != null; 186 } 187 getSubSettingLauncher()188 SubSettingLauncher getSubSettingLauncher() { 189 if (mSubSettingLauncher != null) { 190 return mSubSettingLauncher; 191 } 192 final String destinationClassName = 193 getInfo(WarningBannerInfo::getMainButtonDestination, null); 194 if (!TextUtils.isEmpty(destinationClassName)) { 195 final Integer sourceMetricsCategory = 196 getInfo(WarningBannerInfo::getMainButtonSourceMetricsCategory, null); 197 final String preferenceHighlightKey = 198 getInfo(WarningBannerInfo::getMainButtonSourceHighlightKey, null); 199 Bundle arguments = Bundle.EMPTY; 200 if (!TextUtils.isEmpty(preferenceHighlightKey)) { 201 arguments = new Bundle(1); 202 arguments.putString( 203 SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, preferenceHighlightKey); 204 } 205 mSubSettingLauncher = 206 new SubSettingLauncher(mContext) 207 .setDestination(destinationClassName) 208 .setSourceMetricsCategory(sourceMetricsCategory) 209 .setArguments(arguments); 210 } 211 return mSubSettingLauncher; 212 } 213 hasHighlightSlotPair(BatteryLevelData batteryLevelData)214 boolean hasHighlightSlotPair(BatteryLevelData batteryLevelData) { 215 if (mHighlightSlotPair == null) { 216 mHighlightSlotPair = getHighlightSlotPair(batteryLevelData); 217 } 218 return mHighlightSlotPair != null; 219 } 220 getHighlightSlotPair(BatteryLevelData batteryLevelData)221 Pair<Integer, Integer> getHighlightSlotPair(BatteryLevelData batteryLevelData) { 222 if (mHighlightSlotPair != null) { 223 return mHighlightSlotPair; 224 } 225 if (!mPowerAnomalyEvent.hasWarningItemInfo()) { 226 return null; 227 } 228 final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo(); 229 final Long startTimestamp = 230 warningItemInfo.hasStartTimestamp() ? warningItemInfo.getStartTimestamp() : null; 231 final Long endTimestamp = 232 warningItemInfo.hasEndTimestamp() ? warningItemInfo.getEndTimestamp() : null; 233 if (startTimestamp != null && endTimestamp != null) { 234 mHighlightSlotPair = 235 batteryLevelData.getIndexByTimestamps(startTimestamp, endTimestamp); 236 if (mHighlightSlotPair.first == BatteryChartViewModel.SELECTED_INDEX_INVALID 237 || mHighlightSlotPair.second == BatteryChartViewModel.SELECTED_INDEX_INVALID) { 238 // Drop invalid mHighlightSlotPair index 239 mHighlightSlotPair = null; 240 } 241 } 242 return mHighlightSlotPair; 243 } 244 updateTipsCardPreference(BannerMessagePreference preference)245 boolean updateTipsCardPreference(BannerMessagePreference preference) { 246 final String titleString = getTitleString(); 247 if (TextUtils.isEmpty(titleString)) { 248 return false; 249 } 250 preference.setTitle(titleString); 251 preference.setIcon(getIconDrawable()); 252 preference.setAttentionLevel(mAttentionLevel); 253 preference.setNegativeButtonText(getDismissBtnString()); 254 preference.setPositiveButtonText(getMainBtnString()); 255 return true; 256 } 257 launchSubSetting()258 boolean launchSubSetting() { 259 if (!hasSubSettingLauncher()) { 260 return false; 261 } 262 // Navigate to sub setting page 263 mSubSettingLauncher.launch(); 264 return true; 265 } 266 updateSystemSettingsIfAvailable()267 boolean updateSystemSettingsIfAvailable() { 268 final String settingsName = 269 getInfo(WarningBannerInfo::getMainButtonConfigSettingsName, null); 270 final Integer settingsValue = 271 getInfo(WarningBannerInfo::getMainButtonConfigSettingsValue, null); 272 if (TextUtils.isEmpty(settingsName) || settingsValue == null) { 273 Log.d(TAG, "Failed to update settings due to invalid key or value"); 274 return false; 275 } 276 277 try { 278 Settings.System.putInt(mContext.getContentResolver(), settingsName, settingsValue); 279 Log.d( 280 TAG, 281 String.format( 282 "Update settings name=%s to value=%d", settingsName, settingsValue)); 283 return true; 284 } catch (SecurityException e) { 285 Log.w( 286 TAG, 287 String.format( 288 "Failed to update settings name=%s to value=%d", 289 settingsName, settingsValue), 290 e); 291 return false; 292 } 293 } 294 } 295