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 SignalClusterView(Context context)121 public SignalClusterView(Context context) { 122 this(context, null); 123 } 124 SignalClusterView(Context context, AttributeSet attrs)125 public SignalClusterView(Context context, AttributeSet attrs) { 126 this(context, attrs, 0); 127 } 128 SignalClusterView(Context context, AttributeSet attrs, int defStyle)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 setForceBlockWifi()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 onTuningChanged(String key, String newValue)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 onFinishInflate()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 */ maybeScaleVpnAndNoSimsIcons()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 onAttachedToWindow()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 onDetachedFromWindow()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 onLayout(boolean changed, int l, int t, int r, int b)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 onStateChanged()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 updateActivityEnabled()273 private void updateActivityEnabled() { 274 mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); 275 } 276 277 @Override setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, boolean activityIn, boolean activityOut, String description, boolean isTransient, String secondaryLabel)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 setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, String description, boolean isWide, int subId, boolean roaming)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 setEthernetIndicators(IconState state)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 setNoSims(boolean show, boolean simDetected)320 public void setNoSims(boolean show, boolean simDetected) { 321 // Noop. Status bar no longer shows no sim icon. 322 } 323 324 @Override setSubs(List<SubscriptionInfo> subs)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 hasCorrectSubs(List<SubscriptionInfo> subs)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 getState(int subId)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 inflatePhoneState(int subId)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 setIsAirplaneMode(IconState icon)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 setMobileDataEnabled(boolean enabled)384 public void setMobileDataEnabled(boolean enabled) { 385 // Don't care. 386 } 387 388 @Override dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)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 onRtlPropertiesChanged(int layoutDirection)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 hasOverlappingRendering()435 public boolean hasOverlappingRendering() { 436 return false; 437 } 438 439 // Run after each indicator change. apply()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 */ setIconForView(ImageView imageView, @DrawableRes int iconId)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 onDarkChanged(Rect tintArea, float darkIntensity, int tint)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 applyIconTint()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 applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon)588 private void applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon) { 589 lightIcon.setAlpha(1 - darkIntensity); 590 darkIcon.setAlpha(darkIntensity); 591 } 592 setTint(ImageView v, int tint)593 private void setTint(ImageView v, int tint) { 594 v.setImageTintList(ColorStateList.valueOf(tint)); 595 } 596 currentVpnIconId(boolean isBranded)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 PhoneState(int subId, Context context)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 setViews(ViewGroup root)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 apply(boolean isSecondaryIcon)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 populateAccessibilityEvent(AccessibilityEvent event)674 public void populateAccessibilityEvent(AccessibilityEvent event) { 675 if (mMobileVisible && mMobileGroup != null 676 && mMobileGroup.getContentDescription() != null) { 677 event.getText().add(mMobileGroup.getContentDescription()); 678 } 679 } 680 setIconTint(int tint, float darkIntensity, Rect tintArea)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