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 57 implements BatteryTipPreferenceController.BatteryTipListener { 58 59 static final String TAG = "PowerUsageSummary"; 60 61 @VisibleForTesting static final String KEY_BATTERY_ERROR = "battery_help_message"; 62 @VisibleForTesting static final String KEY_BATTERY_USAGE = "battery_usage_summary"; 63 64 @VisibleForTesting PowerUsageFeatureProvider mPowerFeatureProvider; 65 @VisibleForTesting BatteryUtils mBatteryUtils; 66 @VisibleForTesting BatteryInfo mBatteryInfo; 67 68 @VisibleForTesting BatteryHeaderPreferenceController mBatteryHeaderPreferenceController; 69 @VisibleForTesting BatteryTipPreferenceController mBatteryTipPreferenceController; 70 @VisibleForTesting boolean mNeedUpdateBatteryTip; 71 @VisibleForTesting Preference mHelpPreference; 72 @VisibleForTesting Preference mBatteryUsagePreference; 73 74 @VisibleForTesting 75 final ContentObserver mSettingsObserver = 76 new ContentObserver(new Handler()) { 77 @Override 78 public void onChange(boolean selfChange, Uri uri) { 79 restartBatteryInfoLoader(); 80 } 81 }; 82 83 @VisibleForTesting 84 LoaderManager.LoaderCallbacks<BatteryInfo> mBatteryInfoLoaderCallbacks = 85 new LoaderManager.LoaderCallbacks<BatteryInfo>() { 86 87 @Override 88 public Loader<BatteryInfo> onCreateLoader(int i, Bundle bundle) { 89 return new BatteryInfoLoader(getContext()); 90 } 91 92 @Override 93 public void onLoadFinished(Loader<BatteryInfo> loader, BatteryInfo batteryInfo) { 94 mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo); 95 mBatteryHeaderPreferenceController.updateHeaderByBatteryTips( 96 mBatteryTipPreferenceController.getCurrentBatteryTip(), batteryInfo); 97 mBatteryInfo = batteryInfo; 98 } 99 100 @Override 101 public void onLoaderReset(Loader<BatteryInfo> loader) { 102 // do nothing 103 } 104 }; 105 106 private LoaderManager.LoaderCallbacks<List<BatteryTip>> mBatteryTipsCallbacks = 107 new LoaderManager.LoaderCallbacks<List<BatteryTip>>() { 108 109 @Override 110 public Loader<List<BatteryTip>> onCreateLoader(int id, Bundle args) { 111 return new BatteryTipLoader(getContext(), mBatteryUsageStats); 112 } 113 114 @Override 115 public void onLoadFinished(Loader<List<BatteryTip>> loader, List<BatteryTip> data) { 116 mBatteryTipPreferenceController.updateBatteryTips(data); 117 mBatteryHeaderPreferenceController.updateHeaderByBatteryTips( 118 mBatteryTipPreferenceController.getCurrentBatteryTip(), mBatteryInfo); 119 } 120 121 @Override 122 public void onLoaderReset(Loader<List<BatteryTip>> loader) {} 123 }; 124 125 @Override onAttach(Context context)126 public void onAttach(Context context) { 127 super.onAttach(context); 128 final SettingsActivity activity = (SettingsActivity) getActivity(); 129 130 mBatteryHeaderPreferenceController = use(BatteryHeaderPreferenceController.class); 131 132 mBatteryTipPreferenceController = use(BatteryTipPreferenceController.class); 133 mBatteryTipPreferenceController.setActivity(activity); 134 mBatteryTipPreferenceController.setFragment(this); 135 mBatteryTipPreferenceController.setBatteryTipListener(this::onBatteryTipHandled); 136 } 137 138 @Override onCreate(Bundle icicle)139 public void onCreate(Bundle icicle) { 140 super.onCreate(icicle); 141 setAnimationAllowed(true); 142 143 initFeatureProvider(); 144 initPreference(); 145 146 mBatteryUtils = BatteryUtils.getInstance(getContext()); 147 148 if (Utils.isBatteryPresent(getContext())) { 149 restartBatteryInfoLoader(); 150 } else { 151 // Present help preference when battery is unavailable. 152 mHelpPreference.setVisible(true); 153 } 154 mBatteryTipPreferenceController.restoreInstanceState(icicle); 155 updateBatteryTipFlag(icicle); 156 } 157 158 @Override onResume()159 public void onResume() { 160 super.onResume(); 161 getContentResolver() 162 .registerContentObserver( 163 Global.getUriFor(Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME), 164 false, 165 mSettingsObserver); 166 } 167 168 @Override onPause()169 public void onPause() { 170 getContentResolver().unregisterContentObserver(mSettingsObserver); 171 super.onPause(); 172 } 173 174 @Override getMetricsCategory()175 public int getMetricsCategory() { 176 return SettingsEnums.FUELGAUGE_POWER_USAGE_SUMMARY_V2; 177 } 178 179 @Override getLogTag()180 protected String getLogTag() { 181 return TAG; 182 } 183 184 @Override getPreferenceScreenResId()185 protected int getPreferenceScreenResId() { 186 return R.xml.power_usage_summary; 187 } 188 189 @Override getHelpResource()190 public int getHelpResource() { 191 return R.string.help_url_battery; 192 } 193 refreshUi(@atteryUpdateType int refreshType)194 protected void refreshUi(@BatteryUpdateType int refreshType) { 195 final Context context = getContext(); 196 if (context == null) { 197 return; 198 } 199 // Skip refreshing UI if battery is not present. 200 if (!mIsBatteryPresent) { 201 return; 202 } 203 204 // Skip BatteryTipLoader if device is rotated or only battery level change 205 if (mNeedUpdateBatteryTip && refreshType != BatteryUpdateType.BATTERY_LEVEL) { 206 restartBatteryTipLoader(); 207 } else { 208 mNeedUpdateBatteryTip = true; 209 } 210 // reload BatteryInfo and updateUI 211 restartBatteryInfoLoader(); 212 } 213 214 @VisibleForTesting restartBatteryTipLoader()215 void restartBatteryTipLoader() { 216 restartLoader(LoaderIndex.BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks); 217 } 218 219 @VisibleForTesting initFeatureProvider()220 void initFeatureProvider() { 221 mPowerFeatureProvider = FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider(); 222 } 223 224 @VisibleForTesting initPreference()225 void initPreference() { 226 mBatteryUsagePreference = findPreference(KEY_BATTERY_USAGE); 227 mBatteryUsagePreference.setSummary(getString(R.string.advanced_battery_preference_summary)); 228 mBatteryUsagePreference.setVisible(mPowerFeatureProvider.isBatteryUsageEnabled()); 229 230 mHelpPreference = findPreference(KEY_BATTERY_ERROR); 231 mHelpPreference.setVisible(false); 232 } 233 234 @VisibleForTesting restartBatteryInfoLoader()235 void restartBatteryInfoLoader() { 236 if (getContext() == null) { 237 return; 238 } 239 // Skip restartBatteryInfoLoader if battery is not present. 240 if (!mIsBatteryPresent) { 241 return; 242 } 243 restartLoader(LoaderIndex.BATTERY_INFO_LOADER, Bundle.EMPTY, mBatteryInfoLoaderCallbacks); 244 } 245 246 @VisibleForTesting updateBatteryTipFlag(Bundle icicle)247 void updateBatteryTipFlag(Bundle icicle) { 248 mNeedUpdateBatteryTip = icicle == null || mBatteryTipPreferenceController.needUpdate(); 249 } 250 251 @Override restartBatteryStatsLoader(@atteryUpdateType int refreshType)252 protected void restartBatteryStatsLoader(@BatteryUpdateType int refreshType) { 253 super.restartBatteryStatsLoader(refreshType); 254 // Update battery header if battery is present. 255 if (mIsBatteryPresent) { 256 mBatteryHeaderPreferenceController.quickUpdateHeaderPreference(); 257 } 258 } 259 260 @Override onSaveInstanceState(Bundle outState)261 public void onSaveInstanceState(Bundle outState) { 262 super.onSaveInstanceState(outState); 263 mBatteryTipPreferenceController.saveInstanceState(outState); 264 } 265 266 @Override onBatteryTipHandled(BatteryTip batteryTip)267 public void onBatteryTipHandled(BatteryTip batteryTip) { 268 restartBatteryTipLoader(); 269 } 270 271 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 272 new BaseSearchIndexProvider(R.xml.power_usage_summary); 273 } 274