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