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