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