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