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.ambient.statusbar.ui;
18 
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.graphics.drawable.Drawable;
23 import android.util.AttributeSet;
24 import android.view.View;
25 import android.view.ViewGroup;
26 
27 import androidx.constraintlayout.widget.ConstraintLayout;
28 
29 import com.android.systemui.res.R;
30 import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
31 import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo;
32 import com.android.systemui.statusbar.AlphaOptimizedImageView;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Objects;
40 
41 /**
42  * {@link AmbientStatusBarView} is the view responsible for displaying the status bar in a
43  * dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
44  */
45 public class AmbientStatusBarView extends ConstraintLayout {
46 
47     @Retention(RetentionPolicy.SOURCE)
48     @IntDef(prefix = { "STATUS_ICON_" }, value = {
49             STATUS_ICON_NOTIFICATIONS,
50             STATUS_ICON_WIFI_UNAVAILABLE,
51             STATUS_ICON_ALARM_SET,
52             STATUS_ICON_CAMERA_DISABLED,
53             STATUS_ICON_MIC_DISABLED,
54             STATUS_ICON_MIC_CAMERA_DISABLED,
55             STATUS_ICON_PRIORITY_MODE_ON,
56             STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
57     })
58     public @interface StatusIconType {}
59     public static final int STATUS_ICON_NOTIFICATIONS = 0;
60     public static final int STATUS_ICON_WIFI_UNAVAILABLE = 1;
61     public static final int STATUS_ICON_ALARM_SET = 2;
62     public static final int STATUS_ICON_CAMERA_DISABLED = 3;
63     public static final int STATUS_ICON_MIC_DISABLED = 4;
64     public static final int STATUS_ICON_MIC_CAMERA_DISABLED = 5;
65     public static final int STATUS_ICON_PRIORITY_MODE_ON = 6;
66     public static final int STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE = 7;
67 
68     private final Map<Integer, View> mStatusIcons = new HashMap<>();
69     private Context mContext;
70     private ViewGroup mSystemStatusViewGroup;
71     private ViewGroup mExtraSystemStatusViewGroup;
72     private ShadowInfo mKeyShadowInfo;
73     private ShadowInfo mAmbientShadowInfo;
74     private int mDrawableSize;
75     private int mDrawableInsetSize;
76     private static final float KEY_SHADOW_ALPHA = 0.35f;
77     private static final float AMBIENT_SHADOW_ALPHA = 0.4f;
78 
AmbientStatusBarView(Context context)79     public AmbientStatusBarView(Context context) {
80         this(context, null);
81     }
82 
AmbientStatusBarView(Context context, AttributeSet attrs)83     public AmbientStatusBarView(Context context, AttributeSet attrs) {
84         this(context, attrs, 0);
85     }
86 
AmbientStatusBarView(Context context, AttributeSet attrs, int defStyleAttr)87     public AmbientStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
88         this(context, attrs, defStyleAttr, 0);
89         mContext = context;
90     }
91 
AmbientStatusBarView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)92     public AmbientStatusBarView(
93             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
94         super(context, attrs, defStyleAttr, defStyleRes);
95     }
96 
97 
98     @Override
onFinishInflate()99     protected void onFinishInflate() {
100         super.onFinishInflate();
101 
102         mKeyShadowInfo = createShadowInfo(
103             R.dimen.dream_overlay_status_bar_key_text_shadow_radius,
104             R.dimen.dream_overlay_status_bar_key_text_shadow_dx,
105             R.dimen.dream_overlay_status_bar_key_text_shadow_dy,
106             KEY_SHADOW_ALPHA
107         );
108 
109         mAmbientShadowInfo = createShadowInfo(
110             R.dimen.dream_overlay_status_bar_ambient_text_shadow_radius,
111             R.dimen.dream_overlay_status_bar_ambient_text_shadow_dx,
112             R.dimen.dream_overlay_status_bar_ambient_text_shadow_dy,
113             AMBIENT_SHADOW_ALPHA
114         );
115 
116         mDrawableSize = mContext
117                         .getResources()
118                         .getDimensionPixelSize(R.dimen.dream_overlay_status_bar_icon_size);
119         mDrawableInsetSize = mContext
120                              .getResources()
121                              .getDimensionPixelSize(R.dimen.dream_overlay_icon_inset_dimen);
122 
123         mStatusIcons.put(STATUS_ICON_WIFI_UNAVAILABLE,
124                 addDoubleShadow(fetchStatusIconForResId(R.id.dream_overlay_wifi_status)));
125         mStatusIcons.put(STATUS_ICON_ALARM_SET,
126                 addDoubleShadow(fetchStatusIconForResId(R.id.dream_overlay_alarm_set)));
127         mStatusIcons.put(STATUS_ICON_CAMERA_DISABLED,
128                 fetchStatusIconForResId(R.id.dream_overlay_camera_off));
129         mStatusIcons.put(STATUS_ICON_MIC_DISABLED,
130                 fetchStatusIconForResId(R.id.dream_overlay_mic_off));
131         mStatusIcons.put(STATUS_ICON_MIC_CAMERA_DISABLED,
132                 fetchStatusIconForResId(R.id.dream_overlay_camera_mic_off));
133         mStatusIcons.put(STATUS_ICON_NOTIFICATIONS,
134                 fetchStatusIconForResId(R.id.dream_overlay_notification_indicator));
135         mStatusIcons.put(STATUS_ICON_PRIORITY_MODE_ON,
136                 addDoubleShadow(fetchStatusIconForResId(R.id.dream_overlay_priority_mode)));
137         mStatusIcons.put(STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
138                 fetchStatusIconForResId(R.id.dream_overlay_assistant_attention_indicator));
139 
140         mSystemStatusViewGroup = findViewById(R.id.dream_overlay_system_status);
141         mExtraSystemStatusViewGroup = findViewById(R.id.dream_overlay_extra_items);
142     }
143 
getLoggableStatusIconType(@tatusIconType int type)144     protected static String getLoggableStatusIconType(@StatusIconType int type) {
145         return switch (type) {
146             case STATUS_ICON_NOTIFICATIONS -> "notifications";
147             case STATUS_ICON_WIFI_UNAVAILABLE -> "wifi_unavailable";
148             case STATUS_ICON_ALARM_SET -> "alarm_set";
149             case STATUS_ICON_CAMERA_DISABLED -> "camera_disabled";
150             case STATUS_ICON_MIC_DISABLED -> "mic_disabled";
151             case STATUS_ICON_MIC_CAMERA_DISABLED -> "mic_camera_disabled";
152             case STATUS_ICON_PRIORITY_MODE_ON -> "priority_mode_on";
153             case STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE -> "assistant_attention_active";
154             default -> type + "(unknown)";
155         };
156     }
157 
showIcon(@tatusIconType int iconType, boolean show, @Nullable String contentDescription)158     void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) {
159         View icon = mStatusIcons.get(iconType);
160         if (icon == null) {
161             return;
162         }
163         if (show && contentDescription != null) {
164             icon.setContentDescription(contentDescription);
165         }
166         icon.setVisibility(show ? View.VISIBLE : View.GONE);
167         mSystemStatusViewGroup.setVisibility(areAnyStatusIconsVisible() ? View.VISIBLE : View.GONE);
168     }
169 
setExtraStatusBarItemViews(List<View> views)170     void setExtraStatusBarItemViews(List<View> views) {
171         removeAllExtraStatusBarItemViews();
172         views.forEach(view -> mExtraSystemStatusViewGroup.addView(view));
173     }
174 
fetchStatusIconForResId(int resId)175     private View fetchStatusIconForResId(int resId) {
176         final View statusIcon = findViewById(resId);
177         return Objects.requireNonNull(statusIcon);
178     }
179 
removeAllExtraStatusBarItemViews()180     void removeAllExtraStatusBarItemViews() {
181         mExtraSystemStatusViewGroup.removeAllViews();
182     }
183 
areAnyStatusIconsVisible()184     private boolean areAnyStatusIconsVisible() {
185         for (int i = 0; i < mSystemStatusViewGroup.getChildCount(); i++) {
186             if (mSystemStatusViewGroup.getChildAt(i).getVisibility() == View.VISIBLE) {
187                 return true;
188             }
189         }
190         return false;
191     }
192 
addDoubleShadow(View icon)193     private View addDoubleShadow(View icon) {
194         if (icon instanceof AlphaOptimizedImageView) {
195             AlphaOptimizedImageView i = (AlphaOptimizedImageView) icon;
196             Drawable drawableIcon = i.getDrawable();
197             i.setImageDrawable(new DoubleShadowIconDrawable(
198                     mKeyShadowInfo,
199                     mAmbientShadowInfo,
200                     drawableIcon,
201                     mDrawableSize,
202                     mDrawableInsetSize
203             ));
204         }
205         return icon;
206     }
207 
createShadowInfo(int blurId, int offsetXId, int offsetYId, float alpha)208     private ShadowInfo createShadowInfo(int blurId, int offsetXId, int offsetYId, float alpha) {
209         return new ShadowInfo(
210             fetchDimensionForResId(blurId),
211             fetchDimensionForResId(offsetXId),
212             fetchDimensionForResId(offsetYId),
213             alpha
214         );
215     }
216 
fetchDimensionForResId(int resId)217     private Float fetchDimensionForResId(int resId) {
218         return mContext
219                .getResources()
220                .getDimension(resId);
221     }
222 }
223