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