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 android.annotation.ColorInt;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.content.res.ColorStateList;
23 import android.graphics.Rect;
24 import android.graphics.drawable.Drawable;
25 import android.graphics.drawable.LayerDrawable;
26 import android.util.AttributeSet;
27 import android.view.RemotableViewMethod;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.accessibility.AccessibilityNodeInfo;
31 import android.widget.Button;
32 import android.widget.FrameLayout;
33 import android.widget.ImageView;
34 import android.widget.RemoteViews;
35 import android.widget.TextView;
36 
37 import com.android.internal.R;
38 
39 import java.util.Locale;
40 
41 /**
42  * An expand button in a notification
43  */
44 @RemoteViews.RemoteView
45 public class NotificationExpandButton extends FrameLayout {
46 
47     private Drawable mPillDrawable;
48     private TextView mNumberView;
49     private ImageView mIconView;
50     private boolean mExpanded;
51     private int mNumber;
52     private int mDefaultPillColor;
53     private int mDefaultTextColor;
54     private int mHighlightPillColor;
55     private int mHighlightTextColor;
56 
NotificationExpandButton(Context context)57     public NotificationExpandButton(Context context) {
58         this(context, null, 0, 0);
59     }
60 
NotificationExpandButton(Context context, @Nullable AttributeSet attrs)61     public NotificationExpandButton(Context context, @Nullable AttributeSet attrs) {
62         this(context, attrs, 0, 0);
63     }
64 
NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr)65     public NotificationExpandButton(Context context, @Nullable AttributeSet attrs,
66             int defStyleAttr) {
67         this(context, attrs, defStyleAttr, 0);
68     }
69 
NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)70     public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
71             int defStyleRes) {
72         super(context, attrs, defStyleAttr, defStyleRes);
73     }
74 
75     @Override
onFinishInflate()76     protected void onFinishInflate() {
77         super.onFinishInflate();
78 
79         final View pillView = findViewById(R.id.expand_button_pill);
80         final LayerDrawable layeredPill = (LayerDrawable) pillView.getBackground();
81         mPillDrawable = layeredPill.findDrawableByLayerId(R.id.expand_button_pill_colorized_layer);
82         mNumberView = findViewById(R.id.expand_button_number);
83         mIconView = findViewById(R.id.expand_button_icon);
84     }
85 
86     /**
87      * Show the touchable area of the view for a11y.
88      * If the parent is the touch container, then that view's bounds are the touchable area.
89      */
90     @Override
getBoundsOnScreen(Rect outRect, boolean clipToParent)91     public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
92         ViewGroup parent = (ViewGroup) getParent();
93         if (parent != null && parent.getId() == R.id.expand_button_touch_container) {
94             parent.getBoundsOnScreen(outRect, clipToParent);
95         } else {
96             super.getBoundsOnScreen(outRect, clipToParent);
97         }
98     }
99 
100     /**
101      * Determined if the given point should be touchable.
102      * If the parent is the touch container, then any point in that view should be touchable.
103      */
104     @Override
pointInView(float localX, float localY, float slop)105     public boolean pointInView(float localX, float localY, float slop) {
106         ViewGroup parent = (ViewGroup) getParent();
107         if (parent != null && parent.getId() == R.id.expand_button_touch_container) {
108             // If our parent is checking with us, then the point must be within its bounds.
109             return true;
110         }
111         return super.pointInView(localX, localY, slop);
112     }
113 
114     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)115     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
116         super.onInitializeAccessibilityNodeInfo(info);
117         info.setClassName(Button.class.getName());
118     }
119 
120     /**
121      * Update the button's drawable, content description, and color for the given expanded state.
122      */
123     @RemotableViewMethod
setExpanded(boolean expanded)124     public void setExpanded(boolean expanded) {
125         mExpanded = expanded;
126         updateExpandedState();
127     }
128 
updateExpandedState()129     private void updateExpandedState() {
130         int drawableId;
131         int contentDescriptionId;
132         if (mExpanded) {
133             drawableId = R.drawable.ic_collapse_notification;
134             contentDescriptionId = R.string.expand_button_content_description_expanded;
135         } else {
136             drawableId = R.drawable.ic_expand_notification;
137             contentDescriptionId = R.string.expand_button_content_description_collapsed;
138         }
139         setContentDescription(mContext.getText(contentDescriptionId));
140         mIconView.setImageDrawable(getContext().getDrawable(drawableId));
141 
142         // changing the expanded state can affect the number display
143         updateNumber();
144     }
145 
updateNumber()146     private void updateNumber() {
147         if (shouldShowNumber()) {
148             CharSequence text = mNumber >= 100
149                     ? getResources().getString(R.string.unread_convo_overflow, 99)
150                     : String.format(Locale.getDefault(), "%d", mNumber);
151             mNumberView.setText(text);
152             mNumberView.setVisibility(VISIBLE);
153         } else {
154             mNumberView.setVisibility(GONE);
155         }
156 
157         // changing number can affect the color
158         updateColors();
159     }
160 
updateColors()161     private void updateColors() {
162         if (shouldShowNumber()) {
163             if (mHighlightPillColor != 0) {
164                 mPillDrawable.setTintList(ColorStateList.valueOf(mHighlightPillColor));
165             }
166             mIconView.setColorFilter(mHighlightTextColor);
167             if (mHighlightTextColor != 0) {
168                 mNumberView.setTextColor(mHighlightTextColor);
169             }
170         } else {
171             if (mDefaultPillColor != 0) {
172                 mPillDrawable.setTintList(ColorStateList.valueOf(mDefaultPillColor));
173             }
174             mIconView.setColorFilter(mDefaultTextColor);
175             if (mDefaultTextColor != 0) {
176                 mNumberView.setTextColor(mDefaultTextColor);
177             }
178         }
179     }
180 
shouldShowNumber()181     private boolean shouldShowNumber() {
182         return !mExpanded && mNumber > 1;
183     }
184 
185     /**
186      * Set the color used for the expand chevron and the text
187      */
188     @RemotableViewMethod
setDefaultTextColor(int color)189     public void setDefaultTextColor(int color) {
190         mDefaultTextColor = color;
191         updateColors();
192     }
193 
194     /**
195      * Sets the color used to for the expander when there is no number shown
196      */
197     @RemotableViewMethod
setDefaultPillColor(@olorInt int color)198     public void setDefaultPillColor(@ColorInt int color) {
199         mDefaultPillColor = color;
200         updateColors();
201     }
202 
203     /**
204      * Set the color used for the expand chevron and the text
205      */
206     @RemotableViewMethod
setHighlightTextColor(int color)207     public void setHighlightTextColor(int color) {
208         mHighlightTextColor = color;
209         updateColors();
210     }
211 
212     /**
213      * Sets the color used to highlight the expander when there is a number shown
214      */
215     @RemotableViewMethod
setHighlightPillColor(@olorInt int color)216     public void setHighlightPillColor(@ColorInt int color) {
217         mHighlightPillColor = color;
218         updateColors();
219     }
220 
221     /**
222      * Sets the number shown inside the expand button.
223      * This only appears when the expand button is collapsed, and when greater than 1.
224      */
225     @RemotableViewMethod
setNumber(int number)226     public void setNumber(int number) {
227         if (mNumber != number) {
228             mNumber = number;
229             updateNumber();
230         }
231     }
232 }
233