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.statusbar.notification.emptyshade.ui.view; 18 19 import android.annotation.ColorInt; 20 import android.annotation.DrawableRes; 21 import android.annotation.StringRes; 22 import android.content.Context; 23 import android.content.res.ColorStateList; 24 import android.content.res.Configuration; 25 import android.graphics.Rect; 26 import android.graphics.drawable.Drawable; 27 import android.util.AttributeSet; 28 import android.util.Log; 29 import android.view.View; 30 import android.widget.TextView; 31 32 import androidx.annotation.NonNull; 33 34 import com.android.systemui.animation.LaunchableView; 35 import com.android.systemui.animation.LaunchableViewDelegate; 36 import com.android.systemui.res.R; 37 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix; 38 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; 39 import com.android.systemui.statusbar.notification.stack.ExpandableViewState; 40 41 import kotlin.Unit; 42 43 import java.util.Objects; 44 45 public class EmptyShadeView extends StackScrollerDecorView implements LaunchableView { 46 private static final String TAG = "EmptyShadeView"; 47 48 private TextView mEmptyText; 49 private TextView mEmptyFooterText; 50 51 private @StringRes int mTextId = R.string.empty_shade_text; 52 private String mTextString; 53 54 private @DrawableRes int mFooterIcon; 55 private @StringRes int mFooterText; 56 // This view is initially gone in the xml. 57 private @Visibility int mFooterVisibility = View.GONE; 58 private int mSize; 59 60 private final LaunchableViewDelegate mLaunchableViewDelegate = new LaunchableViewDelegate(this, 61 visibility -> { 62 super.setVisibility(visibility); 63 return Unit.INSTANCE; 64 }); 65 EmptyShadeView(Context context, AttributeSet attrs)66 public EmptyShadeView(Context context, AttributeSet attrs) { 67 super(context, attrs); 68 mSize = getResources().getDimensionPixelSize( 69 R.dimen.notifications_unseen_footer_icon_size); 70 if (ModesEmptyShadeFix.isEnabled()) { 71 mTextString = getContext().getString(R.string.empty_shade_text); 72 } else { 73 // These will be set by the binder when appropriate if ModesEmptyShadeFix is on. 74 mFooterIcon = R.drawable.ic_friction_lock_closed; 75 mFooterText = R.string.unlock_to_see_notif_text; 76 } 77 } 78 79 @Override setVisibility(int visibility)80 public void setVisibility(int visibility) { 81 mLaunchableViewDelegate.setVisibility(visibility); 82 } 83 84 @Override setShouldBlockVisibilityChanges(boolean block)85 public void setShouldBlockVisibilityChanges(boolean block) { 86 /* check if */ ModesEmptyShadeFix.isUnexpectedlyInLegacyMode(); 87 mLaunchableViewDelegate.setShouldBlockVisibilityChanges(block); 88 } 89 90 @Override onActivityLaunchAnimationEnd()91 public void onActivityLaunchAnimationEnd() { 92 /* check if */ ModesEmptyShadeFix.isUnexpectedlyInLegacyMode(); 93 } 94 95 @Override 96 @NonNull getPaddingForLaunchAnimation()97 public Rect getPaddingForLaunchAnimation() { 98 /* check if */ ModesEmptyShadeFix.isUnexpectedlyInLegacyMode(); 99 return new Rect(); 100 } 101 102 @Override onConfigurationChanged(Configuration newConfig)103 protected void onConfigurationChanged(Configuration newConfig) { 104 super.onConfigurationChanged(newConfig); 105 mSize = getResources().getDimensionPixelSize( 106 R.dimen.notifications_unseen_footer_icon_size); 107 if (ModesEmptyShadeFix.isEnabled()) { 108 mEmptyText.setText(mTextString); 109 } else { 110 mEmptyText.setText(mTextId); 111 } 112 mEmptyFooterText.setVisibility(mFooterVisibility); 113 setFooterText(mFooterText); 114 setFooterIcon(mFooterIcon); 115 } 116 117 @Override findContentView()118 protected View findContentView() { 119 return findViewById(R.id.no_notifications); 120 } 121 122 @Override findSecondaryView()123 protected View findSecondaryView() { 124 return findViewById(R.id.no_notifications_footer); 125 } 126 127 /** Update view colors. */ setTextColors(@olorInt int onSurface, @ColorInt int onSurfaceVariant)128 public void setTextColors(@ColorInt int onSurface, @ColorInt int onSurfaceVariant) { 129 mEmptyText.setTextColor(onSurfaceVariant); 130 mEmptyFooterText.setTextColor(onSurface); 131 mEmptyFooterText.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface)); 132 } 133 134 /** Set the resource ID for the main text shown by the view. */ setText(@tringRes int text)135 public void setText(@StringRes int text) { 136 ModesEmptyShadeFix.assertInLegacyMode(); 137 mTextId = text; 138 mEmptyText.setText(mTextId); 139 } 140 141 /** Set the string for the main text shown by the view. */ setText(String text)142 public void setText(String text) { 143 if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode() || Objects.equals(mTextString, text)) { 144 return; 145 } 146 mTextString = text; 147 mEmptyText.setText(text); 148 } 149 150 /** Visibility for the footer (the additional icon+text shown below the main text). */ setFooterVisibility(@isibility int visibility)151 public void setFooterVisibility(@Visibility int visibility) { 152 if (ModesEmptyShadeFix.isEnabled() && mFooterVisibility == visibility) { 153 return; // nothing to change 154 } 155 mFooterVisibility = visibility; 156 setSecondaryVisible(/* visible = */ visibility == View.VISIBLE, 157 /* animate = */false, 158 /* onAnimationEnded = */ null); 159 } 160 161 /** Text resource ID for the footer (the additional icon+text shown below the main text). */ setFooterText(@tringRes int text)162 public void setFooterText(@StringRes int text) { 163 if (ModesEmptyShadeFix.isEnabled() && mFooterText == text) { 164 return; // nothing to change 165 } 166 mFooterText = text; 167 if (text != 0) { 168 mEmptyFooterText.setText(mFooterText); 169 } else { 170 mEmptyFooterText.setText(null); 171 } 172 } 173 174 /** Icon resource ID for the footer (the additional icon+text shown below the main text). */ setFooterIcon(@rawableRes int icon)175 public void setFooterIcon(@DrawableRes int icon) { 176 if (ModesEmptyShadeFix.isEnabled() && mFooterIcon == icon) { 177 return; // nothing to change 178 } 179 mFooterIcon = icon; 180 Drawable drawable; 181 if (icon == 0) { 182 drawable = null; 183 } else { 184 drawable = getContext().getDrawable(icon); 185 if (drawable != null) { 186 drawable.setBounds(0, 0, mSize, mSize); 187 } else { 188 Log.w(TAG, "Invalid footer icon resource ID"); 189 } 190 } 191 mEmptyFooterText.setCompoundDrawablesRelative(drawable, null, null, null); 192 } 193 194 /** Get resource ID for main text. */ 195 @StringRes getTextResource()196 public int getTextResource() { 197 ModesEmptyShadeFix.assertInLegacyMode(); 198 return mTextId; 199 } 200 201 /** Get resource ID for footer text. */ 202 @StringRes getFooterTextResource()203 public int getFooterTextResource() { 204 ModesEmptyShadeFix.assertInLegacyMode(); 205 return mFooterText; 206 } 207 208 /** Get resource ID for footer icon. */ 209 @DrawableRes getFooterIconResource()210 public int getFooterIconResource() { 211 ModesEmptyShadeFix.assertInLegacyMode(); 212 return mFooterIcon; 213 } 214 215 @Override onFinishInflate()216 protected void onFinishInflate() { 217 super.onFinishInflate(); 218 mEmptyText = (TextView) findContentView(); 219 mEmptyFooterText = (TextView) findSecondaryView(); 220 mEmptyFooterText.setCompoundDrawableTintList(mEmptyFooterText.getTextColors()); 221 } 222 223 @Override 224 @NonNull createExpandableViewState()225 public ExpandableViewState createExpandableViewState() { 226 return new EmptyShadeViewState(); 227 } 228 229 public class EmptyShadeViewState extends ExpandableViewState { 230 @Override applyToView(View view)231 public void applyToView(View view) { 232 super.applyToView(view); 233 if (view instanceof EmptyShadeView emptyShadeView) { 234 boolean visible = this.clipTopAmount <= mEmptyText.getPaddingTop() * 0.6f; 235 emptyShadeView.setContentVisibleAnimated(visible && emptyShadeView.isVisible()); 236 } 237 } 238 } 239 } 240