blob: cff9d8e93c98a8b1efe4c77604a3c5c86bc6c3a1 [file] [log] [blame]
Selim Cinek90c8f472015-11-10 17:44:39 -05001/*
2 * Copyright (C) 2015 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
17package android.view;
18
19import android.annotation.Nullable;
20import android.content.Context;
Selim Cinekaef6c762015-11-20 17:00:18 -080021import android.graphics.Rect;
Selim Cinek90c8f472015-11-10 17:44:39 -050022import android.util.AttributeSet;
Selim Cinek7b836392015-12-04 20:02:59 -080023import android.widget.ImageView;
Selim Cinek90c8f472015-11-10 17:44:39 -050024import android.widget.RemoteViews;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080025import android.widget.TextView;
Selim Cinek90c8f472015-11-10 17:44:39 -050026
Selim Cinekaef6c762015-11-20 17:00:18 -080027import java.util.ArrayList;
Selim Cinek90c8f472015-11-10 17:44:39 -050028
29/**
30 * A header of a notification view
31 *
32 * @hide
33 */
34@RemoteViews.RemoteView
Selim Cinekcb445682016-01-29 16:13:12 -080035public class NotificationHeaderView extends ViewGroup {
Selim Cinekea4bef72015-12-02 15:51:10 -080036 public static final int NO_COLOR = -1;
Selim Cinek413142a2016-02-03 10:58:13 -080037 private final int mChildMinWidth;
Selim Cinek6ecc8102016-01-26 18:26:19 -080038 private final int mContentEndMargin;
Selim Cinek90c8f472015-11-10 17:44:39 -050039 private View mAppName;
40 private View mSubTextView;
Selim Cinekaef6c762015-11-20 17:00:18 -080041 private OnClickListener mExpandClickListener;
42 private HeaderTouchListener mTouchListener = new HeaderTouchListener();
Selim Cinek7b836392015-12-04 20:02:59 -080043 private ImageView mExpandButton;
Selim Cinekaef6c762015-11-20 17:00:18 -080044 private View mIcon;
Selim Cinekc848c3a2016-01-13 15:27:30 -080045 private View mProfileBadge;
Selim Cinek413142a2016-02-03 10:58:13 -080046 private View mInfo;
Selim Cinekea4bef72015-12-02 15:51:10 -080047 private int mIconColor;
48 private int mOriginalNotificationColor;
Selim Cinek7b836392015-12-04 20:02:59 -080049 private boolean mExpanded;
Selim Cinek6ecc8102016-01-26 18:26:19 -080050 private boolean mShowWorkBadgeAtEnd;
Selim Cinek90c8f472015-11-10 17:44:39 -050051
52 public NotificationHeaderView(Context context) {
53 this(context, null);
54 }
55
56 public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
57 this(context, attrs, 0);
58 }
59
60 public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
61 this(context, attrs, defStyleAttr, 0);
62 }
63
64 public NotificationHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
65 super(context, attrs, defStyleAttr, defStyleRes);
Selim Cinek413142a2016-02-03 10:58:13 -080066 mChildMinWidth = getResources().getDimensionPixelSize(
Selim Cinek90c8f472015-11-10 17:44:39 -050067 com.android.internal.R.dimen.notification_header_shrink_min_width);
Selim Cinek6ecc8102016-01-26 18:26:19 -080068 mContentEndMargin = getResources().getDimensionPixelSize(
69 com.android.internal.R.dimen.notification_content_margin_end);
Selim Cinek90c8f472015-11-10 17:44:39 -050070 }
71
72 @Override
73 protected void onFinishInflate() {
74 super.onFinishInflate();
75 mAppName = findViewById(com.android.internal.R.id.app_name_text);
76 mSubTextView = findViewById(com.android.internal.R.id.header_sub_text);
Selim Cinek7b836392015-12-04 20:02:59 -080077 mExpandButton = (ImageView) findViewById(com.android.internal.R.id.expand_button);
Selim Cinekaef6c762015-11-20 17:00:18 -080078 mIcon = findViewById(com.android.internal.R.id.icon);
Selim Cinekc848c3a2016-01-13 15:27:30 -080079 mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
Selim Cinek413142a2016-02-03 10:58:13 -080080 mInfo = findViewById(com.android.internal.R.id.header_content_info);
Selim Cinek90c8f472015-11-10 17:44:39 -050081 }
82
83 @Override
84 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
85 final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
86 final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
87 int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
88 MeasureSpec.AT_MOST);
89 int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
90 MeasureSpec.AT_MOST);
91 int totalWidth = getPaddingStart() + getPaddingEnd();
92 for (int i = 0; i < getChildCount(); i++) {
93 final View child = getChildAt(i);
94 if (child.getVisibility() == GONE) {
95 // We'll give it the rest of the space in the end
96 continue;
97 }
98 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
99 int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec,
100 lp.leftMargin + lp.rightMargin, lp.width);
101 int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
102 lp.topMargin + lp.bottomMargin, lp.height);
103 child.measure(childWidthSpec, childHeightSpec);
104 totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
105 }
106 if (totalWidth > givenWidth) {
107 int overFlow = totalWidth - givenWidth;
Selim Cinek413142a2016-02-03 10:58:13 -0800108 // We are overflowing, lets shrink the info first
109 final int infoWidth = mInfo.getMeasuredWidth();
110 if (mInfo.getVisibility() != GONE && infoWidth > mChildMinWidth) {
111 int newSize = infoWidth - Math.min(infoWidth - mChildMinWidth, overFlow);
112 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
113 mInfo.measure(childWidthSpec, wrapContentHeightSpec);
114 overFlow -= infoWidth - newSize;
115 }
116 // still overflowing, lets shrink the app name now
Selim Cinek90c8f472015-11-10 17:44:39 -0500117 final int appWidth = mAppName.getMeasuredWidth();
Selim Cinek413142a2016-02-03 10:58:13 -0800118 if (overFlow > 0 && mAppName.getVisibility() != GONE && appWidth > mChildMinWidth) {
119 int newSize = appWidth - Math.min(appWidth - mChildMinWidth, overFlow);
Selim Cinek90c8f472015-11-10 17:44:39 -0500120 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
121 mAppName.measure(childWidthSpec, wrapContentHeightSpec);
122 overFlow -= appWidth - newSize;
123 }
Selim Cinek413142a2016-02-03 10:58:13 -0800124 // still overflowing, finaly we shrink the subtext
Selim Cinek90c8f472015-11-10 17:44:39 -0500125 if (overFlow > 0 && mSubTextView.getVisibility() != GONE) {
126 // we're still too big
127 final int subTextWidth = mSubTextView.getMeasuredWidth();
128 int newSize = Math.max(0, subTextWidth - overFlow);
129 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
130 mSubTextView.measure(childWidthSpec, wrapContentHeightSpec);
131 }
Selim Cinek90c8f472015-11-10 17:44:39 -0500132 }
Selim Cinek4c4c7382016-02-03 16:17:09 -0800133 setMeasuredDimension(givenWidth, givenHeight);
Selim Cinek90c8f472015-11-10 17:44:39 -0500134 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800135
136 @Override
137 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinekcb445682016-01-29 16:13:12 -0800138 int left = getPaddingStart();
139 int childCount = getChildCount();
140 int ownHeight = getHeight() - getPaddingTop() - getPaddingBottom();
141 for (int i = 0; i < childCount; i++) {
142 View child = getChildAt(i);
143 if (child.getVisibility() == GONE) {
144 continue;
145 }
146 int childHeight = child.getMeasuredHeight();
147 MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
148 left += params.getMarginStart();
149 int right = left + child.getMeasuredWidth();
150 int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
151 int bottom = top + childHeight;
152 int layoutLeft = left;
153 int layoutRight = right;
154 if (child == mProfileBadge) {
155 int paddingEnd = getPaddingEnd();
156 if (mShowWorkBadgeAtEnd) {
157 paddingEnd = mContentEndMargin;
158 }
159 layoutRight = getWidth() - paddingEnd;
160 layoutLeft = layoutRight - child.getMeasuredWidth();
Selim Cinek6ecc8102016-01-26 18:26:19 -0800161 }
Selim Cinekc848c3a2016-01-13 15:27:30 -0800162 if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
Selim Cinekcb445682016-01-29 16:13:12 -0800163 int ltrLeft = layoutLeft;
164 layoutLeft = getWidth() - layoutRight;
165 layoutRight = getWidth() - ltrLeft;
Selim Cinekc848c3a2016-01-13 15:27:30 -0800166 }
Selim Cinekcb445682016-01-29 16:13:12 -0800167 child.layout(layoutLeft, top, layoutRight, bottom);
168 left = right + params.getMarginEnd();
Selim Cinekc848c3a2016-01-13 15:27:30 -0800169 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800170 updateTouchListener();
171 }
172
Selim Cinekcb445682016-01-29 16:13:12 -0800173 @Override
174 public LayoutParams generateLayoutParams(AttributeSet attrs) {
175 return new ViewGroup.MarginLayoutParams(getContext(), attrs);
176 }
177
Selim Cinekaef6c762015-11-20 17:00:18 -0800178 private void updateTouchListener() {
179 if (mExpandClickListener != null) {
180 mTouchListener.bindTouchRects();
181 }
182 }
183
184 @Override
185 public void setOnClickListener(@Nullable OnClickListener l) {
186 mExpandClickListener = l;
187 setOnTouchListener(mExpandClickListener != null ? mTouchListener : null);
188 updateTouchListener();
189 }
190
Selim Cinekea4bef72015-12-02 15:51:10 -0800191 @RemotableViewMethod
192 public void setOriginalIconColor(int color) {
193 mIconColor = color;
194 }
195
196 public int getOriginalIconColor() {
197 return mIconColor;
198 }
199
200 @RemotableViewMethod
201 public void setOriginalNotificationColor(int color) {
202 mOriginalNotificationColor = color;
203 }
204
205 public int getOriginalNotificationColor() {
206 return mOriginalNotificationColor;
207 }
208
Selim Cinek7b836392015-12-04 20:02:59 -0800209 @RemotableViewMethod
210 public void setExpanded(boolean expanded) {
211 mExpanded = expanded;
212 updateExpandButton();
213 }
214
215 private void updateExpandButton() {
216 int drawableId;
Selim Cinek6db57582016-03-04 19:11:27 -0800217 if (mExpanded) {
218 drawableId = com.android.internal.R.drawable.ic_collapse_notification;
Selim Cinek7b836392015-12-04 20:02:59 -0800219 } else {
Selim Cinek6db57582016-03-04 19:11:27 -0800220 drawableId = com.android.internal.R.drawable.ic_expand_notification;
Selim Cinek7b836392015-12-04 20:02:59 -0800221 }
222 mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
223 mExpandButton.setColorFilter(mOriginalNotificationColor);
Selim Cinek7b836392015-12-04 20:02:59 -0800224 }
225
Selim Cinek6ecc8102016-01-26 18:26:19 -0800226 public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
227 if (showWorkBadgeAtEnd != mShowWorkBadgeAtEnd) {
228 setClipToPadding(!showWorkBadgeAtEnd);
229 mShowWorkBadgeAtEnd = showWorkBadgeAtEnd;
230 }
231 }
232
Selim Cinek0d07c7e2016-01-27 18:38:31 -0800233 public View getWorkProfileIcon() {
234 return mProfileBadge;
235 }
236
Selim Cinekaef6c762015-11-20 17:00:18 -0800237 public class HeaderTouchListener implements View.OnTouchListener {
238
239 private final ArrayList<Rect> mTouchRects = new ArrayList<>();
240 private int mTouchSlop;
241 private boolean mTrackGesture;
242 private float mDownX;
243 private float mDownY;
244
245 public HeaderTouchListener() {
246 }
247
248 public void bindTouchRects() {
249 mTouchRects.clear();
250 addRectAroundViewView(mIcon);
251 addRectAroundViewView(mExpandButton);
Selim Cinek4c4c7382016-02-03 16:17:09 -0800252 addWidthRect();
Selim Cinekaef6c762015-11-20 17:00:18 -0800253 mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
254 }
255
Selim Cinek4c4c7382016-02-03 16:17:09 -0800256 private void addWidthRect() {
257 Rect r = new Rect();
Selim Cinekaef6c762015-11-20 17:00:18 -0800258 r.top = 0;
259 r.bottom = (int) (32 * getResources().getDisplayMetrics().density);
Selim Cinek4c4c7382016-02-03 16:17:09 -0800260 r.left = 0;
261 r.right = getWidth();
Selim Cinekaef6c762015-11-20 17:00:18 -0800262 mTouchRects.add(r);
263 }
264
265 private void addRectAroundViewView(View view) {
266 final Rect r = getRectAroundView(view);
267 mTouchRects.add(r);
268 }
269
270 private Rect getRectAroundView(View view) {
271 float size = 48 * getResources().getDisplayMetrics().density;
272 final Rect r = new Rect();
273 if (view.getVisibility() == GONE) {
274 view = getFirstChildNotGone();
275 r.left = (int) (view.getLeft() - size / 2.0f);
276 } else {
277 r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - size / 2.0f);
278 }
279 r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - size / 2.0f);
280 r.bottom = (int) (r.top + size);
281 r.right = (int) (r.left + size);
282 return r;
283 }
284
285 @Override
286 public boolean onTouch(View v, MotionEvent event) {
287 float x = event.getX();
288 float y = event.getY();
289 switch (event.getActionMasked() & MotionEvent.ACTION_MASK) {
290 case MotionEvent.ACTION_DOWN:
291 mTrackGesture = false;
292 if (isInside(x, y)) {
293 mTrackGesture = true;
294 return true;
295 }
296 break;
297 case MotionEvent.ACTION_MOVE:
298 if (mTrackGesture) {
299 if (Math.abs(mDownX - x) > mTouchSlop
300 || Math.abs(mDownY - y) > mTouchSlop) {
301 mTrackGesture = false;
302 }
303 }
304 break;
305 case MotionEvent.ACTION_UP:
306 if (mTrackGesture) {
307 mExpandClickListener.onClick(NotificationHeaderView.this);
308 }
309 break;
310 }
311 return mTrackGesture;
312 }
313
314 private boolean isInside(float x, float y) {
315 for (int i = 0; i < mTouchRects.size(); i++) {
316 Rect r = mTouchRects.get(i);
317 if (r.contains((int) x, (int) y)) {
318 mDownX = x;
319 mDownY = y;
320 return true;
321 }
322 }
323 return false;
324 }
325 }
326
327 private View getFirstChildNotGone() {
328 for (int i = 0; i < getChildCount(); i++) {
329 final View child = getChildAt(i);
330 if (child.getVisibility() != GONE) {
331 return child;
332 }
333 }
334 return this;
335 }
Selim Cinek4ffd6362015-12-29 15:12:23 +0100336
337 public ImageView getExpandButton() {
338 return mExpandButton;
339 }
340
341 @Override
342 public boolean hasOverlappingRendering() {
343 return false;
344 }
Selim Cinek6183d122016-01-14 18:48:41 -0800345
346 public boolean isInTouchRect(float x, float y) {
347 if (mExpandClickListener == null) {
348 return false;
349 }
350 return mTouchListener.isInside(x, y);
351 }
Selim Cinek90c8f472015-11-10 17:44:39 -0500352}