blob: 646617c72128e17c24006627204b63e6abb60d8d [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;
Jorim Jaggibe565df2014-04-28 17:51:23 +020022import android.content.Context;
23import android.graphics.Rect;
Selim Cinek860b6da2015-12-16 19:02:19 -080024import android.os.Build;
Selim Cinek83bc7832015-10-22 13:26:54 -070025import android.service.notification.StatusBarNotification;
Kevind5022f92018-10-08 18:30:26 -070026import android.util.ArrayMap;
Julia Reynoldsfc640012018-02-21 12:25:27 -050027import android.util.ArraySet;
Jorim Jaggibe565df2014-04-28 17:51:23 +020028import android.util.AttributeSet;
Selim Cinek011bde92017-11-22 05:53:38 -080029import android.util.Log;
Selim Cineka77d2c52018-04-09 10:44:52 -070030import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080031import android.view.NotificationHeaderView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020032import android.view.View;
Selim Cinek8d490d42015-04-10 00:05:50 -070033import android.view.ViewGroup;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010034import android.view.ViewTreeObserver;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020035import android.widget.FrameLayout;
Selim Cineke9bad242016-06-15 11:46:37 -070036import android.widget.ImageView;
Petr Cermak7997d7a2018-01-18 15:52:10 +000037import android.widget.LinearLayout;
Selim Cinek8d490d42015-04-10 00:05:50 -070038
Adrian Roose18033c2017-01-17 15:22:49 -080039import com.android.internal.annotations.VisibleForTesting;
Lucas Dupina291d192018-06-07 13:59:42 -070040import com.android.internal.util.ContrastColorUtil;
Petr Cermak10011fa2018-02-05 19:00:54 +000041import com.android.systemui.Dependency;
Jorim Jaggibe565df2014-04-28 17:51:23 +020042import com.android.systemui.R;
linanson2bcd4032019-01-14 15:22:04 +080043import com.android.systemui.statusbar.MediaTransferManager;
Rohan Shah20790b82018-07-02 17:21:04 -070044import com.android.systemui.statusbar.RemoteInputController;
45import com.android.systemui.statusbar.SmartReplyController;
46import com.android.systemui.statusbar.TransformableView;
Selim Cinekc3179332016-03-04 14:44:56 -080047import com.android.systemui.statusbar.notification.NotificationUtils;
Ned Burnsf81c4c42019-01-07 14:10:43 -050048import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Tony Mak638430e2018-10-08 19:19:10 +010049import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
Rohan Shah20790b82018-07-02 17:21:04 -070050import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070051import com.android.systemui.statusbar.phone.NotificationGroupManager;
Gustav Senntonb944ce52019-02-25 18:52:43 +000052import com.android.systemui.statusbar.policy.InflatedSmartReplies;
53import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
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 +000060
Jorim Jaggibe565df2014-04-28 17:51:23 +020061/**
Selim Cinek684a4422015-04-15 16:18:39 -070062 * A frame layout containing the actual payload of the notification, including the contracted,
63 * expanded and heads up layout. This class is responsible for clipping the content and and
64 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020065 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020066public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020067
Selim Cinek011bde92017-11-22 05:53:38 -080068 private static final String TAG = "NotificationContentView";
Gustav Sennton2d66b512019-01-31 17:48:17 +000069 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
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;
Gustav Sennton5759f872019-02-13 17:25:26 +000096 private InflatedSmartReplies mExpandedInflatedSmartReplies;
97 private InflatedSmartReplies mHeadsUpInflatedSmartReplies;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070098
Jorim Jaggi4e857f42014-11-17 19:14:04 +010099 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700100 private NotificationViewWrapper mExpandedWrapper;
101 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800102 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -0700103 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200104 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +0100105 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -0700106 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -0400107 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100108 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -0700109 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800110 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -0700111 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -0800112 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800113 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800114 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800115 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700116 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700117 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800118 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -0800119 private Runnable mExpandedVisibleListener;
Kevind5022f92018-10-08 18:30:26 -0700120 /**
121 * List of listeners for when content views become inactive (i.e. not the showing view).
122 */
123 private final ArrayMap<View, Runnable> mOnContentViewInactiveListeners = new ArrayMap<>();
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700124
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700125 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100126 = new ViewTreeObserver.OnPreDrawListener() {
127 @Override
128 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -0800129 // We need to post since we don't want the notification to animate on the very first
130 // frame
131 post(new Runnable() {
132 @Override
133 public void run() {
134 mAnimate = true;
135 }
136 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100137 getViewTreeObserver().removeOnPreDrawListener(this);
138 return true;
139 }
140 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200141
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800142 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800143 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800144 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100145 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800146 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700147 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800148 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700149 /** The visible type at the start of an animation driven transformation */
150 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800151 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700152 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700153 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700154 private PendingIntent mPreviousExpandedRemoteInputIntent;
155 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700156 private RemoteInputView mCachedExpandedRemoteInput;
157 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800158
Adrian Roos599be342016-06-13 14:54:39 -0700159 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700160 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700161 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700162 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700163 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800164 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800165 private boolean mIsContentExpandable;
Selim Cineked64a142018-02-06 18:06:01 -0800166 private boolean mRemoteInputVisible;
167 private int mUnrestrictedContentHeight;
linanson2bcd4032019-01-14 15:22:04 +0800168 private MediaTransferManager mMediaTransferManager;
Adrian Roos599be342016-06-13 14:54:39 -0700169
170
Jorim Jaggibe565df2014-04-28 17:51:23 +0200171 public NotificationContentView(Context context, AttributeSet attrs) {
172 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700173 mHybridGroupManager = new HybridGroupManager(getContext(), this);
linanson2bcd4032019-01-14 15:22:04 +0800174 mMediaTransferManager = new MediaTransferManager(getContext());
Petr Cermak10011fa2018-02-05 19:00:54 +0000175 mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
Kenny Guya0f6de82018-04-06 16:20:16 +0100176 mSmartReplyController = Dependency.get(SmartReplyController.class);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800177 initView();
178 }
179
180 public void initView() {
Selim Cinek860b6da2015-12-16 19:02:19 -0800181 mMinContractedHeight = getResources().getDimensionPixelSize(
182 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800183 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
184 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200185 }
186
Adrian Roos0aac04f2016-12-08 15:59:29 -0800187 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
188 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800189 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800190 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800191 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800192 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800193 }
194
Jorim Jaggibe565df2014-04-28 17:51:23 +0200195 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700196 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
197 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
198 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
199 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700200 int maxSize = Integer.MAX_VALUE / 2;
Selim Cinekc897bd32016-03-18 17:32:31 -0700201 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700202 if (hasFixedHeight || isHeightLimited) {
203 maxSize = MeasureSpec.getSize(heightMeasureSpec);
204 }
205 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800206 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +0100207 int notificationMaxHeight = mNotificationMaxHeight;
208 if (mExpandedSmartReplyView != null) {
209 notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
210 }
Selim Cinekbee4e072018-05-21 22:06:43 -0700211 notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700212 int size = notificationMaxHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800213 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800214 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800215 if (layoutParams.height >= 0) {
216 // An actual height is set
Selim Cinekbe570ce2018-05-11 12:29:52 -0700217 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800218 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800219 }
Selim Cinekbe570ce2018-05-11 12:29:52 -0700220 int spec = MeasureSpec.makeMeasureSpec(size, useExactly
Selim Cinek6743c0b2017-01-18 18:24:01 -0800221 ? MeasureSpec.EXACTLY
222 : MeasureSpec.AT_MOST);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800223 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800224 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
225 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700226 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800227 int heightSpec;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700228 int size = mSmallHeight;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800229 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
230 boolean useExactly = false;
231 if (layoutParams.height >= 0) {
232 // An actual height is set
233 size = Math.min(size, layoutParams.height);
234 useExactly = true;
235 }
236 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800237 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
238 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800239 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800240 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800241 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800242 int measuredHeight = mContractedChild.getMeasuredHeight();
243 if (measuredHeight < mMinContractedHeight) {
244 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800245 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800246 }
247 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800248 if (updateContractedHeaderWidth()) {
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800249 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700250 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700251 if (mExpandedChild != null
252 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
253 // the Expanded child is smaller then the collapsed. Let's remeasure it.
254 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
255 MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800256 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek1ba41d12016-04-21 16:14:46 -0700257 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700258 }
259 if (mHeadsUpChild != null) {
Selim Cinek396caca2018-04-10 17:46:46 -0700260 int maxHeight = mHeadsUpHeight;
Gustav Senntond0e84532018-12-03 16:48:36 +0000261 if (mHeadsUpSmartReplyView != null) {
262 maxHeight += mHeadsUpSmartReplyView.getHeightUpperLimit();
263 }
Selim Cinek396caca2018-04-10 17:46:46 -0700264 maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700265 int size = maxHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700266 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800267 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700268 if (layoutParams.height >= 0) {
269 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800270 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800271 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700272 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800273 measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800274 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800275 : MeasureSpec.AT_MOST), 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700276 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
277 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700278 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700279 int singleLineWidthSpec = widthMeasureSpec;
280 if (mSingleLineWidthIndention != 0
281 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
282 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
283 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700284 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700285 }
286 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinekbe570ce2018-05-11 12:29:52 -0700287 MeasureSpec.makeMeasureSpec(mNotificationMaxHeight, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700288 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
289 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800290 if (mAmbientChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700291 int size = mNotificationAmbientHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800292 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800293 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800294 if (layoutParams.height >= 0) {
295 // An actual height is set
296 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800297 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800298 }
299 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800300 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
301 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800302 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
303 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700304 if (mAmbientSingleLineChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700305 int size = mNotificationAmbientHeight;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700306 ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
307 boolean useExactly = false;
308 if (layoutParams.height >= 0) {
309 // An actual height is set
310 size = Math.min(size, layoutParams.height);
311 useExactly = true;
312 }
313 int ambientSingleLineWidthSpec = widthMeasureSpec;
314 if (mSingleLineWidthIndention != 0
315 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
316 ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
317 width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
318 MeasureSpec.EXACTLY);
319 }
320 mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
321 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
322 : MeasureSpec.AT_MOST));
323 maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
324 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700325 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700326 setMeasuredDimension(width, ownHeight);
327 }
328
Selim Cineked64a142018-02-06 18:06:01 -0800329 /**
330 * Get the extra height that needs to be added to the notification height for a given
331 * {@link RemoteInputView}.
332 * This is needed when the user is inline replying in order to ensure that the reply bar has
333 * enough padding.
334 *
335 * @param remoteInput The remote input to check.
336 * @return The extra height needed.
337 */
338 private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
Selim Cinek09b442e2018-05-04 15:46:18 -0700339 if (remoteInput != null && (remoteInput.isActive() || remoteInput.isSending())) {
Selim Cineked64a142018-02-06 18:06:01 -0800340 return getResources().getDimensionPixelSize(
341 com.android.internal.R.dimen.notification_content_margin);
342 }
343 return 0;
344 }
345
Selim Cinek6ecc8102016-01-26 18:26:19 -0800346 private boolean updateContractedHeaderWidth() {
347 // We need to update the expanded and the collapsed header to have exactly the same with to
348 // have the expand buttons laid out at the same location.
349 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
350 if (contractedHeader != null) {
351 if (mExpandedChild != null
352 && mExpandedWrapper.getNotificationHeader() != null) {
353 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
354 int expandedSize = expandedHeader.getMeasuredWidth()
355 - expandedHeader.getPaddingEnd();
356 int collapsedSize = contractedHeader.getMeasuredWidth()
357 - expandedHeader.getPaddingEnd();
358 if (expandedSize != collapsedSize) {
359 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
360 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800361 contractedHeader.isLayoutRtl()
362 ? paddingEnd
363 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800364 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800365 contractedHeader.isLayoutRtl()
366 ? contractedHeader.getPaddingLeft()
367 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800368 contractedHeader.getPaddingBottom());
369 contractedHeader.setShowWorkBadgeAtEnd(true);
370 return true;
371 }
372 } else {
373 int paddingEnd = mNotificationContentMarginEnd;
374 if (contractedHeader.getPaddingEnd() != paddingEnd) {
375 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800376 contractedHeader.isLayoutRtl()
377 ? paddingEnd
378 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800379 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800380 contractedHeader.isLayoutRtl()
381 ? contractedHeader.getPaddingLeft()
382 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800383 contractedHeader.getPaddingBottom());
384 contractedHeader.setShowWorkBadgeAtEnd(false);
385 return true;
386 }
387 }
388 }
389 return false;
390 }
391
Selim Cinek860b6da2015-12-16 19:02:19 -0800392 private boolean shouldContractedBeFixedSize() {
393 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
394 }
395
Selim Cinek8d490d42015-04-10 00:05:50 -0700396 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200397 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700398 int previousHeight = 0;
399 if (mExpandedChild != null) {
400 previousHeight = mExpandedChild.getHeight();
401 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200402 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700403 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
404 mContentHeightAtAnimationStart = previousHeight;
405 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200406 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700407 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700408 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
409 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700410 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200411 }
412
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100413 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100414 protected void onAttachedToWindow() {
415 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100416 updateVisibility();
417 }
418
Selim Cinekcab4a602014-09-03 14:47:57 +0200419 public View getContractedChild() {
420 return mContractedChild;
421 }
422
423 public View getExpandedChild() {
424 return mExpandedChild;
425 }
426
Selim Cinek8d490d42015-04-10 00:05:50 -0700427 public View getHeadsUpChild() {
428 return mHeadsUpChild;
429 }
430
Adrian Roos0aac04f2016-12-08 15:59:29 -0800431 public View getAmbientChild() {
432 return mAmbientChild;
433 }
434
Adrian Roos6f6e1592017-05-02 16:22:53 -0700435 public HybridNotificationView getAmbientSingleLineChild() {
436 return mAmbientSingleLineChild;
437 }
438
Kevin38ce6fa2018-10-17 16:00:14 -0700439 /**
440 * Sets the contracted view. Child may be null to remove the content view.
441 *
442 * @param child contracted content view to set
443 */
444 public void setContractedChild(@Nullable View child) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200445 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200446 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200447 removeView(mContractedChild);
448 }
Kevin38ce6fa2018-10-17 16:00:14 -0700449 if (child == null) {
450 mContractedChild = null;
451 mContractedWrapper = null;
452 if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
453 mTransformationStartVisibleType = UNDEFINED;
454 }
455 return;
456 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200457 addView(child);
458 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700459 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
460 mContainingNotification);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800461 }
462
463 private NotificationViewWrapper getWrapperForView(View child) {
464 if (child == mContractedChild) {
465 return mContractedWrapper;
466 }
467 if (child == mExpandedChild) {
468 return mExpandedWrapper;
469 }
470 if (child == mHeadsUpChild) {
471 return mHeadsUpWrapper;
472 }
473 if (child == mAmbientChild) {
474 return mAmbientWrapper;
475 }
476 return null;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200477 }
478
Kevin38ce6fa2018-10-17 16:00:14 -0700479 /**
480 * Sets the expanded view. Child may be null to remove the content view.
481 *
482 * @param child expanded content view to set
483 */
484 public void setExpandedChild(@Nullable View child) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200485 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800486 mPreviousExpandedRemoteInputIntent = null;
487 if (mExpandedRemoteInput != null) {
488 mExpandedRemoteInput.onNotificationUpdateOrReset();
489 if (mExpandedRemoteInput.isActive()) {
490 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
491 mCachedExpandedRemoteInput = mExpandedRemoteInput;
492 mExpandedRemoteInput.dispatchStartTemporaryDetach();
493 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
494 }
495 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200496 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200497 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800498 mExpandedRemoteInput = null;
499 }
500 if (child == null) {
501 mExpandedChild = null;
502 mExpandedWrapper = null;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800503 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
504 mTransformationStartVisibleType = UNDEFINED;
505 }
Selim Cinek9f186642018-11-29 15:47:40 -0800506 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
507 selectLayout(false /* animate */, true /* force */);
508 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800509 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200510 }
511 addView(child);
512 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700513 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
514 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200515 }
516
Kevin38ce6fa2018-10-17 16:00:14 -0700517 /**
518 * Sets the heads up view. Child may be null to remove the content view.
519 *
520 * @param child heads up content view to set
521 */
522 public void setHeadsUpChild(@Nullable View child) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700523 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800524 mPreviousHeadsUpRemoteInputIntent = null;
525 if (mHeadsUpRemoteInput != null) {
526 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
527 if (mHeadsUpRemoteInput.isActive()) {
528 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
529 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
530 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
531 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
532 }
533 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700534 mHeadsUpChild.animate().cancel();
535 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800536 mHeadsUpRemoteInput = null;
537 }
538 if (child == null) {
539 mHeadsUpChild = null;
540 mHeadsUpWrapper = null;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800541 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
542 mTransformationStartVisibleType = UNDEFINED;
543 }
Selim Cinek9f186642018-11-29 15:47:40 -0800544 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
545 selectLayout(false /* animate */, true /* force */);
546 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800547 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700548 }
549 addView(child);
550 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700551 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
552 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700553 }
554
Kevin38ce6fa2018-10-17 16:00:14 -0700555 /**
556 * Sets the ambient view. Child may be null to remove the content view.
557 *
558 * @param child ambient content view to set
559 */
560 public void setAmbientChild(@Nullable View child) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800561 if (mAmbientChild != null) {
562 mAmbientChild.animate().cancel();
563 removeView(mAmbientChild);
564 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700565 if (child == null) {
Kevind4660b22018-09-27 10:57:35 -0700566 mAmbientChild = null;
567 mAmbientWrapper = null;
Kevind4660b22018-09-27 10:57:35 -0700568 if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
569 mTransformationStartVisibleType = UNDEFINED;
570 }
Selim Cinek9f186642018-11-29 15:47:40 -0800571 if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
572 selectLayout(false /* animate */, true /* force */);
573 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700574 return;
575 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800576 addView(child);
577 mAmbientChild = child;
578 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
579 mContainingNotification);
580 }
581
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100582 @Override
583 protected void onVisibilityChanged(View changedView, int visibility) {
584 super.onVisibilityChanged(changedView, visibility);
585 updateVisibility();
Kevin38ce6fa2018-10-17 16:00:14 -0700586 if (visibility != VISIBLE) {
587 // View is no longer visible so all content views are inactive.
588 for (Runnable r : mOnContentViewInactiveListeners.values()) {
589 r.run();
590 }
591 mOnContentViewInactiveListeners.clear();
592 }
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100593 }
594
595 private void updateVisibility() {
596 setVisible(isShown());
597 }
598
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700599 @Override
600 protected void onDetachedFromWindow() {
601 super.onDetachedFromWindow();
602 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
603 }
604
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100605 private void setVisible(final boolean isVisible) {
606 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700607 // This call can happen multiple times, but removing only removes a single one.
608 // We therefore need to remove the old one.
609 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100610 // We only animate if we are drawn at least once, otherwise the view might animate when
611 // it's shown the first time
612 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
613 } else {
614 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
615 mAnimate = false;
616 }
617 }
618
Selim Cineke9bad242016-06-15 11:46:37 -0700619 private void focusExpandButtonIfNecessary() {
620 if (mFocusOnVisibilityChange) {
621 NotificationHeaderView header = getVisibleNotificationHeader();
622 if (header != null) {
623 ImageView expandButton = header.getExpandButton();
624 if (expandButton != null) {
625 expandButton.requestAccessibilityFocus();
626 }
627 }
628 mFocusOnVisibilityChange = false;
629 }
630 }
631
Selim Cinekb5605e52015-02-20 18:21:41 +0100632 public void setContentHeight(int contentHeight) {
Selim Cineked64a142018-02-06 18:06:01 -0800633 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
634 int maxContentHeight = mContainingNotification.getIntrinsicHeight()
635 - getExtraRemoteInputHeight(mExpandedRemoteInput)
636 - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
637 mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100638 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400639
Kevin38ce6fa2018-10-17 16:00:14 -0700640 if (mContractedChild == null) {
641 // Contracted child may be null if this is the public content view and we don't need to
642 // show it.
643 return;
644 }
645
Adrian Roos181385c2016-05-05 17:45:44 -0400646 int minHeightHint = getMinContentHeightHint();
647
648 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
649 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800650 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400651 }
652
653 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
654 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800655 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400656 }
657
Jorim Jaggibe565df2014-04-28 17:51:23 +0200658 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700659 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200660 }
661
Adrian Roos181385c2016-05-05 17:45:44 -0400662 /**
663 * @return the minimum apparent height that the wrapper should allow for the purpose
664 * of aligning elements at the bottom edge. If this is larger than the content
665 * height, the notification is clipped instead of being further shrunk.
666 */
667 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700668 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400669 return mContext.getResources().getDimensionPixelSize(
670 com.android.internal.R.dimen.notification_action_list_height);
671 }
Adrian Roos599be342016-06-13 14:54:39 -0700672
673 // Transition between heads-up & expanded, or pinned.
674 if (mHeadsUpChild != null && mExpandedChild != null) {
675 boolean transitioningBetweenHunAndExpanded =
676 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
677 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700678 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800679 && (mIsHeadsUp || mHeadsUpAnimatingAway)
680 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700681 if (transitioningBetweenHunAndExpanded || pinned) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800682 return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
683 getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700684 }
685 }
686
687 // Size change of the expanded version
688 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
689 && mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800690 return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700691 }
692
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700693 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800694 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
695 hint = mAmbientChild.getHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700696 } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
697 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
698 hint = mAmbientSingleLineChild.getHeight();
Adrian Roos05d3d002017-01-26 15:28:07 -0800699 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800700 hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
Adrian Roos599be342016-06-13 14:54:39 -0700701 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800702 hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
Adrian Roos181385c2016-05-05 17:45:44 -0400703 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800704 hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
705 + mContext.getResources().getDimensionPixelSize(
706 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400707 }
Adrian Roos599be342016-06-13 14:54:39 -0700708
709 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800710 hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700711 }
712 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400713 }
714
Adrian Roos599be342016-06-13 14:54:39 -0700715 private boolean isTransitioningFromTo(int from, int to) {
716 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
717 && mVisibleType == to;
718 }
719
720 private boolean isVisibleOrTransitioning(int type) {
721 return mVisibleType == type || mTransformationStartVisibleType == type
722 || mAnimationStartVisibleType == type;
723 }
724
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800725 private void updateContentTransformation() {
726 int visibleType = calculateVisibleType();
727 if (visibleType != mVisibleType) {
728 // A new transformation starts
729 mTransformationStartVisibleType = mVisibleType;
730 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
731 final TransformableView hiddenView = getTransformableViewForVisibleType(
732 mTransformationStartVisibleType);
733 shownView.transformFrom(hiddenView, 0.0f);
734 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
735 hiddenView.transformTo(shownView, 0.0f);
736 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800737 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800738 }
Selim Cineke8578872016-05-03 16:42:50 -0700739 if (mForceSelectNextLayout) {
740 forceUpdateVisibilities();
741 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800742 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700743 && mVisibleType != mTransformationStartVisibleType
744 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800745 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
746 final TransformableView hiddenView = getTransformableViewForVisibleType(
747 mTransformationStartVisibleType);
748 float transformationAmount = calculateTransformationAmount();
749 shownView.transformFrom(hiddenView, transformationAmount);
750 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800751 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800752 } else {
753 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800754 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800755 }
756 }
757
Selim Cinekc3179332016-03-04 14:44:56 -0800758 private void updateBackgroundTransformation(float transformationAmount) {
759 int endColor = getBackgroundColor(mVisibleType);
760 int startColor = getBackgroundColor(mTransformationStartVisibleType);
761 if (endColor != startColor) {
762 if (startColor == 0) {
763 startColor = mContainingNotification.getBackgroundColorWithoutTint();
764 }
765 if (endColor == 0) {
766 endColor = mContainingNotification.getBackgroundColorWithoutTint();
767 }
768 endColor = NotificationUtils.interpolateColors(startColor, endColor,
769 transformationAmount);
770 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400771 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800772 mContainingNotification.setContentBackground(endColor, false, this);
773 }
774
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800775 private float calculateTransformationAmount() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800776 int startHeight = getViewHeight(mTransformationStartVisibleType);
777 int endHeight = getViewHeight(mVisibleType);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800778 int progress = Math.abs(mContentHeight - startHeight);
779 int totalDistance = Math.abs(endHeight - startHeight);
Selim Cinek011bde92017-11-22 05:53:38 -0800780 if (totalDistance == 0) {
781 Log.wtf(TAG, "the total transformation distance is 0"
782 + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
783 + "\n VisibleType: " + mVisibleType + " height: " + endHeight
784 + "\n mContentHeight: " + mContentHeight);
785 return 1.0f;
786 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800787 float amount = (float) progress / (float) totalDistance;
788 return Math.min(1.0f, amount);
789 }
790
Selim Cinekb5605e52015-02-20 18:21:41 +0100791 public int getContentHeight() {
792 return mContentHeight;
793 }
794
Jorim Jaggibe565df2014-04-28 17:51:23 +0200795 public int getMaxHeight() {
Kevin38ce6fa2018-10-17 16:00:14 -0700796 if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700797 return getShowingAmbientView().getHeight();
798 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800799 return getViewHeight(VISIBLE_TYPE_EXPANDED)
800 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cinekc494e382017-01-31 16:09:23 -0800801 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800802 return getViewHeight(VISIBLE_TYPE_HEADSUP)
803 + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
Kevin38ce6fa2018-10-17 16:00:14 -0700804 } else if (mContractedChild != null) {
805 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
Selim Cinek8d490d42015-04-10 00:05:50 -0700806 }
Kevin38ce6fa2018-10-17 16:00:14 -0700807 return mNotificationMaxHeight;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800808 }
809
810 private int getViewHeight(int visibleType) {
811 View view = getViewForVisibleType(visibleType);
812 int height = view.getHeight();
813 NotificationViewWrapper viewWrapper = getWrapperForView(view);
814 if (viewWrapper != null) {
815 height += viewWrapper.getHeaderTranslation();
816 }
817 return height;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200818 }
819
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200820 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800821 return getMinHeight(false /* likeGroupExpanded */);
822 }
823
824 public int getMinHeight(boolean likeGroupExpanded) {
Kevin38ce6fa2018-10-17 16:00:14 -0700825 if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700826 return getShowingAmbientView().getHeight();
827 } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Kevin38ce6fa2018-10-17 16:00:14 -0700828 return mContractedChild != null
829 ? getViewHeight(VISIBLE_TYPE_CONTRACTED) : mMinContractedHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800830 } else {
831 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700832 }
833 }
834
Adrian Roos6f6e1592017-05-02 16:22:53 -0700835 public View getShowingAmbientView() {
836 View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
837 if (v != null) {
838 return v;
839 } else {
840 return mContractedChild;
841 }
842 }
843
Selim Cinek83bc7832015-10-22 13:26:54 -0700844 private boolean isGroupExpanded() {
845 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200846 }
847
Jorim Jaggibe565df2014-04-28 17:51:23 +0200848 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200849 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200850 updateClipping();
851 }
852
Selim Cineka686b2c2016-10-26 13:58:27 -0700853
854 public void setClipBottomAmount(int clipBottomAmount) {
855 mClipBottomAmount = clipBottomAmount;
856 updateClipping();
857 }
858
Selim Cinek875a3a12016-11-18 17:52:16 -0800859 @Override
860 public void setTranslationY(float translationY) {
861 super.setTranslationY(translationY);
862 updateClipping();
863 }
864
Jorim Jaggibe565df2014-04-28 17:51:23 +0200865 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100866 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800867 int top = (int) (mClipTopAmount - getTranslationY());
Selim Cineked64a142018-02-06 18:06:01 -0800868 int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700869 bottom = Math.max(top, bottom);
870 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100871 setClipBounds(mClipBounds);
872 } else {
873 setClipBounds(null);
874 }
875 }
876
877 public void setClipToActualHeight(boolean clipToActualHeight) {
878 mClipToActualHeight = clipToActualHeight;
879 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200880 }
881
Jorim Jaggi11298832014-05-24 16:18:38 +0200882 private void selectLayout(boolean animate, boolean force) {
883 if (mContractedChild == null) {
884 return;
885 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800886 if (mUserExpanding) {
887 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700888 } else {
889 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700890 boolean changedType = visibleType != mVisibleType;
891 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400892 View visibleView = getViewForVisibleType(visibleType);
893 if (visibleView != null) {
894 visibleView.setVisibility(VISIBLE);
895 transferRemoteInputFocus(visibleType);
896 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700897
Selim Cinek589fd3e2016-04-26 18:17:57 -0700898 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
899 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
900 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
901 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
902 animateToVisibleType(visibleType);
903 } else {
904 updateViewVisibilities(visibleType);
905 }
906 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700907 if (changedType) {
908 focusExpandButtonIfNecessary();
909 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800910 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
911 if (visibleWrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800912 visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
913 getMinContentHeightHint());
Adrian Roos05d3d002017-01-26 15:28:07 -0800914 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700915 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200916 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700917 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700918 }
919
920 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800921 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
922 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
923 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
924 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
925 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700926 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
927 mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800928 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800929 // forceUpdateVisibilities cancels outstanding animations without updating the
930 // mAnimationStartVisibleType. Do so here instead.
931 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800932 }
933
Adrian Roos4320e892017-01-24 12:50:34 -0800934 private void fireExpandedVisibleListenerIfVisible() {
935 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
936 && mExpandedChild.getVisibility() == VISIBLE) {
937 Runnable listener = mExpandedVisibleListener;
938 mExpandedVisibleListener = null;
939 listener.run();
940 }
941 }
942
Adrian Roos0aac04f2016-12-08 15:59:29 -0800943 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
944 if (view == null) {
945 return;
946 }
947 boolean visible = mVisibleType == type
948 || mTransformationStartVisibleType == type;
949 if (!visible) {
950 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700951 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800952 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200953 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200954 }
955
Selim Cinekc3179332016-03-04 14:44:56 -0800956 public void updateBackgroundColor(boolean animate) {
957 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400958 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800959 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
960 }
961
Kenny Guy14d035c2018-05-02 19:10:36 +0100962 public void setBackgroundTintColor(int color) {
963 if (mExpandedSmartReplyView != null) {
964 mExpandedSmartReplyView.setBackgroundTintColor(color);
965 }
Gustav Senntond0e84532018-12-03 16:48:36 +0000966 if (mHeadsUpSmartReplyView != null) {
967 mHeadsUpSmartReplyView.setBackgroundTintColor(color);
968 }
Kenny Guy14d035c2018-05-02 19:10:36 +0100969 }
970
Mady Mellorb0a82462016-04-30 17:31:02 -0700971 public int getVisibleType() {
972 return mVisibleType;
973 }
974
975 public int getBackgroundColorForExpansionState() {
976 // When expanding or user locked we want the new type, when collapsing we want
977 // the original type
978 final int visibleType = (mContainingNotification.isGroupExpanded()
979 || mContainingNotification.isUserLocked())
980 ? calculateVisibleType()
981 : getVisibleType();
982 return getBackgroundColor(visibleType);
983 }
984
985 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800986 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
987 int customBackgroundColor = 0;
988 if (currentVisibleWrapper != null) {
989 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
990 }
991 return customBackgroundColor;
992 }
993
Selim Cinek684a4422015-04-15 16:18:39 -0700994 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800995 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
996 mContractedChild, mContractedWrapper);
997 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
998 mExpandedChild, mExpandedWrapper);
999 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
1000 mHeadsUpChild, mHeadsUpWrapper);
1001 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
1002 mSingleLineView, mSingleLineView);
1003 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
1004 mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -07001005 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
1006 mAmbientSingleLineChild, mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -08001007 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -08001008 // updateViewVisibilities cancels outstanding animations without updating the
1009 // mAnimationStartVisibleType. Do so here instead.
1010 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001011 }
1012
1013 private void updateViewVisibility(int visibleType, int type, View view,
1014 TransformableView wrapper) {
1015 if (view != null) {
1016 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -07001017 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001018 }
1019
Selim Cinek4ffd6362015-12-29 15:12:23 +01001020 private void animateToVisibleType(int visibleType) {
1021 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
1022 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -07001023 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -08001024 shownView.setVisible(true);
1025 return;
1026 }
Adrian Roos599be342016-06-13 14:54:39 -07001027 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001028 shownView.transformFrom(hiddenView);
1029 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
1030 hiddenView.transformTo(shownView, new Runnable() {
1031 @Override
1032 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -08001033 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
1034 hiddenView.setVisible(false);
1035 }
Adrian Roos599be342016-06-13 14:54:39 -07001036 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001037 }
1038 });
Adrian Roos4320e892017-01-24 12:50:34 -08001039 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +02001040 }
1041
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001042 private void transferRemoteInputFocus(int visibleType) {
1043 if (visibleType == VISIBLE_TYPE_HEADSUP
1044 && mHeadsUpRemoteInput != null
1045 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
1046 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
1047 }
1048 if (visibleType == VISIBLE_TYPE_EXPANDED
1049 && mExpandedRemoteInput != null
1050 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
1051 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
1052 }
1053 }
1054
Selim Cinek684a4422015-04-15 16:18:39 -07001055 /**
1056 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +01001057 * @return the corresponding transformable view according to the given visible type
1058 */
1059 private TransformableView getTransformableViewForVisibleType(int visibleType) {
1060 switch (visibleType) {
1061 case VISIBLE_TYPE_EXPANDED:
1062 return mExpandedWrapper;
1063 case VISIBLE_TYPE_HEADSUP:
1064 return mHeadsUpWrapper;
1065 case VISIBLE_TYPE_SINGLELINE:
1066 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001067 case VISIBLE_TYPE_AMBIENT:
1068 return mAmbientWrapper;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001069 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1070 return mAmbientSingleLineChild;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001071 default:
1072 return mContractedWrapper;
1073 }
1074 }
1075
1076 /**
1077 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -07001078 * @return the corresponding view according to the given visible type
1079 */
1080 private View getViewForVisibleType(int visibleType) {
1081 switch (visibleType) {
1082 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -07001083 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001084 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -07001085 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -07001086 case VISIBLE_TYPE_SINGLELINE:
1087 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001088 case VISIBLE_TYPE_AMBIENT:
1089 return mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001090 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1091 return mAmbientSingleLineChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001092 default:
1093 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -07001094 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001095 }
1096
Selim Cinek131f1a42017-06-05 17:50:19 -07001097 public NotificationViewWrapper getVisibleWrapper(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -08001098 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -08001099 case VISIBLE_TYPE_EXPANDED:
1100 return mExpandedWrapper;
1101 case VISIBLE_TYPE_HEADSUP:
1102 return mHeadsUpWrapper;
1103 case VISIBLE_TYPE_CONTRACTED:
1104 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001105 case VISIBLE_TYPE_AMBIENT:
1106 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -08001107 default:
1108 return null;
1109 }
1110 }
1111
Selim Cinek684a4422015-04-15 16:18:39 -07001112 /**
1113 * @return one of the static enum types in this view, calculated form the current state
1114 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001115 public int calculateVisibleType() {
Kevina97ea052018-09-11 13:53:18 -07001116 if (mContainingNotification.isOnAmbient()) {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001117 if (mIsChildInGroup && mAmbientSingleLineChild != null) {
1118 return VISIBLE_TYPE_AMBIENT_SINGLELINE;
1119 } else if (mAmbientChild != null) {
1120 return VISIBLE_TYPE_AMBIENT;
1121 } else {
1122 return VISIBLE_TYPE_CONTRACTED;
1123 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001124 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001125 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -08001126 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001127 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001128 ? mContainingNotification.getMaxContentHeight()
1129 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -08001130 if (height == 0) {
1131 height = mContentHeight;
1132 }
Selim Cinek42357e02016-02-24 18:48:01 -08001133 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -08001134 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -07001135 ? VISIBLE_TYPE_SINGLELINE
1136 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001137 return mTransformationStartVisibleType == collapsedVisualType
1138 ? expandedVisualType
1139 : collapsedVisualType;
1140 }
Selim Cinek51d94912016-03-02 15:34:28 -08001141 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
1142 int viewHeight = mContentHeight;
1143 if (intrinsicHeight != 0) {
1144 // the intrinsicHeight might be 0 because it was just reset.
1145 viewHeight = Math.min(mContentHeight, intrinsicHeight);
1146 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001147 return getVisualTypeForHeight(viewHeight);
1148 }
1149
1150 private int getVisualTypeForHeight(float viewHeight) {
1151 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001152 if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
Adrian Roos777ef562015-12-01 17:37:14 -08001153 return VISIBLE_TYPE_EXPANDED;
1154 }
Selim Cinek414ad332017-02-24 19:06:12 -08001155 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001156 return VISIBLE_TYPE_SINGLELINE;
1157 }
Adrian Roos777ef562015-12-01 17:37:14 -08001158
Selim Cinekc494e382017-01-31 16:09:23 -08001159 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
1160 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001161 if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -07001162 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -07001163 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001164 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001165 }
1166 } else {
Kevin38ce6fa2018-10-17 16:00:14 -07001167 if (noExpandedChild || (mContractedChild != null
1168 && viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
Selim Cinek6ac3fa82016-05-10 17:11:54 -04001169 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001170 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -07001171 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001172 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001173 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001174 }
1175 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001176 }
1177
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001178 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001179 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001180 }
John Spurlocke15452b2014-08-21 09:44:39 -04001181
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001182 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -08001183 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -08001184 return;
1185 }
John Spurlocke15452b2014-08-21 09:44:39 -04001186 mDark = dark;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001187 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -04001188 }
1189
Selim Cinek8d490d42015-04-10 00:05:50 -07001190 public void setHeadsUp(boolean headsUp) {
1191 mIsHeadsUp = headsUp;
1192 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -08001193 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -07001194 }
1195
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +02001196 @Override
1197 public boolean hasOverlappingRendering() {
1198
1199 // This is not really true, but good enough when fading from the contracted to the expanded
1200 // layout, and saves us some layers.
1201 return false;
1202 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001203
Selim Cinek1a48bab2017-02-17 19:38:40 -08001204 public void setLegacy(boolean legacy) {
1205 mLegacy = legacy;
1206 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -08001207 }
1208
Selim Cinek1a48bab2017-02-17 19:38:40 -08001209 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -08001210 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001211 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001212 }
1213 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001214 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001215 }
1216 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001217 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001218 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001219 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001220
Selim Cinek83bc7832015-10-22 13:26:54 -07001221 public void setIsChildInGroup(boolean isChildInGroup) {
1222 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001223 if (mContractedChild != null) {
1224 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1225 }
1226 if (mExpandedChild != null) {
1227 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1228 }
1229 if (mHeadsUpChild != null) {
1230 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1231 }
1232 if (mAmbientChild != null) {
1233 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1234 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001235 updateAllSingleLineViews();
Selim Cinek83bc7832015-10-22 13:26:54 -07001236 }
1237
Ned Burnsf81c4c42019-01-07 14:10:43 -05001238 public void onNotificationUpdated(NotificationEntry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001239 mStatusBarNotification = entry.notification;
Kevind5022f92018-10-08 18:30:26 -07001240 mOnContentViewInactiveListeners.clear();
Selim Cinek860b6da2015-12-16 19:02:19 -08001241 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001242 updateAllSingleLineViews();
Evan Laird94492852018-10-25 13:43:01 -04001243 ExpandableNotificationRow row = entry.getRow();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001244 if (mContractedChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001245 mContractedWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001246 }
1247 if (mExpandedChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001248 mExpandedWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001249 }
1250 if (mHeadsUpChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001251 mHeadsUpWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001252 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001253 if (mAmbientChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001254 mAmbientWrapper.onContentUpdated(row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001255 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001256 applyRemoteInputAndSmartReply(entry);
linanson2bcd4032019-01-14 15:22:04 +08001257 applyMediaTransfer(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001258 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001259 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001260 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001261 mPreviousExpandedRemoteInputIntent = null;
1262 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001263 }
1264
Adrian Roos6f6e1592017-05-02 16:22:53 -07001265 private void updateAllSingleLineViews() {
1266 updateSingleLineView();
1267 updateAmbientSingleLineView();
1268 }
Tony Mak638430e2018-10-08 19:19:10 +01001269
Selim Cinek83bc7832015-10-22 13:26:54 -07001270 private void updateSingleLineView() {
1271 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001272 boolean isNewView = mSingleLineView == null;
Selim Cinekc897bd32016-03-18 17:32:31 -07001273 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001274 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001275 if (isNewView) {
1276 updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
1277 mSingleLineView, mSingleLineView);
1278 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001279 } else if (mSingleLineView != null) {
1280 removeView(mSingleLineView);
1281 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001282 }
1283 }
1284
Adrian Roos6f6e1592017-05-02 16:22:53 -07001285 private void updateAmbientSingleLineView() {
1286 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001287 boolean isNewView = mAmbientSingleLineChild == null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001288 mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
1289 mAmbientSingleLineChild, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001290 if (isNewView) {
1291 updateViewVisibility(mVisibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
1292 mAmbientSingleLineChild, mAmbientSingleLineChild);
1293 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001294 } else if (mAmbientSingleLineChild != null) {
1295 removeView(mAmbientSingleLineChild);
1296 mAmbientSingleLineChild = null;
1297 }
1298 }
1299
linanson2bcd4032019-01-14 15:22:04 +08001300 private void applyMediaTransfer(final NotificationEntry entry) {
1301 View bigContentView = mExpandedChild;
1302 if (bigContentView == null || !entry.isMediaNotification()) {
1303 return;
1304 }
1305
1306 View mediaActionContainer = bigContentView.findViewById(
1307 com.android.internal.R.id.media_actions);
1308 if (!(mediaActionContainer instanceof LinearLayout)) {
1309 return;
1310 }
1311
1312 mMediaTransferManager.applyMediaTransferView((ViewGroup) mediaActionContainer,
1313 entry);
1314 }
1315
Ned Burnsf81c4c42019-01-07 14:10:43 -05001316 private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001317 if (mRemoteInputController == null) {
1318 return;
1319 }
1320
Gustav Sennton5759f872019-02-13 17:25:26 +00001321 applyRemoteInput(entry, InflatedSmartReplies.hasFreeformRemoteInput(entry));
1322
1323 if (mExpandedInflatedSmartReplies == null && mHeadsUpInflatedSmartReplies == null) {
1324 if (DEBUG) {
1325 Log.d(TAG, "Both expanded, and heads-up InflatedSmartReplies are null, "
1326 + "don't add smart replies.");
1327 }
1328 return;
1329 }
1330 // The inflated smart-reply objects for the expanded view and the heads-up view both contain
1331 // the same SmartRepliesAndActions to avoid discrepancies between the two views. We here
1332 // reuse that object for our local SmartRepliesAndActions to avoid discrepancies between
1333 // this class and the InflatedSmartReplies classes.
1334 SmartRepliesAndActions smartRepliesAndActions = mExpandedInflatedSmartReplies != null
1335 ? mExpandedInflatedSmartReplies.getSmartRepliesAndActions()
1336 : mHeadsUpInflatedSmartReplies.getSmartRepliesAndActions();
Gustav Sennton2d66b512019-01-31 17:48:17 +00001337 if (DEBUG) {
1338 Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.",
1339 entry.notification.getKey(),
1340 smartRepliesAndActions.smartActions == null ? 0 :
1341 smartRepliesAndActions.smartActions.actions.size(),
1342 smartRepliesAndActions.smartReplies == null ? 0 :
1343 smartRepliesAndActions.smartReplies.choices.length));
1344 }
Gustav Senntoneab53682018-11-01 16:30:23 +00001345 applySmartReplyView(smartRepliesAndActions, entry);
1346 }
Tony Mak638430e2018-10-08 19:19:10 +01001347
Ned Burnsf81c4c42019-01-07 14:10:43 -05001348 private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001349 View bigContentView = mExpandedChild;
1350 if (bigContentView != null) {
Tony Mak638430e2018-10-08 19:19:10 +01001351 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001352 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1353 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001354 } else {
1355 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001356 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001357 if (mCachedExpandedRemoteInput != null
1358 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1359 // We had a cached remote input but didn't reuse it. Clean up required.
1360 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1361 }
1362 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001363
Adrian Roosb88b1a12015-12-09 18:51:05 -08001364 View headsUpContentView = mHeadsUpChild;
1365 if (headsUpContentView != null) {
Tony Mak638430e2018-10-08 19:19:10 +01001366 mHeadsUpRemoteInput = applyRemoteInput(
1367 headsUpContentView, entry, hasFreeformRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001368 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001369 } else {
1370 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001371 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001372 if (mCachedHeadsUpRemoteInput != null
1373 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1374 // We had a cached remote input but didn't reuse it. Clean up required.
1375 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1376 }
1377 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001378 }
1379
Ned Burnsf81c4c42019-01-07 14:10:43 -05001380 private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001381 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001382 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001383 View actionContainerCandidate = view.findViewById(
1384 com.android.internal.R.id.actions_container);
1385 if (actionContainerCandidate instanceof FrameLayout) {
1386 RemoteInputView existing = (RemoteInputView)
1387 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1388
1389 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001390 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001391 }
1392
1393 if (existing == null && hasRemoteInput) {
1394 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001395 if (cachedView == null) {
1396 RemoteInputView riv = RemoteInputView.inflate(
1397 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001398
Adrian Roos7813dd72016-09-23 17:12:17 -07001399 riv.setVisibility(View.INVISIBLE);
1400 actionContainer.addView(riv, new LayoutParams(
1401 ViewGroup.LayoutParams.MATCH_PARENT,
1402 ViewGroup.LayoutParams.MATCH_PARENT)
1403 );
1404 existing = riv;
1405 } else {
1406 actionContainer.addView(cachedView);
1407 cachedView.dispatchFinishTemporaryDetach();
1408 cachedView.requestFocus();
1409 existing = cachedView;
1410 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001411 }
1412 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001413 int color = entry.notification.getNotification().color;
1414 if (color == Notification.COLOR_DEFAULT) {
1415 color = mContext.getColor(R.color.default_remote_input_background);
1416 }
Lucas Dupina291d192018-06-07 13:59:42 -07001417 existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001418 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001419 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001420
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001421 existing.setWrapper(wrapper);
Selim Cineked64a142018-02-06 18:06:01 -08001422 existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001423
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001424 if (existingPendingIntent != null || existing.isActive()) {
1425 // The current action could be gone, or the pending intent no longer valid.
1426 // If we find a matching action in the new notification, focus, otherwise close.
1427 Notification.Action[] actions = entry.notification.getNotification().actions;
1428 if (existingPendingIntent != null) {
1429 existing.setPendingIntent(existingPendingIntent);
1430 }
1431 if (existing.updatePendingIntentFromActions(actions)) {
1432 if (!existing.isActive()) {
1433 existing.focus();
1434 }
1435 } else {
1436 if (existing.isActive()) {
1437 existing.close();
1438 }
1439 }
1440 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001441 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001442 return existing;
1443 }
1444 return null;
1445 }
1446
Gustav Sennton5759f872019-02-13 17:25:26 +00001447 private void applySmartReplyView(
1448 SmartRepliesAndActions smartRepliesAndActions,
Ned Burnsf81c4c42019-01-07 14:10:43 -05001449 NotificationEntry entry) {
Kenny Guy23991102018-04-05 21:18:38 +01001450 if (mExpandedChild != null) {
Gustav Sennton5759f872019-02-13 17:25:26 +00001451 mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, smartRepliesAndActions,
1452 entry, mExpandedInflatedSmartReplies);
Gustav Senntond25a64d2018-12-07 10:58:39 +00001453 if (mExpandedSmartReplyView != null) {
1454 if (smartRepliesAndActions.smartReplies != null
1455 || smartRepliesAndActions.smartActions != null) {
1456 int numSmartReplies = smartRepliesAndActions.smartReplies == null
1457 ? 0 : smartRepliesAndActions.smartReplies.choices.length;
1458 int numSmartActions = smartRepliesAndActions.smartActions == null
1459 ? 0 : smartRepliesAndActions.smartActions.actions.size();
1460 boolean fromAssistant = smartRepliesAndActions.smartReplies == null
1461 ? smartRepliesAndActions.smartActions.fromAssistant
1462 : smartRepliesAndActions.smartReplies.fromAssistant;
Milo Sredkov13d88112019-02-01 12:23:24 +00001463 boolean editBeforeSending = smartRepliesAndActions.smartReplies != null
1464 && mSmartReplyConstants.getEffectiveEditChoicesBeforeSending(
1465 smartRepliesAndActions.smartReplies.remoteInput
1466 .getEditChoicesBeforeSending());
1467
Gustav Senntond25a64d2018-12-07 10:58:39 +00001468 mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
Milo Sredkov13d88112019-02-01 12:23:24 +00001469 numSmartActions, fromAssistant, editBeforeSending);
Gustav Senntond25a64d2018-12-07 10:58:39 +00001470 }
Kenny Guy23991102018-04-05 21:18:38 +01001471 }
1472 }
Gustav Sennton3f3eaff2019-01-08 09:39:51 +00001473 if (mHeadsUpChild != null && mSmartReplyConstants.getShowInHeadsUp()) {
Gustav Sennton5759f872019-02-13 17:25:26 +00001474 mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, smartRepliesAndActions,
1475 entry, mHeadsUpInflatedSmartReplies);
Gustav Senntond0e84532018-12-03 16:48:36 +00001476 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001477 }
1478
Gustav Sennton5759f872019-02-13 17:25:26 +00001479 @Nullable
Gustav Senntoneab53682018-11-01 16:30:23 +00001480 private SmartReplyView applySmartReplyView(View view,
Gustav Sennton5759f872019-02-13 17:25:26 +00001481 SmartRepliesAndActions smartRepliesAndActions,
1482 NotificationEntry entry, InflatedSmartReplies inflatedSmartReplyView) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001483 View smartReplyContainerCandidate = view.findViewById(
1484 com.android.internal.R.id.smart_reply_container);
1485 if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
1486 return null;
1487 }
Gustav Sennton5759f872019-02-13 17:25:26 +00001488
Petr Cermak7997d7a2018-01-18 15:52:10 +00001489 LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
Gustav Senntonb944ce52019-02-25 18:52:43 +00001490 if (!InflatedSmartReplies.shouldShowSmartReplyView(entry, smartRepliesAndActions)) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001491 smartReplyContainer.setVisibility(View.GONE);
1492 return null;
1493 }
Gustav Senntonb944ce52019-02-25 18:52:43 +00001494
Petr Cermak7997d7a2018-01-18 15:52:10 +00001495 SmartReplyView smartReplyView = null;
Gustav Sennton5759f872019-02-13 17:25:26 +00001496 if (smartReplyContainer.getChildCount() == 1
1497 && smartReplyContainer.getChildAt(0) instanceof SmartReplyView) {
1498 // If we already have a SmartReplyView - replace it with the newly inflated one. The
1499 // newly inflated one is connected to the new inflated smart reply/action buttons.
1500 smartReplyContainer.removeAllViews();
1501 }
1502 if (smartReplyContainer.getChildCount() == 0
1503 && inflatedSmartReplyView != null
1504 && inflatedSmartReplyView.getSmartReplyView() != null) {
1505 smartReplyView = inflatedSmartReplyView.getSmartReplyView();
Petr Cermak7997d7a2018-01-18 15:52:10 +00001506 smartReplyContainer.addView(smartReplyView);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001507 }
1508 if (smartReplyView != null) {
Gustav Senntoneab53682018-11-01 16:30:23 +00001509 smartReplyView.resetSmartSuggestions(smartReplyContainer);
Gustav Sennton5759f872019-02-13 17:25:26 +00001510 smartReplyView.addPreInflatedButtons(
1511 inflatedSmartReplyView.getSmartSuggestionButtons());
Petr Cermak7997d7a2018-01-18 15:52:10 +00001512 smartReplyContainer.setVisibility(View.VISIBLE);
1513 }
1514 return smartReplyView;
1515 }
1516
Gustav Sennton5759f872019-02-13 17:25:26 +00001517 /**
1518 * Set pre-inflated views necessary to display smart replies and actions in the expanded
1519 * notification state.
1520 *
1521 * @param inflatedSmartReplies the pre-inflated state to add to this view. If null the existing
1522 * {@link SmartReplyView} related to the expanded notification state is cleared.
1523 */
1524 public void setExpandedInflatedSmartReplies(
1525 @Nullable InflatedSmartReplies inflatedSmartReplies) {
1526 mExpandedInflatedSmartReplies = inflatedSmartReplies;
1527 if (inflatedSmartReplies == null) {
1528 mExpandedSmartReplyView = null;
1529 }
1530 }
1531
1532 /**
1533 * Set pre-inflated views necessary to display smart replies and actions in the heads-up
1534 * notification state.
1535 *
1536 * @param inflatedSmartReplies the pre-inflated state to add to this view. If null the existing
1537 * {@link SmartReplyView} related to the heads-up notification state is cleared.
1538 */
1539 public void setHeadsUpInflatedSmartReplies(
1540 @Nullable InflatedSmartReplies inflatedSmartReplies) {
1541 mHeadsUpInflatedSmartReplies = inflatedSmartReplies;
1542 if (inflatedSmartReplies == null) {
1543 mHeadsUpSmartReplyView = null;
1544 }
1545 }
1546
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001547 public void closeRemoteInput() {
1548 if (mHeadsUpRemoteInput != null) {
1549 mHeadsUpRemoteInput.close();
1550 }
1551 if (mExpandedRemoteInput != null) {
1552 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001553 }
1554 }
1555
Selim Cinek83bc7832015-10-22 13:26:54 -07001556 public void setGroupManager(NotificationGroupManager groupManager) {
1557 mGroupManager = groupManager;
1558 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001559
Adrian Roosb88b1a12015-12-09 18:51:05 -08001560 public void setRemoteInputController(RemoteInputController r) {
1561 mRemoteInputController = r;
1562 }
1563
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001564 public void setExpandClickListener(OnClickListener expandClickListener) {
1565 mExpandClickListener = expandClickListener;
1566 }
1567
1568 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001569 mExpandable = expandable;
1570 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001571 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001572 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1573 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001574 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001575 expandable = false;
1576 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001577 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001578 expandable = false;
1579 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001580 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001581 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001582 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001583 }
1584 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001585 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001586 }
1587 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001588 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001589 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001590 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001591 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001592
1593 public NotificationHeaderView getNotificationHeader() {
1594 NotificationHeaderView header = null;
1595 if (mContractedChild != null) {
1596 header = mContractedWrapper.getNotificationHeader();
1597 }
1598 if (header == null && mExpandedChild != null) {
1599 header = mExpandedWrapper.getNotificationHeader();
1600 }
1601 if (header == null && mHeadsUpChild != null) {
1602 header = mHeadsUpWrapper.getNotificationHeader();
1603 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001604 if (header == null && mAmbientChild != null) {
1605 header = mAmbientWrapper.getNotificationHeader();
1606 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001607 return header;
1608 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001609
Julia Reynoldsfc640012018-02-21 12:25:27 -05001610 public void showAppOpsIcons(ArraySet<Integer> activeOps) {
1611 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
1612 mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1613 }
1614 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
1615 mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1616 }
1617 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
1618 mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1619 }
1620 }
Selim Cinekb26afa32017-06-29 10:28:17 +02001621
Gus Prevasa3226492018-10-23 11:10:09 -04001622 /** Sets whether the notification being displayed audibly alerted the user. */
Gus Prevas7306b902018-12-11 10:57:06 -05001623 public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
Gus Prevasa3226492018-10-23 11:10:09 -04001624 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001625 mContractedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001626 }
1627 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001628 mExpandedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001629 }
1630 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001631 mHeadsUpWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001632 }
1633 }
1634
Selim Cinekb26afa32017-06-29 10:28:17 +02001635 public NotificationHeaderView getContractedNotificationHeader() {
1636 if (mContractedChild != null) {
1637 return mContractedWrapper.getNotificationHeader();
1638 }
1639 return null;
1640 }
1641
Selim Cinek34eda5e2016-02-18 17:10:43 -08001642 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001643 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001644 return wrapper == null ? null : wrapper.getNotificationHeader();
1645 }
1646
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001647 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1648 mContainingNotification = containingNotification;
1649 }
1650
1651 public void requestSelectLayout(boolean needsAnimation) {
1652 selectLayout(needsAnimation, false);
1653 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001654
1655 public void reInflateViews() {
1656 if (mIsChildInGroup && mSingleLineView != null) {
1657 removeView(mSingleLineView);
1658 mSingleLineView = null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001659 updateAllSingleLineViews();
Selim Cinekde33a4a2016-02-11 16:43:41 -08001660 }
1661 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001662
1663 public void setUserExpanding(boolean userExpanding) {
1664 mUserExpanding = userExpanding;
1665 if (userExpanding) {
1666 mTransformationStartVisibleType = mVisibleType;
1667 } else {
1668 mTransformationStartVisibleType = UNDEFINED;
1669 mVisibleType = calculateVisibleType();
1670 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001671 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001672 }
1673 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001674
1675 /**
1676 * Set by how much the single line view should be indented. Used when a overflow indicator is
1677 * present and only during measuring
1678 */
1679 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1680 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1681 mSingleLineWidthIndention = singleLineWidthIndention;
1682 mContainingNotification.forceLayout();
1683 forceLayout();
1684 }
1685 }
1686
1687 public HybridNotificationView getSingleLineView() {
1688 return mSingleLineView;
1689 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001690
1691 public void setRemoved() {
1692 if (mExpandedRemoteInput != null) {
1693 mExpandedRemoteInput.setRemoved();
1694 }
1695 if (mHeadsUpRemoteInput != null) {
1696 mHeadsUpRemoteInput.setRemoved();
1697 }
1698 }
Adrian Roos599be342016-06-13 14:54:39 -07001699
1700 public void setContentHeightAnimating(boolean animating) {
1701 if (!animating) {
1702 mContentHeightAtAnimationStart = UNDEFINED;
1703 }
1704 }
Selim Cineke9bad242016-06-15 11:46:37 -07001705
Adrian Roose18033c2017-01-17 15:22:49 -08001706 @VisibleForTesting
1707 boolean isAnimatingVisibleType() {
1708 return mAnimationStartVisibleType != UNDEFINED;
1709 }
1710
Selim Cinekcafa87f2016-10-26 17:00:17 -07001711 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1712 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001713 selectLayout(false /* animate */, true /* force */);
1714 }
1715
Selim Cineke9bad242016-06-15 11:46:37 -07001716 public void setFocusOnVisibilityChange() {
1717 mFocusOnVisibilityChange = true;
1718 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001719
1720 public void setIconsVisible(boolean iconsVisible) {
1721 mIconsVisible = iconsVisible;
1722 updateIconVisibilities();
1723 }
1724
1725 private void updateIconVisibilities() {
1726 if (mContractedWrapper != null) {
1727 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1728 if (header != null) {
1729 header.getIcon().setForceHidden(!mIconsVisible);
1730 }
1731 }
1732 if (mHeadsUpWrapper != null) {
1733 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1734 if (header != null) {
1735 header.getIcon().setForceHidden(!mIconsVisible);
1736 }
1737 }
1738 if (mExpandedWrapper != null) {
1739 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1740 if (header != null) {
1741 header.getIcon().setForceHidden(!mIconsVisible);
1742 }
1743 }
1744 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001745
Adrian Roos4320e892017-01-24 12:50:34 -08001746 @Override
1747 public void onVisibilityAggregated(boolean isVisible) {
1748 super.onVisibilityAggregated(isVisible);
1749 if (isVisible) {
1750 fireExpandedVisibleListenerIfVisible();
1751 }
1752 }
1753
1754 /**
1755 * Sets a one-shot listener for when the expanded view becomes visible.
1756 *
1757 * This will fire the listener immediately if the expanded view is already visible.
1758 */
1759 public void setOnExpandedVisibleListener(Runnable r) {
1760 mExpandedVisibleListener = r;
1761 fireExpandedVisibleListenerIfVisible();
1762 }
1763
Kevind5022f92018-10-08 18:30:26 -07001764 /**
1765 * Set a one-shot listener to run when a given content view becomes inactive.
1766 *
1767 * @param visibleType visible type corresponding to the content view to listen
1768 * @param listener runnable to run once when the content view becomes inactive
1769 */
1770 public void performWhenContentInactive(int visibleType, Runnable listener) {
1771 View view = getViewForVisibleType(visibleType);
1772 // View is already inactive
1773 if (view == null || isContentViewInactive(visibleType)) {
1774 listener.run();
1775 return;
1776 }
1777 mOnContentViewInactiveListeners.put(view, listener);
1778 }
1779
1780 /**
1781 * Whether or not the content view is inactive. This means it should not be visible
1782 * or the showing content as removing it would cause visual jank.
1783 *
1784 * @param visibleType visible type corresponding to the content view to be removed
1785 * @return true if the content view is inactive, false otherwise
1786 */
1787 public boolean isContentViewInactive(int visibleType) {
1788 View view = getViewForVisibleType(visibleType);
1789 return isContentViewInactive(view);
1790 }
1791
1792 /**
1793 * Whether or not the content view is inactive.
1794 *
1795 * @param view view to see if its inactive
1796 * @return true if the view is inactive, false o/w
1797 */
1798 private boolean isContentViewInactive(View view) {
1799 if (view == null) {
1800 return true;
1801 }
Kevin38ce6fa2018-10-17 16:00:14 -07001802 return !isShown()
1803 || (view.getVisibility() != VISIBLE && getViewForVisibleType(mVisibleType) != view);
Kevind5022f92018-10-08 18:30:26 -07001804 }
1805
1806 @Override
1807 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
1808 super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
1809 if (isContentViewInactive(child)) {
1810 Runnable listener = mOnContentViewInactiveListeners.remove(child);
1811 if (listener != null) {
1812 listener.run();
1813 }
1814 }
1815 }
1816
Selim Cinek6743c0b2017-01-18 18:24:01 -08001817 public void setIsLowPriority(boolean isLowPriority) {
1818 mIsLowPriority = isLowPriority;
1819 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001820
1821 public boolean isDimmable() {
Kevin38ce6fa2018-10-17 16:00:14 -07001822 return mContractedWrapper != null && mContractedWrapper.isDimmable();
Selim Cinek63edaf22017-04-24 22:18:48 -07001823 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001824
1825 /**
1826 * Should a single click be disallowed on this view when on the keyguard?
1827 */
1828 public boolean disallowSingleClick(float x, float y) {
1829 NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
1830 if (visibleWrapper != null) {
1831 return visibleWrapper.disallowSingleClick(x, y);
1832 }
1833 return false;
1834 }
Selim Cinek515b2032017-11-15 10:20:19 -08001835
Selim Cinek86bfcee2018-01-17 11:00:47 -08001836 public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
1837 boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001838 if (mUserExpanding) {
Selim Cinek86bfcee2018-01-17 11:00:47 -08001839 needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
1840 bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001841 }
1842 return needsPaddings;
1843 }
1844
Selim Cinek86bfcee2018-01-17 11:00:47 -08001845 private boolean shouldClipToRounding(int visibleType, boolean topRounded,
1846 boolean bottomRounded) {
Selim Cinek515b2032017-11-15 10:20:19 -08001847 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
1848 if (visibleWrapper == null) {
1849 return false;
1850 }
Selim Cinek86bfcee2018-01-17 11:00:47 -08001851 return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001852 }
Selim Cinekde4de0e2018-01-24 16:21:07 -08001853
1854 public CharSequence getActiveRemoteInputText() {
1855 if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
1856 return mExpandedRemoteInput.getText();
1857 }
1858 if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
1859 return mHeadsUpRemoteInput.getText();
1860 }
1861 return null;
1862 }
Selim Cineked64a142018-02-06 18:06:01 -08001863
Selim Cineka77d2c52018-04-09 10:44:52 -07001864 @Override
1865 public boolean dispatchTouchEvent(MotionEvent ev) {
1866 float y = ev.getY();
1867 // We still want to distribute touch events to the remote input even if it's outside the
1868 // view boundary. We're therefore manually dispatching these events to the remote view
1869 RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
1870 if (riv != null && riv.getVisibility() == VISIBLE) {
1871 int inputStart = mUnrestrictedContentHeight - riv.getHeight();
1872 if (y <= mUnrestrictedContentHeight && y >= inputStart) {
1873 ev.offsetLocation(0, -inputStart);
1874 return riv.dispatchTouchEvent(ev);
1875 }
1876 }
1877 return super.dispatchTouchEvent(ev);
1878 }
1879
1880 /**
1881 * Overridden to make sure touches to the reply action bar actually go through to this view
1882 */
1883 @Override
1884 public boolean pointInView(float localX, float localY, float slop) {
1885 float top = mClipTopAmount;
1886 float bottom = mUnrestrictedContentHeight;
1887 return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
1888 localY < (bottom + slop);
1889 }
1890
1891 private RemoteInputView getRemoteInputForView(View child) {
1892 if (child == mExpandedChild) {
1893 return mExpandedRemoteInput;
1894 } else if (child == mHeadsUpChild) {
1895 return mHeadsUpRemoteInput;
1896 }
1897 return null;
1898 }
1899
Selim Cineked64a142018-02-06 18:06:01 -08001900 public int getExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001901 int viewType = VISIBLE_TYPE_EXPANDED;
1902 if (mExpandedChild == null) {
1903 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001904 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001905 return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001906 }
1907
1908 public int getHeadsUpHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001909 int viewType = VISIBLE_TYPE_HEADSUP;
1910 if (mHeadsUpChild == null) {
1911 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001912 }
Selim Cinek09b442e2018-05-04 15:46:18 -07001913 // The headsUp remote input quickly switches to the expanded one, so lets also include that
1914 // one
1915 return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
1916 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001917 }
1918
1919 public void setRemoteInputVisible(boolean remoteInputVisible) {
1920 mRemoteInputVisible = remoteInputVisible;
1921 setClipChildren(!remoteInputVisible);
1922 }
1923
1924 @Override
1925 public void setClipChildren(boolean clipChildren) {
1926 clipChildren = clipChildren && !mRemoteInputVisible;
1927 super.setClipChildren(clipChildren);
1928 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001929
1930 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1931 if (mContractedWrapper != null) {
1932 mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1933 }
1934 if (mHeadsUpWrapper != null) {
1935 mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1936 }
1937 if (mExpandedWrapper != null) {
1938 mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1939 }
1940 }
Selim Cinek85a8f9f2018-11-21 13:58:27 -08001941
1942 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1943 pw.print(" ");
1944 pw.print("contentView visibility: " + getVisibility());
1945 pw.print(", alpha: " + getAlpha());
1946 pw.print(", clipBounds: " + getClipBounds());
1947 pw.print(", contentHeight: " + mContentHeight);
1948 pw.print(", visibleType: " + mVisibleType);
1949 View view = getViewForVisibleType(mVisibleType);
1950 pw.print(", visibleView ");
1951 if (view != null) {
1952 pw.print(" visibility: " + view.getVisibility());
1953 pw.print(", alpha: " + view.getAlpha());
1954 pw.print(", clipBounds: " + view.getClipBounds());
1955 } else {
1956 pw.print("null");
1957 }
1958 pw.println();
1959 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001960}