blob: 689d6d530308baeeb1779c76e1e7e4be1baef3a1 [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
Kevin38ce6fa2018-10-17 16:00:14 -070019import android.annotation.Nullable;
Adrian Roosb88b1a12015-12-09 18:51:05 -080020import android.app.Notification;
Adrian Roos4c1fcc82016-03-31 14:39:39 -070021import android.app.PendingIntent;
Adrian Roosb88b1a12015-12-09 18:51:05 -080022import android.app.RemoteInput;
Jorim Jaggibe565df2014-04-28 17:51:23 +020023import android.content.Context;
24import android.graphics.Rect;
Selim Cinek860b6da2015-12-16 19:02:19 -080025import android.os.Build;
Selim Cinek83bc7832015-10-22 13:26:54 -070026import android.service.notification.StatusBarNotification;
Kevind5022f92018-10-08 18:30:26 -070027import android.util.ArrayMap;
Julia Reynoldsfc640012018-02-21 12:25:27 -050028import android.util.ArraySet;
Jorim Jaggibe565df2014-04-28 17:51:23 +020029import android.util.AttributeSet;
Selim Cinek011bde92017-11-22 05:53:38 -080030import android.util.Log;
Tony Mak638430e2018-10-08 19:19:10 +010031import android.util.Pair;
Selim Cineka77d2c52018-04-09 10:44:52 -070032import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080033import android.view.NotificationHeaderView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020034import android.view.View;
Selim Cinek8d490d42015-04-10 00:05:50 -070035import android.view.ViewGroup;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010036import android.view.ViewTreeObserver;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020037import android.widget.FrameLayout;
Selim Cineke9bad242016-06-15 11:46:37 -070038import android.widget.ImageView;
Petr Cermak7997d7a2018-01-18 15:52:10 +000039import android.widget.LinearLayout;
Selim Cinek8d490d42015-04-10 00:05:50 -070040
Adrian Roose18033c2017-01-17 15:22:49 -080041import com.android.internal.annotations.VisibleForTesting;
Tony Makc9acf672018-07-20 13:58:24 +020042import com.android.internal.util.ArrayUtils;
Lucas Dupina291d192018-06-07 13:59:42 -070043import com.android.internal.util.ContrastColorUtil;
Petr Cermak10011fa2018-02-05 19:00:54 +000044import com.android.systemui.Dependency;
Jorim Jaggibe565df2014-04-28 17:51:23 +020045import com.android.systemui.R;
Rohan Shah20790b82018-07-02 17:21:04 -070046import com.android.systemui.statusbar.RemoteInputController;
47import com.android.systemui.statusbar.SmartReplyController;
48import com.android.systemui.statusbar.TransformableView;
Tony Mak638430e2018-10-08 19:19:10 +010049import com.android.systemui.statusbar.notification.NotificationData;
Selim Cinekc3179332016-03-04 14:44:56 -080050import com.android.systemui.statusbar.notification.NotificationUtils;
Tony Mak638430e2018-10-08 19:19:10 +010051import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
Rohan Shah20790b82018-07-02 17:21:04 -070052import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070053import com.android.systemui.statusbar.phone.NotificationGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080054import com.android.systemui.statusbar.policy.RemoteInputView;
Petr Cermak10011fa2018-02-05 19:00:54 +000055import com.android.systemui.statusbar.policy.SmartReplyConstants;
Petr Cermak7997d7a2018-01-18 15:52:10 +000056import com.android.systemui.statusbar.policy.SmartReplyView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020057
Selim Cinek85a8f9f2018-11-21 13:58:27 -080058import java.io.FileDescriptor;
59import java.io.PrintWriter;
Gustav Senntoneab53682018-11-01 16:30:23 +000060import java.util.List;
61
Jorim Jaggibe565df2014-04-28 17:51:23 +020062/**
Selim Cinek684a4422015-04-15 16:18:39 -070063 * A frame layout containing the actual payload of the notification, including the contracted,
64 * expanded and heads up layout. This class is responsible for clipping the content and and
65 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020066 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020067public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020068
Selim Cinek011bde92017-11-22 05:53:38 -080069 private static final String TAG = "NotificationContentView";
Selim Cinek131f1a42017-06-05 17:50:19 -070070 public static final int VISIBLE_TYPE_CONTRACTED = 0;
71 public static final int VISIBLE_TYPE_EXPANDED = 1;
72 public static final int VISIBLE_TYPE_HEADSUP = 2;
Selim Cinek83bc7832015-10-22 13:26:54 -070073 private static final int VISIBLE_TYPE_SINGLELINE = 3;
Selim Cinek131f1a42017-06-05 17:50:19 -070074 public static final int VISIBLE_TYPE_AMBIENT = 4;
Adrian Roos6f6e1592017-05-02 16:22:53 -070075 private static final int VISIBLE_TYPE_AMBIENT_SINGLELINE = 5;
Selim Cinekc3179332016-03-04 14:44:56 -080076 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020077
Jorim Jaggibe565df2014-04-28 17:51:23 +020078 private final Rect mClipBounds = new Rect();
79
Selim Cinek9b49f6d2017-11-13 18:19:35 -080080 private int mMinContractedHeight;
81 private int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020082 private View mContractedChild;
83 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070084 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070085 private HybridNotificationView mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -080086 private View mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -070087 private HybridNotificationView mAmbientSingleLineChild;
Jorim Jaggibe565df2014-04-28 17:51:23 +020088
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070089 private RemoteInputView mExpandedRemoteInput;
90 private RemoteInputView mHeadsUpRemoteInput;
Petr Cermak10011fa2018-02-05 19:00:54 +000091
92 private SmartReplyConstants mSmartReplyConstants;
Milo Sredkove7cf4982018-04-09 15:08:26 +010093 private SmartReplyView mExpandedSmartReplyView;
Kenny Guya0f6de82018-04-06 16:20:16 +010094 private SmartReplyController mSmartReplyController;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070095
Jorim Jaggi4e857f42014-11-17 19:14:04 +010096 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070097 private NotificationViewWrapper mExpandedWrapper;
98 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -080099 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -0700100 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200101 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +0100102 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -0700103 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -0400104 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100105 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -0700106 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800107 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -0700108 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -0800109 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800110 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800111 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800112 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700113 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700114 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800115 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -0800116 private Runnable mExpandedVisibleListener;
Kevind5022f92018-10-08 18:30:26 -0700117 /**
118 * List of listeners for when content views become inactive (i.e. not the showing view).
119 */
120 private final ArrayMap<View, Runnable> mOnContentViewInactiveListeners = new ArrayMap<>();
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700121
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700122 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100123 = new ViewTreeObserver.OnPreDrawListener() {
124 @Override
125 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -0800126 // We need to post since we don't want the notification to animate on the very first
127 // frame
128 post(new Runnable() {
129 @Override
130 public void run() {
131 mAnimate = true;
132 }
133 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100134 getViewTreeObserver().removeOnPreDrawListener(this);
135 return true;
136 }
137 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200138
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800139 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800140 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800141 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100142 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800143 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700144 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800145 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700146 /** The visible type at the start of an animation driven transformation */
147 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800148 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700149 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700150 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700151 private PendingIntent mPreviousExpandedRemoteInputIntent;
152 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700153 private RemoteInputView mCachedExpandedRemoteInput;
154 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800155
Adrian Roos599be342016-06-13 14:54:39 -0700156 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700157 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700158 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700159 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700160 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800161 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800162 private boolean mIsContentExpandable;
Selim Cineked64a142018-02-06 18:06:01 -0800163 private boolean mRemoteInputVisible;
164 private int mUnrestrictedContentHeight;
Adrian Roos599be342016-06-13 14:54:39 -0700165
166
Jorim Jaggibe565df2014-04-28 17:51:23 +0200167 public NotificationContentView(Context context, AttributeSet attrs) {
168 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700169 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Petr Cermak10011fa2018-02-05 19:00:54 +0000170 mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
Kenny Guya0f6de82018-04-06 16:20:16 +0100171 mSmartReplyController = Dependency.get(SmartReplyController.class);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800172 initView();
173 }
174
175 public void initView() {
Selim Cinek860b6da2015-12-16 19:02:19 -0800176 mMinContractedHeight = getResources().getDimensionPixelSize(
177 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800178 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
179 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200180 }
181
Adrian Roos0aac04f2016-12-08 15:59:29 -0800182 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
183 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800184 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800185 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800186 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800187 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800188 }
189
Jorim Jaggibe565df2014-04-28 17:51:23 +0200190 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700191 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
192 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
193 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
194 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700195 int maxSize = Integer.MAX_VALUE / 2;
Selim Cinekc897bd32016-03-18 17:32:31 -0700196 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700197 if (hasFixedHeight || isHeightLimited) {
198 maxSize = MeasureSpec.getSize(heightMeasureSpec);
199 }
200 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800201 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +0100202 int notificationMaxHeight = mNotificationMaxHeight;
203 if (mExpandedSmartReplyView != null) {
204 notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
205 }
Selim Cinekbee4e072018-05-21 22:06:43 -0700206 notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700207 int size = notificationMaxHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800208 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800209 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800210 if (layoutParams.height >= 0) {
211 // An actual height is set
Selim Cinekbe570ce2018-05-11 12:29:52 -0700212 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800213 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800214 }
Selim Cinekbe570ce2018-05-11 12:29:52 -0700215 int spec = MeasureSpec.makeMeasureSpec(size, useExactly
Selim Cinek6743c0b2017-01-18 18:24:01 -0800216 ? MeasureSpec.EXACTLY
217 : MeasureSpec.AT_MOST);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800218 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800219 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
220 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700221 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800222 int heightSpec;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700223 int size = mSmallHeight;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800224 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
225 boolean useExactly = false;
226 if (layoutParams.height >= 0) {
227 // An actual height is set
228 size = Math.min(size, layoutParams.height);
229 useExactly = true;
230 }
231 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800232 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
233 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800234 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800235 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800236 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800237 int measuredHeight = mContractedChild.getMeasuredHeight();
238 if (measuredHeight < mMinContractedHeight) {
239 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800240 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800241 }
242 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800243 if (updateContractedHeaderWidth()) {
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800244 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700245 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700246 if (mExpandedChild != null
247 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
248 // the Expanded child is smaller then the collapsed. Let's remeasure it.
249 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
250 MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800251 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek1ba41d12016-04-21 16:14:46 -0700252 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700253 }
254 if (mHeadsUpChild != null) {
Selim Cinek396caca2018-04-10 17:46:46 -0700255 int maxHeight = mHeadsUpHeight;
256 maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700257 int size = maxHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700258 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800259 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700260 if (layoutParams.height >= 0) {
261 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800262 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800263 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700264 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800265 measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800266 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800267 : MeasureSpec.AT_MOST), 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700268 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
269 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700270 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700271 int singleLineWidthSpec = widthMeasureSpec;
272 if (mSingleLineWidthIndention != 0
273 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
274 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
275 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700276 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700277 }
278 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinekbe570ce2018-05-11 12:29:52 -0700279 MeasureSpec.makeMeasureSpec(mNotificationMaxHeight, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700280 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
281 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800282 if (mAmbientChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700283 int size = mNotificationAmbientHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800284 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800285 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800286 if (layoutParams.height >= 0) {
287 // An actual height is set
288 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800289 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800290 }
291 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800292 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
293 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800294 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
295 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700296 if (mAmbientSingleLineChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700297 int size = mNotificationAmbientHeight;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700298 ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
299 boolean useExactly = false;
300 if (layoutParams.height >= 0) {
301 // An actual height is set
302 size = Math.min(size, layoutParams.height);
303 useExactly = true;
304 }
305 int ambientSingleLineWidthSpec = widthMeasureSpec;
306 if (mSingleLineWidthIndention != 0
307 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
308 ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
309 width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
310 MeasureSpec.EXACTLY);
311 }
312 mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
313 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
314 : MeasureSpec.AT_MOST));
315 maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
316 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700317 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700318 setMeasuredDimension(width, ownHeight);
319 }
320
Selim Cineked64a142018-02-06 18:06:01 -0800321 /**
322 * Get the extra height that needs to be added to the notification height for a given
323 * {@link RemoteInputView}.
324 * This is needed when the user is inline replying in order to ensure that the reply bar has
325 * enough padding.
326 *
327 * @param remoteInput The remote input to check.
328 * @return The extra height needed.
329 */
330 private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
Selim Cinek09b442e2018-05-04 15:46:18 -0700331 if (remoteInput != null && (remoteInput.isActive() || remoteInput.isSending())) {
Selim Cineked64a142018-02-06 18:06:01 -0800332 return getResources().getDimensionPixelSize(
333 com.android.internal.R.dimen.notification_content_margin);
334 }
335 return 0;
336 }
337
Selim Cinek6ecc8102016-01-26 18:26:19 -0800338 private boolean updateContractedHeaderWidth() {
339 // We need to update the expanded and the collapsed header to have exactly the same with to
340 // have the expand buttons laid out at the same location.
341 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
342 if (contractedHeader != null) {
343 if (mExpandedChild != null
344 && mExpandedWrapper.getNotificationHeader() != null) {
345 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
346 int expandedSize = expandedHeader.getMeasuredWidth()
347 - expandedHeader.getPaddingEnd();
348 int collapsedSize = contractedHeader.getMeasuredWidth()
349 - expandedHeader.getPaddingEnd();
350 if (expandedSize != collapsedSize) {
351 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
352 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800353 contractedHeader.isLayoutRtl()
354 ? paddingEnd
355 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800356 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800357 contractedHeader.isLayoutRtl()
358 ? contractedHeader.getPaddingLeft()
359 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800360 contractedHeader.getPaddingBottom());
361 contractedHeader.setShowWorkBadgeAtEnd(true);
362 return true;
363 }
364 } else {
365 int paddingEnd = mNotificationContentMarginEnd;
366 if (contractedHeader.getPaddingEnd() != paddingEnd) {
367 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800368 contractedHeader.isLayoutRtl()
369 ? paddingEnd
370 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800371 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800372 contractedHeader.isLayoutRtl()
373 ? contractedHeader.getPaddingLeft()
374 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800375 contractedHeader.getPaddingBottom());
376 contractedHeader.setShowWorkBadgeAtEnd(false);
377 return true;
378 }
379 }
380 }
381 return false;
382 }
383
Selim Cinek860b6da2015-12-16 19:02:19 -0800384 private boolean shouldContractedBeFixedSize() {
385 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
386 }
387
Selim Cinek8d490d42015-04-10 00:05:50 -0700388 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200389 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700390 int previousHeight = 0;
391 if (mExpandedChild != null) {
392 previousHeight = mExpandedChild.getHeight();
393 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200394 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700395 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
396 mContentHeightAtAnimationStart = previousHeight;
397 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200398 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700399 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700400 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
401 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700402 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200403 }
404
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100405 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100406 protected void onAttachedToWindow() {
407 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100408 updateVisibility();
409 }
410
Selim Cinekcab4a602014-09-03 14:47:57 +0200411 public View getContractedChild() {
412 return mContractedChild;
413 }
414
415 public View getExpandedChild() {
416 return mExpandedChild;
417 }
418
Selim Cinek8d490d42015-04-10 00:05:50 -0700419 public View getHeadsUpChild() {
420 return mHeadsUpChild;
421 }
422
Adrian Roos0aac04f2016-12-08 15:59:29 -0800423 public View getAmbientChild() {
424 return mAmbientChild;
425 }
426
Adrian Roos6f6e1592017-05-02 16:22:53 -0700427 public HybridNotificationView getAmbientSingleLineChild() {
428 return mAmbientSingleLineChild;
429 }
430
Kevin38ce6fa2018-10-17 16:00:14 -0700431 /**
432 * Sets the contracted view. Child may be null to remove the content view.
433 *
434 * @param child contracted content view to set
435 */
436 public void setContractedChild(@Nullable View child) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200437 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200438 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200439 removeView(mContractedChild);
440 }
Kevin38ce6fa2018-10-17 16:00:14 -0700441 if (child == null) {
442 mContractedChild = null;
443 mContractedWrapper = null;
444 if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
445 mTransformationStartVisibleType = UNDEFINED;
446 }
447 return;
448 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200449 addView(child);
450 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700451 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
452 mContainingNotification);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800453 }
454
455 private NotificationViewWrapper getWrapperForView(View child) {
456 if (child == mContractedChild) {
457 return mContractedWrapper;
458 }
459 if (child == mExpandedChild) {
460 return mExpandedWrapper;
461 }
462 if (child == mHeadsUpChild) {
463 return mHeadsUpWrapper;
464 }
465 if (child == mAmbientChild) {
466 return mAmbientWrapper;
467 }
468 return null;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200469 }
470
Kevin38ce6fa2018-10-17 16:00:14 -0700471 /**
472 * Sets the expanded view. Child may be null to remove the content view.
473 *
474 * @param child expanded content view to set
475 */
476 public void setExpandedChild(@Nullable View child) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200477 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800478 mPreviousExpandedRemoteInputIntent = null;
479 if (mExpandedRemoteInput != null) {
480 mExpandedRemoteInput.onNotificationUpdateOrReset();
481 if (mExpandedRemoteInput.isActive()) {
482 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
483 mCachedExpandedRemoteInput = mExpandedRemoteInput;
484 mExpandedRemoteInput.dispatchStartTemporaryDetach();
485 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
486 }
487 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200488 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200489 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800490 mExpandedRemoteInput = null;
491 }
492 if (child == null) {
493 mExpandedChild = null;
494 mExpandedWrapper = null;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800495 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
496 mTransformationStartVisibleType = UNDEFINED;
497 }
Selim Cinek9f186642018-11-29 15:47:40 -0800498 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
499 selectLayout(false /* animate */, true /* force */);
500 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800501 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200502 }
503 addView(child);
504 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700505 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
506 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200507 }
508
Kevin38ce6fa2018-10-17 16:00:14 -0700509 /**
510 * Sets the heads up view. Child may be null to remove the content view.
511 *
512 * @param child heads up content view to set
513 */
514 public void setHeadsUpChild(@Nullable View child) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700515 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800516 mPreviousHeadsUpRemoteInputIntent = null;
517 if (mHeadsUpRemoteInput != null) {
518 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
519 if (mHeadsUpRemoteInput.isActive()) {
520 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
521 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
522 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
523 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
524 }
525 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700526 mHeadsUpChild.animate().cancel();
527 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800528 mHeadsUpRemoteInput = null;
529 }
530 if (child == null) {
531 mHeadsUpChild = null;
532 mHeadsUpWrapper = null;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800533 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
534 mTransformationStartVisibleType = UNDEFINED;
535 }
Selim Cinek9f186642018-11-29 15:47:40 -0800536 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
537 selectLayout(false /* animate */, true /* force */);
538 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800539 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700540 }
541 addView(child);
542 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700543 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
544 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700545 }
546
Kevin38ce6fa2018-10-17 16:00:14 -0700547 /**
548 * Sets the ambient view. Child may be null to remove the content view.
549 *
550 * @param child ambient content view to set
551 */
552 public void setAmbientChild(@Nullable View child) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800553 if (mAmbientChild != null) {
554 mAmbientChild.animate().cancel();
555 removeView(mAmbientChild);
556 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700557 if (child == null) {
Kevind4660b22018-09-27 10:57:35 -0700558 mAmbientChild = null;
559 mAmbientWrapper = null;
Kevind4660b22018-09-27 10:57:35 -0700560 if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
561 mTransformationStartVisibleType = UNDEFINED;
562 }
Selim Cinek9f186642018-11-29 15:47:40 -0800563 if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
564 selectLayout(false /* animate */, true /* force */);
565 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700566 return;
567 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800568 addView(child);
569 mAmbientChild = child;
570 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
571 mContainingNotification);
572 }
573
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100574 @Override
575 protected void onVisibilityChanged(View changedView, int visibility) {
576 super.onVisibilityChanged(changedView, visibility);
577 updateVisibility();
Kevin38ce6fa2018-10-17 16:00:14 -0700578 if (visibility != VISIBLE) {
579 // View is no longer visible so all content views are inactive.
580 for (Runnable r : mOnContentViewInactiveListeners.values()) {
581 r.run();
582 }
583 mOnContentViewInactiveListeners.clear();
584 }
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100585 }
586
587 private void updateVisibility() {
588 setVisible(isShown());
589 }
590
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700591 @Override
592 protected void onDetachedFromWindow() {
593 super.onDetachedFromWindow();
594 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
595 }
596
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100597 private void setVisible(final boolean isVisible) {
598 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700599 // This call can happen multiple times, but removing only removes a single one.
600 // We therefore need to remove the old one.
601 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100602 // We only animate if we are drawn at least once, otherwise the view might animate when
603 // it's shown the first time
604 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
605 } else {
606 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
607 mAnimate = false;
608 }
609 }
610
Selim Cineke9bad242016-06-15 11:46:37 -0700611 private void focusExpandButtonIfNecessary() {
612 if (mFocusOnVisibilityChange) {
613 NotificationHeaderView header = getVisibleNotificationHeader();
614 if (header != null) {
615 ImageView expandButton = header.getExpandButton();
616 if (expandButton != null) {
617 expandButton.requestAccessibilityFocus();
618 }
619 }
620 mFocusOnVisibilityChange = false;
621 }
622 }
623
Selim Cinekb5605e52015-02-20 18:21:41 +0100624 public void setContentHeight(int contentHeight) {
Selim Cineked64a142018-02-06 18:06:01 -0800625 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
626 int maxContentHeight = mContainingNotification.getIntrinsicHeight()
627 - getExtraRemoteInputHeight(mExpandedRemoteInput)
628 - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
629 mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100630 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400631
Kevin38ce6fa2018-10-17 16:00:14 -0700632 if (mContractedChild == null) {
633 // Contracted child may be null if this is the public content view and we don't need to
634 // show it.
635 return;
636 }
637
Adrian Roos181385c2016-05-05 17:45:44 -0400638 int minHeightHint = getMinContentHeightHint();
639
640 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
641 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800642 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400643 }
644
645 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
646 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800647 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400648 }
649
Jorim Jaggibe565df2014-04-28 17:51:23 +0200650 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700651 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200652 }
653
Adrian Roos181385c2016-05-05 17:45:44 -0400654 /**
655 * @return the minimum apparent height that the wrapper should allow for the purpose
656 * of aligning elements at the bottom edge. If this is larger than the content
657 * height, the notification is clipped instead of being further shrunk.
658 */
659 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700660 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400661 return mContext.getResources().getDimensionPixelSize(
662 com.android.internal.R.dimen.notification_action_list_height);
663 }
Adrian Roos599be342016-06-13 14:54:39 -0700664
665 // Transition between heads-up & expanded, or pinned.
666 if (mHeadsUpChild != null && mExpandedChild != null) {
667 boolean transitioningBetweenHunAndExpanded =
668 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
669 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700670 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800671 && (mIsHeadsUp || mHeadsUpAnimatingAway)
672 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700673 if (transitioningBetweenHunAndExpanded || pinned) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800674 return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
675 getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700676 }
677 }
678
679 // Size change of the expanded version
680 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
681 && mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800682 return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700683 }
684
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700685 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800686 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
687 hint = mAmbientChild.getHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700688 } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
689 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
690 hint = mAmbientSingleLineChild.getHeight();
Adrian Roos05d3d002017-01-26 15:28:07 -0800691 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800692 hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
Adrian Roos599be342016-06-13 14:54:39 -0700693 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800694 hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
Adrian Roos181385c2016-05-05 17:45:44 -0400695 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800696 hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
697 + mContext.getResources().getDimensionPixelSize(
698 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400699 }
Adrian Roos599be342016-06-13 14:54:39 -0700700
701 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800702 hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700703 }
704 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400705 }
706
Adrian Roos599be342016-06-13 14:54:39 -0700707 private boolean isTransitioningFromTo(int from, int to) {
708 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
709 && mVisibleType == to;
710 }
711
712 private boolean isVisibleOrTransitioning(int type) {
713 return mVisibleType == type || mTransformationStartVisibleType == type
714 || mAnimationStartVisibleType == type;
715 }
716
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800717 private void updateContentTransformation() {
718 int visibleType = calculateVisibleType();
719 if (visibleType != mVisibleType) {
720 // A new transformation starts
721 mTransformationStartVisibleType = mVisibleType;
722 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
723 final TransformableView hiddenView = getTransformableViewForVisibleType(
724 mTransformationStartVisibleType);
725 shownView.transformFrom(hiddenView, 0.0f);
726 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
727 hiddenView.transformTo(shownView, 0.0f);
728 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800729 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800730 }
Selim Cineke8578872016-05-03 16:42:50 -0700731 if (mForceSelectNextLayout) {
732 forceUpdateVisibilities();
733 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800734 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700735 && mVisibleType != mTransformationStartVisibleType
736 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800737 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
738 final TransformableView hiddenView = getTransformableViewForVisibleType(
739 mTransformationStartVisibleType);
740 float transformationAmount = calculateTransformationAmount();
741 shownView.transformFrom(hiddenView, transformationAmount);
742 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800743 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800744 } else {
745 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800746 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800747 }
748 }
749
Selim Cinekc3179332016-03-04 14:44:56 -0800750 private void updateBackgroundTransformation(float transformationAmount) {
751 int endColor = getBackgroundColor(mVisibleType);
752 int startColor = getBackgroundColor(mTransformationStartVisibleType);
753 if (endColor != startColor) {
754 if (startColor == 0) {
755 startColor = mContainingNotification.getBackgroundColorWithoutTint();
756 }
757 if (endColor == 0) {
758 endColor = mContainingNotification.getBackgroundColorWithoutTint();
759 }
760 endColor = NotificationUtils.interpolateColors(startColor, endColor,
761 transformationAmount);
762 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400763 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800764 mContainingNotification.setContentBackground(endColor, false, this);
765 }
766
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800767 private float calculateTransformationAmount() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800768 int startHeight = getViewHeight(mTransformationStartVisibleType);
769 int endHeight = getViewHeight(mVisibleType);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800770 int progress = Math.abs(mContentHeight - startHeight);
771 int totalDistance = Math.abs(endHeight - startHeight);
Selim Cinek011bde92017-11-22 05:53:38 -0800772 if (totalDistance == 0) {
773 Log.wtf(TAG, "the total transformation distance is 0"
774 + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
775 + "\n VisibleType: " + mVisibleType + " height: " + endHeight
776 + "\n mContentHeight: " + mContentHeight);
777 return 1.0f;
778 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800779 float amount = (float) progress / (float) totalDistance;
780 return Math.min(1.0f, amount);
781 }
782
Selim Cinekb5605e52015-02-20 18:21:41 +0100783 public int getContentHeight() {
784 return mContentHeight;
785 }
786
Jorim Jaggibe565df2014-04-28 17:51:23 +0200787 public int getMaxHeight() {
Kevin38ce6fa2018-10-17 16:00:14 -0700788 if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700789 return getShowingAmbientView().getHeight();
790 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800791 return getViewHeight(VISIBLE_TYPE_EXPANDED)
792 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cinekc494e382017-01-31 16:09:23 -0800793 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800794 return getViewHeight(VISIBLE_TYPE_HEADSUP)
795 + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
Kevin38ce6fa2018-10-17 16:00:14 -0700796 } else if (mContractedChild != null) {
797 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
Selim Cinek8d490d42015-04-10 00:05:50 -0700798 }
Kevin38ce6fa2018-10-17 16:00:14 -0700799 return mNotificationMaxHeight;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800800 }
801
802 private int getViewHeight(int visibleType) {
803 View view = getViewForVisibleType(visibleType);
804 int height = view.getHeight();
805 NotificationViewWrapper viewWrapper = getWrapperForView(view);
806 if (viewWrapper != null) {
807 height += viewWrapper.getHeaderTranslation();
808 }
809 return height;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200810 }
811
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200812 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800813 return getMinHeight(false /* likeGroupExpanded */);
814 }
815
816 public int getMinHeight(boolean likeGroupExpanded) {
Kevin38ce6fa2018-10-17 16:00:14 -0700817 if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700818 return getShowingAmbientView().getHeight();
819 } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Kevin38ce6fa2018-10-17 16:00:14 -0700820 return mContractedChild != null
821 ? getViewHeight(VISIBLE_TYPE_CONTRACTED) : mMinContractedHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800822 } else {
823 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700824 }
825 }
826
Adrian Roos6f6e1592017-05-02 16:22:53 -0700827 public View getShowingAmbientView() {
828 View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
829 if (v != null) {
830 return v;
831 } else {
832 return mContractedChild;
833 }
834 }
835
Selim Cinek83bc7832015-10-22 13:26:54 -0700836 private boolean isGroupExpanded() {
837 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200838 }
839
Jorim Jaggibe565df2014-04-28 17:51:23 +0200840 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200841 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200842 updateClipping();
843 }
844
Selim Cineka686b2c2016-10-26 13:58:27 -0700845
846 public void setClipBottomAmount(int clipBottomAmount) {
847 mClipBottomAmount = clipBottomAmount;
848 updateClipping();
849 }
850
Selim Cinek875a3a12016-11-18 17:52:16 -0800851 @Override
852 public void setTranslationY(float translationY) {
853 super.setTranslationY(translationY);
854 updateClipping();
855 }
856
Jorim Jaggibe565df2014-04-28 17:51:23 +0200857 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100858 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800859 int top = (int) (mClipTopAmount - getTranslationY());
Selim Cineked64a142018-02-06 18:06:01 -0800860 int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700861 bottom = Math.max(top, bottom);
862 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100863 setClipBounds(mClipBounds);
864 } else {
865 setClipBounds(null);
866 }
867 }
868
869 public void setClipToActualHeight(boolean clipToActualHeight) {
870 mClipToActualHeight = clipToActualHeight;
871 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200872 }
873
Jorim Jaggi11298832014-05-24 16:18:38 +0200874 private void selectLayout(boolean animate, boolean force) {
875 if (mContractedChild == null) {
876 return;
877 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800878 if (mUserExpanding) {
879 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700880 } else {
881 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700882 boolean changedType = visibleType != mVisibleType;
883 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400884 View visibleView = getViewForVisibleType(visibleType);
885 if (visibleView != null) {
886 visibleView.setVisibility(VISIBLE);
887 transferRemoteInputFocus(visibleType);
888 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700889
Selim Cinek589fd3e2016-04-26 18:17:57 -0700890 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
891 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
892 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
893 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
894 animateToVisibleType(visibleType);
895 } else {
896 updateViewVisibilities(visibleType);
897 }
898 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700899 if (changedType) {
900 focusExpandButtonIfNecessary();
901 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800902 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
903 if (visibleWrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800904 visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
905 getMinContentHeightHint());
Adrian Roos05d3d002017-01-26 15:28:07 -0800906 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700907 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200908 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700909 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700910 }
911
912 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800913 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
914 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
915 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
916 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
917 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700918 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
919 mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800920 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800921 // forceUpdateVisibilities cancels outstanding animations without updating the
922 // mAnimationStartVisibleType. Do so here instead.
923 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800924 }
925
Adrian Roos4320e892017-01-24 12:50:34 -0800926 private void fireExpandedVisibleListenerIfVisible() {
927 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
928 && mExpandedChild.getVisibility() == VISIBLE) {
929 Runnable listener = mExpandedVisibleListener;
930 mExpandedVisibleListener = null;
931 listener.run();
932 }
933 }
934
Adrian Roos0aac04f2016-12-08 15:59:29 -0800935 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
936 if (view == null) {
937 return;
938 }
939 boolean visible = mVisibleType == type
940 || mTransformationStartVisibleType == type;
941 if (!visible) {
942 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700943 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800944 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200945 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200946 }
947
Selim Cinekc3179332016-03-04 14:44:56 -0800948 public void updateBackgroundColor(boolean animate) {
949 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400950 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800951 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
952 }
953
Kenny Guy14d035c2018-05-02 19:10:36 +0100954 public void setBackgroundTintColor(int color) {
955 if (mExpandedSmartReplyView != null) {
956 mExpandedSmartReplyView.setBackgroundTintColor(color);
957 }
958 }
959
Mady Mellorb0a82462016-04-30 17:31:02 -0700960 public int getVisibleType() {
961 return mVisibleType;
962 }
963
964 public int getBackgroundColorForExpansionState() {
965 // When expanding or user locked we want the new type, when collapsing we want
966 // the original type
967 final int visibleType = (mContainingNotification.isGroupExpanded()
968 || mContainingNotification.isUserLocked())
969 ? calculateVisibleType()
970 : getVisibleType();
971 return getBackgroundColor(visibleType);
972 }
973
974 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800975 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
976 int customBackgroundColor = 0;
977 if (currentVisibleWrapper != null) {
978 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
979 }
980 return customBackgroundColor;
981 }
982
Selim Cinek684a4422015-04-15 16:18:39 -0700983 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800984 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
985 mContractedChild, mContractedWrapper);
986 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
987 mExpandedChild, mExpandedWrapper);
988 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
989 mHeadsUpChild, mHeadsUpWrapper);
990 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
991 mSingleLineView, mSingleLineView);
992 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
993 mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700994 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
995 mAmbientSingleLineChild, mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800996 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800997 // updateViewVisibilities cancels outstanding animations without updating the
998 // mAnimationStartVisibleType. Do so here instead.
999 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001000 }
1001
1002 private void updateViewVisibility(int visibleType, int type, View view,
1003 TransformableView wrapper) {
1004 if (view != null) {
1005 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -07001006 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001007 }
1008
Selim Cinek4ffd6362015-12-29 15:12:23 +01001009 private void animateToVisibleType(int visibleType) {
1010 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
1011 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -07001012 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -08001013 shownView.setVisible(true);
1014 return;
1015 }
Adrian Roos599be342016-06-13 14:54:39 -07001016 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001017 shownView.transformFrom(hiddenView);
1018 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
1019 hiddenView.transformTo(shownView, new Runnable() {
1020 @Override
1021 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -08001022 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
1023 hiddenView.setVisible(false);
1024 }
Adrian Roos599be342016-06-13 14:54:39 -07001025 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001026 }
1027 });
Adrian Roos4320e892017-01-24 12:50:34 -08001028 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +02001029 }
1030
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001031 private void transferRemoteInputFocus(int visibleType) {
1032 if (visibleType == VISIBLE_TYPE_HEADSUP
1033 && mHeadsUpRemoteInput != null
1034 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
1035 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
1036 }
1037 if (visibleType == VISIBLE_TYPE_EXPANDED
1038 && mExpandedRemoteInput != null
1039 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
1040 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
1041 }
1042 }
1043
Selim Cinek684a4422015-04-15 16:18:39 -07001044 /**
1045 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +01001046 * @return the corresponding transformable view according to the given visible type
1047 */
1048 private TransformableView getTransformableViewForVisibleType(int visibleType) {
1049 switch (visibleType) {
1050 case VISIBLE_TYPE_EXPANDED:
1051 return mExpandedWrapper;
1052 case VISIBLE_TYPE_HEADSUP:
1053 return mHeadsUpWrapper;
1054 case VISIBLE_TYPE_SINGLELINE:
1055 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001056 case VISIBLE_TYPE_AMBIENT:
1057 return mAmbientWrapper;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001058 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1059 return mAmbientSingleLineChild;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001060 default:
1061 return mContractedWrapper;
1062 }
1063 }
1064
1065 /**
1066 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -07001067 * @return the corresponding view according to the given visible type
1068 */
1069 private View getViewForVisibleType(int visibleType) {
1070 switch (visibleType) {
1071 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -07001072 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001073 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -07001074 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -07001075 case VISIBLE_TYPE_SINGLELINE:
1076 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001077 case VISIBLE_TYPE_AMBIENT:
1078 return mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001079 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1080 return mAmbientSingleLineChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001081 default:
1082 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -07001083 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001084 }
1085
Selim Cinek131f1a42017-06-05 17:50:19 -07001086 public NotificationViewWrapper getVisibleWrapper(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -08001087 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -08001088 case VISIBLE_TYPE_EXPANDED:
1089 return mExpandedWrapper;
1090 case VISIBLE_TYPE_HEADSUP:
1091 return mHeadsUpWrapper;
1092 case VISIBLE_TYPE_CONTRACTED:
1093 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001094 case VISIBLE_TYPE_AMBIENT:
1095 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -08001096 default:
1097 return null;
1098 }
1099 }
1100
Selim Cinek684a4422015-04-15 16:18:39 -07001101 /**
1102 * @return one of the static enum types in this view, calculated form the current state
1103 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001104 public int calculateVisibleType() {
Kevina97ea052018-09-11 13:53:18 -07001105 if (mContainingNotification.isOnAmbient()) {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001106 if (mIsChildInGroup && mAmbientSingleLineChild != null) {
1107 return VISIBLE_TYPE_AMBIENT_SINGLELINE;
1108 } else if (mAmbientChild != null) {
1109 return VISIBLE_TYPE_AMBIENT;
1110 } else {
1111 return VISIBLE_TYPE_CONTRACTED;
1112 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001113 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001114 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -08001115 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001116 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001117 ? mContainingNotification.getMaxContentHeight()
1118 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -08001119 if (height == 0) {
1120 height = mContentHeight;
1121 }
Selim Cinek42357e02016-02-24 18:48:01 -08001122 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -08001123 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -07001124 ? VISIBLE_TYPE_SINGLELINE
1125 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001126 return mTransformationStartVisibleType == collapsedVisualType
1127 ? expandedVisualType
1128 : collapsedVisualType;
1129 }
Selim Cinek51d94912016-03-02 15:34:28 -08001130 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
1131 int viewHeight = mContentHeight;
1132 if (intrinsicHeight != 0) {
1133 // the intrinsicHeight might be 0 because it was just reset.
1134 viewHeight = Math.min(mContentHeight, intrinsicHeight);
1135 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001136 return getVisualTypeForHeight(viewHeight);
1137 }
1138
1139 private int getVisualTypeForHeight(float viewHeight) {
1140 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001141 if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
Adrian Roos777ef562015-12-01 17:37:14 -08001142 return VISIBLE_TYPE_EXPANDED;
1143 }
Selim Cinek414ad332017-02-24 19:06:12 -08001144 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001145 return VISIBLE_TYPE_SINGLELINE;
1146 }
Adrian Roos777ef562015-12-01 17:37:14 -08001147
Selim Cinekc494e382017-01-31 16:09:23 -08001148 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
1149 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001150 if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -07001151 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -07001152 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001153 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001154 }
1155 } else {
Kevin38ce6fa2018-10-17 16:00:14 -07001156 if (noExpandedChild || (mContractedChild != null
1157 && viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
Selim Cinek6ac3fa82016-05-10 17:11:54 -04001158 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001159 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -07001160 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001161 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001162 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001163 }
1164 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001165 }
1166
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001167 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001168 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001169 }
John Spurlocke15452b2014-08-21 09:44:39 -04001170
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001171 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -08001172 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -08001173 return;
1174 }
John Spurlocke15452b2014-08-21 09:44:39 -04001175 mDark = dark;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001176 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -04001177 }
1178
Selim Cinek8d490d42015-04-10 00:05:50 -07001179 public void setHeadsUp(boolean headsUp) {
1180 mIsHeadsUp = headsUp;
1181 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -08001182 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -07001183 }
1184
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +02001185 @Override
1186 public boolean hasOverlappingRendering() {
1187
1188 // This is not really true, but good enough when fading from the contracted to the expanded
1189 // layout, and saves us some layers.
1190 return false;
1191 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001192
Selim Cinek1a48bab2017-02-17 19:38:40 -08001193 public void setLegacy(boolean legacy) {
1194 mLegacy = legacy;
1195 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -08001196 }
1197
Selim Cinek1a48bab2017-02-17 19:38:40 -08001198 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -08001199 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001200 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001201 }
1202 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001203 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001204 }
1205 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001206 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001207 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001208 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001209
Selim Cinek83bc7832015-10-22 13:26:54 -07001210 public void setIsChildInGroup(boolean isChildInGroup) {
1211 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001212 if (mContractedChild != null) {
1213 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1214 }
1215 if (mExpandedChild != null) {
1216 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1217 }
1218 if (mHeadsUpChild != null) {
1219 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1220 }
1221 if (mAmbientChild != null) {
1222 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1223 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001224 updateAllSingleLineViews();
Selim Cinek83bc7832015-10-22 13:26:54 -07001225 }
1226
Adrian Roosb88b1a12015-12-09 18:51:05 -08001227 public void onNotificationUpdated(NotificationData.Entry entry) {
1228 mStatusBarNotification = entry.notification;
Kevind5022f92018-10-08 18:30:26 -07001229 mOnContentViewInactiveListeners.clear();
Selim Cinek860b6da2015-12-16 19:02:19 -08001230 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001231 updateAllSingleLineViews();
Evan Laird94492852018-10-25 13:43:01 -04001232 ExpandableNotificationRow row = entry.getRow();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001233 if (mContractedChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001234 mContractedWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001235 }
1236 if (mExpandedChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001237 mExpandedWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001238 }
1239 if (mHeadsUpChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001240 mHeadsUpWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001241 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001242 if (mAmbientChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001243 mAmbientWrapper.onContentUpdated(row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001244 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001245 applyRemoteInputAndSmartReply(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001246 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001247 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001248 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001249 mPreviousExpandedRemoteInputIntent = null;
1250 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001251 }
1252
Adrian Roos6f6e1592017-05-02 16:22:53 -07001253 private void updateAllSingleLineViews() {
1254 updateSingleLineView();
1255 updateAmbientSingleLineView();
1256 }
Tony Mak638430e2018-10-08 19:19:10 +01001257
Selim Cinek83bc7832015-10-22 13:26:54 -07001258 private void updateSingleLineView() {
1259 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001260 boolean isNewView = mSingleLineView == null;
Selim Cinekc897bd32016-03-18 17:32:31 -07001261 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001262 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001263 if (isNewView) {
1264 updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
1265 mSingleLineView, mSingleLineView);
1266 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001267 } else if (mSingleLineView != null) {
1268 removeView(mSingleLineView);
1269 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001270 }
1271 }
1272
Adrian Roos6f6e1592017-05-02 16:22:53 -07001273 private void updateAmbientSingleLineView() {
1274 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001275 boolean isNewView = mAmbientSingleLineChild == null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001276 mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
1277 mAmbientSingleLineChild, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001278 if (isNewView) {
1279 updateViewVisibility(mVisibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
1280 mAmbientSingleLineChild, mAmbientSingleLineChild);
1281 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001282 } else if (mAmbientSingleLineChild != null) {
1283 removeView(mAmbientSingleLineChild);
1284 mAmbientSingleLineChild = null;
1285 }
1286 }
1287
Petr Cermak7997d7a2018-01-18 15:52:10 +00001288 private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001289 if (mRemoteInputController == null) {
1290 return;
1291 }
1292
Tony Mak29996702018-11-26 16:23:34 +00001293 SmartRepliesAndActions smartRepliesAndActions =
1294 chooseSmartRepliesAndActions(mSmartReplyConstants, entry);
Tony Mak638430e2018-10-08 19:19:10 +01001295
Tony Mak29996702018-11-26 16:23:34 +00001296 applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput);
Gustav Senntoneab53682018-11-01 16:30:23 +00001297 applySmartReplyView(smartRepliesAndActions, entry);
1298 }
Tony Mak638430e2018-10-08 19:19:10 +01001299
Gustav Senntoneab53682018-11-01 16:30:23 +00001300 /**
1301 * Chose what smart replies and smart actions to display. App generated suggestions take
1302 * precedence. So if the app provides any smart replies, we don't show any
1303 * replies or actions generated by the NotificationAssistantService (NAS), and if the app
1304 * provides any smart actions we also don't show any NAS-generated replies or actions.
1305 */
1306 @VisibleForTesting
1307 static SmartRepliesAndActions chooseSmartRepliesAndActions(
1308 SmartReplyConstants smartReplyConstants,
1309 final NotificationData.Entry entry) {
1310 boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
1311 && (!smartReplyConstants.requiresTargetingP()
Tony Mak638430e2018-10-08 19:19:10 +01001312 || entry.targetSdk >= Build.VERSION_CODES.P));
Petr Cermak7997d7a2018-01-18 15:52:10 +00001313
Gustav Senntoneab53682018-11-01 16:30:23 +00001314 Notification notification = entry.notification.getNotification();
1315 Pair<RemoteInput, Notification.Action> remoteInputActionPair =
1316 notification.findRemoteInputActionPair(false /* freeform */);
1317 Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
1318 notification.findRemoteInputActionPair(true /* freeform */);
1319
1320 boolean appGeneratedSmartRepliesExist =
1321 enableAppGeneratedSmartReplies
1322 && remoteInputActionPair != null
Tony Mak29996702018-11-26 16:23:34 +00001323 && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())
1324 && remoteInputActionPair.second.actionIntent != null;
Gustav Senntoneab53682018-11-01 16:30:23 +00001325
1326 List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
1327 boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
1328
Tony Mak29996702018-11-26 16:23:34 +00001329 SmartReplyView.SmartReplies smartReplies = null;
1330 SmartReplyView.SmartActions smartActions = null;
Gustav Senntoneab53682018-11-01 16:30:23 +00001331 if (appGeneratedSmartRepliesExist) {
Tony Mak29996702018-11-26 16:23:34 +00001332 smartReplies = new SmartReplyView.SmartReplies(
Gustav Senntoneab53682018-11-01 16:30:23 +00001333 remoteInputActionPair.first.getChoices(),
Tony Mak29996702018-11-26 16:23:34 +00001334 remoteInputActionPair.first,
1335 remoteInputActionPair.second.actionIntent,
1336 false /* fromAssistant */);
Gustav Senntoneab53682018-11-01 16:30:23 +00001337 }
Tony Mak29996702018-11-26 16:23:34 +00001338 if (appGeneratedSmartActionsExist) {
1339 smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions,
1340 false /* fromAssistant */);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001341 }
Tony Mak29996702018-11-26 16:23:34 +00001342 // Apps didn't provide any smart replies / actions, use those from NAS (if any).
1343 if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
1344 boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
1345 && freeformRemoteInputActionPair != null
1346 && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
1347 && freeformRemoteInputActionPair.second.actionIntent != null;
1348 if (useGeneratedReplies) {
1349 smartReplies = new SmartReplyView.SmartReplies(
1350 entry.smartReplies,
1351 freeformRemoteInputActionPair.first,
1352 freeformRemoteInputActionPair.second.actionIntent,
1353 true /* fromAssistant */);
1354 }
1355 boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
1356 && notification.getAllowSystemGeneratedContextualActions();
1357 if (useSmartActions) {
1358 smartActions = new SmartReplyView.SmartActions(
1359 entry.systemGeneratedSmartActions, true /* fromAssistant */);
1360 }
Gustav Senntoneab53682018-11-01 16:30:23 +00001361 }
Tony Mak29996702018-11-26 16:23:34 +00001362 return new SmartRepliesAndActions(
1363 smartReplies, smartActions, freeformRemoteInputActionPair != null);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001364 }
1365
Tony Mak638430e2018-10-08 19:19:10 +01001366 private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001367 View bigContentView = mExpandedChild;
1368 if (bigContentView != null) {
Tony Mak638430e2018-10-08 19:19:10 +01001369 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001370 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1371 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001372 } else {
1373 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001374 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001375 if (mCachedExpandedRemoteInput != null
1376 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1377 // We had a cached remote input but didn't reuse it. Clean up required.
1378 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1379 }
1380 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001381
Adrian Roosb88b1a12015-12-09 18:51:05 -08001382 View headsUpContentView = mHeadsUpChild;
1383 if (headsUpContentView != null) {
Tony Mak638430e2018-10-08 19:19:10 +01001384 mHeadsUpRemoteInput = applyRemoteInput(
1385 headsUpContentView, entry, hasFreeformRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001386 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001387 } else {
1388 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001389 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001390 if (mCachedHeadsUpRemoteInput != null
1391 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1392 // We had a cached remote input but didn't reuse it. Clean up required.
1393 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1394 }
1395 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001396 }
1397
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001398 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001399 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001400 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001401 View actionContainerCandidate = view.findViewById(
1402 com.android.internal.R.id.actions_container);
1403 if (actionContainerCandidate instanceof FrameLayout) {
1404 RemoteInputView existing = (RemoteInputView)
1405 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1406
1407 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001408 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001409 }
1410
1411 if (existing == null && hasRemoteInput) {
1412 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001413 if (cachedView == null) {
1414 RemoteInputView riv = RemoteInputView.inflate(
1415 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001416
Adrian Roos7813dd72016-09-23 17:12:17 -07001417 riv.setVisibility(View.INVISIBLE);
1418 actionContainer.addView(riv, new LayoutParams(
1419 ViewGroup.LayoutParams.MATCH_PARENT,
1420 ViewGroup.LayoutParams.MATCH_PARENT)
1421 );
1422 existing = riv;
1423 } else {
1424 actionContainer.addView(cachedView);
1425 cachedView.dispatchFinishTemporaryDetach();
1426 cachedView.requestFocus();
1427 existing = cachedView;
1428 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001429 }
1430 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001431 int color = entry.notification.getNotification().color;
1432 if (color == Notification.COLOR_DEFAULT) {
1433 color = mContext.getColor(R.color.default_remote_input_background);
1434 }
Lucas Dupina291d192018-06-07 13:59:42 -07001435 existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001436 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001437 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001438
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001439 existing.setWrapper(wrapper);
Selim Cineked64a142018-02-06 18:06:01 -08001440 existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001441
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001442 if (existingPendingIntent != null || existing.isActive()) {
1443 // The current action could be gone, or the pending intent no longer valid.
1444 // If we find a matching action in the new notification, focus, otherwise close.
1445 Notification.Action[] actions = entry.notification.getNotification().actions;
1446 if (existingPendingIntent != null) {
1447 existing.setPendingIntent(existingPendingIntent);
1448 }
1449 if (existing.updatePendingIntentFromActions(actions)) {
1450 if (!existing.isActive()) {
1451 existing.focus();
1452 }
1453 } else {
1454 if (existing.isActive()) {
1455 existing.close();
1456 }
1457 }
1458 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001459 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001460 return existing;
1461 }
1462 return null;
1463 }
1464
Gustav Senntoneab53682018-11-01 16:30:23 +00001465 private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
1466 NotificationData.Entry entry) {
Kenny Guy23991102018-04-05 21:18:38 +01001467 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +01001468 mExpandedSmartReplyView =
Gustav Senntoneab53682018-11-01 16:30:23 +00001469 applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
Tony Mak29996702018-11-26 16:23:34 +00001470 if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
1471 mSmartReplyController.smartRepliesAdded(
1472 entry, smartRepliesAndActions.smartReplies.choices.length);
Kenny Guy23991102018-04-05 21:18:38 +01001473 }
1474 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001475 }
1476
Gustav Senntoneab53682018-11-01 16:30:23 +00001477 private SmartReplyView applySmartReplyView(View view,
1478 SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001479 View smartReplyContainerCandidate = view.findViewById(
1480 com.android.internal.R.id.smart_reply_container);
1481 if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
1482 return null;
1483 }
1484 LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
Gustav Senntoneab53682018-11-01 16:30:23 +00001485 // If there are no smart replies and no smart actions - early out.
Tony Mak29996702018-11-26 16:23:34 +00001486 if (smartRepliesAndActions.smartReplies == null
1487 && smartRepliesAndActions.smartActions == null) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001488 smartReplyContainer.setVisibility(View.GONE);
1489 return null;
1490 }
Kenny Guya0f6de82018-04-06 16:20:16 +01001491 // If we are showing the spinner we don't want to add the buttons.
1492 boolean showingSpinner = entry.notification.getNotification()
1493 .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
1494 if (showingSpinner) {
1495 smartReplyContainer.setVisibility(View.GONE);
1496 return null;
1497 }
Kenny Guy8cc15d22018-05-09 09:50:55 +01001498 // If we are keeping the notification around while sending we don't want to add the buttons.
1499 boolean hideSmartReplies = entry.notification.getNotification()
1500 .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false);
1501 if (hideSmartReplies) {
1502 smartReplyContainer.setVisibility(View.GONE);
1503 return null;
1504 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001505 SmartReplyView smartReplyView = null;
1506 if (smartReplyContainer.getChildCount() == 0) {
1507 smartReplyView = SmartReplyView.inflate(mContext, smartReplyContainer);
1508 smartReplyContainer.addView(smartReplyView);
1509 } else if (smartReplyContainer.getChildCount() == 1) {
1510 View child = smartReplyContainer.getChildAt(0);
1511 if (child instanceof SmartReplyView) {
1512 smartReplyView = (SmartReplyView) child;
1513 }
1514 }
1515 if (smartReplyView != null) {
Gustav Senntoneab53682018-11-01 16:30:23 +00001516 smartReplyView.resetSmartSuggestions(smartReplyContainer);
Tony Mak29996702018-11-26 16:23:34 +00001517 if (smartRepliesAndActions.smartReplies != null) {
1518 smartReplyView.addRepliesFromRemoteInput(
1519 smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
1520 }
1521 if (smartRepliesAndActions.smartActions != null) {
Tony Mak7d4b3a52018-11-27 17:29:36 +00001522 smartReplyView.addSmartActions(
1523 smartRepliesAndActions.smartActions, mSmartReplyController, entry);
Tony Mak29996702018-11-26 16:23:34 +00001524 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001525 smartReplyContainer.setVisibility(View.VISIBLE);
1526 }
1527 return smartReplyView;
1528 }
1529
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001530 public void closeRemoteInput() {
1531 if (mHeadsUpRemoteInput != null) {
1532 mHeadsUpRemoteInput.close();
1533 }
1534 if (mExpandedRemoteInput != null) {
1535 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001536 }
1537 }
1538
Selim Cinek83bc7832015-10-22 13:26:54 -07001539 public void setGroupManager(NotificationGroupManager groupManager) {
1540 mGroupManager = groupManager;
1541 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001542
Adrian Roosb88b1a12015-12-09 18:51:05 -08001543 public void setRemoteInputController(RemoteInputController r) {
1544 mRemoteInputController = r;
1545 }
1546
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001547 public void setExpandClickListener(OnClickListener expandClickListener) {
1548 mExpandClickListener = expandClickListener;
1549 }
1550
1551 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001552 mExpandable = expandable;
1553 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001554 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001555 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1556 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001557 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001558 expandable = false;
1559 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001560 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001561 expandable = false;
1562 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001563 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001564 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001565 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001566 }
1567 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001568 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001569 }
1570 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001571 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001572 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001573 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001574 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001575
1576 public NotificationHeaderView getNotificationHeader() {
1577 NotificationHeaderView header = null;
1578 if (mContractedChild != null) {
1579 header = mContractedWrapper.getNotificationHeader();
1580 }
1581 if (header == null && mExpandedChild != null) {
1582 header = mExpandedWrapper.getNotificationHeader();
1583 }
1584 if (header == null && mHeadsUpChild != null) {
1585 header = mHeadsUpWrapper.getNotificationHeader();
1586 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001587 if (header == null && mAmbientChild != null) {
1588 header = mAmbientWrapper.getNotificationHeader();
1589 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001590 return header;
1591 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001592
Julia Reynoldsfc640012018-02-21 12:25:27 -05001593 public void showAppOpsIcons(ArraySet<Integer> activeOps) {
1594 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
1595 mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1596 }
1597 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
1598 mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1599 }
1600 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
1601 mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1602 }
1603 }
Selim Cinekb26afa32017-06-29 10:28:17 +02001604
Gus Prevasa3226492018-10-23 11:10:09 -04001605 /** Sets whether the notification being displayed audibly alerted the user. */
1606 public void setAudiblyAlerted(boolean audiblyAlerted) {
1607 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
1608 mContractedWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
1609 }
1610 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
1611 mExpandedWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
1612 }
1613 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
1614 mHeadsUpWrapper.getNotificationHeader().setAudiblyAlerted(audiblyAlerted);
1615 }
1616 }
1617
Selim Cinekb26afa32017-06-29 10:28:17 +02001618 public NotificationHeaderView getContractedNotificationHeader() {
1619 if (mContractedChild != null) {
1620 return mContractedWrapper.getNotificationHeader();
1621 }
1622 return null;
1623 }
1624
Selim Cinek34eda5e2016-02-18 17:10:43 -08001625 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001626 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001627 return wrapper == null ? null : wrapper.getNotificationHeader();
1628 }
1629
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001630 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1631 mContainingNotification = containingNotification;
1632 }
1633
1634 public void requestSelectLayout(boolean needsAnimation) {
1635 selectLayout(needsAnimation, false);
1636 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001637
1638 public void reInflateViews() {
1639 if (mIsChildInGroup && mSingleLineView != null) {
1640 removeView(mSingleLineView);
1641 mSingleLineView = null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001642 updateAllSingleLineViews();
Selim Cinekde33a4a2016-02-11 16:43:41 -08001643 }
1644 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001645
1646 public void setUserExpanding(boolean userExpanding) {
1647 mUserExpanding = userExpanding;
1648 if (userExpanding) {
1649 mTransformationStartVisibleType = mVisibleType;
1650 } else {
1651 mTransformationStartVisibleType = UNDEFINED;
1652 mVisibleType = calculateVisibleType();
1653 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001654 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001655 }
1656 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001657
1658 /**
1659 * Set by how much the single line view should be indented. Used when a overflow indicator is
1660 * present and only during measuring
1661 */
1662 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1663 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1664 mSingleLineWidthIndention = singleLineWidthIndention;
1665 mContainingNotification.forceLayout();
1666 forceLayout();
1667 }
1668 }
1669
1670 public HybridNotificationView getSingleLineView() {
1671 return mSingleLineView;
1672 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001673
1674 public void setRemoved() {
1675 if (mExpandedRemoteInput != null) {
1676 mExpandedRemoteInput.setRemoved();
1677 }
1678 if (mHeadsUpRemoteInput != null) {
1679 mHeadsUpRemoteInput.setRemoved();
1680 }
1681 }
Adrian Roos599be342016-06-13 14:54:39 -07001682
1683 public void setContentHeightAnimating(boolean animating) {
1684 if (!animating) {
1685 mContentHeightAtAnimationStart = UNDEFINED;
1686 }
1687 }
Selim Cineke9bad242016-06-15 11:46:37 -07001688
Adrian Roose18033c2017-01-17 15:22:49 -08001689 @VisibleForTesting
1690 boolean isAnimatingVisibleType() {
1691 return mAnimationStartVisibleType != UNDEFINED;
1692 }
1693
Selim Cinekcafa87f2016-10-26 17:00:17 -07001694 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1695 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001696 selectLayout(false /* animate */, true /* force */);
1697 }
1698
Selim Cineke9bad242016-06-15 11:46:37 -07001699 public void setFocusOnVisibilityChange() {
1700 mFocusOnVisibilityChange = true;
1701 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001702
1703 public void setIconsVisible(boolean iconsVisible) {
1704 mIconsVisible = iconsVisible;
1705 updateIconVisibilities();
1706 }
1707
1708 private void updateIconVisibilities() {
1709 if (mContractedWrapper != null) {
1710 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1711 if (header != null) {
1712 header.getIcon().setForceHidden(!mIconsVisible);
1713 }
1714 }
1715 if (mHeadsUpWrapper != null) {
1716 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1717 if (header != null) {
1718 header.getIcon().setForceHidden(!mIconsVisible);
1719 }
1720 }
1721 if (mExpandedWrapper != null) {
1722 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1723 if (header != null) {
1724 header.getIcon().setForceHidden(!mIconsVisible);
1725 }
1726 }
1727 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001728
Adrian Roos4320e892017-01-24 12:50:34 -08001729 @Override
1730 public void onVisibilityAggregated(boolean isVisible) {
1731 super.onVisibilityAggregated(isVisible);
1732 if (isVisible) {
1733 fireExpandedVisibleListenerIfVisible();
1734 }
1735 }
1736
1737 /**
1738 * Sets a one-shot listener for when the expanded view becomes visible.
1739 *
1740 * This will fire the listener immediately if the expanded view is already visible.
1741 */
1742 public void setOnExpandedVisibleListener(Runnable r) {
1743 mExpandedVisibleListener = r;
1744 fireExpandedVisibleListenerIfVisible();
1745 }
1746
Kevind5022f92018-10-08 18:30:26 -07001747 /**
1748 * Set a one-shot listener to run when a given content view becomes inactive.
1749 *
1750 * @param visibleType visible type corresponding to the content view to listen
1751 * @param listener runnable to run once when the content view becomes inactive
1752 */
1753 public void performWhenContentInactive(int visibleType, Runnable listener) {
1754 View view = getViewForVisibleType(visibleType);
1755 // View is already inactive
1756 if (view == null || isContentViewInactive(visibleType)) {
1757 listener.run();
1758 return;
1759 }
1760 mOnContentViewInactiveListeners.put(view, listener);
1761 }
1762
1763 /**
1764 * Whether or not the content view is inactive. This means it should not be visible
1765 * or the showing content as removing it would cause visual jank.
1766 *
1767 * @param visibleType visible type corresponding to the content view to be removed
1768 * @return true if the content view is inactive, false otherwise
1769 */
1770 public boolean isContentViewInactive(int visibleType) {
1771 View view = getViewForVisibleType(visibleType);
1772 return isContentViewInactive(view);
1773 }
1774
1775 /**
1776 * Whether or not the content view is inactive.
1777 *
1778 * @param view view to see if its inactive
1779 * @return true if the view is inactive, false o/w
1780 */
1781 private boolean isContentViewInactive(View view) {
1782 if (view == null) {
1783 return true;
1784 }
Kevin38ce6fa2018-10-17 16:00:14 -07001785 return !isShown()
1786 || (view.getVisibility() != VISIBLE && getViewForVisibleType(mVisibleType) != view);
Kevind5022f92018-10-08 18:30:26 -07001787 }
1788
1789 @Override
1790 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
1791 super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
1792 if (isContentViewInactive(child)) {
1793 Runnable listener = mOnContentViewInactiveListeners.remove(child);
1794 if (listener != null) {
1795 listener.run();
1796 }
1797 }
1798 }
1799
Selim Cinek6743c0b2017-01-18 18:24:01 -08001800 public void setIsLowPriority(boolean isLowPriority) {
1801 mIsLowPriority = isLowPriority;
1802 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001803
1804 public boolean isDimmable() {
Kevin38ce6fa2018-10-17 16:00:14 -07001805 return mContractedWrapper != null && mContractedWrapper.isDimmable();
Selim Cinek63edaf22017-04-24 22:18:48 -07001806 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001807
1808 /**
1809 * Should a single click be disallowed on this view when on the keyguard?
1810 */
1811 public boolean disallowSingleClick(float x, float y) {
1812 NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
1813 if (visibleWrapper != null) {
1814 return visibleWrapper.disallowSingleClick(x, y);
1815 }
1816 return false;
1817 }
Selim Cinek515b2032017-11-15 10:20:19 -08001818
Selim Cinek86bfcee2018-01-17 11:00:47 -08001819 public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
1820 boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001821 if (mUserExpanding) {
Selim Cinek86bfcee2018-01-17 11:00:47 -08001822 needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
1823 bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001824 }
1825 return needsPaddings;
1826 }
1827
Selim Cinek86bfcee2018-01-17 11:00:47 -08001828 private boolean shouldClipToRounding(int visibleType, boolean topRounded,
1829 boolean bottomRounded) {
Selim Cinek515b2032017-11-15 10:20:19 -08001830 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
1831 if (visibleWrapper == null) {
1832 return false;
1833 }
Selim Cinek86bfcee2018-01-17 11:00:47 -08001834 return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001835 }
Selim Cinekde4de0e2018-01-24 16:21:07 -08001836
1837 public CharSequence getActiveRemoteInputText() {
1838 if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
1839 return mExpandedRemoteInput.getText();
1840 }
1841 if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
1842 return mHeadsUpRemoteInput.getText();
1843 }
1844 return null;
1845 }
Selim Cineked64a142018-02-06 18:06:01 -08001846
Selim Cineka77d2c52018-04-09 10:44:52 -07001847 @Override
1848 public boolean dispatchTouchEvent(MotionEvent ev) {
1849 float y = ev.getY();
1850 // We still want to distribute touch events to the remote input even if it's outside the
1851 // view boundary. We're therefore manually dispatching these events to the remote view
1852 RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
1853 if (riv != null && riv.getVisibility() == VISIBLE) {
1854 int inputStart = mUnrestrictedContentHeight - riv.getHeight();
1855 if (y <= mUnrestrictedContentHeight && y >= inputStart) {
1856 ev.offsetLocation(0, -inputStart);
1857 return riv.dispatchTouchEvent(ev);
1858 }
1859 }
1860 return super.dispatchTouchEvent(ev);
1861 }
1862
1863 /**
1864 * Overridden to make sure touches to the reply action bar actually go through to this view
1865 */
1866 @Override
1867 public boolean pointInView(float localX, float localY, float slop) {
1868 float top = mClipTopAmount;
1869 float bottom = mUnrestrictedContentHeight;
1870 return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
1871 localY < (bottom + slop);
1872 }
1873
1874 private RemoteInputView getRemoteInputForView(View child) {
1875 if (child == mExpandedChild) {
1876 return mExpandedRemoteInput;
1877 } else if (child == mHeadsUpChild) {
1878 return mHeadsUpRemoteInput;
1879 }
1880 return null;
1881 }
1882
Selim Cineked64a142018-02-06 18:06:01 -08001883 public int getExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001884 int viewType = VISIBLE_TYPE_EXPANDED;
1885 if (mExpandedChild == null) {
1886 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001887 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001888 return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001889 }
1890
1891 public int getHeadsUpHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001892 int viewType = VISIBLE_TYPE_HEADSUP;
1893 if (mHeadsUpChild == null) {
1894 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001895 }
Selim Cinek09b442e2018-05-04 15:46:18 -07001896 // The headsUp remote input quickly switches to the expanded one, so lets also include that
1897 // one
1898 return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
1899 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001900 }
1901
1902 public void setRemoteInputVisible(boolean remoteInputVisible) {
1903 mRemoteInputVisible = remoteInputVisible;
1904 setClipChildren(!remoteInputVisible);
1905 }
1906
1907 @Override
1908 public void setClipChildren(boolean clipChildren) {
1909 clipChildren = clipChildren && !mRemoteInputVisible;
1910 super.setClipChildren(clipChildren);
1911 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001912
1913 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1914 if (mContractedWrapper != null) {
1915 mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1916 }
1917 if (mHeadsUpWrapper != null) {
1918 mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1919 }
1920 if (mExpandedWrapper != null) {
1921 mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1922 }
1923 }
Selim Cinek85a8f9f2018-11-21 13:58:27 -08001924
1925 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1926 pw.print(" ");
1927 pw.print("contentView visibility: " + getVisibility());
1928 pw.print(", alpha: " + getAlpha());
1929 pw.print(", clipBounds: " + getClipBounds());
1930 pw.print(", contentHeight: " + mContentHeight);
1931 pw.print(", visibleType: " + mVisibleType);
1932 View view = getViewForVisibleType(mVisibleType);
1933 pw.print(", visibleView ");
1934 if (view != null) {
1935 pw.print(" visibility: " + view.getVisibility());
1936 pw.print(", alpha: " + view.getAlpha());
1937 pw.print(", clipBounds: " + view.getClipBounds());
1938 } else {
1939 pw.print("null");
1940 }
1941 pw.println();
1942 }
Tony Mak29996702018-11-26 16:23:34 +00001943
1944 @VisibleForTesting
1945 static class SmartRepliesAndActions {
1946 @Nullable
1947 public final SmartReplyView.SmartReplies smartReplies;
1948 @Nullable
1949 public final SmartReplyView.SmartActions smartActions;
1950 public final boolean hasFreeformRemoteInput;
1951
1952 SmartRepliesAndActions(
1953 @Nullable SmartReplyView.SmartReplies smartReplies,
1954 @Nullable SmartReplyView.SmartActions smartActions,
1955 boolean hasFreeformRemoteInput) {
1956 this.smartReplies = smartReplies;
1957 this.smartActions = smartActions;
1958 this.hasFreeformRemoteInput = hasFreeformRemoteInput;
1959 }
1960 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001961}