2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.statusbar;
19 import android.content.Context;
20 import android.content.res.ColorStateList;
21 import android.graphics.Color;
22 import android.graphics.PorterDuff;
23 import android.telephony.SubscriptionInfo;
24 import android.util.AttributeSet;
25 import android.util.Log;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.view.accessibility.AccessibilityEvent;
30 import android.widget.ImageView;
31 import android.widget.LinearLayout;
33 import com.android.systemui.R;
34 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
35 import com.android.systemui.statusbar.policy.SecurityController;
37 import java.util.ArrayList;
38 import java.util.List;
40 // Intimately tied to the design of res/layout/signal_cluster_view.xml
41 public class SignalClusterView
43 implements NetworkControllerImpl.SignalCluster,
44 SecurityController.SecurityControllerCallback {
46 static final String TAG = "SignalClusterView";
47 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
49 NetworkControllerImpl mNC;
50 SecurityController mSC;
52 private boolean mNoSimsVisible = false;
53 private boolean mVpnVisible = false;
54 private boolean mWifiVisible = false;
55 private int mWifiStrengthId = 0;
56 private boolean mIsAirplaneMode = false;
57 private int mAirplaneIconId = 0;
58 private int mAirplaneContentDescription;
59 private String mWifiDescription;
60 private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
61 private int mIconTint = Color.WHITE;
62 private float mDarkIntensity;
65 ImageView mVpn, mWifi, mAirplane, mNoSims, mWifiDark, mNoSimsDark;
66 View mWifiAirplaneSpacer;
67 View mWifiSignalSpacer;
68 LinearLayout mMobileSignalGroup;
70 private int mWideTypeIconStartPadding;
71 private int mSecondaryTelephonyPadding;
72 private int mEndPadding;
73 private int mEndPaddingNothingVisible;
75 public SignalClusterView(Context context) {
79 public SignalClusterView(Context context, AttributeSet attrs) {
80 this(context, attrs, 0);
83 public SignalClusterView(Context context, AttributeSet attrs, int defStyle) {
84 super(context, attrs, defStyle);
87 public void setNetworkController(NetworkControllerImpl nc) {
88 if (DEBUG) Log.d(TAG, "NetworkController=" + nc);
92 public void setSecurityController(SecurityController sc) {
93 if (DEBUG) Log.d(TAG, "SecurityController=" + sc);
95 mSC.addCallback(this);
96 mVpnVisible = mSC.isVpnEnabled();
100 protected void onFinishInflate() {
101 super.onFinishInflate();
102 mWideTypeIconStartPadding = getContext().getResources().getDimensionPixelSize(
103 R.dimen.wide_type_icon_start_padding);
104 mSecondaryTelephonyPadding = getContext().getResources().getDimensionPixelSize(
105 R.dimen.secondary_telephony_padding);
106 mEndPadding = getContext().getResources().getDimensionPixelSize(
107 R.dimen.signal_cluster_battery_padding);
108 mEndPaddingNothingVisible = getContext().getResources().getDimensionPixelSize(
109 R.dimen.no_signal_cluster_battery_padding);
113 protected void onAttachedToWindow() {
114 super.onAttachedToWindow();
116 mVpn = (ImageView) findViewById(R.id.vpn);
117 mWifiGroup = (ViewGroup) findViewById(R.id.wifi_combo);
118 mWifi = (ImageView) findViewById(R.id.wifi_signal);
119 mWifiDark = (ImageView) findViewById(R.id.wifi_signal_dark);
120 mAirplane = (ImageView) findViewById(R.id.airplane);
121 mNoSims = (ImageView) findViewById(R.id.no_sims);
122 mNoSimsDark = (ImageView) findViewById(R.id.no_sims_dark);
123 mWifiAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer);
124 mWifiSignalSpacer = findViewById(R.id.wifi_signal_spacer);
125 mMobileSignalGroup = (LinearLayout) findViewById(R.id.mobile_signal_group);
126 for (PhoneState state : mPhoneStates) {
127 mMobileSignalGroup.addView(state.mMobileGroup);
135 protected void onDetachedFromWindow() {
140 mMobileSignalGroup.removeAllViews();
141 mMobileSignalGroup = null;
143 super.onDetachedFromWindow();
146 // From SecurityController.
148 public void onStateChanged() {
149 post(new Runnable() {
152 mVpnVisible = mSC.isVpnEnabled();
159 public void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription) {
160 mWifiVisible = visible;
161 mWifiStrengthId = strengthIcon;
162 mWifiDescription = contentDescription;
168 public void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
169 String contentDescription, String typeContentDescription, boolean isTypeIconWide,
171 PhoneState state = getOrInflateState(subId);
172 state.mMobileVisible = visible;
173 state.mMobileStrengthId = strengthIcon;
174 state.mMobileTypeId = typeIcon;
175 state.mMobileDescription = contentDescription;
176 state.mMobileTypeDescription = typeContentDescription;
177 state.mIsMobileTypeIconWide = isTypeIconWide;
183 public void setNoSims(boolean show) {
184 mNoSimsVisible = show;
188 public void setSubs(List<SubscriptionInfo> subs) {
189 // Clear out all old subIds.
190 mPhoneStates.clear();
191 if (mMobileSignalGroup != null) {
192 mMobileSignalGroup.removeAllViews();
194 final int n = subs.size();
195 for (int i = 0; i < n; i++) {
196 inflatePhoneState(subs.get(i).getSubscriptionId());
198 if (isAttachedToWindow()) {
203 private PhoneState getOrInflateState(int subId) {
204 for (PhoneState state : mPhoneStates) {
205 if (state.mSubId == subId) {
209 return inflatePhoneState(subId);
212 private PhoneState inflatePhoneState(int subId) {
213 PhoneState state = new PhoneState(subId, mContext);
214 if (mMobileSignalGroup != null) {
215 mMobileSignalGroup.addView(state.mMobileGroup);
217 mPhoneStates.add(state);
222 public void setIsAirplaneMode(boolean is, int airplaneIconId, int contentDescription) {
223 mIsAirplaneMode = is;
224 mAirplaneIconId = airplaneIconId;
225 mAirplaneContentDescription = contentDescription;
231 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
232 // Standard group layout onPopulateAccessibilityEvent() implementations
233 // ignore content description, so populate manually
234 if (mWifiVisible && mWifiGroup != null && mWifiGroup.getContentDescription() != null)
235 event.getText().add(mWifiGroup.getContentDescription());
236 for (PhoneState state : mPhoneStates) {
237 state.populateAccessibilityEvent(event);
239 return super.dispatchPopulateAccessibilityEventInternal(event);
243 public void onRtlPropertiesChanged(int layoutDirection) {
244 super.onRtlPropertiesChanged(layoutDirection);
247 mWifi.setImageDrawable(null);
250 for (PhoneState state : mPhoneStates) {
251 if (state.mMobile != null) {
252 state.mMobile.setImageDrawable(null);
254 if (state.mMobileType != null) {
255 state.mMobileType.setImageDrawable(null);
259 if(mAirplane != null) {
260 mAirplane.setImageDrawable(null);
267 public boolean hasOverlappingRendering() {
271 // Run after each indicator change.
272 private void apply() {
273 if (mWifiGroup == null) return;
275 mVpn.setVisibility(mVpnVisible ? View.VISIBLE : View.GONE);
276 if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE"));
278 mWifi.setImageResource(mWifiStrengthId);
279 mWifiDark.setImageResource(mWifiStrengthId);
280 mWifiGroup.setContentDescription(mWifiDescription);
281 mWifiGroup.setVisibility(View.VISIBLE);
283 mWifiGroup.setVisibility(View.GONE);
286 if (DEBUG) Log.d(TAG,
287 String.format("wifi: %s sig=%d",
288 (mWifiVisible ? "VISIBLE" : "GONE"),
291 boolean anyMobileVisible = false;
292 int firstMobileTypeId = 0;
293 for (PhoneState state : mPhoneStates) {
294 if (state.apply(anyMobileVisible)) {
295 if (!anyMobileVisible) {
296 firstMobileTypeId = state.mMobileTypeId;
297 anyMobileVisible = true;
302 if (mIsAirplaneMode) {
303 mAirplane.setImageResource(mAirplaneIconId);
304 mAirplane.setContentDescription(mAirplaneContentDescription != 0 ?
305 mContext.getString(mAirplaneContentDescription) : null);
306 mAirplane.setVisibility(View.VISIBLE);
308 mAirplane.setVisibility(View.GONE);
311 if (mIsAirplaneMode && mWifiVisible) {
312 mWifiAirplaneSpacer.setVisibility(View.VISIBLE);
314 mWifiAirplaneSpacer.setVisibility(View.GONE);
317 if (((anyMobileVisible && firstMobileTypeId != 0) || mNoSimsVisible) && mWifiVisible) {
318 mWifiSignalSpacer.setVisibility(View.VISIBLE);
320 mWifiSignalSpacer.setVisibility(View.GONE);
323 mNoSims.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE);
324 mNoSimsDark.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE);
326 boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode
327 || anyMobileVisible || mVpnVisible;
328 setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0);
331 public void setIconTint(int tint, float darkIntensity) {
332 boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity;
334 mDarkIntensity = darkIntensity;
335 if (changed && isAttachedToWindow()) {
340 private void applyIconTint() {
341 setTint(mVpn, mIconTint);
342 setTint(mAirplane, mIconTint);
343 applyDarkIntensity(mDarkIntensity, mNoSims, mNoSimsDark);
344 applyDarkIntensity(mDarkIntensity, mWifi, mWifiDark);
345 for (int i = 0; i < mPhoneStates.size(); i++) {
346 mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity);
350 private void applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon) {
351 lightIcon.setAlpha(1 - darkIntensity);
352 darkIcon.setAlpha(darkIntensity);
355 private void setTint(ImageView v, int tint) {
356 v.setImageTintMode(PorterDuff.Mode.SRC_ATOP);
357 v.setImageTintList(ColorStateList.valueOf(tint));
360 private class PhoneState {
361 private final int mSubId;
362 private boolean mMobileVisible = false;
363 private int mMobileStrengthId = 0, mMobileTypeId = 0;
364 private boolean mIsMobileTypeIconWide;
365 private String mMobileDescription, mMobileTypeDescription;
367 private ViewGroup mMobileGroup;
368 private ImageView mMobile, mMobileDark, mMobileType;
370 public PhoneState(int subId, Context context) {
371 ViewGroup root = (ViewGroup) LayoutInflater.from(context)
372 .inflate(R.layout.mobile_signal_group, null);
377 public void setViews(ViewGroup root) {
379 mMobile = (ImageView) root.findViewById(R.id.mobile_signal);
380 mMobileDark = (ImageView) root.findViewById(R.id.mobile_signal_dark);
381 mMobileType = (ImageView) root.findViewById(R.id.mobile_type);
384 public boolean apply(boolean isSecondaryIcon) {
385 if (mMobileVisible && !mIsAirplaneMode) {
386 mMobile.setImageResource(mMobileStrengthId);
387 mMobileDark.setImageResource(mMobileStrengthId);
388 mMobileType.setImageResource(mMobileTypeId);
389 mMobileGroup.setContentDescription(mMobileTypeDescription
390 + " " + mMobileDescription);
391 mMobileGroup.setVisibility(View.VISIBLE);
393 mMobileGroup.setVisibility(View.GONE);
396 // When this isn't next to wifi, give it some extra padding between the signals.
397 mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0,
399 mMobile.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0,
401 mMobileDark.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0,
404 if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
405 (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
407 mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
409 return mMobileVisible;
412 public void populateAccessibilityEvent(AccessibilityEvent event) {
413 if (mMobileVisible && mMobileGroup != null
414 && mMobileGroup.getContentDescription() != null) {
415 event.getText().add(mMobileGroup.getContentDescription());
419 public void setIconTint(int tint, float darkIntensity) {
420 applyDarkIntensity(darkIntensity, mMobile, mMobileDark);
421 setTint(mMobileType, tint);