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