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 17package com.android.systemui.statusbar; 18 19import android.content.Context; 20import android.telephony.SubscriptionInfo; 21import android.util.AttributeSet; 22import android.util.Log; 23import android.view.LayoutInflater; 24import android.view.View; 25import android.view.ViewGroup; 26import android.view.accessibility.AccessibilityEvent; 27import android.widget.ImageView; 28import android.widget.LinearLayout; 29 30import com.android.systemui.R; 31import com.android.systemui.statusbar.policy.NetworkControllerImpl; 32import com.android.systemui.statusbar.policy.SecurityController; 33 34import java.util.ArrayList; 35import java.util.List; 36 37// Intimately tied to the design of res/layout/signal_cluster_view.xml 38public 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 70 public SignalClusterView(Context context) { 71 this(context, null); 72 } 73 74 public SignalClusterView(Context context, AttributeSet attrs) { 75 this(context, attrs, 0); 76 } 77 78 public SignalClusterView(Context context, AttributeSet attrs, int defStyle) { 79 super(context, attrs, defStyle); 80 } 81 82 public void setNetworkController(NetworkControllerImpl nc) { 83 if (DEBUG) Log.d(TAG, "NetworkController=" + nc); 84 mNC = nc; 85 } 86 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 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 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 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 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 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 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 175 public void setNoSims(boolean show) { 176 mNoSimsVisible = show; 177 } 178 179 @Override 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 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 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 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 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 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 256 public boolean hasOverlappingRendering() { 257 return false; 258 } 259 260 // Run after each indicator change. 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 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 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 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 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