Home | History | Annotate | Download | only in statusbar
      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 
    121     public SignalClusterView(Context context) {
    122         this(context, null);
    123     }
    124 
    125     public SignalClusterView(Context context, AttributeSet attrs) {
    126         this(context, attrs, 0);
    127     }
    128 
    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 
    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
    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
    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      */
    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
    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
    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
    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
    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 
    273     private void updateActivityEnabled() {
    274         mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
    275     }
    276 
    277     @Override
    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
    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
    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, boolean simDetected) {
    321         // Noop. Status bar no longer shows no sim icon.
    322     }
    323 
    324     @Override
    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 
    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 
    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 
    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
    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
    384     public void setMobileDataEnabled(boolean enabled) {
    385         // Don't care.
    386     }
    387 
    388     @Override
    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
    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
    435     public boolean hasOverlappingRendering() {
    436         return false;
    437     }
    438 
    439     // Run after each indicator change.
    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      */
    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
    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 
    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 
    588     private void applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon) {
    589         lightIcon.setAlpha(1 - darkIntensity);
    590         darkIcon.setAlpha(darkIntensity);
    591     }
    592 
    593     private void setTint(ImageView v, int tint) {
    594         v.setImageTintList(ColorStateList.valueOf(tint));
    595     }
    596 
    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 
    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 
    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 
    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 
    674         public void populateAccessibilityEvent(AccessibilityEvent event) {
    675             if (mMobileVisible && mMobileGroup != null
    676                     && mMobileGroup.getContentDescription() != null) {
    677                 event.getText().add(mMobileGroup.getContentDescription());
    678             }
    679         }
    680 
    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