1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.statusbar.phone; 16 17 import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; 18 import static android.app.StatusBarManager.DISABLE_NONE; 19 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; 20 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; 21 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; 22 23 import android.content.Context; 24 import android.os.Bundle; 25 import android.support.annotation.VisibleForTesting; 26 import android.text.TextUtils; 27 import android.util.ArraySet; 28 import android.util.Log; 29 import android.view.Gravity; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.widget.ImageView; 33 import android.widget.LinearLayout; 34 import android.widget.LinearLayout.LayoutParams; 35 36 import com.android.internal.statusbar.StatusBarIcon; 37 import com.android.systemui.DemoMode; 38 import com.android.systemui.Dependency; 39 import com.android.systemui.R; 40 import com.android.systemui.statusbar.StatusIconDisplayable; 41 import com.android.systemui.statusbar.StatusBarIconView; 42 import com.android.systemui.statusbar.StatusBarMobileView; 43 import com.android.systemui.statusbar.StatusBarWifiView; 44 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; 45 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; 46 import com.android.systemui.statusbar.policy.DarkIconDispatcher; 47 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; 48 import com.android.systemui.util.Utils.DisableStateTracker; 49 import java.util.List; 50 51 public interface StatusBarIconController { 52 53 /** 54 * When an icon is added with TAG_PRIMARY, it will be treated as the primary icon 55 * in that slot and not added as a sub slot. 56 */ 57 public static final int TAG_PRIMARY = 0; 58 addIconGroup(IconManager iconManager)59 public void addIconGroup(IconManager iconManager); removeIconGroup(IconManager iconManager)60 public void removeIconGroup(IconManager iconManager); setExternalIcon(String slot)61 public void setExternalIcon(String slot); setIcon(String slot, int resourceId, CharSequence contentDescription)62 public void setIcon(String slot, int resourceId, CharSequence contentDescription); setIcon(String slot, StatusBarIcon icon)63 public void setIcon(String slot, StatusBarIcon icon); setSignalIcon(String slot, WifiIconState state)64 public void setSignalIcon(String slot, WifiIconState state); setMobileIcons(String slot, List<MobileIconState> states)65 public void setMobileIcons(String slot, List<MobileIconState> states); setIconVisibility(String slot, boolean b)66 public void setIconVisibility(String slot, boolean b); 67 /** 68 * If you don't know what to pass for `tag`, either remove all icons for slot, or use 69 * TAG_PRIMARY to refer to the first icon at a given slot. 70 */ removeIcon(String slot, int tag)71 public void removeIcon(String slot, int tag); removeAllIconsForSlot(String slot)72 public void removeAllIconsForSlot(String slot); 73 74 public static final String ICON_BLACKLIST = "icon_blacklist"; 75 getIconBlacklist(String blackListStr)76 public static ArraySet<String> getIconBlacklist(String blackListStr) { 77 ArraySet<String> ret = new ArraySet<>(); 78 if (blackListStr == null) { 79 blackListStr = "rotate,headset"; 80 } 81 String[] blacklist = blackListStr.split(","); 82 for (String slot : blacklist) { 83 if (!TextUtils.isEmpty(slot)) { 84 ret.add(slot); 85 } 86 } 87 return ret; 88 } 89 90 /** 91 * Version of ViewGroup that observes state from the DarkIconDispatcher. 92 */ 93 public static class DarkIconManager extends IconManager { 94 private final DarkIconDispatcher mDarkIconDispatcher; 95 private int mIconHPadding; 96 DarkIconManager(LinearLayout linearLayout)97 public DarkIconManager(LinearLayout linearLayout) { 98 super(linearLayout); 99 mIconHPadding = mContext.getResources().getDimensionPixelSize( 100 R.dimen.status_bar_icon_padding); 101 mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); 102 } 103 104 @Override onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder)105 protected void onIconAdded(int index, String slot, boolean blocked, 106 StatusBarIconHolder holder) { 107 StatusIconDisplayable view = addHolder(index, slot, blocked, holder); 108 mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view); 109 } 110 111 @Override onCreateLayoutParams()112 protected LayoutParams onCreateLayoutParams() { 113 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 114 ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); 115 lp.setMargins(mIconHPadding, 0, mIconHPadding, 0); 116 return lp; 117 } 118 119 @Override destroy()120 protected void destroy() { 121 for (int i = 0; i < mGroup.getChildCount(); i++) { 122 mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(i)); 123 } 124 mGroup.removeAllViews(); 125 } 126 127 @Override onRemoveIcon(int viewIndex)128 protected void onRemoveIcon(int viewIndex) { 129 mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(viewIndex)); 130 super.onRemoveIcon(viewIndex); 131 } 132 133 @Override onSetIcon(int viewIndex, StatusBarIcon icon)134 public void onSetIcon(int viewIndex, StatusBarIcon icon) { 135 super.onSetIcon(viewIndex, icon); 136 mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex)); 137 } 138 139 @Override createDemoStatusIcons()140 protected DemoStatusIcons createDemoStatusIcons() { 141 DemoStatusIcons icons = super.createDemoStatusIcons(); 142 mDarkIconDispatcher.addDarkReceiver(icons); 143 144 return icons; 145 } 146 147 @Override exitDemoMode()148 protected void exitDemoMode() { 149 mDarkIconDispatcher.removeDarkReceiver(mDemoStatusIcons); 150 super.exitDemoMode(); 151 } 152 } 153 154 public static class TintedIconManager extends IconManager { 155 private int mColor; 156 TintedIconManager(ViewGroup group)157 public TintedIconManager(ViewGroup group) { 158 super(group); 159 } 160 161 @Override onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder)162 protected void onIconAdded(int index, String slot, boolean blocked, 163 StatusBarIconHolder holder) { 164 StatusIconDisplayable view = addHolder(index, slot, blocked, holder); 165 view.setStaticDrawableColor(mColor); 166 view.setDecorColor(mColor); 167 } 168 setTint(int color)169 public void setTint(int color) { 170 mColor = color; 171 for (int i = 0; i < mGroup.getChildCount(); i++) { 172 View child = mGroup.getChildAt(i); 173 if (child instanceof StatusIconDisplayable) { 174 StatusIconDisplayable icon = (StatusIconDisplayable) child; 175 icon.setStaticDrawableColor(mColor); 176 icon.setDecorColor(mColor); 177 } 178 } 179 } 180 181 @Override createDemoStatusIcons()182 protected DemoStatusIcons createDemoStatusIcons() { 183 DemoStatusIcons icons = super.createDemoStatusIcons(); 184 icons.setColor(mColor); 185 return icons; 186 } 187 } 188 189 /** 190 * Turns info from StatusBarIconController into ImageViews in a ViewGroup. 191 */ 192 public static class IconManager implements DemoMode { 193 protected final ViewGroup mGroup; 194 protected final Context mContext; 195 protected final int mIconSize; 196 // Whether or not these icons show up in dumpsys 197 protected boolean mShouldLog = false; 198 199 // Enables SystemUI demo mode to take effect in this group 200 protected boolean mDemoable = true; 201 private boolean mIsInDemoMode; 202 protected DemoStatusIcons mDemoStatusIcons; 203 IconManager(ViewGroup group)204 public IconManager(ViewGroup group) { 205 mGroup = group; 206 mContext = group.getContext(); 207 mIconSize = mContext.getResources().getDimensionPixelSize( 208 com.android.internal.R.dimen.status_bar_icon_size); 209 210 DisableStateTracker tracker = 211 new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS); 212 mGroup.addOnAttachStateChangeListener(tracker); 213 if (mGroup.isAttachedToWindow()) { 214 // In case we miss the first onAttachedToWindow event 215 tracker.onViewAttachedToWindow(mGroup); 216 } 217 } 218 isDemoable()219 public boolean isDemoable() { 220 return mDemoable; 221 } 222 setIsDemoable(boolean demoable)223 public void setIsDemoable(boolean demoable) { 224 mDemoable = demoable; 225 } 226 setShouldLog(boolean should)227 public void setShouldLog(boolean should) { 228 mShouldLog = should; 229 } 230 shouldLog()231 public boolean shouldLog() { 232 return mShouldLog; 233 } 234 onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder)235 protected void onIconAdded(int index, String slot, boolean blocked, 236 StatusBarIconHolder holder) { 237 addHolder(index, slot, blocked, holder); 238 } 239 addHolder(int index, String slot, boolean blocked, StatusBarIconHolder holder)240 protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked, 241 StatusBarIconHolder holder) { 242 switch (holder.getType()) { 243 case TYPE_ICON: 244 return addIcon(index, slot, blocked, holder.getIcon()); 245 246 case TYPE_WIFI: 247 return addSignalIcon(index, slot, holder.getWifiState()); 248 249 case TYPE_MOBILE: 250 return addMobileIcon(index, slot, holder.getMobileState()); 251 } 252 253 return null; 254 } 255 256 @VisibleForTesting addIcon(int index, String slot, boolean blocked, StatusBarIcon icon)257 protected StatusBarIconView addIcon(int index, String slot, boolean blocked, 258 StatusBarIcon icon) { 259 StatusBarIconView view = onCreateStatusBarIconView(slot, blocked); 260 view.set(icon); 261 mGroup.addView(view, index, onCreateLayoutParams()); 262 return view; 263 } 264 265 @VisibleForTesting addSignalIcon(int index, String slot, WifiIconState state)266 protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) { 267 StatusBarWifiView view = onCreateStatusBarWifiView(slot); 268 view.applyWifiState(state); 269 mGroup.addView(view, index, onCreateLayoutParams()); 270 271 if (mIsInDemoMode) { 272 mDemoStatusIcons.addDemoWifiView(state); 273 } 274 return view; 275 } 276 277 @VisibleForTesting addMobileIcon(int index, String slot, MobileIconState state)278 protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { 279 StatusBarMobileView view = onCreateStatusBarMobileView(slot); 280 view.applyMobileState(state); 281 mGroup.addView(view, index, onCreateLayoutParams()); 282 283 if (mIsInDemoMode) { 284 mDemoStatusIcons.addMobileView(state); 285 } 286 return view; 287 } 288 onCreateStatusBarIconView(String slot, boolean blocked)289 private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { 290 return new StatusBarIconView(mContext, slot, null, blocked); 291 } 292 onCreateStatusBarWifiView(String slot)293 private StatusBarWifiView onCreateStatusBarWifiView(String slot) { 294 StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot); 295 return view; 296 } 297 onCreateStatusBarMobileView(String slot)298 private StatusBarMobileView onCreateStatusBarMobileView(String slot) { 299 StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot); 300 return view; 301 } 302 onCreateLayoutParams()303 protected LinearLayout.LayoutParams onCreateLayoutParams() { 304 return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); 305 } 306 destroy()307 protected void destroy() { 308 mGroup.removeAllViews(); 309 } 310 onIconExternal(int viewIndex, int height)311 protected void onIconExternal(int viewIndex, int height) { 312 ImageView imageView = (ImageView) mGroup.getChildAt(viewIndex); 313 imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); 314 imageView.setAdjustViewBounds(true); 315 setHeightAndCenter(imageView, height); 316 } 317 onDensityOrFontScaleChanged()318 protected void onDensityOrFontScaleChanged() { 319 for (int i = 0; i < mGroup.getChildCount(); i++) { 320 View child = mGroup.getChildAt(i); 321 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 322 ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); 323 child.setLayoutParams(lp); 324 } 325 } 326 setHeightAndCenter(ImageView imageView, int height)327 private void setHeightAndCenter(ImageView imageView, int height) { 328 ViewGroup.LayoutParams params = imageView.getLayoutParams(); 329 params.height = height; 330 if (params instanceof LinearLayout.LayoutParams) { 331 ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL; 332 } 333 imageView.setLayoutParams(params); 334 } 335 onRemoveIcon(int viewIndex)336 protected void onRemoveIcon(int viewIndex) { 337 if (mIsInDemoMode) { 338 mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex)); 339 } 340 mGroup.removeViewAt(viewIndex); 341 } 342 onSetIcon(int viewIndex, StatusBarIcon icon)343 public void onSetIcon(int viewIndex, StatusBarIcon icon) { 344 StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex); 345 view.set(icon); 346 } 347 onSetIconHolder(int viewIndex, StatusBarIconHolder holder)348 public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) { 349 switch (holder.getType()) { 350 case TYPE_ICON: 351 onSetIcon(viewIndex, holder.getIcon()); 352 return; 353 case TYPE_WIFI: 354 onSetSignalIcon(viewIndex, holder.getWifiState()); 355 return; 356 357 case TYPE_MOBILE: 358 onSetMobileIcon(viewIndex, holder.getMobileState()); 359 default: 360 break; 361 } 362 } 363 onSetSignalIcon(int viewIndex, WifiIconState state)364 public void onSetSignalIcon(int viewIndex, WifiIconState state) { 365 StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex); 366 if (wifiView != null) { 367 wifiView.applyWifiState(state); 368 } 369 370 if (mIsInDemoMode) { 371 mDemoStatusIcons.updateWifiState(state); 372 } 373 } 374 onSetMobileIcon(int viewIndex, MobileIconState state)375 public void onSetMobileIcon(int viewIndex, MobileIconState state) { 376 StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex); 377 if (view != null) { 378 view.applyMobileState(state); 379 } 380 381 if (mIsInDemoMode) { 382 mDemoStatusIcons.updateMobileState(state); 383 } 384 } 385 386 @Override dispatchDemoCommand(String command, Bundle args)387 public void dispatchDemoCommand(String command, Bundle args) { 388 if (!mDemoable) { 389 return; 390 } 391 392 if (command.equals(COMMAND_EXIT)) { 393 if (mDemoStatusIcons != null) { 394 mDemoStatusIcons.dispatchDemoCommand(command, args); 395 exitDemoMode(); 396 } 397 mIsInDemoMode = false; 398 } else { 399 if (mDemoStatusIcons == null) { 400 mIsInDemoMode = true; 401 mDemoStatusIcons = createDemoStatusIcons(); 402 } 403 mDemoStatusIcons.dispatchDemoCommand(command, args); 404 } 405 } 406 exitDemoMode()407 protected void exitDemoMode() { 408 mDemoStatusIcons.remove(); 409 mDemoStatusIcons = null; 410 } 411 createDemoStatusIcons()412 protected DemoStatusIcons createDemoStatusIcons() { 413 return new DemoStatusIcons((LinearLayout) mGroup, mIconSize); 414 } 415 } 416 } 417