blob: 7a10364708941de3145ad86a4733abdf82614499 [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;
Julia Reynoldsfc640012018-02-21 12:25:27 -050020import android.app.AppOpsManager;
Selim Cinek99104832017-01-25 14:47:33 -080021import android.app.Notification;
Selim Cinek90c8f472015-11-10 17:44:39 -050022import android.content.Context;
Anthony Chen0f6e96c2017-04-07 15:48:17 -070023import android.content.res.Resources;
Lucas Dupinbd9798f2017-10-24 18:04:51 -070024import android.content.res.TypedArray;
Mady Mellorb0a82462016-04-30 17:31:02 -070025import android.graphics.Canvas;
26import android.graphics.Outline;
Selim Cinekaef6c762015-11-20 17:00:18 -080027import android.graphics.Rect;
Mady Mellorb0a82462016-04-30 17:31:02 -070028import android.graphics.drawable.Drawable;
Julia Reynoldsfc640012018-02-21 12:25:27 -050029import android.util.ArraySet;
Selim Cinek90c8f472015-11-10 17:44:39 -050030import android.util.AttributeSet;
Selim Cinek7b836392015-12-04 20:02:59 -080031import android.widget.ImageView;
Selim Cinek90c8f472015-11-10 17:44:39 -050032import android.widget.RemoteViews;
33
Anthony Chen0f6e96c2017-04-07 15:48:17 -070034import com.android.internal.R;
Selim Cinek0242fbb2016-10-19 13:38:32 -070035import com.android.internal.widget.CachingIconView;
36
Selim Cinekaef6c762015-11-20 17:00:18 -080037import java.util.ArrayList;
Selim Cinek90c8f472015-11-10 17:44:39 -050038
39/**
40 * A header of a notification view
41 *
42 * @hide
43 */
44@RemoteViews.RemoteView
Selim Cinekcb445682016-01-29 16:13:12 -080045public class NotificationHeaderView extends ViewGroup {
Selim Cinek99104832017-01-25 14:47:33 -080046 public static final int NO_COLOR = Notification.COLOR_INVALID;
Selim Cinek413142a2016-02-03 10:58:13 -080047 private final int mChildMinWidth;
Selim Cinek6ecc8102016-01-26 18:26:19 -080048 private final int mContentEndMargin;
Lucas Dupinbd9798f2017-10-24 18:04:51 -070049 private final int mGravity;
Selim Cinek90c8f472015-11-10 17:44:39 -050050 private View mAppName;
Selim Cinek0f9dd1e2016-04-05 17:03:40 -070051 private View mHeaderText;
Selim Cinekafeed292017-12-12 17:32:44 -080052 private View mSecondaryHeaderText;
Selim Cinekaef6c762015-11-20 17:00:18 -080053 private OnClickListener mExpandClickListener;
Julia Reynoldsb5867452018-02-28 16:31:35 -050054 private OnClickListener mAppOpsListener;
Selim Cinekaef6c762015-11-20 17:00:18 -080055 private HeaderTouchListener mTouchListener = new HeaderTouchListener();
Selim Cinek7b836392015-12-04 20:02:59 -080056 private ImageView mExpandButton;
Selim Cinek0242fbb2016-10-19 13:38:32 -070057 private CachingIconView mIcon;
Selim Cinekc848c3a2016-01-13 15:27:30 -080058 private View mProfileBadge;
Julia Reynoldsfc640012018-02-21 12:25:27 -050059 private View mOverlayIcon;
60 private View mCameraIcon;
61 private View mMicIcon;
62 private View mAppOps;
Selim Cinekea4bef72015-12-02 15:51:10 -080063 private int mIconColor;
64 private int mOriginalNotificationColor;
Selim Cinek7b836392015-12-04 20:02:59 -080065 private boolean mExpanded;
Anthony Chen0f6e96c2017-04-07 15:48:17 -070066 private boolean mShowExpandButtonAtEnd;
Selim Cinek6ecc8102016-01-26 18:26:19 -080067 private boolean mShowWorkBadgeAtEnd;
Mady Mellorb0a82462016-04-30 17:31:02 -070068 private Drawable mBackground;
Anthony Chen0f6e96c2017-04-07 15:48:17 -070069 private boolean mEntireHeaderClickable;
Selim Cinek499c20f2017-07-20 14:06:09 -070070 private boolean mExpandOnlyOnButton;
Anthony Chen0f6e96c2017-04-07 15:48:17 -070071 private boolean mAcceptAllTouches;
Lucas Dupinbd9798f2017-10-24 18:04:51 -070072 private int mTotalWidth;
Mady Mellorb0a82462016-04-30 17:31:02 -070073
74 ViewOutlineProvider mProvider = new ViewOutlineProvider() {
75 @Override
76 public void getOutline(View view, Outline outline) {
77 if (mBackground != null) {
Selim Cinekafeed292017-12-12 17:32:44 -080078 outline.setRect(0, 0, getWidth(), getHeight());
Mady Mellorb0a82462016-04-30 17:31:02 -070079 outline.setAlpha(1f);
80 }
81 }
82 };
Selim Cinek90c8f472015-11-10 17:44:39 -050083
84 public NotificationHeaderView(Context context) {
85 this(context, null);
86 }
87
88 public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
89 this(context, attrs, 0);
90 }
91
92 public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
93 this(context, attrs, defStyleAttr, 0);
94 }
95
96 public NotificationHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
97 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen0f6e96c2017-04-07 15:48:17 -070098 Resources res = getResources();
99 mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
100 mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700101 mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand);
Lucas Dupinbd9798f2017-10-24 18:04:51 -0700102
103 int[] attrIds = { android.R.attr.gravity };
104 TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes);
105 mGravity = ta.getInt(0, 0);
106 ta.recycle();
Selim Cinek90c8f472015-11-10 17:44:39 -0500107 }
108
109 @Override
110 protected void onFinishInflate() {
111 super.onFinishInflate();
112 mAppName = findViewById(com.android.internal.R.id.app_name_text);
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700113 mHeaderText = findViewById(com.android.internal.R.id.header_text);
Selim Cinekafeed292017-12-12 17:32:44 -0800114 mSecondaryHeaderText = findViewById(com.android.internal.R.id.header_text_secondary);
Alan Viverette51efddb2017-04-05 10:00:01 -0400115 mExpandButton = findViewById(com.android.internal.R.id.expand_button);
116 mIcon = findViewById(com.android.internal.R.id.icon);
Selim Cinekc848c3a2016-01-13 15:27:30 -0800117 mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
Julia Reynoldsfc640012018-02-21 12:25:27 -0500118 mCameraIcon = findViewById(com.android.internal.R.id.camera);
119 mMicIcon = findViewById(com.android.internal.R.id.mic);
120 mOverlayIcon = findViewById(com.android.internal.R.id.overlay);
121 mAppOps = findViewById(com.android.internal.R.id.app_ops);
Selim Cinek90c8f472015-11-10 17:44:39 -0500122 }
123
124 @Override
125 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
126 final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
127 final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
128 int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
129 MeasureSpec.AT_MOST);
130 int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
131 MeasureSpec.AT_MOST);
132 int totalWidth = getPaddingStart() + getPaddingEnd();
133 for (int i = 0; i < getChildCount(); i++) {
134 final View child = getChildAt(i);
135 if (child.getVisibility() == GONE) {
136 // We'll give it the rest of the space in the end
137 continue;
138 }
139 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
140 int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec,
141 lp.leftMargin + lp.rightMargin, lp.width);
142 int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
143 lp.topMargin + lp.bottomMargin, lp.height);
144 child.measure(childWidthSpec, childHeightSpec);
145 totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
146 }
147 if (totalWidth > givenWidth) {
148 int overFlow = totalWidth - givenWidth;
Selim Cinek0f9dd1e2016-04-05 17:03:40 -0700149 // We are overflowing, lets shrink the app name first
Selim Cinekafeed292017-12-12 17:32:44 -0800150 overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName,
151 mChildMinWidth);
152
153 // still overflowing, we shrink the header text
154 overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0);
155
156 // still overflowing, finally we shrink the secondary header text
157 shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
158 0);
Selim Cinek90c8f472015-11-10 17:44:39 -0500159 }
Lucas Dupinbd9798f2017-10-24 18:04:51 -0700160 mTotalWidth = Math.min(totalWidth, givenWidth);
Selim Cinek4c4c7382016-02-03 16:17:09 -0800161 setMeasuredDimension(givenWidth, givenHeight);
Selim Cinek90c8f472015-11-10 17:44:39 -0500162 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800163
Selim Cinekafeed292017-12-12 17:32:44 -0800164 private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
165 int minimumWidth) {
166 final int oldWidth = targetView.getMeasuredWidth();
167 if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
168 // we're still too big
169 int newSize = Math.max(minimumWidth, oldWidth - overFlow);
170 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
171 targetView.measure(childWidthSpec, heightSpec);
172 overFlow -= oldWidth - newSize;
173 }
174 return overFlow;
175 }
176
Selim Cinekaef6c762015-11-20 17:00:18 -0800177 @Override
178 protected void onLayout(boolean changed, int l, int t, int r, int b) {
Selim Cinekcb445682016-01-29 16:13:12 -0800179 int left = getPaddingStart();
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700180 int end = getMeasuredWidth();
Lucas Dupinbd9798f2017-10-24 18:04:51 -0700181 final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
182 if (centerAligned) {
183 left += getMeasuredWidth() / 2 - mTotalWidth / 2;
184 }
Selim Cinekcb445682016-01-29 16:13:12 -0800185 int childCount = getChildCount();
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700186 int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
Selim Cinekcb445682016-01-29 16:13:12 -0800187 for (int i = 0; i < childCount; i++) {
188 View child = getChildAt(i);
189 if (child.getVisibility() == GONE) {
190 continue;
191 }
192 int childHeight = child.getMeasuredHeight();
193 MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
194 left += params.getMarginStart();
195 int right = left + child.getMeasuredWidth();
196 int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
197 int bottom = top + childHeight;
198 int layoutLeft = left;
199 int layoutRight = right;
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700200 if (child == mExpandButton && mShowExpandButtonAtEnd) {
201 layoutRight = end - mContentEndMargin;
202 end = layoutLeft = layoutRight - child.getMeasuredWidth();
203 }
Selim Cinekcb445682016-01-29 16:13:12 -0800204 if (child == mProfileBadge) {
205 int paddingEnd = getPaddingEnd();
206 if (mShowWorkBadgeAtEnd) {
207 paddingEnd = mContentEndMargin;
208 }
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700209 layoutRight = end - paddingEnd;
210 end = layoutLeft = layoutRight - child.getMeasuredWidth();
Selim Cinek6ecc8102016-01-26 18:26:19 -0800211 }
Julia Reynoldsfc640012018-02-21 12:25:27 -0500212 if (child == mAppOps) {
213 int paddingEnd = mContentEndMargin;
214 layoutRight = end - paddingEnd;
215 end = layoutLeft = layoutRight - child.getMeasuredWidth();
216 }
Selim Cinekc848c3a2016-01-13 15:27:30 -0800217 if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
Selim Cinekcb445682016-01-29 16:13:12 -0800218 int ltrLeft = layoutLeft;
219 layoutLeft = getWidth() - layoutRight;
220 layoutRight = getWidth() - ltrLeft;
Selim Cinekc848c3a2016-01-13 15:27:30 -0800221 }
Selim Cinekcb445682016-01-29 16:13:12 -0800222 child.layout(layoutLeft, top, layoutRight, bottom);
223 left = right + params.getMarginEnd();
Selim Cinekc848c3a2016-01-13 15:27:30 -0800224 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800225 updateTouchListener();
226 }
227
Selim Cinekcb445682016-01-29 16:13:12 -0800228 @Override
229 public LayoutParams generateLayoutParams(AttributeSet attrs) {
230 return new ViewGroup.MarginLayoutParams(getContext(), attrs);
231 }
232
Mady Mellorb0a82462016-04-30 17:31:02 -0700233 /**
234 * Set a {@link Drawable} to be displayed as a background on the header.
235 */
236 public void setHeaderBackgroundDrawable(Drawable drawable) {
237 if (drawable != null) {
238 setWillNotDraw(false);
239 mBackground = drawable;
240 mBackground.setCallback(this);
241 setOutlineProvider(mProvider);
242 } else {
243 setWillNotDraw(true);
244 mBackground = null;
245 setOutlineProvider(null);
246 }
247 invalidate();
248 }
249
250 @Override
251 protected void onDraw(Canvas canvas) {
252 if (mBackground != null) {
Selim Cinekafeed292017-12-12 17:32:44 -0800253 mBackground.setBounds(0, 0, getWidth(), getHeight());
Mady Mellorb0a82462016-04-30 17:31:02 -0700254 mBackground.draw(canvas);
255 }
256 }
257
258 @Override
259 protected boolean verifyDrawable(Drawable who) {
260 return super.verifyDrawable(who) || who == mBackground;
261 }
262
263 @Override
264 protected void drawableStateChanged() {
265 if (mBackground != null && mBackground.isStateful()) {
266 mBackground.setState(getDrawableState());
267 }
268 }
269
Selim Cinekaef6c762015-11-20 17:00:18 -0800270 private void updateTouchListener() {
Julia Reynoldsb5867452018-02-28 16:31:35 -0500271 if (mExpandClickListener == null && mAppOpsListener == null) {
272 setOnTouchListener(null);
273 return;
Selim Cinekaef6c762015-11-20 17:00:18 -0800274 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500275 setOnTouchListener(mTouchListener);
276 mTouchListener.bindTouchRects();
277 }
278
279 /**
280 * Sets onclick listener for app ops icons.
281 */
282 public void setAppOpsOnClickListener(OnClickListener l) {
283 mAppOpsListener = l;
284 mAppOps.setOnClickListener(mAppOpsListener);
285 updateTouchListener();
Selim Cinekaef6c762015-11-20 17:00:18 -0800286 }
287
288 @Override
289 public void setOnClickListener(@Nullable OnClickListener l) {
290 mExpandClickListener = l;
Selim Cineke9bad242016-06-15 11:46:37 -0700291 mExpandButton.setOnClickListener(mExpandClickListener);
Selim Cinekaef6c762015-11-20 17:00:18 -0800292 updateTouchListener();
293 }
294
Selim Cinekea4bef72015-12-02 15:51:10 -0800295 @RemotableViewMethod
296 public void setOriginalIconColor(int color) {
297 mIconColor = color;
298 }
299
300 public int getOriginalIconColor() {
301 return mIconColor;
302 }
303
304 @RemotableViewMethod
305 public void setOriginalNotificationColor(int color) {
306 mOriginalNotificationColor = color;
307 }
308
309 public int getOriginalNotificationColor() {
310 return mOriginalNotificationColor;
311 }
312
Selim Cinek7b836392015-12-04 20:02:59 -0800313 @RemotableViewMethod
314 public void setExpanded(boolean expanded) {
315 mExpanded = expanded;
316 updateExpandButton();
317 }
318
Julia Reynoldsfc640012018-02-21 12:25:27 -0500319 /**
320 * Shows or hides 'app op in use' icons based on app usage.
321 */
322 public void showAppOpsIcons(ArraySet<Integer> appOps) {
Julia Reynoldsb5867452018-02-28 16:31:35 -0500323 if (mOverlayIcon == null || mCameraIcon == null || mMicIcon == null || appOps == null) {
Julia Reynoldsfc640012018-02-21 12:25:27 -0500324 return;
325 }
326
327 mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
328 ? View.VISIBLE : View.GONE);
329 mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA)
330 ? View.VISIBLE : View.GONE);
331 mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO)
332 ? View.VISIBLE : View.GONE);
333 }
334
Selim Cinek7b836392015-12-04 20:02:59 -0800335 private void updateExpandButton() {
336 int drawableId;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800337 int contentDescriptionId;
Selim Cinek6db57582016-03-04 19:11:27 -0800338 if (mExpanded) {
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700339 drawableId = R.drawable.ic_collapse_notification;
340 contentDescriptionId = R.string.expand_button_content_description_expanded;
Selim Cinek7b836392015-12-04 20:02:59 -0800341 } else {
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700342 drawableId = R.drawable.ic_expand_notification;
343 contentDescriptionId = R.string.expand_button_content_description_collapsed;
Selim Cinek7b836392015-12-04 20:02:59 -0800344 }
345 mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
346 mExpandButton.setColorFilter(mOriginalNotificationColor);
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800347 mExpandButton.setContentDescription(mContext.getText(contentDescriptionId));
Selim Cinek7b836392015-12-04 20:02:59 -0800348 }
349
Selim Cinek6ecc8102016-01-26 18:26:19 -0800350 public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
351 if (showWorkBadgeAtEnd != mShowWorkBadgeAtEnd) {
352 setClipToPadding(!showWorkBadgeAtEnd);
353 mShowWorkBadgeAtEnd = showWorkBadgeAtEnd;
354 }
355 }
356
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700357 /**
358 * Sets whether or not the expand button appears at the end of the NotificationHeaderView. If
359 * both this and {@link #setShowWorkBadgeAtEnd(boolean)} have been set to true, then the
360 * expand button will appear closer to the end than the work badge.
361 */
362 public void setShowExpandButtonAtEnd(boolean showExpandButtonAtEnd) {
363 if (showExpandButtonAtEnd != mShowExpandButtonAtEnd) {
364 setClipToPadding(!showExpandButtonAtEnd);
365 mShowExpandButtonAtEnd = showExpandButtonAtEnd;
366 }
367 }
368
Selim Cinek0d07c7e2016-01-27 18:38:31 -0800369 public View getWorkProfileIcon() {
370 return mProfileBadge;
371 }
372
Selim Cinek0242fbb2016-10-19 13:38:32 -0700373 public CachingIconView getIcon() {
Selim Cinek281c2022016-10-13 19:14:43 -0700374 return mIcon;
375 }
376
Selim Cinekaef6c762015-11-20 17:00:18 -0800377 public class HeaderTouchListener implements View.OnTouchListener {
378
379 private final ArrayList<Rect> mTouchRects = new ArrayList<>();
Selim Cinek499c20f2017-07-20 14:06:09 -0700380 private Rect mExpandButtonRect;
Julia Reynoldsb5867452018-02-28 16:31:35 -0500381 private Rect mAppOpsRect;
Selim Cinekaef6c762015-11-20 17:00:18 -0800382 private int mTouchSlop;
383 private boolean mTrackGesture;
384 private float mDownX;
385 private float mDownY;
386
387 public HeaderTouchListener() {
388 }
389
390 public void bindTouchRects() {
391 mTouchRects.clear();
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700392 addRectAroundView(mIcon);
Selim Cinek499c20f2017-07-20 14:06:09 -0700393 mExpandButtonRect = addRectAroundView(mExpandButton);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500394 mAppOpsRect = addRectAroundView(mAppOps);
Selim Cinek4c4c7382016-02-03 16:17:09 -0800395 addWidthRect();
Selim Cinekaef6c762015-11-20 17:00:18 -0800396 mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
397 }
398
Selim Cinek4c4c7382016-02-03 16:17:09 -0800399 private void addWidthRect() {
400 Rect r = new Rect();
Selim Cinekaef6c762015-11-20 17:00:18 -0800401 r.top = 0;
402 r.bottom = (int) (32 * getResources().getDisplayMetrics().density);
Selim Cinek4c4c7382016-02-03 16:17:09 -0800403 r.left = 0;
404 r.right = getWidth();
Selim Cinekaef6c762015-11-20 17:00:18 -0800405 mTouchRects.add(r);
406 }
407
Selim Cinek499c20f2017-07-20 14:06:09 -0700408 private Rect addRectAroundView(View view) {
Selim Cinekaef6c762015-11-20 17:00:18 -0800409 final Rect r = getRectAroundView(view);
410 mTouchRects.add(r);
Selim Cinek499c20f2017-07-20 14:06:09 -0700411 return r;
Selim Cinekaef6c762015-11-20 17:00:18 -0800412 }
413
414 private Rect getRectAroundView(View view) {
415 float size = 48 * getResources().getDisplayMetrics().density;
Julia Reynoldsb5867452018-02-28 16:31:35 -0500416 float width = Math.max(size, view.getWidth());
417 float height = Math.max(size, view.getHeight());
Selim Cinekaef6c762015-11-20 17:00:18 -0800418 final Rect r = new Rect();
419 if (view.getVisibility() == GONE) {
420 view = getFirstChildNotGone();
Julia Reynoldsb5867452018-02-28 16:31:35 -0500421 r.left = (int) (view.getLeft() - width / 2.0f);
Selim Cinekaef6c762015-11-20 17:00:18 -0800422 } else {
Julia Reynoldsb5867452018-02-28 16:31:35 -0500423 r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - width / 2.0f);
Selim Cinekaef6c762015-11-20 17:00:18 -0800424 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500425 r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - height / 2.0f);
426 r.bottom = (int) (r.top + height);
427 r.right = (int) (r.left + width);
Selim Cinekaef6c762015-11-20 17:00:18 -0800428 return r;
429 }
430
431 @Override
432 public boolean onTouch(View v, MotionEvent event) {
433 float x = event.getX();
434 float y = event.getY();
435 switch (event.getActionMasked() & MotionEvent.ACTION_MASK) {
436 case MotionEvent.ACTION_DOWN:
437 mTrackGesture = false;
438 if (isInside(x, y)) {
Selim Cinek1b554392017-02-28 17:22:49 -0800439 mDownX = x;
440 mDownY = y;
Selim Cinekaef6c762015-11-20 17:00:18 -0800441 mTrackGesture = true;
442 return true;
443 }
444 break;
445 case MotionEvent.ACTION_MOVE:
446 if (mTrackGesture) {
447 if (Math.abs(mDownX - x) > mTouchSlop
448 || Math.abs(mDownY - y) > mTouchSlop) {
449 mTrackGesture = false;
450 }
451 }
452 break;
453 case MotionEvent.ACTION_UP:
454 if (mTrackGesture) {
Julia Reynoldsb5867452018-02-28 16:31:35 -0500455 if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y)
456 || mAppOpsRect.contains((int) mDownX, (int) mDownY))) {
457 mAppOps.performClick();
458 return true;
459 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800460 mExpandButton.performClick();
Selim Cinekaef6c762015-11-20 17:00:18 -0800461 }
462 break;
463 }
464 return mTrackGesture;
465 }
466
467 private boolean isInside(float x, float y) {
Selim Cinek1b554392017-02-28 17:22:49 -0800468 if (mAcceptAllTouches) {
469 return true;
470 }
Selim Cinek499c20f2017-07-20 14:06:09 -0700471 if (mExpandOnlyOnButton) {
472 return mExpandButtonRect.contains((int) x, (int) y);
473 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800474 for (int i = 0; i < mTouchRects.size(); i++) {
475 Rect r = mTouchRects.get(i);
476 if (r.contains((int) x, (int) y)) {
Selim Cinekaef6c762015-11-20 17:00:18 -0800477 return true;
478 }
479 }
480 return false;
481 }
482 }
483
484 private View getFirstChildNotGone() {
485 for (int i = 0; i < getChildCount(); i++) {
486 final View child = getChildAt(i);
487 if (child.getVisibility() != GONE) {
488 return child;
489 }
490 }
491 return this;
492 }
Selim Cinek4ffd6362015-12-29 15:12:23 +0100493
494 public ImageView getExpandButton() {
495 return mExpandButton;
496 }
497
498 @Override
499 public boolean hasOverlappingRendering() {
500 return false;
501 }
Selim Cinek6183d122016-01-14 18:48:41 -0800502
503 public boolean isInTouchRect(float x, float y) {
504 if (mExpandClickListener == null) {
505 return false;
506 }
507 return mTouchListener.isInside(x, y);
508 }
Selim Cinek1b554392017-02-28 17:22:49 -0800509
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700510 /**
511 * Sets whether or not all touches to this header view will register as a click. Note that
512 * if the config value for {@code config_notificationHeaderClickableForExpand} is {@code true},
513 * then calling this method with {@code false} will not override that configuration.
514 */
Selim Cinek1b554392017-02-28 17:22:49 -0800515 @RemotableViewMethod
516 public void setAcceptAllTouches(boolean acceptAllTouches) {
Anthony Chen0f6e96c2017-04-07 15:48:17 -0700517 mAcceptAllTouches = mEntireHeaderClickable || acceptAllTouches;
Selim Cinek1b554392017-02-28 17:22:49 -0800518 }
Selim Cinek499c20f2017-07-20 14:06:09 -0700519
520 /**
521 * Sets whether only the expand icon itself should serve as the expand target.
522 */
523 @RemotableViewMethod
524 public void setExpandOnlyOnButton(boolean expandOnlyOnButton) {
525 mExpandOnlyOnButton = expandOnlyOnButton;
526 }
Selim Cinek90c8f472015-11-10 17:44:39 -0500527}