blob: e527be1a61f9801fcfa75df15d9697410db7e66d [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;
Julia Reynoldsfc640012018-02-21 12:25:27 -050026import android.util.ArraySet;
Jorim Jaggibe565df2014-04-28 17:51:23 +020027import android.util.AttributeSet;
Selim Cinek011bde92017-11-22 05:53:38 -080028import android.util.Log;
Selim Cineka77d2c52018-04-09 10:44:52 -070029import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080030import android.view.NotificationHeaderView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020031import android.view.View;
Selim Cinek8d490d42015-04-10 00:05:50 -070032import android.view.ViewGroup;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010033import android.view.ViewTreeObserver;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020034import android.widget.FrameLayout;
Selim Cineke9bad242016-06-15 11:46:37 -070035import android.widget.ImageView;
Petr Cermak7997d7a2018-01-18 15:52:10 +000036import android.widget.LinearLayout;
Selim Cinek8d490d42015-04-10 00:05:50 -070037
Adrian Roose18033c2017-01-17 15:22:49 -080038import com.android.internal.annotations.VisibleForTesting;
Adrian Roos4ff3b122016-02-01 12:26:13 -080039import com.android.internal.util.NotificationColorUtil;
Petr Cermak10011fa2018-02-05 19:00:54 +000040import com.android.systemui.Dependency;
Jorim Jaggibe565df2014-04-28 17:51:23 +020041import com.android.systemui.R;
Selim Cinekc897bd32016-03-18 17:32:31 -070042import com.android.systemui.statusbar.notification.HybridGroupManager;
Selim Cinek011bde92017-11-22 05:53:38 -080043import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinek0ffbda62016-01-01 20:29:12 +010044import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
Selim Cinekc3179332016-03-04 14:44:56 -080045import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cinek0ffbda62016-01-01 20:29:12 +010046import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070047import com.android.systemui.statusbar.phone.NotificationGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080048import com.android.systemui.statusbar.policy.RemoteInputView;
Petr Cermak10011fa2018-02-05 19:00:54 +000049import com.android.systemui.statusbar.policy.SmartReplyConstants;
Petr Cermak7997d7a2018-01-18 15:52:10 +000050import com.android.systemui.statusbar.policy.SmartReplyView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020051
52/**
Selim Cinek684a4422015-04-15 16:18:39 -070053 * A frame layout containing the actual payload of the notification, including the contracted,
54 * expanded and heads up layout. This class is responsible for clipping the content and and
55 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020056 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020057public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020058
Selim Cinek011bde92017-11-22 05:53:38 -080059 private static final String TAG = "NotificationContentView";
Selim Cinek131f1a42017-06-05 17:50:19 -070060 public static final int VISIBLE_TYPE_CONTRACTED = 0;
61 public static final int VISIBLE_TYPE_EXPANDED = 1;
62 public static final int VISIBLE_TYPE_HEADSUP = 2;
Selim Cinek83bc7832015-10-22 13:26:54 -070063 private static final int VISIBLE_TYPE_SINGLELINE = 3;
Selim Cinek131f1a42017-06-05 17:50:19 -070064 public static final int VISIBLE_TYPE_AMBIENT = 4;
Adrian Roos6f6e1592017-05-02 16:22:53 -070065 private static final int VISIBLE_TYPE_AMBIENT_SINGLELINE = 5;
Selim Cinekc3179332016-03-04 14:44:56 -080066 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020067
Jorim Jaggibe565df2014-04-28 17:51:23 +020068 private final Rect mClipBounds = new Rect();
69
Selim Cinek9b49f6d2017-11-13 18:19:35 -080070 private int mMinContractedHeight;
71 private int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020072 private View mContractedChild;
73 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070074 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070075 private HybridNotificationView mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -080076 private View mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -070077 private HybridNotificationView mAmbientSingleLineChild;
Jorim Jaggibe565df2014-04-28 17:51:23 +020078
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070079 private RemoteInputView mExpandedRemoteInput;
80 private RemoteInputView mHeadsUpRemoteInput;
Petr Cermak10011fa2018-02-05 19:00:54 +000081
82 private SmartReplyConstants mSmartReplyConstants;
Milo Sredkove7cf4982018-04-09 15:08:26 +010083 private SmartReplyView mExpandedSmartReplyView;
Kenny Guy23991102018-04-05 21:18:38 +010084 private SmartReplyLogger mSmartReplyLogger;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070085
Jorim Jaggi4e857f42014-11-17 19:14:04 +010086 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070087 private NotificationViewWrapper mExpandedWrapper;
88 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -080089 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -070090 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020091 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +010092 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -070093 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -040094 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010095 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -070096 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -080097 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -070098 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -080099 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800100 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800101 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800102 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700103 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700104 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800105 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -0800106 private Runnable mExpandedVisibleListener;
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700107
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700108 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100109 = new ViewTreeObserver.OnPreDrawListener() {
110 @Override
111 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -0800112 // We need to post since we don't want the notification to animate on the very first
113 // frame
114 post(new Runnable() {
115 @Override
116 public void run() {
117 mAnimate = true;
118 }
119 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100120 getViewTreeObserver().removeOnPreDrawListener(this);
121 return true;
122 }
123 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200124
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800125 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800126 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800127 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100128 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800129 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700130 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800131 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700132 /** The visible type at the start of an animation driven transformation */
133 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800134 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700135 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700136 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700137 private PendingIntent mPreviousExpandedRemoteInputIntent;
138 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700139 private RemoteInputView mCachedExpandedRemoteInput;
140 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800141
Adrian Roos599be342016-06-13 14:54:39 -0700142 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700143 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700144 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700145 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700146 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800147 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800148 private boolean mIsContentExpandable;
Selim Cineked64a142018-02-06 18:06:01 -0800149 private boolean mRemoteInputVisible;
150 private int mUnrestrictedContentHeight;
Adrian Roos599be342016-06-13 14:54:39 -0700151
152
Jorim Jaggibe565df2014-04-28 17:51:23 +0200153 public NotificationContentView(Context context, AttributeSet attrs) {
154 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700155 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Petr Cermak10011fa2018-02-05 19:00:54 +0000156 mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
Kenny Guy23991102018-04-05 21:18:38 +0100157 mSmartReplyLogger = Dependency.get(SmartReplyLogger.class);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800158 initView();
159 }
160
161 public void initView() {
Selim Cinek860b6da2015-12-16 19:02:19 -0800162 mMinContractedHeight = getResources().getDimensionPixelSize(
163 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800164 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
165 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200166 }
167
Adrian Roos0aac04f2016-12-08 15:59:29 -0800168 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
169 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800170 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800171 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800172 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800173 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800174 }
175
Jorim Jaggibe565df2014-04-28 17:51:23 +0200176 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700177 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
178 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
179 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
180 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
181 int maxSize = Integer.MAX_VALUE;
Selim Cinekc897bd32016-03-18 17:32:31 -0700182 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700183 if (hasFixedHeight || isHeightLimited) {
184 maxSize = MeasureSpec.getSize(heightMeasureSpec);
185 }
186 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800187 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +0100188 int notificationMaxHeight = mNotificationMaxHeight;
189 if (mExpandedSmartReplyView != null) {
190 notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
191 }
192 int size = Math.min(maxSize, notificationMaxHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800193 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800194 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800195 if (layoutParams.height >= 0) {
196 // An actual height is set
197 size = Math.min(maxSize, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800198 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800199 }
200 int spec = size == Integer.MAX_VALUE
201 ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
Selim Cinek6743c0b2017-01-18 18:24:01 -0800202 : MeasureSpec.makeMeasureSpec(size, useExactly
203 ? MeasureSpec.EXACTLY
204 : MeasureSpec.AT_MOST);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800205 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800206 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
207 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700208 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800209 int heightSpec;
Selim Cinekf619ffc2016-02-17 14:53:05 -0800210 int size = Math.min(maxSize, mSmallHeight);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800211 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
212 boolean useExactly = false;
213 if (layoutParams.height >= 0) {
214 // An actual height is set
215 size = Math.min(size, layoutParams.height);
216 useExactly = true;
217 }
218 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800219 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
220 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800221 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800222 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800223 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800224 int measuredHeight = mContractedChild.getMeasuredHeight();
225 if (measuredHeight < mMinContractedHeight) {
226 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800227 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800228 }
229 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800230 if (updateContractedHeaderWidth()) {
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800231 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700232 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700233 if (mExpandedChild != null
234 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
235 // the Expanded child is smaller then the collapsed. Let's remeasure it.
236 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
237 MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800238 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek1ba41d12016-04-21 16:14:46 -0700239 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700240 }
241 if (mHeadsUpChild != null) {
Selim Cinek396caca2018-04-10 17:46:46 -0700242 int maxHeight = mHeadsUpHeight;
243 maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
244 int size = Math.min(maxSize, maxHeight);
Selim Cinek8d490d42015-04-10 00:05:50 -0700245 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800246 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700247 if (layoutParams.height >= 0) {
248 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800249 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800250 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700251 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800252 measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800253 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800254 : MeasureSpec.AT_MOST), 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700255 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
256 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700257 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700258 int singleLineWidthSpec = widthMeasureSpec;
259 if (mSingleLineWidthIndention != 0
260 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
261 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
262 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700263 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700264 }
265 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinek7b836392015-12-04 20:02:59 -0800266 MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700267 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
268 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800269 if (mAmbientChild != null) {
270 int size = Math.min(maxSize, mNotificationAmbientHeight);
271 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800272 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800273 if (layoutParams.height >= 0) {
274 // An actual height is set
275 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800276 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800277 }
278 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800279 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
280 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800281 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
282 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700283 if (mAmbientSingleLineChild != null) {
284 int size = Math.min(maxSize, mNotificationAmbientHeight);
285 ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
286 boolean useExactly = false;
287 if (layoutParams.height >= 0) {
288 // An actual height is set
289 size = Math.min(size, layoutParams.height);
290 useExactly = true;
291 }
292 int ambientSingleLineWidthSpec = widthMeasureSpec;
293 if (mSingleLineWidthIndention != 0
294 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
295 ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
296 width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
297 MeasureSpec.EXACTLY);
298 }
299 mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
300 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
301 : MeasureSpec.AT_MOST));
302 maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
303 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700304 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700305 setMeasuredDimension(width, ownHeight);
306 }
307
Selim Cineked64a142018-02-06 18:06:01 -0800308 /**
309 * Get the extra height that needs to be added to the notification height for a given
310 * {@link RemoteInputView}.
311 * This is needed when the user is inline replying in order to ensure that the reply bar has
312 * enough padding.
313 *
314 * @param remoteInput The remote input to check.
315 * @return The extra height needed.
316 */
317 private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
318 if (remoteInput != null && remoteInput.getVisibility() == VISIBLE
319 && remoteInput.isActive()) {
320 return getResources().getDimensionPixelSize(
321 com.android.internal.R.dimen.notification_content_margin);
322 }
323 return 0;
324 }
325
Selim Cinek6ecc8102016-01-26 18:26:19 -0800326 private boolean updateContractedHeaderWidth() {
327 // We need to update the expanded and the collapsed header to have exactly the same with to
328 // have the expand buttons laid out at the same location.
329 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
330 if (contractedHeader != null) {
331 if (mExpandedChild != null
332 && mExpandedWrapper.getNotificationHeader() != null) {
333 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
334 int expandedSize = expandedHeader.getMeasuredWidth()
335 - expandedHeader.getPaddingEnd();
336 int collapsedSize = contractedHeader.getMeasuredWidth()
337 - expandedHeader.getPaddingEnd();
338 if (expandedSize != collapsedSize) {
339 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
340 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800341 contractedHeader.isLayoutRtl()
342 ? paddingEnd
343 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800344 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800345 contractedHeader.isLayoutRtl()
346 ? contractedHeader.getPaddingLeft()
347 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800348 contractedHeader.getPaddingBottom());
349 contractedHeader.setShowWorkBadgeAtEnd(true);
350 return true;
351 }
352 } else {
353 int paddingEnd = mNotificationContentMarginEnd;
354 if (contractedHeader.getPaddingEnd() != paddingEnd) {
355 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800356 contractedHeader.isLayoutRtl()
357 ? paddingEnd
358 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800359 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800360 contractedHeader.isLayoutRtl()
361 ? contractedHeader.getPaddingLeft()
362 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800363 contractedHeader.getPaddingBottom());
364 contractedHeader.setShowWorkBadgeAtEnd(false);
365 return true;
366 }
367 }
368 }
369 return false;
370 }
371
Selim Cinek860b6da2015-12-16 19:02:19 -0800372 private boolean shouldContractedBeFixedSize() {
373 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
374 }
375
Selim Cinek8d490d42015-04-10 00:05:50 -0700376 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200377 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700378 int previousHeight = 0;
379 if (mExpandedChild != null) {
380 previousHeight = mExpandedChild.getHeight();
381 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200382 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700383 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
384 mContentHeightAtAnimationStart = previousHeight;
385 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200386 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700387 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700388 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
389 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700390 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200391 }
392
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100393 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100394 protected void onAttachedToWindow() {
395 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100396 updateVisibility();
397 }
398
Selim Cinekcab4a602014-09-03 14:47:57 +0200399 public View getContractedChild() {
400 return mContractedChild;
401 }
402
403 public View getExpandedChild() {
404 return mExpandedChild;
405 }
406
Selim Cinek8d490d42015-04-10 00:05:50 -0700407 public View getHeadsUpChild() {
408 return mHeadsUpChild;
409 }
410
Adrian Roos0aac04f2016-12-08 15:59:29 -0800411 public View getAmbientChild() {
412 return mAmbientChild;
413 }
414
Adrian Roos6f6e1592017-05-02 16:22:53 -0700415 public HybridNotificationView getAmbientSingleLineChild() {
416 return mAmbientSingleLineChild;
417 }
418
Jorim Jaggibe565df2014-04-28 17:51:23 +0200419 public void setContractedChild(View child) {
420 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200421 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200422 removeView(mContractedChild);
423 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200424 addView(child);
425 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700426 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
427 mContainingNotification);
Jorim Jaggi10ad7612014-12-08 18:41:11 +0100428 mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800429 }
430
431 private NotificationViewWrapper getWrapperForView(View child) {
432 if (child == mContractedChild) {
433 return mContractedWrapper;
434 }
435 if (child == mExpandedChild) {
436 return mExpandedWrapper;
437 }
438 if (child == mHeadsUpChild) {
439 return mHeadsUpWrapper;
440 }
441 if (child == mAmbientChild) {
442 return mAmbientWrapper;
443 }
444 return null;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200445 }
446
447 public void setExpandedChild(View child) {
448 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800449 mPreviousExpandedRemoteInputIntent = null;
450 if (mExpandedRemoteInput != null) {
451 mExpandedRemoteInput.onNotificationUpdateOrReset();
452 if (mExpandedRemoteInput.isActive()) {
453 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
454 mCachedExpandedRemoteInput = mExpandedRemoteInput;
455 mExpandedRemoteInput.dispatchStartTemporaryDetach();
456 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
457 }
458 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200459 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200460 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800461 mExpandedRemoteInput = null;
462 }
463 if (child == null) {
464 mExpandedChild = null;
465 mExpandedWrapper = null;
466 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
467 mVisibleType = VISIBLE_TYPE_CONTRACTED;
468 }
469 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
470 mTransformationStartVisibleType = UNDEFINED;
471 }
472 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200473 }
474 addView(child);
475 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700476 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
477 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200478 }
479
Selim Cinek8d490d42015-04-10 00:05:50 -0700480 public void setHeadsUpChild(View child) {
481 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800482 mPreviousHeadsUpRemoteInputIntent = null;
483 if (mHeadsUpRemoteInput != null) {
484 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
485 if (mHeadsUpRemoteInput.isActive()) {
486 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
487 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
488 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
489 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
490 }
491 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700492 mHeadsUpChild.animate().cancel();
493 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800494 mHeadsUpRemoteInput = null;
495 }
496 if (child == null) {
497 mHeadsUpChild = null;
498 mHeadsUpWrapper = null;
499 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
500 mVisibleType = VISIBLE_TYPE_CONTRACTED;
501 }
502 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
503 mTransformationStartVisibleType = UNDEFINED;
504 }
505 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700506 }
507 addView(child);
508 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700509 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
510 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700511 }
512
Adrian Roos0aac04f2016-12-08 15:59:29 -0800513 public void setAmbientChild(View child) {
514 if (mAmbientChild != null) {
515 mAmbientChild.animate().cancel();
516 removeView(mAmbientChild);
517 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700518 if (child == null) {
519 return;
520 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800521 addView(child);
522 mAmbientChild = child;
523 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
524 mContainingNotification);
525 }
526
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100527 @Override
528 protected void onVisibilityChanged(View changedView, int visibility) {
529 super.onVisibilityChanged(changedView, visibility);
530 updateVisibility();
531 }
532
533 private void updateVisibility() {
534 setVisible(isShown());
535 }
536
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700537 @Override
538 protected void onDetachedFromWindow() {
539 super.onDetachedFromWindow();
540 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
541 }
542
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100543 private void setVisible(final boolean isVisible) {
544 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700545 // This call can happen multiple times, but removing only removes a single one.
546 // We therefore need to remove the old one.
547 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100548 // We only animate if we are drawn at least once, otherwise the view might animate when
549 // it's shown the first time
550 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
551 } else {
552 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
553 mAnimate = false;
554 }
555 }
556
Selim Cineke9bad242016-06-15 11:46:37 -0700557 private void focusExpandButtonIfNecessary() {
558 if (mFocusOnVisibilityChange) {
559 NotificationHeaderView header = getVisibleNotificationHeader();
560 if (header != null) {
561 ImageView expandButton = header.getExpandButton();
562 if (expandButton != null) {
563 expandButton.requestAccessibilityFocus();
564 }
565 }
566 mFocusOnVisibilityChange = false;
567 }
568 }
569
Selim Cinekb5605e52015-02-20 18:21:41 +0100570 public void setContentHeight(int contentHeight) {
Selim Cineked64a142018-02-06 18:06:01 -0800571 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
572 int maxContentHeight = mContainingNotification.getIntrinsicHeight()
573 - getExtraRemoteInputHeight(mExpandedRemoteInput)
574 - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
575 mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100576 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400577
578 int minHeightHint = getMinContentHeightHint();
579
580 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
581 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800582 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400583 }
584
585 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
586 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800587 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400588 }
589
Jorim Jaggibe565df2014-04-28 17:51:23 +0200590 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700591 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200592 }
593
Adrian Roos181385c2016-05-05 17:45:44 -0400594 /**
595 * @return the minimum apparent height that the wrapper should allow for the purpose
596 * of aligning elements at the bottom edge. If this is larger than the content
597 * height, the notification is clipped instead of being further shrunk.
598 */
599 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700600 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400601 return mContext.getResources().getDimensionPixelSize(
602 com.android.internal.R.dimen.notification_action_list_height);
603 }
Adrian Roos599be342016-06-13 14:54:39 -0700604
605 // Transition between heads-up & expanded, or pinned.
606 if (mHeadsUpChild != null && mExpandedChild != null) {
607 boolean transitioningBetweenHunAndExpanded =
608 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
609 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700610 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800611 && (mIsHeadsUp || mHeadsUpAnimatingAway)
612 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700613 if (transitioningBetweenHunAndExpanded || pinned) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800614 return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
615 getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700616 }
617 }
618
619 // Size change of the expanded version
620 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
621 && mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800622 return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700623 }
624
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700625 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800626 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
627 hint = mAmbientChild.getHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700628 } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
629 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
630 hint = mAmbientSingleLineChild.getHeight();
Adrian Roos05d3d002017-01-26 15:28:07 -0800631 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800632 hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
Adrian Roos599be342016-06-13 14:54:39 -0700633 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800634 hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
Adrian Roos181385c2016-05-05 17:45:44 -0400635 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800636 hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
637 + mContext.getResources().getDimensionPixelSize(
638 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400639 }
Adrian Roos599be342016-06-13 14:54:39 -0700640
641 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800642 hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700643 }
644 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400645 }
646
Adrian Roos599be342016-06-13 14:54:39 -0700647 private boolean isTransitioningFromTo(int from, int to) {
648 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
649 && mVisibleType == to;
650 }
651
652 private boolean isVisibleOrTransitioning(int type) {
653 return mVisibleType == type || mTransformationStartVisibleType == type
654 || mAnimationStartVisibleType == type;
655 }
656
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800657 private void updateContentTransformation() {
658 int visibleType = calculateVisibleType();
659 if (visibleType != mVisibleType) {
660 // A new transformation starts
661 mTransformationStartVisibleType = mVisibleType;
662 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
663 final TransformableView hiddenView = getTransformableViewForVisibleType(
664 mTransformationStartVisibleType);
665 shownView.transformFrom(hiddenView, 0.0f);
666 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
667 hiddenView.transformTo(shownView, 0.0f);
668 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800669 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800670 }
Selim Cineke8578872016-05-03 16:42:50 -0700671 if (mForceSelectNextLayout) {
672 forceUpdateVisibilities();
673 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800674 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700675 && mVisibleType != mTransformationStartVisibleType
676 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800677 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
678 final TransformableView hiddenView = getTransformableViewForVisibleType(
679 mTransformationStartVisibleType);
680 float transformationAmount = calculateTransformationAmount();
681 shownView.transformFrom(hiddenView, transformationAmount);
682 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800683 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800684 } else {
685 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800686 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800687 }
688 }
689
Selim Cinekc3179332016-03-04 14:44:56 -0800690 private void updateBackgroundTransformation(float transformationAmount) {
691 int endColor = getBackgroundColor(mVisibleType);
692 int startColor = getBackgroundColor(mTransformationStartVisibleType);
693 if (endColor != startColor) {
694 if (startColor == 0) {
695 startColor = mContainingNotification.getBackgroundColorWithoutTint();
696 }
697 if (endColor == 0) {
698 endColor = mContainingNotification.getBackgroundColorWithoutTint();
699 }
700 endColor = NotificationUtils.interpolateColors(startColor, endColor,
701 transformationAmount);
702 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400703 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800704 mContainingNotification.setContentBackground(endColor, false, this);
705 }
706
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800707 private float calculateTransformationAmount() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800708 int startHeight = getViewHeight(mTransformationStartVisibleType);
709 int endHeight = getViewHeight(mVisibleType);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800710 int progress = Math.abs(mContentHeight - startHeight);
711 int totalDistance = Math.abs(endHeight - startHeight);
Selim Cinek011bde92017-11-22 05:53:38 -0800712 if (totalDistance == 0) {
713 Log.wtf(TAG, "the total transformation distance is 0"
714 + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
715 + "\n VisibleType: " + mVisibleType + " height: " + endHeight
716 + "\n mContentHeight: " + mContentHeight);
717 return 1.0f;
718 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800719 float amount = (float) progress / (float) totalDistance;
720 return Math.min(1.0f, amount);
721 }
722
Selim Cinekb5605e52015-02-20 18:21:41 +0100723 public int getContentHeight() {
724 return mContentHeight;
725 }
726
Jorim Jaggibe565df2014-04-28 17:51:23 +0200727 public int getMaxHeight() {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700728 if (mContainingNotification.isShowingAmbient()) {
729 return getShowingAmbientView().getHeight();
730 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800731 return getViewHeight(VISIBLE_TYPE_EXPANDED)
732 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cinekc494e382017-01-31 16:09:23 -0800733 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800734 return getViewHeight(VISIBLE_TYPE_HEADSUP)
735 + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
Selim Cinek8d490d42015-04-10 00:05:50 -0700736 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800737 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
738 }
739
740 private int getViewHeight(int visibleType) {
741 View view = getViewForVisibleType(visibleType);
742 int height = view.getHeight();
743 NotificationViewWrapper viewWrapper = getWrapperForView(view);
744 if (viewWrapper != null) {
745 height += viewWrapper.getHeaderTranslation();
746 }
747 return height;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200748 }
749
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200750 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800751 return getMinHeight(false /* likeGroupExpanded */);
752 }
753
754 public int getMinHeight(boolean likeGroupExpanded) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700755 if (mContainingNotification.isShowingAmbient()) {
756 return getShowingAmbientView().getHeight();
757 } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800758 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
Selim Cinek42357e02016-02-24 18:48:01 -0800759 } else {
760 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700761 }
762 }
763
Adrian Roos6f6e1592017-05-02 16:22:53 -0700764 public View getShowingAmbientView() {
765 View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
766 if (v != null) {
767 return v;
768 } else {
769 return mContractedChild;
770 }
771 }
772
Selim Cinek83bc7832015-10-22 13:26:54 -0700773 private boolean isGroupExpanded() {
774 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200775 }
776
Jorim Jaggibe565df2014-04-28 17:51:23 +0200777 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200778 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200779 updateClipping();
780 }
781
Selim Cineka686b2c2016-10-26 13:58:27 -0700782
783 public void setClipBottomAmount(int clipBottomAmount) {
784 mClipBottomAmount = clipBottomAmount;
785 updateClipping();
786 }
787
Selim Cinek875a3a12016-11-18 17:52:16 -0800788 @Override
789 public void setTranslationY(float translationY) {
790 super.setTranslationY(translationY);
791 updateClipping();
792 }
793
Jorim Jaggibe565df2014-04-28 17:51:23 +0200794 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100795 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800796 int top = (int) (mClipTopAmount - getTranslationY());
Selim Cineked64a142018-02-06 18:06:01 -0800797 int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700798 bottom = Math.max(top, bottom);
799 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100800 setClipBounds(mClipBounds);
801 } else {
802 setClipBounds(null);
803 }
804 }
805
806 public void setClipToActualHeight(boolean clipToActualHeight) {
807 mClipToActualHeight = clipToActualHeight;
808 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200809 }
810
Jorim Jaggi11298832014-05-24 16:18:38 +0200811 private void selectLayout(boolean animate, boolean force) {
812 if (mContractedChild == null) {
813 return;
814 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800815 if (mUserExpanding) {
816 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700817 } else {
818 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700819 boolean changedType = visibleType != mVisibleType;
820 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400821 View visibleView = getViewForVisibleType(visibleType);
822 if (visibleView != null) {
823 visibleView.setVisibility(VISIBLE);
824 transferRemoteInputFocus(visibleType);
825 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700826
Selim Cinek589fd3e2016-04-26 18:17:57 -0700827 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
828 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
829 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
830 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
831 animateToVisibleType(visibleType);
832 } else {
833 updateViewVisibilities(visibleType);
834 }
835 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700836 if (changedType) {
837 focusExpandButtonIfNecessary();
838 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800839 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
840 if (visibleWrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800841 visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
842 getMinContentHeightHint());
Adrian Roos05d3d002017-01-26 15:28:07 -0800843 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700844 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200845 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700846 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700847 }
848
849 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800850 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
851 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
852 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
853 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
854 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700855 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
856 mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800857 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800858 // forceUpdateVisibilities cancels outstanding animations without updating the
859 // mAnimationStartVisibleType. Do so here instead.
860 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800861 }
862
Adrian Roos4320e892017-01-24 12:50:34 -0800863 private void fireExpandedVisibleListenerIfVisible() {
864 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
865 && mExpandedChild.getVisibility() == VISIBLE) {
866 Runnable listener = mExpandedVisibleListener;
867 mExpandedVisibleListener = null;
868 listener.run();
869 }
870 }
871
Adrian Roos0aac04f2016-12-08 15:59:29 -0800872 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
873 if (view == null) {
874 return;
875 }
876 boolean visible = mVisibleType == type
877 || mTransformationStartVisibleType == type;
878 if (!visible) {
879 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700880 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800881 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200882 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200883 }
884
Selim Cinekc3179332016-03-04 14:44:56 -0800885 public void updateBackgroundColor(boolean animate) {
886 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400887 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800888 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
889 }
890
Mady Mellorb0a82462016-04-30 17:31:02 -0700891 public int getVisibleType() {
892 return mVisibleType;
893 }
894
895 public int getBackgroundColorForExpansionState() {
896 // When expanding or user locked we want the new type, when collapsing we want
897 // the original type
898 final int visibleType = (mContainingNotification.isGroupExpanded()
899 || mContainingNotification.isUserLocked())
900 ? calculateVisibleType()
901 : getVisibleType();
902 return getBackgroundColor(visibleType);
903 }
904
905 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800906 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
907 int customBackgroundColor = 0;
908 if (currentVisibleWrapper != null) {
909 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
910 }
911 return customBackgroundColor;
912 }
913
Selim Cinek684a4422015-04-15 16:18:39 -0700914 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800915 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
916 mContractedChild, mContractedWrapper);
917 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
918 mExpandedChild, mExpandedWrapper);
919 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
920 mHeadsUpChild, mHeadsUpWrapper);
921 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
922 mSingleLineView, mSingleLineView);
923 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
924 mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700925 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
926 mAmbientSingleLineChild, mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800927 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800928 // updateViewVisibilities cancels outstanding animations without updating the
929 // mAnimationStartVisibleType. Do so here instead.
930 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800931 }
932
933 private void updateViewVisibility(int visibleType, int type, View view,
934 TransformableView wrapper) {
935 if (view != null) {
936 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -0700937 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700938 }
939
Selim Cinek4ffd6362015-12-29 15:12:23 +0100940 private void animateToVisibleType(int visibleType) {
941 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
942 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700943 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800944 shownView.setVisible(true);
945 return;
946 }
Adrian Roos599be342016-06-13 14:54:39 -0700947 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100948 shownView.transformFrom(hiddenView);
949 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
950 hiddenView.transformTo(shownView, new Runnable() {
951 @Override
952 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800953 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
954 hiddenView.setVisible(false);
955 }
Adrian Roos599be342016-06-13 14:54:39 -0700956 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100957 }
958 });
Adrian Roos4320e892017-01-24 12:50:34 -0800959 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +0200960 }
961
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700962 private void transferRemoteInputFocus(int visibleType) {
963 if (visibleType == VISIBLE_TYPE_HEADSUP
964 && mHeadsUpRemoteInput != null
965 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
966 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
967 }
968 if (visibleType == VISIBLE_TYPE_EXPANDED
969 && mExpandedRemoteInput != null
970 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
971 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
972 }
973 }
974
Selim Cinek684a4422015-04-15 16:18:39 -0700975 /**
976 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100977 * @return the corresponding transformable view according to the given visible type
978 */
979 private TransformableView getTransformableViewForVisibleType(int visibleType) {
980 switch (visibleType) {
981 case VISIBLE_TYPE_EXPANDED:
982 return mExpandedWrapper;
983 case VISIBLE_TYPE_HEADSUP:
984 return mHeadsUpWrapper;
985 case VISIBLE_TYPE_SINGLELINE:
986 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800987 case VISIBLE_TYPE_AMBIENT:
988 return mAmbientWrapper;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700989 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
990 return mAmbientSingleLineChild;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100991 default:
992 return mContractedWrapper;
993 }
994 }
995
996 /**
997 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -0700998 * @return the corresponding view according to the given visible type
999 */
1000 private View getViewForVisibleType(int visibleType) {
1001 switch (visibleType) {
1002 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -07001003 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001004 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -07001005 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -07001006 case VISIBLE_TYPE_SINGLELINE:
1007 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001008 case VISIBLE_TYPE_AMBIENT:
1009 return mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001010 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1011 return mAmbientSingleLineChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001012 default:
1013 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -07001014 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001015 }
1016
Selim Cinek131f1a42017-06-05 17:50:19 -07001017 public NotificationViewWrapper getVisibleWrapper(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -08001018 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -08001019 case VISIBLE_TYPE_EXPANDED:
1020 return mExpandedWrapper;
1021 case VISIBLE_TYPE_HEADSUP:
1022 return mHeadsUpWrapper;
1023 case VISIBLE_TYPE_CONTRACTED:
1024 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001025 case VISIBLE_TYPE_AMBIENT:
1026 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -08001027 default:
1028 return null;
1029 }
1030 }
1031
Selim Cinek684a4422015-04-15 16:18:39 -07001032 /**
1033 * @return one of the static enum types in this view, calculated form the current state
1034 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001035 public int calculateVisibleType() {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001036 if (mContainingNotification.isShowingAmbient()) {
1037 if (mIsChildInGroup && mAmbientSingleLineChild != null) {
1038 return VISIBLE_TYPE_AMBIENT_SINGLELINE;
1039 } else if (mAmbientChild != null) {
1040 return VISIBLE_TYPE_AMBIENT;
1041 } else {
1042 return VISIBLE_TYPE_CONTRACTED;
1043 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001044 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001045 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -08001046 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001047 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001048 ? mContainingNotification.getMaxContentHeight()
1049 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -08001050 if (height == 0) {
1051 height = mContentHeight;
1052 }
Selim Cinek42357e02016-02-24 18:48:01 -08001053 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -08001054 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -07001055 ? VISIBLE_TYPE_SINGLELINE
1056 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001057 return mTransformationStartVisibleType == collapsedVisualType
1058 ? expandedVisualType
1059 : collapsedVisualType;
1060 }
Selim Cinek51d94912016-03-02 15:34:28 -08001061 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
1062 int viewHeight = mContentHeight;
1063 if (intrinsicHeight != 0) {
1064 // the intrinsicHeight might be 0 because it was just reset.
1065 viewHeight = Math.min(mContentHeight, intrinsicHeight);
1066 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001067 return getVisualTypeForHeight(viewHeight);
1068 }
1069
1070 private int getVisualTypeForHeight(float viewHeight) {
1071 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001072 if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
Adrian Roos777ef562015-12-01 17:37:14 -08001073 return VISIBLE_TYPE_EXPANDED;
1074 }
Selim Cinek414ad332017-02-24 19:06:12 -08001075 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001076 return VISIBLE_TYPE_SINGLELINE;
1077 }
Adrian Roos777ef562015-12-01 17:37:14 -08001078
Selim Cinekc494e382017-01-31 16:09:23 -08001079 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
1080 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001081 if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -07001082 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -07001083 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001084 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001085 }
1086 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001087 if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
Selim Cinek6ac3fa82016-05-10 17:11:54 -04001088 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001089 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -07001090 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001091 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001092 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001093 }
1094 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001095 }
1096
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001097 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001098 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001099 }
John Spurlocke15452b2014-08-21 09:44:39 -04001100
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001101 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -08001102 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -08001103 return;
1104 }
John Spurlocke15452b2014-08-21 09:44:39 -04001105 mDark = dark;
Selim Cinek19ba7052016-01-27 20:04:27 -08001106 if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
Selim Cinek30e387d2016-01-11 18:01:47 -08001107 mContractedWrapper.setDark(dark, fade, delay);
1108 }
Selim Cinek19ba7052016-01-27 20:04:27 -08001109 if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -08001110 mExpandedWrapper.setDark(dark, fade, delay);
1111 }
Selim Cinek19ba7052016-01-27 20:04:27 -08001112 if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -08001113 mHeadsUpWrapper.setDark(dark, fade, delay);
1114 }
Selim Cinek19ba7052016-01-27 20:04:27 -08001115 if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
Selim Cinek9c7712d2015-12-08 19:19:48 -08001116 mSingleLineView.setDark(dark, fade, delay);
1117 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001118 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -04001119 }
1120
Selim Cinek8d490d42015-04-10 00:05:50 -07001121 public void setHeadsUp(boolean headsUp) {
1122 mIsHeadsUp = headsUp;
1123 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -08001124 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -07001125 }
1126
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +02001127 @Override
1128 public boolean hasOverlappingRendering() {
1129
1130 // This is not really true, but good enough when fading from the contracted to the expanded
1131 // layout, and saves us some layers.
1132 return false;
1133 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001134
Selim Cinek1a48bab2017-02-17 19:38:40 -08001135 public void setLegacy(boolean legacy) {
1136 mLegacy = legacy;
1137 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -08001138 }
1139
Selim Cinek1a48bab2017-02-17 19:38:40 -08001140 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -08001141 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001142 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001143 }
1144 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001145 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001146 }
1147 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001148 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001149 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001150 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001151
Selim Cinek83bc7832015-10-22 13:26:54 -07001152 public void setIsChildInGroup(boolean isChildInGroup) {
1153 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001154 if (mContractedChild != null) {
1155 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1156 }
1157 if (mExpandedChild != null) {
1158 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1159 }
1160 if (mHeadsUpChild != null) {
1161 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1162 }
1163 if (mAmbientChild != null) {
1164 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1165 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001166 updateAllSingleLineViews();
Selim Cinek83bc7832015-10-22 13:26:54 -07001167 }
1168
Adrian Roosb88b1a12015-12-09 18:51:05 -08001169 public void onNotificationUpdated(NotificationData.Entry entry) {
1170 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -08001171 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001172 updateAllSingleLineViews();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001173 if (mContractedChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001174 mContractedWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001175 }
1176 if (mExpandedChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001177 mExpandedWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001178 }
1179 if (mHeadsUpChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001180 mHeadsUpWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001181 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001182 if (mAmbientChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001183 mAmbientWrapper.onContentUpdated(entry.row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001184 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001185 applyRemoteInputAndSmartReply(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001186 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001187 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001188 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001189 mPreviousExpandedRemoteInputIntent = null;
1190 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001191 }
1192
Adrian Roos6f6e1592017-05-02 16:22:53 -07001193 private void updateAllSingleLineViews() {
1194 updateSingleLineView();
1195 updateAmbientSingleLineView();
1196 }
Selim Cinek83bc7832015-10-22 13:26:54 -07001197 private void updateSingleLineView() {
1198 if (mIsChildInGroup) {
Selim Cinekc897bd32016-03-18 17:32:31 -07001199 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001200 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekde33a4a2016-02-11 16:43:41 -08001201 } else if (mSingleLineView != null) {
1202 removeView(mSingleLineView);
1203 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001204 }
1205 }
1206
Adrian Roos6f6e1592017-05-02 16:22:53 -07001207 private void updateAmbientSingleLineView() {
1208 if (mIsChildInGroup) {
1209 mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
1210 mAmbientSingleLineChild, mStatusBarNotification.getNotification());
1211 } else if (mAmbientSingleLineChild != null) {
1212 removeView(mAmbientSingleLineChild);
1213 mAmbientSingleLineChild = null;
1214 }
1215 }
1216
Petr Cermak7997d7a2018-01-18 15:52:10 +00001217 private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001218 if (mRemoteInputController == null) {
1219 return;
1220 }
1221
Richard Ledley4069f7a2018-02-26 10:36:00 +00001222 boolean enableSmartReplies = (mSmartReplyConstants.isEnabled()
1223 && (!mSmartReplyConstants.requiresTargetingP()
1224 || entry.targetSdk >= Build.VERSION_CODES.P));
Petr Cermak7997d7a2018-01-18 15:52:10 +00001225
Adrian Roosb88b1a12015-12-09 18:51:05 -08001226 boolean hasRemoteInput = false;
Petr Cermak7997d7a2018-01-18 15:52:10 +00001227 RemoteInput remoteInputWithChoices = null;
1228 PendingIntent pendingIntentWithChoices = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001229
1230 Notification.Action[] actions = entry.notification.getNotification().actions;
1231 if (actions != null) {
1232 for (Notification.Action a : actions) {
1233 if (a.getRemoteInputs() != null) {
1234 for (RemoteInput ri : a.getRemoteInputs()) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001235 boolean showRemoteInputView = ri.getAllowFreeFormInput();
1236 boolean showSmartReplyView = enableSmartReplies && ri.getChoices() != null
1237 && ri.getChoices().length > 0;
1238 if (showRemoteInputView) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001239 hasRemoteInput = true;
Petr Cermak7997d7a2018-01-18 15:52:10 +00001240 }
1241 if (showSmartReplyView) {
1242 remoteInputWithChoices = ri;
1243 pendingIntentWithChoices = a.actionIntent;
1244 }
1245 if (showRemoteInputView || showSmartReplyView) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001246 break;
1247 }
1248 }
1249 }
1250 }
1251 }
1252
Petr Cermak7997d7a2018-01-18 15:52:10 +00001253 applyRemoteInput(entry, hasRemoteInput);
Kenny Guy23991102018-04-05 21:18:38 +01001254 applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001255 }
1256
1257 private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001258 View bigContentView = mExpandedChild;
1259 if (bigContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001260 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001261 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1262 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001263 } else {
1264 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001265 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001266 if (mCachedExpandedRemoteInput != null
1267 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1268 // We had a cached remote input but didn't reuse it. Clean up required.
1269 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1270 }
1271 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001272
Adrian Roosb88b1a12015-12-09 18:51:05 -08001273 View headsUpContentView = mHeadsUpChild;
1274 if (headsUpContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001275 mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001276 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001277 } else {
1278 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001279 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001280 if (mCachedHeadsUpRemoteInput != null
1281 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1282 // We had a cached remote input but didn't reuse it. Clean up required.
1283 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1284 }
1285 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001286 }
1287
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001288 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001289 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001290 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001291 View actionContainerCandidate = view.findViewById(
1292 com.android.internal.R.id.actions_container);
1293 if (actionContainerCandidate instanceof FrameLayout) {
1294 RemoteInputView existing = (RemoteInputView)
1295 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1296
1297 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001298 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001299 }
1300
1301 if (existing == null && hasRemoteInput) {
1302 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001303 if (cachedView == null) {
1304 RemoteInputView riv = RemoteInputView.inflate(
1305 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001306
Adrian Roos7813dd72016-09-23 17:12:17 -07001307 riv.setVisibility(View.INVISIBLE);
1308 actionContainer.addView(riv, new LayoutParams(
1309 ViewGroup.LayoutParams.MATCH_PARENT,
1310 ViewGroup.LayoutParams.MATCH_PARENT)
1311 );
1312 existing = riv;
1313 } else {
1314 actionContainer.addView(cachedView);
1315 cachedView.dispatchFinishTemporaryDetach();
1316 cachedView.requestFocus();
1317 existing = cachedView;
1318 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001319 }
1320 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001321 int color = entry.notification.getNotification().color;
1322 if (color == Notification.COLOR_DEFAULT) {
1323 color = mContext.getColor(R.color.default_remote_input_background);
1324 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001325 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001326 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001327 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001328
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001329 existing.setWrapper(wrapper);
Selim Cineked64a142018-02-06 18:06:01 -08001330 existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001331
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001332 if (existingPendingIntent != null || existing.isActive()) {
1333 // The current action could be gone, or the pending intent no longer valid.
1334 // If we find a matching action in the new notification, focus, otherwise close.
1335 Notification.Action[] actions = entry.notification.getNotification().actions;
1336 if (existingPendingIntent != null) {
1337 existing.setPendingIntent(existingPendingIntent);
1338 }
1339 if (existing.updatePendingIntentFromActions(actions)) {
1340 if (!existing.isActive()) {
1341 existing.focus();
1342 }
1343 } else {
1344 if (existing.isActive()) {
1345 existing.close();
1346 }
1347 }
1348 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001349 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001350 return existing;
1351 }
1352 return null;
1353 }
1354
Kenny Guy23991102018-04-05 21:18:38 +01001355 private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
1356 NotificationData.Entry entry) {
1357 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +01001358 mExpandedSmartReplyView =
Kenny Guy23991102018-04-05 21:18:38 +01001359 applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry);
Milo Sredkove7cf4982018-04-09 15:08:26 +01001360 if (mExpandedSmartReplyView != null && remoteInput != null
1361 && remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
Kenny Guy23991102018-04-05 21:18:38 +01001362 mSmartReplyLogger.smartRepliesAdded(entry, remoteInput.getChoices().length);
1363 }
1364 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001365 }
1366
1367 private SmartReplyView applySmartReplyView(
Kenny Guy23991102018-04-05 21:18:38 +01001368 View view, RemoteInput remoteInput, PendingIntent pendingIntent,
1369 NotificationData.Entry entry) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001370 View smartReplyContainerCandidate = view.findViewById(
1371 com.android.internal.R.id.smart_reply_container);
1372 if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
1373 return null;
1374 }
1375 LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
1376 if (remoteInput == null || pendingIntent == null) {
1377 smartReplyContainer.setVisibility(View.GONE);
1378 return null;
1379 }
1380 SmartReplyView smartReplyView = null;
1381 if (smartReplyContainer.getChildCount() == 0) {
1382 smartReplyView = SmartReplyView.inflate(mContext, smartReplyContainer);
1383 smartReplyContainer.addView(smartReplyView);
1384 } else if (smartReplyContainer.getChildCount() == 1) {
1385 View child = smartReplyContainer.getChildAt(0);
1386 if (child instanceof SmartReplyView) {
1387 smartReplyView = (SmartReplyView) child;
1388 }
1389 }
1390 if (smartReplyView != null) {
Kenny Guy23991102018-04-05 21:18:38 +01001391 smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
1392 mSmartReplyLogger, entry);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001393 smartReplyContainer.setVisibility(View.VISIBLE);
1394 }
1395 return smartReplyView;
1396 }
1397
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001398 public void closeRemoteInput() {
1399 if (mHeadsUpRemoteInput != null) {
1400 mHeadsUpRemoteInput.close();
1401 }
1402 if (mExpandedRemoteInput != null) {
1403 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001404 }
1405 }
1406
Selim Cinek83bc7832015-10-22 13:26:54 -07001407 public void setGroupManager(NotificationGroupManager groupManager) {
1408 mGroupManager = groupManager;
1409 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001410
Adrian Roosb88b1a12015-12-09 18:51:05 -08001411 public void setRemoteInputController(RemoteInputController r) {
1412 mRemoteInputController = r;
1413 }
1414
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001415 public void setExpandClickListener(OnClickListener expandClickListener) {
1416 mExpandClickListener = expandClickListener;
1417 }
1418
1419 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001420 mExpandable = expandable;
1421 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001422 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001423 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1424 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001425 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001426 expandable = false;
1427 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001428 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001429 expandable = false;
1430 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001431 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001432 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001433 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001434 }
1435 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001436 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001437 }
1438 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001439 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001440 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001441 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001442 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001443
1444 public NotificationHeaderView getNotificationHeader() {
1445 NotificationHeaderView header = null;
1446 if (mContractedChild != null) {
1447 header = mContractedWrapper.getNotificationHeader();
1448 }
1449 if (header == null && mExpandedChild != null) {
1450 header = mExpandedWrapper.getNotificationHeader();
1451 }
1452 if (header == null && mHeadsUpChild != null) {
1453 header = mHeadsUpWrapper.getNotificationHeader();
1454 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001455 if (header == null && mAmbientChild != null) {
1456 header = mAmbientWrapper.getNotificationHeader();
1457 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001458 return header;
1459 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001460
Julia Reynoldsfc640012018-02-21 12:25:27 -05001461 public void showAppOpsIcons(ArraySet<Integer> activeOps) {
1462 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
1463 mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1464 }
1465 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
1466 mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1467 }
1468 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
1469 mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1470 }
1471 }
Selim Cinekb26afa32017-06-29 10:28:17 +02001472
1473 public NotificationHeaderView getContractedNotificationHeader() {
1474 if (mContractedChild != null) {
1475 return mContractedWrapper.getNotificationHeader();
1476 }
1477 return null;
1478 }
1479
Selim Cinek34eda5e2016-02-18 17:10:43 -08001480 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001481 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001482 return wrapper == null ? null : wrapper.getNotificationHeader();
1483 }
1484
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001485 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1486 mContainingNotification = containingNotification;
1487 }
1488
1489 public void requestSelectLayout(boolean needsAnimation) {
1490 selectLayout(needsAnimation, false);
1491 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001492
1493 public void reInflateViews() {
1494 if (mIsChildInGroup && mSingleLineView != null) {
1495 removeView(mSingleLineView);
1496 mSingleLineView = null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001497 updateAllSingleLineViews();
Selim Cinekde33a4a2016-02-11 16:43:41 -08001498 }
1499 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001500
1501 public void setUserExpanding(boolean userExpanding) {
1502 mUserExpanding = userExpanding;
1503 if (userExpanding) {
1504 mTransformationStartVisibleType = mVisibleType;
1505 } else {
1506 mTransformationStartVisibleType = UNDEFINED;
1507 mVisibleType = calculateVisibleType();
1508 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001509 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001510 }
1511 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001512
1513 /**
1514 * Set by how much the single line view should be indented. Used when a overflow indicator is
1515 * present and only during measuring
1516 */
1517 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1518 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1519 mSingleLineWidthIndention = singleLineWidthIndention;
1520 mContainingNotification.forceLayout();
1521 forceLayout();
1522 }
1523 }
1524
1525 public HybridNotificationView getSingleLineView() {
1526 return mSingleLineView;
1527 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001528
1529 public void setRemoved() {
1530 if (mExpandedRemoteInput != null) {
1531 mExpandedRemoteInput.setRemoved();
1532 }
1533 if (mHeadsUpRemoteInput != null) {
1534 mHeadsUpRemoteInput.setRemoved();
1535 }
1536 }
Adrian Roos599be342016-06-13 14:54:39 -07001537
1538 public void setContentHeightAnimating(boolean animating) {
1539 if (!animating) {
1540 mContentHeightAtAnimationStart = UNDEFINED;
1541 }
1542 }
Selim Cineke9bad242016-06-15 11:46:37 -07001543
Adrian Roose18033c2017-01-17 15:22:49 -08001544 @VisibleForTesting
1545 boolean isAnimatingVisibleType() {
1546 return mAnimationStartVisibleType != UNDEFINED;
1547 }
1548
Selim Cinekcafa87f2016-10-26 17:00:17 -07001549 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1550 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001551 selectLayout(false /* animate */, true /* force */);
1552 }
1553
Selim Cineke9bad242016-06-15 11:46:37 -07001554 public void setFocusOnVisibilityChange() {
1555 mFocusOnVisibilityChange = true;
1556 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001557
1558 public void setIconsVisible(boolean iconsVisible) {
1559 mIconsVisible = iconsVisible;
1560 updateIconVisibilities();
1561 }
1562
1563 private void updateIconVisibilities() {
1564 if (mContractedWrapper != null) {
1565 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1566 if (header != null) {
1567 header.getIcon().setForceHidden(!mIconsVisible);
1568 }
1569 }
1570 if (mHeadsUpWrapper != null) {
1571 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1572 if (header != null) {
1573 header.getIcon().setForceHidden(!mIconsVisible);
1574 }
1575 }
1576 if (mExpandedWrapper != null) {
1577 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1578 if (header != null) {
1579 header.getIcon().setForceHidden(!mIconsVisible);
1580 }
1581 }
1582 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001583
Adrian Roos4320e892017-01-24 12:50:34 -08001584 @Override
1585 public void onVisibilityAggregated(boolean isVisible) {
1586 super.onVisibilityAggregated(isVisible);
1587 if (isVisible) {
1588 fireExpandedVisibleListenerIfVisible();
1589 }
1590 }
1591
1592 /**
1593 * Sets a one-shot listener for when the expanded view becomes visible.
1594 *
1595 * This will fire the listener immediately if the expanded view is already visible.
1596 */
1597 public void setOnExpandedVisibleListener(Runnable r) {
1598 mExpandedVisibleListener = r;
1599 fireExpandedVisibleListenerIfVisible();
1600 }
1601
Selim Cinek6743c0b2017-01-18 18:24:01 -08001602 public void setIsLowPriority(boolean isLowPriority) {
1603 mIsLowPriority = isLowPriority;
1604 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001605
1606 public boolean isDimmable() {
1607 if (!mContractedWrapper.isDimmable()) {
1608 return false;
1609 }
1610 return true;
1611 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001612
1613 /**
1614 * Should a single click be disallowed on this view when on the keyguard?
1615 */
1616 public boolean disallowSingleClick(float x, float y) {
1617 NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
1618 if (visibleWrapper != null) {
1619 return visibleWrapper.disallowSingleClick(x, y);
1620 }
1621 return false;
1622 }
Selim Cinek515b2032017-11-15 10:20:19 -08001623
Selim Cinek86bfcee2018-01-17 11:00:47 -08001624 public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
1625 boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001626 if (mUserExpanding) {
Selim Cinek86bfcee2018-01-17 11:00:47 -08001627 needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
1628 bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001629 }
1630 return needsPaddings;
1631 }
1632
Selim Cinek86bfcee2018-01-17 11:00:47 -08001633 private boolean shouldClipToRounding(int visibleType, boolean topRounded,
1634 boolean bottomRounded) {
Selim Cinek515b2032017-11-15 10:20:19 -08001635 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
1636 if (visibleWrapper == null) {
1637 return false;
1638 }
Selim Cinek86bfcee2018-01-17 11:00:47 -08001639 return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001640 }
Selim Cinekde4de0e2018-01-24 16:21:07 -08001641
1642 public CharSequence getActiveRemoteInputText() {
1643 if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
1644 return mExpandedRemoteInput.getText();
1645 }
1646 if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
1647 return mHeadsUpRemoteInput.getText();
1648 }
1649 return null;
1650 }
Selim Cineked64a142018-02-06 18:06:01 -08001651
Selim Cineka77d2c52018-04-09 10:44:52 -07001652 @Override
1653 public boolean dispatchTouchEvent(MotionEvent ev) {
1654 float y = ev.getY();
1655 // We still want to distribute touch events to the remote input even if it's outside the
1656 // view boundary. We're therefore manually dispatching these events to the remote view
1657 RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
1658 if (riv != null && riv.getVisibility() == VISIBLE) {
1659 int inputStart = mUnrestrictedContentHeight - riv.getHeight();
1660 if (y <= mUnrestrictedContentHeight && y >= inputStart) {
1661 ev.offsetLocation(0, -inputStart);
1662 return riv.dispatchTouchEvent(ev);
1663 }
1664 }
1665 return super.dispatchTouchEvent(ev);
1666 }
1667
1668 /**
1669 * Overridden to make sure touches to the reply action bar actually go through to this view
1670 */
1671 @Override
1672 public boolean pointInView(float localX, float localY, float slop) {
1673 float top = mClipTopAmount;
1674 float bottom = mUnrestrictedContentHeight;
1675 return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
1676 localY < (bottom + slop);
1677 }
1678
1679 private RemoteInputView getRemoteInputForView(View child) {
1680 if (child == mExpandedChild) {
1681 return mExpandedRemoteInput;
1682 } else if (child == mHeadsUpChild) {
1683 return mHeadsUpRemoteInput;
1684 }
1685 return null;
1686 }
1687
Selim Cineked64a142018-02-06 18:06:01 -08001688 public int getExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001689 int viewType = VISIBLE_TYPE_EXPANDED;
1690 if (mExpandedChild == null) {
1691 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001692 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001693 return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001694 }
1695
1696 public int getHeadsUpHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001697 int viewType = VISIBLE_TYPE_HEADSUP;
1698 if (mHeadsUpChild == null) {
1699 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001700 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001701 return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001702 }
1703
1704 public void setRemoteInputVisible(boolean remoteInputVisible) {
1705 mRemoteInputVisible = remoteInputVisible;
1706 setClipChildren(!remoteInputVisible);
1707 }
1708
1709 @Override
1710 public void setClipChildren(boolean clipChildren) {
1711 clipChildren = clipChildren && !mRemoteInputVisible;
1712 super.setClipChildren(clipChildren);
1713 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001714
1715 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1716 if (mContractedWrapper != null) {
1717 mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1718 }
1719 if (mHeadsUpWrapper != null) {
1720 mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1721 }
1722 if (mExpandedWrapper != null) {
1723 mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1724 }
1725 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001726}