1 /*
2  * Copyright (C) 2021 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 package com.android.systemui.battery;
17 
18 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
19 
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.database.ContentObserver;
23 import android.net.Uri;
24 import android.os.Handler;
25 import android.os.HandlerExecutor;
26 import android.provider.Settings;
27 import android.text.TextUtils;
28 import android.util.ArraySet;
29 import android.view.View;
30 
31 import androidx.annotation.NonNull;
32 
33 import com.android.systemui.dagger.SysUISingleton;
34 import com.android.systemui.dagger.qualifiers.Main;
35 import com.android.systemui.flags.FeatureFlags;
36 import com.android.systemui.flags.Flags;
37 import com.android.systemui.res.R;
38 import com.android.systemui.settings.UserTracker;
39 import com.android.systemui.statusbar.phone.StatusBarLocation;
40 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
41 import com.android.systemui.statusbar.policy.BatteryController;
42 import com.android.systemui.statusbar.policy.ConfigurationController;
43 import com.android.systemui.tuner.TunerService;
44 import com.android.systemui.util.ViewController;
45 
46 import java.io.PrintWriter;
47 
48 import javax.inject.Inject;
49 
50 /** Controller for {@link BatteryMeterView}. **/
51 public class BatteryMeterViewController extends ViewController<BatteryMeterView> {
52     private final ConfigurationController mConfigurationController;
53     private final TunerService mTunerService;
54     private final Handler mMainHandler;
55     private final ContentResolver mContentResolver;
56     private final FeatureFlags mFeatureFlags;
57     private final BatteryController mBatteryController;
58 
59     private final String mSlotBattery;
60     private final SettingObserver mSettingObserver;
61     private final UserTracker mUserTracker;
62     private final StatusBarLocation mLocation;
63 
64     private final ConfigurationController.ConfigurationListener mConfigurationListener =
65             new ConfigurationController.ConfigurationListener() {
66                 @Override
67                 public void onDensityOrFontScaleChanged() {
68                     mView.scaleBatteryMeterViews();
69                 }
70             };
71 
72     private final TunerService.Tunable mTunable = new TunerService.Tunable() {
73         @Override
74         public void onTuningChanged(String key, String newValue) {
75             if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
76                 ArraySet<String> icons = StatusBarIconController.getIconHideList(
77                         getContext(), newValue);
78                 mView.setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
79             }
80         }
81     };
82 
83     private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
84             new BatteryController.BatteryStateChangeCallback() {
85                 @Override
86                 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
87                     mView.onBatteryLevelChanged(level, pluggedIn);
88                 }
89 
90                 @Override
91                 public void onPowerSaveChanged(boolean isPowerSave) {
92                     mView.onPowerSaveChanged(isPowerSave);
93                 }
94 
95                 @Override
96                 public void onBatteryUnknownStateChanged(boolean isUnknown) {
97                     mView.onBatteryUnknownStateChanged(isUnknown);
98                 }
99 
100                 @Override
101                 public void onIsBatteryDefenderChanged(boolean isBatteryDefender) {
102                     mView.onIsBatteryDefenderChanged(isBatteryDefender);
103                 }
104 
105                 @Override
106                 public void onIsIncompatibleChargingChanged(boolean isIncompatibleCharging) {
107                     if (mFeatureFlags.isEnabled(Flags.INCOMPATIBLE_CHARGING_BATTERY_ICON)) {
108                         mView.onIsIncompatibleChargingChanged(isIncompatibleCharging);
109                     }
110                 }
111 
112                 @Override
113                 public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
114                     pw.print(super.toString());
115                     pw.println(" location=" + mLocation);
116                     mView.dump(pw, args);
117                 }
118             };
119 
120     private final UserTracker.Callback mUserChangedCallback =
121             new UserTracker.Callback() {
122                 @Override
123                 public void onUserChanged(int newUser, @NonNull Context userContext) {
124                     mContentResolver.unregisterContentObserver(mSettingObserver);
125                     registerShowBatteryPercentObserver(newUser);
126                     mView.updateShowPercent();
127                 }
128             };
129 
130     // Some places may need to show the battery conditionally, and not obey the tuner
131     private boolean mIgnoreTunerUpdates;
132     private boolean mIsSubscribedForTunerUpdates;
133 
134     @Inject
BatteryMeterViewController( BatteryMeterView view, StatusBarLocation location, UserTracker userTracker, ConfigurationController configurationController, TunerService tunerService, @Main Handler mainHandler, ContentResolver contentResolver, FeatureFlags featureFlags, BatteryController batteryController)135     public BatteryMeterViewController(
136             BatteryMeterView view,
137             StatusBarLocation location,
138             UserTracker userTracker,
139             ConfigurationController configurationController,
140             TunerService tunerService,
141             @Main Handler mainHandler,
142             ContentResolver contentResolver,
143             FeatureFlags featureFlags,
144             BatteryController batteryController) {
145         super(view);
146         mLocation = location;
147         mUserTracker = userTracker;
148         mConfigurationController = configurationController;
149         mTunerService = tunerService;
150         mMainHandler = mainHandler;
151         mContentResolver = contentResolver;
152         mFeatureFlags = featureFlags;
153         mBatteryController = batteryController;
154 
155         mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString);
156         mView.setDisplayShieldEnabled(
157                 getContext().getResources().getBoolean(R.bool.flag_battery_shield_icon));
158 
159         mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery);
160         mSettingObserver = new SettingObserver(mMainHandler);
161     }
162 
163     @Override
onViewAttached()164     protected void onViewAttached() {
165         mConfigurationController.addCallback(mConfigurationListener);
166         subscribeForTunerUpdates();
167         mBatteryController.addCallback(mBatteryStateChangeCallback);
168 
169         registerShowBatteryPercentObserver(mUserTracker.getUserId());
170         registerGlobalBatteryUpdateObserver();
171         mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler));
172 
173         mView.updateShowPercent();
174     }
175 
176     @Override
onViewDetached()177     protected void onViewDetached() {
178         mConfigurationController.removeCallback(mConfigurationListener);
179         unsubscribeFromTunerUpdates();
180         mBatteryController.removeCallback(mBatteryStateChangeCallback);
181 
182         mUserTracker.removeCallback(mUserChangedCallback);
183         mContentResolver.unregisterContentObserver(mSettingObserver);
184     }
185 
186     /**
187      * Turn off {@link BatteryMeterView}'s subscribing to the tuner for updates, and thus avoid it
188      * controlling its own visibility.
189      */
ignoreTunerUpdates()190     public void ignoreTunerUpdates() {
191         mIgnoreTunerUpdates = true;
192         unsubscribeFromTunerUpdates();
193     }
194 
subscribeForTunerUpdates()195     private void subscribeForTunerUpdates() {
196         if (mIsSubscribedForTunerUpdates || mIgnoreTunerUpdates) {
197             return;
198         }
199 
200         mTunerService.addTunable(mTunable, StatusBarIconController.ICON_HIDE_LIST);
201         mIsSubscribedForTunerUpdates = true;
202     }
203 
unsubscribeFromTunerUpdates()204     private void unsubscribeFromTunerUpdates() {
205         if (!mIsSubscribedForTunerUpdates) {
206             return;
207         }
208 
209         mTunerService.removeTunable(mTunable);
210         mIsSubscribedForTunerUpdates = false;
211     }
212 
registerShowBatteryPercentObserver(int user)213     private void registerShowBatteryPercentObserver(int user) {
214         mContentResolver.registerContentObserver(
215                 Settings.System.getUriFor(SHOW_BATTERY_PERCENT),
216                 false,
217                 mSettingObserver,
218                 user);
219     }
220 
registerGlobalBatteryUpdateObserver()221     private void registerGlobalBatteryUpdateObserver() {
222         mContentResolver.registerContentObserver(
223                 Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
224                 false,
225                 mSettingObserver);
226     }
227 
228     private final class SettingObserver extends ContentObserver {
SettingObserver(Handler handler)229         public SettingObserver(Handler handler) {
230             super(handler);
231         }
232 
233         @Override
onChange(boolean selfChange, Uri uri)234         public void onChange(boolean selfChange, Uri uri) {
235             super.onChange(selfChange, uri);
236             mView.updateShowPercent();
237             if (TextUtils.equals(uri.getLastPathSegment(),
238                     Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)) {
239                 // update the text for sure if the estimate in the cache was updated
240                 mView.updatePercentText();
241             }
242         }
243     }
244 
245     /** */
246     @SysUISingleton
247     public static class Factory {
248         private final UserTracker mUserTracker;
249         private final ConfigurationController mConfigurationController;
250         private final TunerService mTunerService;
251         private final @Main Handler mMainHandler;
252         private final ContentResolver mContentResolver;
253         private final FeatureFlags mFeatureFlags;
254         private final BatteryController mBatteryController;
255 
256         @Inject
Factory( UserTracker userTracker, ConfigurationController configurationController, TunerService tunerService, @Main Handler mainHandler, ContentResolver contentResolver, FeatureFlags featureFlags, BatteryController batteryController )257         public Factory(
258                 UserTracker userTracker,
259                 ConfigurationController configurationController,
260                 TunerService tunerService,
261                 @Main Handler mainHandler,
262                 ContentResolver contentResolver,
263                 FeatureFlags featureFlags,
264                 BatteryController batteryController
265         ) {
266             mUserTracker = userTracker;
267             mConfigurationController = configurationController;
268             mTunerService = tunerService;
269             mMainHandler = mainHandler;
270             mContentResolver = contentResolver;
271             mFeatureFlags = featureFlags;
272             mBatteryController = batteryController;
273         }
274 
275         /** */
create(View view, StatusBarLocation location)276         public BatteryMeterViewController create(View view, StatusBarLocation location) {
277             return new BatteryMeterViewController(
278                     (BatteryMeterView) view,
279                     location,
280                     mUserTracker,
281                     mConfigurationController,
282                     mTunerService,
283                     mMainHandler,
284                     mContentResolver,
285                     mFeatureFlags,
286                     mBatteryController
287             );
288         }
289     }
290 }
291