1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.phone.ui; 18 19 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_BINDABLE; 20 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; 21 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW; 22 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW; 23 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.os.Bundle; 27 import android.view.ViewGroup; 28 import android.widget.LinearLayout; 29 30 import androidx.annotation.VisibleForTesting; 31 32 import com.android.internal.statusbar.StatusBarIcon; 33 import com.android.systemui.demomode.DemoModeCommandReceiver; 34 import com.android.systemui.statusbar.BaseStatusBarFrameLayout; 35 import com.android.systemui.statusbar.StatusBarIconView; 36 import com.android.systemui.statusbar.StatusIconDisplayable; 37 import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; 38 import com.android.systemui.statusbar.phone.DemoStatusIcons; 39 import com.android.systemui.statusbar.phone.StatusBarIconHolder; 40 import com.android.systemui.statusbar.phone.StatusBarIconHolder.BindableIconHolder; 41 import com.android.systemui.statusbar.phone.StatusBarLocation; 42 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; 43 import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder; 44 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView; 45 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; 46 import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView; 47 import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter; 48 import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView; 49 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel; 50 import com.android.systemui.util.Assert; 51 52 import java.util.ArrayList; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Map; 56 57 /** 58 * Turns info from StatusBarIconController into ImageViews in a ViewGroup. 59 */ 60 public class IconManager implements DemoModeCommandReceiver { 61 protected final ViewGroup mGroup; 62 private final MobileContextProvider mMobileContextProvider; 63 private final LocationBasedWifiViewModel mWifiViewModel; 64 private final MobileIconsViewModel mMobileIconsViewModel; 65 66 /** 67 * Stores the list of bindable icons that have been added, keyed on slot name. This ensures 68 * we don't accidentally add the same bindable icon twice. 69 */ 70 private final Map<String, BindableIconHolder> mBindableIcons = new HashMap<>(); 71 protected final Context mContext; 72 protected int mIconSize; 73 // Whether or not these icons show up in dumpsys 74 protected boolean mShouldLog = false; 75 private StatusBarIconController mController; 76 private final StatusBarLocation mLocation; 77 78 // Enables SystemUI demo mode to take effect in this group 79 protected boolean mDemoable = true; 80 private boolean mIsInDemoMode; 81 protected DemoStatusIcons mDemoStatusIcons; 82 83 protected ArrayList<String> mBlockList = new ArrayList<>(); 84 IconManager( ViewGroup group, StatusBarLocation location, WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider )85 public IconManager( 86 ViewGroup group, 87 StatusBarLocation location, 88 WifiUiAdapter wifiUiAdapter, 89 MobileUiAdapter mobileUiAdapter, 90 MobileContextProvider mobileContextProvider 91 ) { 92 mGroup = group; 93 mMobileContextProvider = mobileContextProvider; 94 mContext = group.getContext(); 95 mLocation = location; 96 97 reloadDimens(); 98 99 // This starts the flow for the new pipeline, and will notify us of changes via 100 // {@link #setNewMobileIconIds} 101 mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel(); 102 MobileIconsBinder.bind(mGroup, mMobileIconsViewModel); 103 104 mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); 105 } 106 isDemoable()107 public boolean isDemoable() { 108 return mDemoable; 109 } 110 setController(StatusBarIconController controller)111 void setController(StatusBarIconController controller) { 112 mController = controller; 113 } 114 115 /** Sets the list of slots that should be blocked from showing in the status bar. */ setBlockList(@ullable List<String> blockList)116 public void setBlockList(@Nullable List<String> blockList) { 117 Assert.isMainThread(); 118 mBlockList.clear(); 119 mBlockList.addAll(blockList); 120 if (mController != null) { 121 mController.refreshIconGroup(this); 122 } 123 } 124 125 /** Sets whether this manager's changes should be dumped in a bug report. */ setShouldLog(boolean should)126 public void setShouldLog(boolean should) { 127 mShouldLog = should; 128 } 129 130 /** Returns true if this manager's changes should be dumped in a bug report. */ shouldLog()131 public boolean shouldLog() { 132 return mShouldLog; 133 } 134 onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder)135 protected void onIconAdded(int index, String slot, boolean blocked, 136 StatusBarIconHolder holder) { 137 addHolder(index, slot, blocked, holder); 138 } 139 addHolder(int index, String slot, boolean blocked, StatusBarIconHolder holder)140 protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked, 141 StatusBarIconHolder holder) { 142 // This is a little hacky, and probably regrettable, but just set `blocked` on any icon 143 // that is in our blocked list, then we'll never see it 144 if (mBlockList.contains(slot)) { 145 blocked = true; 146 } 147 return switch (holder.getType()) { 148 case TYPE_ICON -> addIcon(index, slot, blocked, holder.getIcon()); 149 case TYPE_WIFI_NEW -> addNewWifiIcon(index, slot); 150 case TYPE_MOBILE_NEW -> addNewMobileIcon(index, slot, holder.getTag()); 151 case TYPE_BINDABLE -> 152 // Safe cast, since only BindableIconHolders can set this tag on themselves 153 addBindableIcon((BindableIconHolder) holder, index); 154 default -> null; 155 }; 156 } 157 158 @VisibleForTesting addIcon(int index, String slot, boolean blocked, StatusBarIcon icon)159 protected StatusBarIconView addIcon(int index, String slot, boolean blocked, 160 StatusBarIcon icon) { 161 StatusBarIconView view = onCreateStatusBarIconView(slot, blocked); 162 view.set(icon); 163 mGroup.addView(view, index, onCreateLayoutParams()); 164 return view; 165 } 166 167 /** 168 * ModernStatusBarViews can be created and bound, and thus do not need to update their 169 * drawable by sending multiple calls to setIcon. Instead, by using a bindable 170 * icon view, we can simply create the icon when requested and allow the 171 * ViewBinder to control its visual state. 172 */ addBindableIcon(BindableIconHolder holder, int index)173 protected StatusIconDisplayable addBindableIcon(BindableIconHolder holder, 174 int index) { 175 mBindableIcons.put(holder.getSlot(), holder); 176 ModernStatusBarView view = holder.getInitializer().createAndBind(mContext); 177 mGroup.addView(view, index, onCreateLayoutParams()); 178 if (mIsInDemoMode) { 179 mDemoStatusIcons.addBindableIcon(holder); 180 } 181 return view; 182 } 183 addNewWifiIcon(int index, String slot)184 protected StatusIconDisplayable addNewWifiIcon(int index, String slot) { 185 ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot); 186 mGroup.addView(view, index, onCreateLayoutParams()); 187 188 if (mIsInDemoMode) { 189 mDemoStatusIcons.addModernWifiView(mWifiViewModel); 190 } 191 192 return view; 193 } 194 195 addNewMobileIcon( int index, String slot, int subId )196 protected StatusIconDisplayable addNewMobileIcon( 197 int index, 198 String slot, 199 int subId 200 ) { 201 BaseStatusBarFrameLayout view = onCreateModernStatusBarMobileView(slot, subId); 202 mGroup.addView(view, index, onCreateLayoutParams()); 203 204 if (mIsInDemoMode) { 205 Context mobileContext = mMobileContextProvider 206 .getMobileContextForSub(subId, mContext); 207 mDemoStatusIcons.addModernMobileView( 208 mobileContext, 209 mMobileIconsViewModel.getLogger(), 210 subId); 211 } 212 213 return view; 214 } 215 onCreateStatusBarIconView(String slot, boolean blocked)216 private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { 217 return new StatusBarIconView(mContext, slot, null, blocked); 218 } 219 onCreateModernStatusBarWifiView(String slot)220 private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) { 221 return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel); 222 } 223 onCreateModernStatusBarMobileView( String slot, int subId)224 private ModernStatusBarMobileView onCreateModernStatusBarMobileView( 225 String slot, int subId) { 226 Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext); 227 return ModernStatusBarMobileView 228 .constructAndBind( 229 mobileContext, 230 mMobileIconsViewModel.getLogger(), 231 slot, 232 mMobileIconsViewModel.viewModelForSub(subId, mLocation) 233 ); 234 } 235 onCreateLayoutParams()236 protected LinearLayout.LayoutParams onCreateLayoutParams() { 237 return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); 238 } 239 destroy()240 protected void destroy() { 241 mGroup.removeAllViews(); 242 } 243 reloadDimens()244 protected void reloadDimens() { 245 mIconSize = mContext.getResources().getDimensionPixelSize( 246 com.android.internal.R.dimen.status_bar_icon_size_sp); 247 } 248 onRemoveIcon(int viewIndex)249 protected void onRemoveIcon(int viewIndex) { 250 if (mIsInDemoMode) { 251 mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex)); 252 } 253 mGroup.removeViewAt(viewIndex); 254 } 255 256 /** Called once an icon has been set. */ onSetIcon(int viewIndex, StatusBarIcon icon)257 public void onSetIcon(int viewIndex, StatusBarIcon icon) { 258 StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex); 259 view.set(icon); 260 } 261 262 /** Called once an icon holder has been set. */ onSetIconHolder(int viewIndex, StatusBarIconHolder holder)263 public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) { 264 switch (holder.getType()) { 265 case TYPE_ICON: 266 onSetIcon(viewIndex, holder.getIcon()); 267 return; 268 case TYPE_MOBILE_NEW: 269 case TYPE_WIFI_NEW: 270 case TYPE_BINDABLE: 271 // Nothing, the new icons update themselves 272 return; 273 default: 274 break; 275 } 276 } 277 278 @Override dispatchDemoCommand(String command, Bundle args)279 public void dispatchDemoCommand(String command, Bundle args) { 280 if (!mDemoable) { 281 return; 282 } 283 284 mDemoStatusIcons.dispatchDemoCommand(command, args); 285 } 286 287 @Override onDemoModeStarted()288 public void onDemoModeStarted() { 289 mIsInDemoMode = true; 290 if (mDemoStatusIcons == null) { 291 mDemoStatusIcons = createDemoStatusIcons(); 292 mDemoStatusIcons.addModernWifiView(mWifiViewModel); 293 for (BindableIconHolder holder : mBindableIcons.values()) { 294 mDemoStatusIcons.addBindableIcon(holder); 295 } 296 } 297 mDemoStatusIcons.onDemoModeStarted(); 298 } 299 300 @Override onDemoModeFinished()301 public void onDemoModeFinished() { 302 if (mDemoStatusIcons != null) { 303 mDemoStatusIcons.onDemoModeFinished(); 304 exitDemoMode(); 305 mIsInDemoMode = false; 306 } 307 } 308 exitDemoMode()309 protected void exitDemoMode() { 310 mDemoStatusIcons.remove(); 311 mDemoStatusIcons = null; 312 } 313 createDemoStatusIcons()314 protected DemoStatusIcons createDemoStatusIcons() { 315 return new DemoStatusIcons( 316 (LinearLayout) mGroup, 317 mMobileIconsViewModel, 318 mLocation, 319 mIconSize 320 ); 321 } 322 } 323