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