OSDN Git Service

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