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