• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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;
18 
19 import android.content.Context;
20 import android.graphics.Rect;
21 import android.graphics.drawable.Icon;
22 import android.os.Bundle;
23 import android.os.UserHandle;
24 import android.util.Log;
25 import android.view.Gravity;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.widget.LinearLayout;
29 
30 import androidx.collection.MutableIntObjectMap;
31 
32 import com.android.internal.statusbar.StatusBarIcon;
33 import com.android.systemui.Flags;
34 import com.android.systemui.demomode.DemoMode;
35 import com.android.systemui.kairos.ExperimentalKairosApi;
36 import com.android.systemui.kairos.KairosNetwork;
37 import com.android.systemui.plugins.DarkIconDispatcher;
38 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
39 import com.android.systemui.res.R;
40 import com.android.systemui.statusbar.StatusBarIconView;
41 import com.android.systemui.statusbar.StatusIconDisplayable;
42 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
43 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
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.view.ModernStatusBarWifiView;
48 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel;
49 
50 import dagger.Lazy;
51 
52 import kotlin.OptIn;
53 import kotlin.Pair;
54 
55 import kotlinx.coroutines.CoroutineScope;
56 import kotlinx.coroutines.Job;
57 
58 import java.util.ArrayList;
59 import java.util.List;
60 import java.util.concurrent.CancellationException;
61 
62 //TODO: This should be a controller, not its own view
63 @OptIn(markerClass = ExperimentalKairosApi.class)
64 public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver {
65     private static final String TAG = "DemoStatusIcons";
66 
67     private final LinearLayout mStatusIcons;
68     private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>();
69     private final int mIconSize;
70 
71     private ModernStatusBarWifiView mModernWifiView;
72     private boolean mDemoMode;
73     private int mColor;
74     private int mContrastColor;
75 
76     private final MobileIconsViewModel mMobileIconsViewModel;
77     private final StatusBarLocation mLocation;
78 
79     private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos;
80     private final KairosNetwork mKairosNetwork;
81     private final CoroutineScope mAppScope;
82 
83     private final MutableIntObjectMap<Job> mBindingJobs = new MutableIntObjectMap<>();
84 
DemoStatusIcons( LinearLayout statusIcons, MobileIconsViewModel mobileIconsViewModel, StatusBarLocation location, int iconSize, Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos, KairosNetwork kairosNetwork, CoroutineScope appScope )85     public DemoStatusIcons(
86             LinearLayout statusIcons,
87             MobileIconsViewModel mobileIconsViewModel,
88             StatusBarLocation location,
89             int iconSize,
90             Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos,
91             KairosNetwork kairosNetwork,
92             CoroutineScope appScope
93     ) {
94         super(statusIcons.getContext());
95         mStatusIcons = statusIcons;
96         mIconSize = iconSize;
97         mMobileUiAdapterKairos = mobileUiAdapterKairos;
98         mKairosNetwork = kairosNetwork;
99         mAppScope = appScope;
100         mColor = DarkIconDispatcher.DEFAULT_ICON_TINT;
101         mContrastColor = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT;
102         mMobileIconsViewModel = mobileIconsViewModel;
103         mLocation = location;
104 
105         if (statusIcons instanceof StatusIconContainer) {
106             setShouldRestrictIcons(((StatusIconContainer) statusIcons).isRestrictingIcons());
107         } else {
108             setShouldRestrictIcons(false);
109         }
110         setLayoutParams(mStatusIcons.getLayoutParams());
111         setPadding(mStatusIcons.getPaddingLeft(), mStatusIcons.getPaddingTop(),
112                 mStatusIcons.getPaddingRight(), mStatusIcons.getPaddingBottom());
113         setOrientation(mStatusIcons.getOrientation());
114         setGravity(Gravity.CENTER_VERTICAL); // no LL.getGravity()
115         ViewGroup p = (ViewGroup) mStatusIcons.getParent();
116         p.addView(this, p.indexOfChild(mStatusIcons));
117     }
118 
remove()119     public void remove() {
120         ((ViewGroup) getParent()).removeView(this);
121     }
122 
123     /** Set the tint colors */
setColor(int color, int contrastColor)124     public void setColor(int color, int contrastColor) {
125         mColor = color;
126         mContrastColor = contrastColor;
127         updateColors();
128     }
129 
updateColors()130     private void updateColors() {
131         for (int i = 0; i < getChildCount(); i++) {
132             StatusIconDisplayable child = (StatusIconDisplayable) getChildAt(i);
133             child.setStaticDrawableColor(mColor, mContrastColor);
134             child.setDecorColor(mColor);
135         }
136     }
137 
138     @Override
demoCommands()139     public List<String> demoCommands() {
140         List<String> commands = new ArrayList<>();
141         commands.add(COMMAND_STATUS);
142         return commands;
143     }
144 
145     @Override
onDemoModeStarted()146     public void onDemoModeStarted() {
147         mDemoMode = true;
148         mStatusIcons.setVisibility(View.GONE);
149         setVisibility(View.VISIBLE);
150     }
151 
152     @Override
onDemoModeFinished()153     public void onDemoModeFinished() {
154         mDemoMode = false;
155         mStatusIcons.setVisibility(View.VISIBLE);
156         mModernMobileViews.clear();
157         setVisibility(View.GONE);
158     }
159 
160     @Override
dispatchDemoCommand(String command, Bundle args)161     public void dispatchDemoCommand(String command, Bundle args) {
162         String volume = args.getString("volume");
163         if (volume != null) {
164             int iconId = volume.equals("vibrate") ? R.drawable.stat_sys_ringer_vibrate
165                     : 0;
166             updateSlot("volume", null, iconId);
167         }
168         String zen = args.getString("zen");
169         if (zen != null) {
170             int iconId = zen.equals("dnd") ? R.drawable.stat_sys_dnd : 0;
171             updateSlot("zen", null, iconId);
172         }
173         String bt = args.getString("bluetooth");
174         if (bt != null) {
175             int iconId = bt.equals("connected")
176                     ? R.drawable.stat_sys_data_bluetooth_connected : 0;
177             updateSlot("bluetooth", null, iconId);
178         }
179         String location = args.getString("location");
180         if (location != null) {
181             int iconId = location.equals("show") ? PhoneStatusBarPolicy.LOCATION_STATUS_ICON_ID
182                     : 0;
183             updateSlot("location", null, iconId);
184         }
185         String alarm = args.getString("alarm");
186         if (alarm != null) {
187             int iconId = alarm.equals("show") ? R.drawable.stat_sys_alarm
188                     : 0;
189             updateSlot("alarm_clock", null, iconId);
190         }
191         String tty = args.getString("tty");
192         if (tty != null) {
193             int iconId = tty.equals("show") ? R.drawable.stat_sys_tty_mode
194                     : 0;
195             updateSlot("tty", null, iconId);
196         }
197         String mute = args.getString("mute");
198         if (mute != null) {
199             int iconId = mute.equals("show") ? android.R.drawable.stat_notify_call_mute
200                     : 0;
201             updateSlot("mute", null, iconId);
202         }
203         String speakerphone = args.getString("speakerphone");
204         if (speakerphone != null) {
205             int iconId = speakerphone.equals("show") ? android.R.drawable.stat_sys_speakerphone
206                     : 0;
207             updateSlot("speakerphone", null, iconId);
208         }
209         String cast = args.getString("cast");
210         if (cast != null) {
211             int iconId = cast.equals("show") ? R.drawable.stat_sys_cast : 0;
212             updateSlot("cast", null, iconId);
213         }
214         String hotspot = args.getString("hotspot");
215         if (hotspot != null) {
216             int iconId = hotspot.equals("show") ? R.drawable.stat_sys_hotspot : 0;
217             updateSlot("hotspot", null, iconId);
218         }
219     }
220 
221     /// Can only be used to update non-signal related slots
updateSlot(String slot, String iconPkg, int iconId)222     private void updateSlot(String slot, String iconPkg, int iconId) {
223         if (!mDemoMode) return;
224         if (iconPkg == null) {
225             iconPkg = mContext.getPackageName();
226         }
227         int removeIndex = -1;
228         for (int i = 0; i < getChildCount(); i++) {
229             View child = getChildAt(i);
230             if (!(child instanceof StatusBarIconView)) {
231                 continue;
232             }
233             StatusBarIconView v = (StatusBarIconView) child;
234             if (slot.equals(v.getTag())) {
235                 if (iconId == 0) {
236                     removeIndex = i;
237                     break;
238                 } else {
239                     StatusBarIcon icon = v.getStatusBarIcon();
240                     icon.visible = true;
241                     icon.icon = Icon.createWithResource(icon.icon.getResPackage(), iconId);
242                     v.set(icon);
243                     v.updateDrawable();
244                     return;
245                 }
246             }
247         }
248         if (iconId == 0) {
249             if (removeIndex != -1) {
250                 removeViewAt(removeIndex);
251             }
252             return;
253         }
254         StatusBarIcon icon = new StatusBarIcon(iconPkg, UserHandle.SYSTEM, iconId, 0, 0, "Demo",
255                 StatusBarIcon.Type.SystemIcon);
256         icon.visible = true;
257         StatusBarIconView v = new StatusBarIconView(getContext(), slot, null, false);
258         v.setTag(slot);
259         v.set(icon);
260         v.setStaticDrawableColor(mColor, mContrastColor);
261         v.setDecorColor(mColor);
262         addView(v, 0, createLayoutParams());
263     }
264 
265     /**
266      * Add a {@link ModernStatusBarMobileView}
267      *
268      * @param mobileContext possibly mcc/mnc overridden mobile context
269      * @param subId         the subscriptionId for this mobile view
270      */
addModernMobileView( Context mobileContext, MobileViewLogger mobileViewLogger, int subId)271     public void addModernMobileView(
272             Context mobileContext,
273             MobileViewLogger mobileViewLogger,
274             int subId) {
275         Log.d(TAG, "addModernMobileView (subId=" + subId + ")");
276         if (Flags.statusBarMobileIconKairos()) {
277             Pair<ModernStatusBarMobileView, Job> viewAndJob =
278                     ModernStatusBarMobileView.constructAndBind(
279                             mobileContext,
280                             mobileViewLogger,
281                             "mobile",
282                             mMobileUiAdapterKairos.get().getMobileIconsViewModel().viewModelForSub(
283                                     subId, mLocation),
284                             mAppScope,
285                             subId,
286                             mLocation,
287                             mKairosNetwork
288                     );
289             ModernStatusBarMobileView view = viewAndJob.getFirst();
290             mBindingJobs.put(subId, viewAndJob.getSecond());
291             // mobile always goes at the end
292             mModernMobileViews.add(view);
293             addView(view, getChildCount(), createLayoutParams());
294         } else {
295             ModernStatusBarMobileView view =
296                     ModernStatusBarMobileView.constructAndBind(
297                             mobileContext,
298                             mobileViewLogger,
299                             "mobile",
300                             mMobileIconsViewModel.viewModelForSub(subId, mLocation)
301                     );
302 
303             // mobile always goes at the end
304             mModernMobileViews.add(view);
305             addView(view, getChildCount(), createLayoutParams());
306         }
307     }
308 
309     /**
310      * Add a {@link ModernStatusBarWifiView}
311      */
addModernWifiView(LocationBasedWifiViewModel viewModel)312     public void addModernWifiView(LocationBasedWifiViewModel viewModel) {
313         Log.d(TAG, "addModernDemoWifiView: ");
314         ModernStatusBarWifiView view = ModernStatusBarWifiView
315                 .constructAndBind(mContext, "wifi", viewModel);
316 
317         int viewIndex = getChildCount();
318         // If we have mobile views, put wifi before them
319         for (int i = 0; i < getChildCount(); i++) {
320             View child = getChildAt(i);
321             if (child instanceof ModernStatusBarMobileView) {
322                 viewIndex = i;
323                 break;
324             }
325         }
326 
327         mModernWifiView = view;
328         mModernWifiView.setStaticDrawableColor(mColor, mContrastColor);
329         addView(view, viewIndex, createLayoutParams());
330     }
331 
332     /** Adds a bindable icon to the demo mode view. */
addBindableIcon(StatusBarIconHolder.BindableIconHolder holder)333     public void addBindableIcon(StatusBarIconHolder.BindableIconHolder holder) {
334         // This doesn't do any correct ordering, and also doesn't check if we already have an
335         // existing icon for the slot. But since we hope to remove this class soon, we won't spend
336         // the time adding that logic.
337         ModernStatusBarView view = holder.getInitializer().createAndBind(mContext);
338         addView(view, createLayoutParams());
339     }
340 
onRemoveIcon(StatusIconDisplayable view)341     public void onRemoveIcon(StatusIconDisplayable view) {
342         if (view.getSlot().equals("wifi")) {
343             if (view instanceof ModernStatusBarWifiView) {
344                 Log.d(TAG, "onRemoveIcon: removing modern wifi view");
345                 removeView(mModernWifiView);
346                 mModernWifiView = null;
347             }
348         } else if (view instanceof ModernStatusBarMobileView) {
349             ModernStatusBarMobileView mobileView = matchingModernMobileView(
350                     (ModernStatusBarMobileView) view);
351             if (mobileView != null) {
352                 if (Flags.statusBarMobileIconKairos()) {
353                     Job job = mBindingJobs.remove(mobileView.getSubId());
354                     if (job != null) {
355                         job.cancel(new CancellationException());
356                     }
357                 }
358                 removeView(mobileView);
359                 mModernMobileViews.remove(mobileView);
360             }
361         }
362     }
363 
matchingModernMobileView(ModernStatusBarMobileView other)364     private ModernStatusBarMobileView matchingModernMobileView(ModernStatusBarMobileView other) {
365         for (ModernStatusBarMobileView v : mModernMobileViews) {
366             if (v.getSubId() == other.getSubId()) {
367                 return v;
368             }
369         }
370 
371         return null;
372     }
373 
createLayoutParams()374     private LayoutParams createLayoutParams() {
375         return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
376     }
377 
378     @Override
onDarkChangedWithContrast(ArrayList<Rect> areas, int tint, int contrastTint)379     public void onDarkChangedWithContrast(ArrayList<Rect> areas, int tint, int contrastTint) {
380         setColor(tint, contrastTint);
381 
382         if (mModernWifiView != null) {
383             mModernWifiView.onDarkChangedWithContrast(areas, tint, contrastTint);
384         }
385 
386         for (ModernStatusBarMobileView view : mModernMobileViews) {
387             view.onDarkChangedWithContrast(areas, tint, contrastTint);
388         }
389     }
390 
391     @Override
onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint)392     public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
393         // not needed
394     }
395 }
396