2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.statusbar;
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;
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;
55 import java.util.ArrayList;
56 import java.util.List;
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,
63 static final String TAG = "SignalClusterView";
64 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
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";
71 private final NetworkController mNetworkController;
72 private final SecurityController mSecurityController;
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();
100 ViewGroup mEthernetGroup, mWifiGroup;
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;
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;
117 private boolean mBlockAirplane;
118 private boolean mBlockMobile;
119 private boolean mBlockWifi;
120 private boolean mBlockEthernet;
121 private boolean mActivityEnabled;
123 public SignalClusterView(Context context) {
127 public SignalClusterView(Context context, AttributeSet attrs) {
128 this(context, attrs, 0);
131 public SignalClusterView(Context context, AttributeSet attrs, int defStyle) {
132 super(context, attrs, defStyle);
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);
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();
154 public void onTuningChanged(String key, String newValue) {
155 if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
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);
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);
177 protected void onFinishInflate() {
178 super.onFinishInflate();
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);
197 maybeScaleVpnAndNoSimsIcons();
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()}.
205 private void maybeScaleVpnAndNoSimsIcons() {
206 if (mIconScaleFactor == 1.f) {
210 mVpn.setImageDrawable(new ScalingDrawableWrapper(mVpn.getDrawable(), mIconScaleFactor));
212 mNoSims.setImageDrawable(
213 new ScalingDrawableWrapper(mNoSims.getDrawable(), mIconScaleFactor));
214 mNoSimsDark.setImageDrawable(
215 new ScalingDrawableWrapper(mNoSimsDark.getDrawable(), mIconScaleFactor));
219 protected void onAttachedToWindow() {
220 super.onAttachedToWindow();
221 mVpnVisible = mSecurityController.isVpnEnabled();
222 mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
224 for (PhoneState state : mPhoneStates) {
225 if (state.mMobileGroup.getParent() == null) {
226 mMobileSignalGroup.addView(state.mMobileGroup);
230 int endPadding = mMobileSignalGroup.getChildCount() > 0 ? mMobileSignalGroupEndPadding : 0;
231 mMobileSignalGroup.setPaddingRelative(0, 0, endPadding, 0);
233 Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
237 mNetworkController.addCallback(this);
238 mSecurityController.addCallback(this);
242 protected void onDetachedFromWindow() {
243 mMobileSignalGroup.removeAllViews();
244 Dependency.get(TunerService.class).removeTunable(this);
245 mSecurityController.removeCallback(this);
246 mNetworkController.removeCallback(this);
248 super.onDetachedFromWindow();
252 protected void onLayout(boolean changed, int l, int t, int r, int b) {
253 super.onLayout(changed, l, t, r, b);
255 // Re-run all checks against the tint area for all icons
259 // From SecurityController.
261 public void onStateChanged() {
262 post(new Runnable() {
265 mVpnVisible = mSecurityController.isVpnEnabled();
266 mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
272 private void updateActivityEnabled() {
273 mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
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;
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);
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;
311 public void setEthernetIndicators(IconState state) {
312 mEthernetVisible = state.visible && !mBlockEthernet;
313 mEthernetIconId = state.icon;
314 mEthernetDescription = state.contentDescription;
320 public void setNoSims(boolean show) {
321 mNoSimsVisible = show && !mBlockMobile;
326 public void setSubs(List<SubscriptionInfo> subs) {
327 if (hasCorrectSubs(subs)) {
330 // Clear out all old subIds.
331 for (PhoneState state : mPhoneStates) {
332 if (state.mMobile != null) {
333 state.maybeStopAnimatableDrawable(state.mMobile);
335 if (state.mMobileDark != null) {
336 state.maybeStopAnimatableDrawable(state.mMobileDark);
339 mPhoneStates.clear();
340 if (mMobileSignalGroup != null) {
341 mMobileSignalGroup.removeAllViews();
343 final int n = subs.size();
344 for (int i = 0; i < n; i++) {
345 inflatePhoneState(subs.get(i).getSubscriptionId());
347 if (isAttachedToWindow()) {
352 private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
353 final int N = subs.size();
354 if (N != mPhoneStates.size()) {
357 for (int i = 0; i < N; i++) {
358 if (mPhoneStates.get(i).mSubId != subs.get(i).getSubscriptionId()) {
365 private PhoneState getState(int subId) {
366 for (PhoneState state : mPhoneStates) {
367 if (state.mSubId == subId) {
371 Log.e(TAG, "Unexpected subscription " + subId);
375 private PhoneState inflatePhoneState(int subId) {
376 PhoneState state = new PhoneState(subId, mContext);
377 if (mMobileSignalGroup != null) {
378 mMobileSignalGroup.addView(state.mMobileGroup);
380 mPhoneStates.add(state);
385 public void setIsAirplaneMode(IconState icon) {
386 mIsAirplaneMode = icon.visible && !mBlockAirplane;
387 mAirplaneIconId = icon.icon;
388 mAirplaneContentDescription = icon.contentDescription;
394 public void setMobileDataEnabled(boolean enabled) {
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);
410 return super.dispatchPopulateAccessibilityEventInternal(event);
414 public void onRtlPropertiesChanged(int layoutDirection) {
415 super.onRtlPropertiesChanged(layoutDirection);
417 if (mEthernet != null) {
418 mEthernet.setImageDrawable(null);
419 mEthernetDark.setImageDrawable(null);
420 mLastEthernetIconId = -1;
424 mWifi.setImageDrawable(null);
425 mWifiDark.setImageDrawable(null);
426 mLastWifiStrengthId = -1;
427 mLastWifiBadgeId = -1;
430 for (PhoneState state : mPhoneStates) {
431 if (state.mMobile != null) {
432 state.maybeStopAnimatableDrawable(state.mMobile);
433 state.mMobile.setImageDrawable(null);
434 state.mLastMobileStrengthId = -1;
436 if (state.mMobileDark != null) {
437 state.maybeStopAnimatableDrawable(state.mMobileDark);
438 state.mMobileDark.setImageDrawable(null);
439 state.mLastMobileStrengthId = -1;
441 if (state.mMobileType != null) {
442 state.mMobileType.setImageDrawable(null);
443 state.mLastMobileTypeId = -1;
447 if (mAirplane != null) {
448 mAirplane.setImageDrawable(null);
449 mLastAirplaneIconId = -1;
456 public boolean hasOverlappingRendering() {
460 // Run after each indicator change.
461 private void apply() {
462 if (mWifiGroup == null) return;
464 mVpn.setVisibility(mVpnVisible ? View.VISIBLE : View.GONE);
466 if (mLastVpnIconId != mVpnIconId) {
467 setIconForView(mVpn, mVpnIconId);
468 mLastVpnIconId = mVpnIconId;
470 mVpn.setVisibility(View.VISIBLE);
472 mVpn.setVisibility(View.GONE);
474 if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE"));
476 if (mEthernetVisible) {
477 if (mLastEthernetIconId != mEthernetIconId) {
478 setIconForView(mEthernet, mEthernetIconId);
479 setIconForView(mEthernetDark, mEthernetIconId);
480 mLastEthernetIconId = mEthernetIconId;
482 mEthernetGroup.setContentDescription(mEthernetDescription);
483 mEthernetGroup.setVisibility(View.VISIBLE);
485 mEthernetGroup.setVisibility(View.GONE);
488 if (DEBUG) Log.d(TAG,
489 String.format("ethernet: %s",
490 (mEthernetVisible ? "VISIBLE" : "GONE")));
493 if (mWifiStrengthId != mLastWifiStrengthId || mWifiBadgeId != mLastWifiBadgeId) {
494 if (mWifiBadgeId == -1) {
495 setIconForView(mWifi, mWifiStrengthId);
496 setIconForView(mWifiDark, mWifiStrengthId);
498 setBadgedWifiIconForView(mWifi, mWifiStrengthId, mWifiBadgeId);
499 setBadgedWifiIconForView(mWifiDark, mWifiStrengthId, mWifiBadgeId);
501 mLastWifiStrengthId = mWifiStrengthId;
502 mLastWifiBadgeId = mWifiBadgeId;
504 mWifiGroup.setContentDescription(mWifiDescription);
505 mWifiGroup.setVisibility(View.VISIBLE);
507 mWifiGroup.setVisibility(View.GONE);
510 if (DEBUG) Log.d(TAG,
511 String.format("wifi: %s sig=%d",
512 (mWifiVisible ? "VISIBLE" : "GONE"),
515 mWifiActivityIn.setVisibility(mWifiIn ? View.VISIBLE : View.GONE);
516 mWifiActivityOut.setVisibility(mWifiOut ? View.VISIBLE : View.GONE);
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;
529 if (mIsAirplaneMode) {
530 if (mLastAirplaneIconId != mAirplaneIconId) {
531 setIconForView(mAirplane, mAirplaneIconId);
532 mLastAirplaneIconId = mAirplaneIconId;
534 mAirplane.setContentDescription(mAirplaneContentDescription);
535 mAirplane.setVisibility(View.VISIBLE);
537 mAirplane.setVisibility(View.GONE);
540 if (mIsAirplaneMode && mWifiVisible) {
541 mWifiAirplaneSpacer.setVisibility(View.VISIBLE);
543 mWifiAirplaneSpacer.setVisibility(View.GONE);
546 if (((anyMobileVisible && firstMobileTypeId != 0) || mNoSimsVisible) && mWifiVisible) {
547 mWifiSignalSpacer.setVisibility(View.VISIBLE);
549 mWifiSignalSpacer.setVisibility(View.GONE);
552 mNoSimsCombo.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE);
554 boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode
555 || anyMobileVisible || mVpnVisible || mEthernetVisible;
556 setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0);
560 * Sets the given drawable id on the view. This method will also scale the icon by
561 * {@link #mIconScaleFactor} if appropriate.
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);
567 setScaledIcon(imageView, icon);
570 private void setScaledIcon(ImageView imageView, Drawable icon) {
571 if (mIconScaleFactor == 1.f) {
572 imageView.setImageDrawable(icon);
574 imageView.setImageDrawable(new ScalingDrawableWrapper(icon, mIconScaleFactor));
579 * Creates and sets a LayerDrawable from the given ids on the given view.
581 * <p>This method will also scale the icon by {@link #mIconScaleFactor} if appropriate.
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)});
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));
594 setScaledIcon(imageView, icon);
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);
606 public void onDarkChanged(Rect tintArea, float darkIntensity, int tint) {
607 boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity
608 || !mTintArea.equals(tintArea);
610 mDarkIntensity = darkIntensity;
611 mTintArea.set(tintArea);
612 if (changed && isAttachedToWindow()) {
617 private void applyIconTint() {
618 setTint(mVpn, DarkIconDispatcher.getTint(mTintArea, mVpn, mIconTint));
619 setTint(mAirplane, DarkIconDispatcher.getTint(mTintArea, mAirplane, mIconTint));
621 DarkIconDispatcher.getDarkIntensity(mTintArea, mNoSims, mDarkIntensity),
622 mNoSims, mNoSimsDark);
624 DarkIconDispatcher.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
626 setTint(mWifiActivityIn,
627 DarkIconDispatcher.getTint(mTintArea, mWifiActivityIn, mIconTint));
628 setTint(mWifiActivityOut,
629 DarkIconDispatcher.getTint(mTintArea, mWifiActivityOut, mIconTint));
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);
638 private void applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon) {
639 lightIcon.setAlpha(1 - darkIntensity);
640 darkIcon.setAlpha(darkIntensity);
643 private void setTint(ImageView v, int tint) {
644 v.setImageTintList(ColorStateList.valueOf(tint));
647 private int currentVpnIconId(boolean isBranded) {
648 return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
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;
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;
669 public PhoneState(int subId, Context context) {
670 ViewGroup root = (ViewGroup) LayoutInflater.from(context)
671 .inflate(R.layout.mobile_signal_group, null);
676 public void setViews(ViewGroup 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);
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;
694 if (mLastMobileTypeId != mMobileTypeId) {
695 mMobileType.setImageResource(mMobileTypeId);
696 mLastMobileTypeId = mMobileTypeId;
699 mMobileGroup.setContentDescription(mMobileTypeDescription
700 + " " + mMobileDescription);
701 mMobileGroup.setVisibility(View.VISIBLE);
703 mMobileGroup.setVisibility(View.GONE);
706 // When this isn't next to wifi, give it some extra padding between the signals.
707 mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0,
709 mMobile.setPaddingRelative(
710 mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding,
712 mMobileDark.setPaddingRelative(
713 mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding,
716 if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
717 (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
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);
724 return mMobileVisible;
727 private void updateAnimatableIcon(ImageView view, int resId) {
728 maybeStopAnimatableDrawable(view);
729 setIconForView(view, resId);
730 maybeStartAnimatableDrawable(view);
733 private void maybeStopAnimatableDrawable(ImageView view) {
734 Drawable drawable = view.getDrawable();
736 // Check if the icon has been scaled. If it has retrieve the actual drawable out of the
738 if (drawable instanceof ScalingDrawableWrapper) {
739 drawable = ((ScalingDrawableWrapper) drawable).getDrawable();
742 if (drawable instanceof Animatable) {
743 Animatable ad = (Animatable) drawable;
744 if (ad.isRunning()) {
750 private void maybeStartAnimatableDrawable(ImageView view) {
751 Drawable drawable = view.getDrawable();
753 // Check if the icon has been scaled. If it has retrieve the actual drawable out of the
755 if (drawable instanceof ScalingDrawableWrapper) {
756 drawable = ((ScalingDrawableWrapper) drawable).getDrawable();
759 if (drawable instanceof Animatable) {
760 Animatable ad = (Animatable) drawable;
761 if (ad instanceof AnimatedVectorDrawable) {
762 ((AnimatedVectorDrawable) ad).forceAnimationOnUI();
764 if (!ad.isRunning()) {
770 public void populateAccessibilityEvent(AccessibilityEvent event) {
771 if (mMobileVisible && mMobileGroup != null
772 && mMobileGroup.getContentDescription() != null) {
773 event.getText().add(mMobileGroup.getContentDescription());
777 public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
779 DarkIconDispatcher.getDarkIntensity(tintArea, mMobile, darkIntensity),
780 mMobile, mMobileDark);
781 setTint(mMobileType, DarkIconDispatcher.getTint(tintArea, mMobileType, tint));
782 setTint(mMobileRoaming, DarkIconDispatcher.getTint(tintArea, mMobileRoaming,
784 setTint(mMobileActivityIn,
785 DarkIconDispatcher.getTint(tintArea, mMobileActivityIn, tint));
786 setTint(mMobileActivityOut,
787 DarkIconDispatcher.getTint(tintArea, mMobileActivityOut, tint));