blob: da1fd3b83452d5fe55e77d65a6de88ecaa2258a1 [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
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.row;
Jorim Jaggibe565df2014-04-28 17:51:23 +020018
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;
Lucas Dupina291d192018-06-07 13:59:42 -070039import com.android.internal.util.ContrastColorUtil;
Petr Cermak10011fa2018-02-05 19:00:54 +000040import com.android.systemui.Dependency;
Jorim Jaggibe565df2014-04-28 17:51:23 +020041import com.android.systemui.R;
Rohan Shah20790b82018-07-02 17:21:04 -070042import com.android.systemui.statusbar.notification.NotificationData;
43import com.android.systemui.statusbar.RemoteInputController;
44import com.android.systemui.statusbar.SmartReplyController;
45import com.android.systemui.statusbar.TransformableView;
46import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
Selim Cinekc3179332016-03-04 14:44:56 -080047import com.android.systemui.statusbar.notification.NotificationUtils;
Rohan Shah20790b82018-07-02 17:21:04 -070048import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070049import com.android.systemui.statusbar.phone.NotificationGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080050import com.android.systemui.statusbar.policy.RemoteInputView;
Petr Cermak10011fa2018-02-05 19:00:54 +000051import com.android.systemui.statusbar.policy.SmartReplyConstants;
Petr Cermak7997d7a2018-01-18 15:52:10 +000052import com.android.systemui.statusbar.policy.SmartReplyView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020053
54/**
Selim Cinek684a4422015-04-15 16:18:39 -070055 * A frame layout containing the actual payload of the notification, including the contracted,
56 * expanded and heads up layout. This class is responsible for clipping the content and and
57 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020058 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020059public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020060
Selim Cinek011bde92017-11-22 05:53:38 -080061 private static final String TAG = "NotificationContentView";
Selim Cinek131f1a42017-06-05 17:50:19 -070062 public static final int VISIBLE_TYPE_CONTRACTED = 0;
63 public static final int VISIBLE_TYPE_EXPANDED = 1;
64 public static final int VISIBLE_TYPE_HEADSUP = 2;
Selim Cinek83bc7832015-10-22 13:26:54 -070065 private static final int VISIBLE_TYPE_SINGLELINE = 3;
Selim Cinek131f1a42017-06-05 17:50:19 -070066 public static final int VISIBLE_TYPE_AMBIENT = 4;
Adrian Roos6f6e1592017-05-02 16:22:53 -070067 private static final int VISIBLE_TYPE_AMBIENT_SINGLELINE = 5;
Selim Cinekc3179332016-03-04 14:44:56 -080068 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020069
Jorim Jaggibe565df2014-04-28 17:51:23 +020070 private final Rect mClipBounds = new Rect();
71
Selim Cinek9b49f6d2017-11-13 18:19:35 -080072 private int mMinContractedHeight;
73 private int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020074 private View mContractedChild;
75 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070076 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070077 private HybridNotificationView mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -080078 private View mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -070079 private HybridNotificationView mAmbientSingleLineChild;
Jorim Jaggibe565df2014-04-28 17:51:23 +020080
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070081 private RemoteInputView mExpandedRemoteInput;
82 private RemoteInputView mHeadsUpRemoteInput;
Petr Cermak10011fa2018-02-05 19:00:54 +000083
84 private SmartReplyConstants mSmartReplyConstants;
Milo Sredkove7cf4982018-04-09 15:08:26 +010085 private SmartReplyView mExpandedSmartReplyView;
Kenny Guya0f6de82018-04-06 16:20:16 +010086 private SmartReplyController mSmartReplyController;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070087
Jorim Jaggi4e857f42014-11-17 19:14:04 +010088 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070089 private NotificationViewWrapper mExpandedWrapper;
90 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -080091 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -070092 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020093 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +010094 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -070095 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -040096 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010097 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -070098 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -080099 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -0700100 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -0800101 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800102 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800103 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800104 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700105 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700106 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800107 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -0800108 private Runnable mExpandedVisibleListener;
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700109
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700110 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100111 = new ViewTreeObserver.OnPreDrawListener() {
112 @Override
113 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -0800114 // We need to post since we don't want the notification to animate on the very first
115 // frame
116 post(new Runnable() {
117 @Override
118 public void run() {
119 mAnimate = true;
120 }
121 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100122 getViewTreeObserver().removeOnPreDrawListener(this);
123 return true;
124 }
125 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200126
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800127 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800128 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800129 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100130 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800131 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700132 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800133 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700134 /** The visible type at the start of an animation driven transformation */
135 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800136 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700137 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700138 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700139 private PendingIntent mPreviousExpandedRemoteInputIntent;
140 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700141 private RemoteInputView mCachedExpandedRemoteInput;
142 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800143
Adrian Roos599be342016-06-13 14:54:39 -0700144 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700145 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700146 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700147 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700148 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800149 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800150 private boolean mIsContentExpandable;
Selim Cineked64a142018-02-06 18:06:01 -0800151 private boolean mRemoteInputVisible;
152 private int mUnrestrictedContentHeight;
Adrian Roos599be342016-06-13 14:54:39 -0700153
154
Jorim Jaggibe565df2014-04-28 17:51:23 +0200155 public NotificationContentView(Context context, AttributeSet attrs) {
156 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700157 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Petr Cermak10011fa2018-02-05 19:00:54 +0000158 mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
Kenny Guya0f6de82018-04-06 16:20:16 +0100159 mSmartReplyController = Dependency.get(SmartReplyController.class);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800160 initView();
161 }
162
163 public void initView() {
Selim Cinek860b6da2015-12-16 19:02:19 -0800164 mMinContractedHeight = getResources().getDimensionPixelSize(
165 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800166 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
167 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200168 }
169
Adrian Roos0aac04f2016-12-08 15:59:29 -0800170 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
171 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800172 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800173 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800174 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800175 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800176 }
177
Jorim Jaggibe565df2014-04-28 17:51:23 +0200178 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700179 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
180 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
181 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
182 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700183 int maxSize = Integer.MAX_VALUE / 2;
Selim Cinekc897bd32016-03-18 17:32:31 -0700184 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700185 if (hasFixedHeight || isHeightLimited) {
186 maxSize = MeasureSpec.getSize(heightMeasureSpec);
187 }
188 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800189 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +0100190 int notificationMaxHeight = mNotificationMaxHeight;
191 if (mExpandedSmartReplyView != null) {
192 notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
193 }
Selim Cinekbee4e072018-05-21 22:06:43 -0700194 notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700195 int size = notificationMaxHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800196 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800197 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800198 if (layoutParams.height >= 0) {
199 // An actual height is set
Selim Cinekbe570ce2018-05-11 12:29:52 -0700200 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800201 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800202 }
Selim Cinekbe570ce2018-05-11 12:29:52 -0700203 int spec = MeasureSpec.makeMeasureSpec(size, useExactly
Selim Cinek6743c0b2017-01-18 18:24:01 -0800204 ? MeasureSpec.EXACTLY
205 : MeasureSpec.AT_MOST);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800206 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800207 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
208 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700209 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800210 int heightSpec;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700211 int size = mSmallHeight;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800212 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
213 boolean useExactly = false;
214 if (layoutParams.height >= 0) {
215 // An actual height is set
216 size = Math.min(size, layoutParams.height);
217 useExactly = true;
218 }
219 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800220 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
221 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800222 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800223 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800224 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800225 int measuredHeight = mContractedChild.getMeasuredHeight();
226 if (measuredHeight < mMinContractedHeight) {
227 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800228 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800229 }
230 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800231 if (updateContractedHeaderWidth()) {
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800232 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700233 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700234 if (mExpandedChild != null
235 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
236 // the Expanded child is smaller then the collapsed. Let's remeasure it.
237 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
238 MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800239 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek1ba41d12016-04-21 16:14:46 -0700240 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700241 }
242 if (mHeadsUpChild != null) {
Selim Cinek396caca2018-04-10 17:46:46 -0700243 int maxHeight = mHeadsUpHeight;
244 maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700245 int size = maxHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700246 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800247 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700248 if (layoutParams.height >= 0) {
249 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800250 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800251 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700252 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800253 measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800254 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800255 : MeasureSpec.AT_MOST), 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700256 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
257 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700258 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700259 int singleLineWidthSpec = widthMeasureSpec;
260 if (mSingleLineWidthIndention != 0
261 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
262 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
263 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700264 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700265 }
266 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinekbe570ce2018-05-11 12:29:52 -0700267 MeasureSpec.makeMeasureSpec(mNotificationMaxHeight, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700268 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
269 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800270 if (mAmbientChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700271 int size = mNotificationAmbientHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800272 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800273 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800274 if (layoutParams.height >= 0) {
275 // An actual height is set
276 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800277 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800278 }
279 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800280 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
281 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800282 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
283 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700284 if (mAmbientSingleLineChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700285 int size = mNotificationAmbientHeight;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700286 ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
287 boolean useExactly = false;
288 if (layoutParams.height >= 0) {
289 // An actual height is set
290 size = Math.min(size, layoutParams.height);
291 useExactly = true;
292 }
293 int ambientSingleLineWidthSpec = widthMeasureSpec;
294 if (mSingleLineWidthIndention != 0
295 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
296 ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
297 width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
298 MeasureSpec.EXACTLY);
299 }
300 mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
301 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
302 : MeasureSpec.AT_MOST));
303 maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
304 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700305 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700306 setMeasuredDimension(width, ownHeight);
307 }
308
Selim Cineked64a142018-02-06 18:06:01 -0800309 /**
310 * Get the extra height that needs to be added to the notification height for a given
311 * {@link RemoteInputView}.
312 * This is needed when the user is inline replying in order to ensure that the reply bar has
313 * enough padding.
314 *
315 * @param remoteInput The remote input to check.
316 * @return The extra height needed.
317 */
318 private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
Selim Cinek09b442e2018-05-04 15:46:18 -0700319 if (remoteInput != null && (remoteInput.isActive() || remoteInput.isSending())) {
Selim Cineked64a142018-02-06 18:06:01 -0800320 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);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800428 }
429
430 private NotificationViewWrapper getWrapperForView(View child) {
431 if (child == mContractedChild) {
432 return mContractedWrapper;
433 }
434 if (child == mExpandedChild) {
435 return mExpandedWrapper;
436 }
437 if (child == mHeadsUpChild) {
438 return mHeadsUpWrapper;
439 }
440 if (child == mAmbientChild) {
441 return mAmbientWrapper;
442 }
443 return null;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200444 }
445
446 public void setExpandedChild(View child) {
447 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800448 mPreviousExpandedRemoteInputIntent = null;
449 if (mExpandedRemoteInput != null) {
450 mExpandedRemoteInput.onNotificationUpdateOrReset();
451 if (mExpandedRemoteInput.isActive()) {
452 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
453 mCachedExpandedRemoteInput = mExpandedRemoteInput;
454 mExpandedRemoteInput.dispatchStartTemporaryDetach();
455 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
456 }
457 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200458 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200459 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800460 mExpandedRemoteInput = null;
461 }
462 if (child == null) {
463 mExpandedChild = null;
464 mExpandedWrapper = null;
465 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
466 mVisibleType = VISIBLE_TYPE_CONTRACTED;
467 }
468 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
469 mTransformationStartVisibleType = UNDEFINED;
470 }
471 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200472 }
473 addView(child);
474 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700475 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
476 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200477 }
478
Selim Cinek8d490d42015-04-10 00:05:50 -0700479 public void setHeadsUpChild(View child) {
480 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800481 mPreviousHeadsUpRemoteInputIntent = null;
482 if (mHeadsUpRemoteInput != null) {
483 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
484 if (mHeadsUpRemoteInput.isActive()) {
485 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
486 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
487 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
488 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
489 }
490 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700491 mHeadsUpChild.animate().cancel();
492 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800493 mHeadsUpRemoteInput = null;
494 }
495 if (child == null) {
496 mHeadsUpChild = null;
497 mHeadsUpWrapper = null;
498 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
499 mVisibleType = VISIBLE_TYPE_CONTRACTED;
500 }
501 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
502 mTransformationStartVisibleType = UNDEFINED;
503 }
504 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700505 }
506 addView(child);
507 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700508 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
509 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700510 }
511
Adrian Roos0aac04f2016-12-08 15:59:29 -0800512 public void setAmbientChild(View child) {
513 if (mAmbientChild != null) {
514 mAmbientChild.animate().cancel();
515 removeView(mAmbientChild);
516 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700517 if (child == null) {
518 return;
519 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800520 addView(child);
521 mAmbientChild = child;
522 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
523 mContainingNotification);
524 }
525
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100526 @Override
527 protected void onVisibilityChanged(View changedView, int visibility) {
528 super.onVisibilityChanged(changedView, visibility);
529 updateVisibility();
530 }
531
532 private void updateVisibility() {
533 setVisible(isShown());
534 }
535
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700536 @Override
537 protected void onDetachedFromWindow() {
538 super.onDetachedFromWindow();
539 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
540 }
541
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100542 private void setVisible(final boolean isVisible) {
543 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700544 // This call can happen multiple times, but removing only removes a single one.
545 // We therefore need to remove the old one.
546 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100547 // We only animate if we are drawn at least once, otherwise the view might animate when
548 // it's shown the first time
549 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
550 } else {
551 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
552 mAnimate = false;
553 }
554 }
555
Selim Cineke9bad242016-06-15 11:46:37 -0700556 private void focusExpandButtonIfNecessary() {
557 if (mFocusOnVisibilityChange) {
558 NotificationHeaderView header = getVisibleNotificationHeader();
559 if (header != null) {
560 ImageView expandButton = header.getExpandButton();
561 if (expandButton != null) {
562 expandButton.requestAccessibilityFocus();
563 }
564 }
565 mFocusOnVisibilityChange = false;
566 }
567 }
568
Selim Cinekb5605e52015-02-20 18:21:41 +0100569 public void setContentHeight(int contentHeight) {
Selim Cineked64a142018-02-06 18:06:01 -0800570 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
571 int maxContentHeight = mContainingNotification.getIntrinsicHeight()
572 - getExtraRemoteInputHeight(mExpandedRemoteInput)
573 - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
574 mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100575 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400576
577 int minHeightHint = getMinContentHeightHint();
578
579 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
580 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800581 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400582 }
583
584 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
585 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800586 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400587 }
588
Jorim Jaggibe565df2014-04-28 17:51:23 +0200589 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700590 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200591 }
592
Adrian Roos181385c2016-05-05 17:45:44 -0400593 /**
594 * @return the minimum apparent height that the wrapper should allow for the purpose
595 * of aligning elements at the bottom edge. If this is larger than the content
596 * height, the notification is clipped instead of being further shrunk.
597 */
598 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700599 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400600 return mContext.getResources().getDimensionPixelSize(
601 com.android.internal.R.dimen.notification_action_list_height);
602 }
Adrian Roos599be342016-06-13 14:54:39 -0700603
604 // Transition between heads-up & expanded, or pinned.
605 if (mHeadsUpChild != null && mExpandedChild != null) {
606 boolean transitioningBetweenHunAndExpanded =
607 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
608 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700609 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800610 && (mIsHeadsUp || mHeadsUpAnimatingAway)
611 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700612 if (transitioningBetweenHunAndExpanded || pinned) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800613 return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
614 getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700615 }
616 }
617
618 // Size change of the expanded version
619 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
620 && mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800621 return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700622 }
623
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700624 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800625 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
626 hint = mAmbientChild.getHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700627 } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
628 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
629 hint = mAmbientSingleLineChild.getHeight();
Adrian Roos05d3d002017-01-26 15:28:07 -0800630 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800631 hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
Adrian Roos599be342016-06-13 14:54:39 -0700632 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800633 hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
Adrian Roos181385c2016-05-05 17:45:44 -0400634 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800635 hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
636 + mContext.getResources().getDimensionPixelSize(
637 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400638 }
Adrian Roos599be342016-06-13 14:54:39 -0700639
640 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800641 hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700642 }
643 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400644 }
645
Adrian Roos599be342016-06-13 14:54:39 -0700646 private boolean isTransitioningFromTo(int from, int to) {
647 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
648 && mVisibleType == to;
649 }
650
651 private boolean isVisibleOrTransitioning(int type) {
652 return mVisibleType == type || mTransformationStartVisibleType == type
653 || mAnimationStartVisibleType == type;
654 }
655
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800656 private void updateContentTransformation() {
657 int visibleType = calculateVisibleType();
658 if (visibleType != mVisibleType) {
659 // A new transformation starts
660 mTransformationStartVisibleType = mVisibleType;
661 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
662 final TransformableView hiddenView = getTransformableViewForVisibleType(
663 mTransformationStartVisibleType);
664 shownView.transformFrom(hiddenView, 0.0f);
665 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
666 hiddenView.transformTo(shownView, 0.0f);
667 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800668 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800669 }
Selim Cineke8578872016-05-03 16:42:50 -0700670 if (mForceSelectNextLayout) {
671 forceUpdateVisibilities();
672 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800673 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700674 && mVisibleType != mTransformationStartVisibleType
675 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800676 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
677 final TransformableView hiddenView = getTransformableViewForVisibleType(
678 mTransformationStartVisibleType);
679 float transformationAmount = calculateTransformationAmount();
680 shownView.transformFrom(hiddenView, transformationAmount);
681 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800682 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800683 } else {
684 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800685 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800686 }
687 }
688
Selim Cinekc3179332016-03-04 14:44:56 -0800689 private void updateBackgroundTransformation(float transformationAmount) {
690 int endColor = getBackgroundColor(mVisibleType);
691 int startColor = getBackgroundColor(mTransformationStartVisibleType);
692 if (endColor != startColor) {
693 if (startColor == 0) {
694 startColor = mContainingNotification.getBackgroundColorWithoutTint();
695 }
696 if (endColor == 0) {
697 endColor = mContainingNotification.getBackgroundColorWithoutTint();
698 }
699 endColor = NotificationUtils.interpolateColors(startColor, endColor,
700 transformationAmount);
701 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400702 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800703 mContainingNotification.setContentBackground(endColor, false, this);
704 }
705
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800706 private float calculateTransformationAmount() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800707 int startHeight = getViewHeight(mTransformationStartVisibleType);
708 int endHeight = getViewHeight(mVisibleType);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800709 int progress = Math.abs(mContentHeight - startHeight);
710 int totalDistance = Math.abs(endHeight - startHeight);
Selim Cinek011bde92017-11-22 05:53:38 -0800711 if (totalDistance == 0) {
712 Log.wtf(TAG, "the total transformation distance is 0"
713 + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
714 + "\n VisibleType: " + mVisibleType + " height: " + endHeight
715 + "\n mContentHeight: " + mContentHeight);
716 return 1.0f;
717 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800718 float amount = (float) progress / (float) totalDistance;
719 return Math.min(1.0f, amount);
720 }
721
Selim Cinekb5605e52015-02-20 18:21:41 +0100722 public int getContentHeight() {
723 return mContentHeight;
724 }
725
Jorim Jaggibe565df2014-04-28 17:51:23 +0200726 public int getMaxHeight() {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700727 if (mContainingNotification.isShowingAmbient()) {
728 return getShowingAmbientView().getHeight();
729 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800730 return getViewHeight(VISIBLE_TYPE_EXPANDED)
731 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cinekc494e382017-01-31 16:09:23 -0800732 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800733 return getViewHeight(VISIBLE_TYPE_HEADSUP)
734 + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
Selim Cinek8d490d42015-04-10 00:05:50 -0700735 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800736 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
737 }
738
739 private int getViewHeight(int visibleType) {
740 View view = getViewForVisibleType(visibleType);
741 int height = view.getHeight();
742 NotificationViewWrapper viewWrapper = getWrapperForView(view);
743 if (viewWrapper != null) {
744 height += viewWrapper.getHeaderTranslation();
745 }
746 return height;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200747 }
748
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200749 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800750 return getMinHeight(false /* likeGroupExpanded */);
751 }
752
753 public int getMinHeight(boolean likeGroupExpanded) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700754 if (mContainingNotification.isShowingAmbient()) {
755 return getShowingAmbientView().getHeight();
756 } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800757 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
Selim Cinek42357e02016-02-24 18:48:01 -0800758 } else {
759 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700760 }
761 }
762
Adrian Roos6f6e1592017-05-02 16:22:53 -0700763 public View getShowingAmbientView() {
764 View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
765 if (v != null) {
766 return v;
767 } else {
768 return mContractedChild;
769 }
770 }
771
Selim Cinek83bc7832015-10-22 13:26:54 -0700772 private boolean isGroupExpanded() {
773 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200774 }
775
Jorim Jaggibe565df2014-04-28 17:51:23 +0200776 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200777 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200778 updateClipping();
779 }
780
Selim Cineka686b2c2016-10-26 13:58:27 -0700781
782 public void setClipBottomAmount(int clipBottomAmount) {
783 mClipBottomAmount = clipBottomAmount;
784 updateClipping();
785 }
786
Selim Cinek875a3a12016-11-18 17:52:16 -0800787 @Override
788 public void setTranslationY(float translationY) {
789 super.setTranslationY(translationY);
790 updateClipping();
791 }
792
Jorim Jaggibe565df2014-04-28 17:51:23 +0200793 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100794 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800795 int top = (int) (mClipTopAmount - getTranslationY());
Selim Cineked64a142018-02-06 18:06:01 -0800796 int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700797 bottom = Math.max(top, bottom);
798 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100799 setClipBounds(mClipBounds);
800 } else {
801 setClipBounds(null);
802 }
803 }
804
805 public void setClipToActualHeight(boolean clipToActualHeight) {
806 mClipToActualHeight = clipToActualHeight;
807 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200808 }
809
Jorim Jaggi11298832014-05-24 16:18:38 +0200810 private void selectLayout(boolean animate, boolean force) {
811 if (mContractedChild == null) {
812 return;
813 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800814 if (mUserExpanding) {
815 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700816 } else {
817 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700818 boolean changedType = visibleType != mVisibleType;
819 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400820 View visibleView = getViewForVisibleType(visibleType);
821 if (visibleView != null) {
822 visibleView.setVisibility(VISIBLE);
823 transferRemoteInputFocus(visibleType);
824 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700825
Selim Cinek589fd3e2016-04-26 18:17:57 -0700826 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
827 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
828 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
829 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
830 animateToVisibleType(visibleType);
831 } else {
832 updateViewVisibilities(visibleType);
833 }
834 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700835 if (changedType) {
836 focusExpandButtonIfNecessary();
837 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800838 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
839 if (visibleWrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800840 visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
841 getMinContentHeightHint());
Adrian Roos05d3d002017-01-26 15:28:07 -0800842 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700843 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200844 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700845 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700846 }
847
848 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800849 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
850 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
851 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
852 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
853 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700854 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
855 mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800856 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800857 // forceUpdateVisibilities cancels outstanding animations without updating the
858 // mAnimationStartVisibleType. Do so here instead.
859 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800860 }
861
Adrian Roos4320e892017-01-24 12:50:34 -0800862 private void fireExpandedVisibleListenerIfVisible() {
863 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
864 && mExpandedChild.getVisibility() == VISIBLE) {
865 Runnable listener = mExpandedVisibleListener;
866 mExpandedVisibleListener = null;
867 listener.run();
868 }
869 }
870
Adrian Roos0aac04f2016-12-08 15:59:29 -0800871 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
872 if (view == null) {
873 return;
874 }
875 boolean visible = mVisibleType == type
876 || mTransformationStartVisibleType == type;
877 if (!visible) {
878 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700879 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800880 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200881 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200882 }
883
Selim Cinekc3179332016-03-04 14:44:56 -0800884 public void updateBackgroundColor(boolean animate) {
885 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400886 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800887 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
888 }
889
Kenny Guy14d035c2018-05-02 19:10:36 +0100890 public void setBackgroundTintColor(int color) {
891 if (mExpandedSmartReplyView != null) {
892 mExpandedSmartReplyView.setBackgroundTintColor(color);
893 }
894 }
895
Mady Mellorb0a82462016-04-30 17:31:02 -0700896 public int getVisibleType() {
897 return mVisibleType;
898 }
899
900 public int getBackgroundColorForExpansionState() {
901 // When expanding or user locked we want the new type, when collapsing we want
902 // the original type
903 final int visibleType = (mContainingNotification.isGroupExpanded()
904 || mContainingNotification.isUserLocked())
905 ? calculateVisibleType()
906 : getVisibleType();
907 return getBackgroundColor(visibleType);
908 }
909
910 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800911 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
912 int customBackgroundColor = 0;
913 if (currentVisibleWrapper != null) {
914 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
915 }
916 return customBackgroundColor;
917 }
918
Selim Cinek684a4422015-04-15 16:18:39 -0700919 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800920 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
921 mContractedChild, mContractedWrapper);
922 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
923 mExpandedChild, mExpandedWrapper);
924 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
925 mHeadsUpChild, mHeadsUpWrapper);
926 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
927 mSingleLineView, mSingleLineView);
928 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
929 mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700930 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
931 mAmbientSingleLineChild, mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800932 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800933 // updateViewVisibilities cancels outstanding animations without updating the
934 // mAnimationStartVisibleType. Do so here instead.
935 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800936 }
937
938 private void updateViewVisibility(int visibleType, int type, View view,
939 TransformableView wrapper) {
940 if (view != null) {
941 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -0700942 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700943 }
944
Selim Cinek4ffd6362015-12-29 15:12:23 +0100945 private void animateToVisibleType(int visibleType) {
946 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
947 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700948 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800949 shownView.setVisible(true);
950 return;
951 }
Adrian Roos599be342016-06-13 14:54:39 -0700952 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100953 shownView.transformFrom(hiddenView);
954 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
955 hiddenView.transformTo(shownView, new Runnable() {
956 @Override
957 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800958 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
959 hiddenView.setVisible(false);
960 }
Adrian Roos599be342016-06-13 14:54:39 -0700961 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100962 }
963 });
Adrian Roos4320e892017-01-24 12:50:34 -0800964 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +0200965 }
966
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700967 private void transferRemoteInputFocus(int visibleType) {
968 if (visibleType == VISIBLE_TYPE_HEADSUP
969 && mHeadsUpRemoteInput != null
970 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
971 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
972 }
973 if (visibleType == VISIBLE_TYPE_EXPANDED
974 && mExpandedRemoteInput != null
975 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
976 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
977 }
978 }
979
Selim Cinek684a4422015-04-15 16:18:39 -0700980 /**
981 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100982 * @return the corresponding transformable view according to the given visible type
983 */
984 private TransformableView getTransformableViewForVisibleType(int visibleType) {
985 switch (visibleType) {
986 case VISIBLE_TYPE_EXPANDED:
987 return mExpandedWrapper;
988 case VISIBLE_TYPE_HEADSUP:
989 return mHeadsUpWrapper;
990 case VISIBLE_TYPE_SINGLELINE:
991 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800992 case VISIBLE_TYPE_AMBIENT:
993 return mAmbientWrapper;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700994 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
995 return mAmbientSingleLineChild;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100996 default:
997 return mContractedWrapper;
998 }
999 }
1000
1001 /**
1002 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -07001003 * @return the corresponding view according to the given visible type
1004 */
1005 private View getViewForVisibleType(int visibleType) {
1006 switch (visibleType) {
1007 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -07001008 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001009 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -07001010 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -07001011 case VISIBLE_TYPE_SINGLELINE:
1012 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001013 case VISIBLE_TYPE_AMBIENT:
1014 return mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001015 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1016 return mAmbientSingleLineChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001017 default:
1018 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -07001019 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001020 }
1021
Selim Cinek131f1a42017-06-05 17:50:19 -07001022 public NotificationViewWrapper getVisibleWrapper(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -08001023 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -08001024 case VISIBLE_TYPE_EXPANDED:
1025 return mExpandedWrapper;
1026 case VISIBLE_TYPE_HEADSUP:
1027 return mHeadsUpWrapper;
1028 case VISIBLE_TYPE_CONTRACTED:
1029 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001030 case VISIBLE_TYPE_AMBIENT:
1031 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -08001032 default:
1033 return null;
1034 }
1035 }
1036
Selim Cinek684a4422015-04-15 16:18:39 -07001037 /**
1038 * @return one of the static enum types in this view, calculated form the current state
1039 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001040 public int calculateVisibleType() {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001041 if (mContainingNotification.isShowingAmbient()) {
1042 if (mIsChildInGroup && mAmbientSingleLineChild != null) {
1043 return VISIBLE_TYPE_AMBIENT_SINGLELINE;
1044 } else if (mAmbientChild != null) {
1045 return VISIBLE_TYPE_AMBIENT;
1046 } else {
1047 return VISIBLE_TYPE_CONTRACTED;
1048 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001049 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001050 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -08001051 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001052 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001053 ? mContainingNotification.getMaxContentHeight()
1054 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -08001055 if (height == 0) {
1056 height = mContentHeight;
1057 }
Selim Cinek42357e02016-02-24 18:48:01 -08001058 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -08001059 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -07001060 ? VISIBLE_TYPE_SINGLELINE
1061 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001062 return mTransformationStartVisibleType == collapsedVisualType
1063 ? expandedVisualType
1064 : collapsedVisualType;
1065 }
Selim Cinek51d94912016-03-02 15:34:28 -08001066 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
1067 int viewHeight = mContentHeight;
1068 if (intrinsicHeight != 0) {
1069 // the intrinsicHeight might be 0 because it was just reset.
1070 viewHeight = Math.min(mContentHeight, intrinsicHeight);
1071 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001072 return getVisualTypeForHeight(viewHeight);
1073 }
1074
1075 private int getVisualTypeForHeight(float viewHeight) {
1076 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001077 if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
Adrian Roos777ef562015-12-01 17:37:14 -08001078 return VISIBLE_TYPE_EXPANDED;
1079 }
Selim Cinek414ad332017-02-24 19:06:12 -08001080 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001081 return VISIBLE_TYPE_SINGLELINE;
1082 }
Adrian Roos777ef562015-12-01 17:37:14 -08001083
Selim Cinekc494e382017-01-31 16:09:23 -08001084 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
1085 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001086 if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -07001087 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -07001088 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001089 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001090 }
1091 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001092 if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
Selim Cinek6ac3fa82016-05-10 17:11:54 -04001093 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001094 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -07001095 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001096 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001097 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001098 }
1099 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001100 }
1101
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001102 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001103 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001104 }
John Spurlocke15452b2014-08-21 09:44:39 -04001105
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001106 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -08001107 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -08001108 return;
1109 }
John Spurlocke15452b2014-08-21 09:44:39 -04001110 mDark = dark;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001111 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -04001112 }
1113
Selim Cinek8d490d42015-04-10 00:05:50 -07001114 public void setHeadsUp(boolean headsUp) {
1115 mIsHeadsUp = headsUp;
1116 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -08001117 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -07001118 }
1119
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +02001120 @Override
1121 public boolean hasOverlappingRendering() {
1122
1123 // This is not really true, but good enough when fading from the contracted to the expanded
1124 // layout, and saves us some layers.
1125 return false;
1126 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001127
Selim Cinek1a48bab2017-02-17 19:38:40 -08001128 public void setLegacy(boolean legacy) {
1129 mLegacy = legacy;
1130 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -08001131 }
1132
Selim Cinek1a48bab2017-02-17 19:38:40 -08001133 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -08001134 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001135 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001136 }
1137 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001138 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001139 }
1140 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001141 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001142 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001143 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001144
Selim Cinek83bc7832015-10-22 13:26:54 -07001145 public void setIsChildInGroup(boolean isChildInGroup) {
1146 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001147 if (mContractedChild != null) {
1148 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1149 }
1150 if (mExpandedChild != null) {
1151 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1152 }
1153 if (mHeadsUpChild != null) {
1154 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1155 }
1156 if (mAmbientChild != null) {
1157 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1158 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001159 updateAllSingleLineViews();
Selim Cinek83bc7832015-10-22 13:26:54 -07001160 }
1161
Adrian Roosb88b1a12015-12-09 18:51:05 -08001162 public void onNotificationUpdated(NotificationData.Entry entry) {
1163 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -08001164 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001165 updateAllSingleLineViews();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001166 if (mContractedChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001167 mContractedWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001168 }
1169 if (mExpandedChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001170 mExpandedWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001171 }
1172 if (mHeadsUpChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001173 mHeadsUpWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001174 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001175 if (mAmbientChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001176 mAmbientWrapper.onContentUpdated(entry.row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001177 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001178 applyRemoteInputAndSmartReply(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001179 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001180 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001181 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001182 mPreviousExpandedRemoteInputIntent = null;
1183 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001184 }
1185
Adrian Roos6f6e1592017-05-02 16:22:53 -07001186 private void updateAllSingleLineViews() {
1187 updateSingleLineView();
1188 updateAmbientSingleLineView();
1189 }
Selim Cinek83bc7832015-10-22 13:26:54 -07001190 private void updateSingleLineView() {
1191 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001192 boolean isNewView = mSingleLineView == null;
Selim Cinekc897bd32016-03-18 17:32:31 -07001193 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001194 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001195 if (isNewView) {
1196 updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
1197 mSingleLineView, mSingleLineView);
1198 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001199 } else if (mSingleLineView != null) {
1200 removeView(mSingleLineView);
1201 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001202 }
1203 }
1204
Adrian Roos6f6e1592017-05-02 16:22:53 -07001205 private void updateAmbientSingleLineView() {
1206 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001207 boolean isNewView = mAmbientSingleLineChild == null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001208 mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
1209 mAmbientSingleLineChild, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001210 if (isNewView) {
1211 updateViewVisibility(mVisibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
1212 mAmbientSingleLineChild, mAmbientSingleLineChild);
1213 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001214 } else if (mAmbientSingleLineChild != null) {
1215 removeView(mAmbientSingleLineChild);
1216 mAmbientSingleLineChild = null;
1217 }
1218 }
1219
Petr Cermak7997d7a2018-01-18 15:52:10 +00001220 private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001221 if (mRemoteInputController == null) {
1222 return;
1223 }
1224
Richard Ledley4069f7a2018-02-26 10:36:00 +00001225 boolean enableSmartReplies = (mSmartReplyConstants.isEnabled()
1226 && (!mSmartReplyConstants.requiresTargetingP()
1227 || entry.targetSdk >= Build.VERSION_CODES.P));
Petr Cermak7997d7a2018-01-18 15:52:10 +00001228
Adrian Roosb88b1a12015-12-09 18:51:05 -08001229 boolean hasRemoteInput = false;
Petr Cermak7997d7a2018-01-18 15:52:10 +00001230 RemoteInput remoteInputWithChoices = null;
1231 PendingIntent pendingIntentWithChoices = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001232
1233 Notification.Action[] actions = entry.notification.getNotification().actions;
1234 if (actions != null) {
1235 for (Notification.Action a : actions) {
1236 if (a.getRemoteInputs() != null) {
1237 for (RemoteInput ri : a.getRemoteInputs()) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001238 boolean showRemoteInputView = ri.getAllowFreeFormInput();
1239 boolean showSmartReplyView = enableSmartReplies && ri.getChoices() != null
1240 && ri.getChoices().length > 0;
1241 if (showRemoteInputView) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001242 hasRemoteInput = true;
Petr Cermak7997d7a2018-01-18 15:52:10 +00001243 }
1244 if (showSmartReplyView) {
1245 remoteInputWithChoices = ri;
1246 pendingIntentWithChoices = a.actionIntent;
1247 }
1248 if (showRemoteInputView || showSmartReplyView) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001249 break;
1250 }
1251 }
1252 }
1253 }
1254 }
1255
Petr Cermak7997d7a2018-01-18 15:52:10 +00001256 applyRemoteInput(entry, hasRemoteInput);
Kenny Guy23991102018-04-05 21:18:38 +01001257 applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001258 }
1259
1260 private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001261 View bigContentView = mExpandedChild;
1262 if (bigContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001263 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001264 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1265 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001266 } else {
1267 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001268 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001269 if (mCachedExpandedRemoteInput != null
1270 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1271 // We had a cached remote input but didn't reuse it. Clean up required.
1272 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1273 }
1274 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001275
Adrian Roosb88b1a12015-12-09 18:51:05 -08001276 View headsUpContentView = mHeadsUpChild;
1277 if (headsUpContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001278 mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001279 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001280 } else {
1281 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001282 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001283 if (mCachedHeadsUpRemoteInput != null
1284 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1285 // We had a cached remote input but didn't reuse it. Clean up required.
1286 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1287 }
1288 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001289 }
1290
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001291 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001292 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001293 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001294 View actionContainerCandidate = view.findViewById(
1295 com.android.internal.R.id.actions_container);
1296 if (actionContainerCandidate instanceof FrameLayout) {
1297 RemoteInputView existing = (RemoteInputView)
1298 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1299
1300 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001301 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001302 }
1303
1304 if (existing == null && hasRemoteInput) {
1305 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001306 if (cachedView == null) {
1307 RemoteInputView riv = RemoteInputView.inflate(
1308 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001309
Adrian Roos7813dd72016-09-23 17:12:17 -07001310 riv.setVisibility(View.INVISIBLE);
1311 actionContainer.addView(riv, new LayoutParams(
1312 ViewGroup.LayoutParams.MATCH_PARENT,
1313 ViewGroup.LayoutParams.MATCH_PARENT)
1314 );
1315 existing = riv;
1316 } else {
1317 actionContainer.addView(cachedView);
1318 cachedView.dispatchFinishTemporaryDetach();
1319 cachedView.requestFocus();
1320 existing = cachedView;
1321 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001322 }
1323 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001324 int color = entry.notification.getNotification().color;
1325 if (color == Notification.COLOR_DEFAULT) {
1326 color = mContext.getColor(R.color.default_remote_input_background);
1327 }
Lucas Dupina291d192018-06-07 13:59:42 -07001328 existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001329 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001330 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001331
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001332 existing.setWrapper(wrapper);
Selim Cineked64a142018-02-06 18:06:01 -08001333 existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001334
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001335 if (existingPendingIntent != null || existing.isActive()) {
1336 // The current action could be gone, or the pending intent no longer valid.
1337 // If we find a matching action in the new notification, focus, otherwise close.
1338 Notification.Action[] actions = entry.notification.getNotification().actions;
1339 if (existingPendingIntent != null) {
1340 existing.setPendingIntent(existingPendingIntent);
1341 }
1342 if (existing.updatePendingIntentFromActions(actions)) {
1343 if (!existing.isActive()) {
1344 existing.focus();
1345 }
1346 } else {
1347 if (existing.isActive()) {
1348 existing.close();
1349 }
1350 }
1351 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001352 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001353 return existing;
1354 }
1355 return null;
1356 }
1357
Kenny Guy23991102018-04-05 21:18:38 +01001358 private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
1359 NotificationData.Entry entry) {
1360 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +01001361 mExpandedSmartReplyView =
Kenny Guy23991102018-04-05 21:18:38 +01001362 applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry);
Milo Sredkove7cf4982018-04-09 15:08:26 +01001363 if (mExpandedSmartReplyView != null && remoteInput != null
1364 && remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
Kenny Guya0f6de82018-04-06 16:20:16 +01001365 mSmartReplyController.smartRepliesAdded(entry, remoteInput.getChoices().length);
Kenny Guy23991102018-04-05 21:18:38 +01001366 }
1367 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001368 }
1369
1370 private SmartReplyView applySmartReplyView(
Kenny Guy23991102018-04-05 21:18:38 +01001371 View view, RemoteInput remoteInput, PendingIntent pendingIntent,
1372 NotificationData.Entry entry) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001373 View smartReplyContainerCandidate = view.findViewById(
1374 com.android.internal.R.id.smart_reply_container);
1375 if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
1376 return null;
1377 }
1378 LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
1379 if (remoteInput == null || pendingIntent == null) {
1380 smartReplyContainer.setVisibility(View.GONE);
1381 return null;
1382 }
Kenny Guya0f6de82018-04-06 16:20:16 +01001383 // If we are showing the spinner we don't want to add the buttons.
1384 boolean showingSpinner = entry.notification.getNotification()
1385 .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
1386 if (showingSpinner) {
1387 smartReplyContainer.setVisibility(View.GONE);
1388 return null;
1389 }
Kenny Guy8cc15d22018-05-09 09:50:55 +01001390 // If we are keeping the notification around while sending we don't want to add the buttons.
1391 boolean hideSmartReplies = entry.notification.getNotification()
1392 .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false);
1393 if (hideSmartReplies) {
1394 smartReplyContainer.setVisibility(View.GONE);
1395 return null;
1396 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001397 SmartReplyView smartReplyView = null;
1398 if (smartReplyContainer.getChildCount() == 0) {
1399 smartReplyView = SmartReplyView.inflate(mContext, smartReplyContainer);
1400 smartReplyContainer.addView(smartReplyView);
1401 } else if (smartReplyContainer.getChildCount() == 1) {
1402 View child = smartReplyContainer.getChildAt(0);
1403 if (child instanceof SmartReplyView) {
1404 smartReplyView = (SmartReplyView) child;
1405 }
1406 }
1407 if (smartReplyView != null) {
Kenny Guy23991102018-04-05 21:18:38 +01001408 smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
Kenny Guya0f6de82018-04-06 16:20:16 +01001409 mSmartReplyController, entry, smartReplyContainer);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001410 smartReplyContainer.setVisibility(View.VISIBLE);
1411 }
1412 return smartReplyView;
1413 }
1414
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001415 public void closeRemoteInput() {
1416 if (mHeadsUpRemoteInput != null) {
1417 mHeadsUpRemoteInput.close();
1418 }
1419 if (mExpandedRemoteInput != null) {
1420 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001421 }
1422 }
1423
Selim Cinek83bc7832015-10-22 13:26:54 -07001424 public void setGroupManager(NotificationGroupManager groupManager) {
1425 mGroupManager = groupManager;
1426 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001427
Adrian Roosb88b1a12015-12-09 18:51:05 -08001428 public void setRemoteInputController(RemoteInputController r) {
1429 mRemoteInputController = r;
1430 }
1431
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001432 public void setExpandClickListener(OnClickListener expandClickListener) {
1433 mExpandClickListener = expandClickListener;
1434 }
1435
1436 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001437 mExpandable = expandable;
1438 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001439 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001440 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1441 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001442 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001443 expandable = false;
1444 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001445 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001446 expandable = false;
1447 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001448 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001449 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001450 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001451 }
1452 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001453 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001454 }
1455 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001456 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001457 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001458 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001459 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001460
1461 public NotificationHeaderView getNotificationHeader() {
1462 NotificationHeaderView header = null;
1463 if (mContractedChild != null) {
1464 header = mContractedWrapper.getNotificationHeader();
1465 }
1466 if (header == null && mExpandedChild != null) {
1467 header = mExpandedWrapper.getNotificationHeader();
1468 }
1469 if (header == null && mHeadsUpChild != null) {
1470 header = mHeadsUpWrapper.getNotificationHeader();
1471 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001472 if (header == null && mAmbientChild != null) {
1473 header = mAmbientWrapper.getNotificationHeader();
1474 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001475 return header;
1476 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001477
Julia Reynoldsfc640012018-02-21 12:25:27 -05001478 public void showAppOpsIcons(ArraySet<Integer> activeOps) {
1479 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
1480 mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1481 }
1482 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
1483 mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1484 }
1485 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
1486 mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1487 }
1488 }
Selim Cinekb26afa32017-06-29 10:28:17 +02001489
1490 public NotificationHeaderView getContractedNotificationHeader() {
1491 if (mContractedChild != null) {
1492 return mContractedWrapper.getNotificationHeader();
1493 }
1494 return null;
1495 }
1496
Selim Cinek34eda5e2016-02-18 17:10:43 -08001497 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001498 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001499 return wrapper == null ? null : wrapper.getNotificationHeader();
1500 }
1501
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001502 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1503 mContainingNotification = containingNotification;
1504 }
1505
1506 public void requestSelectLayout(boolean needsAnimation) {
1507 selectLayout(needsAnimation, false);
1508 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001509
1510 public void reInflateViews() {
1511 if (mIsChildInGroup && mSingleLineView != null) {
1512 removeView(mSingleLineView);
1513 mSingleLineView = null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001514 updateAllSingleLineViews();
Selim Cinekde33a4a2016-02-11 16:43:41 -08001515 }
1516 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001517
1518 public void setUserExpanding(boolean userExpanding) {
1519 mUserExpanding = userExpanding;
1520 if (userExpanding) {
1521 mTransformationStartVisibleType = mVisibleType;
1522 } else {
1523 mTransformationStartVisibleType = UNDEFINED;
1524 mVisibleType = calculateVisibleType();
1525 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001526 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001527 }
1528 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001529
1530 /**
1531 * Set by how much the single line view should be indented. Used when a overflow indicator is
1532 * present and only during measuring
1533 */
1534 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1535 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1536 mSingleLineWidthIndention = singleLineWidthIndention;
1537 mContainingNotification.forceLayout();
1538 forceLayout();
1539 }
1540 }
1541
1542 public HybridNotificationView getSingleLineView() {
1543 return mSingleLineView;
1544 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001545
1546 public void setRemoved() {
1547 if (mExpandedRemoteInput != null) {
1548 mExpandedRemoteInput.setRemoved();
1549 }
1550 if (mHeadsUpRemoteInput != null) {
1551 mHeadsUpRemoteInput.setRemoved();
1552 }
1553 }
Adrian Roos599be342016-06-13 14:54:39 -07001554
1555 public void setContentHeightAnimating(boolean animating) {
1556 if (!animating) {
1557 mContentHeightAtAnimationStart = UNDEFINED;
1558 }
1559 }
Selim Cineke9bad242016-06-15 11:46:37 -07001560
Adrian Roose18033c2017-01-17 15:22:49 -08001561 @VisibleForTesting
1562 boolean isAnimatingVisibleType() {
1563 return mAnimationStartVisibleType != UNDEFINED;
1564 }
1565
Selim Cinekcafa87f2016-10-26 17:00:17 -07001566 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1567 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001568 selectLayout(false /* animate */, true /* force */);
1569 }
1570
Selim Cineke9bad242016-06-15 11:46:37 -07001571 public void setFocusOnVisibilityChange() {
1572 mFocusOnVisibilityChange = true;
1573 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001574
1575 public void setIconsVisible(boolean iconsVisible) {
1576 mIconsVisible = iconsVisible;
1577 updateIconVisibilities();
1578 }
1579
1580 private void updateIconVisibilities() {
1581 if (mContractedWrapper != null) {
1582 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1583 if (header != null) {
1584 header.getIcon().setForceHidden(!mIconsVisible);
1585 }
1586 }
1587 if (mHeadsUpWrapper != null) {
1588 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1589 if (header != null) {
1590 header.getIcon().setForceHidden(!mIconsVisible);
1591 }
1592 }
1593 if (mExpandedWrapper != null) {
1594 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1595 if (header != null) {
1596 header.getIcon().setForceHidden(!mIconsVisible);
1597 }
1598 }
1599 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001600
Adrian Roos4320e892017-01-24 12:50:34 -08001601 @Override
1602 public void onVisibilityAggregated(boolean isVisible) {
1603 super.onVisibilityAggregated(isVisible);
1604 if (isVisible) {
1605 fireExpandedVisibleListenerIfVisible();
1606 }
1607 }
1608
1609 /**
1610 * Sets a one-shot listener for when the expanded view becomes visible.
1611 *
1612 * This will fire the listener immediately if the expanded view is already visible.
1613 */
1614 public void setOnExpandedVisibleListener(Runnable r) {
1615 mExpandedVisibleListener = r;
1616 fireExpandedVisibleListenerIfVisible();
1617 }
1618
Selim Cinek6743c0b2017-01-18 18:24:01 -08001619 public void setIsLowPriority(boolean isLowPriority) {
1620 mIsLowPriority = isLowPriority;
1621 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001622
1623 public boolean isDimmable() {
1624 if (!mContractedWrapper.isDimmable()) {
1625 return false;
1626 }
1627 return true;
1628 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001629
1630 /**
1631 * Should a single click be disallowed on this view when on the keyguard?
1632 */
1633 public boolean disallowSingleClick(float x, float y) {
1634 NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
1635 if (visibleWrapper != null) {
1636 return visibleWrapper.disallowSingleClick(x, y);
1637 }
1638 return false;
1639 }
Selim Cinek515b2032017-11-15 10:20:19 -08001640
Selim Cinek86bfcee2018-01-17 11:00:47 -08001641 public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
1642 boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001643 if (mUserExpanding) {
Selim Cinek86bfcee2018-01-17 11:00:47 -08001644 needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
1645 bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001646 }
1647 return needsPaddings;
1648 }
1649
Selim Cinek86bfcee2018-01-17 11:00:47 -08001650 private boolean shouldClipToRounding(int visibleType, boolean topRounded,
1651 boolean bottomRounded) {
Selim Cinek515b2032017-11-15 10:20:19 -08001652 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
1653 if (visibleWrapper == null) {
1654 return false;
1655 }
Selim Cinek86bfcee2018-01-17 11:00:47 -08001656 return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001657 }
Selim Cinekde4de0e2018-01-24 16:21:07 -08001658
1659 public CharSequence getActiveRemoteInputText() {
1660 if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
1661 return mExpandedRemoteInput.getText();
1662 }
1663 if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
1664 return mHeadsUpRemoteInput.getText();
1665 }
1666 return null;
1667 }
Selim Cineked64a142018-02-06 18:06:01 -08001668
Selim Cineka77d2c52018-04-09 10:44:52 -07001669 @Override
1670 public boolean dispatchTouchEvent(MotionEvent ev) {
1671 float y = ev.getY();
1672 // We still want to distribute touch events to the remote input even if it's outside the
1673 // view boundary. We're therefore manually dispatching these events to the remote view
1674 RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
1675 if (riv != null && riv.getVisibility() == VISIBLE) {
1676 int inputStart = mUnrestrictedContentHeight - riv.getHeight();
1677 if (y <= mUnrestrictedContentHeight && y >= inputStart) {
1678 ev.offsetLocation(0, -inputStart);
1679 return riv.dispatchTouchEvent(ev);
1680 }
1681 }
1682 return super.dispatchTouchEvent(ev);
1683 }
1684
1685 /**
1686 * Overridden to make sure touches to the reply action bar actually go through to this view
1687 */
1688 @Override
1689 public boolean pointInView(float localX, float localY, float slop) {
1690 float top = mClipTopAmount;
1691 float bottom = mUnrestrictedContentHeight;
1692 return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
1693 localY < (bottom + slop);
1694 }
1695
1696 private RemoteInputView getRemoteInputForView(View child) {
1697 if (child == mExpandedChild) {
1698 return mExpandedRemoteInput;
1699 } else if (child == mHeadsUpChild) {
1700 return mHeadsUpRemoteInput;
1701 }
1702 return null;
1703 }
1704
Selim Cineked64a142018-02-06 18:06:01 -08001705 public int getExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001706 int viewType = VISIBLE_TYPE_EXPANDED;
1707 if (mExpandedChild == null) {
1708 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001709 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001710 return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001711 }
1712
1713 public int getHeadsUpHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001714 int viewType = VISIBLE_TYPE_HEADSUP;
1715 if (mHeadsUpChild == null) {
1716 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001717 }
Selim Cinek09b442e2018-05-04 15:46:18 -07001718 // The headsUp remote input quickly switches to the expanded one, so lets also include that
1719 // one
1720 return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
1721 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001722 }
1723
1724 public void setRemoteInputVisible(boolean remoteInputVisible) {
1725 mRemoteInputVisible = remoteInputVisible;
1726 setClipChildren(!remoteInputVisible);
1727 }
1728
1729 @Override
1730 public void setClipChildren(boolean clipChildren) {
1731 clipChildren = clipChildren && !mRemoteInputVisible;
1732 super.setClipChildren(clipChildren);
1733 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001734
1735 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1736 if (mContractedWrapper != null) {
1737 mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1738 }
1739 if (mHeadsUpWrapper != null) {
1740 mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1741 }
1742 if (mExpandedWrapper != null) {
1743 mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1744 }
1745 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001746}