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