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