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