blob: c161da348c28f32b716b9db763c48e272d408e51 [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;
Selim Cinekc3179332016-03-04 14:44:56 -080049import com.android.systemui.statusbar.notification.NotificationUtils;
Ned Burnsf81c4c42019-01-07 14:10:43 -050050import com.android.systemui.statusbar.notification.collection.NotificationEntry;
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;
Gustav Senntond0e84532018-12-03 16:48:36 +000094 private SmartReplyView mHeadsUpSmartReplyView;
Kenny Guya0f6de82018-04-06 16:20:16 +010095 private SmartReplyController mSmartReplyController;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070096
Jorim Jaggi4e857f42014-11-17 19:14:04 +010097 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070098 private NotificationViewWrapper mExpandedWrapper;
99 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800100 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -0700101 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200102 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +0100103 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -0700104 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -0400105 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100106 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -0700107 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800108 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -0700109 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -0800110 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800111 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800112 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800113 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700114 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700115 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800116 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -0800117 private Runnable mExpandedVisibleListener;
Kevind5022f92018-10-08 18:30:26 -0700118 /**
119 * List of listeners for when content views become inactive (i.e. not the showing view).
120 */
121 private final ArrayMap<View, Runnable> mOnContentViewInactiveListeners = new ArrayMap<>();
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700122
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700123 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100124 = new ViewTreeObserver.OnPreDrawListener() {
125 @Override
126 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -0800127 // We need to post since we don't want the notification to animate on the very first
128 // frame
129 post(new Runnable() {
130 @Override
131 public void run() {
132 mAnimate = true;
133 }
134 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100135 getViewTreeObserver().removeOnPreDrawListener(this);
136 return true;
137 }
138 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200139
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800140 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800141 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800142 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100143 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800144 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700145 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800146 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700147 /** The visible type at the start of an animation driven transformation */
148 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800149 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700150 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700151 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700152 private PendingIntent mPreviousExpandedRemoteInputIntent;
153 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700154 private RemoteInputView mCachedExpandedRemoteInput;
155 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800156
Adrian Roos599be342016-06-13 14:54:39 -0700157 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700158 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700159 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700160 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700161 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800162 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800163 private boolean mIsContentExpandable;
Selim Cineked64a142018-02-06 18:06:01 -0800164 private boolean mRemoteInputVisible;
165 private int mUnrestrictedContentHeight;
Adrian Roos599be342016-06-13 14:54:39 -0700166
167
Jorim Jaggibe565df2014-04-28 17:51:23 +0200168 public NotificationContentView(Context context, AttributeSet attrs) {
169 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700170 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Petr Cermak10011fa2018-02-05 19:00:54 +0000171 mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
Kenny Guya0f6de82018-04-06 16:20:16 +0100172 mSmartReplyController = Dependency.get(SmartReplyController.class);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800173 initView();
174 }
175
176 public void initView() {
Selim Cinek860b6da2015-12-16 19:02:19 -0800177 mMinContractedHeight = getResources().getDimensionPixelSize(
178 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800179 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
180 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200181 }
182
Adrian Roos0aac04f2016-12-08 15:59:29 -0800183 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
184 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800185 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800186 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800187 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800188 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800189 }
190
Jorim Jaggibe565df2014-04-28 17:51:23 +0200191 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700192 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
193 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
194 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
195 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700196 int maxSize = Integer.MAX_VALUE / 2;
Selim Cinekc897bd32016-03-18 17:32:31 -0700197 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700198 if (hasFixedHeight || isHeightLimited) {
199 maxSize = MeasureSpec.getSize(heightMeasureSpec);
200 }
201 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800202 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +0100203 int notificationMaxHeight = mNotificationMaxHeight;
204 if (mExpandedSmartReplyView != null) {
205 notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
206 }
Selim Cinekbee4e072018-05-21 22:06:43 -0700207 notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700208 int size = notificationMaxHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800209 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800210 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800211 if (layoutParams.height >= 0) {
212 // An actual height is set
Selim Cinekbe570ce2018-05-11 12:29:52 -0700213 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800214 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800215 }
Selim Cinekbe570ce2018-05-11 12:29:52 -0700216 int spec = MeasureSpec.makeMeasureSpec(size, useExactly
Selim Cinek6743c0b2017-01-18 18:24:01 -0800217 ? MeasureSpec.EXACTLY
218 : MeasureSpec.AT_MOST);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800219 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800220 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
221 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700222 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800223 int heightSpec;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700224 int size = mSmallHeight;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800225 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
226 boolean useExactly = false;
227 if (layoutParams.height >= 0) {
228 // An actual height is set
229 size = Math.min(size, layoutParams.height);
230 useExactly = true;
231 }
232 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800233 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
234 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800235 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800236 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800237 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800238 int measuredHeight = mContractedChild.getMeasuredHeight();
239 if (measuredHeight < mMinContractedHeight) {
240 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800241 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800242 }
243 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800244 if (updateContractedHeaderWidth()) {
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800245 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700246 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700247 if (mExpandedChild != null
248 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
249 // the Expanded child is smaller then the collapsed. Let's remeasure it.
250 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
251 MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800252 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek1ba41d12016-04-21 16:14:46 -0700253 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700254 }
255 if (mHeadsUpChild != null) {
Selim Cinek396caca2018-04-10 17:46:46 -0700256 int maxHeight = mHeadsUpHeight;
Gustav Senntond0e84532018-12-03 16:48:36 +0000257 if (mHeadsUpSmartReplyView != null) {
258 maxHeight += mHeadsUpSmartReplyView.getHeightUpperLimit();
259 }
Selim Cinek396caca2018-04-10 17:46:46 -0700260 maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700261 int size = maxHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700262 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800263 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700264 if (layoutParams.height >= 0) {
265 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800266 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800267 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700268 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800269 measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800270 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800271 : MeasureSpec.AT_MOST), 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700272 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
273 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700274 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700275 int singleLineWidthSpec = widthMeasureSpec;
276 if (mSingleLineWidthIndention != 0
277 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
278 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
279 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700280 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700281 }
282 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinekbe570ce2018-05-11 12:29:52 -0700283 MeasureSpec.makeMeasureSpec(mNotificationMaxHeight, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700284 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
285 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800286 if (mAmbientChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700287 int size = mNotificationAmbientHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800288 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800289 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800290 if (layoutParams.height >= 0) {
291 // An actual height is set
292 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800293 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800294 }
295 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800296 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
297 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800298 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
299 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700300 if (mAmbientSingleLineChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700301 int size = mNotificationAmbientHeight;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700302 ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
303 boolean useExactly = false;
304 if (layoutParams.height >= 0) {
305 // An actual height is set
306 size = Math.min(size, layoutParams.height);
307 useExactly = true;
308 }
309 int ambientSingleLineWidthSpec = widthMeasureSpec;
310 if (mSingleLineWidthIndention != 0
311 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
312 ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
313 width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
314 MeasureSpec.EXACTLY);
315 }
316 mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
317 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
318 : MeasureSpec.AT_MOST));
319 maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
320 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700321 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700322 setMeasuredDimension(width, ownHeight);
323 }
324
Selim Cineked64a142018-02-06 18:06:01 -0800325 /**
326 * Get the extra height that needs to be added to the notification height for a given
327 * {@link RemoteInputView}.
328 * This is needed when the user is inline replying in order to ensure that the reply bar has
329 * enough padding.
330 *
331 * @param remoteInput The remote input to check.
332 * @return The extra height needed.
333 */
334 private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
Selim Cinek09b442e2018-05-04 15:46:18 -0700335 if (remoteInput != null && (remoteInput.isActive() || remoteInput.isSending())) {
Selim Cineked64a142018-02-06 18:06:01 -0800336 return getResources().getDimensionPixelSize(
337 com.android.internal.R.dimen.notification_content_margin);
338 }
339 return 0;
340 }
341
Selim Cinek6ecc8102016-01-26 18:26:19 -0800342 private boolean updateContractedHeaderWidth() {
343 // We need to update the expanded and the collapsed header to have exactly the same with to
344 // have the expand buttons laid out at the same location.
345 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
346 if (contractedHeader != null) {
347 if (mExpandedChild != null
348 && mExpandedWrapper.getNotificationHeader() != null) {
349 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
350 int expandedSize = expandedHeader.getMeasuredWidth()
351 - expandedHeader.getPaddingEnd();
352 int collapsedSize = contractedHeader.getMeasuredWidth()
353 - expandedHeader.getPaddingEnd();
354 if (expandedSize != collapsedSize) {
355 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
356 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800357 contractedHeader.isLayoutRtl()
358 ? paddingEnd
359 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800360 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800361 contractedHeader.isLayoutRtl()
362 ? contractedHeader.getPaddingLeft()
363 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800364 contractedHeader.getPaddingBottom());
365 contractedHeader.setShowWorkBadgeAtEnd(true);
366 return true;
367 }
368 } else {
369 int paddingEnd = mNotificationContentMarginEnd;
370 if (contractedHeader.getPaddingEnd() != paddingEnd) {
371 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800372 contractedHeader.isLayoutRtl()
373 ? paddingEnd
374 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800375 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800376 contractedHeader.isLayoutRtl()
377 ? contractedHeader.getPaddingLeft()
378 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800379 contractedHeader.getPaddingBottom());
380 contractedHeader.setShowWorkBadgeAtEnd(false);
381 return true;
382 }
383 }
384 }
385 return false;
386 }
387
Selim Cinek860b6da2015-12-16 19:02:19 -0800388 private boolean shouldContractedBeFixedSize() {
389 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
390 }
391
Selim Cinek8d490d42015-04-10 00:05:50 -0700392 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200393 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700394 int previousHeight = 0;
395 if (mExpandedChild != null) {
396 previousHeight = mExpandedChild.getHeight();
397 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200398 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700399 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
400 mContentHeightAtAnimationStart = previousHeight;
401 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200402 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700403 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700404 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
405 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700406 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200407 }
408
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100409 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100410 protected void onAttachedToWindow() {
411 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100412 updateVisibility();
413 }
414
Selim Cinekcab4a602014-09-03 14:47:57 +0200415 public View getContractedChild() {
416 return mContractedChild;
417 }
418
419 public View getExpandedChild() {
420 return mExpandedChild;
421 }
422
Selim Cinek8d490d42015-04-10 00:05:50 -0700423 public View getHeadsUpChild() {
424 return mHeadsUpChild;
425 }
426
Adrian Roos0aac04f2016-12-08 15:59:29 -0800427 public View getAmbientChild() {
428 return mAmbientChild;
429 }
430
Adrian Roos6f6e1592017-05-02 16:22:53 -0700431 public HybridNotificationView getAmbientSingleLineChild() {
432 return mAmbientSingleLineChild;
433 }
434
Kevin38ce6fa2018-10-17 16:00:14 -0700435 /**
436 * Sets the contracted view. Child may be null to remove the content view.
437 *
438 * @param child contracted content view to set
439 */
440 public void setContractedChild(@Nullable View child) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200441 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200442 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200443 removeView(mContractedChild);
444 }
Kevin38ce6fa2018-10-17 16:00:14 -0700445 if (child == null) {
446 mContractedChild = null;
447 mContractedWrapper = null;
448 if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
449 mTransformationStartVisibleType = UNDEFINED;
450 }
451 return;
452 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200453 addView(child);
454 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700455 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
456 mContainingNotification);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800457 }
458
459 private NotificationViewWrapper getWrapperForView(View child) {
460 if (child == mContractedChild) {
461 return mContractedWrapper;
462 }
463 if (child == mExpandedChild) {
464 return mExpandedWrapper;
465 }
466 if (child == mHeadsUpChild) {
467 return mHeadsUpWrapper;
468 }
469 if (child == mAmbientChild) {
470 return mAmbientWrapper;
471 }
472 return null;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200473 }
474
Kevin38ce6fa2018-10-17 16:00:14 -0700475 /**
476 * Sets the expanded view. Child may be null to remove the content view.
477 *
478 * @param child expanded content view to set
479 */
480 public void setExpandedChild(@Nullable View child) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200481 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800482 mPreviousExpandedRemoteInputIntent = null;
483 if (mExpandedRemoteInput != null) {
484 mExpandedRemoteInput.onNotificationUpdateOrReset();
485 if (mExpandedRemoteInput.isActive()) {
486 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
487 mCachedExpandedRemoteInput = mExpandedRemoteInput;
488 mExpandedRemoteInput.dispatchStartTemporaryDetach();
489 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
490 }
491 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200492 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200493 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800494 mExpandedRemoteInput = null;
495 }
496 if (child == null) {
497 mExpandedChild = null;
498 mExpandedWrapper = null;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800499 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
500 mTransformationStartVisibleType = UNDEFINED;
501 }
Selim Cinek9f186642018-11-29 15:47:40 -0800502 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
503 selectLayout(false /* animate */, true /* force */);
504 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800505 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200506 }
507 addView(child);
508 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700509 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
510 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200511 }
512
Kevin38ce6fa2018-10-17 16:00:14 -0700513 /**
514 * Sets the heads up view. Child may be null to remove the content view.
515 *
516 * @param child heads up content view to set
517 */
518 public void setHeadsUpChild(@Nullable View child) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700519 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800520 mPreviousHeadsUpRemoteInputIntent = null;
521 if (mHeadsUpRemoteInput != null) {
522 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
523 if (mHeadsUpRemoteInput.isActive()) {
524 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
525 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
526 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
527 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
528 }
529 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700530 mHeadsUpChild.animate().cancel();
531 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800532 mHeadsUpRemoteInput = null;
533 }
534 if (child == null) {
535 mHeadsUpChild = null;
536 mHeadsUpWrapper = null;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800537 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
538 mTransformationStartVisibleType = UNDEFINED;
539 }
Selim Cinek9f186642018-11-29 15:47:40 -0800540 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
541 selectLayout(false /* animate */, true /* force */);
542 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800543 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700544 }
545 addView(child);
546 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700547 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
548 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700549 }
550
Kevin38ce6fa2018-10-17 16:00:14 -0700551 /**
552 * Sets the ambient view. Child may be null to remove the content view.
553 *
554 * @param child ambient content view to set
555 */
556 public void setAmbientChild(@Nullable View child) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800557 if (mAmbientChild != null) {
558 mAmbientChild.animate().cancel();
559 removeView(mAmbientChild);
560 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700561 if (child == null) {
Kevind4660b22018-09-27 10:57:35 -0700562 mAmbientChild = null;
563 mAmbientWrapper = null;
Kevind4660b22018-09-27 10:57:35 -0700564 if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
565 mTransformationStartVisibleType = UNDEFINED;
566 }
Selim Cinek9f186642018-11-29 15:47:40 -0800567 if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
568 selectLayout(false /* animate */, true /* force */);
569 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700570 return;
571 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800572 addView(child);
573 mAmbientChild = child;
574 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
575 mContainingNotification);
576 }
577
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100578 @Override
579 protected void onVisibilityChanged(View changedView, int visibility) {
580 super.onVisibilityChanged(changedView, visibility);
581 updateVisibility();
Kevin38ce6fa2018-10-17 16:00:14 -0700582 if (visibility != VISIBLE) {
583 // View is no longer visible so all content views are inactive.
584 for (Runnable r : mOnContentViewInactiveListeners.values()) {
585 r.run();
586 }
587 mOnContentViewInactiveListeners.clear();
588 }
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100589 }
590
591 private void updateVisibility() {
592 setVisible(isShown());
593 }
594
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700595 @Override
596 protected void onDetachedFromWindow() {
597 super.onDetachedFromWindow();
598 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
599 }
600
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100601 private void setVisible(final boolean isVisible) {
602 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700603 // This call can happen multiple times, but removing only removes a single one.
604 // We therefore need to remove the old one.
605 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100606 // We only animate if we are drawn at least once, otherwise the view might animate when
607 // it's shown the first time
608 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
609 } else {
610 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
611 mAnimate = false;
612 }
613 }
614
Selim Cineke9bad242016-06-15 11:46:37 -0700615 private void focusExpandButtonIfNecessary() {
616 if (mFocusOnVisibilityChange) {
617 NotificationHeaderView header = getVisibleNotificationHeader();
618 if (header != null) {
619 ImageView expandButton = header.getExpandButton();
620 if (expandButton != null) {
621 expandButton.requestAccessibilityFocus();
622 }
623 }
624 mFocusOnVisibilityChange = false;
625 }
626 }
627
Selim Cinekb5605e52015-02-20 18:21:41 +0100628 public void setContentHeight(int contentHeight) {
Selim Cineked64a142018-02-06 18:06:01 -0800629 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
630 int maxContentHeight = mContainingNotification.getIntrinsicHeight()
631 - getExtraRemoteInputHeight(mExpandedRemoteInput)
632 - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
633 mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100634 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400635
Kevin38ce6fa2018-10-17 16:00:14 -0700636 if (mContractedChild == null) {
637 // Contracted child may be null if this is the public content view and we don't need to
638 // show it.
639 return;
640 }
641
Adrian Roos181385c2016-05-05 17:45:44 -0400642 int minHeightHint = getMinContentHeightHint();
643
644 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
645 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800646 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400647 }
648
649 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
650 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800651 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400652 }
653
Jorim Jaggibe565df2014-04-28 17:51:23 +0200654 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700655 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200656 }
657
Adrian Roos181385c2016-05-05 17:45:44 -0400658 /**
659 * @return the minimum apparent height that the wrapper should allow for the purpose
660 * of aligning elements at the bottom edge. If this is larger than the content
661 * height, the notification is clipped instead of being further shrunk.
662 */
663 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700664 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400665 return mContext.getResources().getDimensionPixelSize(
666 com.android.internal.R.dimen.notification_action_list_height);
667 }
Adrian Roos599be342016-06-13 14:54:39 -0700668
669 // Transition between heads-up & expanded, or pinned.
670 if (mHeadsUpChild != null && mExpandedChild != null) {
671 boolean transitioningBetweenHunAndExpanded =
672 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
673 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700674 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800675 && (mIsHeadsUp || mHeadsUpAnimatingAway)
676 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700677 if (transitioningBetweenHunAndExpanded || pinned) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800678 return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
679 getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700680 }
681 }
682
683 // Size change of the expanded version
684 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
685 && mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800686 return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700687 }
688
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700689 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800690 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
691 hint = mAmbientChild.getHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700692 } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
693 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
694 hint = mAmbientSingleLineChild.getHeight();
Adrian Roos05d3d002017-01-26 15:28:07 -0800695 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800696 hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
Adrian Roos599be342016-06-13 14:54:39 -0700697 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800698 hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
Adrian Roos181385c2016-05-05 17:45:44 -0400699 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800700 hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
701 + mContext.getResources().getDimensionPixelSize(
702 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400703 }
Adrian Roos599be342016-06-13 14:54:39 -0700704
705 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800706 hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700707 }
708 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400709 }
710
Adrian Roos599be342016-06-13 14:54:39 -0700711 private boolean isTransitioningFromTo(int from, int to) {
712 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
713 && mVisibleType == to;
714 }
715
716 private boolean isVisibleOrTransitioning(int type) {
717 return mVisibleType == type || mTransformationStartVisibleType == type
718 || mAnimationStartVisibleType == type;
719 }
720
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800721 private void updateContentTransformation() {
722 int visibleType = calculateVisibleType();
723 if (visibleType != mVisibleType) {
724 // A new transformation starts
725 mTransformationStartVisibleType = mVisibleType;
726 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
727 final TransformableView hiddenView = getTransformableViewForVisibleType(
728 mTransformationStartVisibleType);
729 shownView.transformFrom(hiddenView, 0.0f);
730 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
731 hiddenView.transformTo(shownView, 0.0f);
732 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800733 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800734 }
Selim Cineke8578872016-05-03 16:42:50 -0700735 if (mForceSelectNextLayout) {
736 forceUpdateVisibilities();
737 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800738 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700739 && mVisibleType != mTransformationStartVisibleType
740 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800741 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
742 final TransformableView hiddenView = getTransformableViewForVisibleType(
743 mTransformationStartVisibleType);
744 float transformationAmount = calculateTransformationAmount();
745 shownView.transformFrom(hiddenView, transformationAmount);
746 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800747 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800748 } else {
749 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800750 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800751 }
752 }
753
Selim Cinekc3179332016-03-04 14:44:56 -0800754 private void updateBackgroundTransformation(float transformationAmount) {
755 int endColor = getBackgroundColor(mVisibleType);
756 int startColor = getBackgroundColor(mTransformationStartVisibleType);
757 if (endColor != startColor) {
758 if (startColor == 0) {
759 startColor = mContainingNotification.getBackgroundColorWithoutTint();
760 }
761 if (endColor == 0) {
762 endColor = mContainingNotification.getBackgroundColorWithoutTint();
763 }
764 endColor = NotificationUtils.interpolateColors(startColor, endColor,
765 transformationAmount);
766 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400767 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800768 mContainingNotification.setContentBackground(endColor, false, this);
769 }
770
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800771 private float calculateTransformationAmount() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800772 int startHeight = getViewHeight(mTransformationStartVisibleType);
773 int endHeight = getViewHeight(mVisibleType);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800774 int progress = Math.abs(mContentHeight - startHeight);
775 int totalDistance = Math.abs(endHeight - startHeight);
Selim Cinek011bde92017-11-22 05:53:38 -0800776 if (totalDistance == 0) {
777 Log.wtf(TAG, "the total transformation distance is 0"
778 + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
779 + "\n VisibleType: " + mVisibleType + " height: " + endHeight
780 + "\n mContentHeight: " + mContentHeight);
781 return 1.0f;
782 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800783 float amount = (float) progress / (float) totalDistance;
784 return Math.min(1.0f, amount);
785 }
786
Selim Cinekb5605e52015-02-20 18:21:41 +0100787 public int getContentHeight() {
788 return mContentHeight;
789 }
790
Jorim Jaggibe565df2014-04-28 17:51:23 +0200791 public int getMaxHeight() {
Kevin38ce6fa2018-10-17 16:00:14 -0700792 if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700793 return getShowingAmbientView().getHeight();
794 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800795 return getViewHeight(VISIBLE_TYPE_EXPANDED)
796 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cinekc494e382017-01-31 16:09:23 -0800797 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800798 return getViewHeight(VISIBLE_TYPE_HEADSUP)
799 + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
Kevin38ce6fa2018-10-17 16:00:14 -0700800 } else if (mContractedChild != null) {
801 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
Selim Cinek8d490d42015-04-10 00:05:50 -0700802 }
Kevin38ce6fa2018-10-17 16:00:14 -0700803 return mNotificationMaxHeight;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800804 }
805
806 private int getViewHeight(int visibleType) {
807 View view = getViewForVisibleType(visibleType);
808 int height = view.getHeight();
809 NotificationViewWrapper viewWrapper = getWrapperForView(view);
810 if (viewWrapper != null) {
811 height += viewWrapper.getHeaderTranslation();
812 }
813 return height;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200814 }
815
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200816 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800817 return getMinHeight(false /* likeGroupExpanded */);
818 }
819
820 public int getMinHeight(boolean likeGroupExpanded) {
Kevin38ce6fa2018-10-17 16:00:14 -0700821 if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700822 return getShowingAmbientView().getHeight();
823 } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Kevin38ce6fa2018-10-17 16:00:14 -0700824 return mContractedChild != null
825 ? getViewHeight(VISIBLE_TYPE_CONTRACTED) : mMinContractedHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800826 } else {
827 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700828 }
829 }
830
Adrian Roos6f6e1592017-05-02 16:22:53 -0700831 public View getShowingAmbientView() {
832 View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
833 if (v != null) {
834 return v;
835 } else {
836 return mContractedChild;
837 }
838 }
839
Selim Cinek83bc7832015-10-22 13:26:54 -0700840 private boolean isGroupExpanded() {
841 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200842 }
843
Jorim Jaggibe565df2014-04-28 17:51:23 +0200844 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200845 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200846 updateClipping();
847 }
848
Selim Cineka686b2c2016-10-26 13:58:27 -0700849
850 public void setClipBottomAmount(int clipBottomAmount) {
851 mClipBottomAmount = clipBottomAmount;
852 updateClipping();
853 }
854
Selim Cinek875a3a12016-11-18 17:52:16 -0800855 @Override
856 public void setTranslationY(float translationY) {
857 super.setTranslationY(translationY);
858 updateClipping();
859 }
860
Jorim Jaggibe565df2014-04-28 17:51:23 +0200861 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100862 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800863 int top = (int) (mClipTopAmount - getTranslationY());
Selim Cineked64a142018-02-06 18:06:01 -0800864 int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700865 bottom = Math.max(top, bottom);
866 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100867 setClipBounds(mClipBounds);
868 } else {
869 setClipBounds(null);
870 }
871 }
872
873 public void setClipToActualHeight(boolean clipToActualHeight) {
874 mClipToActualHeight = clipToActualHeight;
875 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200876 }
877
Jorim Jaggi11298832014-05-24 16:18:38 +0200878 private void selectLayout(boolean animate, boolean force) {
879 if (mContractedChild == null) {
880 return;
881 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800882 if (mUserExpanding) {
883 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700884 } else {
885 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700886 boolean changedType = visibleType != mVisibleType;
887 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400888 View visibleView = getViewForVisibleType(visibleType);
889 if (visibleView != null) {
890 visibleView.setVisibility(VISIBLE);
891 transferRemoteInputFocus(visibleType);
892 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700893
Selim Cinek589fd3e2016-04-26 18:17:57 -0700894 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
895 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
896 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
897 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
898 animateToVisibleType(visibleType);
899 } else {
900 updateViewVisibilities(visibleType);
901 }
902 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700903 if (changedType) {
904 focusExpandButtonIfNecessary();
905 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800906 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
907 if (visibleWrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800908 visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
909 getMinContentHeightHint());
Adrian Roos05d3d002017-01-26 15:28:07 -0800910 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700911 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200912 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700913 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700914 }
915
916 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800917 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
918 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
919 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
920 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
921 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700922 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
923 mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800924 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800925 // forceUpdateVisibilities cancels outstanding animations without updating the
926 // mAnimationStartVisibleType. Do so here instead.
927 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800928 }
929
Adrian Roos4320e892017-01-24 12:50:34 -0800930 private void fireExpandedVisibleListenerIfVisible() {
931 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
932 && mExpandedChild.getVisibility() == VISIBLE) {
933 Runnable listener = mExpandedVisibleListener;
934 mExpandedVisibleListener = null;
935 listener.run();
936 }
937 }
938
Adrian Roos0aac04f2016-12-08 15:59:29 -0800939 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
940 if (view == null) {
941 return;
942 }
943 boolean visible = mVisibleType == type
944 || mTransformationStartVisibleType == type;
945 if (!visible) {
946 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700947 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800948 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200949 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200950 }
951
Selim Cinekc3179332016-03-04 14:44:56 -0800952 public void updateBackgroundColor(boolean animate) {
953 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400954 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800955 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
956 }
957
Kenny Guy14d035c2018-05-02 19:10:36 +0100958 public void setBackgroundTintColor(int color) {
959 if (mExpandedSmartReplyView != null) {
960 mExpandedSmartReplyView.setBackgroundTintColor(color);
961 }
Gustav Senntond0e84532018-12-03 16:48:36 +0000962 if (mHeadsUpSmartReplyView != null) {
963 mHeadsUpSmartReplyView.setBackgroundTintColor(color);
964 }
Kenny Guy14d035c2018-05-02 19:10:36 +0100965 }
966
Mady Mellorb0a82462016-04-30 17:31:02 -0700967 public int getVisibleType() {
968 return mVisibleType;
969 }
970
971 public int getBackgroundColorForExpansionState() {
972 // When expanding or user locked we want the new type, when collapsing we want
973 // the original type
974 final int visibleType = (mContainingNotification.isGroupExpanded()
975 || mContainingNotification.isUserLocked())
976 ? calculateVisibleType()
977 : getVisibleType();
978 return getBackgroundColor(visibleType);
979 }
980
981 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800982 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
983 int customBackgroundColor = 0;
984 if (currentVisibleWrapper != null) {
985 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
986 }
987 return customBackgroundColor;
988 }
989
Selim Cinek684a4422015-04-15 16:18:39 -0700990 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800991 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
992 mContractedChild, mContractedWrapper);
993 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
994 mExpandedChild, mExpandedWrapper);
995 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
996 mHeadsUpChild, mHeadsUpWrapper);
997 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
998 mSingleLineView, mSingleLineView);
999 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
1000 mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -07001001 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
1002 mAmbientSingleLineChild, mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -08001003 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -08001004 // updateViewVisibilities cancels outstanding animations without updating the
1005 // mAnimationStartVisibleType. Do so here instead.
1006 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001007 }
1008
1009 private void updateViewVisibility(int visibleType, int type, View view,
1010 TransformableView wrapper) {
1011 if (view != null) {
1012 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -07001013 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001014 }
1015
Selim Cinek4ffd6362015-12-29 15:12:23 +01001016 private void animateToVisibleType(int visibleType) {
1017 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
1018 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -07001019 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -08001020 shownView.setVisible(true);
1021 return;
1022 }
Adrian Roos599be342016-06-13 14:54:39 -07001023 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001024 shownView.transformFrom(hiddenView);
1025 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
1026 hiddenView.transformTo(shownView, new Runnable() {
1027 @Override
1028 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -08001029 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
1030 hiddenView.setVisible(false);
1031 }
Adrian Roos599be342016-06-13 14:54:39 -07001032 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001033 }
1034 });
Adrian Roos4320e892017-01-24 12:50:34 -08001035 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +02001036 }
1037
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001038 private void transferRemoteInputFocus(int visibleType) {
1039 if (visibleType == VISIBLE_TYPE_HEADSUP
1040 && mHeadsUpRemoteInput != null
1041 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
1042 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
1043 }
1044 if (visibleType == VISIBLE_TYPE_EXPANDED
1045 && mExpandedRemoteInput != null
1046 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
1047 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
1048 }
1049 }
1050
Selim Cinek684a4422015-04-15 16:18:39 -07001051 /**
1052 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +01001053 * @return the corresponding transformable view according to the given visible type
1054 */
1055 private TransformableView getTransformableViewForVisibleType(int visibleType) {
1056 switch (visibleType) {
1057 case VISIBLE_TYPE_EXPANDED:
1058 return mExpandedWrapper;
1059 case VISIBLE_TYPE_HEADSUP:
1060 return mHeadsUpWrapper;
1061 case VISIBLE_TYPE_SINGLELINE:
1062 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001063 case VISIBLE_TYPE_AMBIENT:
1064 return mAmbientWrapper;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001065 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1066 return mAmbientSingleLineChild;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001067 default:
1068 return mContractedWrapper;
1069 }
1070 }
1071
1072 /**
1073 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -07001074 * @return the corresponding view according to the given visible type
1075 */
1076 private View getViewForVisibleType(int visibleType) {
1077 switch (visibleType) {
1078 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -07001079 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001080 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -07001081 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -07001082 case VISIBLE_TYPE_SINGLELINE:
1083 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001084 case VISIBLE_TYPE_AMBIENT:
1085 return mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001086 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1087 return mAmbientSingleLineChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001088 default:
1089 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -07001090 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001091 }
1092
Selim Cinek131f1a42017-06-05 17:50:19 -07001093 public NotificationViewWrapper getVisibleWrapper(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -08001094 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -08001095 case VISIBLE_TYPE_EXPANDED:
1096 return mExpandedWrapper;
1097 case VISIBLE_TYPE_HEADSUP:
1098 return mHeadsUpWrapper;
1099 case VISIBLE_TYPE_CONTRACTED:
1100 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001101 case VISIBLE_TYPE_AMBIENT:
1102 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -08001103 default:
1104 return null;
1105 }
1106 }
1107
Selim Cinek684a4422015-04-15 16:18:39 -07001108 /**
1109 * @return one of the static enum types in this view, calculated form the current state
1110 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001111 public int calculateVisibleType() {
Kevina97ea052018-09-11 13:53:18 -07001112 if (mContainingNotification.isOnAmbient()) {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001113 if (mIsChildInGroup && mAmbientSingleLineChild != null) {
1114 return VISIBLE_TYPE_AMBIENT_SINGLELINE;
1115 } else if (mAmbientChild != null) {
1116 return VISIBLE_TYPE_AMBIENT;
1117 } else {
1118 return VISIBLE_TYPE_CONTRACTED;
1119 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001120 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001121 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -08001122 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001123 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001124 ? mContainingNotification.getMaxContentHeight()
1125 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -08001126 if (height == 0) {
1127 height = mContentHeight;
1128 }
Selim Cinek42357e02016-02-24 18:48:01 -08001129 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -08001130 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -07001131 ? VISIBLE_TYPE_SINGLELINE
1132 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001133 return mTransformationStartVisibleType == collapsedVisualType
1134 ? expandedVisualType
1135 : collapsedVisualType;
1136 }
Selim Cinek51d94912016-03-02 15:34:28 -08001137 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
1138 int viewHeight = mContentHeight;
1139 if (intrinsicHeight != 0) {
1140 // the intrinsicHeight might be 0 because it was just reset.
1141 viewHeight = Math.min(mContentHeight, intrinsicHeight);
1142 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001143 return getVisualTypeForHeight(viewHeight);
1144 }
1145
1146 private int getVisualTypeForHeight(float viewHeight) {
1147 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001148 if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
Adrian Roos777ef562015-12-01 17:37:14 -08001149 return VISIBLE_TYPE_EXPANDED;
1150 }
Selim Cinek414ad332017-02-24 19:06:12 -08001151 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001152 return VISIBLE_TYPE_SINGLELINE;
1153 }
Adrian Roos777ef562015-12-01 17:37:14 -08001154
Selim Cinekc494e382017-01-31 16:09:23 -08001155 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
1156 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001157 if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -07001158 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -07001159 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001160 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001161 }
1162 } else {
Kevin38ce6fa2018-10-17 16:00:14 -07001163 if (noExpandedChild || (mContractedChild != null
1164 && viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
Selim Cinek6ac3fa82016-05-10 17:11:54 -04001165 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001166 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -07001167 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001168 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001169 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001170 }
1171 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001172 }
1173
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001174 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001175 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001176 }
John Spurlocke15452b2014-08-21 09:44:39 -04001177
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001178 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -08001179 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -08001180 return;
1181 }
John Spurlocke15452b2014-08-21 09:44:39 -04001182 mDark = dark;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001183 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -04001184 }
1185
Selim Cinek8d490d42015-04-10 00:05:50 -07001186 public void setHeadsUp(boolean headsUp) {
1187 mIsHeadsUp = headsUp;
1188 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -08001189 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -07001190 }
1191
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +02001192 @Override
1193 public boolean hasOverlappingRendering() {
1194
1195 // This is not really true, but good enough when fading from the contracted to the expanded
1196 // layout, and saves us some layers.
1197 return false;
1198 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001199
Selim Cinek1a48bab2017-02-17 19:38:40 -08001200 public void setLegacy(boolean legacy) {
1201 mLegacy = legacy;
1202 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -08001203 }
1204
Selim Cinek1a48bab2017-02-17 19:38:40 -08001205 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -08001206 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001207 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001208 }
1209 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001210 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001211 }
1212 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001213 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001214 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001215 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001216
Selim Cinek83bc7832015-10-22 13:26:54 -07001217 public void setIsChildInGroup(boolean isChildInGroup) {
1218 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001219 if (mContractedChild != null) {
1220 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1221 }
1222 if (mExpandedChild != null) {
1223 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1224 }
1225 if (mHeadsUpChild != null) {
1226 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1227 }
1228 if (mAmbientChild != null) {
1229 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1230 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001231 updateAllSingleLineViews();
Selim Cinek83bc7832015-10-22 13:26:54 -07001232 }
1233
Ned Burnsf81c4c42019-01-07 14:10:43 -05001234 public void onNotificationUpdated(NotificationEntry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001235 mStatusBarNotification = entry.notification;
Kevind5022f92018-10-08 18:30:26 -07001236 mOnContentViewInactiveListeners.clear();
Selim Cinek860b6da2015-12-16 19:02:19 -08001237 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001238 updateAllSingleLineViews();
Evan Laird94492852018-10-25 13:43:01 -04001239 ExpandableNotificationRow row = entry.getRow();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001240 if (mContractedChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001241 mContractedWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001242 }
1243 if (mExpandedChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001244 mExpandedWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001245 }
1246 if (mHeadsUpChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001247 mHeadsUpWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001248 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001249 if (mAmbientChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001250 mAmbientWrapper.onContentUpdated(row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001251 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001252 applyRemoteInputAndSmartReply(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001253 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001254 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001255 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001256 mPreviousExpandedRemoteInputIntent = null;
1257 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001258 }
1259
Adrian Roos6f6e1592017-05-02 16:22:53 -07001260 private void updateAllSingleLineViews() {
1261 updateSingleLineView();
1262 updateAmbientSingleLineView();
1263 }
Tony Mak638430e2018-10-08 19:19:10 +01001264
Selim Cinek83bc7832015-10-22 13:26:54 -07001265 private void updateSingleLineView() {
1266 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001267 boolean isNewView = mSingleLineView == null;
Selim Cinekc897bd32016-03-18 17:32:31 -07001268 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001269 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001270 if (isNewView) {
1271 updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
1272 mSingleLineView, mSingleLineView);
1273 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001274 } else if (mSingleLineView != null) {
1275 removeView(mSingleLineView);
1276 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001277 }
1278 }
1279
Adrian Roos6f6e1592017-05-02 16:22:53 -07001280 private void updateAmbientSingleLineView() {
1281 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001282 boolean isNewView = mAmbientSingleLineChild == null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001283 mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
1284 mAmbientSingleLineChild, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001285 if (isNewView) {
1286 updateViewVisibility(mVisibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
1287 mAmbientSingleLineChild, mAmbientSingleLineChild);
1288 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001289 } else if (mAmbientSingleLineChild != null) {
1290 removeView(mAmbientSingleLineChild);
1291 mAmbientSingleLineChild = null;
1292 }
1293 }
1294
Ned Burnsf81c4c42019-01-07 14:10:43 -05001295 private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001296 if (mRemoteInputController == null) {
1297 return;
1298 }
1299
Tony Mak29996702018-11-26 16:23:34 +00001300 SmartRepliesAndActions smartRepliesAndActions =
1301 chooseSmartRepliesAndActions(mSmartReplyConstants, entry);
Tony Mak638430e2018-10-08 19:19:10 +01001302
Tony Mak29996702018-11-26 16:23:34 +00001303 applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput);
Gustav Senntoneab53682018-11-01 16:30:23 +00001304 applySmartReplyView(smartRepliesAndActions, entry);
1305 }
Tony Mak638430e2018-10-08 19:19:10 +01001306
Gustav Senntoneab53682018-11-01 16:30:23 +00001307 /**
1308 * Chose what smart replies and smart actions to display. App generated suggestions take
1309 * precedence. So if the app provides any smart replies, we don't show any
1310 * replies or actions generated by the NotificationAssistantService (NAS), and if the app
1311 * provides any smart actions we also don't show any NAS-generated replies or actions.
1312 */
1313 @VisibleForTesting
1314 static SmartRepliesAndActions chooseSmartRepliesAndActions(
1315 SmartReplyConstants smartReplyConstants,
Ned Burnsf81c4c42019-01-07 14:10:43 -05001316 final NotificationEntry entry) {
Gustav Senntoneab53682018-11-01 16:30:23 +00001317 boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
1318 && (!smartReplyConstants.requiresTargetingP()
Tony Mak638430e2018-10-08 19:19:10 +01001319 || entry.targetSdk >= Build.VERSION_CODES.P));
Petr Cermak7997d7a2018-01-18 15:52:10 +00001320
Gustav Senntoneab53682018-11-01 16:30:23 +00001321 Notification notification = entry.notification.getNotification();
1322 Pair<RemoteInput, Notification.Action> remoteInputActionPair =
1323 notification.findRemoteInputActionPair(false /* freeform */);
1324 Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
1325 notification.findRemoteInputActionPair(true /* freeform */);
1326
1327 boolean appGeneratedSmartRepliesExist =
1328 enableAppGeneratedSmartReplies
1329 && remoteInputActionPair != null
Tony Mak29996702018-11-26 16:23:34 +00001330 && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())
1331 && remoteInputActionPair.second.actionIntent != null;
Gustav Senntoneab53682018-11-01 16:30:23 +00001332
1333 List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
1334 boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
1335
Tony Mak29996702018-11-26 16:23:34 +00001336 SmartReplyView.SmartReplies smartReplies = null;
1337 SmartReplyView.SmartActions smartActions = null;
Gustav Senntoneab53682018-11-01 16:30:23 +00001338 if (appGeneratedSmartRepliesExist) {
Tony Mak29996702018-11-26 16:23:34 +00001339 smartReplies = new SmartReplyView.SmartReplies(
Gustav Senntoneab53682018-11-01 16:30:23 +00001340 remoteInputActionPair.first.getChoices(),
Tony Mak29996702018-11-26 16:23:34 +00001341 remoteInputActionPair.first,
1342 remoteInputActionPair.second.actionIntent,
1343 false /* fromAssistant */);
Gustav Senntoneab53682018-11-01 16:30:23 +00001344 }
Tony Mak29996702018-11-26 16:23:34 +00001345 if (appGeneratedSmartActionsExist) {
1346 smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions,
1347 false /* fromAssistant */);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001348 }
Tony Mak29996702018-11-26 16:23:34 +00001349 // Apps didn't provide any smart replies / actions, use those from NAS (if any).
1350 if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
1351 boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
1352 && freeformRemoteInputActionPair != null
1353 && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
1354 && freeformRemoteInputActionPair.second.actionIntent != null;
1355 if (useGeneratedReplies) {
1356 smartReplies = new SmartReplyView.SmartReplies(
1357 entry.smartReplies,
1358 freeformRemoteInputActionPair.first,
1359 freeformRemoteInputActionPair.second.actionIntent,
1360 true /* fromAssistant */);
1361 }
1362 boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
1363 && notification.getAllowSystemGeneratedContextualActions();
1364 if (useSmartActions) {
1365 smartActions = new SmartReplyView.SmartActions(
1366 entry.systemGeneratedSmartActions, true /* fromAssistant */);
1367 }
Gustav Senntoneab53682018-11-01 16:30:23 +00001368 }
Tony Mak29996702018-11-26 16:23:34 +00001369 return new SmartRepliesAndActions(
1370 smartReplies, smartActions, freeformRemoteInputActionPair != null);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001371 }
1372
Ned Burnsf81c4c42019-01-07 14:10:43 -05001373 private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001374 View bigContentView = mExpandedChild;
1375 if (bigContentView != null) {
Tony Mak638430e2018-10-08 19:19:10 +01001376 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001377 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1378 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001379 } else {
1380 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001381 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001382 if (mCachedExpandedRemoteInput != null
1383 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1384 // We had a cached remote input but didn't reuse it. Clean up required.
1385 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1386 }
1387 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001388
Adrian Roosb88b1a12015-12-09 18:51:05 -08001389 View headsUpContentView = mHeadsUpChild;
1390 if (headsUpContentView != null) {
Tony Mak638430e2018-10-08 19:19:10 +01001391 mHeadsUpRemoteInput = applyRemoteInput(
1392 headsUpContentView, entry, hasFreeformRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001393 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001394 } else {
1395 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001396 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001397 if (mCachedHeadsUpRemoteInput != null
1398 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1399 // We had a cached remote input but didn't reuse it. Clean up required.
1400 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1401 }
1402 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001403 }
1404
Ned Burnsf81c4c42019-01-07 14:10:43 -05001405 private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001406 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001407 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001408 View actionContainerCandidate = view.findViewById(
1409 com.android.internal.R.id.actions_container);
1410 if (actionContainerCandidate instanceof FrameLayout) {
1411 RemoteInputView existing = (RemoteInputView)
1412 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1413
1414 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001415 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001416 }
1417
1418 if (existing == null && hasRemoteInput) {
1419 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001420 if (cachedView == null) {
1421 RemoteInputView riv = RemoteInputView.inflate(
1422 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001423
Adrian Roos7813dd72016-09-23 17:12:17 -07001424 riv.setVisibility(View.INVISIBLE);
1425 actionContainer.addView(riv, new LayoutParams(
1426 ViewGroup.LayoutParams.MATCH_PARENT,
1427 ViewGroup.LayoutParams.MATCH_PARENT)
1428 );
1429 existing = riv;
1430 } else {
1431 actionContainer.addView(cachedView);
1432 cachedView.dispatchFinishTemporaryDetach();
1433 cachedView.requestFocus();
1434 existing = cachedView;
1435 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001436 }
1437 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001438 int color = entry.notification.getNotification().color;
1439 if (color == Notification.COLOR_DEFAULT) {
1440 color = mContext.getColor(R.color.default_remote_input_background);
1441 }
Lucas Dupina291d192018-06-07 13:59:42 -07001442 existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001443 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001444 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001445
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001446 existing.setWrapper(wrapper);
Selim Cineked64a142018-02-06 18:06:01 -08001447 existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001448
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001449 if (existingPendingIntent != null || existing.isActive()) {
1450 // The current action could be gone, or the pending intent no longer valid.
1451 // If we find a matching action in the new notification, focus, otherwise close.
1452 Notification.Action[] actions = entry.notification.getNotification().actions;
1453 if (existingPendingIntent != null) {
1454 existing.setPendingIntent(existingPendingIntent);
1455 }
1456 if (existing.updatePendingIntentFromActions(actions)) {
1457 if (!existing.isActive()) {
1458 existing.focus();
1459 }
1460 } else {
1461 if (existing.isActive()) {
1462 existing.close();
1463 }
1464 }
1465 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001466 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001467 return existing;
1468 }
1469 return null;
1470 }
1471
Gustav Senntoneab53682018-11-01 16:30:23 +00001472 private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
Ned Burnsf81c4c42019-01-07 14:10:43 -05001473 NotificationEntry entry) {
Kenny Guy23991102018-04-05 21:18:38 +01001474 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +01001475 mExpandedSmartReplyView =
Gustav Senntoneab53682018-11-01 16:30:23 +00001476 applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
Gustav Senntond25a64d2018-12-07 10:58:39 +00001477 if (mExpandedSmartReplyView != null) {
1478 if (smartRepliesAndActions.smartReplies != null
1479 || smartRepliesAndActions.smartActions != null) {
1480 int numSmartReplies = smartRepliesAndActions.smartReplies == null
1481 ? 0 : smartRepliesAndActions.smartReplies.choices.length;
1482 int numSmartActions = smartRepliesAndActions.smartActions == null
1483 ? 0 : smartRepliesAndActions.smartActions.actions.size();
1484 boolean fromAssistant = smartRepliesAndActions.smartReplies == null
1485 ? smartRepliesAndActions.smartActions.fromAssistant
1486 : smartRepliesAndActions.smartReplies.fromAssistant;
1487 mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
1488 numSmartActions, fromAssistant);
1489 }
Kenny Guy23991102018-04-05 21:18:38 +01001490 }
1491 }
Gustav Sennton3f3eaff2019-01-08 09:39:51 +00001492 if (mHeadsUpChild != null && mSmartReplyConstants.getShowInHeadsUp()) {
Gustav Senntond0e84532018-12-03 16:48:36 +00001493 mHeadsUpSmartReplyView =
1494 applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, entry);
1495 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001496 }
1497
Gustav Senntoneab53682018-11-01 16:30:23 +00001498 private SmartReplyView applySmartReplyView(View view,
Ned Burnsf81c4c42019-01-07 14:10:43 -05001499 SmartRepliesAndActions smartRepliesAndActions, NotificationEntry entry) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001500 View smartReplyContainerCandidate = view.findViewById(
1501 com.android.internal.R.id.smart_reply_container);
1502 if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
1503 return null;
1504 }
1505 LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
Gustav Senntoneab53682018-11-01 16:30:23 +00001506 // If there are no smart replies and no smart actions - early out.
Tony Mak29996702018-11-26 16:23:34 +00001507 if (smartRepliesAndActions.smartReplies == null
1508 && smartRepliesAndActions.smartActions == null) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001509 smartReplyContainer.setVisibility(View.GONE);
1510 return null;
1511 }
Kenny Guya0f6de82018-04-06 16:20:16 +01001512 // If we are showing the spinner we don't want to add the buttons.
1513 boolean showingSpinner = entry.notification.getNotification()
1514 .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
1515 if (showingSpinner) {
1516 smartReplyContainer.setVisibility(View.GONE);
1517 return null;
1518 }
Kenny Guy8cc15d22018-05-09 09:50:55 +01001519 // If we are keeping the notification around while sending we don't want to add the buttons.
1520 boolean hideSmartReplies = entry.notification.getNotification()
1521 .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false);
1522 if (hideSmartReplies) {
1523 smartReplyContainer.setVisibility(View.GONE);
1524 return null;
1525 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001526 SmartReplyView smartReplyView = null;
1527 if (smartReplyContainer.getChildCount() == 0) {
1528 smartReplyView = SmartReplyView.inflate(mContext, smartReplyContainer);
1529 smartReplyContainer.addView(smartReplyView);
1530 } else if (smartReplyContainer.getChildCount() == 1) {
1531 View child = smartReplyContainer.getChildAt(0);
1532 if (child instanceof SmartReplyView) {
1533 smartReplyView = (SmartReplyView) child;
1534 }
1535 }
1536 if (smartReplyView != null) {
Gustav Senntoneab53682018-11-01 16:30:23 +00001537 smartReplyView.resetSmartSuggestions(smartReplyContainer);
Tony Mak29996702018-11-26 16:23:34 +00001538 if (smartRepliesAndActions.smartReplies != null) {
1539 smartReplyView.addRepliesFromRemoteInput(
1540 smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
1541 }
1542 if (smartRepliesAndActions.smartActions != null) {
Tony Mak7d4b3a52018-11-27 17:29:36 +00001543 smartReplyView.addSmartActions(
Gustav Senntond0e84532018-12-03 16:48:36 +00001544 smartRepliesAndActions.smartActions, mSmartReplyController, entry,
1545 mContainingNotification.getHeadsUpManager());
Tony Mak29996702018-11-26 16:23:34 +00001546 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001547 smartReplyContainer.setVisibility(View.VISIBLE);
1548 }
1549 return smartReplyView;
1550 }
1551
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001552 public void closeRemoteInput() {
1553 if (mHeadsUpRemoteInput != null) {
1554 mHeadsUpRemoteInput.close();
1555 }
1556 if (mExpandedRemoteInput != null) {
1557 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001558 }
1559 }
1560
Selim Cinek83bc7832015-10-22 13:26:54 -07001561 public void setGroupManager(NotificationGroupManager groupManager) {
1562 mGroupManager = groupManager;
1563 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001564
Adrian Roosb88b1a12015-12-09 18:51:05 -08001565 public void setRemoteInputController(RemoteInputController r) {
1566 mRemoteInputController = r;
1567 }
1568
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001569 public void setExpandClickListener(OnClickListener expandClickListener) {
1570 mExpandClickListener = expandClickListener;
1571 }
1572
1573 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001574 mExpandable = expandable;
1575 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001576 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001577 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1578 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001579 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001580 expandable = false;
1581 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001582 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001583 expandable = false;
1584 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001585 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001586 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001587 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001588 }
1589 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001590 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001591 }
1592 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001593 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001594 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001595 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001596 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001597
1598 public NotificationHeaderView getNotificationHeader() {
1599 NotificationHeaderView header = null;
1600 if (mContractedChild != null) {
1601 header = mContractedWrapper.getNotificationHeader();
1602 }
1603 if (header == null && mExpandedChild != null) {
1604 header = mExpandedWrapper.getNotificationHeader();
1605 }
1606 if (header == null && mHeadsUpChild != null) {
1607 header = mHeadsUpWrapper.getNotificationHeader();
1608 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001609 if (header == null && mAmbientChild != null) {
1610 header = mAmbientWrapper.getNotificationHeader();
1611 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001612 return header;
1613 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001614
Julia Reynoldsfc640012018-02-21 12:25:27 -05001615 public void showAppOpsIcons(ArraySet<Integer> activeOps) {
1616 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
1617 mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1618 }
1619 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
1620 mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1621 }
1622 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
1623 mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1624 }
1625 }
Selim Cinekb26afa32017-06-29 10:28:17 +02001626
Gus Prevasa3226492018-10-23 11:10:09 -04001627 /** Sets whether the notification being displayed audibly alerted the user. */
Gus Prevas7306b902018-12-11 10:57:06 -05001628 public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
Gus Prevasa3226492018-10-23 11:10:09 -04001629 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001630 mContractedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001631 }
1632 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001633 mExpandedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001634 }
1635 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001636 mHeadsUpWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001637 }
1638 }
1639
Selim Cinekb26afa32017-06-29 10:28:17 +02001640 public NotificationHeaderView getContractedNotificationHeader() {
1641 if (mContractedChild != null) {
1642 return mContractedWrapper.getNotificationHeader();
1643 }
1644 return null;
1645 }
1646
Selim Cinek34eda5e2016-02-18 17:10:43 -08001647 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001648 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001649 return wrapper == null ? null : wrapper.getNotificationHeader();
1650 }
1651
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001652 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1653 mContainingNotification = containingNotification;
1654 }
1655
1656 public void requestSelectLayout(boolean needsAnimation) {
1657 selectLayout(needsAnimation, false);
1658 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001659
1660 public void reInflateViews() {
1661 if (mIsChildInGroup && mSingleLineView != null) {
1662 removeView(mSingleLineView);
1663 mSingleLineView = null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001664 updateAllSingleLineViews();
Selim Cinekde33a4a2016-02-11 16:43:41 -08001665 }
1666 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001667
1668 public void setUserExpanding(boolean userExpanding) {
1669 mUserExpanding = userExpanding;
1670 if (userExpanding) {
1671 mTransformationStartVisibleType = mVisibleType;
1672 } else {
1673 mTransformationStartVisibleType = UNDEFINED;
1674 mVisibleType = calculateVisibleType();
1675 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001676 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001677 }
1678 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001679
1680 /**
1681 * Set by how much the single line view should be indented. Used when a overflow indicator is
1682 * present and only during measuring
1683 */
1684 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1685 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1686 mSingleLineWidthIndention = singleLineWidthIndention;
1687 mContainingNotification.forceLayout();
1688 forceLayout();
1689 }
1690 }
1691
1692 public HybridNotificationView getSingleLineView() {
1693 return mSingleLineView;
1694 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001695
1696 public void setRemoved() {
1697 if (mExpandedRemoteInput != null) {
1698 mExpandedRemoteInput.setRemoved();
1699 }
1700 if (mHeadsUpRemoteInput != null) {
1701 mHeadsUpRemoteInput.setRemoved();
1702 }
1703 }
Adrian Roos599be342016-06-13 14:54:39 -07001704
1705 public void setContentHeightAnimating(boolean animating) {
1706 if (!animating) {
1707 mContentHeightAtAnimationStart = UNDEFINED;
1708 }
1709 }
Selim Cineke9bad242016-06-15 11:46:37 -07001710
Adrian Roose18033c2017-01-17 15:22:49 -08001711 @VisibleForTesting
1712 boolean isAnimatingVisibleType() {
1713 return mAnimationStartVisibleType != UNDEFINED;
1714 }
1715
Selim Cinekcafa87f2016-10-26 17:00:17 -07001716 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1717 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001718 selectLayout(false /* animate */, true /* force */);
1719 }
1720
Selim Cineke9bad242016-06-15 11:46:37 -07001721 public void setFocusOnVisibilityChange() {
1722 mFocusOnVisibilityChange = true;
1723 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001724
1725 public void setIconsVisible(boolean iconsVisible) {
1726 mIconsVisible = iconsVisible;
1727 updateIconVisibilities();
1728 }
1729
1730 private void updateIconVisibilities() {
1731 if (mContractedWrapper != null) {
1732 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1733 if (header != null) {
1734 header.getIcon().setForceHidden(!mIconsVisible);
1735 }
1736 }
1737 if (mHeadsUpWrapper != null) {
1738 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1739 if (header != null) {
1740 header.getIcon().setForceHidden(!mIconsVisible);
1741 }
1742 }
1743 if (mExpandedWrapper != null) {
1744 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1745 if (header != null) {
1746 header.getIcon().setForceHidden(!mIconsVisible);
1747 }
1748 }
1749 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001750
Adrian Roos4320e892017-01-24 12:50:34 -08001751 @Override
1752 public void onVisibilityAggregated(boolean isVisible) {
1753 super.onVisibilityAggregated(isVisible);
1754 if (isVisible) {
1755 fireExpandedVisibleListenerIfVisible();
1756 }
1757 }
1758
1759 /**
1760 * Sets a one-shot listener for when the expanded view becomes visible.
1761 *
1762 * This will fire the listener immediately if the expanded view is already visible.
1763 */
1764 public void setOnExpandedVisibleListener(Runnable r) {
1765 mExpandedVisibleListener = r;
1766 fireExpandedVisibleListenerIfVisible();
1767 }
1768
Kevind5022f92018-10-08 18:30:26 -07001769 /**
1770 * Set a one-shot listener to run when a given content view becomes inactive.
1771 *
1772 * @param visibleType visible type corresponding to the content view to listen
1773 * @param listener runnable to run once when the content view becomes inactive
1774 */
1775 public void performWhenContentInactive(int visibleType, Runnable listener) {
1776 View view = getViewForVisibleType(visibleType);
1777 // View is already inactive
1778 if (view == null || isContentViewInactive(visibleType)) {
1779 listener.run();
1780 return;
1781 }
1782 mOnContentViewInactiveListeners.put(view, listener);
1783 }
1784
1785 /**
1786 * Whether or not the content view is inactive. This means it should not be visible
1787 * or the showing content as removing it would cause visual jank.
1788 *
1789 * @param visibleType visible type corresponding to the content view to be removed
1790 * @return true if the content view is inactive, false otherwise
1791 */
1792 public boolean isContentViewInactive(int visibleType) {
1793 View view = getViewForVisibleType(visibleType);
1794 return isContentViewInactive(view);
1795 }
1796
1797 /**
1798 * Whether or not the content view is inactive.
1799 *
1800 * @param view view to see if its inactive
1801 * @return true if the view is inactive, false o/w
1802 */
1803 private boolean isContentViewInactive(View view) {
1804 if (view == null) {
1805 return true;
1806 }
Kevin38ce6fa2018-10-17 16:00:14 -07001807 return !isShown()
1808 || (view.getVisibility() != VISIBLE && getViewForVisibleType(mVisibleType) != view);
Kevind5022f92018-10-08 18:30:26 -07001809 }
1810
1811 @Override
1812 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
1813 super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
1814 if (isContentViewInactive(child)) {
1815 Runnable listener = mOnContentViewInactiveListeners.remove(child);
1816 if (listener != null) {
1817 listener.run();
1818 }
1819 }
1820 }
1821
Selim Cinek6743c0b2017-01-18 18:24:01 -08001822 public void setIsLowPriority(boolean isLowPriority) {
1823 mIsLowPriority = isLowPriority;
1824 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001825
1826 public boolean isDimmable() {
Kevin38ce6fa2018-10-17 16:00:14 -07001827 return mContractedWrapper != null && mContractedWrapper.isDimmable();
Selim Cinek63edaf22017-04-24 22:18:48 -07001828 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001829
1830 /**
1831 * Should a single click be disallowed on this view when on the keyguard?
1832 */
1833 public boolean disallowSingleClick(float x, float y) {
1834 NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
1835 if (visibleWrapper != null) {
1836 return visibleWrapper.disallowSingleClick(x, y);
1837 }
1838 return false;
1839 }
Selim Cinek515b2032017-11-15 10:20:19 -08001840
Selim Cinek86bfcee2018-01-17 11:00:47 -08001841 public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
1842 boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001843 if (mUserExpanding) {
Selim Cinek86bfcee2018-01-17 11:00:47 -08001844 needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
1845 bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001846 }
1847 return needsPaddings;
1848 }
1849
Selim Cinek86bfcee2018-01-17 11:00:47 -08001850 private boolean shouldClipToRounding(int visibleType, boolean topRounded,
1851 boolean bottomRounded) {
Selim Cinek515b2032017-11-15 10:20:19 -08001852 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
1853 if (visibleWrapper == null) {
1854 return false;
1855 }
Selim Cinek86bfcee2018-01-17 11:00:47 -08001856 return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001857 }
Selim Cinekde4de0e2018-01-24 16:21:07 -08001858
1859 public CharSequence getActiveRemoteInputText() {
1860 if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
1861 return mExpandedRemoteInput.getText();
1862 }
1863 if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
1864 return mHeadsUpRemoteInput.getText();
1865 }
1866 return null;
1867 }
Selim Cineked64a142018-02-06 18:06:01 -08001868
Selim Cineka77d2c52018-04-09 10:44:52 -07001869 @Override
1870 public boolean dispatchTouchEvent(MotionEvent ev) {
1871 float y = ev.getY();
1872 // We still want to distribute touch events to the remote input even if it's outside the
1873 // view boundary. We're therefore manually dispatching these events to the remote view
1874 RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
1875 if (riv != null && riv.getVisibility() == VISIBLE) {
1876 int inputStart = mUnrestrictedContentHeight - riv.getHeight();
1877 if (y <= mUnrestrictedContentHeight && y >= inputStart) {
1878 ev.offsetLocation(0, -inputStart);
1879 return riv.dispatchTouchEvent(ev);
1880 }
1881 }
1882 return super.dispatchTouchEvent(ev);
1883 }
1884
1885 /**
1886 * Overridden to make sure touches to the reply action bar actually go through to this view
1887 */
1888 @Override
1889 public boolean pointInView(float localX, float localY, float slop) {
1890 float top = mClipTopAmount;
1891 float bottom = mUnrestrictedContentHeight;
1892 return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
1893 localY < (bottom + slop);
1894 }
1895
1896 private RemoteInputView getRemoteInputForView(View child) {
1897 if (child == mExpandedChild) {
1898 return mExpandedRemoteInput;
1899 } else if (child == mHeadsUpChild) {
1900 return mHeadsUpRemoteInput;
1901 }
1902 return null;
1903 }
1904
Selim Cineked64a142018-02-06 18:06:01 -08001905 public int getExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001906 int viewType = VISIBLE_TYPE_EXPANDED;
1907 if (mExpandedChild == null) {
1908 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001909 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001910 return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001911 }
1912
1913 public int getHeadsUpHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001914 int viewType = VISIBLE_TYPE_HEADSUP;
1915 if (mHeadsUpChild == null) {
1916 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001917 }
Selim Cinek09b442e2018-05-04 15:46:18 -07001918 // The headsUp remote input quickly switches to the expanded one, so lets also include that
1919 // one
1920 return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
1921 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001922 }
1923
1924 public void setRemoteInputVisible(boolean remoteInputVisible) {
1925 mRemoteInputVisible = remoteInputVisible;
1926 setClipChildren(!remoteInputVisible);
1927 }
1928
1929 @Override
1930 public void setClipChildren(boolean clipChildren) {
1931 clipChildren = clipChildren && !mRemoteInputVisible;
1932 super.setClipChildren(clipChildren);
1933 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001934
1935 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1936 if (mContractedWrapper != null) {
1937 mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1938 }
1939 if (mHeadsUpWrapper != null) {
1940 mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1941 }
1942 if (mExpandedWrapper != null) {
1943 mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1944 }
1945 }
Selim Cinek85a8f9f2018-11-21 13:58:27 -08001946
1947 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1948 pw.print(" ");
1949 pw.print("contentView visibility: " + getVisibility());
1950 pw.print(", alpha: " + getAlpha());
1951 pw.print(", clipBounds: " + getClipBounds());
1952 pw.print(", contentHeight: " + mContentHeight);
1953 pw.print(", visibleType: " + mVisibleType);
1954 View view = getViewForVisibleType(mVisibleType);
1955 pw.print(", visibleView ");
1956 if (view != null) {
1957 pw.print(" visibility: " + view.getVisibility());
1958 pw.print(", alpha: " + view.getAlpha());
1959 pw.print(", clipBounds: " + view.getClipBounds());
1960 } else {
1961 pw.print("null");
1962 }
1963 pw.println();
1964 }
Tony Mak29996702018-11-26 16:23:34 +00001965
1966 @VisibleForTesting
1967 static class SmartRepliesAndActions {
1968 @Nullable
1969 public final SmartReplyView.SmartReplies smartReplies;
1970 @Nullable
1971 public final SmartReplyView.SmartActions smartActions;
1972 public final boolean hasFreeformRemoteInput;
1973
1974 SmartRepliesAndActions(
1975 @Nullable SmartReplyView.SmartReplies smartReplies,
1976 @Nullable SmartReplyView.SmartActions smartActions,
1977 boolean hasFreeformRemoteInput) {
1978 this.smartReplies = smartReplies;
1979 this.smartActions = smartActions;
1980 this.hasFreeformRemoteInput = hasFreeformRemoteInput;
1981 }
1982 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001983}