blob: a11263a3b9f2487f45e285a6ce3865a609320a55 [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;
Adrian Roos4c1fcc82016-03-31 14:39:39 -070020import android.app.PendingIntent;
Adrian Roosb88b1a12015-12-09 18:51:05 -080021import android.app.RemoteInput;
Jorim Jaggibe565df2014-04-28 17:51:23 +020022import android.content.Context;
23import android.graphics.Rect;
Selim Cinek860b6da2015-12-16 19:02:19 -080024import android.os.Build;
Selim Cinek83bc7832015-10-22 13:26:54 -070025import android.service.notification.StatusBarNotification;
Jorim Jaggibe565df2014-04-28 17:51:23 +020026import android.util.AttributeSet;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080027import android.view.NotificationHeaderView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020028import android.view.View;
Selim Cinek8d490d42015-04-10 00:05:50 -070029import android.view.ViewGroup;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010030import android.view.ViewTreeObserver;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020031import android.widget.FrameLayout;
Selim Cinek8d490d42015-04-10 00:05:50 -070032
Adrian Roos4ff3b122016-02-01 12:26:13 -080033import com.android.internal.util.NotificationColorUtil;
Jorim Jaggibe565df2014-04-28 17:51:23 +020034import com.android.systemui.R;
Selim Cinek83bc7832015-10-22 13:26:54 -070035import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinekc897bd32016-03-18 17:32:31 -070036import com.android.systemui.statusbar.notification.HybridGroupManager;
Selim Cinek0ffbda62016-01-01 20:29:12 +010037import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
Selim Cinekc3179332016-03-04 14:44:56 -080038import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cinek0ffbda62016-01-01 20:29:12 +010039import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070040import com.android.systemui.statusbar.phone.NotificationGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080041import com.android.systemui.statusbar.policy.RemoteInputView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020042
43/**
Selim Cinek684a4422015-04-15 16:18:39 -070044 * A frame layout containing the actual payload of the notification, including the contracted,
45 * expanded and heads up layout. This class is responsible for clipping the content and and
46 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020047 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020048public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020049
Selim Cinek684a4422015-04-15 16:18:39 -070050 private static final int VISIBLE_TYPE_CONTRACTED = 0;
51 private static final int VISIBLE_TYPE_EXPANDED = 1;
52 private static final int VISIBLE_TYPE_HEADSUP = 2;
Selim Cinek83bc7832015-10-22 13:26:54 -070053 private static final int VISIBLE_TYPE_SINGLELINE = 3;
Selim Cinekc3179332016-03-04 14:44:56 -080054 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020055
Jorim Jaggibe565df2014-04-28 17:51:23 +020056 private final Rect mClipBounds = new Rect();
Selim Cinek860b6da2015-12-16 19:02:19 -080057 private final int mMinContractedHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -080058 private final int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020059
60 private View mContractedChild;
61 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070062 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070063 private HybridNotificationView mSingleLineView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020064
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070065 private RemoteInputView mExpandedRemoteInput;
66 private RemoteInputView mHeadsUpRemoteInput;
67
Jorim Jaggi4e857f42014-11-17 19:14:04 +010068 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070069 private NotificationViewWrapper mExpandedWrapper;
70 private NotificationViewWrapper mHeadsUpWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -070071 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020072 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +010073 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -070074 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -040075 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010076 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -070077 private boolean mIsHeadsUp;
Jorim Jaggi59ec3042015-06-05 15:18:43 -070078 private boolean mShowingLegacyBackground;
Selim Cinek83bc7832015-10-22 13:26:54 -070079 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -080080 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -080081 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -080082 private int mNotificationMaxHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -070083 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070084 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080085 private RemoteInputController mRemoteInputController;
Jorim Jaggi59ec3042015-06-05 15:18:43 -070086
Jorim Jaggibe4116a2015-05-20 20:04:08 -070087 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010088 = new ViewTreeObserver.OnPreDrawListener() {
89 @Override
90 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -080091 // We need to post since we don't want the notification to animate on the very first
92 // frame
93 post(new Runnable() {
94 @Override
95 public void run() {
96 mAnimate = true;
97 }
98 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010099 getViewTreeObserver().removeOnPreDrawListener(this);
100 return true;
101 }
102 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200103
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800104 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800105 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800106 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100107 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800108 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700109 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800110 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700111 /** The visible type at the start of an animation driven transformation */
112 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800113 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700114 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700115 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700116 private PendingIntent mPreviousExpandedRemoteInputIntent;
117 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Selim Cinek98be3f32015-12-17 16:39:06 -0800118
Adrian Roos599be342016-06-13 14:54:39 -0700119 private int mContentHeightAtAnimationStart = UNDEFINED;
120
121
Jorim Jaggibe565df2014-04-28 17:51:23 +0200122 public NotificationContentView(Context context, AttributeSet attrs) {
123 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700124 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Selim Cinek860b6da2015-12-16 19:02:19 -0800125 mMinContractedHeight = getResources().getDimensionPixelSize(
126 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800127 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
128 com.android.internal.R.dimen.notification_content_margin_end);
Selim Cinek51d94912016-03-02 15:34:28 -0800129 reset();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200130 }
131
Selim Cinekd84a5932015-12-15 11:45:36 -0800132 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800133 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800134 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800135 mNotificationMaxHeight = maxHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800136 }
137
Jorim Jaggibe565df2014-04-28 17:51:23 +0200138 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700139 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
140 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
141 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
142 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
143 int maxSize = Integer.MAX_VALUE;
Selim Cinekc897bd32016-03-18 17:32:31 -0700144 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700145 if (hasFixedHeight || isHeightLimited) {
146 maxSize = MeasureSpec.getSize(heightMeasureSpec);
147 }
148 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800149 if (mExpandedChild != null) {
150 int size = Math.min(maxSize, mNotificationMaxHeight);
151 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
152 if (layoutParams.height >= 0) {
153 // An actual height is set
154 size = Math.min(maxSize, layoutParams.height);
155 }
156 int spec = size == Integer.MAX_VALUE
157 ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
158 : MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
159 mExpandedChild.measure(widthMeasureSpec, spec);
160 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
161 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700162 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800163 int heightSpec;
Selim Cinekf619ffc2016-02-17 14:53:05 -0800164 int size = Math.min(maxSize, mSmallHeight);
Selim Cinek860b6da2015-12-16 19:02:19 -0800165 if (shouldContractedBeFixedSize()) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800166 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
167 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800168 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800169 }
170 mContractedChild.measure(widthMeasureSpec, heightSpec);
171 int measuredHeight = mContractedChild.getMeasuredHeight();
172 if (measuredHeight < mMinContractedHeight) {
173 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
174 mContractedChild.measure(widthMeasureSpec, heightSpec);
175 }
176 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800177 if (updateContractedHeaderWidth()) {
178 mContractedChild.measure(widthMeasureSpec, heightSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700179 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700180 if (mExpandedChild != null
181 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
182 // the Expanded child is smaller then the collapsed. Let's remeasure it.
183 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
184 MeasureSpec.EXACTLY);
185 mExpandedChild.measure(widthMeasureSpec, heightSpec);
186 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700187 }
188 if (mHeadsUpChild != null) {
189 int size = Math.min(maxSize, mHeadsUpHeight);
190 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
191 if (layoutParams.height >= 0) {
192 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800193 size = Math.min(size, layoutParams.height);
Selim Cinek8d490d42015-04-10 00:05:50 -0700194 }
195 mHeadsUpChild.measure(widthMeasureSpec,
196 MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
197 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
198 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700199 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700200 int singleLineWidthSpec = widthMeasureSpec;
201 if (mSingleLineWidthIndention != 0
202 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
203 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
204 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
205 MeasureSpec.AT_MOST);
206 }
207 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinek7b836392015-12-04 20:02:59 -0800208 MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700209 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
210 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700211 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700212 setMeasuredDimension(width, ownHeight);
213 }
214
Selim Cinek6ecc8102016-01-26 18:26:19 -0800215 private boolean updateContractedHeaderWidth() {
216 // We need to update the expanded and the collapsed header to have exactly the same with to
217 // have the expand buttons laid out at the same location.
218 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
219 if (contractedHeader != null) {
220 if (mExpandedChild != null
221 && mExpandedWrapper.getNotificationHeader() != null) {
222 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
223 int expandedSize = expandedHeader.getMeasuredWidth()
224 - expandedHeader.getPaddingEnd();
225 int collapsedSize = contractedHeader.getMeasuredWidth()
226 - expandedHeader.getPaddingEnd();
227 if (expandedSize != collapsedSize) {
228 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
229 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800230 contractedHeader.isLayoutRtl()
231 ? paddingEnd
232 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800233 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800234 contractedHeader.isLayoutRtl()
235 ? contractedHeader.getPaddingLeft()
236 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800237 contractedHeader.getPaddingBottom());
238 contractedHeader.setShowWorkBadgeAtEnd(true);
239 return true;
240 }
241 } else {
242 int paddingEnd = mNotificationContentMarginEnd;
243 if (contractedHeader.getPaddingEnd() != paddingEnd) {
244 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800245 contractedHeader.isLayoutRtl()
246 ? paddingEnd
247 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800248 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800249 contractedHeader.isLayoutRtl()
250 ? contractedHeader.getPaddingLeft()
251 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800252 contractedHeader.getPaddingBottom());
253 contractedHeader.setShowWorkBadgeAtEnd(false);
254 return true;
255 }
256 }
257 }
258 return false;
259 }
260
Selim Cinek860b6da2015-12-16 19:02:19 -0800261 private boolean shouldContractedBeFixedSize() {
262 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
263 }
264
Selim Cinek8d490d42015-04-10 00:05:50 -0700265 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200266 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700267 int previousHeight = 0;
268 if (mExpandedChild != null) {
269 previousHeight = mExpandedChild.getHeight();
270 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200271 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700272 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
273 mContentHeightAtAnimationStart = previousHeight;
274 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200275 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700276 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700277 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
278 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700279 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200280 }
281
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100282 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100283 protected void onAttachedToWindow() {
284 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100285 updateVisibility();
286 }
287
Selim Cinek51d94912016-03-02 15:34:28 -0800288 public void reset() {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200289 if (mContractedChild != null) {
290 mContractedChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800291 removeView(mContractedChild);
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200292 }
Adrian Roos0789ee02016-06-01 11:34:58 -0700293 mPreviousExpandedRemoteInputIntent = null;
294 if (mExpandedRemoteInput != null) {
295 mExpandedRemoteInput.onNotificationUpdateOrReset();
296 if (mExpandedRemoteInput.isActive()) {
297 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
298 }
299 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200300 if (mExpandedChild != null) {
301 mExpandedChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800302 removeView(mExpandedChild);
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700303 mExpandedRemoteInput = null;
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200304 }
Adrian Roos0789ee02016-06-01 11:34:58 -0700305 mPreviousHeadsUpRemoteInputIntent = null;
306 if (mHeadsUpRemoteInput != null) {
307 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
308 if (mHeadsUpRemoteInput.isActive()) {
309 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
310 }
311 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700312 if (mHeadsUpChild != null) {
313 mHeadsUpChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800314 removeView(mHeadsUpChild);
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700315 mHeadsUpRemoteInput = null;
Selim Cinek8d490d42015-04-10 00:05:50 -0700316 }
Christoph Studera7fe6312014-06-27 19:32:44 +0200317 mContractedChild = null;
318 mExpandedChild = null;
Selim Cinek1f3f5442015-04-10 17:54:46 -0700319 mHeadsUpChild = null;
Christoph Studera7fe6312014-06-27 19:32:44 +0200320 }
321
Selim Cinekcab4a602014-09-03 14:47:57 +0200322 public View getContractedChild() {
323 return mContractedChild;
324 }
325
326 public View getExpandedChild() {
327 return mExpandedChild;
328 }
329
Selim Cinek8d490d42015-04-10 00:05:50 -0700330 public View getHeadsUpChild() {
331 return mHeadsUpChild;
332 }
333
Jorim Jaggibe565df2014-04-28 17:51:23 +0200334 public void setContractedChild(View child) {
335 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200336 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200337 removeView(mContractedChild);
338 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200339 addView(child);
340 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700341 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
342 mContainingNotification);
Jorim Jaggi10ad7612014-12-08 18:41:11 +0100343 mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200344 }
345
346 public void setExpandedChild(View child) {
347 if (mExpandedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200348 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200349 removeView(mExpandedChild);
350 }
351 addView(child);
352 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700353 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
354 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200355 }
356
Selim Cinek8d490d42015-04-10 00:05:50 -0700357 public void setHeadsUpChild(View child) {
358 if (mHeadsUpChild != null) {
359 mHeadsUpChild.animate().cancel();
360 removeView(mHeadsUpChild);
361 }
362 addView(child);
363 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700364 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
365 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700366 }
367
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100368 @Override
369 protected void onVisibilityChanged(View changedView, int visibility) {
370 super.onVisibilityChanged(changedView, visibility);
371 updateVisibility();
372 }
373
374 private void updateVisibility() {
375 setVisible(isShown());
376 }
377
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700378 @Override
379 protected void onDetachedFromWindow() {
380 super.onDetachedFromWindow();
381 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
382 }
383
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100384 private void setVisible(final boolean isVisible) {
385 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700386 // This call can happen multiple times, but removing only removes a single one.
387 // We therefore need to remove the old one.
388 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100389 // We only animate if we are drawn at least once, otherwise the view might animate when
390 // it's shown the first time
391 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
392 } else {
393 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
394 mAnimate = false;
395 }
396 }
397
Selim Cinekb5605e52015-02-20 18:21:41 +0100398 public void setContentHeight(int contentHeight) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700399 mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100400 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400401
402 int minHeightHint = getMinContentHeightHint();
403
404 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
405 if (wrapper != null) {
406 wrapper.setContentHeight(mContentHeight, minHeightHint);
407 }
408
409 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
410 if (wrapper != null) {
411 wrapper.setContentHeight(mContentHeight, minHeightHint);
412 }
413
Jorim Jaggibe565df2014-04-28 17:51:23 +0200414 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700415 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200416 }
417
Adrian Roos181385c2016-05-05 17:45:44 -0400418 /**
419 * @return the minimum apparent height that the wrapper should allow for the purpose
420 * of aligning elements at the bottom edge. If this is larger than the content
421 * height, the notification is clipped instead of being further shrunk.
422 */
423 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700424 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400425 return mContext.getResources().getDimensionPixelSize(
426 com.android.internal.R.dimen.notification_action_list_height);
427 }
Adrian Roos599be342016-06-13 14:54:39 -0700428
429 // Transition between heads-up & expanded, or pinned.
430 if (mHeadsUpChild != null && mExpandedChild != null) {
431 boolean transitioningBetweenHunAndExpanded =
432 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
433 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
434 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED) && mIsHeadsUp;
435 if (transitioningBetweenHunAndExpanded || pinned) {
436 return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight());
437 }
438 }
439
440 // Size change of the expanded version
441 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
442 && mExpandedChild != null) {
443 return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight());
444 }
445
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700446 int hint;
Adrian Roos599be342016-06-13 14:54:39 -0700447 if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700448 hint = mHeadsUpChild.getHeight();
Adrian Roos599be342016-06-13 14:54:39 -0700449 } else if (mExpandedChild != null) {
450 hint = mExpandedChild.getHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400451 } else {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700452 hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
453 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400454 }
Adrian Roos599be342016-06-13 14:54:39 -0700455
456 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700457 hint = Math.min(hint, mExpandedChild.getHeight());
458 }
459 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400460 }
461
Adrian Roos599be342016-06-13 14:54:39 -0700462 private boolean isTransitioningFromTo(int from, int to) {
463 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
464 && mVisibleType == to;
465 }
466
467 private boolean isVisibleOrTransitioning(int type) {
468 return mVisibleType == type || mTransformationStartVisibleType == type
469 || mAnimationStartVisibleType == type;
470 }
471
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800472 private void updateContentTransformation() {
473 int visibleType = calculateVisibleType();
474 if (visibleType != mVisibleType) {
475 // A new transformation starts
476 mTransformationStartVisibleType = mVisibleType;
477 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
478 final TransformableView hiddenView = getTransformableViewForVisibleType(
479 mTransformationStartVisibleType);
480 shownView.transformFrom(hiddenView, 0.0f);
481 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
482 hiddenView.transformTo(shownView, 0.0f);
483 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800484 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800485 }
Selim Cineke8578872016-05-03 16:42:50 -0700486 if (mForceSelectNextLayout) {
487 forceUpdateVisibilities();
488 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800489 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700490 && mVisibleType != mTransformationStartVisibleType
491 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800492 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
493 final TransformableView hiddenView = getTransformableViewForVisibleType(
494 mTransformationStartVisibleType);
495 float transformationAmount = calculateTransformationAmount();
496 shownView.transformFrom(hiddenView, transformationAmount);
497 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800498 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800499 } else {
500 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800501 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800502 }
503 }
504
Selim Cinekc3179332016-03-04 14:44:56 -0800505 private void updateBackgroundTransformation(float transformationAmount) {
506 int endColor = getBackgroundColor(mVisibleType);
507 int startColor = getBackgroundColor(mTransformationStartVisibleType);
508 if (endColor != startColor) {
509 if (startColor == 0) {
510 startColor = mContainingNotification.getBackgroundColorWithoutTint();
511 }
512 if (endColor == 0) {
513 endColor = mContainingNotification.getBackgroundColorWithoutTint();
514 }
515 endColor = NotificationUtils.interpolateColors(startColor, endColor,
516 transformationAmount);
517 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400518 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800519 mContainingNotification.setContentBackground(endColor, false, this);
520 }
521
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800522 private float calculateTransformationAmount() {
523 int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
524 int endHeight = getViewForVisibleType(mVisibleType).getHeight();
525 int progress = Math.abs(mContentHeight - startHeight);
526 int totalDistance = Math.abs(endHeight - startHeight);
527 float amount = (float) progress / (float) totalDistance;
528 return Math.min(1.0f, amount);
529 }
530
Selim Cinekb5605e52015-02-20 18:21:41 +0100531 public int getContentHeight() {
532 return mContentHeight;
533 }
534
Jorim Jaggibe565df2014-04-28 17:51:23 +0200535 public int getMaxHeight() {
Selim Cinek77019c72015-12-09 10:18:02 -0800536 if (mExpandedChild != null) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700537 return mExpandedChild.getHeight();
Selim Cinek77019c72015-12-09 10:18:02 -0800538 } else if (mIsHeadsUp && mHeadsUpChild != null) {
539 return mHeadsUpChild.getHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -0700540 }
Selim Cinek860b6da2015-12-16 19:02:19 -0800541 return mContractedChild.getHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200542 }
543
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200544 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800545 return getMinHeight(false /* likeGroupExpanded */);
546 }
547
548 public int getMinHeight(boolean likeGroupExpanded) {
549 if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800550 return mContractedChild.getHeight();
Selim Cinek42357e02016-02-24 18:48:01 -0800551 } else {
552 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700553 }
554 }
555
556 private boolean isGroupExpanded() {
557 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200558 }
559
Jorim Jaggibe565df2014-04-28 17:51:23 +0200560 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200561 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200562 updateClipping();
563 }
564
Jorim Jaggibe565df2014-04-28 17:51:23 +0200565 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100566 if (mClipToActualHeight) {
567 mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
568 setClipBounds(mClipBounds);
569 } else {
570 setClipBounds(null);
571 }
572 }
573
574 public void setClipToActualHeight(boolean clipToActualHeight) {
575 mClipToActualHeight = clipToActualHeight;
576 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200577 }
578
Jorim Jaggi11298832014-05-24 16:18:38 +0200579 private void selectLayout(boolean animate, boolean force) {
580 if (mContractedChild == null) {
581 return;
582 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800583 if (mUserExpanding) {
584 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700585 } else {
586 int visibleType = calculateVisibleType();
587 if (visibleType != mVisibleType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400588 View visibleView = getViewForVisibleType(visibleType);
589 if (visibleView != null) {
590 visibleView.setVisibility(VISIBLE);
591 transferRemoteInputFocus(visibleType);
592 }
593 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
594 if (visibleWrapper != null) {
595 visibleWrapper.setContentHeight(mContentHeight, getMinContentHeightHint());
596 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700597
Selim Cinek589fd3e2016-04-26 18:17:57 -0700598 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
599 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
600 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
601 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
602 animateToVisibleType(visibleType);
603 } else {
604 updateViewVisibilities(visibleType);
605 }
606 mVisibleType = visibleType;
607 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200608 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700609 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700610 }
611
612 private void forceUpdateVisibilities() {
613 boolean contractedVisible = mVisibleType == VISIBLE_TYPE_CONTRACTED
614 || mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED;
615 boolean expandedVisible = mVisibleType == VISIBLE_TYPE_EXPANDED
616 || mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED;
617 boolean headsUpVisible = mVisibleType == VISIBLE_TYPE_HEADSUP
618 || mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP;
619 boolean singleLineVisible = mVisibleType == VISIBLE_TYPE_SINGLELINE
620 || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE;
621 if (!contractedVisible) {
622 mContractedChild.setVisibility(View.INVISIBLE);
623 } else {
624 mContractedWrapper.setVisible(true);
625 }
626 if (mExpandedChild != null) {
627 if (!expandedVisible) {
628 mExpandedChild.setVisibility(View.INVISIBLE);
629 } else {
630 mExpandedWrapper.setVisible(true);
631 }
632 }
633 if (mHeadsUpChild != null) {
634 if (!headsUpVisible) {
635 mHeadsUpChild.setVisibility(View.INVISIBLE);
636 } else {
637 mHeadsUpWrapper.setVisible(true);
638 }
639 }
640 if (mSingleLineView != null) {
641 if (!singleLineVisible) {
642 mSingleLineView.setVisibility(View.INVISIBLE);
643 } else {
644 mSingleLineView.setVisible(true);
645 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200646 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200647 }
648
Selim Cinekc3179332016-03-04 14:44:56 -0800649 public void updateBackgroundColor(boolean animate) {
650 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400651 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800652 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
653 }
654
Mady Mellorb0a82462016-04-30 17:31:02 -0700655 public int getVisibleType() {
656 return mVisibleType;
657 }
658
659 public int getBackgroundColorForExpansionState() {
660 // When expanding or user locked we want the new type, when collapsing we want
661 // the original type
662 final int visibleType = (mContainingNotification.isGroupExpanded()
663 || mContainingNotification.isUserLocked())
664 ? calculateVisibleType()
665 : getVisibleType();
666 return getBackgroundColor(visibleType);
667 }
668
669 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800670 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
671 int customBackgroundColor = 0;
672 if (currentVisibleWrapper != null) {
673 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
674 }
675 return customBackgroundColor;
676 }
677
Selim Cinek684a4422015-04-15 16:18:39 -0700678 private void updateViewVisibilities(int visibleType) {
679 boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100680 mContractedWrapper.setVisible(contractedVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700681 if (mExpandedChild != null) {
Selim Cinek684a4422015-04-15 16:18:39 -0700682 boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100683 mExpandedWrapper.setVisible(expandedVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700684 }
685 if (mHeadsUpChild != null) {
Selim Cinek684a4422015-04-15 16:18:39 -0700686 boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100687 mHeadsUpWrapper.setVisible(headsUpVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700688 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700689 if (mSingleLineView != null) {
690 boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100691 mSingleLineView.setVisible(singleLineVisible);
Selim Cinek83bc7832015-10-22 13:26:54 -0700692 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700693 }
694
Selim Cinek4ffd6362015-12-29 15:12:23 +0100695 private void animateToVisibleType(int visibleType) {
696 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
697 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700698 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800699 shownView.setVisible(true);
700 return;
701 }
Adrian Roos599be342016-06-13 14:54:39 -0700702 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100703 shownView.transformFrom(hiddenView);
704 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
705 hiddenView.transformTo(shownView, new Runnable() {
706 @Override
707 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800708 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
709 hiddenView.setVisible(false);
710 }
Adrian Roos599be342016-06-13 14:54:39 -0700711 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100712 }
713 });
Jorim Jaggi11298832014-05-24 16:18:38 +0200714 }
715
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700716 private void transferRemoteInputFocus(int visibleType) {
717 if (visibleType == VISIBLE_TYPE_HEADSUP
718 && mHeadsUpRemoteInput != null
719 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
720 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
721 }
722 if (visibleType == VISIBLE_TYPE_EXPANDED
723 && mExpandedRemoteInput != null
724 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
725 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
726 }
727 }
728
Selim Cinek684a4422015-04-15 16:18:39 -0700729 /**
730 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100731 * @return the corresponding transformable view according to the given visible type
732 */
733 private TransformableView getTransformableViewForVisibleType(int visibleType) {
734 switch (visibleType) {
735 case VISIBLE_TYPE_EXPANDED:
736 return mExpandedWrapper;
737 case VISIBLE_TYPE_HEADSUP:
738 return mHeadsUpWrapper;
739 case VISIBLE_TYPE_SINGLELINE:
740 return mSingleLineView;
741 default:
742 return mContractedWrapper;
743 }
744 }
745
746 /**
747 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -0700748 * @return the corresponding view according to the given visible type
749 */
750 private View getViewForVisibleType(int visibleType) {
751 switch (visibleType) {
752 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -0700753 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700754 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -0700755 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -0700756 case VISIBLE_TYPE_SINGLELINE:
757 return mSingleLineView;
Selim Cinek684a4422015-04-15 16:18:39 -0700758 default:
759 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -0700760 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700761 }
762
Selim Cinekc3179332016-03-04 14:44:56 -0800763 private NotificationViewWrapper getVisibleWrapper(int visibleType) {
764 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -0800765 case VISIBLE_TYPE_EXPANDED:
766 return mExpandedWrapper;
767 case VISIBLE_TYPE_HEADSUP:
768 return mHeadsUpWrapper;
769 case VISIBLE_TYPE_CONTRACTED:
770 return mContractedWrapper;
771 default:
772 return null;
773 }
774 }
775
Selim Cinek684a4422015-04-15 16:18:39 -0700776 /**
777 * @return one of the static enum types in this view, calculated form the current state
778 */
Mady Mellorb0a82462016-04-30 17:31:02 -0700779 public int calculateVisibleType() {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800780 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -0800781 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -0800782 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -0800783 ? mContainingNotification.getMaxContentHeight()
784 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -0800785 if (height == 0) {
786 height = mContentHeight;
787 }
Selim Cinek42357e02016-02-24 18:48:01 -0800788 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700789 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
790 ? VISIBLE_TYPE_SINGLELINE
791 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800792 return mTransformationStartVisibleType == collapsedVisualType
793 ? expandedVisualType
794 : collapsedVisualType;
795 }
Selim Cinek51d94912016-03-02 15:34:28 -0800796 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
797 int viewHeight = mContentHeight;
798 if (intrinsicHeight != 0) {
799 // the intrinsicHeight might be 0 because it was just reset.
800 viewHeight = Math.min(mContentHeight, intrinsicHeight);
801 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800802 return getVisualTypeForHeight(viewHeight);
803 }
804
805 private int getVisualTypeForHeight(float viewHeight) {
806 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800807 if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
Adrian Roos777ef562015-12-01 17:37:14 -0800808 return VISIBLE_TYPE_EXPANDED;
809 }
Selim Cinek42357e02016-02-24 18:48:01 -0800810 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800811 return VISIBLE_TYPE_SINGLELINE;
812 }
Adrian Roos777ef562015-12-01 17:37:14 -0800813
Selim Cinek1f3f5442015-04-10 17:54:46 -0700814 if (mIsHeadsUp && mHeadsUpChild != null) {
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800815 if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -0700816 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -0700817 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700818 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700819 }
820 } else {
Selim Cineka4091502016-02-11 17:21:16 -0800821 if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
Selim Cinek6ac3fa82016-05-10 17:11:54 -0400822 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -0800823 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -0700824 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700825 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700826 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700827 }
828 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200829 }
830
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200831 public boolean isContentExpandable() {
832 return mExpandedChild != null;
833 }
John Spurlocke15452b2014-08-21 09:44:39 -0400834
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100835 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -0800836 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -0800837 return;
838 }
John Spurlocke15452b2014-08-21 09:44:39 -0400839 mDark = dark;
Selim Cinek19ba7052016-01-27 20:04:27 -0800840 if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800841 mContractedWrapper.setDark(dark, fade, delay);
842 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800843 if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800844 mExpandedWrapper.setDark(dark, fade, delay);
845 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800846 if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800847 mHeadsUpWrapper.setDark(dark, fade, delay);
848 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800849 if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
Selim Cinek9c7712d2015-12-08 19:19:48 -0800850 mSingleLineView.setDark(dark, fade, delay);
851 }
John Spurlocke15452b2014-08-21 09:44:39 -0400852 }
853
Selim Cinek8d490d42015-04-10 00:05:50 -0700854 public void setHeadsUp(boolean headsUp) {
855 mIsHeadsUp = headsUp;
856 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -0800857 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -0700858 }
859
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +0200860 @Override
861 public boolean hasOverlappingRendering() {
862
863 // This is not really true, but good enough when fading from the contracted to the expanded
864 // layout, and saves us some layers.
865 return false;
866 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700867
868 public void setShowingLegacyBackground(boolean showing) {
869 mShowingLegacyBackground = showing;
Selim Cinekc3179332016-03-04 14:44:56 -0800870 updateShowingLegacyBackground();
871 }
872
873 private void updateShowingLegacyBackground() {
874 if (mContractedChild != null) {
875 mContractedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
876 }
877 if (mExpandedChild != null) {
878 mExpandedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
879 }
880 if (mHeadsUpChild != null) {
881 mHeadsUpWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
882 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700883 }
Selim Cinek8d6440d2015-10-22 13:00:05 -0700884
Selim Cinek83bc7832015-10-22 13:26:54 -0700885 public void setIsChildInGroup(boolean isChildInGroup) {
886 mIsChildInGroup = isChildInGroup;
887 updateSingleLineView();
888 }
889
Adrian Roosb88b1a12015-12-09 18:51:05 -0800890 public void onNotificationUpdated(NotificationData.Entry entry) {
891 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -0800892 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Selim Cinek83bc7832015-10-22 13:26:54 -0700893 updateSingleLineView();
Adrian Roosb88b1a12015-12-09 18:51:05 -0800894 applyRemoteInput(entry);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800895 if (mContractedChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100896 mContractedWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800897 }
898 if (mExpandedChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100899 mExpandedWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800900 }
901 if (mHeadsUpChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100902 mHeadsUpWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800903 }
Selim Cinekc3179332016-03-04 14:44:56 -0800904 updateShowingLegacyBackground();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700905 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -0800906 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700907 mPreviousExpandedRemoteInputIntent = null;
908 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -0700909 }
910
911 private void updateSingleLineView() {
912 if (mIsChildInGroup) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700913 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -0700914 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekde33a4a2016-02-11 16:43:41 -0800915 } else if (mSingleLineView != null) {
916 removeView(mSingleLineView);
917 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -0700918 }
919 }
920
Adrian Roosb88b1a12015-12-09 18:51:05 -0800921 private void applyRemoteInput(final NotificationData.Entry entry) {
922 if (mRemoteInputController == null) {
923 return;
924 }
925
926 boolean hasRemoteInput = false;
927
928 Notification.Action[] actions = entry.notification.getNotification().actions;
929 if (actions != null) {
930 for (Notification.Action a : actions) {
931 if (a.getRemoteInputs() != null) {
932 for (RemoteInput ri : a.getRemoteInputs()) {
933 if (ri.getAllowFreeFormInput()) {
934 hasRemoteInput = true;
935 break;
936 }
937 }
938 }
939 }
940 }
941
942 View bigContentView = mExpandedChild;
943 if (bigContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700944 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
945 mPreviousExpandedRemoteInputIntent);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700946 } else {
947 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800948 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700949
Adrian Roosb88b1a12015-12-09 18:51:05 -0800950 View headsUpContentView = mHeadsUpChild;
951 if (headsUpContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700952 mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
953 mPreviousHeadsUpRemoteInputIntent);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700954 } else {
955 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800956 }
957 }
958
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700959 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
960 boolean hasRemoteInput, PendingIntent existingPendingIntent) {
Adrian Roosb88b1a12015-12-09 18:51:05 -0800961 View actionContainerCandidate = view.findViewById(
962 com.android.internal.R.id.actions_container);
963 if (actionContainerCandidate instanceof FrameLayout) {
964 RemoteInputView existing = (RemoteInputView)
965 view.findViewWithTag(RemoteInputView.VIEW_TAG);
966
967 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -0700968 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -0800969 }
970
971 if (existing == null && hasRemoteInput) {
972 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
973 RemoteInputView riv = RemoteInputView.inflate(
974 mContext, actionContainer, entry, mRemoteInputController);
975
976 riv.setVisibility(View.INVISIBLE);
977 actionContainer.addView(riv, new LayoutParams(
978 ViewGroup.LayoutParams.MATCH_PARENT,
979 ViewGroup.LayoutParams.MATCH_PARENT)
980 );
Adrian Roos4ff3b122016-02-01 12:26:13 -0800981 existing = riv;
982 }
983 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -0800984 int color = entry.notification.getNotification().color;
985 if (color == Notification.COLOR_DEFAULT) {
986 color = mContext.getColor(R.color.default_remote_input_background);
987 }
Adrian Roos4ff3b122016-02-01 12:26:13 -0800988 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -0700989 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -0800990 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700991
992 if (existingPendingIntent != null || existing.isActive()) {
993 // The current action could be gone, or the pending intent no longer valid.
994 // If we find a matching action in the new notification, focus, otherwise close.
995 Notification.Action[] actions = entry.notification.getNotification().actions;
996 if (existingPendingIntent != null) {
997 existing.setPendingIntent(existingPendingIntent);
998 }
999 if (existing.updatePendingIntentFromActions(actions)) {
1000 if (!existing.isActive()) {
1001 existing.focus();
1002 }
1003 } else {
1004 if (existing.isActive()) {
1005 existing.close();
1006 }
1007 }
1008 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001009 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001010 return existing;
1011 }
1012 return null;
1013 }
1014
1015 public void closeRemoteInput() {
1016 if (mHeadsUpRemoteInput != null) {
1017 mHeadsUpRemoteInput.close();
1018 }
1019 if (mExpandedRemoteInput != null) {
1020 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001021 }
1022 }
1023
Selim Cinek83bc7832015-10-22 13:26:54 -07001024 public void setGroupManager(NotificationGroupManager groupManager) {
1025 mGroupManager = groupManager;
1026 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001027
Adrian Roosb88b1a12015-12-09 18:51:05 -08001028 public void setRemoteInputController(RemoteInputController r) {
1029 mRemoteInputController = r;
1030 }
1031
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001032 public void setExpandClickListener(OnClickListener expandClickListener) {
1033 mExpandClickListener = expandClickListener;
1034 }
1035
1036 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001037 mExpandable = expandable;
1038 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001039 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
1040 if ((!mIsHeadsUp || mHeadsUpChild == null)) {
1041 if (mExpandedChild.getHeight() == mContractedChild.getHeight()) {
1042 expandable = false;
1043 }
1044 } else if (mExpandedChild.getHeight() == mHeadsUpChild.getHeight()) {
1045 expandable = false;
1046 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001047 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001048 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001049 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001050 }
1051 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001052 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001053 }
1054 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001055 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001056 }
1057 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001058
1059 public NotificationHeaderView getNotificationHeader() {
1060 NotificationHeaderView header = null;
1061 if (mContractedChild != null) {
1062 header = mContractedWrapper.getNotificationHeader();
1063 }
1064 if (header == null && mExpandedChild != null) {
1065 header = mExpandedWrapper.getNotificationHeader();
1066 }
1067 if (header == null && mHeadsUpChild != null) {
1068 header = mHeadsUpWrapper.getNotificationHeader();
1069 }
1070 return header;
1071 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001072
Selim Cinek34eda5e2016-02-18 17:10:43 -08001073 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001074 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001075 return wrapper == null ? null : wrapper.getNotificationHeader();
1076 }
1077
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001078 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1079 mContainingNotification = containingNotification;
1080 }
1081
1082 public void requestSelectLayout(boolean needsAnimation) {
1083 selectLayout(needsAnimation, false);
1084 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001085
1086 public void reInflateViews() {
1087 if (mIsChildInGroup && mSingleLineView != null) {
1088 removeView(mSingleLineView);
1089 mSingleLineView = null;
1090 updateSingleLineView();
1091 }
1092 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001093
1094 public void setUserExpanding(boolean userExpanding) {
1095 mUserExpanding = userExpanding;
1096 if (userExpanding) {
1097 mTransformationStartVisibleType = mVisibleType;
1098 } else {
1099 mTransformationStartVisibleType = UNDEFINED;
1100 mVisibleType = calculateVisibleType();
1101 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001102 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001103 }
1104 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001105
1106 /**
1107 * Set by how much the single line view should be indented. Used when a overflow indicator is
1108 * present and only during measuring
1109 */
1110 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1111 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1112 mSingleLineWidthIndention = singleLineWidthIndention;
1113 mContainingNotification.forceLayout();
1114 forceLayout();
1115 }
1116 }
1117
1118 public HybridNotificationView getSingleLineView() {
1119 return mSingleLineView;
1120 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001121
1122 public void setRemoved() {
1123 if (mExpandedRemoteInput != null) {
1124 mExpandedRemoteInput.setRemoved();
1125 }
1126 if (mHeadsUpRemoteInput != null) {
1127 mHeadsUpRemoteInput.setRemoved();
1128 }
1129 }
Adrian Roos599be342016-06-13 14:54:39 -07001130
1131 public void setContentHeightAnimating(boolean animating) {
1132 if (!animating) {
1133 mContentHeightAtAnimationStart = UNDEFINED;
1134 }
1135 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001136}