• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.internal.widget;
18 
19 import static android.app.Flags.notificationsRedesignTemplates;
20 
21 import android.annotation.ColorInt;
22 import android.annotation.Nullable;
23 import android.content.Context;
24 import android.content.res.ColorStateList;
25 import android.graphics.Rect;
26 import android.graphics.drawable.Drawable;
27 import android.graphics.drawable.LayerDrawable;
28 import android.util.AttributeSet;
29 import android.view.RemotableViewMethod;
30 import android.view.ViewGroup;
31 import android.view.accessibility.AccessibilityNodeInfo;
32 import android.widget.Button;
33 import android.widget.FrameLayout;
34 import android.widget.ImageView;
35 import android.widget.LinearLayout;
36 import android.widget.RemoteViews;
37 import android.widget.TextView;
38 
39 import com.android.internal.R;
40 
41 import java.util.Locale;
42 
43 /**
44  * An expand button in a notification
45  */
46 @RemoteViews.RemoteView
47 public class NotificationExpandButton extends FrameLayout {
48 
49     private Drawable mPillDrawable;
50     private TextView mNumberView;
51     private ImageView mIconView;
52     private LinearLayout mPillView;
53     private boolean mExpanded;
54     private int mNumber;
55     private int mDefaultPillColor;
56     private int mDefaultTextColor;
57     private int mHighlightPillColor;
58     private int mHighlightTextColor;
59 
NotificationExpandButton(Context context)60     public NotificationExpandButton(Context context) {
61         this(context, null, 0, 0);
62     }
63 
NotificationExpandButton(Context context, @Nullable AttributeSet attrs)64     public NotificationExpandButton(Context context, @Nullable AttributeSet attrs) {
65         this(context, attrs, 0, 0);
66     }
67 
NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr)68     public NotificationExpandButton(Context context, @Nullable AttributeSet attrs,
69             int defStyleAttr) {
70         this(context, attrs, defStyleAttr, 0);
71     }
72 
NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)73     public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
74             int defStyleRes) {
75         super(context, attrs, defStyleAttr, defStyleRes);
76     }
77 
78     @Override
onFinishInflate()79     protected void onFinishInflate() {
80         super.onFinishInflate();
81 
82         mPillView = findViewById(R.id.expand_button_pill);
83         final LayerDrawable layeredPill = (LayerDrawable) mPillView.getBackground();
84         mPillDrawable = layeredPill.findDrawableByLayerId(R.id.expand_button_pill_colorized_layer);
85         mNumberView = findViewById(R.id.expand_button_number);
86         mIconView = findViewById(R.id.expand_button_icon);
87     }
88 
89     /**
90      * Show the touchable area of the view for a11y.
91      * If the parent is the touch container, then that view's bounds are the touchable area.
92      */
93     @Override
getBoundsOnScreen(Rect outRect, boolean clipToParent)94     public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
95         ViewGroup parent = (ViewGroup) getParent();
96         if (parent != null && parent.getId() == R.id.expand_button_touch_container) {
97             parent.getBoundsOnScreen(outRect, clipToParent);
98         } else {
99             super.getBoundsOnScreen(outRect, clipToParent);
100         }
101     }
102 
103     /**
104      * Determined if the given point should be touchable.
105      * If the parent is the touch container, then any point in that view should be touchable.
106      */
107     @Override
pointInView(float localX, float localY, float slop)108     public boolean pointInView(float localX, float localY, float slop) {
109         ViewGroup parent = (ViewGroup) getParent();
110         if (parent != null && parent.getId() == R.id.expand_button_touch_container) {
111             // If our parent is checking with us, then the point must be within its bounds.
112             return true;
113         }
114         return super.pointInView(localX, localY, slop);
115     }
116 
117     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)118     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
119         super.onInitializeAccessibilityNodeInfo(info);
120         info.setClassName(Button.class.getName());
121     }
122 
123     /**
124      * Update the button's drawable, content description, and color for the given expanded state.
125      */
126     @RemotableViewMethod
setExpanded(boolean expanded)127     public void setExpanded(boolean expanded) {
128         mExpanded = expanded;
129         updateExpandedState();
130     }
131 
132     /**
133      * Adjust the padding at the start of the view based on the layout direction (RTL/LTR).
134      * This is needed because RemoteViews don't have an equivalent for
135      * {@link this#setPaddingRelative}.
136      */
137     @RemotableViewMethod
setStartPadding(int startPadding)138     public void setStartPadding(int startPadding) {
139         setPaddingRelative(startPadding, getPaddingTop(), getPaddingEnd(), getPaddingBottom());
140     }
141 
updateExpandedState()142     private void updateExpandedState() {
143         int drawableId;
144         int contentDescriptionId;
145         if (mExpanded) {
146             if (notificationsRedesignTemplates()) {
147                 drawableId = R.drawable.ic_notification_2025_collapse;
148             } else {
149                 drawableId = R.drawable.ic_collapse_notification;
150             }
151             contentDescriptionId = R.string.expand_button_content_description_expanded;
152         } else {
153             if (notificationsRedesignTemplates()) {
154                 drawableId = R.drawable.ic_notification_2025_expand;
155             } else {
156                 drawableId = R.drawable.ic_expand_notification;
157             }
158             contentDescriptionId = R.string.expand_button_content_description_collapsed;
159         }
160         setContentDescription(mContext.getText(contentDescriptionId));
161         mIconView.setImageDrawable(getContext().getDrawable(drawableId));
162 
163         if (!notificationsRedesignTemplates()) {
164             // changing the expanded state can affect the number display
165             updateNumber();
166         }
167     }
168 
updateNumber()169     private void updateNumber() {
170         if (shouldShowNumber()) {
171             CharSequence text = mNumber >= 100
172                     ? getResources().getString(R.string.unread_convo_overflow, 99)
173                     : String.format(Locale.getDefault(), "%d", mNumber);
174             mNumberView.setText(text);
175             mNumberView.setVisibility(VISIBLE);
176         } else {
177             mNumberView.setVisibility(GONE);
178         }
179 
180         // changing number can affect the color and padding
181         updateColors();
182         updatePadding();
183     }
184 
updatePadding()185     private void updatePadding() {
186         if (!notificationsRedesignTemplates()) {
187             return;
188         }
189 
190         // Reduce the padding at the end when showing the number, since the arrow icon has more
191         // inherent spacing than the number does. This makes the content look more centered.
192         // Vertical padding remains unchanged.
193         int reducedPadding = getResources().getDimensionPixelSize(
194                 R.dimen.notification_2025_expand_button_reduced_end_padding);
195         int normalPadding = getResources().getDimensionPixelSize(
196                 R.dimen.notification_2025_expand_button_horizontal_icon_padding);
197         mPillView.setPaddingRelative(
198                 /* start = */ normalPadding,
199                 /* top = */ mPillView.getPaddingTop(),
200                 /* end = */ shouldShowNumber() ? reducedPadding : normalPadding,
201                 /* bottom = */ mPillView.getPaddingBottom()
202         );
203     }
204 
updateColors()205     private void updateColors() {
206         if (shouldShowNumber()) {
207             if (mHighlightPillColor != 0) {
208                 mPillDrawable.setTintList(ColorStateList.valueOf(mHighlightPillColor));
209             }
210             mIconView.setColorFilter(mHighlightTextColor);
211             if (mHighlightTextColor != 0) {
212                 mNumberView.setTextColor(mHighlightTextColor);
213             }
214         } else {
215             if (mDefaultPillColor != 0) {
216                 mPillDrawable.setTintList(ColorStateList.valueOf(mDefaultPillColor));
217             }
218             mIconView.setColorFilter(mDefaultTextColor);
219             if (mDefaultTextColor != 0) {
220                 mNumberView.setTextColor(mDefaultTextColor);
221             }
222         }
223     }
224 
shouldShowNumber()225     private boolean shouldShowNumber() {
226         if (notificationsRedesignTemplates()) {
227             return mNumber > 1;
228         }
229         return !mExpanded && mNumber > 1;
230     }
231 
232     /**
233      * Set the color used for the expand chevron and the text
234      */
235     @RemotableViewMethod
setDefaultTextColor(int color)236     public void setDefaultTextColor(int color) {
237         mDefaultTextColor = color;
238         updateColors();
239     }
240 
241     /**
242      * Sets the color used to for the expander when there is no number shown
243      */
244     @RemotableViewMethod
setDefaultPillColor(@olorInt int color)245     public void setDefaultPillColor(@ColorInt int color) {
246         mDefaultPillColor = color;
247         updateColors();
248     }
249 
250     /**
251      * Set the color used for the expand chevron and the text
252      */
253     @RemotableViewMethod
setHighlightTextColor(int color)254     public void setHighlightTextColor(int color) {
255         mHighlightTextColor = color;
256         updateColors();
257     }
258 
259     /**
260      * Sets the color used to highlight the expander when there is a number shown
261      */
262     @RemotableViewMethod
setHighlightPillColor(@olorInt int color)263     public void setHighlightPillColor(@ColorInt int color) {
264         mHighlightPillColor = color;
265         updateColors();
266     }
267 
268     /**
269      * Sets the number shown inside the expand button.
270      * This only appears when {@link this#shouldShowNumber()} is true.
271      */
272     @RemotableViewMethod
setNumber(int number)273     public void setNumber(int number) {
274         if (mNumber != number) {
275             mNumber = number;
276             updateNumber();
277         }
278     }
279 }
280