blob: 8286d26e9999d3681fedbd450376c871314c4491 [file] [log] [blame]
Evan Lairde1d13c92018-03-20 16:58:01 -04001/*
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
17package com.android.systemui.statusbar.phone;
18
19import android.content.Context;
Evan Lairde1d13c92018-03-20 16:58:01 -040020import android.os.Handler;
Evan Lairde1d13c92018-03-20 16:58:01 -040021import android.telephony.SubscriptionInfo;
22import android.util.ArraySet;
23import android.util.Log;
Gus Prevasab336792018-11-14 13:52:20 -050024
Evan Lairde1d13c92018-03-20 16:58:01 -040025import com.android.systemui.Dependency;
26import com.android.systemui.R;
Evan Lairde1d13c92018-03-20 16:58:01 -040027import com.android.systemui.statusbar.policy.NetworkController;
28import com.android.systemui.statusbar.policy.NetworkController.IconState;
29import com.android.systemui.statusbar.policy.NetworkControllerImpl;
30import com.android.systemui.statusbar.policy.SecurityController;
Luca Stefani4e411422018-08-31 22:05:08 +020031import com.android.systemui.tuner.TunerService;
Evan Lairde1d13c92018-03-20 16:58:01 -040032import com.android.systemui.tuner.TunerService.Tunable;
Gus Prevasab336792018-11-14 13:52:20 -050033
Evan Lairde1d13c92018-03-20 16:58:01 -040034import java.util.ArrayList;
35import java.util.List;
36import java.util.Objects;
37
38
39public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
40 SecurityController.SecurityControllerCallback, Tunable {
41 private static final String TAG = "StatusBarSignalPolicy";
42
43 private final String mSlotAirplane;
44 private final String mSlotMobile;
45 private final String mSlotWifi;
46 private final String mSlotEthernet;
47 private final String mSlotVpn;
48
49 private final Context mContext;
50 private final StatusBarIconController mIconController;
51 private final NetworkController mNetworkController;
52 private final SecurityController mSecurityController;
53 private final Handler mHandler = Handler.getMain();
54
55 private boolean mBlockAirplane;
56 private boolean mBlockMobile;
57 private boolean mBlockWifi;
58 private boolean mBlockEthernet;
59 private boolean mActivityEnabled;
60 private boolean mForceBlockWifi;
61
62 // Track as little state as possible, and only for padding purposes
63 private boolean mIsAirplaneMode = false;
64 private boolean mWifiVisible = false;
65
66 private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>();
67 private WifiIconState mWifiIconState = new WifiIconState();
68
69 public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) {
70 mContext = context;
71
72 mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
73 mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile);
74 mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi);
75 mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet);
76 mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn);
Evan Laird6fd04172018-04-23 11:37:04 -040077 mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
Evan Lairde1d13c92018-03-20 16:58:01 -040078
79 mIconController = iconController;
80 mNetworkController = Dependency.get(NetworkController.class);
81 mSecurityController = Dependency.get(SecurityController.class);
82
Luca Stefani4e411422018-08-31 22:05:08 +020083 Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
Evan Lairde1d13c92018-03-20 16:58:01 -040084 mNetworkController.addCallback(this);
85 mSecurityController.addCallback(this);
86 }
87
88 public void destroy() {
Luca Stefani4e411422018-08-31 22:05:08 +020089 Dependency.get(TunerService.class).removeTunable(this);
Evan Lairde1d13c92018-03-20 16:58:01 -040090 mNetworkController.removeCallback(this);
91 mSecurityController.removeCallback(this);
92 }
93
94 private void updateVpn() {
95 boolean vpnVisible = mSecurityController.isVpnEnabled();
96 int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
97
Evan Lairdc5b79d92018-10-29 14:08:14 -040098 mIconController.setIcon(mSlotVpn, vpnIconId,
99 mContext.getResources().getString(R.string.accessibility_vpn_on));
Evan Lairde1d13c92018-03-20 16:58:01 -0400100 mIconController.setIconVisibility(mSlotVpn, vpnVisible);
101 }
102
103 private int currentVpnIconId(boolean isBranded) {
104 return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
105 }
106
Evan Lairde1d13c92018-03-20 16:58:01 -0400107 /**
108 * From SecurityController
109 */
110 @Override
111 public void onStateChanged() {
112 mHandler.post(this::updateVpn);
113 }
114
115 @Override
116 public void onTuningChanged(String key, String newValue) {
117 if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
118 return;
119 }
120 ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue);
121 boolean blockAirplane = blockList.contains(mSlotAirplane);
122 boolean blockMobile = blockList.contains(mSlotMobile);
123 boolean blockWifi = blockList.contains(mSlotWifi);
124 boolean blockEthernet = blockList.contains(mSlotEthernet);
125
126 if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile
127 || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) {
128 mBlockAirplane = blockAirplane;
129 mBlockMobile = blockMobile;
130 mBlockEthernet = blockEthernet;
131 mBlockWifi = blockWifi || mForceBlockWifi;
132 // Re-register to get new callbacks.
133 mNetworkController.removeCallback(this);
Luca Stefani4e411422018-08-31 22:05:08 +0200134 mNetworkController.addCallback(this);
Evan Lairde1d13c92018-03-20 16:58:01 -0400135 }
136 }
137
138 @Override
139 public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
140 boolean activityIn, boolean activityOut, String description, boolean isTransient,
141 String statusLabel) {
142
143 boolean visible = statusIcon.visible && !mBlockWifi;
144 boolean in = activityIn && mActivityEnabled && visible;
145 boolean out = activityOut && mActivityEnabled && visible;
146
Evan Lairdaf56a332018-05-23 19:52:34 -0400147 WifiIconState newState = mWifiIconState.copy();
Evan Lairde1d13c92018-03-20 16:58:01 -0400148
Evan Lairdaf56a332018-05-23 19:52:34 -0400149 newState.visible = visible;
150 newState.resId = statusIcon.icon;
151 newState.activityIn = in;
152 newState.activityOut = out;
153 newState.slot = mSlotWifi;
154 newState.airplaneSpacerVisible = mIsAirplaneMode;
155 newState.contentDescription = statusIcon.contentDescription;
156
157 MobileIconState first = getFirstMobileState();
158 newState.signalSpacerVisible = first != null && first.typeId != 0;
159
160 updateWifiIconWithState(newState);
161 mWifiIconState = newState;
162 }
163
164 private void updateShowWifiSignalSpacer(WifiIconState state) {
165 MobileIconState first = getFirstMobileState();
166 state.signalSpacerVisible = first != null && first.typeId != 0;
167 }
168
169 private void updateWifiIconWithState(WifiIconState state) {
170 if (state.visible && state.resId > 0) {
171 mIconController.setSignalIcon(mSlotWifi, state);
Evan Lairde1d13c92018-03-20 16:58:01 -0400172 mIconController.setIconVisibility(mSlotWifi, true);
173 } else {
174 mIconController.setIconVisibility(mSlotWifi, false);
175 }
176 }
177
178 @Override
179 public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
180 int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
181 String description, boolean isWide, int subId, boolean roaming) {
182 MobileIconState state = getState(subId);
183 if (state == null) {
184 return;
185 }
186
Evan Lairdaf56a332018-05-23 19:52:34 -0400187 // Visibility of the data type indicator changed
188 boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0);
189
Evan Lairde1d13c92018-03-20 16:58:01 -0400190 state.visible = statusIcon.visible && !mBlockMobile;
191 state.strengthId = statusIcon.icon;
192 state.typeId = statusType;
193 state.contentDescription = statusIcon.contentDescription;
194 state.typeContentDescription = typeContentDescription;
195 state.roaming = roaming;
196 state.activityIn = activityIn && mActivityEnabled;
197 state.activityOut = activityOut && mActivityEnabled;
198
199 // Always send a copy to maintain value type semantics
200 mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
Evan Lairdaf56a332018-05-23 19:52:34 -0400201
202 if (typeChanged) {
203 WifiIconState wifiCopy = mWifiIconState.copy();
204 updateShowWifiSignalSpacer(wifiCopy);
205 if (!Objects.equals(wifiCopy, mWifiIconState)) {
206 updateWifiIconWithState(wifiCopy);
207 mWifiIconState = wifiCopy;
208 }
209 }
Evan Lairde1d13c92018-03-20 16:58:01 -0400210 }
211
212 private MobileIconState getState(int subId) {
213 for (MobileIconState state : mMobileStates) {
214 if (state.subId == subId) {
215 return state;
216 }
217 }
218 Log.e(TAG, "Unexpected subscription " + subId);
219 return null;
220 }
221
Evan Lairdaf56a332018-05-23 19:52:34 -0400222 private MobileIconState getFirstMobileState() {
223 if (mMobileStates.size() > 0) {
224 return mMobileStates.get(0);
225 }
226
227 return null;
228 }
229
Evan Lairde1d13c92018-03-20 16:58:01 -0400230
231 /**
232 * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators
233 * so we don't have to update the icon manager at this point, just remove the old ones
234 * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8)
235 */
236 @Override
237 public void setSubs(List<SubscriptionInfo> subs) {
238 if (hasCorrectSubs(subs)) {
239 return;
240 }
241
242 mIconController.removeAllIconsForSlot(mSlotMobile);
243 mMobileStates.clear();
244 final int n = subs.size();
245 for (int i = 0; i < n; i++) {
246 mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
247 }
248 }
249
250 private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
251 final int N = subs.size();
252 if (N != mMobileStates.size()) {
253 return false;
254 }
255 for (int i = 0; i < N; i++) {
256 if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) {
257 return false;
258 }
259 }
260 return true;
261 }
262
263 @Override
264 public void setNoSims(boolean show, boolean simDetected) {
265 // Noop yay!
266 }
267
268
269 @Override
270 public void setEthernetIndicators(IconState state) {
271 boolean visible = state.visible && !mBlockEthernet;
272 int resId = state.icon;
273 String description = state.contentDescription;
274
275 if (resId > 0) {
276 mIconController.setIcon(mSlotEthernet, resId, description);
277 mIconController.setIconVisibility(mSlotEthernet, true);
278 } else {
279 mIconController.setIconVisibility(mSlotEthernet, false);
280 }
281 }
282
283 @Override
284 public void setIsAirplaneMode(IconState icon) {
285 mIsAirplaneMode = icon.visible && !mBlockAirplane;
286 int resId = icon.icon;
287 String description = icon.contentDescription;
288
289 if (mIsAirplaneMode && resId > 0) {
290 mIconController.setIcon(mSlotAirplane, resId, description);
291 mIconController.setIconVisibility(mSlotAirplane, true);
292 } else {
293 mIconController.setIconVisibility(mSlotAirplane, false);
294 }
295 }
296
297 @Override
298 public void setMobileDataEnabled(boolean enabled) {
299 // Don't care.
300 }
301
302 private static abstract class SignalIconState {
303 public boolean visible;
304 public boolean activityOut;
305 public boolean activityIn;
306 public String slot;
307 public String contentDescription;
308
309 @Override
310 public boolean equals(Object o) {
311 // Skipping reference equality bc this should be more of a value type
312 if (o == null || getClass() != o.getClass()) {
313 return false;
314 }
315 SignalIconState that = (SignalIconState) o;
316 return visible == that.visible &&
317 activityOut == that.activityOut &&
318 activityIn == that.activityIn &&
319 Objects.equals(contentDescription, that.contentDescription) &&
320 Objects.equals(slot, that.slot);
321 }
322
323 @Override
324 public int hashCode() {
325 return Objects.hash(visible, activityOut, slot);
326 }
327
328 protected void copyTo(SignalIconState other) {
329 other.visible = visible;
330 other.activityIn = activityIn;
331 other.activityOut = activityOut;
332 other.slot = slot;
333 other.contentDescription = contentDescription;
334 }
335 }
336
337 public static class WifiIconState extends SignalIconState{
338 public int resId;
339 public boolean airplaneSpacerVisible;
340 public boolean signalSpacerVisible;
341
342 @Override
343 public boolean equals(Object o) {
344 // Skipping reference equality bc this should be more of a value type
345 if (o == null || getClass() != o.getClass()) {
346 return false;
347 }
348 if (!super.equals(o)) {
349 return false;
350 }
351 WifiIconState that = (WifiIconState) o;
352 return resId == that.resId &&
353 airplaneSpacerVisible == that.airplaneSpacerVisible &&
354 signalSpacerVisible == that.signalSpacerVisible;
355 }
356
357 public void copyTo(WifiIconState other) {
358 super.copyTo(other);
359 other.resId = resId;
360 other.airplaneSpacerVisible = airplaneSpacerVisible;
361 other.signalSpacerVisible = signalSpacerVisible;
362 }
363
364 public WifiIconState copy() {
365 WifiIconState newState = new WifiIconState();
366 copyTo(newState);
367 return newState;
368 }
369
370 @Override
371 public int hashCode() {
372 return Objects.hash(super.hashCode(),
373 resId, airplaneSpacerVisible, signalSpacerVisible);
374 }
375
376 @Override public String toString() {
377 return "WifiIconState(resId=" + resId + ", visible=" + visible + ")";
378 }
379 }
380
381 /**
382 * A little different. This one delegates to SignalDrawable instead of a specific resId
383 */
384 public static class MobileIconState extends SignalIconState {
385 public int subId;
386 public int strengthId;
387 public int typeId;
388 public boolean roaming;
389 public boolean needsLeadingPadding;
390 public String typeContentDescription;
391
392 private MobileIconState(int subId) {
393 super();
394 this.subId = subId;
395 }
396
397 @Override
398 public boolean equals(Object o) {
399 if (o == null || getClass() != o.getClass()) {
400 return false;
401 }
402 if (!super.equals(o)) {
403 return false;
404 }
405 MobileIconState that = (MobileIconState) o;
406 return subId == that.subId &&
407 strengthId == that.strengthId &&
408 typeId == that.typeId &&
409 roaming == that.roaming &&
410 needsLeadingPadding == that.needsLeadingPadding &&
411 Objects.equals(typeContentDescription, that.typeContentDescription);
412 }
413
414 @Override
415 public int hashCode() {
416
417 return Objects
418 .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding,
419 typeContentDescription);
420 }
421
Evan Laird56fb9f82018-04-24 21:11:15 -0400422 public MobileIconState copy() {
423 MobileIconState copy = new MobileIconState(this.subId);
424 copyTo(copy);
425 return copy;
426 }
427
Evan Lairde1d13c92018-03-20 16:58:01 -0400428 public void copyTo(MobileIconState other) {
429 super.copyTo(other);
430 other.subId = subId;
431 other.strengthId = strengthId;
432 other.typeId = typeId;
433 other.roaming = roaming;
434 other.needsLeadingPadding = needsLeadingPadding;
435 other.typeContentDescription = typeContentDescription;
436 }
437
438 private static List<MobileIconState> copyStates(List<MobileIconState> inStates) {
439 ArrayList<MobileIconState> outStates = new ArrayList<>();
440 for (MobileIconState state : inStates) {
441 MobileIconState copy = new MobileIconState(state.subId);
442 state.copyTo(copy);
443 outStates.add(copy);
444 }
445
446 return outStates;
447 }
448
449 @Override public String toString() {
450 return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
Evan Laird6dedbe62018-04-05 16:22:36 -0400451 + roaming + ", typeId=" + typeId + ", visible=" + visible + ")";
Evan Lairde1d13c92018-03-20 16:58:01 -0400452 }
453 }
454}