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