blob: 15f3908f735b6739566081e65a1a39749959902f [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;
Adrian Roos6f6e1592017-05-02 16:22:53 -070057 private static final int VISIBLE_TYPE_AMBIENT_SINGLELINE = 5;
Selim Cinekc3179332016-03-04 14:44:56 -080058 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020059
Jorim Jaggibe565df2014-04-28 17:51:23 +020060 private final Rect mClipBounds = new Rect();
Selim Cinek860b6da2015-12-16 19:02:19 -080061 private final int mMinContractedHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -080062 private final int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020063
64 private View mContractedChild;
65 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070066 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070067 private HybridNotificationView mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -080068 private View mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -070069 private HybridNotificationView mAmbientSingleLineChild;
Jorim Jaggibe565df2014-04-28 17:51:23 +020070
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070071 private RemoteInputView mExpandedRemoteInput;
72 private RemoteInputView mHeadsUpRemoteInput;
73
Jorim Jaggi4e857f42014-11-17 19:14:04 +010074 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070075 private NotificationViewWrapper mExpandedWrapper;
76 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -080077 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -070078 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020079 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +010080 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -070081 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -040082 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010083 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -070084 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -080085 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -070086 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -080087 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -080088 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -080089 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -080090 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -070091 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070092 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080093 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -080094 private Runnable mExpandedVisibleListener;
Jorim Jaggi59ec3042015-06-05 15:18:43 -070095
Jorim Jaggibe4116a2015-05-20 20:04:08 -070096 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010097 = new ViewTreeObserver.OnPreDrawListener() {
98 @Override
99 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -0800100 // We need to post since we don't want the notification to animate on the very first
101 // frame
102 post(new Runnable() {
103 @Override
104 public void run() {
105 mAnimate = true;
106 }
107 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100108 getViewTreeObserver().removeOnPreDrawListener(this);
109 return true;
110 }
111 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200112
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800113 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800114 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800115 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100116 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800117 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700118 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800119 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700120 /** The visible type at the start of an animation driven transformation */
121 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800122 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700123 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700124 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700125 private PendingIntent mPreviousExpandedRemoteInputIntent;
126 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700127 private RemoteInputView mCachedExpandedRemoteInput;
128 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800129
Adrian Roos599be342016-06-13 14:54:39 -0700130 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700131 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700132 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700133 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700134 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800135 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800136 private boolean mIsContentExpandable;
Adrian Roos599be342016-06-13 14:54:39 -0700137
138
Jorim Jaggibe565df2014-04-28 17:51:23 +0200139 public NotificationContentView(Context context, AttributeSet attrs) {
140 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700141 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Selim Cinek860b6da2015-12-16 19:02:19 -0800142 mMinContractedHeight = getResources().getDimensionPixelSize(
143 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800144 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
145 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200146 }
147
Adrian Roos0aac04f2016-12-08 15:59:29 -0800148 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
149 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800150 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800151 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800152 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800153 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800154 }
155
Jorim Jaggibe565df2014-04-28 17:51:23 +0200156 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700157 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
158 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
159 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
160 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
161 int maxSize = Integer.MAX_VALUE;
Selim Cinekc897bd32016-03-18 17:32:31 -0700162 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700163 if (hasFixedHeight || isHeightLimited) {
164 maxSize = MeasureSpec.getSize(heightMeasureSpec);
165 }
166 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800167 if (mExpandedChild != null) {
168 int size = Math.min(maxSize, mNotificationMaxHeight);
169 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800170 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800171 if (layoutParams.height >= 0) {
172 // An actual height is set
173 size = Math.min(maxSize, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800174 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800175 }
176 int spec = size == Integer.MAX_VALUE
177 ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
Selim Cinek6743c0b2017-01-18 18:24:01 -0800178 : MeasureSpec.makeMeasureSpec(size, useExactly
179 ? MeasureSpec.EXACTLY
180 : MeasureSpec.AT_MOST);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800181 mExpandedChild.measure(widthMeasureSpec, spec);
182 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
183 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700184 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800185 int heightSpec;
Selim Cinekf619ffc2016-02-17 14:53:05 -0800186 int size = Math.min(maxSize, mSmallHeight);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800187 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
188 boolean useExactly = false;
189 if (layoutParams.height >= 0) {
190 // An actual height is set
191 size = Math.min(size, layoutParams.height);
192 useExactly = true;
193 }
194 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800195 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
196 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800197 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800198 }
199 mContractedChild.measure(widthMeasureSpec, heightSpec);
200 int measuredHeight = mContractedChild.getMeasuredHeight();
201 if (measuredHeight < mMinContractedHeight) {
202 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
203 mContractedChild.measure(widthMeasureSpec, heightSpec);
204 }
205 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800206 if (updateContractedHeaderWidth()) {
207 mContractedChild.measure(widthMeasureSpec, heightSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700208 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700209 if (mExpandedChild != null
210 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
211 // the Expanded child is smaller then the collapsed. Let's remeasure it.
212 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
213 MeasureSpec.EXACTLY);
214 mExpandedChild.measure(widthMeasureSpec, heightSpec);
215 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700216 }
217 if (mHeadsUpChild != null) {
218 int size = Math.min(maxSize, mHeadsUpHeight);
219 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800220 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700221 if (layoutParams.height >= 0) {
222 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800223 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800224 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700225 }
226 mHeadsUpChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800227 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
228 : MeasureSpec.AT_MOST));
Selim Cinek8d490d42015-04-10 00:05:50 -0700229 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
230 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700231 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700232 int singleLineWidthSpec = widthMeasureSpec;
233 if (mSingleLineWidthIndention != 0
234 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
235 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
236 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700237 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700238 }
239 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinek7b836392015-12-04 20:02:59 -0800240 MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700241 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
242 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800243 if (mAmbientChild != null) {
244 int size = Math.min(maxSize, mNotificationAmbientHeight);
245 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800246 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800247 if (layoutParams.height >= 0) {
248 // An actual height is set
249 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800250 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800251 }
252 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800253 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
254 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800255 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
256 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700257 if (mAmbientSingleLineChild != null) {
258 int size = Math.min(maxSize, mNotificationAmbientHeight);
259 ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
260 boolean useExactly = false;
261 if (layoutParams.height >= 0) {
262 // An actual height is set
263 size = Math.min(size, layoutParams.height);
264 useExactly = true;
265 }
266 int ambientSingleLineWidthSpec = widthMeasureSpec;
267 if (mSingleLineWidthIndention != 0
268 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
269 ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
270 width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
271 MeasureSpec.EXACTLY);
272 }
273 mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
274 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
275 : MeasureSpec.AT_MOST));
276 maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
277 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700278 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700279 setMeasuredDimension(width, ownHeight);
280 }
281
Selim Cinek6ecc8102016-01-26 18:26:19 -0800282 private boolean updateContractedHeaderWidth() {
283 // We need to update the expanded and the collapsed header to have exactly the same with to
284 // have the expand buttons laid out at the same location.
285 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
286 if (contractedHeader != null) {
287 if (mExpandedChild != null
288 && mExpandedWrapper.getNotificationHeader() != null) {
289 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
290 int expandedSize = expandedHeader.getMeasuredWidth()
291 - expandedHeader.getPaddingEnd();
292 int collapsedSize = contractedHeader.getMeasuredWidth()
293 - expandedHeader.getPaddingEnd();
294 if (expandedSize != collapsedSize) {
295 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
296 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800297 contractedHeader.isLayoutRtl()
298 ? paddingEnd
299 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800300 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800301 contractedHeader.isLayoutRtl()
302 ? contractedHeader.getPaddingLeft()
303 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800304 contractedHeader.getPaddingBottom());
305 contractedHeader.setShowWorkBadgeAtEnd(true);
306 return true;
307 }
308 } else {
309 int paddingEnd = mNotificationContentMarginEnd;
310 if (contractedHeader.getPaddingEnd() != paddingEnd) {
311 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800312 contractedHeader.isLayoutRtl()
313 ? paddingEnd
314 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800315 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800316 contractedHeader.isLayoutRtl()
317 ? contractedHeader.getPaddingLeft()
318 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800319 contractedHeader.getPaddingBottom());
320 contractedHeader.setShowWorkBadgeAtEnd(false);
321 return true;
322 }
323 }
324 }
325 return false;
326 }
327
Selim Cinek860b6da2015-12-16 19:02:19 -0800328 private boolean shouldContractedBeFixedSize() {
329 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
330 }
331
Selim Cinek8d490d42015-04-10 00:05:50 -0700332 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200333 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700334 int previousHeight = 0;
335 if (mExpandedChild != null) {
336 previousHeight = mExpandedChild.getHeight();
337 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200338 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700339 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
340 mContentHeightAtAnimationStart = previousHeight;
341 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200342 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700343 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700344 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
345 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700346 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200347 }
348
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100349 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100350 protected void onAttachedToWindow() {
351 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100352 updateVisibility();
353 }
354
Selim Cinekcab4a602014-09-03 14:47:57 +0200355 public View getContractedChild() {
356 return mContractedChild;
357 }
358
359 public View getExpandedChild() {
360 return mExpandedChild;
361 }
362
Selim Cinek8d490d42015-04-10 00:05:50 -0700363 public View getHeadsUpChild() {
364 return mHeadsUpChild;
365 }
366
Adrian Roos0aac04f2016-12-08 15:59:29 -0800367 public View getAmbientChild() {
368 return mAmbientChild;
369 }
370
Adrian Roos6f6e1592017-05-02 16:22:53 -0700371 public HybridNotificationView getAmbientSingleLineChild() {
372 return mAmbientSingleLineChild;
373 }
374
Jorim Jaggibe565df2014-04-28 17:51:23 +0200375 public void setContractedChild(View child) {
376 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200377 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200378 removeView(mContractedChild);
379 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200380 addView(child);
381 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700382 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
383 mContainingNotification);
Jorim Jaggi10ad7612014-12-08 18:41:11 +0100384 mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200385 }
386
387 public void setExpandedChild(View child) {
388 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800389 mPreviousExpandedRemoteInputIntent = null;
390 if (mExpandedRemoteInput != null) {
391 mExpandedRemoteInput.onNotificationUpdateOrReset();
392 if (mExpandedRemoteInput.isActive()) {
393 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
394 mCachedExpandedRemoteInput = mExpandedRemoteInput;
395 mExpandedRemoteInput.dispatchStartTemporaryDetach();
396 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
397 }
398 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200399 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200400 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800401 mExpandedRemoteInput = null;
402 }
403 if (child == null) {
404 mExpandedChild = null;
405 mExpandedWrapper = null;
406 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
407 mVisibleType = VISIBLE_TYPE_CONTRACTED;
408 }
409 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
410 mTransformationStartVisibleType = UNDEFINED;
411 }
412 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200413 }
414 addView(child);
415 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700416 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
417 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200418 }
419
Selim Cinek8d490d42015-04-10 00:05:50 -0700420 public void setHeadsUpChild(View child) {
421 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800422 mPreviousHeadsUpRemoteInputIntent = null;
423 if (mHeadsUpRemoteInput != null) {
424 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
425 if (mHeadsUpRemoteInput.isActive()) {
426 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
427 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
428 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
429 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
430 }
431 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700432 mHeadsUpChild.animate().cancel();
433 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800434 mHeadsUpRemoteInput = null;
435 }
436 if (child == null) {
437 mHeadsUpChild = null;
438 mHeadsUpWrapper = null;
439 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
440 mVisibleType = VISIBLE_TYPE_CONTRACTED;
441 }
442 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
443 mTransformationStartVisibleType = UNDEFINED;
444 }
445 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700446 }
447 addView(child);
448 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700449 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
450 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700451 }
452
Adrian Roos0aac04f2016-12-08 15:59:29 -0800453 public void setAmbientChild(View child) {
454 if (mAmbientChild != null) {
455 mAmbientChild.animate().cancel();
456 removeView(mAmbientChild);
457 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700458 if (child == null) {
459 return;
460 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800461 addView(child);
462 mAmbientChild = child;
463 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
464 mContainingNotification);
465 }
466
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100467 @Override
468 protected void onVisibilityChanged(View changedView, int visibility) {
469 super.onVisibilityChanged(changedView, visibility);
470 updateVisibility();
471 }
472
473 private void updateVisibility() {
474 setVisible(isShown());
475 }
476
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700477 @Override
478 protected void onDetachedFromWindow() {
479 super.onDetachedFromWindow();
480 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
481 }
482
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100483 private void setVisible(final boolean isVisible) {
484 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700485 // This call can happen multiple times, but removing only removes a single one.
486 // We therefore need to remove the old one.
487 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100488 // We only animate if we are drawn at least once, otherwise the view might animate when
489 // it's shown the first time
490 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
491 } else {
492 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
493 mAnimate = false;
494 }
495 }
496
Selim Cineke9bad242016-06-15 11:46:37 -0700497 private void focusExpandButtonIfNecessary() {
498 if (mFocusOnVisibilityChange) {
499 NotificationHeaderView header = getVisibleNotificationHeader();
500 if (header != null) {
501 ImageView expandButton = header.getExpandButton();
502 if (expandButton != null) {
503 expandButton.requestAccessibilityFocus();
504 }
505 }
506 mFocusOnVisibilityChange = false;
507 }
508 }
509
Selim Cinekb5605e52015-02-20 18:21:41 +0100510 public void setContentHeight(int contentHeight) {
Mady Mellorb0a82462016-04-30 17:31:02 -0700511 mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100512 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400513
514 int minHeightHint = getMinContentHeightHint();
515
516 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
517 if (wrapper != null) {
518 wrapper.setContentHeight(mContentHeight, minHeightHint);
519 }
520
521 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
522 if (wrapper != null) {
523 wrapper.setContentHeight(mContentHeight, minHeightHint);
524 }
525
Jorim Jaggibe565df2014-04-28 17:51:23 +0200526 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700527 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200528 }
529
Adrian Roos181385c2016-05-05 17:45:44 -0400530 /**
531 * @return the minimum apparent height that the wrapper should allow for the purpose
532 * of aligning elements at the bottom edge. If this is larger than the content
533 * height, the notification is clipped instead of being further shrunk.
534 */
535 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700536 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400537 return mContext.getResources().getDimensionPixelSize(
538 com.android.internal.R.dimen.notification_action_list_height);
539 }
Adrian Roos599be342016-06-13 14:54:39 -0700540
541 // Transition between heads-up & expanded, or pinned.
542 if (mHeadsUpChild != null && mExpandedChild != null) {
543 boolean transitioningBetweenHunAndExpanded =
544 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
545 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700546 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800547 && (mIsHeadsUp || mHeadsUpAnimatingAway)
548 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700549 if (transitioningBetweenHunAndExpanded || pinned) {
550 return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight());
551 }
552 }
553
554 // Size change of the expanded version
555 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
556 && mExpandedChild != null) {
557 return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight());
558 }
559
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700560 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800561 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
562 hint = mAmbientChild.getHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700563 } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
564 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
565 hint = mAmbientSingleLineChild.getHeight();
Adrian Roos05d3d002017-01-26 15:28:07 -0800566 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700567 hint = mHeadsUpChild.getHeight();
Adrian Roos599be342016-06-13 14:54:39 -0700568 } else if (mExpandedChild != null) {
569 hint = mExpandedChild.getHeight();
Adrian Roos181385c2016-05-05 17:45:44 -0400570 } else {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700571 hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
572 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400573 }
Adrian Roos599be342016-06-13 14:54:39 -0700574
575 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700576 hint = Math.min(hint, mExpandedChild.getHeight());
577 }
578 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400579 }
580
Adrian Roos599be342016-06-13 14:54:39 -0700581 private boolean isTransitioningFromTo(int from, int to) {
582 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
583 && mVisibleType == to;
584 }
585
586 private boolean isVisibleOrTransitioning(int type) {
587 return mVisibleType == type || mTransformationStartVisibleType == type
588 || mAnimationStartVisibleType == type;
589 }
590
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800591 private void updateContentTransformation() {
592 int visibleType = calculateVisibleType();
593 if (visibleType != mVisibleType) {
594 // A new transformation starts
595 mTransformationStartVisibleType = mVisibleType;
596 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
597 final TransformableView hiddenView = getTransformableViewForVisibleType(
598 mTransformationStartVisibleType);
599 shownView.transformFrom(hiddenView, 0.0f);
600 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
601 hiddenView.transformTo(shownView, 0.0f);
602 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800603 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800604 }
Selim Cineke8578872016-05-03 16:42:50 -0700605 if (mForceSelectNextLayout) {
606 forceUpdateVisibilities();
607 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800608 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700609 && mVisibleType != mTransformationStartVisibleType
610 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800611 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
612 final TransformableView hiddenView = getTransformableViewForVisibleType(
613 mTransformationStartVisibleType);
614 float transformationAmount = calculateTransformationAmount();
615 shownView.transformFrom(hiddenView, transformationAmount);
616 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800617 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800618 } else {
619 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800620 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800621 }
622 }
623
Selim Cinekc3179332016-03-04 14:44:56 -0800624 private void updateBackgroundTransformation(float transformationAmount) {
625 int endColor = getBackgroundColor(mVisibleType);
626 int startColor = getBackgroundColor(mTransformationStartVisibleType);
627 if (endColor != startColor) {
628 if (startColor == 0) {
629 startColor = mContainingNotification.getBackgroundColorWithoutTint();
630 }
631 if (endColor == 0) {
632 endColor = mContainingNotification.getBackgroundColorWithoutTint();
633 }
634 endColor = NotificationUtils.interpolateColors(startColor, endColor,
635 transformationAmount);
636 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400637 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800638 mContainingNotification.setContentBackground(endColor, false, this);
639 }
640
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800641 private float calculateTransformationAmount() {
642 int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
643 int endHeight = getViewForVisibleType(mVisibleType).getHeight();
644 int progress = Math.abs(mContentHeight - startHeight);
645 int totalDistance = Math.abs(endHeight - startHeight);
646 float amount = (float) progress / (float) totalDistance;
647 return Math.min(1.0f, amount);
648 }
649
Selim Cinekb5605e52015-02-20 18:21:41 +0100650 public int getContentHeight() {
651 return mContentHeight;
652 }
653
Jorim Jaggibe565df2014-04-28 17:51:23 +0200654 public int getMaxHeight() {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700655 if (mContainingNotification.isShowingAmbient()) {
656 return getShowingAmbientView().getHeight();
657 } else if (mExpandedChild != null) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700658 return mExpandedChild.getHeight();
Selim Cinekc494e382017-01-31 16:09:23 -0800659 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinek77019c72015-12-09 10:18:02 -0800660 return mHeadsUpChild.getHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -0700661 }
Selim Cinek860b6da2015-12-16 19:02:19 -0800662 return mContractedChild.getHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200663 }
664
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200665 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800666 return getMinHeight(false /* likeGroupExpanded */);
667 }
668
669 public int getMinHeight(boolean likeGroupExpanded) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700670 if (mContainingNotification.isShowingAmbient()) {
671 return getShowingAmbientView().getHeight();
672 } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800673 return mContractedChild.getHeight();
Selim Cinek42357e02016-02-24 18:48:01 -0800674 } else {
675 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700676 }
677 }
678
Adrian Roos6f6e1592017-05-02 16:22:53 -0700679 public View getShowingAmbientView() {
680 View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
681 if (v != null) {
682 return v;
683 } else {
684 return mContractedChild;
685 }
686 }
687
Selim Cinek83bc7832015-10-22 13:26:54 -0700688 private boolean isGroupExpanded() {
689 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200690 }
691
Jorim Jaggibe565df2014-04-28 17:51:23 +0200692 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200693 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200694 updateClipping();
695 }
696
Selim Cineka686b2c2016-10-26 13:58:27 -0700697
698 public void setClipBottomAmount(int clipBottomAmount) {
699 mClipBottomAmount = clipBottomAmount;
700 updateClipping();
701 }
702
Selim Cinek875a3a12016-11-18 17:52:16 -0800703 @Override
704 public void setTranslationY(float translationY) {
705 super.setTranslationY(translationY);
706 updateClipping();
707 }
708
Jorim Jaggibe565df2014-04-28 17:51:23 +0200709 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100710 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800711 int top = (int) (mClipTopAmount - getTranslationY());
712 int bottom = (int) (mContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700713 bottom = Math.max(top, bottom);
714 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100715 setClipBounds(mClipBounds);
716 } else {
717 setClipBounds(null);
718 }
719 }
720
721 public void setClipToActualHeight(boolean clipToActualHeight) {
722 mClipToActualHeight = clipToActualHeight;
723 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200724 }
725
Jorim Jaggi11298832014-05-24 16:18:38 +0200726 private void selectLayout(boolean animate, boolean force) {
727 if (mContractedChild == null) {
728 return;
729 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800730 if (mUserExpanding) {
731 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700732 } else {
733 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700734 boolean changedType = visibleType != mVisibleType;
735 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400736 View visibleView = getViewForVisibleType(visibleType);
737 if (visibleView != null) {
738 visibleView.setVisibility(VISIBLE);
739 transferRemoteInputFocus(visibleType);
740 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700741
Selim Cinek589fd3e2016-04-26 18:17:57 -0700742 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
743 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
744 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
745 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
746 animateToVisibleType(visibleType);
747 } else {
748 updateViewVisibilities(visibleType);
749 }
750 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700751 if (changedType) {
752 focusExpandButtonIfNecessary();
753 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800754 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
755 if (visibleWrapper != null) {
756 visibleWrapper.setContentHeight(mContentHeight, getMinContentHeightHint());
757 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700758 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200759 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700760 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700761 }
762
763 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800764 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
765 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
766 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
767 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
768 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700769 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
770 mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800771 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800772 // forceUpdateVisibilities cancels outstanding animations without updating the
773 // mAnimationStartVisibleType. Do so here instead.
774 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800775 }
776
Adrian Roos4320e892017-01-24 12:50:34 -0800777 private void fireExpandedVisibleListenerIfVisible() {
778 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
779 && mExpandedChild.getVisibility() == VISIBLE) {
780 Runnable listener = mExpandedVisibleListener;
781 mExpandedVisibleListener = null;
782 listener.run();
783 }
784 }
785
Adrian Roos0aac04f2016-12-08 15:59:29 -0800786 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
787 if (view == null) {
788 return;
789 }
790 boolean visible = mVisibleType == type
791 || mTransformationStartVisibleType == type;
792 if (!visible) {
793 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700794 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800795 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200796 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200797 }
798
Selim Cinekc3179332016-03-04 14:44:56 -0800799 public void updateBackgroundColor(boolean animate) {
800 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400801 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800802 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
803 }
804
Mady Mellorb0a82462016-04-30 17:31:02 -0700805 public int getVisibleType() {
806 return mVisibleType;
807 }
808
809 public int getBackgroundColorForExpansionState() {
810 // When expanding or user locked we want the new type, when collapsing we want
811 // the original type
812 final int visibleType = (mContainingNotification.isGroupExpanded()
813 || mContainingNotification.isUserLocked())
814 ? calculateVisibleType()
815 : getVisibleType();
816 return getBackgroundColor(visibleType);
817 }
818
819 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800820 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
821 int customBackgroundColor = 0;
822 if (currentVisibleWrapper != null) {
823 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
824 }
825 return customBackgroundColor;
826 }
827
Selim Cinek684a4422015-04-15 16:18:39 -0700828 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800829 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
830 mContractedChild, mContractedWrapper);
831 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
832 mExpandedChild, mExpandedWrapper);
833 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
834 mHeadsUpChild, mHeadsUpWrapper);
835 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
836 mSingleLineView, mSingleLineView);
837 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
838 mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700839 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
840 mAmbientSingleLineChild, mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800841 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800842 // updateViewVisibilities cancels outstanding animations without updating the
843 // mAnimationStartVisibleType. Do so here instead.
844 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800845 }
846
847 private void updateViewVisibility(int visibleType, int type, View view,
848 TransformableView wrapper) {
849 if (view != null) {
850 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -0700851 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700852 }
853
Selim Cinek4ffd6362015-12-29 15:12:23 +0100854 private void animateToVisibleType(int visibleType) {
855 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
856 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700857 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800858 shownView.setVisible(true);
859 return;
860 }
Adrian Roos599be342016-06-13 14:54:39 -0700861 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100862 shownView.transformFrom(hiddenView);
863 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
864 hiddenView.transformTo(shownView, new Runnable() {
865 @Override
866 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800867 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
868 hiddenView.setVisible(false);
869 }
Adrian Roos599be342016-06-13 14:54:39 -0700870 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100871 }
872 });
Adrian Roos4320e892017-01-24 12:50:34 -0800873 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +0200874 }
875
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700876 private void transferRemoteInputFocus(int visibleType) {
877 if (visibleType == VISIBLE_TYPE_HEADSUP
878 && mHeadsUpRemoteInput != null
879 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
880 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
881 }
882 if (visibleType == VISIBLE_TYPE_EXPANDED
883 && mExpandedRemoteInput != null
884 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
885 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
886 }
887 }
888
Selim Cinek684a4422015-04-15 16:18:39 -0700889 /**
890 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100891 * @return the corresponding transformable view according to the given visible type
892 */
893 private TransformableView getTransformableViewForVisibleType(int visibleType) {
894 switch (visibleType) {
895 case VISIBLE_TYPE_EXPANDED:
896 return mExpandedWrapper;
897 case VISIBLE_TYPE_HEADSUP:
898 return mHeadsUpWrapper;
899 case VISIBLE_TYPE_SINGLELINE:
900 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800901 case VISIBLE_TYPE_AMBIENT:
902 return mAmbientWrapper;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700903 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
904 return mAmbientSingleLineChild;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100905 default:
906 return mContractedWrapper;
907 }
908 }
909
910 /**
911 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -0700912 * @return the corresponding view according to the given visible type
913 */
914 private View getViewForVisibleType(int visibleType) {
915 switch (visibleType) {
916 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -0700917 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700918 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -0700919 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -0700920 case VISIBLE_TYPE_SINGLELINE:
921 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800922 case VISIBLE_TYPE_AMBIENT:
923 return mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700924 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
925 return mAmbientSingleLineChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700926 default:
927 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -0700928 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700929 }
930
Selim Cinekc3179332016-03-04 14:44:56 -0800931 private NotificationViewWrapper getVisibleWrapper(int visibleType) {
932 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -0800933 case VISIBLE_TYPE_EXPANDED:
934 return mExpandedWrapper;
935 case VISIBLE_TYPE_HEADSUP:
936 return mHeadsUpWrapper;
937 case VISIBLE_TYPE_CONTRACTED:
938 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800939 case VISIBLE_TYPE_AMBIENT:
940 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -0800941 default:
942 return null;
943 }
944 }
945
Selim Cinek684a4422015-04-15 16:18:39 -0700946 /**
947 * @return one of the static enum types in this view, calculated form the current state
948 */
Mady Mellorb0a82462016-04-30 17:31:02 -0700949 public int calculateVisibleType() {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700950 if (mContainingNotification.isShowingAmbient()) {
951 if (mIsChildInGroup && mAmbientSingleLineChild != null) {
952 return VISIBLE_TYPE_AMBIENT_SINGLELINE;
953 } else if (mAmbientChild != null) {
954 return VISIBLE_TYPE_AMBIENT;
955 } else {
956 return VISIBLE_TYPE_CONTRACTED;
957 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800958 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800959 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -0800960 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -0800961 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -0800962 ? mContainingNotification.getMaxContentHeight()
963 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -0800964 if (height == 0) {
965 height = mContentHeight;
966 }
Selim Cinek42357e02016-02-24 18:48:01 -0800967 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -0800968 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -0700969 ? VISIBLE_TYPE_SINGLELINE
970 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800971 return mTransformationStartVisibleType == collapsedVisualType
972 ? expandedVisualType
973 : collapsedVisualType;
974 }
Selim Cinek51d94912016-03-02 15:34:28 -0800975 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
976 int viewHeight = mContentHeight;
977 if (intrinsicHeight != 0) {
978 // the intrinsicHeight might be 0 because it was just reset.
979 viewHeight = Math.min(mContentHeight, intrinsicHeight);
980 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800981 return getVisualTypeForHeight(viewHeight);
982 }
983
984 private int getVisualTypeForHeight(float viewHeight) {
985 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800986 if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
Adrian Roos777ef562015-12-01 17:37:14 -0800987 return VISIBLE_TYPE_EXPANDED;
988 }
Selim Cinek414ad332017-02-24 19:06:12 -0800989 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800990 return VISIBLE_TYPE_SINGLELINE;
991 }
Adrian Roos777ef562015-12-01 17:37:14 -0800992
Selim Cinekc494e382017-01-31 16:09:23 -0800993 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
994 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800995 if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -0700996 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -0700997 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700998 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700999 }
1000 } else {
Selim Cineka4091502016-02-11 17:21:16 -08001001 if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
Selim Cinek6ac3fa82016-05-10 17:11:54 -04001002 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001003 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -07001004 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001005 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001006 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001007 }
1008 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001009 }
1010
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001011 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001012 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001013 }
John Spurlocke15452b2014-08-21 09:44:39 -04001014
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001015 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -08001016 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -08001017 return;
1018 }
John Spurlocke15452b2014-08-21 09:44:39 -04001019 mDark = dark;
Selim Cinek19ba7052016-01-27 20:04:27 -08001020 if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
Selim Cinek30e387d2016-01-11 18:01:47 -08001021 mContractedWrapper.setDark(dark, fade, delay);
1022 }
Selim Cinek19ba7052016-01-27 20:04:27 -08001023 if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -08001024 mExpandedWrapper.setDark(dark, fade, delay);
1025 }
Selim Cinek19ba7052016-01-27 20:04:27 -08001026 if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -08001027 mHeadsUpWrapper.setDark(dark, fade, delay);
1028 }
Selim Cinek19ba7052016-01-27 20:04:27 -08001029 if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
Selim Cinek9c7712d2015-12-08 19:19:48 -08001030 mSingleLineView.setDark(dark, fade, delay);
1031 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001032 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -04001033 }
1034
Selim Cinek8d490d42015-04-10 00:05:50 -07001035 public void setHeadsUp(boolean headsUp) {
1036 mIsHeadsUp = headsUp;
1037 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -08001038 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -07001039 }
1040
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +02001041 @Override
1042 public boolean hasOverlappingRendering() {
1043
1044 // This is not really true, but good enough when fading from the contracted to the expanded
1045 // layout, and saves us some layers.
1046 return false;
1047 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001048
Selim Cinek1a48bab2017-02-17 19:38:40 -08001049 public void setLegacy(boolean legacy) {
1050 mLegacy = legacy;
1051 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -08001052 }
1053
Selim Cinek1a48bab2017-02-17 19:38:40 -08001054 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -08001055 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001056 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001057 }
1058 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001059 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001060 }
1061 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001062 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001063 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001064 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001065
Selim Cinek83bc7832015-10-22 13:26:54 -07001066 public void setIsChildInGroup(boolean isChildInGroup) {
1067 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001068 if (mContractedChild != null) {
1069 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1070 }
1071 if (mExpandedChild != null) {
1072 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1073 }
1074 if (mHeadsUpChild != null) {
1075 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1076 }
1077 if (mAmbientChild != null) {
1078 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1079 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001080 updateAllSingleLineViews();
Selim Cinek83bc7832015-10-22 13:26:54 -07001081 }
1082
Adrian Roosb88b1a12015-12-09 18:51:05 -08001083 public void onNotificationUpdated(NotificationData.Entry entry) {
1084 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -08001085 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001086 updateAllSingleLineViews();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001087 if (mContractedChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001088 mContractedWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001089 }
1090 if (mExpandedChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001091 mExpandedWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001092 }
1093 if (mHeadsUpChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001094 mHeadsUpWrapper.notifyContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001095 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001096 if (mAmbientChild != null) {
Selim Cinek414ad332017-02-24 19:06:12 -08001097 mAmbientWrapper.notifyContentUpdated(entry.row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001098 }
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001099 applyRemoteInput(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001100 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001101 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001102 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001103 mPreviousExpandedRemoteInputIntent = null;
1104 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001105 }
1106
Adrian Roos6f6e1592017-05-02 16:22:53 -07001107 private void updateAllSingleLineViews() {
1108 updateSingleLineView();
1109 updateAmbientSingleLineView();
1110 }
Selim Cinek83bc7832015-10-22 13:26:54 -07001111 private void updateSingleLineView() {
1112 if (mIsChildInGroup) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001113 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001114 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekde33a4a2016-02-11 16:43:41 -08001115 } else if (mSingleLineView != null) {
1116 removeView(mSingleLineView);
1117 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001118 }
1119 }
1120
Adrian Roos6f6e1592017-05-02 16:22:53 -07001121 private void updateAmbientSingleLineView() {
1122 if (mIsChildInGroup) {
1123 mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
1124 mAmbientSingleLineChild, mStatusBarNotification.getNotification());
1125 } else if (mAmbientSingleLineChild != null) {
1126 removeView(mAmbientSingleLineChild);
1127 mAmbientSingleLineChild = null;
1128 }
1129 }
1130
Adrian Roosb88b1a12015-12-09 18:51:05 -08001131 private void applyRemoteInput(final NotificationData.Entry entry) {
1132 if (mRemoteInputController == null) {
1133 return;
1134 }
1135
1136 boolean hasRemoteInput = false;
1137
1138 Notification.Action[] actions = entry.notification.getNotification().actions;
1139 if (actions != null) {
1140 for (Notification.Action a : actions) {
1141 if (a.getRemoteInputs() != null) {
1142 for (RemoteInput ri : a.getRemoteInputs()) {
1143 if (ri.getAllowFreeFormInput()) {
1144 hasRemoteInput = true;
1145 break;
1146 }
1147 }
1148 }
1149 }
1150 }
1151
1152 View bigContentView = mExpandedChild;
1153 if (bigContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001154 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001155 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1156 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001157 } else {
1158 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001159 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001160 if (mCachedExpandedRemoteInput != null
1161 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1162 // We had a cached remote input but didn't reuse it. Clean up required.
1163 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1164 }
1165 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001166
Adrian Roosb88b1a12015-12-09 18:51:05 -08001167 View headsUpContentView = mHeadsUpChild;
1168 if (headsUpContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001169 mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001170 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001171 } else {
1172 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001173 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001174 if (mCachedHeadsUpRemoteInput != null
1175 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1176 // We had a cached remote input but didn't reuse it. Clean up required.
1177 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1178 }
1179 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001180 }
1181
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001182 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001183 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001184 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001185 View actionContainerCandidate = view.findViewById(
1186 com.android.internal.R.id.actions_container);
1187 if (actionContainerCandidate instanceof FrameLayout) {
1188 RemoteInputView existing = (RemoteInputView)
1189 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1190
1191 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001192 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001193 }
1194
1195 if (existing == null && hasRemoteInput) {
1196 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001197 if (cachedView == null) {
1198 RemoteInputView riv = RemoteInputView.inflate(
1199 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001200
Adrian Roos7813dd72016-09-23 17:12:17 -07001201 riv.setVisibility(View.INVISIBLE);
1202 actionContainer.addView(riv, new LayoutParams(
1203 ViewGroup.LayoutParams.MATCH_PARENT,
1204 ViewGroup.LayoutParams.MATCH_PARENT)
1205 );
1206 existing = riv;
1207 } else {
1208 actionContainer.addView(cachedView);
1209 cachedView.dispatchFinishTemporaryDetach();
1210 cachedView.requestFocus();
1211 existing = cachedView;
1212 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001213 }
1214 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001215 int color = entry.notification.getNotification().color;
1216 if (color == Notification.COLOR_DEFAULT) {
1217 color = mContext.getColor(R.color.default_remote_input_background);
1218 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001219 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001220 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001221 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001222
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001223 existing.setWrapper(wrapper);
1224
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001225 if (existingPendingIntent != null || existing.isActive()) {
1226 // The current action could be gone, or the pending intent no longer valid.
1227 // If we find a matching action in the new notification, focus, otherwise close.
1228 Notification.Action[] actions = entry.notification.getNotification().actions;
1229 if (existingPendingIntent != null) {
1230 existing.setPendingIntent(existingPendingIntent);
1231 }
1232 if (existing.updatePendingIntentFromActions(actions)) {
1233 if (!existing.isActive()) {
1234 existing.focus();
1235 }
1236 } else {
1237 if (existing.isActive()) {
1238 existing.close();
1239 }
1240 }
1241 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001242 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001243 return existing;
1244 }
1245 return null;
1246 }
1247
1248 public void closeRemoteInput() {
1249 if (mHeadsUpRemoteInput != null) {
1250 mHeadsUpRemoteInput.close();
1251 }
1252 if (mExpandedRemoteInput != null) {
1253 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001254 }
1255 }
1256
Selim Cinek83bc7832015-10-22 13:26:54 -07001257 public void setGroupManager(NotificationGroupManager groupManager) {
1258 mGroupManager = groupManager;
1259 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001260
Adrian Roosb88b1a12015-12-09 18:51:05 -08001261 public void setRemoteInputController(RemoteInputController r) {
1262 mRemoteInputController = r;
1263 }
1264
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001265 public void setExpandClickListener(OnClickListener expandClickListener) {
1266 mExpandClickListener = expandClickListener;
1267 }
1268
1269 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001270 mExpandable = expandable;
1271 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001272 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001273 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1274 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001275 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001276 expandable = false;
1277 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001278 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001279 expandable = false;
1280 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001281 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001282 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001283 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001284 }
1285 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001286 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001287 }
1288 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001289 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001290 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001291 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001292 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001293
1294 public NotificationHeaderView getNotificationHeader() {
1295 NotificationHeaderView header = null;
1296 if (mContractedChild != null) {
1297 header = mContractedWrapper.getNotificationHeader();
1298 }
1299 if (header == null && mExpandedChild != null) {
1300 header = mExpandedWrapper.getNotificationHeader();
1301 }
1302 if (header == null && mHeadsUpChild != null) {
1303 header = mHeadsUpWrapper.getNotificationHeader();
1304 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001305 if (header == null && mAmbientChild != null) {
1306 header = mAmbientWrapper.getNotificationHeader();
1307 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001308 return header;
1309 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001310
Selim Cinek34eda5e2016-02-18 17:10:43 -08001311 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001312 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001313 return wrapper == null ? null : wrapper.getNotificationHeader();
1314 }
1315
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001316 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1317 mContainingNotification = containingNotification;
1318 }
1319
1320 public void requestSelectLayout(boolean needsAnimation) {
1321 selectLayout(needsAnimation, false);
1322 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001323
1324 public void reInflateViews() {
1325 if (mIsChildInGroup && mSingleLineView != null) {
1326 removeView(mSingleLineView);
1327 mSingleLineView = null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001328 updateAllSingleLineViews();
Selim Cinekde33a4a2016-02-11 16:43:41 -08001329 }
1330 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001331
1332 public void setUserExpanding(boolean userExpanding) {
1333 mUserExpanding = userExpanding;
1334 if (userExpanding) {
1335 mTransformationStartVisibleType = mVisibleType;
1336 } else {
1337 mTransformationStartVisibleType = UNDEFINED;
1338 mVisibleType = calculateVisibleType();
1339 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001340 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001341 }
1342 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001343
1344 /**
1345 * Set by how much the single line view should be indented. Used when a overflow indicator is
1346 * present and only during measuring
1347 */
1348 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1349 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1350 mSingleLineWidthIndention = singleLineWidthIndention;
1351 mContainingNotification.forceLayout();
1352 forceLayout();
1353 }
1354 }
1355
1356 public HybridNotificationView getSingleLineView() {
1357 return mSingleLineView;
1358 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001359
1360 public void setRemoved() {
1361 if (mExpandedRemoteInput != null) {
1362 mExpandedRemoteInput.setRemoved();
1363 }
1364 if (mHeadsUpRemoteInput != null) {
1365 mHeadsUpRemoteInput.setRemoved();
1366 }
1367 }
Adrian Roos599be342016-06-13 14:54:39 -07001368
1369 public void setContentHeightAnimating(boolean animating) {
1370 if (!animating) {
1371 mContentHeightAtAnimationStart = UNDEFINED;
1372 }
1373 }
Selim Cineke9bad242016-06-15 11:46:37 -07001374
Adrian Roose18033c2017-01-17 15:22:49 -08001375 @VisibleForTesting
1376 boolean isAnimatingVisibleType() {
1377 return mAnimationStartVisibleType != UNDEFINED;
1378 }
1379
Selim Cinekcafa87f2016-10-26 17:00:17 -07001380 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1381 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001382 selectLayout(false /* animate */, true /* force */);
1383 }
1384
Selim Cineke9bad242016-06-15 11:46:37 -07001385 public void setFocusOnVisibilityChange() {
1386 mFocusOnVisibilityChange = true;
1387 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001388
1389 public void setIconsVisible(boolean iconsVisible) {
1390 mIconsVisible = iconsVisible;
1391 updateIconVisibilities();
1392 }
1393
1394 private void updateIconVisibilities() {
1395 if (mContractedWrapper != null) {
1396 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1397 if (header != null) {
1398 header.getIcon().setForceHidden(!mIconsVisible);
1399 }
1400 }
1401 if (mHeadsUpWrapper != null) {
1402 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1403 if (header != null) {
1404 header.getIcon().setForceHidden(!mIconsVisible);
1405 }
1406 }
1407 if (mExpandedWrapper != null) {
1408 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1409 if (header != null) {
1410 header.getIcon().setForceHidden(!mIconsVisible);
1411 }
1412 }
1413 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001414
Adrian Roos4320e892017-01-24 12:50:34 -08001415 @Override
1416 public void onVisibilityAggregated(boolean isVisible) {
1417 super.onVisibilityAggregated(isVisible);
1418 if (isVisible) {
1419 fireExpandedVisibleListenerIfVisible();
1420 }
1421 }
1422
1423 /**
1424 * Sets a one-shot listener for when the expanded view becomes visible.
1425 *
1426 * This will fire the listener immediately if the expanded view is already visible.
1427 */
1428 public void setOnExpandedVisibleListener(Runnable r) {
1429 mExpandedVisibleListener = r;
1430 fireExpandedVisibleListenerIfVisible();
1431 }
1432
Selim Cinek6743c0b2017-01-18 18:24:01 -08001433 public void setIsLowPriority(boolean isLowPriority) {
1434 mIsLowPriority = isLowPriority;
1435 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001436
1437 public boolean isDimmable() {
1438 if (!mContractedWrapper.isDimmable()) {
1439 return false;
1440 }
1441 return true;
1442 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001443
1444 /**
1445 * Should a single click be disallowed on this view when on the keyguard?
1446 */
1447 public boolean disallowSingleClick(float x, float y) {
1448 NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
1449 if (visibleWrapper != null) {
1450 return visibleWrapper.disallowSingleClick(x, y);
1451 }
1452 return false;
1453 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001454}