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