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