1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 package com.android.settings.fuelgauge;
15 
16 import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType;
17 
18 import android.app.settings.SettingsEnums;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import android.database.ContentObserver;
23 import android.net.Uri;
24 import android.os.BatteryManager;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.provider.SearchIndexableResource;
28 import android.util.Log;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.VisibleForTesting;
32 import androidx.loader.app.LoaderManager;
33 import androidx.loader.content.Loader;
34 
35 import com.android.settings.R;
36 import com.android.settings.SettingsActivity;
37 import com.android.settings.fuelgauge.BatteryBroadcastReceiver;
38 import com.android.settings.overlay.FeatureFactory;
39 import com.android.settings.search.BaseSearchIndexProvider;
40 import com.android.settingslib.core.AbstractPreferenceController;
41 import com.android.settingslib.search.SearchIndexable;
42 
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.List;
46 import java.util.Map;
47 
48 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
49 public class PowerUsageAdvanced extends PowerUsageBase {
50     private static final String TAG = "AdvancedBatteryUsage";
51     private static final String KEY_REFRESH_TYPE = "refresh_type";
52     private static final String KEY_BATTERY_GRAPH = "battery_graph";
53     private static final String KEY_APP_LIST = "app_list";
54     private static final int LOADER_BATTERY_USAGE_STATS = 2;
55 
56     @VisibleForTesting
57     BatteryHistoryPreference mHistPref;
58     @VisibleForTesting
59     Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
60     @VisibleForTesting
61     final BatteryHistoryLoaderCallbacks mBatteryHistoryLoaderCallbacks =
62             new BatteryHistoryLoaderCallbacks();
63 
64     private boolean mIsChartDataLoaded = false;
65     private boolean mIsChartGraphEnabled = false;
66     private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
67     private BatteryChartPreferenceController mBatteryChartPreferenceController;
68     private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
69 
70     private final ContentObserver mBatteryObserver =
71             new ContentObserver(new Handler()) {
72             @Override
73             public void onChange(boolean selfChange) {
74                 Log.d(TAG, "onBatteryContentChange: " + selfChange);
75                 mIsChartDataLoaded = false;
76                 restartBatteryStatsLoader(
77                         BatteryBroadcastReceiver.BatteryUpdateType.MANUAL);
78             }
79     };
80 
81     @Override
onCreate(Bundle icicle)82     public void onCreate(Bundle icicle) {
83         super.onCreate(icicle);
84         final Context context = getContext();
85         refreshFeatureFlag(context);
86         mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
87         if (mIsChartGraphEnabled) {
88             setBatteryChartPreferenceController();
89         } else {
90            updateHistPrefSummary(context);
91         }
92     }
93 
94     @Override
onDestroy()95     public void onDestroy() {
96         super.onDestroy();
97         if (getActivity().isChangingConfigurations()) {
98             BatteryEntry.clearUidCache();
99         }
100     }
101 
102     @Override
getMetricsCategory()103     public int getMetricsCategory() {
104         return SettingsEnums.FUELGAUGE_BATTERY_HISTORY_DETAIL;
105     }
106 
107     @Override
getLogTag()108     protected String getLogTag() {
109         return TAG;
110     }
111 
112     @Override
getPreferenceScreenResId()113     protected int getPreferenceScreenResId() {
114         return R.xml.power_usage_advanced;
115     }
116 
117     @Override
onPause()118     public void onPause() {
119         super.onPause();
120         // Resets the flag to reload usage data in onResume() callback.
121         mIsChartDataLoaded = false;
122         final Uri uri = mPowerUsageFeatureProvider.getBatteryHistoryUri();
123         if (uri != null) {
124             getContext().getContentResolver().unregisterContentObserver(mBatteryObserver);
125         }
126     }
127 
128     @Override
onResume()129     public void onResume() {
130         super.onResume();
131         final Uri uri = mPowerUsageFeatureProvider.getBatteryHistoryUri();
132         if (uri != null) {
133             getContext().getContentResolver().registerContentObserver(
134                     uri, /*notifyForDescendants*/ true, mBatteryObserver);
135         }
136     }
137 
138     @Override
createPreferenceControllers(Context context)139     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
140         refreshFeatureFlag(context);
141         final List<AbstractPreferenceController> controllers = new ArrayList<>();
142         // Creates based on the chart design is enabled or not.
143         if (mIsChartGraphEnabled) {
144             mBatteryChartPreferenceController =
145                     new BatteryChartPreferenceController(context, KEY_APP_LIST,
146                         getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
147             controllers.add(mBatteryChartPreferenceController);
148             setBatteryChartPreferenceController();
149         } else {
150             mBatteryAppListPreferenceController =
151                     new BatteryAppListPreferenceController(context, KEY_APP_LIST,
152                         getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
153             controllers.add(mBatteryAppListPreferenceController);
154         }
155         return controllers;
156     }
157 
158     @Override
isBatteryHistoryNeeded()159     protected boolean isBatteryHistoryNeeded() {
160         return true;
161     }
162 
163     @Override
refreshUi(@atteryUpdateType int refreshType)164     protected void refreshUi(@BatteryUpdateType int refreshType) {
165         final Context context = getContext();
166         if (context == null) {
167             return;
168         }
169         updatePreference(mHistPref);
170         if (mBatteryAppListPreferenceController != null && mBatteryUsageStats != null) {
171             updateHistPrefSummary(context);
172             mBatteryAppListPreferenceController.refreshAppListGroup(
173                     mBatteryUsageStats, /* showAllApps */true);
174         }
175         if (mBatteryChartPreferenceController != null && mBatteryHistoryMap != null) {
176             mBatteryChartPreferenceController.setBatteryHistoryMap(mBatteryHistoryMap);
177         }
178     }
179 
180     @Override
restartBatteryStatsLoader(int refreshType)181     protected void restartBatteryStatsLoader(int refreshType) {
182         final Bundle bundle = new Bundle();
183         bundle.putInt(KEY_REFRESH_TYPE, refreshType);
184         // Uses customized battery history loader if chart design is enabled.
185         if (mIsChartGraphEnabled && !mIsChartDataLoaded) {
186             mIsChartDataLoaded = true;
187             getLoaderManager().restartLoader(LOADER_BATTERY_USAGE_STATS, bundle,
188                 mBatteryHistoryLoaderCallbacks);
189         } else if (!mIsChartGraphEnabled) {
190             super.restartBatteryStatsLoader(refreshType);
191         }
192     }
193 
updateHistPrefSummary(Context context)194     private void updateHistPrefSummary(Context context) {
195         final Intent batteryIntent =
196                 context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
197         final boolean plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;
198         if (mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(context) && !plugged) {
199             mHistPref.setBottomSummary(
200                     mPowerUsageFeatureProvider.getAdvancedUsageScreenInfoString());
201         } else {
202             mHistPref.hideBottomSummary();
203         }
204     }
205 
refreshFeatureFlag(Context context)206     private void refreshFeatureFlag(Context context) {
207         if (mPowerUsageFeatureProvider == null) {
208             mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
209                     .getPowerUsageFeatureProvider(context);
210             mIsChartGraphEnabled = mPowerUsageFeatureProvider.isChartGraphEnabled(context);
211         }
212     }
213 
setBatteryChartPreferenceController()214     private void setBatteryChartPreferenceController() {
215         if (mHistPref != null && mBatteryChartPreferenceController != null) {
216             mHistPref.setChartPreferenceController(mBatteryChartPreferenceController);
217         }
218     }
219 
220     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
221             new BaseSearchIndexProvider() {
222                 @Override
223                 public List<SearchIndexableResource> getXmlResourcesToIndex(
224                         Context context, boolean enabled) {
225                     final SearchIndexableResource sir = new SearchIndexableResource(context);
226                     sir.xmlResId = R.xml.power_usage_advanced;
227                     return Arrays.asList(sir);
228                 }
229 
230                 @Override
231                 public List<AbstractPreferenceController> createPreferenceControllers(
232                         Context context) {
233                     final List<AbstractPreferenceController> controllers = new ArrayList<>();
234                     controllers.add(new BatteryAppListPreferenceController(context,
235                             KEY_APP_LIST, null /* lifecycle */, null /* activity */,
236                             null /* fragment */));
237                     return controllers;
238                 }
239             };
240 
241     private class BatteryHistoryLoaderCallbacks
242             implements LoaderManager.LoaderCallbacks<Map<Long, Map<String, BatteryHistEntry>>> {
243         private int mRefreshType;
244 
245         @Override
246         @NonNull
onCreateLoader( int id, Bundle bundle)247         public Loader<Map<Long, Map<String, BatteryHistEntry>>> onCreateLoader(
248                 int id, Bundle bundle) {
249             mRefreshType = bundle.getInt(KEY_REFRESH_TYPE);
250             return new BatteryHistoryLoader(getContext());
251         }
252 
253         @Override
onLoadFinished(Loader<Map<Long, Map<String, BatteryHistEntry>>> loader, Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap)254         public void onLoadFinished(Loader<Map<Long, Map<String, BatteryHistEntry>>> loader,
255                 Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
256             mBatteryHistoryMap = batteryHistoryMap;
257             PowerUsageAdvanced.this.onLoadFinished(mRefreshType);
258         }
259 
260         @Override
onLoaderReset(Loader<Map<Long, Map<String, BatteryHistEntry>>> loader)261         public void onLoaderReset(Loader<Map<Long, Map<String, BatteryHistEntry>>> loader) {
262         }
263     }
264 
265 }
266