| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.systemui.statusbar.phone; |
| |
| import android.content.Context; |
| import android.os.Handler; |
| import android.telephony.SubscriptionInfo; |
| import android.util.ArraySet; |
| import android.util.Log; |
| |
| import com.android.settingslib.mobile.TelephonyIcons; |
| import com.android.systemui.R; |
| import com.android.systemui.dagger.SysUISingleton; |
| import com.android.systemui.statusbar.FeatureFlags; |
| import com.android.systemui.statusbar.policy.NetworkController; |
| import com.android.systemui.statusbar.policy.NetworkController.IconState; |
| import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; |
| import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; |
| import com.android.systemui.statusbar.policy.NetworkControllerImpl; |
| import com.android.systemui.statusbar.policy.SecurityController; |
| import com.android.systemui.tuner.TunerService; |
| import com.android.systemui.tuner.TunerService.Tunable; |
| import com.android.systemui.util.CarrierConfigTracker; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| |
| import javax.inject.Inject; |
| |
| /** Controls the signal policies for icons shown in the StatusBar. **/ |
| @SysUISingleton |
| public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback, |
| SecurityController.SecurityControllerCallback, Tunable { |
| private static final String TAG = "StatusBarSignalPolicy"; |
| private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); |
| |
| private final String mSlotAirplane; |
| private final String mSlotMobile; |
| private final String mSlotWifi; |
| private final String mSlotEthernet; |
| private final String mSlotVpn; |
| private final String mSlotNoCalling; |
| private final String mSlotCallStrength; |
| |
| private final Context mContext; |
| private final StatusBarIconController mIconController; |
| private final NetworkController mNetworkController; |
| private final SecurityController mSecurityController; |
| private final Handler mHandler = Handler.getMain(); |
| private final CarrierConfigTracker mCarrierConfigTracker; |
| private final TunerService mTunerService; |
| private final FeatureFlags mFeatureFlags; |
| |
| private boolean mHideAirplane; |
| private boolean mHideMobile; |
| private boolean mHideWifi; |
| private boolean mHideEthernet; |
| private boolean mActivityEnabled; |
| private boolean mForceHideWifi; |
| |
| // Track as little state as possible, and only for padding purposes |
| private boolean mIsAirplaneMode = false; |
| private boolean mIsWifiEnabled = false; |
| private boolean mWifiVisible = false; |
| |
| private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>(); |
| private ArrayList<CallIndicatorIconState> mCallIndicatorStates = |
| new ArrayList<CallIndicatorIconState>(); |
| private WifiIconState mWifiIconState = new WifiIconState(); |
| |
| @Inject |
| public StatusBarSignalPolicy( |
| Context context, |
| StatusBarIconController iconController, |
| CarrierConfigTracker carrierConfigTracker, |
| NetworkController networkController, |
| SecurityController securityController, |
| TunerService tunerService, |
| FeatureFlags featureFlags |
| ) { |
| mContext = context; |
| |
| mIconController = iconController; |
| mCarrierConfigTracker = carrierConfigTracker; |
| mNetworkController = networkController; |
| mSecurityController = securityController; |
| mTunerService = tunerService; |
| mFeatureFlags = featureFlags; |
| |
| mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); |
| mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); |
| mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); |
| mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); |
| mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); |
| mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling); |
| mSlotCallStrength = |
| mContext.getString(com.android.internal.R.string.status_bar_call_strength); |
| mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); |
| |
| |
| tunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST); |
| mNetworkController.addCallback(this); |
| mSecurityController.addCallback(this); |
| } |
| |
| public void destroy() { |
| mTunerService.removeTunable(this); |
| mNetworkController.removeCallback(this); |
| mSecurityController.removeCallback(this); |
| } |
| |
| private void updateVpn() { |
| boolean vpnVisible = mSecurityController.isVpnEnabled(); |
| int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); |
| |
| mIconController.setIcon(mSlotVpn, vpnIconId, |
| mContext.getResources().getString(R.string.accessibility_vpn_on)); |
| mIconController.setIconVisibility(mSlotVpn, vpnVisible); |
| } |
| |
| private int currentVpnIconId(boolean isBranded) { |
| return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic; |
| } |
| |
| /** |
| * From SecurityController |
| */ |
| @Override |
| public void onStateChanged() { |
| mHandler.post(this::updateVpn); |
| } |
| |
| @Override |
| public void onTuningChanged(String key, String newValue) { |
| if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) { |
| return; |
| } |
| ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue); |
| boolean hideAirplane = hideList.contains(mSlotAirplane); |
| boolean hideMobile = hideList.contains(mSlotMobile); |
| boolean hideWifi = hideList.contains(mSlotWifi); |
| boolean hideEthernet = hideList.contains(mSlotEthernet); |
| |
| if (hideAirplane != mHideAirplane || hideMobile != mHideMobile |
| || hideEthernet != mHideEthernet || hideWifi != mHideWifi) { |
| mHideAirplane = hideAirplane; |
| mHideMobile = hideMobile; |
| mHideEthernet = hideEthernet; |
| mHideWifi = hideWifi || mForceHideWifi; |
| // Re-register to get new callbacks. |
| mNetworkController.removeCallback(this); |
| mNetworkController.addCallback(this); |
| } |
| } |
| |
| @Override |
| public void setWifiIndicators(WifiIndicators indicators) { |
| if (DEBUG) { |
| Log.d(TAG, "setWifiIndicators: " + indicators); |
| } |
| boolean visible = indicators.statusIcon.visible && !mHideWifi; |
| boolean in = indicators.activityIn && mActivityEnabled && visible; |
| boolean out = indicators.activityOut && mActivityEnabled && visible; |
| mIsWifiEnabled = indicators.enabled; |
| |
| WifiIconState newState = mWifiIconState.copy(); |
| |
| if (mWifiIconState.noDefaultNetwork && mWifiIconState.noNetworksAvailable |
| && !mIsAirplaneMode) { |
| newState.visible = true; |
| newState.resId = R.drawable.ic_qs_no_internet_unavailable; |
| } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable |
| && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { |
| newState.visible = true; |
| newState.resId = R.drawable.ic_qs_no_internet_available; |
| } else { |
| newState.visible = visible; |
| newState.resId = indicators.statusIcon.icon; |
| newState.activityIn = in; |
| newState.activityOut = out; |
| newState.contentDescription = indicators.statusIcon.contentDescription; |
| MobileIconState first = getFirstMobileState(); |
| newState.signalSpacerVisible = first != null && first.typeId != 0; |
| } |
| newState.slot = mSlotWifi; |
| newState.airplaneSpacerVisible = mIsAirplaneMode; |
| updateWifiIconWithState(newState); |
| mWifiIconState = newState; |
| } |
| |
| private void updateShowWifiSignalSpacer(WifiIconState state) { |
| MobileIconState first = getFirstMobileState(); |
| state.signalSpacerVisible = first != null && first.typeId != 0; |
| } |
| |
| private void updateWifiIconWithState(WifiIconState state) { |
| if (DEBUG) Log.d(TAG, "WifiIconState: " + state == null ? "" : state.toString()); |
| if (state.visible && state.resId > 0) { |
| mIconController.setSignalIcon(mSlotWifi, state); |
| mIconController.setIconVisibility(mSlotWifi, true); |
| } else { |
| mIconController.setIconVisibility(mSlotWifi, false); |
| } |
| } |
| |
| @Override |
| public void setCallIndicator(IconState statusIcon, int subId) { |
| if (DEBUG) { |
| Log.d(TAG, "setCallIndicator: " |
| + "statusIcon = " + statusIcon + "," |
| + "subId = " + subId); |
| } |
| CallIndicatorIconState state = getNoCallingState(subId); |
| if (state == null) { |
| return; |
| } |
| if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) { |
| state.isNoCalling = statusIcon.visible; |
| state.noCallingDescription = statusIcon.contentDescription; |
| } else { |
| state.callStrengthResId = statusIcon.icon; |
| state.callStrengthDescription = statusIcon.contentDescription; |
| } |
| if (mCarrierConfigTracker.getCallStrengthConfig(subId)) { |
| mIconController.setCallStrengthIcons(mSlotCallStrength, |
| CallIndicatorIconState.copyStates(mCallIndicatorStates)); |
| } else { |
| mIconController.removeIcon(mSlotCallStrength, subId); |
| } |
| mIconController.setNoCallingIcons(mSlotNoCalling, |
| CallIndicatorIconState.copyStates(mCallIndicatorStates)); |
| } |
| |
| @Override |
| public void setMobileDataIndicators(MobileDataIndicators indicators) { |
| if (DEBUG) { |
| Log.d(TAG, "setMobileDataIndicators: " + indicators); |
| } |
| MobileIconState state = getState(indicators.subId); |
| if (state == null) { |
| return; |
| } |
| |
| // Visibility of the data type indicator changed |
| boolean typeChanged = indicators.statusType != state.typeId |
| && (indicators.statusType == 0 || state.typeId == 0); |
| |
| state.visible = indicators.statusIcon.visible && !mHideMobile; |
| state.strengthId = indicators.statusIcon.icon; |
| state.typeId = indicators.statusType; |
| state.contentDescription = indicators.statusIcon.contentDescription; |
| state.typeContentDescription = indicators.typeContentDescription; |
| state.showTriangle = indicators.showTriangle; |
| state.roaming = indicators.roaming; |
| state.activityIn = indicators.activityIn && mActivityEnabled; |
| state.activityOut = indicators.activityOut && mActivityEnabled; |
| |
| if (DEBUG) { |
| Log.d(TAG, "MobileIconStates: " |
| + (mMobileStates == null ? "" : mMobileStates.toString())); |
| } |
| // Always send a copy to maintain value type semantics |
| mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); |
| |
| if (typeChanged) { |
| WifiIconState wifiCopy = mWifiIconState.copy(); |
| updateShowWifiSignalSpacer(wifiCopy); |
| if (!Objects.equals(wifiCopy, mWifiIconState)) { |
| updateWifiIconWithState(wifiCopy); |
| mWifiIconState = wifiCopy; |
| } |
| } |
| } |
| |
| private CallIndicatorIconState getNoCallingState(int subId) { |
| for (CallIndicatorIconState state : mCallIndicatorStates) { |
| if (state.subId == subId) { |
| return state; |
| } |
| } |
| Log.e(TAG, "Unexpected subscription " + subId); |
| return null; |
| } |
| |
| private MobileIconState getState(int subId) { |
| for (MobileIconState state : mMobileStates) { |
| if (state.subId == subId) { |
| return state; |
| } |
| } |
| Log.e(TAG, "Unexpected subscription " + subId); |
| return null; |
| } |
| |
| private MobileIconState getFirstMobileState() { |
| if (mMobileStates.size() > 0) { |
| return mMobileStates.get(0); |
| } |
| |
| return null; |
| } |
| |
| |
| /** |
| * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators |
| * so we don't have to update the icon manager at this point, just remove the old ones |
| * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8) |
| */ |
| @Override |
| public void setSubs(List<SubscriptionInfo> subs) { |
| if (DEBUG) Log.d(TAG, "setSubs: " + (subs == null ? "" : subs.toString())); |
| if (hasCorrectSubs(subs)) { |
| return; |
| } |
| |
| mIconController.removeAllIconsForSlot(mSlotMobile); |
| mIconController.removeAllIconsForSlot(mSlotNoCalling); |
| mIconController.removeAllIconsForSlot(mSlotCallStrength); |
| mMobileStates.clear(); |
| List<CallIndicatorIconState> noCallingStates = new ArrayList<CallIndicatorIconState>(); |
| noCallingStates.addAll(mCallIndicatorStates); |
| mCallIndicatorStates.clear(); |
| final int n = subs.size(); |
| for (int i = 0; i < n; i++) { |
| mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId())); |
| boolean isNewSub = true; |
| for (CallIndicatorIconState state : noCallingStates) { |
| if (state.subId == subs.get(i).getSubscriptionId()) { |
| mCallIndicatorStates.add(state); |
| isNewSub = false; |
| break; |
| } |
| } |
| if (isNewSub) { |
| mCallIndicatorStates.add( |
| new CallIndicatorIconState(subs.get(i).getSubscriptionId())); |
| } |
| } |
| } |
| |
| private boolean hasCorrectSubs(List<SubscriptionInfo> subs) { |
| final int N = subs.size(); |
| if (N != mMobileStates.size()) { |
| return false; |
| } |
| for (int i = 0; i < N; i++) { |
| if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public void setNoSims(boolean show, boolean simDetected) { |
| // Noop yay! |
| } |
| |
| @Override |
| public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, |
| boolean noNetworksAvailable) { |
| if (!mFeatureFlags.isCombinedStatusBarSignalIconsEnabled()) { |
| return; |
| } |
| if (DEBUG) { |
| Log.d(TAG, "setConnectivityStatus: " |
| + "noDefaultNetwork = " + noDefaultNetwork + "," |
| + "noValidatedNetwork = " + noValidatedNetwork + "," |
| + "noNetworksAvailable = " + noNetworksAvailable); |
| } |
| WifiIconState newState = mWifiIconState.copy(); |
| newState.noDefaultNetwork = noDefaultNetwork; |
| newState.noValidatedNetwork = noValidatedNetwork; |
| newState.noNetworksAvailable = noNetworksAvailable; |
| newState.slot = mSlotWifi; |
| newState.airplaneSpacerVisible = mIsAirplaneMode; |
| if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) { |
| newState.visible = true; |
| newState.resId = R.drawable.ic_qs_no_internet_unavailable; |
| } else if (noDefaultNetwork && !noNetworksAvailable |
| && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { |
| newState.visible = true; |
| newState.resId = R.drawable.ic_qs_no_internet_available; |
| } else { |
| newState.visible = false; |
| newState.resId = 0; |
| } |
| updateWifiIconWithState(newState); |
| mWifiIconState = newState; |
| } |
| |
| |
| @Override |
| public void setEthernetIndicators(IconState state) { |
| boolean visible = state.visible && !mHideEthernet; |
| int resId = state.icon; |
| String description = state.contentDescription; |
| |
| if (resId > 0) { |
| mIconController.setIcon(mSlotEthernet, resId, description); |
| mIconController.setIconVisibility(mSlotEthernet, true); |
| } else { |
| mIconController.setIconVisibility(mSlotEthernet, false); |
| } |
| } |
| |
| @Override |
| public void setIsAirplaneMode(IconState icon) { |
| if (DEBUG) { |
| Log.d(TAG, "setIsAirplaneMode: " |
| + "icon = " + (icon == null ? "" : icon.toString())); |
| } |
| mIsAirplaneMode = icon.visible && !mHideAirplane; |
| int resId = icon.icon; |
| String description = icon.contentDescription; |
| |
| if (mIsAirplaneMode && resId > 0) { |
| mIconController.setIcon(mSlotAirplane, resId, description); |
| mIconController.setIconVisibility(mSlotAirplane, true); |
| } else { |
| mIconController.setIconVisibility(mSlotAirplane, false); |
| } |
| } |
| |
| @Override |
| public void setMobileDataEnabled(boolean enabled) { |
| // Don't care. |
| } |
| |
| /** |
| * Stores the StatusBar state for no Calling & SMS. |
| */ |
| public static class CallIndicatorIconState { |
| public boolean isNoCalling; |
| public int noCallingResId; |
| public int callStrengthResId; |
| public int subId; |
| public String noCallingDescription; |
| public String callStrengthDescription; |
| |
| private CallIndicatorIconState(int subId) { |
| this.subId = subId; |
| this.noCallingResId = R.drawable.ic_qs_no_calling_sms; |
| this.callStrengthResId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0]; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| // Skipping reference equality bc this should be more of a value type |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| CallIndicatorIconState that = (CallIndicatorIconState) o; |
| return isNoCalling == that.isNoCalling |
| && noCallingResId == that.noCallingResId |
| && callStrengthResId == that.callStrengthResId |
| && subId == that.subId |
| && noCallingDescription == that.noCallingDescription |
| && callStrengthDescription == that.callStrengthDescription; |
| |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(isNoCalling, noCallingResId, |
| callStrengthResId, subId, noCallingDescription, callStrengthDescription); |
| } |
| |
| private void copyTo(CallIndicatorIconState other) { |
| other.isNoCalling = isNoCalling; |
| other.noCallingResId = noCallingResId; |
| other.callStrengthResId = callStrengthResId; |
| other.subId = subId; |
| other.noCallingDescription = noCallingDescription; |
| other.callStrengthDescription = callStrengthDescription; |
| } |
| |
| private static List<CallIndicatorIconState> copyStates( |
| List<CallIndicatorIconState> inStates) { |
| ArrayList<CallIndicatorIconState> outStates = new ArrayList<>(); |
| for (CallIndicatorIconState state : inStates) { |
| CallIndicatorIconState copy = new CallIndicatorIconState(state.subId); |
| state.copyTo(copy); |
| outStates.add(copy); |
| } |
| return outStates; |
| } |
| } |
| |
| private static abstract class SignalIconState { |
| public boolean visible; |
| public boolean activityOut; |
| public boolean activityIn; |
| public String slot; |
| public String contentDescription; |
| |
| @Override |
| public boolean equals(Object o) { |
| // Skipping reference equality bc this should be more of a value type |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| SignalIconState that = (SignalIconState) o; |
| return visible == that.visible && |
| activityOut == that.activityOut && |
| activityIn == that.activityIn && |
| Objects.equals(contentDescription, that.contentDescription) && |
| Objects.equals(slot, that.slot); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(visible, activityOut, slot); |
| } |
| |
| protected void copyTo(SignalIconState other) { |
| other.visible = visible; |
| other.activityIn = activityIn; |
| other.activityOut = activityOut; |
| other.slot = slot; |
| other.contentDescription = contentDescription; |
| } |
| } |
| |
| public static class WifiIconState extends SignalIconState{ |
| public int resId; |
| public boolean airplaneSpacerVisible; |
| public boolean signalSpacerVisible; |
| public boolean noDefaultNetwork; |
| public boolean noValidatedNetwork; |
| public boolean noNetworksAvailable; |
| |
| @Override |
| public boolean equals(Object o) { |
| // Skipping reference equality bc this should be more of a value type |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| if (!super.equals(o)) { |
| return false; |
| } |
| WifiIconState that = (WifiIconState) o; |
| return resId == that.resId |
| && airplaneSpacerVisible == that.airplaneSpacerVisible |
| && signalSpacerVisible == that.signalSpacerVisible |
| && noDefaultNetwork == that.noDefaultNetwork |
| && noValidatedNetwork == that.noValidatedNetwork |
| && noNetworksAvailable == that.noNetworksAvailable; |
| } |
| |
| public void copyTo(WifiIconState other) { |
| super.copyTo(other); |
| other.resId = resId; |
| other.airplaneSpacerVisible = airplaneSpacerVisible; |
| other.signalSpacerVisible = signalSpacerVisible; |
| other.noDefaultNetwork = noDefaultNetwork; |
| other.noValidatedNetwork = noValidatedNetwork; |
| other.noNetworksAvailable = noNetworksAvailable; |
| } |
| |
| public WifiIconState copy() { |
| WifiIconState newState = new WifiIconState(); |
| copyTo(newState); |
| return newState; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(super.hashCode(), |
| resId, airplaneSpacerVisible, signalSpacerVisible, noDefaultNetwork, |
| noValidatedNetwork, noNetworksAvailable); |
| } |
| |
| @Override public String toString() { |
| return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; |
| } |
| } |
| |
| /** |
| * A little different. This one delegates to SignalDrawable instead of a specific resId |
| */ |
| public static class MobileIconState extends SignalIconState { |
| public int subId; |
| public int strengthId; |
| public int typeId; |
| public boolean showTriangle; |
| public boolean roaming; |
| public boolean needsLeadingPadding; |
| public CharSequence typeContentDescription; |
| |
| private MobileIconState(int subId) { |
| super(); |
| this.subId = subId; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| if (!super.equals(o)) { |
| return false; |
| } |
| MobileIconState that = (MobileIconState) o; |
| return subId == that.subId |
| && strengthId == that.strengthId |
| && typeId == that.typeId |
| && showTriangle == that.showTriangle |
| && roaming == that.roaming |
| && needsLeadingPadding == that.needsLeadingPadding |
| && Objects.equals(typeContentDescription, that.typeContentDescription); |
| } |
| |
| @Override |
| public int hashCode() { |
| |
| return Objects |
| .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming, |
| needsLeadingPadding, typeContentDescription); |
| } |
| |
| public MobileIconState copy() { |
| MobileIconState copy = new MobileIconState(this.subId); |
| copyTo(copy); |
| return copy; |
| } |
| |
| public void copyTo(MobileIconState other) { |
| super.copyTo(other); |
| other.subId = subId; |
| other.strengthId = strengthId; |
| other.typeId = typeId; |
| other.showTriangle = showTriangle; |
| other.roaming = roaming; |
| other.needsLeadingPadding = needsLeadingPadding; |
| other.typeContentDescription = typeContentDescription; |
| } |
| |
| private static List<MobileIconState> copyStates(List<MobileIconState> inStates) { |
| ArrayList<MobileIconState> outStates = new ArrayList<>(); |
| for (MobileIconState state : inStates) { |
| MobileIconState copy = new MobileIconState(state.subId); |
| state.copyTo(copy); |
| outStates.add(copy); |
| } |
| |
| return outStates; |
| } |
| |
| @Override public String toString() { |
| return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId |
| + ", showTriangle=" + showTriangle + ", roaming=" + roaming |
| + ", typeId=" + typeId + ", visible=" + visible + ")"; |
| } |
| } |
| } |