blob: 8f160dc0384ec6c9bafda2cdc08ef61a54853607 [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 Cineke9bad242016-06-15 11:46:37 -070032import android.widget.ImageView;
Selim Cinek8d490d42015-04-10 00:05:50 -070033
Adrian Roose18033c2017-01-17 15:22:49 -080034import com.android.internal.annotations.VisibleForTesting;
Adrian Roos4ff3b122016-02-01 12:26:13 -080035import com.android.internal.util.NotificationColorUtil;
Jorim Jaggibe565df2014-04-28 17:51:23 +020036import com.android.systemui.R;
Selim Cinek83bc7832015-10-22 13:26:54 -070037import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinekc897bd32016-03-18 17:32:31 -070038import com.android.systemui.statusbar.notification.HybridGroupManager;
Selim Cinek0ffbda62016-01-01 20:29:12 +010039import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
Selim Cinekc3179332016-03-04 14:44:56 -080040import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cinek0ffbda62016-01-01 20:29:12 +010041import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070042import com.android.systemui.statusbar.phone.NotificationGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080043import com.android.systemui.statusbar.policy.RemoteInputView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020044
45/**
Selim Cinek684a4422015-04-15 16:18:39 -070046 * A frame layout containing the actual payload of the notification, including the contracted,
47 * expanded and heads up layout. This class is responsible for clipping the content and and
48 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020049 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020050public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020051
Selim Cinek684a4422015-04-15 16:18:39 -070052 private static final int VISIBLE_TYPE_CONTRACTED = 0;
53 private static final int VISIBLE_TYPE_EXPANDED = 1;
54 private static final int VISIBLE_TYPE_HEADSUP = 2;
Selim Cinek83bc7832015-10-22 13:26:54 -070055 private static final int VISIBLE_TYPE_SINGLELINE = 3;
Adrian Roos0aac04f2016-12-08 15:59:29 -080056 private static final int VISIBLE_TYPE_AMBIENT = 4;
Selim Cinekc3179332016-03-04 14:44:56 -080057 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020058
Jorim Jaggibe565df2014-04-28 17:51:23 +020059 private final Rect mClipBounds = new Rect();
Selim Cinek860b6da2015-12-16 19:02:19 -080060 private final int mMinContractedHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -080061 private final int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020062
63 private View mContractedChild;
64 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070065 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070066 private HybridNotificationView mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -080067 private View mAmbientChild;
Jorim Jaggibe565df2014-04-28 17:51:23 +020068
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070069 private RemoteInputView mExpandedRemoteInput;
70 private RemoteInputView mHeadsUpRemoteInput;
71
Jorim Jaggi4e857f42014-11-17 19:14:04 +010072 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070073 private NotificationViewWrapper mExpandedWrapper;
74 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -080075 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -070076 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020077 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +010078 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -070079 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -040080 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010081 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -070082 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -080083 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -070084 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -080085 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -080086 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -080087 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -080088 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -070089 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070090 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080091 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -080092 private Runnable mExpandedVisibleListener;
Jorim Jaggi59ec3042015-06-05 15:18:43 -070093
Jorim Jaggibe4116a2015-05-20 20:04:08 -070094 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010095 = new ViewTreeObserver.OnPreDrawListener() {
96 @Override
97 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -080098 // We need to post since we don't want the notification to animate on the very first
99 // frame
100 post(new Runnable() {
101 @Override
102 public void run() {
103 mAnimate = true;
104 }
105 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100106 getViewTreeObserver().removeOnPreDrawListener(this);
107 return true;
108 }
109 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200110
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800111 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800112 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800113 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100114 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800115 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700116 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800117 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700118 /** The visible type at the start of an animation driven transformation */
119 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800120 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700121 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700122 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700123 private PendingIntent mPreviousExpandedRemoteInputIntent;
124 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700125 private RemoteInputView mCachedExpandedRemoteInput;
126 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800127
Adrian Roos599be342016-06-13 14:54:39 -0700128 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700129 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700130 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700131 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700132 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800133 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800134 private boolean mIsContentExpandable;
Adrian Roos599be342016-06-13 14:54:39 -0700135
136
Jorim Jaggibe565df2014-04-28 17:51:23 +0200137 public NotificationContentView(Context context, AttributeSet attrs) {
138 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700139 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Selim Cinek860b6da2015-12-16 19:02:19 -0800140 mMinContractedHeight = getResources().getDimensionPixelSize(
141 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800142 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
143 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200144 }
145
Adrian Roos0aac04f2016-12-08 15:59:29 -0800146 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
147 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800148 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800149 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800150 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800151 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800152 }
153
Jorim Jaggibe565df2014-04-28 17:51:23 +0200154 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700155 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
156 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
157 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
158 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
159 int maxSize = Integer.MAX_VALUE;
Selim Cinekc897bd32016-03-18 17:32:31 -0700160 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700161 if (hasFixedHeight || isHeightLimited) {
162 maxSize = MeasureSpec.getSize(heightMeasureSpec);
163 }
164 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800165 if (mExpandedChild != null) {
166 int size = Math.min(maxSize, mNotificationMaxHeight);
167 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800168 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800169 if (layoutParams.height >= 0) {
170 // An actual height is set
171 size = Math.min(maxSize, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800172 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800173 }
174 int spec = size == Integer.MAX_VALUE
175 ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
Selim Cinek6743c0b2017-01-18 18:24:01 -0800176 : MeasureSpec.makeMeasureSpec(size, useExactly
177 ? MeasureSpec.EXACTLY
178 : MeasureSpec.AT_MOST);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800179 mExpandedChild.measure(widthMeasureSpec, spec);
180 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
181 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700182 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800183 int heightSpec;
Selim Cinekf619ffc2016-02-17 14:53:05 -0800184 int size = Math.min(maxSize, mSmallHeight);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800185 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
186 boolean useExactly = false;
187 if (layoutParams.height >= 0) {
188 // An actual height is set
189 size = Math.min(size, layoutParams.height);
190 useExactly = true;
191 }
192 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800193 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
194 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800195 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800196 }
197 mContractedChild.measure(widthMeasureSpec, heightSpec);
198 int measuredHeight = mContractedChild.getMeasuredHeight();
199 if (measuredHeight < mMinContractedHeight) {
200 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
201 mContractedChild.measure(widthMeasureSpec, heightSpec);
202 }
203 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800204 if (updateContractedHeaderWidth()) {
205 mContractedChild.measure(widthMeasureSpec, heightSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700206 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700207 if (mExpandedChild != null
208 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
209 // the Expanded child is smaller then the collapsed. Let's remeasure it.
210 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
211 MeasureSpec.EXACTLY);
212 mExpandedChild.measure(widthMeasureSpec, heightSpec);
213 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700214 }
215 if (mHeadsUpChild != null) {
216 int size = Math.min(maxSize, mHeadsUpHeight);
217 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800218 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700219 if (layoutParams.height >= 0) {
220 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800221 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800222 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700223 }
224 mHeadsUpChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800225 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
226 : MeasureSpec.AT_MOST));
Selim Cinek8d490d42015-04-10 00:05:50 -0700227 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
228 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700229 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700230 int singleLineWidthSpec = widthMeasureSpec;
231 if (mSingleLineWidthIndention != 0
232 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
233 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
234 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700235 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700236 }
237 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinek7b836392015-12-04 20:02:59 -0800238 MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700239 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
240 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800241 if (mAmbientChild != null) {
242 int size = Math.min(maxSize, mNotificationAmbientHeight);
243 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800244 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800245 if (layoutParams.height >= 0) {
246 // An actual height is set
247 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800248 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800249 }
250 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800251 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
252 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800253 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
254 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700255 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700256 setMeasuredDimension(width, ownHeight);
257 }
258
Selim Cinek6ecc8102016-01-26 18:26:19 -0800259 private boolean updateContractedHeaderWidth() {
260 // We need to update the expanded and the collapsed header to have exactly the same with to
261 // have the expand buttons laid out at the same location.
262 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
263 if (contractedHeader != null) {
264 if (mExpandedChild != null
265 && mExpandedWrapper.getNotificationHeader() != null) {
266 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
267 int expandedSize = expandedHeader.getMeasuredWidth()
268 - expandedHeader.getPaddingEnd();
269 int collapsedSize = contractedHeader.getMeasuredWidth()
270 - expandedHeader.getPaddingEnd();
271 if (expandedSize != collapsedSize) {
272 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
273 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800274 contractedHeader.isLayoutRtl()
275 ? paddingEnd
276 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800277 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800278 contractedHeader.isLayoutRtl()
279 ? contractedHeader.getPaddingLeft()
280 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800281 contractedHeader.getPaddingBottom());
282 contractedHeader.setShowWorkBadgeAtEnd(true);
283 return true;
284 }
285 } else {
286 int paddingEnd = mNotificationContentMarginEnd;
287 if (contractedHeader.getPaddingEnd() != paddingEnd) {
288 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800289 contractedHeader.isLayoutRtl()
290 ? paddingEnd
291 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800292 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800293 contractedHeader.isLayoutRtl()
294 ? contractedHeader.getPaddingLeft()
295 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800296 contractedHeader.getPaddingBottom());
297 contractedHeader.setShowWorkBadgeAtEnd(false);
298 return true;
299 }
300 }
301 }
302 return false;
303 }
304
Selim Cinek860b6da2015-12-16 19:02:19 -0800305 private boolean shouldContractedBeFixedSize() {
306 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
307 }
308
Selim Cinek8d490d42015-04-10 00:05:50 -0700309 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200310 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700311 int previousHeight = 0;
312 if (mExpandedChild != null) {
313 previousHeight = mExpandedChild.getHeight();
314 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200315 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700316 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
317 mContentHeightAtAnimationStart = previousHeight;
318 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200319 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700320 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700321 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
322 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700323 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200324 }
325
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100326 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100327 protected void onAttachedToWindow() {
328 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100329 updateVisibility();
330 }
331
Selim Cinekcab4a602014-09-03 14:47:57 +0200332 public View getContractedChild() {
333 return mContractedChild;
334 }
335
336 public View getExpandedChild() {
337 return mExpandedChild;
338 }
339
Selim Cinek8d490d42015-04-10 00:05:50 -0700340 public View getHeadsUpChild() {
341 return mHeadsUpChild;
342 }
343
Adrian Roos0aac04f2016-12-08 15:59:29 -0800344 public View getAmbientChild() {
345 return mAmbientChild;
346 }
347
Jorim Jaggibe565df2014-04-28 17:51:23 +0200348 public void setContractedChild(View child) {
349 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200350 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200351 removeView(mContractedChild);
352 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200353 addView(child);
354 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700355 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
356 mContainingNotification);
Jorim Jaggi10ad7612014-12-08 18:41:11 +0100357 mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200358 }
359
360 public void setExpandedChild(View child) {
361 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800362 mPreviousExpandedRemoteInputIntent = null;
363 if (mExpandedRemoteInput != null) {
364 mExpandedRemoteInput.onNotificationUpdateOrReset();
365 if (mExpandedRemoteInput.isActive()) {
366 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
367 mCachedExpandedRemoteInput = mExpandedRemoteInput;
368 mExpandedRemoteInput.dispatchStartTemporaryDetach();
369 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
370 }
371 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200372 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200373 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800374 mExpandedRemoteInput = null;
375 }
376 if (child == null) {
377 mExpandedChild = null;
378 mExpandedWrapper = null;
379 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
380 mVisibleType = VISIBLE_TYPE_CONTRACTED;
381 }
382 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
383 mTransformationStartVisibleType = UNDEFINED;
384 }
385 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200386 }
387 addView(child);
388 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700389 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
390 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200391 }
392
Selim Cinek8d490d42015-04-10 00:05:50 -0700393 public void setHeadsUpChild(View child) {
394 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800395 mPreviousHeadsUpRemoteInputIntent = null;
396 if (mHeadsUpRemoteInput != null) {
397 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
398 if (mHeadsUpRemoteInput.isActive()) {
399 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
400 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
401 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
402 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
403 }
404 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700405 mHeadsUpChild.animate().cancel();
406 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800407 mHeadsUpRemoteInput = null;
408 }
409 if (child == null) {
410 mHeadsUpChild = null;
411 mHeadsUpWrapper = null;
412 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
413 mVisibleType = VISIBLE_TYPE_CONTRACTED;
414 }
415 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
416 mTransformationStartVisibleType = UNDEFINED;
417 }
418 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700419 }
420 addView(child);
421 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700422 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
423 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700424 }
425
Adrian Roos0aac04f2016-12-08 15:59:29 -0800426 public void setAmbientChild(View child) {
427 if (mAmbientChild != null) {
428 mAmbientChild.animate().cancel();
429 removeView(mAmbientChild);
430 }
431 addView(child);
432 mAmbientChild = child;
433 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
434 mContainingNotification);
435 }
436
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100437 @Override
438 protected void onVisibilityChanged(View changedView, int visibility) {
439 super.onVisibilityChanged(changedView, visibility);
440 updateVisibility();
441 }
442
443 private void updateVisibility() {
444 setVisible(isShown());
445 }
446
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700447 @Override
448 protected void onDetachedFromWindow() {
449 super.onDetachedFromWindow();
450 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
451 }
452
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100453 private void setVisible(final boolean isVisible) {
454 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700455 // This call can happen multiple times, but removing only removes a single one.
456 // We therefore need to remove the old one.
457 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100458 // We only animate if we are drawn at least once, otherwise the view might animate when
459 // it's shown the first time
460 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
461 } else {
462 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
463 mAnimate = false;
464 }
465 }
466
Selim Cineke9bad242016-06-15 11:46:37 -0700467 private void focusExpandButtonIfNecessary() {
468 if (mFocusOnVisibilityChange) {
469 NotificationHeaderView header = getVisibleNotificationHeader();
470 if (header != null) {
471 ImageView expandButton = header.getExpandButton();
472 if (expandButton != null) {
473 expandButton.requestAccessibilityFocus();
474 }
475 }
476 mFocusOnVisibilityChange = false;
477 }
478 }
479
Selim Cinekb5605e52015-02-20 18:21:41 +0100480 public void setContentHeight(int contentHeight) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700481 mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100482 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400483
484 int minHeightHint = getMinContentHeightHint();
485
486 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
487 if (wrapper != null) {
488 wrapper.setContentHeight(mContentHeight, minHeightHint);
489 }
490
491 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
492 if (wrapper != null) {
493 wrapper.setContentHeight(mContentHeight, minHeightHint);
494 }
495
Jorim Jaggibe565df2014-04-28 17:51:23 +0200496 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700497 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200498 }
499
Adrian Roos181385c2016-05-05 17:45:44 -0400500 /**
501 * @return the minimum apparent height that the wrapper should allow for the purpose
502 * of aligning elements at the bottom edge. If this is larger than the content
503 * height, the notification is clipped instead of being further shrunk.
504 */
505 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700506 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400507 return mContext.getResources().getDimensionPixelSize(
508 com.android.internal.R.dimen.notification_action_list_height);
509 }
Adrian Roos599be342016-06-13 14:54:39 -0700510
511 // Transition between heads-up & expanded, or pinned.
512 if (mHeadsUpChild != null && mExpandedChild != null) {
513 boolean transitioningBetweenHunAndExpanded =
514 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
515 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700516 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800517 && (mIsHeadsUp || mHeadsUpAnimatingAway)
518 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700519 if (transitioningBetweenHunAndExpanded || pinned) {
520 return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight());
521 }
522 }
523
524 // Size change of the expanded version
525 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
526 && mExpandedChild != null) {
527 return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight());
528 }
529
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700530 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800531 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
532 hint = mAmbientChild.getHeight();
533 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700534 hint = mHeadsUpChild.getHeight();
Adrian Roos599be342016-06-13 14:54:39 -0700535 } else if (mExpandedChild != null) {
536 hint = mExpandedChild.getHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400537 } else {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700538 hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
539 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400540 }
Adrian Roos599be342016-06-13 14:54:39 -0700541
542 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700543 hint = Math.min(hint, mExpandedChild.getHeight());
544 }
545 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400546 }
547
Adrian Roos599be342016-06-13 14:54:39 -0700548 private boolean isTransitioningFromTo(int from, int to) {
549 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
550 && mVisibleType == to;
551 }
552
553 private boolean isVisibleOrTransitioning(int type) {
554 return mVisibleType == type || mTransformationStartVisibleType == type
555 || mAnimationStartVisibleType == type;
556 }
557
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800558 private void updateContentTransformation() {
559 int visibleType = calculateVisibleType();
560 if (visibleType != mVisibleType) {
561 // A new transformation starts
562 mTransformationStartVisibleType = mVisibleType;
563 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
564 final TransformableView hiddenView = getTransformableViewForVisibleType(
565 mTransformationStartVisibleType);
566 shownView.transformFrom(hiddenView, 0.0f);
567 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
568 hiddenView.transformTo(shownView, 0.0f);
569 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800570 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800571 }
Selim Cineke8578872016-05-03 16:42:50 -0700572 if (mForceSelectNextLayout) {
573 forceUpdateVisibilities();
574 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800575 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700576 && mVisibleType != mTransformationStartVisibleType
577 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800578 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
579 final TransformableView hiddenView = getTransformableViewForVisibleType(
580 mTransformationStartVisibleType);
581 float transformationAmount = calculateTransformationAmount();
582 shownView.transformFrom(hiddenView, transformationAmount);
583 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800584 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800585 } else {
586 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800587 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800588 }
589 }
590
Selim Cinekc3179332016-03-04 14:44:56 -0800591 private void updateBackgroundTransformation(float transformationAmount) {
592 int endColor = getBackgroundColor(mVisibleType);
593 int startColor = getBackgroundColor(mTransformationStartVisibleType);
594 if (endColor != startColor) {
595 if (startColor == 0) {
596 startColor = mContainingNotification.getBackgroundColorWithoutTint();
597 }
598 if (endColor == 0) {
599 endColor = mContainingNotification.getBackgroundColorWithoutTint();
600 }
601 endColor = NotificationUtils.interpolateColors(startColor, endColor,
602 transformationAmount);
603 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400604 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800605 mContainingNotification.setContentBackground(endColor, false, this);
606 }
607
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800608 private float calculateTransformationAmount() {
609 int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
610 int endHeight = getViewForVisibleType(mVisibleType).getHeight();
611 int progress = Math.abs(mContentHeight - startHeight);
612 int totalDistance = Math.abs(endHeight - startHeight);
613 float amount = (float) progress / (float) totalDistance;
614 return Math.min(1.0f, amount);
615 }
616
Selim Cinekb5605e52015-02-20 18:21:41 +0100617 public int getContentHeight() {
618 return mContentHeight;
619 }
620
Jorim Jaggibe565df2014-04-28 17:51:23 +0200621 public int getMaxHeight() {
Selim Cinek77019c72015-12-09 10:18:02 -0800622 if (mExpandedChild != null) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700623 return mExpandedChild.getHeight();
Selim Cinekc494e382017-01-31 16:09:23 -0800624 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinek77019c72015-12-09 10:18:02 -0800625 return mHeadsUpChild.getHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -0700626 }
Selim Cinek860b6da2015-12-16 19:02:19 -0800627 return mContractedChild.getHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200628 }
629
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200630 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800631 return getMinHeight(false /* likeGroupExpanded */);
632 }
633
634 public int getMinHeight(boolean likeGroupExpanded) {
Selim Cinek414ad332017-02-24 19:06:12 -0800635 if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800636 return mContractedChild.getHeight();
Selim Cinek42357e02016-02-24 18:48:01 -0800637 } else {
638 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700639 }
640 }
641
642 private boolean isGroupExpanded() {
643 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200644 }
645
Jorim Jaggibe565df2014-04-28 17:51:23 +0200646 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200647 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200648 updateClipping();
649 }
650
Selim Cineka686b2c2016-10-26 13:58:27 -0700651
652 public void setClipBottomAmount(int clipBottomAmount) {
653 mClipBottomAmount = clipBottomAmount;
654 updateClipping();
655 }
656
Selim Cinek875a3a12016-11-18 17:52:16 -0800657 @Override
658 public void setTranslationY(float translationY) {
659 super.setTranslationY(translationY);
660 updateClipping();
661 }
662
Jorim Jaggibe565df2014-04-28 17:51:23 +0200663 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100664 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800665 int top = (int) (mClipTopAmount - getTranslationY());
666 int bottom = (int) (mContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700667 bottom = Math.max(top, bottom);
668 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100669 setClipBounds(mClipBounds);
670 } else {
671 setClipBounds(null);
672 }
673 }
674
675 public void setClipToActualHeight(boolean clipToActualHeight) {
676 mClipToActualHeight = clipToActualHeight;
677 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200678 }
679
Jorim Jaggi11298832014-05-24 16:18:38 +0200680 private void selectLayout(boolean animate, boolean force) {
681 if (mContractedChild == null) {
682 return;
683 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800684 if (mUserExpanding) {
685 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700686 } else {
687 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700688 boolean changedType = visibleType != mVisibleType;
689 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400690 View visibleView = getViewForVisibleType(visibleType);
691 if (visibleView != null) {
692 visibleView.setVisibility(VISIBLE);
693 transferRemoteInputFocus(visibleType);
694 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700695
Selim Cinek589fd3e2016-04-26 18:17:57 -0700696 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
697 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
698 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
699 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
700 animateToVisibleType(visibleType);
701 } else {
702 updateViewVisibilities(visibleType);
703 }
704 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700705 if (changedType) {
706 focusExpandButtonIfNecessary();
707 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800708 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
709 if (visibleWrapper != null) {
710 visibleWrapper.setContentHeight(mContentHeight, getMinContentHeightHint());
711 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700712 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200713 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700714 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700715 }
716
717 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800718 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
719 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
720 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
721 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
722 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos4320e892017-01-24 12:50:34 -0800723 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800724 // forceUpdateVisibilities cancels outstanding animations without updating the
725 // mAnimationStartVisibleType. Do so here instead.
726 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800727 }
728
Adrian Roos4320e892017-01-24 12:50:34 -0800729 private void fireExpandedVisibleListenerIfVisible() {
730 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
731 && mExpandedChild.getVisibility() == VISIBLE) {
732 Runnable listener = mExpandedVisibleListener;
733 mExpandedVisibleListener = null;
734 listener.run();
735 }
736 }
737
Adrian Roos0aac04f2016-12-08 15:59:29 -0800738 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
739 if (view == null) {
740 return;
741 }
742 boolean visible = mVisibleType == type
743 || mTransformationStartVisibleType == type;
744 if (!visible) {
745 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700746 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800747 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200748 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200749 }
750
Selim Cinekc3179332016-03-04 14:44:56 -0800751 public void updateBackgroundColor(boolean animate) {
752 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400753 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800754 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
755 }
756
Mady Mellorb0a82462016-04-30 17:31:02 -0700757 public int getVisibleType() {
758 return mVisibleType;
759 }
760
761 public int getBackgroundColorForExpansionState() {
762 // When expanding or user locked we want the new type, when collapsing we want
763 // the original type
764 final int visibleType = (mContainingNotification.isGroupExpanded()
765 || mContainingNotification.isUserLocked())
766 ? calculateVisibleType()
767 : getVisibleType();
768 return getBackgroundColor(visibleType);
769 }
770
771 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800772 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
773 int customBackgroundColor = 0;
774 if (currentVisibleWrapper != null) {
775 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
776 }
777 return customBackgroundColor;
778 }
779
Selim Cinek684a4422015-04-15 16:18:39 -0700780 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800781 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
782 mContractedChild, mContractedWrapper);
783 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
784 mExpandedChild, mExpandedWrapper);
785 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
786 mHeadsUpChild, mHeadsUpWrapper);
787 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
788 mSingleLineView, mSingleLineView);
789 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
790 mAmbientChild, mAmbientWrapper);
Adrian Roos4320e892017-01-24 12:50:34 -0800791 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800792 // updateViewVisibilities cancels outstanding animations without updating the
793 // mAnimationStartVisibleType. Do so here instead.
794 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800795 }
796
797 private void updateViewVisibility(int visibleType, int type, View view,
798 TransformableView wrapper) {
799 if (view != null) {
800 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -0700801 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700802 }
803
Selim Cinek4ffd6362015-12-29 15:12:23 +0100804 private void animateToVisibleType(int visibleType) {
805 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
806 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700807 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800808 shownView.setVisible(true);
809 return;
810 }
Adrian Roos599be342016-06-13 14:54:39 -0700811 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100812 shownView.transformFrom(hiddenView);
813 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
814 hiddenView.transformTo(shownView, new Runnable() {
815 @Override
816 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800817 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
818 hiddenView.setVisible(false);
819 }
Adrian Roos599be342016-06-13 14:54:39 -0700820 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100821 }
822 });
Adrian Roos4320e892017-01-24 12:50:34 -0800823 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +0200824 }
825
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700826 private void transferRemoteInputFocus(int visibleType) {
827 if (visibleType == VISIBLE_TYPE_HEADSUP
828 && mHeadsUpRemoteInput != null
829 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
830 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
831 }
832 if (visibleType == VISIBLE_TYPE_EXPANDED
833 && mExpandedRemoteInput != null
834 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
835 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
836 }
837 }
838
Selim Cinek684a4422015-04-15 16:18:39 -0700839 /**
840 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100841 * @return the corresponding transformable view according to the given visible type
842 */
843 private TransformableView getTransformableViewForVisibleType(int visibleType) {
844 switch (visibleType) {
845 case VISIBLE_TYPE_EXPANDED:
846 return mExpandedWrapper;
847 case VISIBLE_TYPE_HEADSUP:
848 return mHeadsUpWrapper;
849 case VISIBLE_TYPE_SINGLELINE:
850 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800851 case VISIBLE_TYPE_AMBIENT:
852 return mAmbientWrapper;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100853 default:
854 return mContractedWrapper;
855 }
856 }
857
858 /**
859 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -0700860 * @return the corresponding view according to the given visible type
861 */
862 private View getViewForVisibleType(int visibleType) {
863 switch (visibleType) {
864 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -0700865 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700866 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -0700867 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -0700868 case VISIBLE_TYPE_SINGLELINE:
869 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800870 case VISIBLE_TYPE_AMBIENT:
871 return mAmbientChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700872 default:
873 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -0700874 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700875 }
876
Selim Cinekc3179332016-03-04 14:44:56 -0800877 private NotificationViewWrapper getVisibleWrapper(int visibleType) {
878 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -0800879 case VISIBLE_TYPE_EXPANDED:
880 return mExpandedWrapper;
881 case VISIBLE_TYPE_HEADSUP:
882 return mHeadsUpWrapper;
883 case VISIBLE_TYPE_CONTRACTED:
884 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800885 case VISIBLE_TYPE_AMBIENT:
886 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -0800887 default:
888 return null;
889 }
890 }
891
Selim Cinek684a4422015-04-15 16:18:39 -0700892 /**
893 * @return one of the static enum types in this view, calculated form the current state
894 */
Mady Mellorb0a82462016-04-30 17:31:02 -0700895 public int calculateVisibleType() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800896 if (mDark && !mIsChildInGroup) {
897 // TODO: Handle notification groups
898 return VISIBLE_TYPE_AMBIENT;
899 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800900 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -0800901 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -0800902 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -0800903 ? mContainingNotification.getMaxContentHeight()
904 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -0800905 if (height == 0) {
906 height = mContentHeight;
907 }
Selim Cinek42357e02016-02-24 18:48:01 -0800908 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -0800909 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -0700910 ? VISIBLE_TYPE_SINGLELINE
911 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800912 return mTransformationStartVisibleType == collapsedVisualType
913 ? expandedVisualType
914 : collapsedVisualType;
915 }
Selim Cinek51d94912016-03-02 15:34:28 -0800916 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
917 int viewHeight = mContentHeight;
918 if (intrinsicHeight != 0) {
919 // the intrinsicHeight might be 0 because it was just reset.
920 viewHeight = Math.min(mContentHeight, intrinsicHeight);
921 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800922 return getVisualTypeForHeight(viewHeight);
923 }
924
925 private int getVisualTypeForHeight(float viewHeight) {
926 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800927 if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
Adrian Roos777ef562015-12-01 17:37:14 -0800928 return VISIBLE_TYPE_EXPANDED;
929 }
Selim Cinek414ad332017-02-24 19:06:12 -0800930 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800931 return VISIBLE_TYPE_SINGLELINE;
932 }
Adrian Roos777ef562015-12-01 17:37:14 -0800933
Selim Cinekc494e382017-01-31 16:09:23 -0800934 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
935 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800936 if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -0700937 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -0700938 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700939 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700940 }
941 } else {
Selim Cineka4091502016-02-11 17:21:16 -0800942 if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
Selim Cinek6ac3fa82016-05-10 17:11:54 -0400943 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -0800944 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -0700945 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700946 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700947 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700948 }
949 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200950 }
951
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200952 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800953 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200954 }
John Spurlocke15452b2014-08-21 09:44:39 -0400955
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100956 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -0800957 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -0800958 return;
959 }
John Spurlocke15452b2014-08-21 09:44:39 -0400960 mDark = dark;
Selim Cinek19ba7052016-01-27 20:04:27 -0800961 if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800962 mContractedWrapper.setDark(dark, fade, delay);
963 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800964 if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800965 mExpandedWrapper.setDark(dark, fade, delay);
966 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800967 if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800968 mHeadsUpWrapper.setDark(dark, fade, delay);
969 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800970 if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
Selim Cinek9c7712d2015-12-08 19:19:48 -0800971 mSingleLineView.setDark(dark, fade, delay);
972 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800973 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -0400974 }
975
Selim Cinek8d490d42015-04-10 00:05:50 -0700976 public void setHeadsUp(boolean headsUp) {
977 mIsHeadsUp = headsUp;
978 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -0800979 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -0700980 }
981
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +0200982 @Override
983 public boolean hasOverlappingRendering() {
984
985 // This is not really true, but good enough when fading from the contracted to the expanded
986 // layout, and saves us some layers.
987 return false;
988 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700989
Selim Cinek1a48bab2017-02-17 19:38:40 -0800990 public void setLegacy(boolean legacy) {
991 mLegacy = legacy;
992 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -0800993 }
994
Selim Cinek1a48bab2017-02-17 19:38:40 -0800995 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -0800996 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800997 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -0800998 }
999 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001000 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001001 }
1002 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001003 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001004 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001005 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001006
Selim Cinek83bc7832015-10-22 13:26:54 -07001007 public void setIsChildInGroup(boolean isChildInGroup) {
1008 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001009 if (mContractedChild != null) {
1010 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1011 }
1012 if (mExpandedChild != null) {
1013 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1014 }
1015 if (mHeadsUpChild != null) {
1016 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1017 }
1018 if (mAmbientChild != null) {
1019 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1020 }
Selim Cinek83bc7832015-10-22 13:26:54 -07001021 updateSingleLineView();
1022 }
1023
Adrian Roosb88b1a12015-12-09 18:51:05 -08001024 public void onNotificationUpdated(NotificationData.Entry entry) {
1025 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -08001026 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Selim Cinek83bc7832015-10-22 13:26:54 -07001027 updateSingleLineView();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001028 if (mContractedChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001029 mContractedWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001030 }
1031 if (mExpandedChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001032 mExpandedWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001033 }
1034 if (mHeadsUpChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001035 mHeadsUpWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001036 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001037 if (mAmbientChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001038 mAmbientWrapper.notifyContentUpdated(entry.row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001039 }
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001040 applyRemoteInput(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001041 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001042 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001043 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001044 mPreviousExpandedRemoteInputIntent = null;
1045 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001046 }
1047
1048 private void updateSingleLineView() {
1049 if (mIsChildInGroup) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001050 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001051 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekde33a4a2016-02-11 16:43:41 -08001052 } else if (mSingleLineView != null) {
1053 removeView(mSingleLineView);
1054 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001055 }
1056 }
1057
Adrian Roosb88b1a12015-12-09 18:51:05 -08001058 private void applyRemoteInput(final NotificationData.Entry entry) {
1059 if (mRemoteInputController == null) {
1060 return;
1061 }
1062
1063 boolean hasRemoteInput = false;
1064
1065 Notification.Action[] actions = entry.notification.getNotification().actions;
1066 if (actions != null) {
1067 for (Notification.Action a : actions) {
1068 if (a.getRemoteInputs() != null) {
1069 for (RemoteInput ri : a.getRemoteInputs()) {
1070 if (ri.getAllowFreeFormInput()) {
1071 hasRemoteInput = true;
1072 break;
1073 }
1074 }
1075 }
1076 }
1077 }
1078
1079 View bigContentView = mExpandedChild;
1080 if (bigContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001081 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001082 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1083 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001084 } else {
1085 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001086 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001087 if (mCachedExpandedRemoteInput != null
1088 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1089 // We had a cached remote input but didn't reuse it. Clean up required.
1090 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1091 }
1092 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001093
Adrian Roosb88b1a12015-12-09 18:51:05 -08001094 View headsUpContentView = mHeadsUpChild;
1095 if (headsUpContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001096 mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001097 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001098 } else {
1099 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001100 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001101 if (mCachedHeadsUpRemoteInput != null
1102 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1103 // We had a cached remote input but didn't reuse it. Clean up required.
1104 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1105 }
1106 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001107 }
1108
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001109 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001110 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001111 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001112 View actionContainerCandidate = view.findViewById(
1113 com.android.internal.R.id.actions_container);
1114 if (actionContainerCandidate instanceof FrameLayout) {
1115 RemoteInputView existing = (RemoteInputView)
1116 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1117
1118 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001119 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001120 }
1121
1122 if (existing == null && hasRemoteInput) {
1123 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001124 if (cachedView == null) {
1125 RemoteInputView riv = RemoteInputView.inflate(
1126 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001127
Adrian Roos7813dd72016-09-23 17:12:17 -07001128 riv.setVisibility(View.INVISIBLE);
1129 actionContainer.addView(riv, new LayoutParams(
1130 ViewGroup.LayoutParams.MATCH_PARENT,
1131 ViewGroup.LayoutParams.MATCH_PARENT)
1132 );
1133 existing = riv;
1134 } else {
1135 actionContainer.addView(cachedView);
1136 cachedView.dispatchFinishTemporaryDetach();
1137 cachedView.requestFocus();
1138 existing = cachedView;
1139 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001140 }
1141 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001142 int color = entry.notification.getNotification().color;
1143 if (color == Notification.COLOR_DEFAULT) {
1144 color = mContext.getColor(R.color.default_remote_input_background);
1145 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001146 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001147 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001148 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001149
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001150 existing.setWrapper(wrapper);
1151
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001152 if (existingPendingIntent != null || existing.isActive()) {
1153 // The current action could be gone, or the pending intent no longer valid.
1154 // If we find a matching action in the new notification, focus, otherwise close.
1155 Notification.Action[] actions = entry.notification.getNotification().actions;
1156 if (existingPendingIntent != null) {
1157 existing.setPendingIntent(existingPendingIntent);
1158 }
1159 if (existing.updatePendingIntentFromActions(actions)) {
1160 if (!existing.isActive()) {
1161 existing.focus();
1162 }
1163 } else {
1164 if (existing.isActive()) {
1165 existing.close();
1166 }
1167 }
1168 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001169 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001170 return existing;
1171 }
1172 return null;
1173 }
1174
1175 public void closeRemoteInput() {
1176 if (mHeadsUpRemoteInput != null) {
1177 mHeadsUpRemoteInput.close();
1178 }
1179 if (mExpandedRemoteInput != null) {
1180 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001181 }
1182 }
1183
Selim Cinek83bc7832015-10-22 13:26:54 -07001184 public void setGroupManager(NotificationGroupManager groupManager) {
1185 mGroupManager = groupManager;
1186 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001187
Adrian Roosb88b1a12015-12-09 18:51:05 -08001188 public void setRemoteInputController(RemoteInputController r) {
1189 mRemoteInputController = r;
1190 }
1191
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001192 public void setExpandClickListener(OnClickListener expandClickListener) {
1193 mExpandClickListener = expandClickListener;
1194 }
1195
1196 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001197 mExpandable = expandable;
1198 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001199 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001200 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1201 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001202 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001203 expandable = false;
1204 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001205 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001206 expandable = false;
1207 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001208 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001209 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001210 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001211 }
1212 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001213 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001214 }
1215 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001216 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001217 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001218 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001219 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001220
1221 public NotificationHeaderView getNotificationHeader() {
1222 NotificationHeaderView header = null;
1223 if (mContractedChild != null) {
1224 header = mContractedWrapper.getNotificationHeader();
1225 }
1226 if (header == null && mExpandedChild != null) {
1227 header = mExpandedWrapper.getNotificationHeader();
1228 }
1229 if (header == null && mHeadsUpChild != null) {
1230 header = mHeadsUpWrapper.getNotificationHeader();
1231 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001232 if (header == null && mAmbientChild != null) {
1233 header = mAmbientWrapper.getNotificationHeader();
1234 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001235 return header;
1236 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001237
Selim Cinek34eda5e2016-02-18 17:10:43 -08001238 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001239 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001240 return wrapper == null ? null : wrapper.getNotificationHeader();
1241 }
1242
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001243 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1244 mContainingNotification = containingNotification;
1245 }
1246
1247 public void requestSelectLayout(boolean needsAnimation) {
1248 selectLayout(needsAnimation, false);
1249 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001250
1251 public void reInflateViews() {
1252 if (mIsChildInGroup && mSingleLineView != null) {
1253 removeView(mSingleLineView);
1254 mSingleLineView = null;
1255 updateSingleLineView();
1256 }
1257 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001258
1259 public void setUserExpanding(boolean userExpanding) {
1260 mUserExpanding = userExpanding;
1261 if (userExpanding) {
1262 mTransformationStartVisibleType = mVisibleType;
1263 } else {
1264 mTransformationStartVisibleType = UNDEFINED;
1265 mVisibleType = calculateVisibleType();
1266 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001267 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001268 }
1269 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001270
1271 /**
1272 * Set by how much the single line view should be indented. Used when a overflow indicator is
1273 * present and only during measuring
1274 */
1275 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1276 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1277 mSingleLineWidthIndention = singleLineWidthIndention;
1278 mContainingNotification.forceLayout();
1279 forceLayout();
1280 }
1281 }
1282
1283 public HybridNotificationView getSingleLineView() {
1284 return mSingleLineView;
1285 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001286
1287 public void setRemoved() {
1288 if (mExpandedRemoteInput != null) {
1289 mExpandedRemoteInput.setRemoved();
1290 }
1291 if (mHeadsUpRemoteInput != null) {
1292 mHeadsUpRemoteInput.setRemoved();
1293 }
1294 }
Adrian Roos599be342016-06-13 14:54:39 -07001295
1296 public void setContentHeightAnimating(boolean animating) {
1297 if (!animating) {
1298 mContentHeightAtAnimationStart = UNDEFINED;
1299 }
1300 }
Selim Cineke9bad242016-06-15 11:46:37 -07001301
Adrian Roose18033c2017-01-17 15:22:49 -08001302 @VisibleForTesting
1303 boolean isAnimatingVisibleType() {
1304 return mAnimationStartVisibleType != UNDEFINED;
1305 }
1306
Selim Cinekcafa87f2016-10-26 17:00:17 -07001307 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1308 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001309 selectLayout(false /* animate */, true /* force */);
1310 }
1311
Selim Cineke9bad242016-06-15 11:46:37 -07001312 public void setFocusOnVisibilityChange() {
1313 mFocusOnVisibilityChange = true;
1314 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001315
1316 public void setIconsVisible(boolean iconsVisible) {
1317 mIconsVisible = iconsVisible;
1318 updateIconVisibilities();
1319 }
1320
1321 private void updateIconVisibilities() {
1322 if (mContractedWrapper != null) {
1323 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1324 if (header != null) {
1325 header.getIcon().setForceHidden(!mIconsVisible);
1326 }
1327 }
1328 if (mHeadsUpWrapper != null) {
1329 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1330 if (header != null) {
1331 header.getIcon().setForceHidden(!mIconsVisible);
1332 }
1333 }
1334 if (mExpandedWrapper != null) {
1335 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1336 if (header != null) {
1337 header.getIcon().setForceHidden(!mIconsVisible);
1338 }
1339 }
1340 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001341
Adrian Roos4320e892017-01-24 12:50:34 -08001342 @Override
1343 public void onVisibilityAggregated(boolean isVisible) {
1344 super.onVisibilityAggregated(isVisible);
1345 if (isVisible) {
1346 fireExpandedVisibleListenerIfVisible();
1347 }
1348 }
1349
1350 /**
1351 * Sets a one-shot listener for when the expanded view becomes visible.
1352 *
1353 * This will fire the listener immediately if the expanded view is already visible.
1354 */
1355 public void setOnExpandedVisibleListener(Runnable r) {
1356 mExpandedVisibleListener = r;
1357 fireExpandedVisibleListenerIfVisible();
1358 }
1359
Selim Cinek6743c0b2017-01-18 18:24:01 -08001360 public void setIsLowPriority(boolean isLowPriority) {
1361 mIsLowPriority = isLowPriority;
1362 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001363}