blob: 647f0bfd0e264a7de6e44813a086d2417edf6e34 [file] [log] [blame]
Jorim Jaggibe565df2014-04-28 17:51:23 +02001/*
2 * Copyright (C) 2014 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 com.android.systemui.statusbar;
18
Adrian Roosb88b1a12015-12-09 18:51:05 -080019import android.app.Notification;
20import android.app.RemoteInput;
Jorim Jaggibe565df2014-04-28 17:51:23 +020021import android.content.Context;
22import android.graphics.Rect;
Selim Cinek860b6da2015-12-16 19:02:19 -080023import android.os.Build;
Selim Cinek83bc7832015-10-22 13:26:54 -070024import android.service.notification.StatusBarNotification;
Jorim Jaggibe565df2014-04-28 17:51:23 +020025import android.util.AttributeSet;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080026import android.view.NotificationHeaderView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020027import android.view.View;
Selim Cinek8d490d42015-04-10 00:05:50 -070028import android.view.ViewGroup;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010029import android.view.ViewTreeObserver;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020030import android.widget.FrameLayout;
Selim Cinek8d490d42015-04-10 00:05:50 -070031
Jorim Jaggibe565df2014-04-28 17:51:23 +020032import com.android.systemui.R;
Selim Cinek83bc7832015-10-22 13:26:54 -070033import com.android.systemui.statusbar.notification.HybridNotificationView;
34import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
Selim Cinek0ffbda62016-01-01 20:29:12 +010035import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
36import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070037import com.android.systemui.statusbar.phone.NotificationGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080038import com.android.systemui.statusbar.policy.RemoteInputView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020039
40/**
Selim Cinek684a4422015-04-15 16:18:39 -070041 * A frame layout containing the actual payload of the notification, including the contracted,
42 * expanded and heads up layout. This class is responsible for clipping the content and and
43 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020044 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020045public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020046
Selim Cinek684a4422015-04-15 16:18:39 -070047 private static final int VISIBLE_TYPE_CONTRACTED = 0;
48 private static final int VISIBLE_TYPE_EXPANDED = 1;
49 private static final int VISIBLE_TYPE_HEADSUP = 2;
Selim Cinek83bc7832015-10-22 13:26:54 -070050 private static final int VISIBLE_TYPE_SINGLELINE = 3;
Jorim Jaggi11298832014-05-24 16:18:38 +020051
Jorim Jaggibe565df2014-04-28 17:51:23 +020052 private final Rect mClipBounds = new Rect();
Selim Cinek860b6da2015-12-16 19:02:19 -080053 private final int mMinContractedHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -080054 private final int mNotificationContentMarginEnd;
Selim Cinek30e387d2016-01-11 18:01:47 -080055 private final OnLayoutChangeListener mLayoutUpdater = new OnLayoutChangeListener() {
56 @Override
57 public void onLayoutChange(View v, int left, int top, int right, int bottom,
58 int oldLeft,
59 int oldTop, int oldRight, int oldBottom) {
60 selectLayout(false /* animate */, false /* force */);
61 }
62 };
Selim Cinek860b6da2015-12-16 19:02:19 -080063
Jorim Jaggibe565df2014-04-28 17:51:23 +020064
65 private View mContractedChild;
66 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070067 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070068 private HybridNotificationView mSingleLineView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020069
Jorim Jaggi4e857f42014-11-17 19:14:04 +010070 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070071 private NotificationViewWrapper mExpandedWrapper;
72 private NotificationViewWrapper mHeadsUpWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070073 private HybridNotificationViewManager mHybridViewManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020074 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +010075 private int mContentHeight;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070076 private int mUnrestrictedContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -070077 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -040078 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010079 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -070080 private boolean mIsHeadsUp;
Jorim Jaggi59ec3042015-06-05 15:18:43 -070081 private boolean mShowingLegacyBackground;
Selim Cinek83bc7832015-10-22 13:26:54 -070082 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -080083 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -080084 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -080085 private int mNotificationMaxHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -070086 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070087 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080088 private RemoteInputController mRemoteInputController;
Jorim Jaggi59ec3042015-06-05 15:18:43 -070089
Jorim Jaggibe4116a2015-05-20 20:04:08 -070090 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010091 = new ViewTreeObserver.OnPreDrawListener() {
92 @Override
93 public boolean onPreDraw() {
94 mAnimate = true;
95 getViewTreeObserver().removeOnPreDrawListener(this);
96 return true;
97 }
98 };
Jorim Jaggi11298832014-05-24 16:18:38 +020099
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800100 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800101 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800102 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100103 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800104 private ExpandableNotificationRow mContainingNotification;
Selim Cinek98be3f32015-12-17 16:39:06 -0800105
Jorim Jaggibe565df2014-04-28 17:51:23 +0200106 public NotificationContentView(Context context, AttributeSet attrs) {
107 super(context, attrs);
Selim Cinek83bc7832015-10-22 13:26:54 -0700108 mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
Selim Cinek860b6da2015-12-16 19:02:19 -0800109 mMinContractedHeight = getResources().getDimensionPixelSize(
110 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800111 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
112 com.android.internal.R.dimen.notification_content_margin_end);
Selim Cinek1a521f32014-11-03 17:39:29 +0100113 reset(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200114 }
115
Selim Cinekd84a5932015-12-15 11:45:36 -0800116 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800117 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800118 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800119 mNotificationMaxHeight = maxHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800120 }
121
Jorim Jaggibe565df2014-04-28 17:51:23 +0200122 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700123 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
124 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
125 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
126 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
127 int maxSize = Integer.MAX_VALUE;
128 if (hasFixedHeight || isHeightLimited) {
129 maxSize = MeasureSpec.getSize(heightMeasureSpec);
130 }
131 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800132 if (mExpandedChild != null) {
133 int size = Math.min(maxSize, mNotificationMaxHeight);
134 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
135 if (layoutParams.height >= 0) {
136 // An actual height is set
137 size = Math.min(maxSize, layoutParams.height);
138 }
139 int spec = size == Integer.MAX_VALUE
140 ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
141 : MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
142 mExpandedChild.measure(widthMeasureSpec, spec);
143 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
144 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700145 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800146 int heightSpec;
Selim Cinekf619ffc2016-02-17 14:53:05 -0800147 int size = Math.min(maxSize, mSmallHeight);
Selim Cinek860b6da2015-12-16 19:02:19 -0800148 if (shouldContractedBeFixedSize()) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800149 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
150 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800151 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800152 }
153 mContractedChild.measure(widthMeasureSpec, heightSpec);
154 int measuredHeight = mContractedChild.getMeasuredHeight();
155 if (measuredHeight < mMinContractedHeight) {
156 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
157 mContractedChild.measure(widthMeasureSpec, heightSpec);
158 }
159 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800160 if (updateContractedHeaderWidth()) {
161 mContractedChild.measure(widthMeasureSpec, heightSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700162 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700163 }
164 if (mHeadsUpChild != null) {
165 int size = Math.min(maxSize, mHeadsUpHeight);
166 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
167 if (layoutParams.height >= 0) {
168 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800169 size = Math.min(size, layoutParams.height);
Selim Cinek8d490d42015-04-10 00:05:50 -0700170 }
171 mHeadsUpChild.measure(widthMeasureSpec,
172 MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
173 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
174 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700175 if (mSingleLineView != null) {
Selim Cinek83bc7832015-10-22 13:26:54 -0700176 mSingleLineView.measure(widthMeasureSpec,
Selim Cinek7b836392015-12-04 20:02:59 -0800177 MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700178 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
179 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700180 int ownHeight = Math.min(maxChildHeight, maxSize);
181 int width = MeasureSpec.getSize(widthMeasureSpec);
182 setMeasuredDimension(width, ownHeight);
183 }
184
Selim Cinek6ecc8102016-01-26 18:26:19 -0800185 private boolean updateContractedHeaderWidth() {
186 // We need to update the expanded and the collapsed header to have exactly the same with to
187 // have the expand buttons laid out at the same location.
188 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
189 if (contractedHeader != null) {
190 if (mExpandedChild != null
191 && mExpandedWrapper.getNotificationHeader() != null) {
192 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
193 int expandedSize = expandedHeader.getMeasuredWidth()
194 - expandedHeader.getPaddingEnd();
195 int collapsedSize = contractedHeader.getMeasuredWidth()
196 - expandedHeader.getPaddingEnd();
197 if (expandedSize != collapsedSize) {
198 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
199 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800200 contractedHeader.isLayoutRtl()
201 ? paddingEnd
202 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800203 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800204 contractedHeader.isLayoutRtl()
205 ? contractedHeader.getPaddingLeft()
206 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800207 contractedHeader.getPaddingBottom());
208 contractedHeader.setShowWorkBadgeAtEnd(true);
209 return true;
210 }
211 } else {
212 int paddingEnd = mNotificationContentMarginEnd;
213 if (contractedHeader.getPaddingEnd() != paddingEnd) {
214 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800215 contractedHeader.isLayoutRtl()
216 ? paddingEnd
217 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800218 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800219 contractedHeader.isLayoutRtl()
220 ? contractedHeader.getPaddingLeft()
221 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800222 contractedHeader.getPaddingBottom());
223 contractedHeader.setShowWorkBadgeAtEnd(false);
224 return true;
225 }
226 }
227 }
228 return false;
229 }
230
Selim Cinek860b6da2015-12-16 19:02:19 -0800231 private boolean shouldContractedBeFixedSize() {
232 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
233 }
234
Selim Cinek8d490d42015-04-10 00:05:50 -0700235 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200236 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
237 super.onLayout(changed, left, top, right, bottom);
238 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700239 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200240 }
241
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100242 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100243 protected void onAttachedToWindow() {
244 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100245 updateVisibility();
246 }
247
Selim Cinek1a521f32014-11-03 17:39:29 +0100248 public void reset(boolean resetActualHeight) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200249 if (mContractedChild != null) {
250 mContractedChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800251 removeView(mContractedChild);
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200252 }
253 if (mExpandedChild != null) {
254 mExpandedChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800255 removeView(mExpandedChild);
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200256 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700257 if (mHeadsUpChild != null) {
258 mHeadsUpChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800259 removeView(mHeadsUpChild);
Selim Cinek8d490d42015-04-10 00:05:50 -0700260 }
Christoph Studera7fe6312014-06-27 19:32:44 +0200261 mContractedChild = null;
262 mExpandedChild = null;
Selim Cinek1f3f5442015-04-10 17:54:46 -0700263 mHeadsUpChild = null;
Selim Cinek684a4422015-04-15 16:18:39 -0700264 mVisibleType = VISIBLE_TYPE_CONTRACTED;
Selim Cinek1a521f32014-11-03 17:39:29 +0100265 if (resetActualHeight) {
Selim Cinekb5605e52015-02-20 18:21:41 +0100266 mContentHeight = mSmallHeight;
Selim Cinek1a521f32014-11-03 17:39:29 +0100267 }
Christoph Studera7fe6312014-06-27 19:32:44 +0200268 }
269
Selim Cinekcab4a602014-09-03 14:47:57 +0200270 public View getContractedChild() {
271 return mContractedChild;
272 }
273
274 public View getExpandedChild() {
275 return mExpandedChild;
276 }
277
Selim Cinek8d490d42015-04-10 00:05:50 -0700278 public View getHeadsUpChild() {
279 return mHeadsUpChild;
280 }
281
Jorim Jaggibe565df2014-04-28 17:51:23 +0200282 public void setContractedChild(View child) {
283 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200284 mContractedChild.animate().cancel();
Selim Cinek30e387d2016-01-11 18:01:47 -0800285 mContractedChild.removeOnLayoutChangeListener(mLayoutUpdater);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200286 removeView(mContractedChild);
287 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200288 addView(child);
289 mContractedChild = child;
Selim Cinek30e387d2016-01-11 18:01:47 -0800290 mContractedChild.addOnLayoutChangeListener(mLayoutUpdater);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100291 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
Jorim Jaggi11298832014-05-24 16:18:38 +0200292 selectLayout(false /* animate */, true /* force */);
Jorim Jaggi10ad7612014-12-08 18:41:11 +0100293 mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200294 }
295
296 public void setExpandedChild(View child) {
297 if (mExpandedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200298 mExpandedChild.animate().cancel();
Selim Cinek30e387d2016-01-11 18:01:47 -0800299 mExpandedChild.removeOnLayoutChangeListener(mLayoutUpdater);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200300 removeView(mExpandedChild);
301 }
302 addView(child);
303 mExpandedChild = child;
Selim Cinek30e387d2016-01-11 18:01:47 -0800304 mExpandedChild.addOnLayoutChangeListener(mLayoutUpdater);
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700305 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child);
Jorim Jaggi11298832014-05-24 16:18:38 +0200306 selectLayout(false /* animate */, true /* force */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200307 }
308
Selim Cinek8d490d42015-04-10 00:05:50 -0700309 public void setHeadsUpChild(View child) {
310 if (mHeadsUpChild != null) {
311 mHeadsUpChild.animate().cancel();
Selim Cinek30e387d2016-01-11 18:01:47 -0800312 mHeadsUpChild.removeOnLayoutChangeListener(mLayoutUpdater);
Selim Cinek8d490d42015-04-10 00:05:50 -0700313 removeView(mHeadsUpChild);
314 }
315 addView(child);
316 mHeadsUpChild = child;
Selim Cinek30e387d2016-01-11 18:01:47 -0800317 mHeadsUpChild.addOnLayoutChangeListener(mLayoutUpdater);
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700318 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child);
Selim Cinek8d490d42015-04-10 00:05:50 -0700319 selectLayout(false /* animate */, true /* force */);
320 }
321
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100322 @Override
323 protected void onVisibilityChanged(View changedView, int visibility) {
324 super.onVisibilityChanged(changedView, visibility);
325 updateVisibility();
326 }
327
328 private void updateVisibility() {
329 setVisible(isShown());
330 }
331
332 private void setVisible(final boolean isVisible) {
333 if (isVisible) {
334
335 // We only animate if we are drawn at least once, otherwise the view might animate when
336 // it's shown the first time
337 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
338 } else {
339 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
340 mAnimate = false;
341 }
342 }
343
Selim Cinekb5605e52015-02-20 18:21:41 +0100344 public void setContentHeight(int contentHeight) {
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700345 mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());;
346 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100347 selectLayout(mAnimate /* animate */, false /* force */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200348 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700349 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200350 }
351
Selim Cinekb5605e52015-02-20 18:21:41 +0100352 public int getContentHeight() {
353 return mContentHeight;
354 }
355
Jorim Jaggibe565df2014-04-28 17:51:23 +0200356 public int getMaxHeight() {
Selim Cinek77019c72015-12-09 10:18:02 -0800357 if (mExpandedChild != null) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700358 return mExpandedChild.getHeight();
Selim Cinek77019c72015-12-09 10:18:02 -0800359 } else if (mIsHeadsUp && mHeadsUpChild != null) {
360 return mHeadsUpChild.getHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -0700361 }
Selim Cinek860b6da2015-12-16 19:02:19 -0800362 return mContractedChild.getHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200363 }
364
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200365 public int getMinHeight() {
Selim Cinek83bc7832015-10-22 13:26:54 -0700366 if (mIsChildInGroup && !isGroupExpanded()) {
Selim Cinek7b836392015-12-04 20:02:59 -0800367 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700368 } else {
Selim Cinek860b6da2015-12-16 19:02:19 -0800369 return mContractedChild.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700370 }
371 }
372
373 private boolean isGroupExpanded() {
374 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200375 }
376
Jorim Jaggibe565df2014-04-28 17:51:23 +0200377 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200378 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200379 updateClipping();
380 }
381
Jorim Jaggibe565df2014-04-28 17:51:23 +0200382 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100383 if (mClipToActualHeight) {
384 mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
385 setClipBounds(mClipBounds);
386 } else {
387 setClipBounds(null);
388 }
389 }
390
391 public void setClipToActualHeight(boolean clipToActualHeight) {
392 mClipToActualHeight = clipToActualHeight;
393 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200394 }
395
Jorim Jaggi11298832014-05-24 16:18:38 +0200396 private void selectLayout(boolean animate, boolean force) {
397 if (mContractedChild == null) {
398 return;
399 }
Selim Cinek684a4422015-04-15 16:18:39 -0700400 int visibleType = calculateVisibleType();
401 if (visibleType != mVisibleType || force) {
Jorim Jaggi021eee52015-06-11 17:07:18 -0700402 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
Selim Cinek684a4422015-04-15 16:18:39 -0700403 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
Selim Cinek83bc7832015-10-22 13:26:54 -0700404 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
Jorim Jaggi021eee52015-06-11 17:07:18 -0700405 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100406 animateToVisibleType(visibleType);
Selim Cinek8d490d42015-04-10 00:05:50 -0700407 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700408 updateViewVisibilities(visibleType);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200409 }
Selim Cinek684a4422015-04-15 16:18:39 -0700410 mVisibleType = visibleType;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200411 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200412 }
413
Selim Cinek684a4422015-04-15 16:18:39 -0700414 private void updateViewVisibilities(int visibleType) {
415 boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100416 mContractedWrapper.setVisible(contractedVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700417 if (mExpandedChild != null) {
Selim Cinek684a4422015-04-15 16:18:39 -0700418 boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100419 mExpandedWrapper.setVisible(expandedVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700420 }
421 if (mHeadsUpChild != null) {
Selim Cinek684a4422015-04-15 16:18:39 -0700422 boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100423 mHeadsUpWrapper.setVisible(headsUpVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700424 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700425 if (mSingleLineView != null) {
426 boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100427 mSingleLineView.setVisible(singleLineVisible);
Selim Cinek83bc7832015-10-22 13:26:54 -0700428 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700429 }
430
Selim Cinek4ffd6362015-12-29 15:12:23 +0100431 private void animateToVisibleType(int visibleType) {
432 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
433 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
434 shownView.transformFrom(hiddenView);
435 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
436 hiddenView.transformTo(shownView, new Runnable() {
437 @Override
438 public void run() {
439 hiddenView.setVisible(false);
440 }
441 });
Jorim Jaggi11298832014-05-24 16:18:38 +0200442 }
443
Selim Cinek684a4422015-04-15 16:18:39 -0700444 /**
445 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100446 * @return the corresponding transformable view according to the given visible type
447 */
448 private TransformableView getTransformableViewForVisibleType(int visibleType) {
449 switch (visibleType) {
450 case VISIBLE_TYPE_EXPANDED:
451 return mExpandedWrapper;
452 case VISIBLE_TYPE_HEADSUP:
453 return mHeadsUpWrapper;
454 case VISIBLE_TYPE_SINGLELINE:
455 return mSingleLineView;
456 default:
457 return mContractedWrapper;
458 }
459 }
460
461 /**
462 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -0700463 * @return the corresponding view according to the given visible type
464 */
465 private View getViewForVisibleType(int visibleType) {
466 switch (visibleType) {
467 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -0700468 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700469 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -0700470 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -0700471 case VISIBLE_TYPE_SINGLELINE:
472 return mSingleLineView;
Selim Cinek684a4422015-04-15 16:18:39 -0700473 default:
474 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -0700475 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700476 }
477
Selim Cinek34eda5e2016-02-18 17:10:43 -0800478 private NotificationViewWrapper getCurrentVisibleWrapper() {
479 switch (mVisibleType) {
480 case VISIBLE_TYPE_EXPANDED:
481 return mExpandedWrapper;
482 case VISIBLE_TYPE_HEADSUP:
483 return mHeadsUpWrapper;
484 case VISIBLE_TYPE_CONTRACTED:
485 return mContractedWrapper;
486 default:
487 return null;
488 }
489 }
490
Selim Cinek684a4422015-04-15 16:18:39 -0700491 /**
492 * @return one of the static enum types in this view, calculated form the current state
493 */
494 private int calculateVisibleType() {
Selim Cinek8d490d42015-04-10 00:05:50 -0700495 boolean noExpandedChild = mExpandedChild == null;
Adrian Roos777ef562015-12-01 17:37:14 -0800496
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800497 int viewHeight = Math.min(mContentHeight, mContainingNotification.getIntrinsicHeight());
498 if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
Adrian Roos777ef562015-12-01 17:37:14 -0800499 return VISIBLE_TYPE_EXPANDED;
500 }
Selim Cinekd84a5932015-12-15 11:45:36 -0800501 if (mIsChildInGroup && !isGroupExpanded()) {
502 return VISIBLE_TYPE_SINGLELINE;
503 }
Adrian Roos777ef562015-12-01 17:37:14 -0800504
Selim Cinek1f3f5442015-04-10 17:54:46 -0700505 if (mIsHeadsUp && mHeadsUpChild != null) {
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800506 if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -0700507 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -0700508 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700509 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700510 }
511 } else {
Selim Cineka4091502016-02-11 17:21:16 -0800512 if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
513 && (!mIsChildInGroup || !mContainingNotification.isExpanded()))) {
Selim Cinek684a4422015-04-15 16:18:39 -0700514 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700515 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700516 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700517 }
518 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200519 }
520
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200521 public boolean isContentExpandable() {
522 return mExpandedChild != null;
523 }
John Spurlocke15452b2014-08-21 09:44:39 -0400524
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100525 public void setDark(boolean dark, boolean fade, long delay) {
John Spurlockc3dfd322014-08-27 14:55:54 -0400526 if (mDark == dark || mContractedChild == null) return;
John Spurlocke15452b2014-08-21 09:44:39 -0400527 mDark = dark;
Selim Cinek30e387d2016-01-11 18:01:47 -0800528 dark = dark && !mShowingLegacyBackground;
Selim Cinek19ba7052016-01-27 20:04:27 -0800529 if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800530 mContractedWrapper.setDark(dark, fade, delay);
531 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800532 if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800533 mExpandedWrapper.setDark(dark, fade, delay);
534 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800535 if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800536 mHeadsUpWrapper.setDark(dark, fade, delay);
537 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800538 if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
Selim Cinek9c7712d2015-12-08 19:19:48 -0800539 mSingleLineView.setDark(dark, fade, delay);
540 }
John Spurlocke15452b2014-08-21 09:44:39 -0400541 }
542
Selim Cinek8d490d42015-04-10 00:05:50 -0700543 public void setHeadsUp(boolean headsUp) {
544 mIsHeadsUp = headsUp;
545 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -0800546 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -0700547 }
548
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +0200549 @Override
550 public boolean hasOverlappingRendering() {
551
552 // This is not really true, but good enough when fading from the contracted to the expanded
553 // layout, and saves us some layers.
554 return false;
555 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700556
557 public void setShowingLegacyBackground(boolean showing) {
558 mShowingLegacyBackground = showing;
559 }
Selim Cinek8d6440d2015-10-22 13:00:05 -0700560
Selim Cinek83bc7832015-10-22 13:26:54 -0700561 public void setIsChildInGroup(boolean isChildInGroup) {
562 mIsChildInGroup = isChildInGroup;
563 updateSingleLineView();
564 }
565
Adrian Roosb88b1a12015-12-09 18:51:05 -0800566 public void onNotificationUpdated(NotificationData.Entry entry) {
567 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -0800568 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Selim Cinek83bc7832015-10-22 13:26:54 -0700569 updateSingleLineView();
Adrian Roosb88b1a12015-12-09 18:51:05 -0800570 applyRemoteInput(entry);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800571 selectLayout(false /* animate */, true /* force */);
572 if (mContractedChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100573 mContractedWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800574 mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
575 }
576 if (mExpandedChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100577 mExpandedWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800578 }
579 if (mHeadsUpChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100580 mHeadsUpWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800581 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700582 }
583
584 private void updateSingleLineView() {
585 if (mIsChildInGroup) {
586 mSingleLineView = mHybridViewManager.bindFromNotification(
587 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekde33a4a2016-02-11 16:43:41 -0800588 } else if (mSingleLineView != null) {
589 removeView(mSingleLineView);
590 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -0700591 }
592 }
593
Adrian Roosb88b1a12015-12-09 18:51:05 -0800594 private void applyRemoteInput(final NotificationData.Entry entry) {
595 if (mRemoteInputController == null) {
596 return;
597 }
598
599 boolean hasRemoteInput = false;
600
601 Notification.Action[] actions = entry.notification.getNotification().actions;
602 if (actions != null) {
603 for (Notification.Action a : actions) {
604 if (a.getRemoteInputs() != null) {
605 for (RemoteInput ri : a.getRemoteInputs()) {
606 if (ri.getAllowFreeFormInput()) {
607 hasRemoteInput = true;
608 break;
609 }
610 }
611 }
612 }
613 }
614
615 View bigContentView = mExpandedChild;
616 if (bigContentView != null) {
617 applyRemoteInput(bigContentView, entry, hasRemoteInput);
618 }
619 View headsUpContentView = mHeadsUpChild;
620 if (headsUpContentView != null) {
621 applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
622 }
623 }
624
625 private void applyRemoteInput(View view, NotificationData.Entry entry, boolean hasRemoteInput) {
626 View actionContainerCandidate = view.findViewById(
627 com.android.internal.R.id.actions_container);
628 if (actionContainerCandidate instanceof FrameLayout) {
629 RemoteInputView existing = (RemoteInputView)
630 view.findViewWithTag(RemoteInputView.VIEW_TAG);
631
632 if (existing != null) {
633 existing.onNotificationUpdate();
634 }
635
636 if (existing == null && hasRemoteInput) {
637 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
638 RemoteInputView riv = RemoteInputView.inflate(
639 mContext, actionContainer, entry, mRemoteInputController);
640
641 riv.setVisibility(View.INVISIBLE);
642 actionContainer.addView(riv, new LayoutParams(
643 ViewGroup.LayoutParams.MATCH_PARENT,
644 ViewGroup.LayoutParams.MATCH_PARENT)
645 );
646 int color = entry.notification.getNotification().color;
647 if (color == Notification.COLOR_DEFAULT) {
648 color = mContext.getColor(R.color.default_remote_input_background);
649 }
650 riv.setBackgroundColor(color);
651 }
652 }
653 }
654
Selim Cinek83bc7832015-10-22 13:26:54 -0700655 public void setGroupManager(NotificationGroupManager groupManager) {
656 mGroupManager = groupManager;
657 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700658
Adrian Roosb88b1a12015-12-09 18:51:05 -0800659 public void setRemoteInputController(RemoteInputController r) {
660 mRemoteInputController = r;
661 }
662
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800663 public void setExpandClickListener(OnClickListener expandClickListener) {
664 mExpandClickListener = expandClickListener;
665 }
666
667 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -0800668 mExpandable = expandable;
669 // if the expanded child has the same height as the collapsed one we hide it.
670 if (mExpandedChild != null && mExpandedChild.getHeight() != 0 &&
671 ((mIsHeadsUp && mExpandedChild.getHeight() == mHeadsUpChild.getHeight()) ||
672 (!mIsHeadsUp && mExpandedChild.getHeight() == mContractedChild.getHeight()))) {
673 expandable = false;
674 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700675 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800676 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700677 }
678 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800679 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700680 }
681 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800682 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700683 }
684 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800685
686 public NotificationHeaderView getNotificationHeader() {
687 NotificationHeaderView header = null;
688 if (mContractedChild != null) {
689 header = mContractedWrapper.getNotificationHeader();
690 }
691 if (header == null && mExpandedChild != null) {
692 header = mExpandedWrapper.getNotificationHeader();
693 }
694 if (header == null && mHeadsUpChild != null) {
695 header = mHeadsUpWrapper.getNotificationHeader();
696 }
697 return header;
698 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800699
Selim Cinek34eda5e2016-02-18 17:10:43 -0800700 public NotificationHeaderView getVisibleNotificationHeader() {
701 NotificationViewWrapper wrapper = getCurrentVisibleWrapper();
702 return wrapper == null ? null : wrapper.getNotificationHeader();
703 }
704
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800705 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
706 mContainingNotification = containingNotification;
707 }
708
709 public void requestSelectLayout(boolean needsAnimation) {
710 selectLayout(needsAnimation, false);
711 }
Selim Cinekde33a4a2016-02-11 16:43:41 -0800712
713 public void reInflateViews() {
714 if (mIsChildInGroup && mSingleLineView != null) {
715 removeView(mSingleLineView);
716 mSingleLineView = null;
717 updateSingleLineView();
718 }
719 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200720}