blob: 46cd7aea9f6369cb15e5443eac5cfbf1afe72a96 [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;
Selim Cinekaef6c762015-11-20 17:00:18 -080020import android.app.Notification;
Selim Cinek90c8f472015-11-10 17:44:39 -050021import android.content.Context;
Selim Cinekaef6c762015-11-20 17:00:18 -080022import android.graphics.Rect;
Selim Cinek90c8f472015-11-10 17:44:39 -050023import android.util.AttributeSet;
24import android.widget.LinearLayout;
25import android.widget.RemoteViews;
26
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
35public class NotificationHeaderView extends LinearLayout {
36 private final int mHeaderMinWidth;
37 private View mAppName;
38 private View mSubTextView;
Selim Cinekaef6c762015-11-20 17:00:18 -080039 private OnClickListener mExpandClickListener;
40 private HeaderTouchListener mTouchListener = new HeaderTouchListener();
41 private View mExpandButton;
42 private View mIcon;
Selim Cinek90c8f472015-11-10 17:44:39 -050043
44 public NotificationHeaderView(Context context) {
45 this(context, null);
46 }
47
48 public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
49 this(context, attrs, 0);
50 }
51
52 public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
53 this(context, attrs, defStyleAttr, 0);
54 }
55
56 public NotificationHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
57 super(context, attrs, defStyleAttr, defStyleRes);
58 mHeaderMinWidth = getResources().getDimensionPixelSize(
59 com.android.internal.R.dimen.notification_header_shrink_min_width);
60 }
61
62 @Override
63 protected void onFinishInflate() {
64 super.onFinishInflate();
65 mAppName = findViewById(com.android.internal.R.id.app_name_text);
66 mSubTextView = findViewById(com.android.internal.R.id.header_sub_text);
Selim Cinekaef6c762015-11-20 17:00:18 -080067 mExpandButton = findViewById(com.android.internal.R.id.expand_button);
68 mIcon = findViewById(com.android.internal.R.id.icon);
Selim Cinek90c8f472015-11-10 17:44:39 -050069 }
70
71 @Override
72 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
73 final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
74 final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
75 int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
76 MeasureSpec.AT_MOST);
77 int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
78 MeasureSpec.AT_MOST);
79 int totalWidth = getPaddingStart() + getPaddingEnd();
80 for (int i = 0; i < getChildCount(); i++) {
81 final View child = getChildAt(i);
82 if (child.getVisibility() == GONE) {
83 // We'll give it the rest of the space in the end
84 continue;
85 }
86 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
87 int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec,
88 lp.leftMargin + lp.rightMargin, lp.width);
89 int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
90 lp.topMargin + lp.bottomMargin, lp.height);
91 child.measure(childWidthSpec, childHeightSpec);
92 totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
93 }
94 if (totalWidth > givenWidth) {
95 int overFlow = totalWidth - givenWidth;
96 // We are overflowing, lets shrink
97 final int appWidth = mAppName.getMeasuredWidth();
98 if (appWidth > mHeaderMinWidth) {
99 int newSize = appWidth - Math.min(appWidth - mHeaderMinWidth, overFlow);
100 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
101 mAppName.measure(childWidthSpec, wrapContentHeightSpec);
102 overFlow -= appWidth - newSize;
103 }
104 if (overFlow > 0 && mSubTextView.getVisibility() != GONE) {
105 // we're still too big
106 final int subTextWidth = mSubTextView.getMeasuredWidth();
107 int newSize = Math.max(0, subTextWidth - overFlow);
108 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
109 mSubTextView.measure(childWidthSpec, wrapContentHeightSpec);
110 }
111 totalWidth = givenWidth;
112 }
113 setMeasuredDimension(totalWidth, givenHeight);
114 }
Selim Cinekaef6c762015-11-20 17:00:18 -0800115
116 @Override
117 protected void onLayout(boolean changed, int l, int t, int r, int b) {
118 super.onLayout(changed, l, t, r, b);
119 updateTouchListener();
120 }
121
122 private void updateTouchListener() {
123 if (mExpandClickListener != null) {
124 mTouchListener.bindTouchRects();
125 }
126 }
127
128 @Override
129 public void setOnClickListener(@Nullable OnClickListener l) {
130 mExpandClickListener = l;
131 setOnTouchListener(mExpandClickListener != null ? mTouchListener : null);
132 updateTouchListener();
133 }
134
135 public class HeaderTouchListener implements View.OnTouchListener {
136
137 private final ArrayList<Rect> mTouchRects = new ArrayList<>();
138 private int mTouchSlop;
139 private boolean mTrackGesture;
140 private float mDownX;
141 private float mDownY;
142
143 public HeaderTouchListener() {
144 }
145
146 public void bindTouchRects() {
147 mTouchRects.clear();
148 addRectAroundViewView(mIcon);
149 addRectAroundViewView(mExpandButton);
150 addInBetweenRect();
151 mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
152 }
153
154 private void addInBetweenRect() {
155 final Rect r = new Rect();
156 r.top = 0;
157 r.bottom = (int) (32 * getResources().getDisplayMetrics().density);
158 Rect leftRect = mTouchRects.get(0);
159 r.left = leftRect.right;
160 Rect rightRect = mTouchRects.get(1);
161 r.right = rightRect.left;
162 mTouchRects.add(r);
163 }
164
165 private void addRectAroundViewView(View view) {
166 final Rect r = getRectAroundView(view);
167 mTouchRects.add(r);
168 }
169
170 private Rect getRectAroundView(View view) {
171 float size = 48 * getResources().getDisplayMetrics().density;
172 final Rect r = new Rect();
173 if (view.getVisibility() == GONE) {
174 view = getFirstChildNotGone();
175 r.left = (int) (view.getLeft() - size / 2.0f);
176 } else {
177 r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - size / 2.0f);
178 }
179 r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - size / 2.0f);
180 r.bottom = (int) (r.top + size);
181 r.right = (int) (r.left + size);
182 return r;
183 }
184
185 @Override
186 public boolean onTouch(View v, MotionEvent event) {
187 float x = event.getX();
188 float y = event.getY();
189 switch (event.getActionMasked() & MotionEvent.ACTION_MASK) {
190 case MotionEvent.ACTION_DOWN:
191 mTrackGesture = false;
192 if (isInside(x, y)) {
193 mTrackGesture = true;
194 return true;
195 }
196 break;
197 case MotionEvent.ACTION_MOVE:
198 if (mTrackGesture) {
199 if (Math.abs(mDownX - x) > mTouchSlop
200 || Math.abs(mDownY - y) > mTouchSlop) {
201 mTrackGesture = false;
202 }
203 }
204 break;
205 case MotionEvent.ACTION_UP:
206 if (mTrackGesture) {
207 mExpandClickListener.onClick(NotificationHeaderView.this);
208 }
209 break;
210 }
211 return mTrackGesture;
212 }
213
214 private boolean isInside(float x, float y) {
215 for (int i = 0; i < mTouchRects.size(); i++) {
216 Rect r = mTouchRects.get(i);
217 if (r.contains((int) x, (int) y)) {
218 mDownX = x;
219 mDownY = y;
220 return true;
221 }
222 }
223 return false;
224 }
225 }
226
227 private View getFirstChildNotGone() {
228 for (int i = 0; i < getChildCount(); i++) {
229 final View child = getChildAt(i);
230 if (child.getVisibility() != GONE) {
231 return child;
232 }
233 }
234 return this;
235 }
Selim Cinek90c8f472015-11-10 17:44:39 -0500236}