1 /* 2 * Copyright (C) 2018 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.systemui.statusbar.phone; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.os.Handler; 22 import android.util.ArraySet; 23 import android.util.Log; 24 25 import com.android.settingslib.mobile.TelephonyIcons; 26 import com.android.systemui.dagger.SysUISingleton; 27 import com.android.systemui.res.R; 28 import com.android.systemui.statusbar.connectivity.IconState; 29 import com.android.systemui.statusbar.connectivity.NetworkController; 30 import com.android.systemui.statusbar.connectivity.SignalCallback; 31 import com.android.systemui.statusbar.phone.ui.StatusBarIconController; 32 import com.android.systemui.statusbar.policy.SecurityController; 33 import com.android.systemui.tuner.TunerService; 34 import com.android.systemui.tuner.TunerService.Tunable; 35 import com.android.systemui.util.CarrierConfigTracker; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.Objects; 40 41 import javax.inject.Inject; 42 43 /** Controls the signal policies for icons shown in the statusbar. **/ 44 @SysUISingleton 45 public class StatusBarSignalPolicy implements SignalCallback, 46 SecurityController.SecurityControllerCallback, Tunable { 47 private static final String TAG = "StatusBarSignalPolicy"; 48 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 49 50 private final String mSlotAirplane; 51 private final String mSlotMobile; 52 private final String mSlotEthernet; 53 private final String mSlotVpn; 54 private final String mSlotNoCalling; 55 private final String mSlotCallStrength; 56 57 private final Context mContext; 58 private final StatusBarIconController mIconController; 59 private final NetworkController mNetworkController; 60 private final SecurityController mSecurityController; 61 private final Handler mHandler = Handler.getMain(); 62 private final CarrierConfigTracker mCarrierConfigTracker; 63 private final TunerService mTunerService; 64 65 private boolean mHideAirplane; 66 private boolean mHideMobile; 67 private boolean mHideEthernet; 68 private boolean mActivityEnabled; 69 70 // Track as little state as possible, and only for padding purposes 71 private boolean mIsAirplaneMode = false; 72 73 private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); 74 private boolean mInitialized; 75 76 @Inject StatusBarSignalPolicy( Context context, StatusBarIconController iconController, CarrierConfigTracker carrierConfigTracker, NetworkController networkController, SecurityController securityController, TunerService tunerService )77 public StatusBarSignalPolicy( 78 Context context, 79 StatusBarIconController iconController, 80 CarrierConfigTracker carrierConfigTracker, 81 NetworkController networkController, 82 SecurityController securityController, 83 TunerService tunerService 84 ) { 85 mContext = context; 86 87 mIconController = iconController; 88 mCarrierConfigTracker = carrierConfigTracker; 89 mNetworkController = networkController; 90 mSecurityController = securityController; 91 mTunerService = tunerService; 92 93 mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); 94 mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); 95 mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); 96 mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); 97 mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling); 98 mSlotCallStrength = 99 mContext.getString(com.android.internal.R.string.status_bar_call_strength); 100 mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); 101 } 102 103 /** Call to initialize and register this class with the system. */ init()104 public void init() { 105 if (mInitialized) { 106 return; 107 } 108 mInitialized = true; 109 mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST); 110 mNetworkController.addCallback(this); 111 mSecurityController.addCallback(this); 112 } 113 destroy()114 public void destroy() { 115 mTunerService.removeTunable(this); 116 mNetworkController.removeCallback(this); 117 mSecurityController.removeCallback(this); 118 } 119 updateVpn()120 private void updateVpn() { 121 boolean vpnVisible = mSecurityController.isVpnEnabled(); 122 int vpnIconId = currentVpnIconId( 123 mSecurityController.isVpnBranded(), 124 mSecurityController.isVpnValidated()); 125 126 mIconController.setIcon(mSlotVpn, vpnIconId, 127 mContext.getResources().getString(R.string.accessibility_vpn_on)); 128 mIconController.setIconVisibility(mSlotVpn, vpnVisible); 129 } 130 currentVpnIconId(boolean isBranded, boolean isValidated)131 private int currentVpnIconId(boolean isBranded, boolean isValidated) { 132 if (isBranded) { 133 return isValidated 134 ? R.drawable.stat_sys_branded_vpn 135 : R.drawable.stat_sys_no_internet_branded_vpn; 136 } else { 137 return isValidated 138 ? R.drawable.stat_sys_vpn_ic 139 : R.drawable.stat_sys_no_internet_vpn_ic; 140 } 141 } 142 143 /** 144 * From SecurityController 145 */ 146 @Override onStateChanged()147 public void onStateChanged() { 148 mHandler.post(this::updateVpn); 149 } 150 151 @Override onTuningChanged(String key, String newValue)152 public void onTuningChanged(String key, String newValue) { 153 if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) { 154 return; 155 } 156 ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue); 157 boolean hideAirplane = hideList.contains(mSlotAirplane); 158 boolean hideMobile = hideList.contains(mSlotMobile); 159 boolean hideEthernet = hideList.contains(mSlotEthernet); 160 161 if (hideAirplane != mHideAirplane || hideMobile != mHideMobile 162 || hideEthernet != mHideEthernet) { 163 mHideAirplane = hideAirplane; 164 mHideMobile = hideMobile; 165 mHideEthernet = hideEthernet; 166 // Re-register to get new callbacks. 167 mNetworkController.removeCallback(this); 168 mNetworkController.addCallback(this); 169 } 170 } 171 172 @Override setCallIndicator(@onNull IconState statusIcon, int subId)173 public void setCallIndicator(@NonNull IconState statusIcon, int subId) { 174 if (DEBUG) { 175 Log.d(TAG, "setCallIndicator: " 176 + "statusIcon = " + statusIcon + "," 177 + "subId = " + subId); 178 } 179 CallIndicatorIconState state = getNoCallingState(subId); 180 if (state == null) { 181 return; 182 } 183 if (statusIcon.icon == R.drawable.ic_shade_no_calling_sms) { 184 state.isNoCalling = statusIcon.visible; 185 state.noCallingDescription = statusIcon.contentDescription; 186 } else { 187 state.callStrengthResId = statusIcon.icon; 188 state.callStrengthDescription = statusIcon.contentDescription; 189 } 190 if (mCarrierConfigTracker.getCallStrengthConfig(subId)) { 191 mIconController.setCallStrengthIcons(mSlotCallStrength, 192 CallIndicatorIconState.copyStates(mCallIndicatorStates)); 193 } else { 194 mIconController.removeIcon(mSlotCallStrength, subId); 195 } 196 mIconController.setNoCallingIcons(mSlotNoCalling, 197 CallIndicatorIconState.copyStates(mCallIndicatorStates)); 198 } 199 getNoCallingState(int subId)200 private CallIndicatorIconState getNoCallingState(int subId) { 201 for (CallIndicatorIconState state : mCallIndicatorStates) { 202 if (state.subId == subId) { 203 return state; 204 } 205 } 206 Log.e(TAG, "Unexpected subscription " + subId); 207 return null; 208 } 209 210 @Override setEthernetIndicators(IconState state)211 public void setEthernetIndicators(IconState state) { 212 int resId = state.icon; 213 String description = state.contentDescription; 214 215 if (resId > 0) { 216 mIconController.setIcon(mSlotEthernet, resId, description); 217 mIconController.setIconVisibility(mSlotEthernet, true); 218 } else { 219 mIconController.setIconVisibility(mSlotEthernet, false); 220 } 221 } 222 223 @Override setIsAirplaneMode(IconState icon)224 public void setIsAirplaneMode(IconState icon) { 225 if (DEBUG) { 226 Log.d(TAG, "setIsAirplaneMode: " 227 + "icon = " + (icon == null ? "" : icon.toString())); 228 } 229 mIsAirplaneMode = icon.visible && !mHideAirplane; 230 int resId = icon.icon; 231 String description = icon.contentDescription; 232 233 if (mIsAirplaneMode && resId > 0) { 234 mIconController.setIcon(mSlotAirplane, resId, description); 235 mIconController.setIconVisibility(mSlotAirplane, true); 236 } else { 237 mIconController.setIconVisibility(mSlotAirplane, false); 238 } 239 } 240 241 /** 242 * Stores the statusbar state for no Calling & SMS. 243 */ 244 public static class CallIndicatorIconState { 245 public boolean isNoCalling; 246 public int noCallingResId; 247 public int callStrengthResId; 248 public int subId; 249 public String noCallingDescription; 250 public String callStrengthDescription; 251 CallIndicatorIconState(int subId)252 private CallIndicatorIconState(int subId) { 253 this.subId = subId; 254 this.noCallingResId = R.drawable.ic_shade_no_calling_sms; 255 this.callStrengthResId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0]; 256 } 257 258 @Override equals(Object o)259 public boolean equals(Object o) { 260 // Skipping reference equality bc this should be more of a value type 261 if (o == null || getClass() != o.getClass()) { 262 return false; 263 } 264 CallIndicatorIconState that = (CallIndicatorIconState) o; 265 return isNoCalling == that.isNoCalling 266 && noCallingResId == that.noCallingResId 267 && callStrengthResId == that.callStrengthResId 268 && subId == that.subId 269 && noCallingDescription == that.noCallingDescription 270 && callStrengthDescription == that.callStrengthDescription; 271 272 } 273 274 @Override hashCode()275 public int hashCode() { 276 return Objects.hash(isNoCalling, noCallingResId, 277 callStrengthResId, subId, noCallingDescription, callStrengthDescription); 278 } 279 copyTo(CallIndicatorIconState other)280 private void copyTo(CallIndicatorIconState other) { 281 other.isNoCalling = isNoCalling; 282 other.noCallingResId = noCallingResId; 283 other.callStrengthResId = callStrengthResId; 284 other.subId = subId; 285 other.noCallingDescription = noCallingDescription; 286 other.callStrengthDescription = callStrengthDescription; 287 } 288 copyStates( List<CallIndicatorIconState> inStates)289 private static List<CallIndicatorIconState> copyStates( 290 List<CallIndicatorIconState> inStates) { 291 ArrayList<CallIndicatorIconState> outStates = new ArrayList<>(); 292 for (CallIndicatorIconState state : inStates) { 293 CallIndicatorIconState copy = new CallIndicatorIconState(state.subId); 294 state.copyTo(copy); 295 outStates.add(copy); 296 } 297 return outStates; 298 } 299 } 300 } 301