blob: f8d5014a29c99096e99fad5d10216749dbf9103b [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;
Mathew Inwooda570dee2018-08-17 14:56:00 +010020import android.annotation.UnsupportedAppUsage;
Julia Reynoldsfc640012018-02-21 12:25:27 -050021import android.app.AppOpsManager;
Selim Cinek99104832017-01-25 14:47:33 -080022import android.app.Notification;
Selim Cinek90c8f472015-11-10 17:44:39 -050023import android.content.Context;
Anthony Chen0f6e96c2017-04-07 15:48:17 -070024import android.content.res.Resources;
Lucas Dupinbd9798f2017-10-24 18:04:51 -070025import android.content.res.TypedArray;
Mady Mellorb0a82462016-04-30 17:31:02 -070026import android.graphics.Canvas;
27import android.graphics.Outline;
Selim Cinekaef6c762015-11-20 17:00:18 -080028import android.graphics.Rect;
Mady Mellorb0a82462016-04-30 17:31:02 -070029import android.graphics.drawable.Drawable;
Julia Reynoldsfc640012018-02-21 12:25:27 -050030import android.util.ArraySet;
Selim Cinek90c8f472015-11-10 17:44:39 -050031import android.util.AttributeSet;
Selim Cinek7b836392015-12-04 20:02:59 -080032import android.widget.ImageView;
Selim Cinek90c8f472015-11-10 17:44:39 -050033import android.widget.RemoteViews;
34
Anthony Chen0f6e96c2017-04-07 15:48:17 -070035import com.android.internal.R;
Selim Cinek0242fbb2016-10-19 13:38:32 -070036import com.android.internal.widget.CachingIconView;
37
Selim Cinekaef6c762015-11-20 17:00:18 -080038import java.util.ArrayList;
Selim Cinek90c8f472015-11-10 17:44:39 -050039
40/**
41 * A header of a notification view
42 *
43 * @hide
44 */
45@RemoteViews.RemoteView
Selim Cinekcb445682016-01-29 16:13:12 -080046public class NotificationHeaderView extends ViewGroup {
Selim Cinek99104832017-01-25 14:47:33 -080047 public static final int NO_COLOR = Notification.COLOR_INVALID;
Selim Cinek413142a2016-02-03 10:58:13 -080048 private final int mChildMinWidth;
Selim Cinek6ecc8102016-01-26 18:26:19 -080049 private final int mContentEndMargin;
Lucas Dupinbd9798f2017-10-24 18:04:51 -070050 private final int mGravity;
Selim Cinek90c8f472015-11-10 17:44:39 -050051 private View mAppName;
Selim Cinek0f9dd1e2016-04-05 17:03:40 -070052 private View mHeaderText;
Selim Cinekafeed292017-12-12 17:32:44 -080053 private View mSecondaryHeaderText;
Selim Cinekaef6c762015-11-20 17:00:18 -080054 private OnClickListener mExpandClickListener;
Julia Reynoldsb5867452018-02-28 16:31:35 -050055 private OnClickListener mAppOpsListener;
Selim Cinekaef6c762015-11-20 17:00:18 -080056 private HeaderTouchListener mTouchListener = new HeaderTouchListener();
Selim Cinek7b836392015-12-04 20:02:59 -080057 private ImageView mExpandButton;
Selim Cinek0242fbb2016-10-19 13:38:32 -070058 private CachingIconView mIcon;
Selim Cinekc848c3a2016-01-13 15:27:30 -080059 private View mProfileBadge;
Julia Reynoldsfc640012018-02-21 12:25:27 -050060 private View mOverlayIcon;
61 private View mCameraIcon;
62 private View mMicIcon;
63 private View mAppOps;
Gus Prevasa3226492018-10-23 11:10:09 -040064 private View mAudiblyAlertedIcon;
Selim Cinekea4bef72015-12-02 15:51:10 -080065 private int mIconColor;
66 private int mOriginalNotificationColor;
Selim Cinek7b836392015-12-04 20:02:59 -080067 private boolean mExpanded;
Anthony Chen0f6e96c2017-04-07 15:48:17 -070068 private boolean mShowExpandButtonAtEnd;
Selim Cinek6ecc8102016-01-26 18:26:19 -080069 private boolean mShowWorkBadgeAtEnd;
Mady Mellorb0a82462016-04-30 17:31:02 -070070 private Drawable mBackground;
Anthony Chen0f6e96c2017-04-07 15:48:17 -070071 private boolean mEntireHeaderClickable;
Selim Cinek499c20f2017-07-20 14:06:09 -070072 private boolean mExpandOnlyOnButton;
Anthony Chen0f6e96c2017-04-07 15:48:17 -070073 private boolean mAcceptAllTouches;
Lucas Dupinbd9798f2017-10-24 18:04:51 -070074 private int mTotalWidth;
Mady Mellorb0a82462016-04-30 17:31:02 -070075
76 ViewOutlineProvider mProvider = new ViewOutlineProvider() {
77 @Override
78 public void getOutline(View view, Outline outline) {
79 if (mBackground != null) {
Selim Cinekafeed292017-12-12 17:32:44 -080080 outline.setRect(0, 0, getWidth(), getHeight());
Mady Mellorb0a82462016-04-30 17:31:02 -070081 outline.setAlpha(1f);
82 }
83 }
84 };
Selim Cinek90c8f472015-11-10 17:44:39 -050085
86 public NotificationHeaderView(Context context) {
87 this(context, null);
88 }
89
Mathew Inwooda570dee2018-08-17 14:56:00 +010090 @UnsupportedAppUsage
Selim Cinek90c8f472015-11-10 17:44:39 -050091 public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
92 this(context, attrs, 0);
93 }
94
95 public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
96 this(context, attrs, defStyleAttr, 0);
97 }
98
99 public NotificationHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
100 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700101 Resources res = getResources();
102 mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
103 mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700104 mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand);
Lucas Dupinbd9798f2017-10-24 18:04:51 -0700105
106 int[] attrIds = { android.R.attr.gravity };
107 TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes);
108 mGravity = ta.getInt(0, 0);
109 ta.recycle();
Selim Cinek90c8f472015-11-10 17:44:39 -0500110 }
111
112 @Override
113 protected void onFinishInflate() {
114 super.onFinishInflate();
115 mAppName = findViewById(com.android.internal.R.id.app_name_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700116 mHeaderText = findViewById(com.android.internal.R.id.header_text);
Selim Cinekafeed292017-12-12 17:32:44 -0800117 mSecondaryHeaderText = findViewById(com.android.internal.R.id.header_text_secondary);
Alan Viverette51efddb2017-04-05 10:00:01 -0400118 mExpandButton = findViewById(com.android.internal.R.id.expand_button);
119 mIcon = findViewById(com.android.internal.R.id.icon);
Selim Cinekc848c3a2016-01-13 15:27:30 -0800120 mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
Julia Reynoldsfc640012018-02-21 12:25:27 -0500121 mCameraIcon = findViewById(com.android.internal.R.id.camera);
122 mMicIcon = findViewById(com.android.internal.R.id.mic);
123 mOverlayIcon = findViewById(com.android.internal.R.id.overlay);
124 mAppOps = findViewById(com.android.internal.R.id.app_ops);
Gus Prevasa3226492018-10-23 11:10:09 -0400125 mAudiblyAlertedIcon = findViewById(com.android.internal.R.id.alerted_icon);
Selim Cinek90c8f472015-11-10 17:44:39 -0500126 }
127
128 @Override
129 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
130 final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
131 final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
132 int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
133 MeasureSpec.AT_MOST);
134 int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
135 MeasureSpec.AT_MOST);
136 int totalWidth = getPaddingStart() + getPaddingEnd();
137 for (int i = 0; i < getChildCount(); i++) {
138 final View child = getChildAt(i);
139 if (child.getVisibility() == GONE) {
140 // We'll give it the rest of the space in the end
141 continue;
142 }
143 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
144 int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec,
145 lp.leftMargin + lp.rightMargin, lp.width);
146 int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
147 lp.topMargin + lp.bottomMargin, lp.height);
148 child.measure(childWidthSpec, childHeightSpec);
149 totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
150 }
151 if (totalWidth > givenWidth) {
152 int overFlow = totalWidth - givenWidth;
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700153 // We are overflowing, lets shrink the app name first
Selim Cinekafeed292017-12-12 17:32:44 -0800154 overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName,
155 mChildMinWidth);
156
157 // still overflowing, we shrink the header text
158 overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0);
159
160 // still overflowing, finally we shrink the secondary header text
161 shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
162 0);
Selim Cinek90c8f472015-11-10 17:44:39 -0500163 }
Lucas Dupinbd9798f2017-10-24 18:04:51 -0700164 mTotalWidth = Math.min(totalWidth, givenWidth);
Selim Cinek4c4c7382016-02-03 16:17:09 -0800165 setMeasuredDimension(givenWidth, givenHeight);
Selim Cinek90c8f472015-11-10 17:44:39 -0500166 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800167
Selim Cinekafeed292017-12-12 17:32:44 -0800168 private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
169 int minimumWidth) {
170 final int oldWidth = targetView.getMeasuredWidth();
171 if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
172 // we're still too big
173 int newSize = Math.max(minimumWidth, oldWidth - overFlow);
174 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
175 targetView.measure(childWidthSpec, heightSpec);
176 overFlow -= oldWidth - newSize;
177 }
178 return overFlow;
179 }
180
Selim Cinekaef6c762015-11-20 17:00:18 -0800181 @Override
182 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinekcb445682016-01-29 16:13:12 -0800183 int left = getPaddingStart();
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700184 int end = getMeasuredWidth();
Lucas Dupinbd9798f2017-10-24 18:04:51 -0700185 final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
186 if (centerAligned) {
187 left += getMeasuredWidth() / 2 - mTotalWidth / 2;
188 }
Selim Cinekcb445682016-01-29 16:13:12 -0800189 int childCount = getChildCount();
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700190 int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
Selim Cinekcb445682016-01-29 16:13:12 -0800191 for (int i = 0; i < childCount; i++) {
192 View child = getChildAt(i);
193 if (child.getVisibility() == GONE) {
194 continue;
195 }
196 int childHeight = child.getMeasuredHeight();
197 MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
Gus Prevasc79816b2018-12-27 15:46:00 -0500198 int layoutLeft;
199 int layoutRight;
Selim Cinekcb445682016-01-29 16:13:12 -0800200 int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
201 int bottom = top + childHeight;
Gus Prevas87717612018-12-17 14:00:12 -0500202 if ((child == mExpandButton && mShowExpandButtonAtEnd)
203 || child == mProfileBadge
204 || child == mAppOps) {
205 if (end == getMeasuredWidth()) {
206 layoutRight = end - mContentEndMargin;
207 } else {
208 layoutRight = end - params.getMarginEnd();
Selim Cinekcb445682016-01-29 16:13:12 -0800209 }
Gus Prevas87717612018-12-17 14:00:12 -0500210 layoutLeft = layoutRight - child.getMeasuredWidth();
211 end = layoutLeft - params.getMarginStart();
Gus Prevasc79816b2018-12-27 15:46:00 -0500212 } else {
213 left += params.getMarginStart();
214 int right = left + child.getMeasuredWidth();
215 layoutLeft = left;
216 layoutRight = right;
217 left = right + params.getMarginEnd();
Julia Reynoldsfc640012018-02-21 12:25:27 -0500218 }
Selim Cinekc848c3a2016-01-13 15:27:30 -0800219 if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
Selim Cinekcb445682016-01-29 16:13:12 -0800220 int ltrLeft = layoutLeft;
221 layoutLeft = getWidth() - layoutRight;
222 layoutRight = getWidth() - ltrLeft;
Selim Cinekc848c3a2016-01-13 15:27:30 -0800223 }
Selim Cinekcb445682016-01-29 16:13:12 -0800224 child.layout(layoutLeft, top, layoutRight, bottom);
Selim Cinekc848c3a2016-01-13 15:27:30 -0800225 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800226 updateTouchListener();
227 }
228
Selim Cinekcb445682016-01-29 16:13:12 -0800229 @Override
230 public LayoutParams generateLayoutParams(AttributeSet attrs) {
231 return new ViewGroup.MarginLayoutParams(getContext(), attrs);
232 }
233
Mady Mellorb0a82462016-04-30 17:31:02 -0700234 /**
235 * Set a {@link Drawable} to be displayed as a background on the header.
236 */
237 public void setHeaderBackgroundDrawable(Drawable drawable) {
238 if (drawable != null) {
239 setWillNotDraw(false);
240 mBackground = drawable;
241 mBackground.setCallback(this);
242 setOutlineProvider(mProvider);
243 } else {
244 setWillNotDraw(true);
245 mBackground = null;
246 setOutlineProvider(null);
247 }
248 invalidate();
249 }
250
251 @Override
252 protected void onDraw(Canvas canvas) {
253 if (mBackground != null) {
Selim Cinekafeed292017-12-12 17:32:44 -0800254 mBackground.setBounds(0, 0, getWidth(), getHeight());
Mady Mellorb0a82462016-04-30 17:31:02 -0700255 mBackground.draw(canvas);
256 }
257 }
258
259 @Override
260 protected boolean verifyDrawable(Drawable who) {
261 return super.verifyDrawable(who) || who == mBackground;
262 }
263
264 @Override
265 protected void drawableStateChanged() {
266 if (mBackground != null && mBackground.isStateful()) {
267 mBackground.setState(getDrawableState());
268 }
269 }
270
Selim Cinekaef6c762015-11-20 17:00:18 -0800271 private void updateTouchListener() {
Julia Reynoldsb5867452018-02-28 16:31:35 -0500272 if (mExpandClickListener == null && mAppOpsListener == null) {
273 setOnTouchListener(null);
274 return;
Selim Cinekaef6c762015-11-20 17:00:18 -0800275 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500276 setOnTouchListener(mTouchListener);
277 mTouchListener.bindTouchRects();
278 }
279
280 /**
281 * Sets onclick listener for app ops icons.
282 */
283 public void setAppOpsOnClickListener(OnClickListener l) {
284 mAppOpsListener = l;
285 mAppOps.setOnClickListener(mAppOpsListener);
Dieter Hsu3a9b5a92018-05-15 19:02:52 +0800286 mCameraIcon.setOnClickListener(mAppOpsListener);
287 mMicIcon.setOnClickListener(mAppOpsListener);
288 mOverlayIcon.setOnClickListener(mAppOpsListener);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500289 updateTouchListener();
Selim Cinekaef6c762015-11-20 17:00:18 -0800290 }
291
292 @Override
293 public void setOnClickListener(@Nullable OnClickListener l) {
294 mExpandClickListener = l;
Selim Cineke9bad242016-06-15 11:46:37 -0700295 mExpandButton.setOnClickListener(mExpandClickListener);
Selim Cinekaef6c762015-11-20 17:00:18 -0800296 updateTouchListener();
297 }
298
Selim Cinekea4bef72015-12-02 15:51:10 -0800299 @RemotableViewMethod
300 public void setOriginalIconColor(int color) {
301 mIconColor = color;
302 }
303
304 public int getOriginalIconColor() {
305 return mIconColor;
306 }
307
308 @RemotableViewMethod
309 public void setOriginalNotificationColor(int color) {
310 mOriginalNotificationColor = color;
311 }
312
313 public int getOriginalNotificationColor() {
314 return mOriginalNotificationColor;
315 }
316
Selim Cinek7b836392015-12-04 20:02:59 -0800317 @RemotableViewMethod
318 public void setExpanded(boolean expanded) {
319 mExpanded = expanded;
320 updateExpandButton();
321 }
322
Julia Reynoldsfc640012018-02-21 12:25:27 -0500323 /**
324 * Shows or hides 'app op in use' icons based on app usage.
325 */
326 public void showAppOpsIcons(ArraySet<Integer> appOps) {
Julia Reynoldsb5867452018-02-28 16:31:35 -0500327 if (mOverlayIcon == null || mCameraIcon == null || mMicIcon == null || appOps == null) {
Julia Reynoldsfc640012018-02-21 12:25:27 -0500328 return;
329 }
330
331 mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
332 ? View.VISIBLE : View.GONE);
333 mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA)
334 ? View.VISIBLE : View.GONE);
335 mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO)
336 ? View.VISIBLE : View.GONE);
337 }
338
Gus Prevasa3226492018-10-23 11:10:09 -0400339 /** Updates icon visibility based on the noisiness of the notification. */
Gus Prevas7306b902018-12-11 10:57:06 -0500340 public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
Gus Prevasa3226492018-10-23 11:10:09 -0400341 mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE);
342 }
343
Selim Cinek7b836392015-12-04 20:02:59 -0800344 private void updateExpandButton() {
345 int drawableId;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800346 int contentDescriptionId;
Selim Cinek6db57582016-03-04 19:11:27 -0800347 if (mExpanded) {
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700348 drawableId = R.drawable.ic_collapse_notification;
349 contentDescriptionId = R.string.expand_button_content_description_expanded;
Selim Cinek7b836392015-12-04 20:02:59 -0800350 } else {
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700351 drawableId = R.drawable.ic_expand_notification;
352 contentDescriptionId = R.string.expand_button_content_description_collapsed;
Selim Cinek7b836392015-12-04 20:02:59 -0800353 }
354 mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
355 mExpandButton.setColorFilter(mOriginalNotificationColor);
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800356 mExpandButton.setContentDescription(mContext.getText(contentDescriptionId));
Selim Cinek7b836392015-12-04 20:02:59 -0800357 }
358
Selim Cinek6ecc8102016-01-26 18:26:19 -0800359 public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
360 if (showWorkBadgeAtEnd != mShowWorkBadgeAtEnd) {
361 setClipToPadding(!showWorkBadgeAtEnd);
362 mShowWorkBadgeAtEnd = showWorkBadgeAtEnd;
363 }
364 }
365
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700366 /**
367 * Sets whether or not the expand button appears at the end of the NotificationHeaderView. If
368 * both this and {@link #setShowWorkBadgeAtEnd(boolean)} have been set to true, then the
369 * expand button will appear closer to the end than the work badge.
370 */
371 public void setShowExpandButtonAtEnd(boolean showExpandButtonAtEnd) {
372 if (showExpandButtonAtEnd != mShowExpandButtonAtEnd) {
373 setClipToPadding(!showExpandButtonAtEnd);
374 mShowExpandButtonAtEnd = showExpandButtonAtEnd;
375 }
376 }
377
Selim Cinek0d07c7e2016-01-27 18:38:31 -0800378 public View getWorkProfileIcon() {
379 return mProfileBadge;
380 }
381
Selim Cinek0242fbb2016-10-19 13:38:32 -0700382 public CachingIconView getIcon() {
Selim Cinek281c2022016-10-13 19:14:43 -0700383 return mIcon;
384 }
385
Selim Cinekaef6c762015-11-20 17:00:18 -0800386 public class HeaderTouchListener implements View.OnTouchListener {
387
388 private final ArrayList<Rect> mTouchRects = new ArrayList<>();
Selim Cinek499c20f2017-07-20 14:06:09 -0700389 private Rect mExpandButtonRect;
Julia Reynoldsb5867452018-02-28 16:31:35 -0500390 private Rect mAppOpsRect;
Selim Cinekaef6c762015-11-20 17:00:18 -0800391 private int mTouchSlop;
392 private boolean mTrackGesture;
393 private float mDownX;
394 private float mDownY;
395
396 public HeaderTouchListener() {
397 }
398
399 public void bindTouchRects() {
400 mTouchRects.clear();
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700401 addRectAroundView(mIcon);
Selim Cinek499c20f2017-07-20 14:06:09 -0700402 mExpandButtonRect = addRectAroundView(mExpandButton);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500403 mAppOpsRect = addRectAroundView(mAppOps);
Selim Cinek4c4c7382016-02-03 16:17:09 -0800404 addWidthRect();
Selim Cinekaef6c762015-11-20 17:00:18 -0800405 mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
406 }
407
Selim Cinek4c4c7382016-02-03 16:17:09 -0800408 private void addWidthRect() {
409 Rect r = new Rect();
Selim Cinekaef6c762015-11-20 17:00:18 -0800410 r.top = 0;
411 r.bottom = (int) (32 * getResources().getDisplayMetrics().density);
Selim Cinek4c4c7382016-02-03 16:17:09 -0800412 r.left = 0;
413 r.right = getWidth();
Selim Cinekaef6c762015-11-20 17:00:18 -0800414 mTouchRects.add(r);
415 }
416
Selim Cinek499c20f2017-07-20 14:06:09 -0700417 private Rect addRectAroundView(View view) {
Selim Cinekaef6c762015-11-20 17:00:18 -0800418 final Rect r = getRectAroundView(view);
419 mTouchRects.add(r);
Selim Cinek499c20f2017-07-20 14:06:09 -0700420 return r;
Selim Cinekaef6c762015-11-20 17:00:18 -0800421 }
422
423 private Rect getRectAroundView(View view) {
424 float size = 48 * getResources().getDisplayMetrics().density;
Julia Reynoldsb5867452018-02-28 16:31:35 -0500425 float width = Math.max(size, view.getWidth());
426 float height = Math.max(size, view.getHeight());
Selim Cinekaef6c762015-11-20 17:00:18 -0800427 final Rect r = new Rect();
428 if (view.getVisibility() == GONE) {
429 view = getFirstChildNotGone();
Julia Reynoldsb5867452018-02-28 16:31:35 -0500430 r.left = (int) (view.getLeft() - width / 2.0f);
Selim Cinekaef6c762015-11-20 17:00:18 -0800431 } else {
Julia Reynoldsb5867452018-02-28 16:31:35 -0500432 r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - width / 2.0f);
Selim Cinekaef6c762015-11-20 17:00:18 -0800433 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500434 r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - height / 2.0f);
435 r.bottom = (int) (r.top + height);
436 r.right = (int) (r.left + width);
Selim Cinekaef6c762015-11-20 17:00:18 -0800437 return r;
438 }
439
440 @Override
441 public boolean onTouch(View v, MotionEvent event) {
442 float x = event.getX();
443 float y = event.getY();
444 switch (event.getActionMasked() & MotionEvent.ACTION_MASK) {
445 case MotionEvent.ACTION_DOWN:
446 mTrackGesture = false;
447 if (isInside(x, y)) {
Selim Cinek1b554392017-02-28 17:22:49 -0800448 mDownX = x;
449 mDownY = y;
Selim Cinekaef6c762015-11-20 17:00:18 -0800450 mTrackGesture = true;
451 return true;
452 }
453 break;
454 case MotionEvent.ACTION_MOVE:
455 if (mTrackGesture) {
456 if (Math.abs(mDownX - x) > mTouchSlop
457 || Math.abs(mDownY - y) > mTouchSlop) {
458 mTrackGesture = false;
459 }
460 }
461 break;
462 case MotionEvent.ACTION_UP:
463 if (mTrackGesture) {
Julia Reynoldsb5867452018-02-28 16:31:35 -0500464 if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y)
465 || mAppOpsRect.contains((int) mDownX, (int) mDownY))) {
466 mAppOps.performClick();
467 return true;
468 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800469 mExpandButton.performClick();
Selim Cinekaef6c762015-11-20 17:00:18 -0800470 }
471 break;
472 }
473 return mTrackGesture;
474 }
475
476 private boolean isInside(float x, float y) {
Selim Cinek1b554392017-02-28 17:22:49 -0800477 if (mAcceptAllTouches) {
478 return true;
479 }
Selim Cinek499c20f2017-07-20 14:06:09 -0700480 if (mExpandOnlyOnButton) {
481 return mExpandButtonRect.contains((int) x, (int) y);
482 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800483 for (int i = 0; i < mTouchRects.size(); i++) {
484 Rect r = mTouchRects.get(i);
485 if (r.contains((int) x, (int) y)) {
Selim Cinekaef6c762015-11-20 17:00:18 -0800486 return true;
487 }
488 }
489 return false;
490 }
491 }
492
493 private View getFirstChildNotGone() {
494 for (int i = 0; i < getChildCount(); i++) {
495 final View child = getChildAt(i);
496 if (child.getVisibility() != GONE) {
497 return child;
498 }
499 }
500 return this;
501 }
Selim Cinek4ffd6362015-12-29 15:12:23 +0100502
503 public ImageView getExpandButton() {
504 return mExpandButton;
505 }
506
507 @Override
508 public boolean hasOverlappingRendering() {
509 return false;
510 }
Selim Cinek6183d122016-01-14 18:48:41 -0800511
512 public boolean isInTouchRect(float x, float y) {
513 if (mExpandClickListener == null) {
514 return false;
515 }
516 return mTouchListener.isInside(x, y);
517 }
Selim Cinek1b554392017-02-28 17:22:49 -0800518
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700519 /**
520 * Sets whether or not all touches to this header view will register as a click. Note that
521 * if the config value for {@code config_notificationHeaderClickableForExpand} is {@code true},
522 * then calling this method with {@code false} will not override that configuration.
523 */
Selim Cinek1b554392017-02-28 17:22:49 -0800524 @RemotableViewMethod
525 public void setAcceptAllTouches(boolean acceptAllTouches) {
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700526 mAcceptAllTouches = mEntireHeaderClickable || acceptAllTouches;
Selim Cinek1b554392017-02-28 17:22:49 -0800527 }
Selim Cinek499c20f2017-07-20 14:06:09 -0700528
529 /**
530 * Sets whether only the expand icon itself should serve as the expand target.
531 */
532 @RemotableViewMethod
533 public void setExpandOnlyOnButton(boolean expandOnlyOnButton) {
534 mExpandOnlyOnButton = expandOnlyOnButton;
535 }
Selim Cinek90c8f472015-11-10 17:44:39 -0500536}