1 /*
2  * Copyright (C) 2011 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;
18 
19 import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
20 import static android.app.StatusBarManager.DISABLE_NONE;
21 
22 import android.annotation.DrawableRes;
23 import android.content.Context;
24 import android.content.res.ColorStateList;
25 import android.content.res.Resources;
26 import android.graphics.Color;
27 import android.graphics.Rect;
28 import android.graphics.drawable.Drawable;
29 import android.telephony.SubscriptionInfo;
30 import android.util.ArraySet;
31 import android.util.AttributeSet;
32 import android.util.Log;
33 import android.util.TypedValue;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.view.accessibility.AccessibilityEvent;
38 import android.widget.ImageView;
39 import android.widget.LinearLayout;
40 
41 import com.android.settingslib.graph.SignalDrawable;
42 import com.android.systemui.Dependency;
43 import com.android.systemui.R;
44 import com.android.systemui.statusbar.phone.StatusBarIconController;
45 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
46 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
47 import com.android.systemui.statusbar.policy.IconLogger;
48 import com.android.systemui.statusbar.policy.NetworkController;
49 import com.android.systemui.statusbar.policy.NetworkController.IconState;
50 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
51 import com.android.systemui.statusbar.policy.SecurityController;
52 import com.android.systemui.tuner.TunerService;
53 import com.android.systemui.tuner.TunerService.Tunable;
54 import com.android.systemui.util.Utils.DisableStateTracker;
55 
56 import java.util.ArrayList;
57 import java.util.List;
58 
59 // Intimately tied to the design of res/layout/signal_cluster_view.xml
60 public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback,
61         SecurityController.SecurityControllerCallback, Tunable, DarkReceiver {
62 
63     static final String TAG = "SignalClusterView";
64     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
65 
66     private static final String SLOT_AIRPLANE = "airplane";
67     private static final String SLOT_MOBILE = "mobile";
68     private static final String SLOT_WIFI = "wifi";
69     private static final String SLOT_ETHERNET = "ethernet";
70     private static final String SLOT_VPN = "vpn";
71 
72     private final NetworkController mNetworkController;
73     private final SecurityController mSecurityController;
74 
75     private boolean mVpnVisible = false;
76     private int mVpnIconId = 0;
77     private int mLastVpnIconId = -1;
78     private boolean mEthernetVisible = false;
79     private int mEthernetIconId = 0;
80     private int mLastEthernetIconId = -1;
81     private boolean mWifiVisible = false;
82     private int mWifiStrengthId = 0;
83     private int mLastWifiStrengthId = -1;
84     private boolean mWifiIn;
85     private boolean mWifiOut;
86     private boolean mIsAirplaneMode = false;
87     private int mAirplaneIconId = 0;
88     private int mLastAirplaneIconId = -1;
89     private String mAirplaneContentDescription;
90     private String mWifiDescription;
91     private String mEthernetDescription;
92     private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
93     private int mIconTint = Color.WHITE;
94     private float mDarkIntensity;
95     private final Rect mTintArea = new Rect();
96 
97     ViewGroup mEthernetGroup, mWifiGroup;
98     ImageView mVpn, mEthernet, mWifi, mAirplane, mEthernetDark, mWifiDark;
99     ImageView mWifiActivityIn;
100     ImageView mWifiActivityOut;
101     View mWifiAirplaneSpacer;
102     View mWifiSignalSpacer;
103     LinearLayout mMobileSignalGroup;
104 
105     private final int mMobileSignalGroupEndPadding;
106     private final int mMobileDataIconStartPadding;
107     private final int mSecondaryTelephonyPadding;
108     private final int mEndPadding;
109     private final int mEndPaddingNothingVisible;
110     private final float mIconScaleFactor;
111 
112     private boolean mBlockAirplane;
113     private boolean mBlockMobile;
114     private boolean mBlockWifi;
115     private boolean mBlockEthernet;
116     private boolean mActivityEnabled;
117     private boolean mForceBlockWifi;
118 
119     private final IconLogger mIconLogger = Dependency.get(IconLogger.class);
120 
SignalClusterView(Context context)121     public SignalClusterView(Context context) {
122         this(context, null);
123     }
124 
SignalClusterView(Context context, AttributeSet attrs)125     public SignalClusterView(Context context, AttributeSet attrs) {
126         this(context, attrs, 0);
127     }
128 
SignalClusterView(Context context, AttributeSet attrs, int defStyle)129     public SignalClusterView(Context context, AttributeSet attrs, int defStyle) {
130         super(context, attrs, defStyle);
131 
132         Resources res = getResources();
133         mMobileSignalGroupEndPadding =
134                 res.getDimensionPixelSize(R.dimen.mobile_signal_group_end_padding);
135         mMobileDataIconStartPadding =
136                 res.getDimensionPixelSize(R.dimen.mobile_data_icon_start_padding);
137         mSecondaryTelephonyPadding = res.getDimensionPixelSize(R.dimen.secondary_telephony_padding);
138         mEndPadding = res.getDimensionPixelSize(R.dimen.signal_cluster_battery_padding);
139         mEndPaddingNothingVisible = res.getDimensionPixelSize(
140                 R.dimen.no_signal_cluster_battery_padding);
141 
142         TypedValue typedValue = new TypedValue();
143         res.getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
144         mIconScaleFactor = typedValue.getFloat();
145         mNetworkController = Dependency.get(NetworkController.class);
146         mSecurityController = Dependency.get(SecurityController.class);
147         addOnAttachStateChangeListener(
148                 new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
149         updateActivityEnabled();
150     }
151 
setForceBlockWifi()152     public void setForceBlockWifi() {
153         mForceBlockWifi = true;
154         mBlockWifi = true;
155         if (isAttachedToWindow()) {
156             // Re-register to get new callbacks.
157             mNetworkController.removeCallback(this);
158             mNetworkController.addCallback(this);
159         }
160     }
161 
162     @Override
onTuningChanged(String key, String newValue)163     public void onTuningChanged(String key, String newValue) {
164         if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
165             return;
166         }
167         ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue);
168         boolean blockAirplane = blockList.contains(SLOT_AIRPLANE);
169         boolean blockMobile = blockList.contains(SLOT_MOBILE);
170         boolean blockWifi = blockList.contains(SLOT_WIFI);
171         boolean blockEthernet = blockList.contains(SLOT_ETHERNET);
172 
173         if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile
174                 || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) {
175             mBlockAirplane = blockAirplane;
176             mBlockMobile = blockMobile;
177             mBlockEthernet = blockEthernet;
178             mBlockWifi = blockWifi || mForceBlockWifi;
179             // Re-register to get new callbacks.
180             mNetworkController.removeCallback(this);
181             mNetworkController.addCallback(this);
182         }
183     }
184 
185     @Override
onFinishInflate()186     protected void onFinishInflate() {
187         super.onFinishInflate();
188 
189         mVpn            = findViewById(R.id.vpn);
190         mEthernetGroup  = findViewById(R.id.ethernet_combo);
191         mEthernet       = findViewById(R.id.ethernet);
192         mEthernetDark   = findViewById(R.id.ethernet_dark);
193         mWifiGroup      = findViewById(R.id.wifi_combo);
194         mWifi           = findViewById(R.id.wifi_signal);
195         mWifiDark       = findViewById(R.id.wifi_signal_dark);
196         mWifiActivityIn = findViewById(R.id.wifi_in);
197         mWifiActivityOut= findViewById(R.id.wifi_out);
198         mAirplane       = findViewById(R.id.airplane);
199         mWifiAirplaneSpacer =         findViewById(R.id.wifi_airplane_spacer);
200         mWifiSignalSpacer =           findViewById(R.id.wifi_signal_spacer);
201         mMobileSignalGroup =          findViewById(R.id.mobile_signal_group);
202 
203         maybeScaleVpnAndNoSimsIcons();
204     }
205 
206     /**
207      * Extracts the icon off of the VPN and no sims views and maybe scale them by
208      * {@link #mIconScaleFactor}. Note that the other icons are not scaled here because they are
209      * dynamic. As such, they need to be scaled each time the icon changes in {@link #apply()}.
210      */
maybeScaleVpnAndNoSimsIcons()211     private void maybeScaleVpnAndNoSimsIcons() {
212         if (mIconScaleFactor == 1.f) {
213             return;
214         }
215 
216         mVpn.setImageDrawable(new ScalingDrawableWrapper(mVpn.getDrawable(), mIconScaleFactor));
217     }
218 
219     @Override
onAttachedToWindow()220     protected void onAttachedToWindow() {
221         super.onAttachedToWindow();
222         mVpnVisible = mSecurityController.isVpnEnabled();
223         mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
224 
225         for (PhoneState state : mPhoneStates) {
226             if (state.mMobileGroup.getParent() == null) {
227                 mMobileSignalGroup.addView(state.mMobileGroup);
228             }
229         }
230 
231         int endPadding = mMobileSignalGroup.getChildCount() > 0 ? mMobileSignalGroupEndPadding : 0;
232         mMobileSignalGroup.setPaddingRelative(0, 0, endPadding, 0);
233 
234         Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
235 
236         apply();
237         applyIconTint();
238         mNetworkController.addCallback(this);
239         mSecurityController.addCallback(this);
240     }
241 
242     @Override
onDetachedFromWindow()243     protected void onDetachedFromWindow() {
244         mMobileSignalGroup.removeAllViews();
245         Dependency.get(TunerService.class).removeTunable(this);
246         mSecurityController.removeCallback(this);
247         mNetworkController.removeCallback(this);
248 
249         super.onDetachedFromWindow();
250     }
251 
252     @Override
onLayout(boolean changed, int l, int t, int r, int b)253     protected void onLayout(boolean changed, int l, int t, int r, int b) {
254         super.onLayout(changed, l, t, r, b);
255 
256         // Re-run all checks against the tint area for all icons
257         applyIconTint();
258     }
259 
260     // From SecurityController.
261     @Override
onStateChanged()262     public void onStateChanged() {
263         post(new Runnable() {
264             @Override
265             public void run() {
266                 mVpnVisible = mSecurityController.isVpnEnabled();
267                 mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
268                 apply();
269             }
270         });
271     }
272 
updateActivityEnabled()273     private void updateActivityEnabled() {
274         mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
275     }
276 
277     @Override
setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, boolean activityIn, boolean activityOut, String description, boolean isTransient, String secondaryLabel)278     public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
279             boolean activityIn, boolean activityOut, String description, boolean isTransient,
280             String secondaryLabel) {
281         mWifiVisible = statusIcon.visible && !mBlockWifi;
282         mWifiStrengthId = statusIcon.icon;
283         mWifiDescription = statusIcon.contentDescription;
284         mWifiIn = activityIn && mActivityEnabled && mWifiVisible;
285         mWifiOut = activityOut && mActivityEnabled && mWifiVisible;
286 
287         apply();
288     }
289 
290     @Override
setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, String description, boolean isWide, int subId, boolean roaming)291     public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
292             int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
293             String description, boolean isWide, int subId, boolean roaming) {
294         PhoneState state = getState(subId);
295         if (state == null) {
296             return;
297         }
298         state.mMobileVisible = statusIcon.visible && !mBlockMobile;
299         state.mMobileStrengthId = statusIcon.icon;
300         state.mMobileTypeId = statusType;
301         state.mMobileDescription = statusIcon.contentDescription;
302         state.mMobileTypeDescription = typeContentDescription;
303         state.mRoaming = roaming;
304         state.mActivityIn = activityIn && mActivityEnabled;
305         state.mActivityOut = activityOut && mActivityEnabled;
306 
307         apply();
308     }
309 
310     @Override
setEthernetIndicators(IconState state)311     public void setEthernetIndicators(IconState state) {
312         mEthernetVisible = state.visible && !mBlockEthernet;
313         mEthernetIconId = state.icon;
314         mEthernetDescription = state.contentDescription;
315 
316         apply();
317     }
318 
319     @Override
setNoSims(boolean show, boolean simDetected)320     public void setNoSims(boolean show, boolean simDetected) {
321         // Noop. Status bar no longer shows no sim icon.
322     }
323 
324     @Override
setSubs(List<SubscriptionInfo> subs)325     public void setSubs(List<SubscriptionInfo> subs) {
326         if (hasCorrectSubs(subs)) {
327             return;
328         }
329         mPhoneStates.clear();
330         if (mMobileSignalGroup != null) {
331             mMobileSignalGroup.removeAllViews();
332         }
333         final int n = subs.size();
334         for (int i = 0; i < n; i++) {
335             inflatePhoneState(subs.get(i).getSubscriptionId());
336         }
337         if (isAttachedToWindow()) {
338             applyIconTint();
339         }
340     }
341 
hasCorrectSubs(List<SubscriptionInfo> subs)342     private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
343         final int N = subs.size();
344         if (N != mPhoneStates.size()) {
345             return false;
346         }
347         for (int i = 0; i < N; i++) {
348             if (mPhoneStates.get(i).mSubId != subs.get(i).getSubscriptionId()) {
349                 return false;
350             }
351         }
352         return true;
353     }
354 
getState(int subId)355     private PhoneState getState(int subId) {
356         for (PhoneState state : mPhoneStates) {
357             if (state.mSubId == subId) {
358                 return state;
359             }
360         }
361         Log.e(TAG, "Unexpected subscription " + subId);
362         return null;
363     }
364 
inflatePhoneState(int subId)365     private PhoneState inflatePhoneState(int subId) {
366         PhoneState state = new PhoneState(subId, mContext);
367         if (mMobileSignalGroup != null) {
368             mMobileSignalGroup.addView(state.mMobileGroup);
369         }
370         mPhoneStates.add(state);
371         return state;
372     }
373 
374     @Override
setIsAirplaneMode(IconState icon)375     public void setIsAirplaneMode(IconState icon) {
376         mIsAirplaneMode = icon.visible && !mBlockAirplane;
377         mAirplaneIconId = icon.icon;
378         mAirplaneContentDescription = icon.contentDescription;
379 
380         apply();
381     }
382 
383     @Override
setMobileDataEnabled(boolean enabled)384     public void setMobileDataEnabled(boolean enabled) {
385         // Don't care.
386     }
387 
388     @Override
dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)389     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
390         // Standard group layout onPopulateAccessibilityEvent() implementations
391         // ignore content description, so populate manually
392         if (mEthernetVisible && mEthernetGroup != null &&
393                 mEthernetGroup.getContentDescription() != null)
394             event.getText().add(mEthernetGroup.getContentDescription());
395         if (mWifiVisible && mWifiGroup != null && mWifiGroup.getContentDescription() != null)
396             event.getText().add(mWifiGroup.getContentDescription());
397         for (PhoneState state : mPhoneStates) {
398             state.populateAccessibilityEvent(event);
399         }
400         return super.dispatchPopulateAccessibilityEventInternal(event);
401     }
402 
403     @Override
onRtlPropertiesChanged(int layoutDirection)404     public void onRtlPropertiesChanged(int layoutDirection) {
405         super.onRtlPropertiesChanged(layoutDirection);
406 
407         if (mEthernet != null) {
408             mEthernet.setImageDrawable(null);
409             mEthernetDark.setImageDrawable(null);
410             mLastEthernetIconId = -1;
411         }
412 
413         if (mWifi != null) {
414             mWifi.setImageDrawable(null);
415             mWifiDark.setImageDrawable(null);
416             mLastWifiStrengthId = -1;
417         }
418 
419         for (PhoneState state : mPhoneStates) {
420             if (state.mMobileType != null) {
421                 state.mMobileType.setImageDrawable(null);
422                 state.mLastMobileTypeId = -1;
423             }
424         }
425 
426         if (mAirplane != null) {
427             mAirplane.setImageDrawable(null);
428             mLastAirplaneIconId = -1;
429         }
430 
431         apply();
432     }
433 
434     @Override
hasOverlappingRendering()435     public boolean hasOverlappingRendering() {
436         return false;
437     }
438 
439     // Run after each indicator change.
apply()440     private void apply() {
441         if (mWifiGroup == null) return;
442 
443         if (mVpnVisible) {
444             if (mLastVpnIconId != mVpnIconId) {
445                 setIconForView(mVpn, mVpnIconId);
446                 mLastVpnIconId = mVpnIconId;
447             }
448             mIconLogger.onIconShown(SLOT_VPN);
449             mVpn.setVisibility(View.VISIBLE);
450         } else {
451             mIconLogger.onIconHidden(SLOT_VPN);
452             mVpn.setVisibility(View.GONE);
453         }
454         if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE"));
455 
456         if (mEthernetVisible) {
457             if (mLastEthernetIconId != mEthernetIconId) {
458                 setIconForView(mEthernet, mEthernetIconId);
459                 setIconForView(mEthernetDark, mEthernetIconId);
460                 mLastEthernetIconId = mEthernetIconId;
461             }
462             mEthernetGroup.setContentDescription(mEthernetDescription);
463             mIconLogger.onIconShown(SLOT_ETHERNET);
464             mEthernetGroup.setVisibility(View.VISIBLE);
465         } else {
466             mIconLogger.onIconHidden(SLOT_ETHERNET);
467             mEthernetGroup.setVisibility(View.GONE);
468         }
469 
470         if (DEBUG) Log.d(TAG,
471                 String.format("ethernet: %s",
472                     (mEthernetVisible ? "VISIBLE" : "GONE")));
473 
474         if (mWifiVisible) {
475             if (mWifiStrengthId != mLastWifiStrengthId) {
476                 setIconForView(mWifi, mWifiStrengthId);
477                 setIconForView(mWifiDark, mWifiStrengthId);
478                 mLastWifiStrengthId = mWifiStrengthId;
479             }
480             mIconLogger.onIconShown(SLOT_WIFI);
481             mWifiGroup.setContentDescription(mWifiDescription);
482             mWifiGroup.setVisibility(View.VISIBLE);
483         } else {
484             mIconLogger.onIconHidden(SLOT_WIFI);
485             mWifiGroup.setVisibility(View.GONE);
486         }
487 
488         if (DEBUG) Log.d(TAG,
489                 String.format("wifi: %s sig=%d",
490                     (mWifiVisible ? "VISIBLE" : "GONE"),
491                     mWifiStrengthId));
492 
493         mWifiActivityIn.setVisibility(mWifiIn ? View.VISIBLE : View.GONE);
494         mWifiActivityOut.setVisibility(mWifiOut ? View.VISIBLE : View.GONE);
495 
496         boolean anyMobileVisible = false;
497         int firstMobileTypeId = 0;
498         for (PhoneState state : mPhoneStates) {
499             if (state.apply(anyMobileVisible)) {
500                 if (!anyMobileVisible) {
501                     firstMobileTypeId = state.mMobileTypeId;
502                     anyMobileVisible = true;
503                 }
504             }
505         }
506         if (anyMobileVisible) {
507             mIconLogger.onIconShown(SLOT_MOBILE);
508         } else {
509             mIconLogger.onIconHidden(SLOT_MOBILE);
510         }
511 
512         if (mIsAirplaneMode) {
513             if (mLastAirplaneIconId != mAirplaneIconId) {
514                 setIconForView(mAirplane, mAirplaneIconId);
515                 mLastAirplaneIconId = mAirplaneIconId;
516             }
517             mAirplane.setContentDescription(mAirplaneContentDescription);
518             mIconLogger.onIconShown(SLOT_AIRPLANE);
519             mAirplane.setVisibility(View.VISIBLE);
520         } else {
521             mIconLogger.onIconHidden(SLOT_AIRPLANE);
522             mAirplane.setVisibility(View.GONE);
523         }
524 
525         if (mIsAirplaneMode && mWifiVisible) {
526             mWifiAirplaneSpacer.setVisibility(View.VISIBLE);
527         } else {
528             mWifiAirplaneSpacer.setVisibility(View.GONE);
529         }
530 
531         if ((anyMobileVisible && firstMobileTypeId != 0) && mWifiVisible) {
532             mWifiSignalSpacer.setVisibility(View.VISIBLE);
533         } else {
534             mWifiSignalSpacer.setVisibility(View.GONE);
535         }
536 
537         boolean anythingVisible = mWifiVisible || mIsAirplaneMode
538                 || anyMobileVisible || mVpnVisible || mEthernetVisible;
539         setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0);
540     }
541 
542     /**
543      * Sets the given drawable id on the view. This method will also scale the icon by
544      * {@link #mIconScaleFactor} if appropriate.
545      */
setIconForView(ImageView imageView, @DrawableRes int iconId)546     private void setIconForView(ImageView imageView, @DrawableRes int iconId) {
547         // Using the imageView's context to retrieve the Drawable so that theme is preserved.
548         Drawable icon = imageView.getContext().getDrawable(iconId);
549 
550         if (mIconScaleFactor == 1.f) {
551             imageView.setImageDrawable(icon);
552         } else {
553             imageView.setImageDrawable(new ScalingDrawableWrapper(icon, mIconScaleFactor));
554         }
555     }
556 
557 
558     @Override
onDarkChanged(Rect tintArea, float darkIntensity, int tint)559     public void onDarkChanged(Rect tintArea, float darkIntensity, int tint) {
560         boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity
561                 || !mTintArea.equals(tintArea);
562         mIconTint = tint;
563         mDarkIntensity = darkIntensity;
564         mTintArea.set(tintArea);
565         if (changed && isAttachedToWindow()) {
566             applyIconTint();
567         }
568     }
569 
applyIconTint()570     private void applyIconTint() {
571         setTint(mVpn, DarkIconDispatcher.getTint(mTintArea, mVpn, mIconTint));
572         setTint(mAirplane, DarkIconDispatcher.getTint(mTintArea, mAirplane, mIconTint));
573         applyDarkIntensity(
574                 DarkIconDispatcher.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
575                 mWifi, mWifiDark);
576         setTint(mWifiActivityIn,
577                 DarkIconDispatcher.getTint(mTintArea, mWifiActivityIn, mIconTint));
578         setTint(mWifiActivityOut,
579                 DarkIconDispatcher.getTint(mTintArea, mWifiActivityOut, mIconTint));
580         applyDarkIntensity(
581                 DarkIconDispatcher.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity),
582                 mEthernet, mEthernetDark);
583         for (int i = 0; i < mPhoneStates.size(); i++) {
584             mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity, mTintArea);
585         }
586     }
587 
applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon)588     private void applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon) {
589         lightIcon.setAlpha(1 - darkIntensity);
590         darkIcon.setAlpha(darkIntensity);
591     }
592 
setTint(ImageView v, int tint)593     private void setTint(ImageView v, int tint) {
594         v.setImageTintList(ColorStateList.valueOf(tint));
595     }
596 
currentVpnIconId(boolean isBranded)597     private int currentVpnIconId(boolean isBranded) {
598         return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
599     }
600 
601     private class PhoneState {
602         private final int mSubId;
603         private boolean mMobileVisible = false;
604         private int mMobileStrengthId = 0, mMobileTypeId = 0;
605         private int mLastMobileStrengthId = -1;
606         private int mLastMobileTypeId = -1;
607         private String mMobileDescription, mMobileTypeDescription;
608 
609         private ViewGroup mMobileGroup;
610         private ImageView mMobile, mMobileType, mMobileRoaming;
611         private View mMobileRoamingSpace;
612         public boolean mRoaming;
613         private ImageView mMobileActivityIn;
614         private ImageView mMobileActivityOut;
615         public boolean mActivityIn;
616         public boolean mActivityOut;
617         private SignalDrawable mMobileSignalDrawable;
618 
PhoneState(int subId, Context context)619         public PhoneState(int subId, Context context) {
620             ViewGroup root = (ViewGroup) LayoutInflater.from(context)
621                     .inflate(R.layout.mobile_signal_group, null);
622             setViews(root);
623             mSubId = subId;
624         }
625 
setViews(ViewGroup root)626         public void setViews(ViewGroup root) {
627             mMobileGroup    = root;
628             mMobile         = root.findViewById(R.id.mobile_signal);
629             mMobileType     = root.findViewById(R.id.mobile_type);
630             mMobileRoaming  = root.findViewById(R.id.mobile_roaming);
631             mMobileRoamingSpace  = root.findViewById(R.id.mobile_roaming_space);
632             mMobileActivityIn = root.findViewById(R.id.mobile_in);
633             mMobileActivityOut = root.findViewById(R.id.mobile_out);
634             mMobileSignalDrawable = new SignalDrawable(mMobile.getContext());
635             mMobile.setImageDrawable(mMobileSignalDrawable);
636         }
637 
apply(boolean isSecondaryIcon)638         public boolean apply(boolean isSecondaryIcon) {
639             if (mMobileVisible && !mIsAirplaneMode) {
640                 if (mLastMobileStrengthId != mMobileStrengthId) {
641                     mMobile.getDrawable().setLevel(mMobileStrengthId);
642                     mLastMobileStrengthId = mMobileStrengthId;
643                 }
644 
645                 if (mLastMobileTypeId != mMobileTypeId) {
646                     mMobileType.setImageResource(mMobileTypeId);
647                     mLastMobileTypeId = mMobileTypeId;
648                 }
649 
650                 mMobileGroup.setContentDescription(mMobileTypeDescription
651                         + " " + mMobileDescription);
652                 mMobileGroup.setVisibility(View.VISIBLE);
653             } else {
654                 mMobileGroup.setVisibility(View.GONE);
655             }
656 
657             // When this isn't next to wifi, give it some extra padding between the signals.
658             mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0,
659                     0, 0, 0);
660             mMobile.setPaddingRelative(mMobileDataIconStartPadding, 0, 0, 0);
661 
662             if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
663                         (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
664 
665             mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
666             mMobileRoaming.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
667             mMobileRoamingSpace.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
668             mMobileActivityIn.setVisibility(mActivityIn ? View.VISIBLE : View.GONE);
669             mMobileActivityOut.setVisibility(mActivityOut ? View.VISIBLE : View.GONE);
670 
671             return mMobileVisible;
672         }
673 
populateAccessibilityEvent(AccessibilityEvent event)674         public void populateAccessibilityEvent(AccessibilityEvent event) {
675             if (mMobileVisible && mMobileGroup != null
676                     && mMobileGroup.getContentDescription() != null) {
677                 event.getText().add(mMobileGroup.getContentDescription());
678             }
679         }
680 
setIconTint(int tint, float darkIntensity, Rect tintArea)681         public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
682             mMobileSignalDrawable.setDarkIntensity(darkIntensity);
683             setTint(mMobileType, DarkIconDispatcher.getTint(tintArea, mMobileType, tint));
684             setTint(mMobileRoaming, DarkIconDispatcher.getTint(tintArea, mMobileRoaming,
685                     tint));
686             setTint(mMobileActivityIn,
687                     DarkIconDispatcher.getTint(tintArea, mMobileActivityIn, tint));
688             setTint(mMobileActivityOut,
689                     DarkIconDispatcher.getTint(tintArea, mMobileActivityOut, tint));
690         }
691     }
692 }
693