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