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.content.Context; 20 import android.telephony.SubscriptionInfo; 21 import android.util.AttributeSet; 22 import android.util.Log; 23 import android.view.LayoutInflater; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.view.accessibility.AccessibilityEvent; 27 import android.widget.ImageView; 28 import android.widget.LinearLayout; 29 30 import com.android.systemui.R; 31 import com.android.systemui.statusbar.policy.NetworkControllerImpl; 32 import com.android.systemui.statusbar.policy.SecurityController; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 37 // Intimately tied to the design of res/layout/signal_cluster_view.xml 38 public class SignalClusterView 39 extends LinearLayout 40 implements NetworkControllerImpl.SignalCluster, 41 SecurityController.SecurityControllerCallback { 42 43 static final String TAG = "SignalClusterView"; 44 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 45 46 NetworkControllerImpl mNC; 47 SecurityController mSC; 48 49 private boolean mNoSimsVisible = false; 50 private boolean mVpnVisible = false; 51 private boolean mWifiVisible = false; 52 private int mWifiStrengthId = 0; 53 private boolean mIsAirplaneMode = false; 54 private int mAirplaneIconId = 0; 55 private int mAirplaneContentDescription; 56 private String mWifiDescription; 57 private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>(); 58 59 ViewGroup mWifiGroup; 60 ImageView mVpn, mWifi, mAirplane, mNoSims; 61 View mWifiAirplaneSpacer; 62 View mWifiSignalSpacer; 63 LinearLayout mMobileSignalGroup; 64 65 private int mWideTypeIconStartPadding; 66 private int mSecondaryTelephonyPadding; 67 private int mEndPadding; 68 private int mEndPaddingNothingVisible; 69 SignalClusterView(Context context)70 public SignalClusterView(Context context) { 71 this(context, null); 72 } 73 SignalClusterView(Context context, AttributeSet attrs)74 public SignalClusterView(Context context, AttributeSet attrs) { 75 this(context, attrs, 0); 76 } 77 SignalClusterView(Context context, AttributeSet attrs, int defStyle)78 public SignalClusterView(Context context, AttributeSet attrs, int defStyle) { 79 super(context, attrs, defStyle); 80 } 81 setNetworkController(NetworkControllerImpl nc)82 public void setNetworkController(NetworkControllerImpl nc) { 83 if (DEBUG) Log.d(TAG, "NetworkController=" + nc); 84 mNC = nc; 85 } 86 setSecurityController(SecurityController sc)87 public void setSecurityController(SecurityController sc) { 88 if (DEBUG) Log.d(TAG, "SecurityController=" + sc); 89 mSC = sc; 90 mSC.addCallback(this); 91 mVpnVisible = mSC.isVpnEnabled(); 92 } 93 94 @Override onFinishInflate()95 protected void onFinishInflate() { 96 super.onFinishInflate(); 97 mWideTypeIconStartPadding = getContext().getResources().getDimensionPixelSize( 98 R.dimen.wide_type_icon_start_padding); 99 mSecondaryTelephonyPadding = getContext().getResources().getDimensionPixelSize( 100 R.dimen.secondary_telephony_padding); 101 mEndPadding = getContext().getResources().getDimensionPixelSize( 102 R.dimen.signal_cluster_battery_padding); 103 mEndPaddingNothingVisible = getContext().getResources().getDimensionPixelSize( 104 R.dimen.no_signal_cluster_battery_padding); 105 } 106 107 @Override onAttachedToWindow()108 protected void onAttachedToWindow() { 109 super.onAttachedToWindow(); 110 111 mVpn = (ImageView) findViewById(R.id.vpn); 112 mWifiGroup = (ViewGroup) findViewById(R.id.wifi_combo); 113 mWifi = (ImageView) findViewById(R.id.wifi_signal); 114 mAirplane = (ImageView) findViewById(R.id.airplane); 115 mNoSims = (ImageView) findViewById(R.id.no_sims); 116 mWifiAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); 117 mWifiSignalSpacer = findViewById(R.id.wifi_signal_spacer); 118 mMobileSignalGroup = (LinearLayout) findViewById(R.id.mobile_signal_group); 119 for (PhoneState state : mPhoneStates) { 120 mMobileSignalGroup.addView(state.mMobileGroup); 121 } 122 123 apply(); 124 } 125 126 @Override onDetachedFromWindow()127 protected void onDetachedFromWindow() { 128 mVpn = null; 129 mWifiGroup = null; 130 mWifi = null; 131 mAirplane = null; 132 mMobileSignalGroup.removeAllViews(); 133 mMobileSignalGroup = null; 134 135 super.onDetachedFromWindow(); 136 } 137 138 // From SecurityController. 139 @Override onStateChanged()140 public void onStateChanged() { 141 post(new Runnable() { 142 @Override 143 public void run() { 144 mVpnVisible = mSC.isVpnEnabled(); 145 apply(); 146 } 147 }); 148 } 149 150 @Override setWifiIndicators(boolean visible, int strengthIcon, String contentDescription)151 public void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription) { 152 mWifiVisible = visible; 153 mWifiStrengthId = strengthIcon; 154 mWifiDescription = contentDescription; 155 156 apply(); 157 } 158 159 @Override setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon, String contentDescription, String typeContentDescription, boolean isTypeIconWide, int subId)160 public void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon, 161 String contentDescription, String typeContentDescription, boolean isTypeIconWide, 162 int subId) { 163 PhoneState state = getOrInflateState(subId); 164 state.mMobileVisible = visible; 165 state.mMobileStrengthId = strengthIcon; 166 state.mMobileTypeId = typeIcon; 167 state.mMobileDescription = contentDescription; 168 state.mMobileTypeDescription = typeContentDescription; 169 state.mIsMobileTypeIconWide = isTypeIconWide; 170 171 apply(); 172 } 173 174 @Override setNoSims(boolean show)175 public void setNoSims(boolean show) { 176 mNoSimsVisible = show; 177 } 178 179 @Override setSubs(List<SubscriptionInfo> subs)180 public void setSubs(List<SubscriptionInfo> subs) { 181 // Clear out all old subIds. 182 mPhoneStates.clear(); 183 if (mMobileSignalGroup != null) { 184 mMobileSignalGroup.removeAllViews(); 185 } 186 final int n = subs.size(); 187 for (int i = 0; i < n; i++) { 188 inflatePhoneState(subs.get(i).getSubscriptionId()); 189 } 190 } 191 getOrInflateState(int subId)192 private PhoneState getOrInflateState(int subId) { 193 for (PhoneState state : mPhoneStates) { 194 if (state.mSubId == subId) { 195 return state; 196 } 197 } 198 return inflatePhoneState(subId); 199 } 200 inflatePhoneState(int subId)201 private PhoneState inflatePhoneState(int subId) { 202 PhoneState state = new PhoneState(subId, mContext); 203 if (mMobileSignalGroup != null) { 204 mMobileSignalGroup.addView(state.mMobileGroup); 205 } 206 mPhoneStates.add(state); 207 return state; 208 } 209 210 @Override setIsAirplaneMode(boolean is, int airplaneIconId, int contentDescription)211 public void setIsAirplaneMode(boolean is, int airplaneIconId, int contentDescription) { 212 mIsAirplaneMode = is; 213 mAirplaneIconId = airplaneIconId; 214 mAirplaneContentDescription = contentDescription; 215 216 apply(); 217 } 218 219 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)220 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 221 // Standard group layout onPopulateAccessibilityEvent() implementations 222 // ignore content description, so populate manually 223 if (mWifiVisible && mWifiGroup != null && mWifiGroup.getContentDescription() != null) 224 event.getText().add(mWifiGroup.getContentDescription()); 225 for (PhoneState state : mPhoneStates) { 226 state.populateAccessibilityEvent(event); 227 } 228 return super.dispatchPopulateAccessibilityEvent(event); 229 } 230 231 @Override onRtlPropertiesChanged(int layoutDirection)232 public void onRtlPropertiesChanged(int layoutDirection) { 233 super.onRtlPropertiesChanged(layoutDirection); 234 235 if (mWifi != null) { 236 mWifi.setImageDrawable(null); 237 } 238 239 for (PhoneState state : mPhoneStates) { 240 if (state.mMobile != null) { 241 state.mMobile.setImageDrawable(null); 242 } 243 if (state.mMobileType != null) { 244 state.mMobileType.setImageDrawable(null); 245 } 246 } 247 248 if(mAirplane != null) { 249 mAirplane.setImageDrawable(null); 250 } 251 252 apply(); 253 } 254 255 @Override hasOverlappingRendering()256 public boolean hasOverlappingRendering() { 257 return false; 258 } 259 260 // Run after each indicator change. apply()261 private void apply() { 262 if (mWifiGroup == null) return; 263 264 mVpn.setVisibility(mVpnVisible ? View.VISIBLE : View.GONE); 265 if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE")); 266 if (mWifiVisible) { 267 mWifi.setImageResource(mWifiStrengthId); 268 mWifiGroup.setContentDescription(mWifiDescription); 269 mWifiGroup.setVisibility(View.VISIBLE); 270 } else { 271 mWifiGroup.setVisibility(View.GONE); 272 } 273 274 if (DEBUG) Log.d(TAG, 275 String.format("wifi: %s sig=%d", 276 (mWifiVisible ? "VISIBLE" : "GONE"), 277 mWifiStrengthId)); 278 279 boolean anyMobileVisible = false; 280 int firstMobileTypeId = 0; 281 for (PhoneState state : mPhoneStates) { 282 if (state.apply(anyMobileVisible)) { 283 if (!anyMobileVisible) { 284 firstMobileTypeId = state.mMobileTypeId; 285 anyMobileVisible = true; 286 } 287 } 288 } 289 290 if (mIsAirplaneMode) { 291 mAirplane.setImageResource(mAirplaneIconId); 292 mAirplane.setContentDescription(mAirplaneContentDescription != 0 ? 293 mContext.getString(mAirplaneContentDescription) : null); 294 mAirplane.setVisibility(View.VISIBLE); 295 } else { 296 mAirplane.setVisibility(View.GONE); 297 } 298 299 if (mIsAirplaneMode && mWifiVisible) { 300 mWifiAirplaneSpacer.setVisibility(View.VISIBLE); 301 } else { 302 mWifiAirplaneSpacer.setVisibility(View.GONE); 303 } 304 305 if (((anyMobileVisible && firstMobileTypeId != 0) || mNoSimsVisible) && mWifiVisible) { 306 mWifiSignalSpacer.setVisibility(View.VISIBLE); 307 } else { 308 mWifiSignalSpacer.setVisibility(View.GONE); 309 } 310 311 mNoSims.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE); 312 313 boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode 314 || anyMobileVisible || mVpnVisible; 315 setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0); 316 } 317 318 private class PhoneState { 319 private final int mSubId; 320 private boolean mMobileVisible = false; 321 private int mMobileStrengthId = 0, mMobileTypeId = 0; 322 private boolean mIsMobileTypeIconWide; 323 private String mMobileDescription, mMobileTypeDescription; 324 325 private ViewGroup mMobileGroup; 326 private ImageView mMobile, mMobileType; 327 PhoneState(int subId, Context context)328 public PhoneState(int subId, Context context) { 329 ViewGroup root = (ViewGroup) LayoutInflater.from(context) 330 .inflate(R.layout.mobile_signal_group, null); 331 setViews(root); 332 mSubId = subId; 333 } 334 setViews(ViewGroup root)335 public void setViews(ViewGroup root) { 336 mMobileGroup = root; 337 mMobile = (ImageView) root.findViewById(R.id.mobile_signal); 338 mMobileType = (ImageView) root.findViewById(R.id.mobile_type); 339 } 340 apply(boolean isSecondaryIcon)341 public boolean apply(boolean isSecondaryIcon) { 342 if (mMobileVisible && !mIsAirplaneMode) { 343 mMobile.setImageResource(mMobileStrengthId); 344 mMobileType.setImageResource(mMobileTypeId); 345 mMobileGroup.setContentDescription(mMobileTypeDescription 346 + " " + mMobileDescription); 347 mMobileGroup.setVisibility(View.VISIBLE); 348 } else { 349 mMobileGroup.setVisibility(View.GONE); 350 } 351 352 // When this isn't next to wifi, give it some extra padding between the signals. 353 mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0, 354 0, 0, 0); 355 mMobile.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0, 356 0, 0, 0); 357 358 if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d", 359 (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId)); 360 361 mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE); 362 363 return mMobileVisible; 364 } 365 populateAccessibilityEvent(AccessibilityEvent event)366 public void populateAccessibilityEvent(AccessibilityEvent event) { 367 if (mMobileVisible && mMobileGroup != null 368 && mMobileGroup.getContentDescription() != null) { 369 event.getText().add(mMobileGroup.getContentDescription()); 370 } 371 } 372 } 373 } 374 375