1 /* 2 * Copyright (C) 2022 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 static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType; 20 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.database.ContentObserver; 24 import android.net.Uri; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.provider.Settings.Global; 28 29 import androidx.annotation.VisibleForTesting; 30 import androidx.loader.app.LoaderManager; 31 import androidx.loader.content.Loader; 32 import androidx.preference.Preference; 33 34 import com.android.settings.R; 35 import com.android.settings.SettingsActivity; 36 import com.android.settings.Utils; 37 import com.android.settings.fuelgauge.BatteryHeaderPreferenceController; 38 import com.android.settings.fuelgauge.BatteryInfo; 39 import com.android.settings.fuelgauge.BatteryInfoLoader; 40 import com.android.settings.fuelgauge.BatteryUtils; 41 import com.android.settings.fuelgauge.PowerUsageFeatureProvider; 42 import com.android.settings.fuelgauge.batterytip.BatteryTipLoader; 43 import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController; 44 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; 45 import com.android.settings.overlay.FeatureFactory; 46 import com.android.settings.search.BaseSearchIndexProvider; 47 import com.android.settingslib.search.SearchIndexable; 48 49 import java.util.List; 50 51 /** 52 * Displays a list of apps and subsystems that consume power, ordered by how much power was consumed 53 * since the last time it was unplugged. 54 */ 55 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) 56 public class PowerUsageSummary extends PowerUsageBase implements 57 BatteryTipPreferenceController.BatteryTipListener { 58 59 static final String TAG = "PowerUsageSummary"; 60 61 @VisibleForTesting 62 static final String KEY_BATTERY_ERROR = "battery_help_message"; 63 @VisibleForTesting 64 static final String KEY_BATTERY_USAGE = "battery_usage_summary"; 65 66 @VisibleForTesting 67 PowerUsageFeatureProvider mPowerFeatureProvider; 68 @VisibleForTesting 69 BatteryUtils mBatteryUtils; 70 @VisibleForTesting 71 BatteryInfo mBatteryInfo; 72 73 @VisibleForTesting 74 BatteryHeaderPreferenceController mBatteryHeaderPreferenceController; 75 @VisibleForTesting 76 BatteryTipPreferenceController mBatteryTipPreferenceController; 77 @VisibleForTesting 78 boolean mNeedUpdateBatteryTip; 79 @VisibleForTesting 80 Preference mHelpPreference; 81 @VisibleForTesting 82 Preference mBatteryUsagePreference; 83 84 @VisibleForTesting 85 final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 86 @Override 87 public void onChange(boolean selfChange, Uri uri) { 88 restartBatteryInfoLoader(); 89 } 90 }; 91 92 @VisibleForTesting 93 LoaderManager.LoaderCallbacks<BatteryInfo> mBatteryInfoLoaderCallbacks = 94 new LoaderManager.LoaderCallbacks<BatteryInfo>() { 95 96 @Override 97 public Loader<BatteryInfo> onCreateLoader(int i, Bundle bundle) { 98 return new BatteryInfoLoader(getContext()); 99 } 100 101 @Override 102 public void onLoadFinished(Loader<BatteryInfo> loader, BatteryInfo batteryInfo) { 103 mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo); 104 mBatteryHeaderPreferenceController.updateHeaderByBatteryTips( 105 mBatteryTipPreferenceController.getCurrentBatteryTip(), batteryInfo); 106 mBatteryInfo = batteryInfo; 107 } 108 109 @Override 110 public void onLoaderReset(Loader<BatteryInfo> loader) { 111 // do nothing 112 } 113 }; 114 115 private LoaderManager.LoaderCallbacks<List<BatteryTip>> mBatteryTipsCallbacks = 116 new LoaderManager.LoaderCallbacks<List<BatteryTip>>() { 117 118 @Override 119 public Loader<List<BatteryTip>> onCreateLoader(int id, Bundle args) { 120 return new BatteryTipLoader(getContext(), mBatteryUsageStats); 121 } 122 123 @Override 124 public void onLoadFinished(Loader<List<BatteryTip>> loader, 125 List<BatteryTip> data) { 126 mBatteryTipPreferenceController.updateBatteryTips(data); 127 mBatteryHeaderPreferenceController.updateHeaderByBatteryTips( 128 mBatteryTipPreferenceController.getCurrentBatteryTip(), mBatteryInfo); 129 } 130 131 @Override 132 public void onLoaderReset(Loader<List<BatteryTip>> loader) { 133 134 } 135 }; 136 137 @Override onAttach(Context context)138 public void onAttach(Context context) { 139 super.onAttach(context); 140 final SettingsActivity activity = (SettingsActivity) getActivity(); 141 142 mBatteryHeaderPreferenceController = use(BatteryHeaderPreferenceController.class); 143 mBatteryHeaderPreferenceController.setActivity(activity); 144 mBatteryHeaderPreferenceController.setFragment(this); 145 mBatteryHeaderPreferenceController.setLifecycle(getSettingsLifecycle()); 146 147 mBatteryTipPreferenceController = use(BatteryTipPreferenceController.class); 148 mBatteryTipPreferenceController.setActivity(activity); 149 mBatteryTipPreferenceController.setFragment(this); 150 mBatteryTipPreferenceController.setBatteryTipListener(this::onBatteryTipHandled); 151 } 152 153 @Override onCreate(Bundle icicle)154 public void onCreate(Bundle icicle) { 155 super.onCreate(icicle); 156 setAnimationAllowed(true); 157 158 initFeatureProvider(); 159 initPreference(); 160 161 mBatteryUtils = BatteryUtils.getInstance(getContext()); 162 163 if (Utils.isBatteryPresent(getContext())) { 164 restartBatteryInfoLoader(); 165 } else { 166 // Present help preference when battery is unavailable. 167 mHelpPreference.setVisible(true); 168 } 169 mBatteryTipPreferenceController.restoreInstanceState(icicle); 170 updateBatteryTipFlag(icicle); 171 } 172 173 @Override onResume()174 public void onResume() { 175 super.onResume(); 176 getContentResolver().registerContentObserver( 177 Global.getUriFor(Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME), 178 false, 179 mSettingsObserver); 180 } 181 182 @Override onPause()183 public void onPause() { 184 getContentResolver().unregisterContentObserver(mSettingsObserver); 185 super.onPause(); 186 } 187 188 @Override getMetricsCategory()189 public int getMetricsCategory() { 190 return SettingsEnums.FUELGAUGE_POWER_USAGE_SUMMARY_V2; 191 } 192 193 @Override getLogTag()194 protected String getLogTag() { 195 return TAG; 196 } 197 198 @Override getPreferenceScreenResId()199 protected int getPreferenceScreenResId() { 200 return R.xml.power_usage_summary; 201 } 202 203 @Override getHelpResource()204 public int getHelpResource() { 205 return R.string.help_url_battery; 206 } 207 refreshUi(@atteryUpdateType int refreshType)208 protected void refreshUi(@BatteryUpdateType int refreshType) { 209 final Context context = getContext(); 210 if (context == null) { 211 return; 212 } 213 // Skip refreshing UI if battery is not present. 214 if (!mIsBatteryPresent) { 215 return; 216 } 217 218 // Skip BatteryTipLoader if device is rotated or only battery level change 219 if (mNeedUpdateBatteryTip 220 && refreshType != BatteryUpdateType.BATTERY_LEVEL) { 221 restartBatteryTipLoader(); 222 } else { 223 mNeedUpdateBatteryTip = true; 224 } 225 // reload BatteryInfo and updateUI 226 restartBatteryInfoLoader(); 227 } 228 229 @VisibleForTesting restartBatteryTipLoader()230 void restartBatteryTipLoader() { 231 restartLoader(LoaderIndex.BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks); 232 } 233 234 @VisibleForTesting initFeatureProvider()235 void initFeatureProvider() { 236 final Context context = getContext(); 237 mPowerFeatureProvider = FeatureFactory.getFactory(context) 238 .getPowerUsageFeatureProvider(context); 239 } 240 241 @VisibleForTesting initPreference()242 void initPreference() { 243 mBatteryUsagePreference = findPreference(KEY_BATTERY_USAGE); 244 mBatteryUsagePreference.setSummary(getString(R.string.advanced_battery_preference_summary)); 245 mBatteryUsagePreference.setVisible( 246 mPowerFeatureProvider.isBatteryUsageEnabled()); 247 248 mHelpPreference = findPreference(KEY_BATTERY_ERROR); 249 mHelpPreference.setVisible(false); 250 } 251 252 @VisibleForTesting restartBatteryInfoLoader()253 void restartBatteryInfoLoader() { 254 if (getContext() == null) { 255 return; 256 } 257 // Skip restartBatteryInfoLoader if battery is not present. 258 if (!mIsBatteryPresent) { 259 return; 260 } 261 restartLoader(LoaderIndex.BATTERY_INFO_LOADER, Bundle.EMPTY, mBatteryInfoLoaderCallbacks); 262 } 263 264 @VisibleForTesting updateBatteryTipFlag(Bundle icicle)265 void updateBatteryTipFlag(Bundle icicle) { 266 mNeedUpdateBatteryTip = icicle == null || mBatteryTipPreferenceController.needUpdate(); 267 } 268 269 @Override restartBatteryStatsLoader(@atteryUpdateType int refreshType)270 protected void restartBatteryStatsLoader(@BatteryUpdateType int refreshType) { 271 super.restartBatteryStatsLoader(refreshType); 272 // Update battery header if battery is present. 273 if (mIsBatteryPresent) { 274 mBatteryHeaderPreferenceController.quickUpdateHeaderPreference(); 275 } 276 } 277 278 @Override onSaveInstanceState(Bundle outState)279 public void onSaveInstanceState(Bundle outState) { 280 super.onSaveInstanceState(outState); 281 mBatteryTipPreferenceController.saveInstanceState(outState); 282 } 283 284 @Override onBatteryTipHandled(BatteryTip batteryTip)285 public void onBatteryTipHandled(BatteryTip batteryTip) { 286 restartBatteryTipLoader(); 287 } 288 289 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 290 new BaseSearchIndexProvider(R.xml.power_usage_summary); 291 } 292