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