| /* |
| * Copyright (C) 2011 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; |
| |
| import android.content.Context; |
| import android.content.res.ColorStateList; |
| import android.graphics.Color; |
| import android.graphics.PorterDuff; |
| import android.telephony.SubscriptionInfo; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.accessibility.AccessibilityEvent; |
| import android.widget.ImageView; |
| import android.widget.LinearLayout; |
| |
| import com.android.systemui.R; |
| import com.android.systemui.statusbar.policy.NetworkControllerImpl; |
| import com.android.systemui.statusbar.policy.SecurityController; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| // Intimately tied to the design of res/layout/signal_cluster_view.xml |
| public class SignalClusterView |
| extends LinearLayout |
| implements NetworkControllerImpl.SignalCluster, |
| SecurityController.SecurityControllerCallback { |
| |
| static final String TAG = "SignalClusterView"; |
| static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); |
| |
| NetworkControllerImpl mNC; |
| SecurityController mSC; |
| |
| private boolean mNoSimsVisible = false; |
| private boolean mVpnVisible = false; |
| private boolean mWifiVisible = false; |
| private int mWifiStrengthId = 0; |
| private boolean mIsAirplaneMode = false; |
| private int mAirplaneIconId = 0; |
| private int mAirplaneContentDescription; |
| private String mWifiDescription; |
| private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>(); |
| private int mIconTint = Color.WHITE; |
| |
| ViewGroup mWifiGroup; |
| ImageView mVpn, mWifi, mAirplane, mNoSims; |
| View mWifiAirplaneSpacer; |
| View mWifiSignalSpacer; |
| LinearLayout mMobileSignalGroup; |
| |
| private int mWideTypeIconStartPadding; |
| private int mSecondaryTelephonyPadding; |
| private int mEndPadding; |
| private int mEndPaddingNothingVisible; |
| |
| public SignalClusterView(Context context) { |
| this(context, null); |
| } |
| |
| public SignalClusterView(Context context, AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| public SignalClusterView(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| } |
| |
| public void setNetworkController(NetworkControllerImpl nc) { |
| if (DEBUG) Log.d(TAG, "NetworkController=" + nc); |
| mNC = nc; |
| } |
| |
| public void setSecurityController(SecurityController sc) { |
| if (DEBUG) Log.d(TAG, "SecurityController=" + sc); |
| mSC = sc; |
| mSC.addCallback(this); |
| mVpnVisible = mSC.isVpnEnabled(); |
| } |
| |
| @Override |
| protected void onFinishInflate() { |
| super.onFinishInflate(); |
| mWideTypeIconStartPadding = getContext().getResources().getDimensionPixelSize( |
| R.dimen.wide_type_icon_start_padding); |
| mSecondaryTelephonyPadding = getContext().getResources().getDimensionPixelSize( |
| R.dimen.secondary_telephony_padding); |
| mEndPadding = getContext().getResources().getDimensionPixelSize( |
| R.dimen.signal_cluster_battery_padding); |
| mEndPaddingNothingVisible = getContext().getResources().getDimensionPixelSize( |
| R.dimen.no_signal_cluster_battery_padding); |
| } |
| |
| @Override |
| protected void onAttachedToWindow() { |
| super.onAttachedToWindow(); |
| |
| mVpn = (ImageView) findViewById(R.id.vpn); |
| mWifiGroup = (ViewGroup) findViewById(R.id.wifi_combo); |
| mWifi = (ImageView) findViewById(R.id.wifi_signal); |
| mAirplane = (ImageView) findViewById(R.id.airplane); |
| mNoSims = (ImageView) findViewById(R.id.no_sims); |
| mWifiAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); |
| mWifiSignalSpacer = findViewById(R.id.wifi_signal_spacer); |
| mMobileSignalGroup = (LinearLayout) findViewById(R.id.mobile_signal_group); |
| for (PhoneState state : mPhoneStates) { |
| mMobileSignalGroup.addView(state.mMobileGroup); |
| } |
| |
| apply(); |
| applyIconTint(); |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| mVpn = null; |
| mWifiGroup = null; |
| mWifi = null; |
| mAirplane = null; |
| mMobileSignalGroup.removeAllViews(); |
| mMobileSignalGroup = null; |
| |
| super.onDetachedFromWindow(); |
| } |
| |
| // From SecurityController. |
| @Override |
| public void onStateChanged() { |
| post(new Runnable() { |
| @Override |
| public void run() { |
| mVpnVisible = mSC.isVpnEnabled(); |
| apply(); |
| } |
| }); |
| } |
| |
| @Override |
| public void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription) { |
| mWifiVisible = visible; |
| mWifiStrengthId = strengthIcon; |
| mWifiDescription = contentDescription; |
| |
| apply(); |
| } |
| |
| @Override |
| public void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon, |
| String contentDescription, String typeContentDescription, boolean isTypeIconWide, |
| int subId) { |
| PhoneState state = getOrInflateState(subId); |
| state.mMobileVisible = visible; |
| state.mMobileStrengthId = strengthIcon; |
| state.mMobileTypeId = typeIcon; |
| state.mMobileDescription = contentDescription; |
| state.mMobileTypeDescription = typeContentDescription; |
| state.mIsMobileTypeIconWide = isTypeIconWide; |
| |
| apply(); |
| } |
| |
| @Override |
| public void setNoSims(boolean show) { |
| mNoSimsVisible = show; |
| } |
| |
| @Override |
| public void setSubs(List<SubscriptionInfo> subs) { |
| // Clear out all old subIds. |
| mPhoneStates.clear(); |
| if (mMobileSignalGroup != null) { |
| mMobileSignalGroup.removeAllViews(); |
| } |
| final int n = subs.size(); |
| for (int i = 0; i < n; i++) { |
| inflatePhoneState(subs.get(i).getSubscriptionId()); |
| } |
| if (isAttachedToWindow()) { |
| applyIconTint(); |
| } |
| } |
| |
| private PhoneState getOrInflateState(int subId) { |
| for (PhoneState state : mPhoneStates) { |
| if (state.mSubId == subId) { |
| return state; |
| } |
| } |
| return inflatePhoneState(subId); |
| } |
| |
| private PhoneState inflatePhoneState(int subId) { |
| PhoneState state = new PhoneState(subId, mContext); |
| if (mMobileSignalGroup != null) { |
| mMobileSignalGroup.addView(state.mMobileGroup); |
| } |
| mPhoneStates.add(state); |
| return state; |
| } |
| |
| @Override |
| public void setIsAirplaneMode(boolean is, int airplaneIconId, int contentDescription) { |
| mIsAirplaneMode = is; |
| mAirplaneIconId = airplaneIconId; |
| mAirplaneContentDescription = contentDescription; |
| |
| apply(); |
| } |
| |
| @Override |
| public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { |
| // Standard group layout onPopulateAccessibilityEvent() implementations |
| // ignore content description, so populate manually |
| if (mWifiVisible && mWifiGroup != null && mWifiGroup.getContentDescription() != null) |
| event.getText().add(mWifiGroup.getContentDescription()); |
| for (PhoneState state : mPhoneStates) { |
| state.populateAccessibilityEvent(event); |
| } |
| return super.dispatchPopulateAccessibilityEventInternal(event); |
| } |
| |
| @Override |
| public void onRtlPropertiesChanged(int layoutDirection) { |
| super.onRtlPropertiesChanged(layoutDirection); |
| |
| if (mWifi != null) { |
| mWifi.setImageDrawable(null); |
| } |
| |
| for (PhoneState state : mPhoneStates) { |
| if (state.mMobile != null) { |
| state.mMobile.setImageDrawable(null); |
| } |
| if (state.mMobileType != null) { |
| state.mMobileType.setImageDrawable(null); |
| } |
| } |
| |
| if(mAirplane != null) { |
| mAirplane.setImageDrawable(null); |
| } |
| |
| apply(); |
| } |
| |
| @Override |
| public boolean hasOverlappingRendering() { |
| return false; |
| } |
| |
| // Run after each indicator change. |
| private void apply() { |
| if (mWifiGroup == null) return; |
| |
| mVpn.setVisibility(mVpnVisible ? View.VISIBLE : View.GONE); |
| if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE")); |
| if (mWifiVisible) { |
| mWifi.setImageResource(mWifiStrengthId); |
| mWifiGroup.setContentDescription(mWifiDescription); |
| mWifiGroup.setVisibility(View.VISIBLE); |
| } else { |
| mWifiGroup.setVisibility(View.GONE); |
| } |
| |
| if (DEBUG) Log.d(TAG, |
| String.format("wifi: %s sig=%d", |
| (mWifiVisible ? "VISIBLE" : "GONE"), |
| mWifiStrengthId)); |
| |
| boolean anyMobileVisible = false; |
| int firstMobileTypeId = 0; |
| for (PhoneState state : mPhoneStates) { |
| if (state.apply(anyMobileVisible)) { |
| if (!anyMobileVisible) { |
| firstMobileTypeId = state.mMobileTypeId; |
| anyMobileVisible = true; |
| } |
| } |
| } |
| |
| if (mIsAirplaneMode) { |
| mAirplane.setImageResource(mAirplaneIconId); |
| mAirplane.setContentDescription(mAirplaneContentDescription != 0 ? |
| mContext.getString(mAirplaneContentDescription) : null); |
| mAirplane.setVisibility(View.VISIBLE); |
| } else { |
| mAirplane.setVisibility(View.GONE); |
| } |
| |
| if (mIsAirplaneMode && mWifiVisible) { |
| mWifiAirplaneSpacer.setVisibility(View.VISIBLE); |
| } else { |
| mWifiAirplaneSpacer.setVisibility(View.GONE); |
| } |
| |
| if (((anyMobileVisible && firstMobileTypeId != 0) || mNoSimsVisible) && mWifiVisible) { |
| mWifiSignalSpacer.setVisibility(View.VISIBLE); |
| } else { |
| mWifiSignalSpacer.setVisibility(View.GONE); |
| } |
| |
| mNoSims.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE); |
| |
| boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode |
| || anyMobileVisible || mVpnVisible; |
| setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0); |
| } |
| |
| public void setIconTint(int tint) { |
| boolean changed = tint != mIconTint; |
| mIconTint = tint; |
| if (changed && isAttachedToWindow()) { |
| applyIconTint(); |
| } |
| } |
| |
| private void applyIconTint() { |
| setTint(mVpn, mIconTint); |
| setTint(mWifi, mIconTint); |
| setTint(mNoSims, mIconTint); |
| setTint(mAirplane, mIconTint); |
| for (int i = 0; i < mPhoneStates.size(); i++) { |
| mPhoneStates.get(i).setIconTint(mIconTint); |
| } |
| } |
| |
| private void setTint(ImageView v, int tint) { |
| v.setImageTintMode(PorterDuff.Mode.SRC_ATOP); |
| v.setImageTintList(ColorStateList.valueOf(tint)); |
| } |
| |
| private class PhoneState { |
| private final int mSubId; |
| private boolean mMobileVisible = false; |
| private int mMobileStrengthId = 0, mMobileTypeId = 0; |
| private boolean mIsMobileTypeIconWide; |
| private String mMobileDescription, mMobileTypeDescription; |
| |
| private ViewGroup mMobileGroup; |
| private ImageView mMobile, mMobileType; |
| |
| public PhoneState(int subId, Context context) { |
| ViewGroup root = (ViewGroup) LayoutInflater.from(context) |
| .inflate(R.layout.mobile_signal_group, null); |
| setViews(root); |
| mSubId = subId; |
| } |
| |
| public void setViews(ViewGroup root) { |
| mMobileGroup = root; |
| mMobile = (ImageView) root.findViewById(R.id.mobile_signal); |
| mMobileType = (ImageView) root.findViewById(R.id.mobile_type); |
| } |
| |
| public boolean apply(boolean isSecondaryIcon) { |
| if (mMobileVisible && !mIsAirplaneMode) { |
| mMobile.setImageResource(mMobileStrengthId); |
| mMobileType.setImageResource(mMobileTypeId); |
| mMobileGroup.setContentDescription(mMobileTypeDescription |
| + " " + mMobileDescription); |
| mMobileGroup.setVisibility(View.VISIBLE); |
| } else { |
| mMobileGroup.setVisibility(View.GONE); |
| } |
| |
| // When this isn't next to wifi, give it some extra padding between the signals. |
| mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0, |
| 0, 0, 0); |
| mMobile.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0, |
| 0, 0, 0); |
| |
| if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d", |
| (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId)); |
| |
| mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE); |
| |
| return mMobileVisible; |
| } |
| |
| public void populateAccessibilityEvent(AccessibilityEvent event) { |
| if (mMobileVisible && mMobileGroup != null |
| && mMobileGroup.getContentDescription() != null) { |
| event.getText().add(mMobileGroup.getContentDescription()); |
| } |
| } |
| |
| public void setIconTint(int tint) { |
| setTint(mMobile, tint); |
| setTint(mMobileType, tint); |
| } |
| } |
| } |
| |