• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_MOBILE_NEW;
20 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
21 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW;
22 
23 import android.annotation.Nullable;
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.R;
39 import com.android.systemui.dagger.SysUISingleton;
40 import com.android.systemui.demomode.DemoModeCommandReceiver;
41 import com.android.systemui.plugins.DarkIconDispatcher;
42 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
43 import com.android.systemui.statusbar.BaseStatusBarFrameLayout;
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.connectivity.ui.MobileContextProvider;
49 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
50 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
51 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
52 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
53 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
54 import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
55 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
56 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
57 import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
58 import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
59 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel;
60 import com.android.systemui.util.Assert;
61 
62 import java.util.ArrayList;
63 import java.util.List;
64 
65 import javax.inject.Inject;
66 
67 public interface StatusBarIconController {
68 
69     /**
70      * When an icon is added with TAG_PRIMARY, it will be treated as the primary icon
71      * in that slot and not added as a sub slot.
72      */
73     int TAG_PRIMARY = 0;
74 
75     /** */
addIconGroup(IconManager iconManager)76     void addIconGroup(IconManager iconManager);
77     /** */
removeIconGroup(IconManager iconManager)78     void removeIconGroup(IconManager iconManager);
79 
80     /** Refresh the state of an IconManager by recreating the views */
refreshIconGroup(IconManager iconManager)81     void refreshIconGroup(IconManager iconManager);
82 
83     /**
84      * Adds or updates an icon that comes from an active tile service.
85      *
86      * If the icon is null, the icon will be removed.
87      */
setIconFromTile(String slot, @Nullable StatusBarIcon icon)88     void setIconFromTile(String slot, @Nullable StatusBarIcon icon);
89 
90     /** Removes an icon that had come from an active tile service. */
removeIconForTile(String slot)91     void removeIconForTile(String slot);
92 
93     /**
94      * Adds or updates an icon for the given slot for **internal system icons**.
95      *
96      * TODO(b/265307726): Re-name this to `setInternalIcon`.
97      */
setIcon(String slot, int resourceId, CharSequence contentDescription)98     void setIcon(String slot, int resourceId, CharSequence contentDescription);
99 
100     /** */
setWifiIcon(String slot, WifiIconState state)101     void setWifiIcon(String slot, WifiIconState state);
102 
103     /**
104      * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been
105      * set up (inflated and added to the view hierarchy).
106      *
107      * This method completely replaces {@link #setWifiIcon} with the information from the new wifi
108      * data pipeline. Icons will automatically keep their state up to date, so we don't have to
109      * worry about funneling state objects through anymore.
110      */
setNewWifiIcon()111     void setNewWifiIcon();
112 
113     /** */
setMobileIcons(String slot, List<MobileIconState> states)114     void setMobileIcons(String slot, List<MobileIconState> states);
115 
116     /**
117      * This method completely replaces {@link #setMobileIcons} with the information from the new
118      * mobile data pipeline. Icons will automatically keep their state up to date, so we don't have
119      * to worry about funneling MobileIconState objects through anymore.
120      */
setNewMobileIconSubIds(List<Integer> subIds)121     void setNewMobileIconSubIds(List<Integer> subIds);
122     /**
123      * Display the no calling & SMS icons.
124      */
setCallStrengthIcons(String slot, List<CallIndicatorIconState> states)125     void setCallStrengthIcons(String slot, List<CallIndicatorIconState> states);
126 
127     /**
128      * Display the no calling & SMS icons.
129      */
setNoCallingIcons(String slot, List<CallIndicatorIconState> states)130     void setNoCallingIcons(String slot, List<CallIndicatorIconState> states);
131 
setIconVisibility(String slot, boolean b)132     public void setIconVisibility(String slot, boolean b);
133 
134     /**
135      * Sets the live region mode for the icon
136      *
137      * @param slot                    Icon slot to set region for
138      * @param accessibilityLiveRegion live region mode for the icon
139      * @see android.view.View#setAccessibilityLiveRegion(int)
140      */
setIconAccessibilityLiveRegion(String slot, int accessibilityLiveRegion)141     void setIconAccessibilityLiveRegion(String slot, int accessibilityLiveRegion);
142 
143     /**
144      * If you don't know what to pass for `tag`, either remove all icons for slot, or use
145      * TAG_PRIMARY to refer to the first icon at a given slot.
146      */
removeIcon(String slot, int tag)147     void removeIcon(String slot, int tag);
148 
149     /**
150      * TODO(b/265307726): Re-name this to `removeAllIconsForInternalSlot`.
151      */
removeAllIconsForSlot(String slot)152     void removeAllIconsForSlot(String slot);
153 
154     // TODO: See if we can rename this tunable name.
155     String ICON_HIDE_LIST = "icon_blacklist";
156 
157     /** Reads the default hide list from config value unless hideListStr is provided. */
getIconHideList(Context context, String hideListStr)158     static ArraySet<String> getIconHideList(Context context, String hideListStr) {
159         ArraySet<String> ret = new ArraySet<>();
160         String[] hideList = hideListStr == null
161                 ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude)
162                 : hideListStr.split(",");
163         for (String slot : hideList) {
164             if (!TextUtils.isEmpty(slot)) {
165                 ret.add(slot);
166             }
167         }
168         return ret;
169     }
170 
171     /**
172      * Version of ViewGroup that observes state from the DarkIconDispatcher.
173      */
174     class DarkIconManager extends IconManager {
175         private final DarkIconDispatcher mDarkIconDispatcher;
176         private int mIconHPadding;
177 
DarkIconManager( LinearLayout linearLayout, StatusBarLocation location, StatusBarPipelineFlags statusBarPipelineFlags, WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider, DarkIconDispatcher darkIconDispatcher)178         public DarkIconManager(
179                 LinearLayout linearLayout,
180                 StatusBarLocation location,
181                 StatusBarPipelineFlags statusBarPipelineFlags,
182                 WifiUiAdapter wifiUiAdapter,
183                 MobileUiAdapter mobileUiAdapter,
184                 MobileContextProvider mobileContextProvider,
185                 DarkIconDispatcher darkIconDispatcher) {
186             super(linearLayout,
187                     location,
188                     statusBarPipelineFlags,
189                     wifiUiAdapter,
190                     mobileUiAdapter,
191                     mobileContextProvider);
192             mIconHPadding = mContext.getResources().getDimensionPixelSize(
193                     R.dimen.status_bar_icon_padding);
194             mDarkIconDispatcher = darkIconDispatcher;
195         }
196 
197         @Override
onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder)198         protected void onIconAdded(int index, String slot, boolean blocked,
199                 StatusBarIconHolder holder) {
200             StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
201             mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);
202         }
203 
204         @Override
onCreateLayoutParams()205         protected LayoutParams onCreateLayoutParams() {
206             LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
207                     ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
208             lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
209             return lp;
210         }
211 
212         @Override
destroy()213         protected void destroy() {
214             for (int i = 0; i < mGroup.getChildCount(); i++) {
215                 mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(i));
216             }
217             mGroup.removeAllViews();
218         }
219 
220         @Override
onRemoveIcon(int viewIndex)221         protected void onRemoveIcon(int viewIndex) {
222             mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(viewIndex));
223             super.onRemoveIcon(viewIndex);
224         }
225 
226         @Override
onSetIcon(int viewIndex, StatusBarIcon icon)227         public void onSetIcon(int viewIndex, StatusBarIcon icon) {
228             super.onSetIcon(viewIndex, icon);
229             mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex));
230         }
231 
232         @Override
createDemoStatusIcons()233         protected DemoStatusIcons createDemoStatusIcons() {
234             DemoStatusIcons icons = super.createDemoStatusIcons();
235             mDarkIconDispatcher.addDarkReceiver(icons);
236 
237             return icons;
238         }
239 
240         @Override
exitDemoMode()241         protected void exitDemoMode() {
242             mDarkIconDispatcher.removeDarkReceiver(mDemoStatusIcons);
243             super.exitDemoMode();
244         }
245 
246         @SysUISingleton
247         public static class Factory {
248             private final StatusBarPipelineFlags mStatusBarPipelineFlags;
249             private final WifiUiAdapter mWifiUiAdapter;
250             private final MobileContextProvider mMobileContextProvider;
251             private final MobileUiAdapter mMobileUiAdapter;
252             private final DarkIconDispatcher mDarkIconDispatcher;
253 
254             @Inject
Factory( StatusBarPipelineFlags statusBarPipelineFlags, WifiUiAdapter wifiUiAdapter, MobileContextProvider mobileContextProvider, MobileUiAdapter mobileUiAdapter, DarkIconDispatcher darkIconDispatcher)255             public Factory(
256                     StatusBarPipelineFlags statusBarPipelineFlags,
257                     WifiUiAdapter wifiUiAdapter,
258                     MobileContextProvider mobileContextProvider,
259                     MobileUiAdapter mobileUiAdapter,
260                     DarkIconDispatcher darkIconDispatcher) {
261                 mStatusBarPipelineFlags = statusBarPipelineFlags;
262                 mWifiUiAdapter = wifiUiAdapter;
263                 mMobileContextProvider = mobileContextProvider;
264                 mMobileUiAdapter = mobileUiAdapter;
265                 mDarkIconDispatcher = darkIconDispatcher;
266             }
267 
create(LinearLayout group, StatusBarLocation location)268             public DarkIconManager create(LinearLayout group, StatusBarLocation location) {
269                 return new DarkIconManager(
270                         group,
271                         location,
272                         mStatusBarPipelineFlags,
273                         mWifiUiAdapter,
274                         mMobileUiAdapter,
275                         mMobileContextProvider,
276                         mDarkIconDispatcher);
277             }
278         }
279     }
280 
281     /**
282      *
283      */
284     class TintedIconManager extends IconManager {
285         private int mColor;
286 
TintedIconManager( ViewGroup group, StatusBarLocation location, StatusBarPipelineFlags statusBarPipelineFlags, WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider )287         public TintedIconManager(
288                 ViewGroup group,
289                 StatusBarLocation location,
290                 StatusBarPipelineFlags statusBarPipelineFlags,
291                 WifiUiAdapter wifiUiAdapter,
292                 MobileUiAdapter mobileUiAdapter,
293                 MobileContextProvider mobileContextProvider
294         ) {
295             super(group,
296                     location,
297                     statusBarPipelineFlags,
298                     wifiUiAdapter,
299                     mobileUiAdapter,
300                     mobileContextProvider);
301         }
302 
303         @Override
onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder)304         protected void onIconAdded(int index, String slot, boolean blocked,
305                 StatusBarIconHolder holder) {
306             StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
307             view.setStaticDrawableColor(mColor);
308             view.setDecorColor(mColor);
309         }
310 
setTint(int color)311         public void setTint(int color) {
312             mColor = color;
313             for (int i = 0; i < mGroup.getChildCount(); i++) {
314                 View child = mGroup.getChildAt(i);
315                 if (child instanceof StatusIconDisplayable) {
316                     StatusIconDisplayable icon = (StatusIconDisplayable) child;
317                     icon.setStaticDrawableColor(mColor);
318                     icon.setDecorColor(mColor);
319                 }
320             }
321         }
322 
323         @Override
createDemoStatusIcons()324         protected DemoStatusIcons createDemoStatusIcons() {
325             DemoStatusIcons icons = super.createDemoStatusIcons();
326             icons.setColor(mColor);
327             return icons;
328         }
329 
330         @SysUISingleton
331         public static class Factory {
332             private final StatusBarPipelineFlags mStatusBarPipelineFlags;
333             private final WifiUiAdapter mWifiUiAdapter;
334             private final MobileContextProvider mMobileContextProvider;
335             private final MobileUiAdapter mMobileUiAdapter;
336 
337             @Inject
Factory( StatusBarPipelineFlags statusBarPipelineFlags, WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider )338             public Factory(
339                     StatusBarPipelineFlags statusBarPipelineFlags,
340                     WifiUiAdapter wifiUiAdapter,
341                     MobileUiAdapter mobileUiAdapter,
342                     MobileContextProvider mobileContextProvider
343             ) {
344                 mStatusBarPipelineFlags = statusBarPipelineFlags;
345                 mWifiUiAdapter = wifiUiAdapter;
346                 mMobileUiAdapter = mobileUiAdapter;
347                 mMobileContextProvider = mobileContextProvider;
348             }
349 
create(ViewGroup group, StatusBarLocation location)350             public TintedIconManager create(ViewGroup group, StatusBarLocation location) {
351                 return new TintedIconManager(
352                         group,
353                         location,
354                         mStatusBarPipelineFlags,
355                         mWifiUiAdapter,
356                         mMobileUiAdapter,
357                         mMobileContextProvider);
358             }
359         }
360     }
361 
362     /**
363      * Turns info from StatusBarIconController into ImageViews in a ViewGroup.
364      */
365     class IconManager implements DemoModeCommandReceiver {
366         protected final ViewGroup mGroup;
367         private final StatusBarPipelineFlags mStatusBarPipelineFlags;
368         private final MobileContextProvider mMobileContextProvider;
369         private final LocationBasedWifiViewModel mWifiViewModel;
370         private final MobileIconsViewModel mMobileIconsViewModel;
371 
372         protected final Context mContext;
373         protected final int mIconSize;
374         // Whether or not these icons show up in dumpsys
375         protected boolean mShouldLog = false;
376         private StatusBarIconController mController;
377         private final StatusBarLocation mLocation;
378 
379         // Enables SystemUI demo mode to take effect in this group
380         protected boolean mDemoable = true;
381         private boolean mIsInDemoMode;
382         protected DemoStatusIcons mDemoStatusIcons;
383 
384         protected ArrayList<String> mBlockList = new ArrayList<>();
385 
IconManager( ViewGroup group, StatusBarLocation location, StatusBarPipelineFlags statusBarPipelineFlags, WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider )386         public IconManager(
387                 ViewGroup group,
388                 StatusBarLocation location,
389                 StatusBarPipelineFlags statusBarPipelineFlags,
390                 WifiUiAdapter wifiUiAdapter,
391                 MobileUiAdapter mobileUiAdapter,
392                 MobileContextProvider mobileContextProvider
393         ) {
394             mGroup = group;
395             mStatusBarPipelineFlags = statusBarPipelineFlags;
396             mMobileContextProvider = mobileContextProvider;
397             mContext = group.getContext();
398             mIconSize = mContext.getResources().getDimensionPixelSize(
399                     com.android.internal.R.dimen.status_bar_icon_size);
400             mLocation = location;
401 
402             if (statusBarPipelineFlags.runNewMobileIconsBackend()) {
403                 // This starts the flow for the new pipeline, and will notify us of changes if
404                 // {@link StatusBarPipelineFlags#useNewMobileIcons} is also true.
405                 mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
406                 MobileIconsBinder.bind(mGroup, mMobileIconsViewModel);
407             } else {
408                 mMobileIconsViewModel = null;
409             }
410 
411             if (statusBarPipelineFlags.runNewWifiIconBackend()) {
412                 // This starts the flow for the new pipeline, and will notify us of changes if
413                 // {@link StatusBarPipelineFlags#useNewWifiIcon} is also true.
414                 mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation);
415             } else {
416                 mWifiViewModel = null;
417             }
418         }
419 
isDemoable()420         public boolean isDemoable() {
421             return mDemoable;
422         }
423 
setIsDemoable(boolean demoable)424         public void setIsDemoable(boolean demoable) {
425             mDemoable = demoable;
426         }
427 
setController(StatusBarIconController controller)428         void setController(StatusBarIconController controller) {
429             mController = controller;
430         }
431 
setBlockList(@ullable List<String> blockList)432         public void setBlockList(@Nullable List<String> blockList) {
433             Assert.isMainThread();
434             mBlockList.clear();
435             mBlockList.addAll(blockList);
436             if (mController != null) {
437                 mController.refreshIconGroup(this);
438             }
439         }
440 
setShouldLog(boolean should)441         public void setShouldLog(boolean should) {
442             mShouldLog = should;
443         }
444 
shouldLog()445         public boolean shouldLog() {
446             return mShouldLog;
447         }
448 
onIconAdded(int index, String slot, boolean blocked, StatusBarIconHolder holder)449         protected void onIconAdded(int index, String slot, boolean blocked,
450                 StatusBarIconHolder holder) {
451             addHolder(index, slot, blocked, holder);
452         }
453 
addHolder(int index, String slot, boolean blocked, StatusBarIconHolder holder)454         protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
455                 StatusBarIconHolder holder) {
456             // This is a little hacky, and probably regrettable, but just set `blocked` on any icon
457             // that is in our blocked list, then we'll never see it
458             if (mBlockList.contains(slot)) {
459                 blocked = true;
460             }
461             switch (holder.getType()) {
462                 case TYPE_ICON:
463                     return addIcon(index, slot, blocked, holder.getIcon());
464 
465                 case TYPE_WIFI:
466                     return addWifiIcon(index, slot, holder.getWifiState());
467 
468                 case TYPE_WIFI_NEW:
469                     return addNewWifiIcon(index, slot);
470 
471                 case TYPE_MOBILE:
472                     return addMobileIcon(index, slot, holder.getMobileState());
473 
474                 case TYPE_MOBILE_NEW:
475                     return addNewMobileIcon(index, slot, holder.getTag());
476             }
477 
478             return null;
479         }
480 
481         @VisibleForTesting
addIcon(int index, String slot, boolean blocked, StatusBarIcon icon)482         protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
483                 StatusBarIcon icon) {
484             StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
485             view.set(icon);
486             mGroup.addView(view, index, onCreateLayoutParams());
487             return view;
488         }
489 
490         @VisibleForTesting
addWifiIcon(int index, String slot, WifiIconState state)491         protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) {
492             if (mStatusBarPipelineFlags.useNewWifiIcon()) {
493                 throw new IllegalStateException("Attempting to add a wifi icon while the new "
494                         + "icons are enabled is not supported");
495             }
496 
497             final StatusBarWifiView view = onCreateStatusBarWifiView(slot);
498             view.applyWifiState(state);
499             mGroup.addView(view, index, onCreateLayoutParams());
500 
501             if (mIsInDemoMode) {
502                 mDemoStatusIcons.addDemoWifiView(state);
503             }
504             return view;
505         }
506 
addNewWifiIcon(int index, String slot)507         protected StatusIconDisplayable addNewWifiIcon(int index, String slot) {
508             if (!mStatusBarPipelineFlags.useNewWifiIcon()) {
509                 throw new IllegalStateException("Attempting to add a wifi icon using the new"
510                         + "pipeline, but the enabled flag is false.");
511             }
512 
513             ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot);
514             mGroup.addView(view, index, onCreateLayoutParams());
515 
516             if (mIsInDemoMode) {
517                 mDemoStatusIcons.addModernWifiView(mWifiViewModel);
518             }
519 
520             return view;
521         }
522 
523         @VisibleForTesting
addMobileIcon( int index, String slot, MobileIconState state )524         protected StatusIconDisplayable addMobileIcon(
525                 int index,
526                 String slot,
527                 MobileIconState state
528         ) {
529             if (mStatusBarPipelineFlags.useNewMobileIcons()) {
530                 throw new IllegalStateException("Attempting to add a mobile icon while the new "
531                         + "icons are enabled is not supported");
532             }
533 
534             // Use the `subId` field as a key to query for the correct context
535             StatusBarMobileView mobileView = onCreateStatusBarMobileView(state.subId, slot);
536             mobileView.applyMobileState(state);
537             mGroup.addView(mobileView, index, onCreateLayoutParams());
538 
539             if (mIsInDemoMode) {
540                 Context mobileContext = mMobileContextProvider
541                         .getMobileContextForSub(state.subId, mContext);
542                 mDemoStatusIcons.addMobileView(state, mobileContext);
543             }
544             return mobileView;
545         }
546 
addNewMobileIcon( int index, String slot, int subId )547         protected StatusIconDisplayable addNewMobileIcon(
548                 int index,
549                 String slot,
550                 int subId
551         ) {
552             if (!mStatusBarPipelineFlags.useNewMobileIcons()) {
553                 throw new IllegalStateException("Attempting to add a mobile icon using the new"
554                         + "pipeline, but the enabled flag is false.");
555             }
556 
557             BaseStatusBarFrameLayout view = onCreateModernStatusBarMobileView(slot, subId);
558             mGroup.addView(view, index, onCreateLayoutParams());
559 
560             if (mIsInDemoMode) {
561                 mDemoStatusIcons.addModernMobileView(
562                         mContext,
563                         mMobileIconsViewModel.getLogger(),
564                         subId);
565             }
566 
567             return view;
568         }
569 
onCreateStatusBarIconView(String slot, boolean blocked)570         private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
571             return new StatusBarIconView(mContext, slot, null, blocked);
572         }
573 
onCreateStatusBarWifiView(String slot)574         private StatusBarWifiView onCreateStatusBarWifiView(String slot) {
575             StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot);
576             return view;
577         }
578 
onCreateModernStatusBarWifiView(String slot)579         private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) {
580             return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel);
581         }
582 
onCreateStatusBarMobileView(int subId, String slot)583         private StatusBarMobileView onCreateStatusBarMobileView(int subId, String slot) {
584             Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
585             StatusBarMobileView view = StatusBarMobileView
586                     .fromContext(mobileContext, slot);
587             return view;
588         }
589 
onCreateModernStatusBarMobileView( String slot, int subId)590         private ModernStatusBarMobileView onCreateModernStatusBarMobileView(
591                 String slot, int subId) {
592             Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
593             return ModernStatusBarMobileView
594                     .constructAndBind(
595                             mobileContext,
596                             mMobileIconsViewModel.getLogger(),
597                             slot,
598                             mMobileIconsViewModel.viewModelForSub(subId, mLocation)
599                         );
600         }
601 
onCreateLayoutParams()602         protected LinearLayout.LayoutParams onCreateLayoutParams() {
603             return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
604         }
605 
destroy()606         protected void destroy() {
607             mGroup.removeAllViews();
608         }
609 
onDensityOrFontScaleChanged()610         protected void onDensityOrFontScaleChanged() {
611             for (int i = 0; i < mGroup.getChildCount(); i++) {
612                 View child = mGroup.getChildAt(i);
613                 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
614                         ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
615                 child.setLayoutParams(lp);
616             }
617         }
618 
setHeightAndCenter(ImageView imageView, int height)619         private void setHeightAndCenter(ImageView imageView, int height) {
620             ViewGroup.LayoutParams params = imageView.getLayoutParams();
621             params.height = height;
622             if (params instanceof LinearLayout.LayoutParams) {
623                 ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL;
624             }
625             imageView.setLayoutParams(params);
626         }
627 
onRemoveIcon(int viewIndex)628         protected void onRemoveIcon(int viewIndex) {
629             if (mIsInDemoMode) {
630                 mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex));
631             }
632             mGroup.removeViewAt(viewIndex);
633         }
634 
onSetIcon(int viewIndex, StatusBarIcon icon)635         public void onSetIcon(int viewIndex, StatusBarIcon icon) {
636             StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex);
637             view.set(icon);
638         }
639 
onSetIconHolder(int viewIndex, StatusBarIconHolder holder)640         public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
641             switch (holder.getType()) {
642                 case TYPE_ICON:
643                     onSetIcon(viewIndex, holder.getIcon());
644                     return;
645                 case TYPE_WIFI:
646                     onSetWifiIcon(viewIndex, holder.getWifiState());
647                     return;
648                 case TYPE_MOBILE:
649                     onSetMobileIcon(viewIndex, holder.getMobileState());
650                     return;
651                 case TYPE_MOBILE_NEW:
652                 case TYPE_WIFI_NEW:
653                     // Nothing, the new icons update themselves
654                     return;
655                 default:
656                     break;
657             }
658         }
659 
onSetWifiIcon(int viewIndex, WifiIconState state)660         public void onSetWifiIcon(int viewIndex, WifiIconState state) {
661             View view = mGroup.getChildAt(viewIndex);
662             if (view instanceof StatusBarWifiView) {
663                 ((StatusBarWifiView) view).applyWifiState(state);
664             } else if (view instanceof ModernStatusBarWifiView) {
665                 // ModernStatusBarWifiView will automatically apply state based on its callbacks, so
666                 // we don't need to call applyWifiState.
667             } else {
668                 throw new IllegalStateException("View at " + viewIndex + " must be of type "
669                         + "StatusBarWifiView or ModernStatusBarWifiView");
670             }
671 
672             if (mIsInDemoMode) {
673                 mDemoStatusIcons.updateWifiState(state);
674             }
675         }
676 
onSetMobileIcon(int viewIndex, MobileIconState state)677         public void onSetMobileIcon(int viewIndex, MobileIconState state) {
678             View view = mGroup.getChildAt(viewIndex);
679             if (view instanceof StatusBarMobileView) {
680                 ((StatusBarMobileView) view).applyMobileState(state);
681             } else {
682                 // ModernStatusBarMobileView automatically updates via the ViewModel
683                 throw new IllegalStateException("Cannot update ModernStatusBarMobileView outside of"
684                         + "the new pipeline");
685             }
686 
687             if (mIsInDemoMode) {
688                 Context mobileContext = mMobileContextProvider
689                         .getMobileContextForSub(state.subId, mContext);
690                 mDemoStatusIcons.updateMobileState(state, mobileContext);
691             }
692         }
693 
694         @Override
dispatchDemoCommand(String command, Bundle args)695         public void dispatchDemoCommand(String command, Bundle args) {
696             if (!mDemoable) {
697                 return;
698             }
699 
700             mDemoStatusIcons.dispatchDemoCommand(command, args);
701         }
702 
703         @Override
onDemoModeStarted()704         public void onDemoModeStarted() {
705             mIsInDemoMode = true;
706             if (mDemoStatusIcons == null) {
707                 mDemoStatusIcons = createDemoStatusIcons();
708                 if (mStatusBarPipelineFlags.useNewWifiIcon()) {
709                     mDemoStatusIcons.addModernWifiView(mWifiViewModel);
710                 }
711             }
712             mDemoStatusIcons.onDemoModeStarted();
713         }
714 
715         @Override
onDemoModeFinished()716         public void onDemoModeFinished() {
717             if (mDemoStatusIcons != null) {
718                 mDemoStatusIcons.onDemoModeFinished();
719                 exitDemoMode();
720                 mIsInDemoMode = false;
721             }
722         }
723 
exitDemoMode()724         protected void exitDemoMode() {
725             mDemoStatusIcons.remove();
726             mDemoStatusIcons = null;
727         }
728 
createDemoStatusIcons()729         protected DemoStatusIcons createDemoStatusIcons() {
730             return new DemoStatusIcons(
731                     (LinearLayout) mGroup,
732                     mMobileIconsViewModel,
733                     mLocation,
734                     mIconSize
735             );
736         }
737     }
738 }
739