1 /* 2 * Copyright (C) 2018 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.phone; 18 19 import android.content.Context; 20 import android.os.Handler; 21 import android.telephony.SubscriptionInfo; 22 import android.util.ArraySet; 23 import android.util.Log; 24 25 import com.android.settingslib.mobile.TelephonyIcons; 26 import com.android.systemui.R; 27 import com.android.systemui.dagger.SysUISingleton; 28 import com.android.systemui.statusbar.FeatureFlags; 29 import com.android.systemui.statusbar.policy.NetworkController; 30 import com.android.systemui.statusbar.policy.NetworkController.IconState; 31 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; 32 import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; 33 import com.android.systemui.statusbar.policy.NetworkControllerImpl; 34 import com.android.systemui.statusbar.policy.SecurityController; 35 import com.android.systemui.tuner.TunerService; 36 import com.android.systemui.tuner.TunerService.Tunable; 37 import com.android.systemui.util.CarrierConfigTracker; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Objects; 42 43 import javax.inject.Inject; 44 45 /** Controls the signal policies for icons shown in the StatusBar. **/ 46 @SysUISingleton 47 public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback, 48 SecurityController.SecurityControllerCallback, Tunable { 49 private static final String TAG = "StatusBarSignalPolicy"; 50 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 51 52 private final String mSlotAirplane; 53 private final String mSlotMobile; 54 private final String mSlotWifi; 55 private final String mSlotEthernet; 56 private final String mSlotVpn; 57 private final String mSlotNoCalling; 58 private final String mSlotCallStrength; 59 60 private final Context mContext; 61 private final StatusBarIconController mIconController; 62 private final NetworkController mNetworkController; 63 private final SecurityController mSecurityController; 64 private final Handler mHandler = Handler.getMain(); 65 private final CarrierConfigTracker mCarrierConfigTracker; 66 private final TunerService mTunerService; 67 private final FeatureFlags mFeatureFlags; 68 69 private boolean mHideAirplane; 70 private boolean mHideMobile; 71 private boolean mHideWifi; 72 private boolean mHideEthernet; 73 private boolean mActivityEnabled; 74 private boolean mForceHideWifi; 75 76 // Track as little state as possible, and only for padding purposes 77 private boolean mIsAirplaneMode = false; 78 private boolean mIsWifiEnabled = false; 79 private boolean mWifiVisible = false; 80 81 private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>(); 82 private ArrayList<CallIndicatorIconState> mCallIndicatorStates = 83 new ArrayList<CallIndicatorIconState>(); 84 private WifiIconState mWifiIconState = new WifiIconState(); 85 86 @Inject StatusBarSignalPolicy( Context context, StatusBarIconController iconController, CarrierConfigTracker carrierConfigTracker, NetworkController networkController, SecurityController securityController, TunerService tunerService, FeatureFlags featureFlags )87 public StatusBarSignalPolicy( 88 Context context, 89 StatusBarIconController iconController, 90 CarrierConfigTracker carrierConfigTracker, 91 NetworkController networkController, 92 SecurityController securityController, 93 TunerService tunerService, 94 FeatureFlags featureFlags 95 ) { 96 mContext = context; 97 98 mIconController = iconController; 99 mCarrierConfigTracker = carrierConfigTracker; 100 mNetworkController = networkController; 101 mSecurityController = securityController; 102 mTunerService = tunerService; 103 mFeatureFlags = featureFlags; 104 105 mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); 106 mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); 107 mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); 108 mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); 109 mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); 110 mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling); 111 mSlotCallStrength = 112 mContext.getString(com.android.internal.R.string.status_bar_call_strength); 113 mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); 114 115 116 tunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST); 117 mNetworkController.addCallback(this); 118 mSecurityController.addCallback(this); 119 } 120 destroy()121 public void destroy() { 122 mTunerService.removeTunable(this); 123 mNetworkController.removeCallback(this); 124 mSecurityController.removeCallback(this); 125 } 126 updateVpn()127 private void updateVpn() { 128 boolean vpnVisible = mSecurityController.isVpnEnabled(); 129 int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); 130 131 mIconController.setIcon(mSlotVpn, vpnIconId, 132 mContext.getResources().getString(R.string.accessibility_vpn_on)); 133 mIconController.setIconVisibility(mSlotVpn, vpnVisible); 134 } 135 currentVpnIconId(boolean isBranded)136 private int currentVpnIconId(boolean isBranded) { 137 return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic; 138 } 139 140 /** 141 * From SecurityController 142 */ 143 @Override onStateChanged()144 public void onStateChanged() { 145 mHandler.post(this::updateVpn); 146 } 147 148 @Override onTuningChanged(String key, String newValue)149 public void onTuningChanged(String key, String newValue) { 150 if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) { 151 return; 152 } 153 ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue); 154 boolean hideAirplane = hideList.contains(mSlotAirplane); 155 boolean hideMobile = hideList.contains(mSlotMobile); 156 boolean hideWifi = hideList.contains(mSlotWifi); 157 boolean hideEthernet = hideList.contains(mSlotEthernet); 158 159 if (hideAirplane != mHideAirplane || hideMobile != mHideMobile 160 || hideEthernet != mHideEthernet || hideWifi != mHideWifi) { 161 mHideAirplane = hideAirplane; 162 mHideMobile = hideMobile; 163 mHideEthernet = hideEthernet; 164 mHideWifi = hideWifi || mForceHideWifi; 165 // Re-register to get new callbacks. 166 mNetworkController.removeCallback(this); 167 mNetworkController.addCallback(this); 168 } 169 } 170 171 @Override setWifiIndicators(WifiIndicators indicators)172 public void setWifiIndicators(WifiIndicators indicators) { 173 if (DEBUG) { 174 Log.d(TAG, "setWifiIndicators: " + indicators); 175 } 176 boolean visible = indicators.statusIcon.visible && !mHideWifi; 177 boolean in = indicators.activityIn && mActivityEnabled && visible; 178 boolean out = indicators.activityOut && mActivityEnabled && visible; 179 mIsWifiEnabled = indicators.enabled; 180 181 WifiIconState newState = mWifiIconState.copy(); 182 183 if (mWifiIconState.noDefaultNetwork && mWifiIconState.noNetworksAvailable 184 && !mIsAirplaneMode) { 185 newState.visible = true; 186 newState.resId = R.drawable.ic_qs_no_internet_unavailable; 187 } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable 188 && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { 189 newState.visible = true; 190 newState.resId = R.drawable.ic_qs_no_internet_available; 191 } else { 192 newState.visible = visible; 193 newState.resId = indicators.statusIcon.icon; 194 newState.activityIn = in; 195 newState.activityOut = out; 196 newState.contentDescription = indicators.statusIcon.contentDescription; 197 MobileIconState first = getFirstMobileState(); 198 newState.signalSpacerVisible = first != null && first.typeId != 0; 199 } 200 newState.slot = mSlotWifi; 201 newState.airplaneSpacerVisible = mIsAirplaneMode; 202 updateWifiIconWithState(newState); 203 mWifiIconState = newState; 204 } 205 updateShowWifiSignalSpacer(WifiIconState state)206 private void updateShowWifiSignalSpacer(WifiIconState state) { 207 MobileIconState first = getFirstMobileState(); 208 state.signalSpacerVisible = first != null && first.typeId != 0; 209 } 210 updateWifiIconWithState(WifiIconState state)211 private void updateWifiIconWithState(WifiIconState state) { 212 if (DEBUG) Log.d(TAG, "WifiIconState: " + state == null ? "" : state.toString()); 213 if (state.visible && state.resId > 0) { 214 mIconController.setSignalIcon(mSlotWifi, state); 215 mIconController.setIconVisibility(mSlotWifi, true); 216 } else { 217 mIconController.setIconVisibility(mSlotWifi, false); 218 } 219 } 220 221 @Override setCallIndicator(IconState statusIcon, int subId)222 public void setCallIndicator(IconState statusIcon, int subId) { 223 if (DEBUG) { 224 Log.d(TAG, "setCallIndicator: " 225 + "statusIcon = " + statusIcon + "," 226 + "subId = " + subId); 227 } 228 CallIndicatorIconState state = getNoCallingState(subId); 229 if (state == null) { 230 return; 231 } 232 if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) { 233 state.isNoCalling = statusIcon.visible; 234 state.noCallingDescription = statusIcon.contentDescription; 235 } else { 236 state.callStrengthResId = statusIcon.icon; 237 state.callStrengthDescription = statusIcon.contentDescription; 238 } 239 if (mCarrierConfigTracker.getCallStrengthConfig(subId)) { 240 mIconController.setCallStrengthIcons(mSlotCallStrength, 241 CallIndicatorIconState.copyStates(mCallIndicatorStates)); 242 } else { 243 mIconController.removeIcon(mSlotCallStrength, subId); 244 } 245 mIconController.setNoCallingIcons(mSlotNoCalling, 246 CallIndicatorIconState.copyStates(mCallIndicatorStates)); 247 } 248 249 @Override setMobileDataIndicators(MobileDataIndicators indicators)250 public void setMobileDataIndicators(MobileDataIndicators indicators) { 251 if (DEBUG) { 252 Log.d(TAG, "setMobileDataIndicators: " + indicators); 253 } 254 MobileIconState state = getState(indicators.subId); 255 if (state == null) { 256 return; 257 } 258 259 // Visibility of the data type indicator changed 260 boolean typeChanged = indicators.statusType != state.typeId 261 && (indicators.statusType == 0 || state.typeId == 0); 262 263 state.visible = indicators.statusIcon.visible && !mHideMobile; 264 state.strengthId = indicators.statusIcon.icon; 265 state.typeId = indicators.statusType; 266 state.contentDescription = indicators.statusIcon.contentDescription; 267 state.typeContentDescription = indicators.typeContentDescription; 268 state.showTriangle = indicators.showTriangle; 269 state.roaming = indicators.roaming; 270 state.activityIn = indicators.activityIn && mActivityEnabled; 271 state.activityOut = indicators.activityOut && mActivityEnabled; 272 273 if (DEBUG) { 274 Log.d(TAG, "MobileIconStates: " 275 + (mMobileStates == null ? "" : mMobileStates.toString())); 276 } 277 // Always send a copy to maintain value type semantics 278 mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); 279 280 if (typeChanged) { 281 WifiIconState wifiCopy = mWifiIconState.copy(); 282 updateShowWifiSignalSpacer(wifiCopy); 283 if (!Objects.equals(wifiCopy, mWifiIconState)) { 284 updateWifiIconWithState(wifiCopy); 285 mWifiIconState = wifiCopy; 286 } 287 } 288 } 289 getNoCallingState(int subId)290 private CallIndicatorIconState getNoCallingState(int subId) { 291 for (CallIndicatorIconState state : mCallIndicatorStates) { 292 if (state.subId == subId) { 293 return state; 294 } 295 } 296 Log.e(TAG, "Unexpected subscription " + subId); 297 return null; 298 } 299 getState(int subId)300 private MobileIconState getState(int subId) { 301 for (MobileIconState state : mMobileStates) { 302 if (state.subId == subId) { 303 return state; 304 } 305 } 306 Log.e(TAG, "Unexpected subscription " + subId); 307 return null; 308 } 309 getFirstMobileState()310 private MobileIconState getFirstMobileState() { 311 if (mMobileStates.size() > 0) { 312 return mMobileStates.get(0); 313 } 314 315 return null; 316 } 317 318 319 /** 320 * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators 321 * so we don't have to update the icon manager at this point, just remove the old ones 322 * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8) 323 */ 324 @Override setSubs(List<SubscriptionInfo> subs)325 public void setSubs(List<SubscriptionInfo> subs) { 326 if (DEBUG) Log.d(TAG, "setSubs: " + (subs == null ? "" : subs.toString())); 327 if (hasCorrectSubs(subs)) { 328 return; 329 } 330 331 mIconController.removeAllIconsForSlot(mSlotMobile); 332 mIconController.removeAllIconsForSlot(mSlotNoCalling); 333 mIconController.removeAllIconsForSlot(mSlotCallStrength); 334 mMobileStates.clear(); 335 List<CallIndicatorIconState> noCallingStates = new ArrayList<CallIndicatorIconState>(); 336 noCallingStates.addAll(mCallIndicatorStates); 337 mCallIndicatorStates.clear(); 338 final int n = subs.size(); 339 for (int i = 0; i < n; i++) { 340 mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId())); 341 boolean isNewSub = true; 342 for (CallIndicatorIconState state : noCallingStates) { 343 if (state.subId == subs.get(i).getSubscriptionId()) { 344 mCallIndicatorStates.add(state); 345 isNewSub = false; 346 break; 347 } 348 } 349 if (isNewSub) { 350 mCallIndicatorStates.add( 351 new CallIndicatorIconState(subs.get(i).getSubscriptionId())); 352 } 353 } 354 } 355 hasCorrectSubs(List<SubscriptionInfo> subs)356 private boolean hasCorrectSubs(List<SubscriptionInfo> subs) { 357 final int N = subs.size(); 358 if (N != mMobileStates.size()) { 359 return false; 360 } 361 for (int i = 0; i < N; i++) { 362 if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) { 363 return false; 364 } 365 } 366 return true; 367 } 368 369 @Override setNoSims(boolean show, boolean simDetected)370 public void setNoSims(boolean show, boolean simDetected) { 371 // Noop yay! 372 } 373 374 @Override setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, boolean noNetworksAvailable)375 public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, 376 boolean noNetworksAvailable) { 377 if (!mFeatureFlags.isCombinedStatusBarSignalIconsEnabled()) { 378 return; 379 } 380 if (DEBUG) { 381 Log.d(TAG, "setConnectivityStatus: " 382 + "noDefaultNetwork = " + noDefaultNetwork + "," 383 + "noValidatedNetwork = " + noValidatedNetwork + "," 384 + "noNetworksAvailable = " + noNetworksAvailable); 385 } 386 WifiIconState newState = mWifiIconState.copy(); 387 newState.noDefaultNetwork = noDefaultNetwork; 388 newState.noValidatedNetwork = noValidatedNetwork; 389 newState.noNetworksAvailable = noNetworksAvailable; 390 newState.slot = mSlotWifi; 391 newState.airplaneSpacerVisible = mIsAirplaneMode; 392 if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) { 393 newState.visible = true; 394 newState.resId = R.drawable.ic_qs_no_internet_unavailable; 395 } else if (noDefaultNetwork && !noNetworksAvailable 396 && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { 397 newState.visible = true; 398 newState.resId = R.drawable.ic_qs_no_internet_available; 399 } else { 400 newState.visible = false; 401 newState.resId = 0; 402 } 403 updateWifiIconWithState(newState); 404 mWifiIconState = newState; 405 } 406 407 408 @Override setEthernetIndicators(IconState state)409 public void setEthernetIndicators(IconState state) { 410 boolean visible = state.visible && !mHideEthernet; 411 int resId = state.icon; 412 String description = state.contentDescription; 413 414 if (resId > 0) { 415 mIconController.setIcon(mSlotEthernet, resId, description); 416 mIconController.setIconVisibility(mSlotEthernet, true); 417 } else { 418 mIconController.setIconVisibility(mSlotEthernet, false); 419 } 420 } 421 422 @Override setIsAirplaneMode(IconState icon)423 public void setIsAirplaneMode(IconState icon) { 424 if (DEBUG) { 425 Log.d(TAG, "setIsAirplaneMode: " 426 + "icon = " + (icon == null ? "" : icon.toString())); 427 } 428 mIsAirplaneMode = icon.visible && !mHideAirplane; 429 int resId = icon.icon; 430 String description = icon.contentDescription; 431 432 if (mIsAirplaneMode && resId > 0) { 433 mIconController.setIcon(mSlotAirplane, resId, description); 434 mIconController.setIconVisibility(mSlotAirplane, true); 435 } else { 436 mIconController.setIconVisibility(mSlotAirplane, false); 437 } 438 } 439 440 @Override setMobileDataEnabled(boolean enabled)441 public void setMobileDataEnabled(boolean enabled) { 442 // Don't care. 443 } 444 445 /** 446 * Stores the StatusBar state for no Calling & SMS. 447 */ 448 public static class CallIndicatorIconState { 449 public boolean isNoCalling; 450 public int noCallingResId; 451 public int callStrengthResId; 452 public int subId; 453 public String noCallingDescription; 454 public String callStrengthDescription; 455 CallIndicatorIconState(int subId)456 private CallIndicatorIconState(int subId) { 457 this.subId = subId; 458 this.noCallingResId = R.drawable.ic_qs_no_calling_sms; 459 this.callStrengthResId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0]; 460 } 461 462 @Override equals(Object o)463 public boolean equals(Object o) { 464 // Skipping reference equality bc this should be more of a value type 465 if (o == null || getClass() != o.getClass()) { 466 return false; 467 } 468 CallIndicatorIconState that = (CallIndicatorIconState) o; 469 return isNoCalling == that.isNoCalling 470 && noCallingResId == that.noCallingResId 471 && callStrengthResId == that.callStrengthResId 472 && subId == that.subId 473 && noCallingDescription == that.noCallingDescription 474 && callStrengthDescription == that.callStrengthDescription; 475 476 } 477 478 @Override hashCode()479 public int hashCode() { 480 return Objects.hash(isNoCalling, noCallingResId, 481 callStrengthResId, subId, noCallingDescription, callStrengthDescription); 482 } 483 copyTo(CallIndicatorIconState other)484 private void copyTo(CallIndicatorIconState other) { 485 other.isNoCalling = isNoCalling; 486 other.noCallingResId = noCallingResId; 487 other.callStrengthResId = callStrengthResId; 488 other.subId = subId; 489 other.noCallingDescription = noCallingDescription; 490 other.callStrengthDescription = callStrengthDescription; 491 } 492 copyStates( List<CallIndicatorIconState> inStates)493 private static List<CallIndicatorIconState> copyStates( 494 List<CallIndicatorIconState> inStates) { 495 ArrayList<CallIndicatorIconState> outStates = new ArrayList<>(); 496 for (CallIndicatorIconState state : inStates) { 497 CallIndicatorIconState copy = new CallIndicatorIconState(state.subId); 498 state.copyTo(copy); 499 outStates.add(copy); 500 } 501 return outStates; 502 } 503 } 504 505 private static abstract class SignalIconState { 506 public boolean visible; 507 public boolean activityOut; 508 public boolean activityIn; 509 public String slot; 510 public String contentDescription; 511 512 @Override equals(Object o)513 public boolean equals(Object o) { 514 // Skipping reference equality bc this should be more of a value type 515 if (o == null || getClass() != o.getClass()) { 516 return false; 517 } 518 SignalIconState that = (SignalIconState) o; 519 return visible == that.visible && 520 activityOut == that.activityOut && 521 activityIn == that.activityIn && 522 Objects.equals(contentDescription, that.contentDescription) && 523 Objects.equals(slot, that.slot); 524 } 525 526 @Override hashCode()527 public int hashCode() { 528 return Objects.hash(visible, activityOut, slot); 529 } 530 copyTo(SignalIconState other)531 protected void copyTo(SignalIconState other) { 532 other.visible = visible; 533 other.activityIn = activityIn; 534 other.activityOut = activityOut; 535 other.slot = slot; 536 other.contentDescription = contentDescription; 537 } 538 } 539 540 public static class WifiIconState extends SignalIconState{ 541 public int resId; 542 public boolean airplaneSpacerVisible; 543 public boolean signalSpacerVisible; 544 public boolean noDefaultNetwork; 545 public boolean noValidatedNetwork; 546 public boolean noNetworksAvailable; 547 548 @Override equals(Object o)549 public boolean equals(Object o) { 550 // Skipping reference equality bc this should be more of a value type 551 if (o == null || getClass() != o.getClass()) { 552 return false; 553 } 554 if (!super.equals(o)) { 555 return false; 556 } 557 WifiIconState that = (WifiIconState) o; 558 return resId == that.resId 559 && airplaneSpacerVisible == that.airplaneSpacerVisible 560 && signalSpacerVisible == that.signalSpacerVisible 561 && noDefaultNetwork == that.noDefaultNetwork 562 && noValidatedNetwork == that.noValidatedNetwork 563 && noNetworksAvailable == that.noNetworksAvailable; 564 } 565 copyTo(WifiIconState other)566 public void copyTo(WifiIconState other) { 567 super.copyTo(other); 568 other.resId = resId; 569 other.airplaneSpacerVisible = airplaneSpacerVisible; 570 other.signalSpacerVisible = signalSpacerVisible; 571 other.noDefaultNetwork = noDefaultNetwork; 572 other.noValidatedNetwork = noValidatedNetwork; 573 other.noNetworksAvailable = noNetworksAvailable; 574 } 575 copy()576 public WifiIconState copy() { 577 WifiIconState newState = new WifiIconState(); 578 copyTo(newState); 579 return newState; 580 } 581 582 @Override hashCode()583 public int hashCode() { 584 return Objects.hash(super.hashCode(), 585 resId, airplaneSpacerVisible, signalSpacerVisible, noDefaultNetwork, 586 noValidatedNetwork, noNetworksAvailable); 587 } 588 toString()589 @Override public String toString() { 590 return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; 591 } 592 } 593 594 /** 595 * A little different. This one delegates to SignalDrawable instead of a specific resId 596 */ 597 public static class MobileIconState extends SignalIconState { 598 public int subId; 599 public int strengthId; 600 public int typeId; 601 public boolean showTriangle; 602 public boolean roaming; 603 public boolean needsLeadingPadding; 604 public CharSequence typeContentDescription; 605 MobileIconState(int subId)606 private MobileIconState(int subId) { 607 super(); 608 this.subId = subId; 609 } 610 611 @Override equals(Object o)612 public boolean equals(Object o) { 613 if (o == null || getClass() != o.getClass()) { 614 return false; 615 } 616 if (!super.equals(o)) { 617 return false; 618 } 619 MobileIconState that = (MobileIconState) o; 620 return subId == that.subId 621 && strengthId == that.strengthId 622 && typeId == that.typeId 623 && showTriangle == that.showTriangle 624 && roaming == that.roaming 625 && needsLeadingPadding == that.needsLeadingPadding 626 && Objects.equals(typeContentDescription, that.typeContentDescription); 627 } 628 629 @Override hashCode()630 public int hashCode() { 631 632 return Objects 633 .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming, 634 needsLeadingPadding, typeContentDescription); 635 } 636 copy()637 public MobileIconState copy() { 638 MobileIconState copy = new MobileIconState(this.subId); 639 copyTo(copy); 640 return copy; 641 } 642 copyTo(MobileIconState other)643 public void copyTo(MobileIconState other) { 644 super.copyTo(other); 645 other.subId = subId; 646 other.strengthId = strengthId; 647 other.typeId = typeId; 648 other.showTriangle = showTriangle; 649 other.roaming = roaming; 650 other.needsLeadingPadding = needsLeadingPadding; 651 other.typeContentDescription = typeContentDescription; 652 } 653 copyStates(List<MobileIconState> inStates)654 private static List<MobileIconState> copyStates(List<MobileIconState> inStates) { 655 ArrayList<MobileIconState> outStates = new ArrayList<>(); 656 for (MobileIconState state : inStates) { 657 MobileIconState copy = new MobileIconState(state.subId); 658 state.copyTo(copy); 659 outStates.add(copy); 660 } 661 662 return outStates; 663 } 664 toString()665 @Override public String toString() { 666 return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId 667 + ", showTriangle=" + showTriangle + ", roaming=" + roaming 668 + ", typeId=" + typeId + ", visible=" + visible + ")"; 669 } 670 } 671 } 672