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