blob: e7bf98336e8c0809eba2de16fc4ea2d5e6daff5d [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 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700431 if (child == null) {
432 return;
433 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800434 addView(child);
435 mAmbientChild = child;
436 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
437 mContainingNotification);
438 }
439
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100440 @Override
441 protected void onVisibilityChanged(View changedView, int visibility) {
442 super.onVisibilityChanged(changedView, visibility);
443 updateVisibility();
444 }
445
446 private void updateVisibility() {
447 setVisible(isShown());
448 }
449
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700450 @Override
451 protected void onDetachedFromWindow() {
452 super.onDetachedFromWindow();
453 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
454 }
455
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100456 private void setVisible(final boolean isVisible) {
457 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700458 // This call can happen multiple times, but removing only removes a single one.
459 // We therefore need to remove the old one.
460 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100461 // We only animate if we are drawn at least once, otherwise the view might animate when
462 // it's shown the first time
463 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
464 } else {
465 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
466 mAnimate = false;
467 }
468 }
469
Selim Cineke9bad242016-06-15 11:46:37 -0700470 private void focusExpandButtonIfNecessary() {
471 if (mFocusOnVisibilityChange) {
472 NotificationHeaderView header = getVisibleNotificationHeader();
473 if (header != null) {
474 ImageView expandButton = header.getExpandButton();
475 if (expandButton != null) {
476 expandButton.requestAccessibilityFocus();
477 }
478 }
479 mFocusOnVisibilityChange = false;
480 }
481 }
482
Selim Cinekb5605e52015-02-20 18:21:41 +0100483 public void setContentHeight(int contentHeight) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700484 mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100485 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400486
487 int minHeightHint = getMinContentHeightHint();
488
489 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
490 if (wrapper != null) {
491 wrapper.setContentHeight(mContentHeight, minHeightHint);
492 }
493
494 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
495 if (wrapper != null) {
496 wrapper.setContentHeight(mContentHeight, minHeightHint);
497 }
498
Jorim Jaggibe565df2014-04-28 17:51:23 +0200499 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700500 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200501 }
502
Adrian Roos181385c2016-05-05 17:45:44 -0400503 /**
504 * @return the minimum apparent height that the wrapper should allow for the purpose
505 * of aligning elements at the bottom edge. If this is larger than the content
506 * height, the notification is clipped instead of being further shrunk.
507 */
508 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700509 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400510 return mContext.getResources().getDimensionPixelSize(
511 com.android.internal.R.dimen.notification_action_list_height);
512 }
Adrian Roos599be342016-06-13 14:54:39 -0700513
514 // Transition between heads-up & expanded, or pinned.
515 if (mHeadsUpChild != null && mExpandedChild != null) {
516 boolean transitioningBetweenHunAndExpanded =
517 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
518 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700519 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800520 && (mIsHeadsUp || mHeadsUpAnimatingAway)
521 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700522 if (transitioningBetweenHunAndExpanded || pinned) {
523 return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight());
524 }
525 }
526
527 // Size change of the expanded version
528 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
529 && mExpandedChild != null) {
530 return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight());
531 }
532
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700533 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800534 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
535 hint = mAmbientChild.getHeight();
536 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700537 hint = mHeadsUpChild.getHeight();
Adrian Roos599be342016-06-13 14:54:39 -0700538 } else if (mExpandedChild != null) {
539 hint = mExpandedChild.getHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400540 } else {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700541 hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
542 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400543 }
Adrian Roos599be342016-06-13 14:54:39 -0700544
545 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700546 hint = Math.min(hint, mExpandedChild.getHeight());
547 }
548 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400549 }
550
Adrian Roos599be342016-06-13 14:54:39 -0700551 private boolean isTransitioningFromTo(int from, int to) {
552 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
553 && mVisibleType == to;
554 }
555
556 private boolean isVisibleOrTransitioning(int type) {
557 return mVisibleType == type || mTransformationStartVisibleType == type
558 || mAnimationStartVisibleType == type;
559 }
560
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800561 private void updateContentTransformation() {
562 int visibleType = calculateVisibleType();
563 if (visibleType != mVisibleType) {
564 // A new transformation starts
565 mTransformationStartVisibleType = mVisibleType;
566 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
567 final TransformableView hiddenView = getTransformableViewForVisibleType(
568 mTransformationStartVisibleType);
569 shownView.transformFrom(hiddenView, 0.0f);
570 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
571 hiddenView.transformTo(shownView, 0.0f);
572 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800573 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800574 }
Selim Cineke8578872016-05-03 16:42:50 -0700575 if (mForceSelectNextLayout) {
576 forceUpdateVisibilities();
577 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800578 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700579 && mVisibleType != mTransformationStartVisibleType
580 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800581 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
582 final TransformableView hiddenView = getTransformableViewForVisibleType(
583 mTransformationStartVisibleType);
584 float transformationAmount = calculateTransformationAmount();
585 shownView.transformFrom(hiddenView, transformationAmount);
586 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800587 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800588 } else {
589 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800590 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800591 }
592 }
593
Selim Cinekc3179332016-03-04 14:44:56 -0800594 private void updateBackgroundTransformation(float transformationAmount) {
595 int endColor = getBackgroundColor(mVisibleType);
596 int startColor = getBackgroundColor(mTransformationStartVisibleType);
597 if (endColor != startColor) {
598 if (startColor == 0) {
599 startColor = mContainingNotification.getBackgroundColorWithoutTint();
600 }
601 if (endColor == 0) {
602 endColor = mContainingNotification.getBackgroundColorWithoutTint();
603 }
604 endColor = NotificationUtils.interpolateColors(startColor, endColor,
605 transformationAmount);
606 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400607 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800608 mContainingNotification.setContentBackground(endColor, false, this);
609 }
610
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800611 private float calculateTransformationAmount() {
612 int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
613 int endHeight = getViewForVisibleType(mVisibleType).getHeight();
614 int progress = Math.abs(mContentHeight - startHeight);
615 int totalDistance = Math.abs(endHeight - startHeight);
616 float amount = (float) progress / (float) totalDistance;
617 return Math.min(1.0f, amount);
618 }
619
Selim Cinekb5605e52015-02-20 18:21:41 +0100620 public int getContentHeight() {
621 return mContentHeight;
622 }
623
Jorim Jaggibe565df2014-04-28 17:51:23 +0200624 public int getMaxHeight() {
Selim Cinek77019c72015-12-09 10:18:02 -0800625 if (mExpandedChild != null) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700626 return mExpandedChild.getHeight();
Selim Cinekc494e382017-01-31 16:09:23 -0800627 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinek77019c72015-12-09 10:18:02 -0800628 return mHeadsUpChild.getHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -0700629 }
Selim Cinek860b6da2015-12-16 19:02:19 -0800630 return mContractedChild.getHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200631 }
632
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200633 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800634 return getMinHeight(false /* likeGroupExpanded */);
635 }
636
637 public int getMinHeight(boolean likeGroupExpanded) {
Selim Cinek414ad332017-02-24 19:06:12 -0800638 if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800639 return mContractedChild.getHeight();
Selim Cinek42357e02016-02-24 18:48:01 -0800640 } else {
641 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700642 }
643 }
644
645 private boolean isGroupExpanded() {
646 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200647 }
648
Jorim Jaggibe565df2014-04-28 17:51:23 +0200649 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200650 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200651 updateClipping();
652 }
653
Selim Cineka686b2c2016-10-26 13:58:27 -0700654
655 public void setClipBottomAmount(int clipBottomAmount) {
656 mClipBottomAmount = clipBottomAmount;
657 updateClipping();
658 }
659
Selim Cinek875a3a12016-11-18 17:52:16 -0800660 @Override
661 public void setTranslationY(float translationY) {
662 super.setTranslationY(translationY);
663 updateClipping();
664 }
665
Jorim Jaggibe565df2014-04-28 17:51:23 +0200666 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100667 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800668 int top = (int) (mClipTopAmount - getTranslationY());
669 int bottom = (int) (mContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700670 bottom = Math.max(top, bottom);
671 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100672 setClipBounds(mClipBounds);
673 } else {
674 setClipBounds(null);
675 }
676 }
677
678 public void setClipToActualHeight(boolean clipToActualHeight) {
679 mClipToActualHeight = clipToActualHeight;
680 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200681 }
682
Jorim Jaggi11298832014-05-24 16:18:38 +0200683 private void selectLayout(boolean animate, boolean force) {
684 if (mContractedChild == null) {
685 return;
686 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800687 if (mUserExpanding) {
688 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700689 } else {
690 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700691 boolean changedType = visibleType != mVisibleType;
692 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400693 View visibleView = getViewForVisibleType(visibleType);
694 if (visibleView != null) {
695 visibleView.setVisibility(VISIBLE);
696 transferRemoteInputFocus(visibleType);
697 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700698
Selim Cinek589fd3e2016-04-26 18:17:57 -0700699 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
700 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
701 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
702 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
703 animateToVisibleType(visibleType);
704 } else {
705 updateViewVisibilities(visibleType);
706 }
707 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700708 if (changedType) {
709 focusExpandButtonIfNecessary();
710 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800711 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
712 if (visibleWrapper != null) {
713 visibleWrapper.setContentHeight(mContentHeight, getMinContentHeightHint());
714 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700715 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200716 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700717 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700718 }
719
720 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800721 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
722 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
723 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
724 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
725 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos4320e892017-01-24 12:50:34 -0800726 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800727 // forceUpdateVisibilities cancels outstanding animations without updating the
728 // mAnimationStartVisibleType. Do so here instead.
729 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800730 }
731
Adrian Roos4320e892017-01-24 12:50:34 -0800732 private void fireExpandedVisibleListenerIfVisible() {
733 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
734 && mExpandedChild.getVisibility() == VISIBLE) {
735 Runnable listener = mExpandedVisibleListener;
736 mExpandedVisibleListener = null;
737 listener.run();
738 }
739 }
740
Adrian Roos0aac04f2016-12-08 15:59:29 -0800741 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
742 if (view == null) {
743 return;
744 }
745 boolean visible = mVisibleType == type
746 || mTransformationStartVisibleType == type;
747 if (!visible) {
748 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700749 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800750 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200751 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200752 }
753
Selim Cinekc3179332016-03-04 14:44:56 -0800754 public void updateBackgroundColor(boolean animate) {
755 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400756 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800757 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
758 }
759
Mady Mellorb0a82462016-04-30 17:31:02 -0700760 public int getVisibleType() {
761 return mVisibleType;
762 }
763
764 public int getBackgroundColorForExpansionState() {
765 // When expanding or user locked we want the new type, when collapsing we want
766 // the original type
767 final int visibleType = (mContainingNotification.isGroupExpanded()
768 || mContainingNotification.isUserLocked())
769 ? calculateVisibleType()
770 : getVisibleType();
771 return getBackgroundColor(visibleType);
772 }
773
774 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800775 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
776 int customBackgroundColor = 0;
777 if (currentVisibleWrapper != null) {
778 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
779 }
780 return customBackgroundColor;
781 }
782
Selim Cinek684a4422015-04-15 16:18:39 -0700783 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800784 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
785 mContractedChild, mContractedWrapper);
786 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
787 mExpandedChild, mExpandedWrapper);
788 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
789 mHeadsUpChild, mHeadsUpWrapper);
790 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
791 mSingleLineView, mSingleLineView);
792 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
793 mAmbientChild, mAmbientWrapper);
Adrian Roos4320e892017-01-24 12:50:34 -0800794 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800795 // updateViewVisibilities cancels outstanding animations without updating the
796 // mAnimationStartVisibleType. Do so here instead.
797 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800798 }
799
800 private void updateViewVisibility(int visibleType, int type, View view,
801 TransformableView wrapper) {
802 if (view != null) {
803 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -0700804 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700805 }
806
Selim Cinek4ffd6362015-12-29 15:12:23 +0100807 private void animateToVisibleType(int visibleType) {
808 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
809 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700810 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800811 shownView.setVisible(true);
812 return;
813 }
Adrian Roos599be342016-06-13 14:54:39 -0700814 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100815 shownView.transformFrom(hiddenView);
816 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
817 hiddenView.transformTo(shownView, new Runnable() {
818 @Override
819 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800820 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
821 hiddenView.setVisible(false);
822 }
Adrian Roos599be342016-06-13 14:54:39 -0700823 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100824 }
825 });
Adrian Roos4320e892017-01-24 12:50:34 -0800826 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +0200827 }
828
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700829 private void transferRemoteInputFocus(int visibleType) {
830 if (visibleType == VISIBLE_TYPE_HEADSUP
831 && mHeadsUpRemoteInput != null
832 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
833 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
834 }
835 if (visibleType == VISIBLE_TYPE_EXPANDED
836 && mExpandedRemoteInput != null
837 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
838 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
839 }
840 }
841
Selim Cinek684a4422015-04-15 16:18:39 -0700842 /**
843 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100844 * @return the corresponding transformable view according to the given visible type
845 */
846 private TransformableView getTransformableViewForVisibleType(int visibleType) {
847 switch (visibleType) {
848 case VISIBLE_TYPE_EXPANDED:
849 return mExpandedWrapper;
850 case VISIBLE_TYPE_HEADSUP:
851 return mHeadsUpWrapper;
852 case VISIBLE_TYPE_SINGLELINE:
853 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800854 case VISIBLE_TYPE_AMBIENT:
855 return mAmbientWrapper;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100856 default:
857 return mContractedWrapper;
858 }
859 }
860
861 /**
862 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -0700863 * @return the corresponding view according to the given visible type
864 */
865 private View getViewForVisibleType(int visibleType) {
866 switch (visibleType) {
867 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -0700868 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700869 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -0700870 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -0700871 case VISIBLE_TYPE_SINGLELINE:
872 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800873 case VISIBLE_TYPE_AMBIENT:
874 return mAmbientChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700875 default:
876 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -0700877 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700878 }
879
Selim Cinekc3179332016-03-04 14:44:56 -0800880 private NotificationViewWrapper getVisibleWrapper(int visibleType) {
881 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -0800882 case VISIBLE_TYPE_EXPANDED:
883 return mExpandedWrapper;
884 case VISIBLE_TYPE_HEADSUP:
885 return mHeadsUpWrapper;
886 case VISIBLE_TYPE_CONTRACTED:
887 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800888 case VISIBLE_TYPE_AMBIENT:
889 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -0800890 default:
891 return null;
892 }
893 }
894
Selim Cinek684a4422015-04-15 16:18:39 -0700895 /**
896 * @return one of the static enum types in this view, calculated form the current state
897 */
Mady Mellorb0a82462016-04-30 17:31:02 -0700898 public int calculateVisibleType() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800899 if (mDark && !mIsChildInGroup) {
900 // TODO: Handle notification groups
901 return VISIBLE_TYPE_AMBIENT;
902 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800903 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -0800904 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -0800905 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -0800906 ? mContainingNotification.getMaxContentHeight()
907 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -0800908 if (height == 0) {
909 height = mContentHeight;
910 }
Selim Cinek42357e02016-02-24 18:48:01 -0800911 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -0800912 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -0700913 ? VISIBLE_TYPE_SINGLELINE
914 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800915 return mTransformationStartVisibleType == collapsedVisualType
916 ? expandedVisualType
917 : collapsedVisualType;
918 }
Selim Cinek51d94912016-03-02 15:34:28 -0800919 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
920 int viewHeight = mContentHeight;
921 if (intrinsicHeight != 0) {
922 // the intrinsicHeight might be 0 because it was just reset.
923 viewHeight = Math.min(mContentHeight, intrinsicHeight);
924 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800925 return getVisualTypeForHeight(viewHeight);
926 }
927
928 private int getVisualTypeForHeight(float viewHeight) {
929 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800930 if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
Adrian Roos777ef562015-12-01 17:37:14 -0800931 return VISIBLE_TYPE_EXPANDED;
932 }
Selim Cinek414ad332017-02-24 19:06:12 -0800933 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800934 return VISIBLE_TYPE_SINGLELINE;
935 }
Adrian Roos777ef562015-12-01 17:37:14 -0800936
Selim Cinekc494e382017-01-31 16:09:23 -0800937 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
938 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800939 if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -0700940 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -0700941 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700942 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700943 }
944 } else {
Selim Cineka4091502016-02-11 17:21:16 -0800945 if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
Selim Cinek6ac3fa82016-05-10 17:11:54 -0400946 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -0800947 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -0700948 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700949 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700950 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700951 }
952 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200953 }
954
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200955 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800956 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200957 }
John Spurlocke15452b2014-08-21 09:44:39 -0400958
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100959 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -0800960 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -0800961 return;
962 }
John Spurlocke15452b2014-08-21 09:44:39 -0400963 mDark = dark;
Selim Cinek19ba7052016-01-27 20:04:27 -0800964 if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800965 mContractedWrapper.setDark(dark, fade, delay);
966 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800967 if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800968 mExpandedWrapper.setDark(dark, fade, delay);
969 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800970 if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800971 mHeadsUpWrapper.setDark(dark, fade, delay);
972 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800973 if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
Selim Cinek9c7712d2015-12-08 19:19:48 -0800974 mSingleLineView.setDark(dark, fade, delay);
975 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800976 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -0400977 }
978
Selim Cinek8d490d42015-04-10 00:05:50 -0700979 public void setHeadsUp(boolean headsUp) {
980 mIsHeadsUp = headsUp;
981 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -0800982 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -0700983 }
984
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +0200985 @Override
986 public boolean hasOverlappingRendering() {
987
988 // This is not really true, but good enough when fading from the contracted to the expanded
989 // layout, and saves us some layers.
990 return false;
991 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700992
Selim Cinek1a48bab2017-02-17 19:38:40 -0800993 public void setLegacy(boolean legacy) {
994 mLegacy = legacy;
995 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -0800996 }
997
Selim Cinek1a48bab2017-02-17 19:38:40 -0800998 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -0800999 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001000 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001001 }
1002 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001003 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001004 }
1005 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001006 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001007 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001008 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001009
Selim Cinek83bc7832015-10-22 13:26:54 -07001010 public void setIsChildInGroup(boolean isChildInGroup) {
1011 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001012 if (mContractedChild != null) {
1013 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1014 }
1015 if (mExpandedChild != null) {
1016 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1017 }
1018 if (mHeadsUpChild != null) {
1019 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1020 }
1021 if (mAmbientChild != null) {
1022 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1023 }
Selim Cinek83bc7832015-10-22 13:26:54 -07001024 updateSingleLineView();
1025 }
1026
Adrian Roosb88b1a12015-12-09 18:51:05 -08001027 public void onNotificationUpdated(NotificationData.Entry entry) {
1028 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -08001029 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Selim Cinek83bc7832015-10-22 13:26:54 -07001030 updateSingleLineView();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001031 if (mContractedChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001032 mContractedWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001033 }
1034 if (mExpandedChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001035 mExpandedWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001036 }
1037 if (mHeadsUpChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001038 mHeadsUpWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001039 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001040 if (mAmbientChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001041 mAmbientWrapper.notifyContentUpdated(entry.row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001042 }
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001043 applyRemoteInput(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001044 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001045 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001046 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001047 mPreviousExpandedRemoteInputIntent = null;
1048 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001049 }
1050
1051 private void updateSingleLineView() {
1052 if (mIsChildInGroup) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001053 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001054 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekde33a4a2016-02-11 16:43:41 -08001055 } else if (mSingleLineView != null) {
1056 removeView(mSingleLineView);
1057 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001058 }
1059 }
1060
Adrian Roosb88b1a12015-12-09 18:51:05 -08001061 private void applyRemoteInput(final NotificationData.Entry entry) {
1062 if (mRemoteInputController == null) {
1063 return;
1064 }
1065
1066 boolean hasRemoteInput = false;
1067
1068 Notification.Action[] actions = entry.notification.getNotification().actions;
1069 if (actions != null) {
1070 for (Notification.Action a : actions) {
1071 if (a.getRemoteInputs() != null) {
1072 for (RemoteInput ri : a.getRemoteInputs()) {
1073 if (ri.getAllowFreeFormInput()) {
1074 hasRemoteInput = true;
1075 break;
1076 }
1077 }
1078 }
1079 }
1080 }
1081
1082 View bigContentView = mExpandedChild;
1083 if (bigContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001084 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001085 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1086 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001087 } else {
1088 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001089 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001090 if (mCachedExpandedRemoteInput != null
1091 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1092 // We had a cached remote input but didn't reuse it. Clean up required.
1093 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1094 }
1095 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001096
Adrian Roosb88b1a12015-12-09 18:51:05 -08001097 View headsUpContentView = mHeadsUpChild;
1098 if (headsUpContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001099 mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001100 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001101 } else {
1102 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001103 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001104 if (mCachedHeadsUpRemoteInput != null
1105 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1106 // We had a cached remote input but didn't reuse it. Clean up required.
1107 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1108 }
1109 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001110 }
1111
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001112 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001113 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001114 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001115 View actionContainerCandidate = view.findViewById(
1116 com.android.internal.R.id.actions_container);
1117 if (actionContainerCandidate instanceof FrameLayout) {
1118 RemoteInputView existing = (RemoteInputView)
1119 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1120
1121 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001122 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001123 }
1124
1125 if (existing == null && hasRemoteInput) {
1126 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001127 if (cachedView == null) {
1128 RemoteInputView riv = RemoteInputView.inflate(
1129 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001130
Adrian Roos7813dd72016-09-23 17:12:17 -07001131 riv.setVisibility(View.INVISIBLE);
1132 actionContainer.addView(riv, new LayoutParams(
1133 ViewGroup.LayoutParams.MATCH_PARENT,
1134 ViewGroup.LayoutParams.MATCH_PARENT)
1135 );
1136 existing = riv;
1137 } else {
1138 actionContainer.addView(cachedView);
1139 cachedView.dispatchFinishTemporaryDetach();
1140 cachedView.requestFocus();
1141 existing = cachedView;
1142 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001143 }
1144 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001145 int color = entry.notification.getNotification().color;
1146 if (color == Notification.COLOR_DEFAULT) {
1147 color = mContext.getColor(R.color.default_remote_input_background);
1148 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001149 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001150 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001151 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001152
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001153 existing.setWrapper(wrapper);
1154
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001155 if (existingPendingIntent != null || existing.isActive()) {
1156 // The current action could be gone, or the pending intent no longer valid.
1157 // If we find a matching action in the new notification, focus, otherwise close.
1158 Notification.Action[] actions = entry.notification.getNotification().actions;
1159 if (existingPendingIntent != null) {
1160 existing.setPendingIntent(existingPendingIntent);
1161 }
1162 if (existing.updatePendingIntentFromActions(actions)) {
1163 if (!existing.isActive()) {
1164 existing.focus();
1165 }
1166 } else {
1167 if (existing.isActive()) {
1168 existing.close();
1169 }
1170 }
1171 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001172 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001173 return existing;
1174 }
1175 return null;
1176 }
1177
1178 public void closeRemoteInput() {
1179 if (mHeadsUpRemoteInput != null) {
1180 mHeadsUpRemoteInput.close();
1181 }
1182 if (mExpandedRemoteInput != null) {
1183 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001184 }
1185 }
1186
Selim Cinek83bc7832015-10-22 13:26:54 -07001187 public void setGroupManager(NotificationGroupManager groupManager) {
1188 mGroupManager = groupManager;
1189 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001190
Adrian Roosb88b1a12015-12-09 18:51:05 -08001191 public void setRemoteInputController(RemoteInputController r) {
1192 mRemoteInputController = r;
1193 }
1194
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001195 public void setExpandClickListener(OnClickListener expandClickListener) {
1196 mExpandClickListener = expandClickListener;
1197 }
1198
1199 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001200 mExpandable = expandable;
1201 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001202 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001203 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1204 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001205 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001206 expandable = false;
1207 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001208 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001209 expandable = false;
1210 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001211 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001212 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001213 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001214 }
1215 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001216 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001217 }
1218 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001219 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001220 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001221 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001222 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001223
1224 public NotificationHeaderView getNotificationHeader() {
1225 NotificationHeaderView header = null;
1226 if (mContractedChild != null) {
1227 header = mContractedWrapper.getNotificationHeader();
1228 }
1229 if (header == null && mExpandedChild != null) {
1230 header = mExpandedWrapper.getNotificationHeader();
1231 }
1232 if (header == null && mHeadsUpChild != null) {
1233 header = mHeadsUpWrapper.getNotificationHeader();
1234 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001235 if (header == null && mAmbientChild != null) {
1236 header = mAmbientWrapper.getNotificationHeader();
1237 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001238 return header;
1239 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001240
Selim Cinek34eda5e2016-02-18 17:10:43 -08001241 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001242 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001243 return wrapper == null ? null : wrapper.getNotificationHeader();
1244 }
1245
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001246 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1247 mContainingNotification = containingNotification;
1248 }
1249
1250 public void requestSelectLayout(boolean needsAnimation) {
1251 selectLayout(needsAnimation, false);
1252 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001253
1254 public void reInflateViews() {
1255 if (mIsChildInGroup && mSingleLineView != null) {
1256 removeView(mSingleLineView);
1257 mSingleLineView = null;
1258 updateSingleLineView();
1259 }
1260 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001261
1262 public void setUserExpanding(boolean userExpanding) {
1263 mUserExpanding = userExpanding;
1264 if (userExpanding) {
1265 mTransformationStartVisibleType = mVisibleType;
1266 } else {
1267 mTransformationStartVisibleType = UNDEFINED;
1268 mVisibleType = calculateVisibleType();
1269 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001270 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001271 }
1272 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001273
1274 /**
1275 * Set by how much the single line view should be indented. Used when a overflow indicator is
1276 * present and only during measuring
1277 */
1278 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1279 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1280 mSingleLineWidthIndention = singleLineWidthIndention;
1281 mContainingNotification.forceLayout();
1282 forceLayout();
1283 }
1284 }
1285
1286 public HybridNotificationView getSingleLineView() {
1287 return mSingleLineView;
1288 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001289
1290 public void setRemoved() {
1291 if (mExpandedRemoteInput != null) {
1292 mExpandedRemoteInput.setRemoved();
1293 }
1294 if (mHeadsUpRemoteInput != null) {
1295 mHeadsUpRemoteInput.setRemoved();
1296 }
1297 }
Adrian Roos599be342016-06-13 14:54:39 -07001298
1299 public void setContentHeightAnimating(boolean animating) {
1300 if (!animating) {
1301 mContentHeightAtAnimationStart = UNDEFINED;
1302 }
1303 }
Selim Cineke9bad242016-06-15 11:46:37 -07001304
Adrian Roose18033c2017-01-17 15:22:49 -08001305 @VisibleForTesting
1306 boolean isAnimatingVisibleType() {
1307 return mAnimationStartVisibleType != UNDEFINED;
1308 }
1309
Selim Cinekcafa87f2016-10-26 17:00:17 -07001310 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1311 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001312 selectLayout(false /* animate */, true /* force */);
1313 }
1314
Selim Cineke9bad242016-06-15 11:46:37 -07001315 public void setFocusOnVisibilityChange() {
1316 mFocusOnVisibilityChange = true;
1317 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001318
1319 public void setIconsVisible(boolean iconsVisible) {
1320 mIconsVisible = iconsVisible;
1321 updateIconVisibilities();
1322 }
1323
1324 private void updateIconVisibilities() {
1325 if (mContractedWrapper != null) {
1326 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1327 if (header != null) {
1328 header.getIcon().setForceHidden(!mIconsVisible);
1329 }
1330 }
1331 if (mHeadsUpWrapper != null) {
1332 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1333 if (header != null) {
1334 header.getIcon().setForceHidden(!mIconsVisible);
1335 }
1336 }
1337 if (mExpandedWrapper != null) {
1338 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1339 if (header != null) {
1340 header.getIcon().setForceHidden(!mIconsVisible);
1341 }
1342 }
1343 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001344
Adrian Roos4320e892017-01-24 12:50:34 -08001345 @Override
1346 public void onVisibilityAggregated(boolean isVisible) {
1347 super.onVisibilityAggregated(isVisible);
1348 if (isVisible) {
1349 fireExpandedVisibleListenerIfVisible();
1350 }
1351 }
1352
1353 /**
1354 * Sets a one-shot listener for when the expanded view becomes visible.
1355 *
1356 * This will fire the listener immediately if the expanded view is already visible.
1357 */
1358 public void setOnExpandedVisibleListener(Runnable r) {
1359 mExpandedVisibleListener = r;
1360 fireExpandedVisibleListenerIfVisible();
1361 }
1362
Selim Cinek6743c0b2017-01-18 18:24:01 -08001363 public void setIsLowPriority(boolean isLowPriority) {
1364 mIsLowPriority = isLowPriority;
1365 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001366
1367 public boolean isDimmable() {
1368 if (!mContractedWrapper.isDimmable()) {
1369 return false;
1370 }
1371 return true;
1372 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001373}