blob: 671b451bd6b0ae39ce24047f0cb52508dc6416a0 [file] [log] [blame]
Jorim Jaggibe565df2014-04-28 17:51:23 +02001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar;
18
Adrian Roosb88b1a12015-12-09 18:51:05 -080019import android.app.Notification;
Adrian Roos4c1fcc82016-03-31 14:39:39 -070020import android.app.PendingIntent;
Adrian Roosb88b1a12015-12-09 18:51:05 -080021import android.app.RemoteInput;
Jorim Jaggibe565df2014-04-28 17:51:23 +020022import android.content.Context;
23import android.graphics.Rect;
Selim Cinek860b6da2015-12-16 19:02:19 -080024import android.os.Build;
Selim Cinek83bc7832015-10-22 13:26:54 -070025import android.service.notification.StatusBarNotification;
Julia Reynoldsfc640012018-02-21 12:25:27 -050026import android.util.ArraySet;
Jorim Jaggibe565df2014-04-28 17:51:23 +020027import android.util.AttributeSet;
Selim Cinek011bde92017-11-22 05:53:38 -080028import android.util.Log;
Selim Cineka77d2c52018-04-09 10:44:52 -070029import android.view.MotionEvent;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080030import android.view.NotificationHeaderView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020031import android.view.View;
Selim Cinek8d490d42015-04-10 00:05:50 -070032import android.view.ViewGroup;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010033import android.view.ViewTreeObserver;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020034import android.widget.FrameLayout;
Selim Cineke9bad242016-06-15 11:46:37 -070035import android.widget.ImageView;
Petr Cermak7997d7a2018-01-18 15:52:10 +000036import android.widget.LinearLayout;
Selim Cinek8d490d42015-04-10 00:05:50 -070037
Adrian Roose18033c2017-01-17 15:22:49 -080038import com.android.internal.annotations.VisibleForTesting;
Adrian Roos4ff3b122016-02-01 12:26:13 -080039import com.android.internal.util.NotificationColorUtil;
Petr Cermak10011fa2018-02-05 19:00:54 +000040import com.android.systemui.Dependency;
Jorim Jaggibe565df2014-04-28 17:51:23 +020041import com.android.systemui.R;
Selim Cinekc897bd32016-03-18 17:32:31 -070042import com.android.systemui.statusbar.notification.HybridGroupManager;
Selim Cinek011bde92017-11-22 05:53:38 -080043import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinek0ffbda62016-01-01 20:29:12 +010044import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
Selim Cinekc3179332016-03-04 14:44:56 -080045import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cinek0ffbda62016-01-01 20:29:12 +010046import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070047import com.android.systemui.statusbar.phone.NotificationGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080048import com.android.systemui.statusbar.policy.RemoteInputView;
Petr Cermak10011fa2018-02-05 19:00:54 +000049import com.android.systemui.statusbar.policy.SmartReplyConstants;
Petr Cermak7997d7a2018-01-18 15:52:10 +000050import com.android.systemui.statusbar.policy.SmartReplyView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020051
52/**
Selim Cinek684a4422015-04-15 16:18:39 -070053 * A frame layout containing the actual payload of the notification, including the contracted,
54 * expanded and heads up layout. This class is responsible for clipping the content and and
55 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020056 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020057public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020058
Selim Cinek011bde92017-11-22 05:53:38 -080059 private static final String TAG = "NotificationContentView";
Selim Cinek131f1a42017-06-05 17:50:19 -070060 public static final int VISIBLE_TYPE_CONTRACTED = 0;
61 public static final int VISIBLE_TYPE_EXPANDED = 1;
62 public static final int VISIBLE_TYPE_HEADSUP = 2;
Selim Cinek83bc7832015-10-22 13:26:54 -070063 private static final int VISIBLE_TYPE_SINGLELINE = 3;
Selim Cinek131f1a42017-06-05 17:50:19 -070064 public static final int VISIBLE_TYPE_AMBIENT = 4;
Adrian Roos6f6e1592017-05-02 16:22:53 -070065 private static final int VISIBLE_TYPE_AMBIENT_SINGLELINE = 5;
Selim Cinekc3179332016-03-04 14:44:56 -080066 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020067
Jorim Jaggibe565df2014-04-28 17:51:23 +020068 private final Rect mClipBounds = new Rect();
69
Selim Cinek9b49f6d2017-11-13 18:19:35 -080070 private int mMinContractedHeight;
71 private int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020072 private View mContractedChild;
73 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070074 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070075 private HybridNotificationView mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -080076 private View mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -070077 private HybridNotificationView mAmbientSingleLineChild;
Jorim Jaggibe565df2014-04-28 17:51:23 +020078
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070079 private RemoteInputView mExpandedRemoteInput;
80 private RemoteInputView mHeadsUpRemoteInput;
Petr Cermak10011fa2018-02-05 19:00:54 +000081
82 private SmartReplyConstants mSmartReplyConstants;
Milo Sredkove7cf4982018-04-09 15:08:26 +010083 private SmartReplyView mExpandedSmartReplyView;
Kenny Guya0f6de82018-04-06 16:20:16 +010084 private SmartReplyController mSmartReplyController;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070085
Jorim Jaggi4e857f42014-11-17 19:14:04 +010086 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070087 private NotificationViewWrapper mExpandedWrapper;
88 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -080089 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -070090 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020091 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +010092 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -070093 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -040094 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010095 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -070096 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -080097 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -070098 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -080099 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800100 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800101 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800102 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700103 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700104 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800105 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -0800106 private Runnable mExpandedVisibleListener;
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700107
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700108 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100109 = new ViewTreeObserver.OnPreDrawListener() {
110 @Override
111 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -0800112 // We need to post since we don't want the notification to animate on the very first
113 // frame
114 post(new Runnable() {
115 @Override
116 public void run() {
117 mAnimate = true;
118 }
119 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100120 getViewTreeObserver().removeOnPreDrawListener(this);
121 return true;
122 }
123 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200124
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800125 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800126 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800127 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100128 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800129 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700130 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800131 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700132 /** The visible type at the start of an animation driven transformation */
133 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800134 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700135 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700136 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700137 private PendingIntent mPreviousExpandedRemoteInputIntent;
138 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700139 private RemoteInputView mCachedExpandedRemoteInput;
140 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800141
Adrian Roos599be342016-06-13 14:54:39 -0700142 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700143 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700144 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700145 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700146 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800147 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800148 private boolean mIsContentExpandable;
Selim Cineked64a142018-02-06 18:06:01 -0800149 private boolean mRemoteInputVisible;
150 private int mUnrestrictedContentHeight;
Adrian Roos599be342016-06-13 14:54:39 -0700151
152
Jorim Jaggibe565df2014-04-28 17:51:23 +0200153 public NotificationContentView(Context context, AttributeSet attrs) {
154 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700155 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Petr Cermak10011fa2018-02-05 19:00:54 +0000156 mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
Kenny Guya0f6de82018-04-06 16:20:16 +0100157 mSmartReplyController = Dependency.get(SmartReplyController.class);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800158 initView();
159 }
160
161 public void initView() {
Selim Cinek860b6da2015-12-16 19:02:19 -0800162 mMinContractedHeight = getResources().getDimensionPixelSize(
163 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800164 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
165 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200166 }
167
Adrian Roos0aac04f2016-12-08 15:59:29 -0800168 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
169 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800170 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800171 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800172 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800173 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800174 }
175
Jorim Jaggibe565df2014-04-28 17:51:23 +0200176 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700177 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
178 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
179 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
180 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700181 int maxSize = Integer.MAX_VALUE / 2;
Selim Cinekc897bd32016-03-18 17:32:31 -0700182 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700183 if (hasFixedHeight || isHeightLimited) {
184 maxSize = MeasureSpec.getSize(heightMeasureSpec);
185 }
186 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800187 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +0100188 int notificationMaxHeight = mNotificationMaxHeight;
189 if (mExpandedSmartReplyView != null) {
190 notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
191 }
Selim Cinekbee4e072018-05-21 22:06:43 -0700192 notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700193 int size = notificationMaxHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800194 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800195 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800196 if (layoutParams.height >= 0) {
197 // An actual height is set
Selim Cinekbe570ce2018-05-11 12:29:52 -0700198 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800199 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800200 }
Selim Cinekbe570ce2018-05-11 12:29:52 -0700201 int spec = MeasureSpec.makeMeasureSpec(size, useExactly
Selim Cinek6743c0b2017-01-18 18:24:01 -0800202 ? MeasureSpec.EXACTLY
203 : MeasureSpec.AT_MOST);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800204 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800205 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
206 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700207 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800208 int heightSpec;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700209 int size = mSmallHeight;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800210 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
211 boolean useExactly = false;
212 if (layoutParams.height >= 0) {
213 // An actual height is set
214 size = Math.min(size, layoutParams.height);
215 useExactly = true;
216 }
217 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800218 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
219 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800220 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800221 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800222 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800223 int measuredHeight = mContractedChild.getMeasuredHeight();
224 if (measuredHeight < mMinContractedHeight) {
225 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800226 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800227 }
228 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800229 if (updateContractedHeaderWidth()) {
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800230 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700231 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700232 if (mExpandedChild != null
233 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
234 // the Expanded child is smaller then the collapsed. Let's remeasure it.
235 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
236 MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800237 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek1ba41d12016-04-21 16:14:46 -0700238 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700239 }
240 if (mHeadsUpChild != null) {
Selim Cinek396caca2018-04-10 17:46:46 -0700241 int maxHeight = mHeadsUpHeight;
242 maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700243 int size = maxHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700244 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800245 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700246 if (layoutParams.height >= 0) {
247 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800248 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800249 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700250 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800251 measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800252 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800253 : MeasureSpec.AT_MOST), 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700254 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
255 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700256 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700257 int singleLineWidthSpec = widthMeasureSpec;
258 if (mSingleLineWidthIndention != 0
259 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
260 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
261 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700262 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700263 }
264 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinekbe570ce2018-05-11 12:29:52 -0700265 MeasureSpec.makeMeasureSpec(mNotificationMaxHeight, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700266 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
267 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800268 if (mAmbientChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700269 int size = mNotificationAmbientHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800270 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800271 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800272 if (layoutParams.height >= 0) {
273 // An actual height is set
274 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800275 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800276 }
277 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800278 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
279 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800280 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
281 }
Adrian Roos6f6e1592017-05-02 16:22:53 -0700282 if (mAmbientSingleLineChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700283 int size = mNotificationAmbientHeight;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700284 ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams();
285 boolean useExactly = false;
286 if (layoutParams.height >= 0) {
287 // An actual height is set
288 size = Math.min(size, layoutParams.height);
289 useExactly = true;
290 }
291 int ambientSingleLineWidthSpec = widthMeasureSpec;
292 if (mSingleLineWidthIndention != 0
293 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
294 ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec(
295 width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(),
296 MeasureSpec.EXACTLY);
297 }
298 mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec,
299 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
300 : MeasureSpec.AT_MOST));
301 maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight());
302 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700303 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700304 setMeasuredDimension(width, ownHeight);
305 }
306
Selim Cineked64a142018-02-06 18:06:01 -0800307 /**
308 * Get the extra height that needs to be added to the notification height for a given
309 * {@link RemoteInputView}.
310 * This is needed when the user is inline replying in order to ensure that the reply bar has
311 * enough padding.
312 *
313 * @param remoteInput The remote input to check.
314 * @return The extra height needed.
315 */
316 private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
Selim Cinek09b442e2018-05-04 15:46:18 -0700317 if (remoteInput != null && (remoteInput.isActive() || remoteInput.isSending())) {
Selim Cineked64a142018-02-06 18:06:01 -0800318 return getResources().getDimensionPixelSize(
319 com.android.internal.R.dimen.notification_content_margin);
320 }
321 return 0;
322 }
323
Selim Cinek6ecc8102016-01-26 18:26:19 -0800324 private boolean updateContractedHeaderWidth() {
325 // We need to update the expanded and the collapsed header to have exactly the same with to
326 // have the expand buttons laid out at the same location.
327 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
328 if (contractedHeader != null) {
329 if (mExpandedChild != null
330 && mExpandedWrapper.getNotificationHeader() != null) {
331 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
332 int expandedSize = expandedHeader.getMeasuredWidth()
333 - expandedHeader.getPaddingEnd();
334 int collapsedSize = contractedHeader.getMeasuredWidth()
335 - expandedHeader.getPaddingEnd();
336 if (expandedSize != collapsedSize) {
337 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
338 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800339 contractedHeader.isLayoutRtl()
340 ? paddingEnd
341 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800342 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800343 contractedHeader.isLayoutRtl()
344 ? contractedHeader.getPaddingLeft()
345 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800346 contractedHeader.getPaddingBottom());
347 contractedHeader.setShowWorkBadgeAtEnd(true);
348 return true;
349 }
350 } else {
351 int paddingEnd = mNotificationContentMarginEnd;
352 if (contractedHeader.getPaddingEnd() != paddingEnd) {
353 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800354 contractedHeader.isLayoutRtl()
355 ? paddingEnd
356 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800357 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800358 contractedHeader.isLayoutRtl()
359 ? contractedHeader.getPaddingLeft()
360 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800361 contractedHeader.getPaddingBottom());
362 contractedHeader.setShowWorkBadgeAtEnd(false);
363 return true;
364 }
365 }
366 }
367 return false;
368 }
369
Selim Cinek860b6da2015-12-16 19:02:19 -0800370 private boolean shouldContractedBeFixedSize() {
371 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
372 }
373
Selim Cinek8d490d42015-04-10 00:05:50 -0700374 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200375 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700376 int previousHeight = 0;
377 if (mExpandedChild != null) {
378 previousHeight = mExpandedChild.getHeight();
379 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200380 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700381 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
382 mContentHeightAtAnimationStart = previousHeight;
383 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200384 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700385 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700386 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
387 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700388 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200389 }
390
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100391 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100392 protected void onAttachedToWindow() {
393 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100394 updateVisibility();
395 }
396
Selim Cinekcab4a602014-09-03 14:47:57 +0200397 public View getContractedChild() {
398 return mContractedChild;
399 }
400
401 public View getExpandedChild() {
402 return mExpandedChild;
403 }
404
Selim Cinek8d490d42015-04-10 00:05:50 -0700405 public View getHeadsUpChild() {
406 return mHeadsUpChild;
407 }
408
Adrian Roos0aac04f2016-12-08 15:59:29 -0800409 public View getAmbientChild() {
410 return mAmbientChild;
411 }
412
Adrian Roos6f6e1592017-05-02 16:22:53 -0700413 public HybridNotificationView getAmbientSingleLineChild() {
414 return mAmbientSingleLineChild;
415 }
416
Jorim Jaggibe565df2014-04-28 17:51:23 +0200417 public void setContractedChild(View child) {
418 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200419 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200420 removeView(mContractedChild);
421 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200422 addView(child);
423 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700424 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
425 mContainingNotification);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800426 }
427
428 private NotificationViewWrapper getWrapperForView(View child) {
429 if (child == mContractedChild) {
430 return mContractedWrapper;
431 }
432 if (child == mExpandedChild) {
433 return mExpandedWrapper;
434 }
435 if (child == mHeadsUpChild) {
436 return mHeadsUpWrapper;
437 }
438 if (child == mAmbientChild) {
439 return mAmbientWrapper;
440 }
441 return null;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200442 }
443
444 public void setExpandedChild(View child) {
445 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800446 mPreviousExpandedRemoteInputIntent = null;
447 if (mExpandedRemoteInput != null) {
448 mExpandedRemoteInput.onNotificationUpdateOrReset();
449 if (mExpandedRemoteInput.isActive()) {
450 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
451 mCachedExpandedRemoteInput = mExpandedRemoteInput;
452 mExpandedRemoteInput.dispatchStartTemporaryDetach();
453 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
454 }
455 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200456 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200457 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800458 mExpandedRemoteInput = null;
459 }
460 if (child == null) {
461 mExpandedChild = null;
462 mExpandedWrapper = null;
463 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
464 mVisibleType = VISIBLE_TYPE_CONTRACTED;
465 }
466 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
467 mTransformationStartVisibleType = UNDEFINED;
468 }
469 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200470 }
471 addView(child);
472 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700473 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
474 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200475 }
476
Selim Cinek8d490d42015-04-10 00:05:50 -0700477 public void setHeadsUpChild(View child) {
478 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800479 mPreviousHeadsUpRemoteInputIntent = null;
480 if (mHeadsUpRemoteInput != null) {
481 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
482 if (mHeadsUpRemoteInput.isActive()) {
483 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
484 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
485 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
486 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
487 }
488 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700489 mHeadsUpChild.animate().cancel();
490 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800491 mHeadsUpRemoteInput = null;
492 }
493 if (child == null) {
494 mHeadsUpChild = null;
495 mHeadsUpWrapper = null;
496 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
497 mVisibleType = VISIBLE_TYPE_CONTRACTED;
498 }
499 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
500 mTransformationStartVisibleType = UNDEFINED;
501 }
502 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700503 }
504 addView(child);
505 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700506 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
507 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700508 }
509
Adrian Roos0aac04f2016-12-08 15:59:29 -0800510 public void setAmbientChild(View child) {
511 if (mAmbientChild != null) {
512 mAmbientChild.animate().cancel();
513 removeView(mAmbientChild);
514 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700515 if (child == null) {
516 return;
517 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800518 addView(child);
519 mAmbientChild = child;
520 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
521 mContainingNotification);
522 }
523
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100524 @Override
525 protected void onVisibilityChanged(View changedView, int visibility) {
526 super.onVisibilityChanged(changedView, visibility);
527 updateVisibility();
528 }
529
530 private void updateVisibility() {
531 setVisible(isShown());
532 }
533
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700534 @Override
535 protected void onDetachedFromWindow() {
536 super.onDetachedFromWindow();
537 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
538 }
539
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100540 private void setVisible(final boolean isVisible) {
541 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700542 // This call can happen multiple times, but removing only removes a single one.
543 // We therefore need to remove the old one.
544 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100545 // We only animate if we are drawn at least once, otherwise the view might animate when
546 // it's shown the first time
547 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
548 } else {
549 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
550 mAnimate = false;
551 }
552 }
553
Selim Cineke9bad242016-06-15 11:46:37 -0700554 private void focusExpandButtonIfNecessary() {
555 if (mFocusOnVisibilityChange) {
556 NotificationHeaderView header = getVisibleNotificationHeader();
557 if (header != null) {
558 ImageView expandButton = header.getExpandButton();
559 if (expandButton != null) {
560 expandButton.requestAccessibilityFocus();
561 }
562 }
563 mFocusOnVisibilityChange = false;
564 }
565 }
566
Selim Cinekb5605e52015-02-20 18:21:41 +0100567 public void setContentHeight(int contentHeight) {
Selim Cineked64a142018-02-06 18:06:01 -0800568 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
569 int maxContentHeight = mContainingNotification.getIntrinsicHeight()
570 - getExtraRemoteInputHeight(mExpandedRemoteInput)
571 - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
572 mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100573 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400574
575 int minHeightHint = getMinContentHeightHint();
576
577 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
578 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800579 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400580 }
581
582 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
583 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800584 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400585 }
586
Jorim Jaggibe565df2014-04-28 17:51:23 +0200587 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700588 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200589 }
590
Adrian Roos181385c2016-05-05 17:45:44 -0400591 /**
592 * @return the minimum apparent height that the wrapper should allow for the purpose
593 * of aligning elements at the bottom edge. If this is larger than the content
594 * height, the notification is clipped instead of being further shrunk.
595 */
596 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700597 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400598 return mContext.getResources().getDimensionPixelSize(
599 com.android.internal.R.dimen.notification_action_list_height);
600 }
Adrian Roos599be342016-06-13 14:54:39 -0700601
602 // Transition between heads-up & expanded, or pinned.
603 if (mHeadsUpChild != null && mExpandedChild != null) {
604 boolean transitioningBetweenHunAndExpanded =
605 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
606 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700607 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800608 && (mIsHeadsUp || mHeadsUpAnimatingAway)
609 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700610 if (transitioningBetweenHunAndExpanded || pinned) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800611 return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
612 getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700613 }
614 }
615
616 // Size change of the expanded version
617 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
618 && mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800619 return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700620 }
621
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700622 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800623 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
624 hint = mAmbientChild.getHeight();
Adrian Roos6f6e1592017-05-02 16:22:53 -0700625 } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning(
626 VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
627 hint = mAmbientSingleLineChild.getHeight();
Adrian Roos05d3d002017-01-26 15:28:07 -0800628 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800629 hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
Adrian Roos599be342016-06-13 14:54:39 -0700630 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800631 hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
Adrian Roos181385c2016-05-05 17:45:44 -0400632 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800633 hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
634 + mContext.getResources().getDimensionPixelSize(
635 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400636 }
Adrian Roos599be342016-06-13 14:54:39 -0700637
638 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800639 hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700640 }
641 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400642 }
643
Adrian Roos599be342016-06-13 14:54:39 -0700644 private boolean isTransitioningFromTo(int from, int to) {
645 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
646 && mVisibleType == to;
647 }
648
649 private boolean isVisibleOrTransitioning(int type) {
650 return mVisibleType == type || mTransformationStartVisibleType == type
651 || mAnimationStartVisibleType == type;
652 }
653
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800654 private void updateContentTransformation() {
655 int visibleType = calculateVisibleType();
656 if (visibleType != mVisibleType) {
657 // A new transformation starts
658 mTransformationStartVisibleType = mVisibleType;
659 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
660 final TransformableView hiddenView = getTransformableViewForVisibleType(
661 mTransformationStartVisibleType);
662 shownView.transformFrom(hiddenView, 0.0f);
663 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
664 hiddenView.transformTo(shownView, 0.0f);
665 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800666 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800667 }
Selim Cineke8578872016-05-03 16:42:50 -0700668 if (mForceSelectNextLayout) {
669 forceUpdateVisibilities();
670 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800671 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700672 && mVisibleType != mTransformationStartVisibleType
673 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800674 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
675 final TransformableView hiddenView = getTransformableViewForVisibleType(
676 mTransformationStartVisibleType);
677 float transformationAmount = calculateTransformationAmount();
678 shownView.transformFrom(hiddenView, transformationAmount);
679 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800680 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800681 } else {
682 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800683 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800684 }
685 }
686
Selim Cinekc3179332016-03-04 14:44:56 -0800687 private void updateBackgroundTransformation(float transformationAmount) {
688 int endColor = getBackgroundColor(mVisibleType);
689 int startColor = getBackgroundColor(mTransformationStartVisibleType);
690 if (endColor != startColor) {
691 if (startColor == 0) {
692 startColor = mContainingNotification.getBackgroundColorWithoutTint();
693 }
694 if (endColor == 0) {
695 endColor = mContainingNotification.getBackgroundColorWithoutTint();
696 }
697 endColor = NotificationUtils.interpolateColors(startColor, endColor,
698 transformationAmount);
699 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400700 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800701 mContainingNotification.setContentBackground(endColor, false, this);
702 }
703
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800704 private float calculateTransformationAmount() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800705 int startHeight = getViewHeight(mTransformationStartVisibleType);
706 int endHeight = getViewHeight(mVisibleType);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800707 int progress = Math.abs(mContentHeight - startHeight);
708 int totalDistance = Math.abs(endHeight - startHeight);
Selim Cinek011bde92017-11-22 05:53:38 -0800709 if (totalDistance == 0) {
710 Log.wtf(TAG, "the total transformation distance is 0"
711 + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
712 + "\n VisibleType: " + mVisibleType + " height: " + endHeight
713 + "\n mContentHeight: " + mContentHeight);
714 return 1.0f;
715 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800716 float amount = (float) progress / (float) totalDistance;
717 return Math.min(1.0f, amount);
718 }
719
Selim Cinekb5605e52015-02-20 18:21:41 +0100720 public int getContentHeight() {
721 return mContentHeight;
722 }
723
Jorim Jaggibe565df2014-04-28 17:51:23 +0200724 public int getMaxHeight() {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700725 if (mContainingNotification.isShowingAmbient()) {
726 return getShowingAmbientView().getHeight();
727 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800728 return getViewHeight(VISIBLE_TYPE_EXPANDED)
729 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cinekc494e382017-01-31 16:09:23 -0800730 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800731 return getViewHeight(VISIBLE_TYPE_HEADSUP)
732 + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
Selim Cinek8d490d42015-04-10 00:05:50 -0700733 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800734 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
735 }
736
737 private int getViewHeight(int visibleType) {
738 View view = getViewForVisibleType(visibleType);
739 int height = view.getHeight();
740 NotificationViewWrapper viewWrapper = getWrapperForView(view);
741 if (viewWrapper != null) {
742 height += viewWrapper.getHeaderTranslation();
743 }
744 return height;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200745 }
746
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200747 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800748 return getMinHeight(false /* likeGroupExpanded */);
749 }
750
751 public int getMinHeight(boolean likeGroupExpanded) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700752 if (mContainingNotification.isShowingAmbient()) {
753 return getShowingAmbientView().getHeight();
754 } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800755 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
Selim Cinek42357e02016-02-24 18:48:01 -0800756 } else {
757 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700758 }
759 }
760
Adrian Roos6f6e1592017-05-02 16:22:53 -0700761 public View getShowingAmbientView() {
762 View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild;
763 if (v != null) {
764 return v;
765 } else {
766 return mContractedChild;
767 }
768 }
769
Selim Cinek83bc7832015-10-22 13:26:54 -0700770 private boolean isGroupExpanded() {
771 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200772 }
773
Jorim Jaggibe565df2014-04-28 17:51:23 +0200774 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200775 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200776 updateClipping();
777 }
778
Selim Cineka686b2c2016-10-26 13:58:27 -0700779
780 public void setClipBottomAmount(int clipBottomAmount) {
781 mClipBottomAmount = clipBottomAmount;
782 updateClipping();
783 }
784
Selim Cinek875a3a12016-11-18 17:52:16 -0800785 @Override
786 public void setTranslationY(float translationY) {
787 super.setTranslationY(translationY);
788 updateClipping();
789 }
790
Jorim Jaggibe565df2014-04-28 17:51:23 +0200791 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100792 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800793 int top = (int) (mClipTopAmount - getTranslationY());
Selim Cineked64a142018-02-06 18:06:01 -0800794 int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700795 bottom = Math.max(top, bottom);
796 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100797 setClipBounds(mClipBounds);
798 } else {
799 setClipBounds(null);
800 }
801 }
802
803 public void setClipToActualHeight(boolean clipToActualHeight) {
804 mClipToActualHeight = clipToActualHeight;
805 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200806 }
807
Jorim Jaggi11298832014-05-24 16:18:38 +0200808 private void selectLayout(boolean animate, boolean force) {
809 if (mContractedChild == null) {
810 return;
811 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800812 if (mUserExpanding) {
813 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700814 } else {
815 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700816 boolean changedType = visibleType != mVisibleType;
817 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400818 View visibleView = getViewForVisibleType(visibleType);
819 if (visibleView != null) {
820 visibleView.setVisibility(VISIBLE);
821 transferRemoteInputFocus(visibleType);
822 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700823
Selim Cinek589fd3e2016-04-26 18:17:57 -0700824 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
825 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
826 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
827 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
828 animateToVisibleType(visibleType);
829 } else {
830 updateViewVisibilities(visibleType);
831 }
832 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700833 if (changedType) {
834 focusExpandButtonIfNecessary();
835 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800836 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
837 if (visibleWrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800838 visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
839 getMinContentHeightHint());
Adrian Roos05d3d002017-01-26 15:28:07 -0800840 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700841 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200842 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700843 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700844 }
845
846 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800847 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
848 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
849 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
850 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
851 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700852 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild,
853 mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800854 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800855 // forceUpdateVisibilities cancels outstanding animations without updating the
856 // mAnimationStartVisibleType. Do so here instead.
857 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800858 }
859
Adrian Roos4320e892017-01-24 12:50:34 -0800860 private void fireExpandedVisibleListenerIfVisible() {
861 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
862 && mExpandedChild.getVisibility() == VISIBLE) {
863 Runnable listener = mExpandedVisibleListener;
864 mExpandedVisibleListener = null;
865 listener.run();
866 }
867 }
868
Adrian Roos0aac04f2016-12-08 15:59:29 -0800869 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
870 if (view == null) {
871 return;
872 }
873 boolean visible = mVisibleType == type
874 || mTransformationStartVisibleType == type;
875 if (!visible) {
876 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700877 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800878 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200879 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200880 }
881
Selim Cinekc3179332016-03-04 14:44:56 -0800882 public void updateBackgroundColor(boolean animate) {
883 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400884 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800885 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
886 }
887
Kenny Guy14d035c2018-05-02 19:10:36 +0100888 public void setBackgroundTintColor(int color) {
889 if (mExpandedSmartReplyView != null) {
890 mExpandedSmartReplyView.setBackgroundTintColor(color);
891 }
892 }
893
Mady Mellorb0a82462016-04-30 17:31:02 -0700894 public int getVisibleType() {
895 return mVisibleType;
896 }
897
898 public int getBackgroundColorForExpansionState() {
899 // When expanding or user locked we want the new type, when collapsing we want
900 // the original type
901 final int visibleType = (mContainingNotification.isGroupExpanded()
902 || mContainingNotification.isUserLocked())
903 ? calculateVisibleType()
904 : getVisibleType();
905 return getBackgroundColor(visibleType);
906 }
907
908 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800909 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
910 int customBackgroundColor = 0;
911 if (currentVisibleWrapper != null) {
912 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
913 }
914 return customBackgroundColor;
915 }
916
Selim Cinek684a4422015-04-15 16:18:39 -0700917 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800918 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
919 mContractedChild, mContractedWrapper);
920 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
921 mExpandedChild, mExpandedWrapper);
922 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
923 mHeadsUpChild, mHeadsUpWrapper);
924 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
925 mSingleLineView, mSingleLineView);
926 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
927 mAmbientChild, mAmbientWrapper);
Adrian Roos6f6e1592017-05-02 16:22:53 -0700928 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
929 mAmbientSingleLineChild, mAmbientSingleLineChild);
Adrian Roos4320e892017-01-24 12:50:34 -0800930 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800931 // updateViewVisibilities cancels outstanding animations without updating the
932 // mAnimationStartVisibleType. Do so here instead.
933 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800934 }
935
936 private void updateViewVisibility(int visibleType, int type, View view,
937 TransformableView wrapper) {
938 if (view != null) {
939 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -0700940 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700941 }
942
Selim Cinek4ffd6362015-12-29 15:12:23 +0100943 private void animateToVisibleType(int visibleType) {
944 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
945 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700946 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800947 shownView.setVisible(true);
948 return;
949 }
Adrian Roos599be342016-06-13 14:54:39 -0700950 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100951 shownView.transformFrom(hiddenView);
952 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
953 hiddenView.transformTo(shownView, new Runnable() {
954 @Override
955 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800956 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
957 hiddenView.setVisible(false);
958 }
Adrian Roos599be342016-06-13 14:54:39 -0700959 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100960 }
961 });
Adrian Roos4320e892017-01-24 12:50:34 -0800962 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +0200963 }
964
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700965 private void transferRemoteInputFocus(int visibleType) {
966 if (visibleType == VISIBLE_TYPE_HEADSUP
967 && mHeadsUpRemoteInput != null
968 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
969 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
970 }
971 if (visibleType == VISIBLE_TYPE_EXPANDED
972 && mExpandedRemoteInput != null
973 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
974 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
975 }
976 }
977
Selim Cinek684a4422015-04-15 16:18:39 -0700978 /**
979 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100980 * @return the corresponding transformable view according to the given visible type
981 */
982 private TransformableView getTransformableViewForVisibleType(int visibleType) {
983 switch (visibleType) {
984 case VISIBLE_TYPE_EXPANDED:
985 return mExpandedWrapper;
986 case VISIBLE_TYPE_HEADSUP:
987 return mHeadsUpWrapper;
988 case VISIBLE_TYPE_SINGLELINE:
989 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800990 case VISIBLE_TYPE_AMBIENT:
991 return mAmbientWrapper;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700992 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
993 return mAmbientSingleLineChild;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100994 default:
995 return mContractedWrapper;
996 }
997 }
998
999 /**
1000 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -07001001 * @return the corresponding view according to the given visible type
1002 */
1003 private View getViewForVisibleType(int visibleType) {
1004 switch (visibleType) {
1005 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -07001006 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001007 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -07001008 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -07001009 case VISIBLE_TYPE_SINGLELINE:
1010 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001011 case VISIBLE_TYPE_AMBIENT:
1012 return mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001013 case VISIBLE_TYPE_AMBIENT_SINGLELINE:
1014 return mAmbientSingleLineChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001015 default:
1016 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -07001017 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001018 }
1019
Selim Cinek131f1a42017-06-05 17:50:19 -07001020 public NotificationViewWrapper getVisibleWrapper(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -08001021 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -08001022 case VISIBLE_TYPE_EXPANDED:
1023 return mExpandedWrapper;
1024 case VISIBLE_TYPE_HEADSUP:
1025 return mHeadsUpWrapper;
1026 case VISIBLE_TYPE_CONTRACTED:
1027 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001028 case VISIBLE_TYPE_AMBIENT:
1029 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -08001030 default:
1031 return null;
1032 }
1033 }
1034
Selim Cinek684a4422015-04-15 16:18:39 -07001035 /**
1036 * @return one of the static enum types in this view, calculated form the current state
1037 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001038 public int calculateVisibleType() {
Adrian Roos6f6e1592017-05-02 16:22:53 -07001039 if (mContainingNotification.isShowingAmbient()) {
1040 if (mIsChildInGroup && mAmbientSingleLineChild != null) {
1041 return VISIBLE_TYPE_AMBIENT_SINGLELINE;
1042 } else if (mAmbientChild != null) {
1043 return VISIBLE_TYPE_AMBIENT;
1044 } else {
1045 return VISIBLE_TYPE_CONTRACTED;
1046 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001047 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001048 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -08001049 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001050 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001051 ? mContainingNotification.getMaxContentHeight()
1052 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -08001053 if (height == 0) {
1054 height = mContentHeight;
1055 }
Selim Cinek42357e02016-02-24 18:48:01 -08001056 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -08001057 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -07001058 ? VISIBLE_TYPE_SINGLELINE
1059 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001060 return mTransformationStartVisibleType == collapsedVisualType
1061 ? expandedVisualType
1062 : collapsedVisualType;
1063 }
Selim Cinek51d94912016-03-02 15:34:28 -08001064 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
1065 int viewHeight = mContentHeight;
1066 if (intrinsicHeight != 0) {
1067 // the intrinsicHeight might be 0 because it was just reset.
1068 viewHeight = Math.min(mContentHeight, intrinsicHeight);
1069 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001070 return getVisualTypeForHeight(viewHeight);
1071 }
1072
1073 private int getVisualTypeForHeight(float viewHeight) {
1074 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001075 if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
Adrian Roos777ef562015-12-01 17:37:14 -08001076 return VISIBLE_TYPE_EXPANDED;
1077 }
Selim Cinek414ad332017-02-24 19:06:12 -08001078 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -08001079 return VISIBLE_TYPE_SINGLELINE;
1080 }
Adrian Roos777ef562015-12-01 17:37:14 -08001081
Selim Cinekc494e382017-01-31 16:09:23 -08001082 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
1083 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001084 if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -07001085 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -07001086 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001087 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001088 }
1089 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001090 if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
Selim Cinek6ac3fa82016-05-10 17:11:54 -04001091 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001092 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -07001093 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001094 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001095 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001096 }
1097 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001098 }
1099
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001100 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001101 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001102 }
John Spurlocke15452b2014-08-21 09:44:39 -04001103
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001104 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -08001105 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -08001106 return;
1107 }
John Spurlocke15452b2014-08-21 09:44:39 -04001108 mDark = dark;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001109 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -04001110 }
1111
Selim Cinek8d490d42015-04-10 00:05:50 -07001112 public void setHeadsUp(boolean headsUp) {
1113 mIsHeadsUp = headsUp;
1114 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -08001115 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -07001116 }
1117
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +02001118 @Override
1119 public boolean hasOverlappingRendering() {
1120
1121 // This is not really true, but good enough when fading from the contracted to the expanded
1122 // layout, and saves us some layers.
1123 return false;
1124 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001125
Selim Cinek1a48bab2017-02-17 19:38:40 -08001126 public void setLegacy(boolean legacy) {
1127 mLegacy = legacy;
1128 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -08001129 }
1130
Selim Cinek1a48bab2017-02-17 19:38:40 -08001131 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -08001132 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001133 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001134 }
1135 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001136 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001137 }
1138 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001139 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001140 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001141 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001142
Selim Cinek83bc7832015-10-22 13:26:54 -07001143 public void setIsChildInGroup(boolean isChildInGroup) {
1144 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001145 if (mContractedChild != null) {
1146 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1147 }
1148 if (mExpandedChild != null) {
1149 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1150 }
1151 if (mHeadsUpChild != null) {
1152 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1153 }
1154 if (mAmbientChild != null) {
1155 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1156 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001157 updateAllSingleLineViews();
Selim Cinek83bc7832015-10-22 13:26:54 -07001158 }
1159
Adrian Roosb88b1a12015-12-09 18:51:05 -08001160 public void onNotificationUpdated(NotificationData.Entry entry) {
1161 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -08001162 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001163 updateAllSingleLineViews();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001164 if (mContractedChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001165 mContractedWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001166 }
1167 if (mExpandedChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001168 mExpandedWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001169 }
1170 if (mHeadsUpChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001171 mHeadsUpWrapper.onContentUpdated(entry.row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001172 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001173 if (mAmbientChild != null) {
Selim Cinek131f1a42017-06-05 17:50:19 -07001174 mAmbientWrapper.onContentUpdated(entry.row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001175 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001176 applyRemoteInputAndSmartReply(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001177 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001178 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001179 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001180 mPreviousExpandedRemoteInputIntent = null;
1181 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001182 }
1183
Adrian Roos6f6e1592017-05-02 16:22:53 -07001184 private void updateAllSingleLineViews() {
1185 updateSingleLineView();
1186 updateAmbientSingleLineView();
1187 }
Selim Cinek83bc7832015-10-22 13:26:54 -07001188 private void updateSingleLineView() {
1189 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001190 boolean isNewView = mSingleLineView == null;
Selim Cinekc897bd32016-03-18 17:32:31 -07001191 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001192 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001193 if (isNewView) {
1194 updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
1195 mSingleLineView, mSingleLineView);
1196 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001197 } else if (mSingleLineView != null) {
1198 removeView(mSingleLineView);
1199 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001200 }
1201 }
1202
Adrian Roos6f6e1592017-05-02 16:22:53 -07001203 private void updateAmbientSingleLineView() {
1204 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001205 boolean isNewView = mAmbientSingleLineChild == null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001206 mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
1207 mAmbientSingleLineChild, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001208 if (isNewView) {
1209 updateViewVisibility(mVisibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
1210 mAmbientSingleLineChild, mAmbientSingleLineChild);
1211 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001212 } else if (mAmbientSingleLineChild != null) {
1213 removeView(mAmbientSingleLineChild);
1214 mAmbientSingleLineChild = null;
1215 }
1216 }
1217
Petr Cermak7997d7a2018-01-18 15:52:10 +00001218 private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001219 if (mRemoteInputController == null) {
1220 return;
1221 }
1222
Richard Ledley4069f7a2018-02-26 10:36:00 +00001223 boolean enableSmartReplies = (mSmartReplyConstants.isEnabled()
1224 && (!mSmartReplyConstants.requiresTargetingP()
1225 || entry.targetSdk >= Build.VERSION_CODES.P));
Petr Cermak7997d7a2018-01-18 15:52:10 +00001226
Adrian Roosb88b1a12015-12-09 18:51:05 -08001227 boolean hasRemoteInput = false;
Petr Cermak7997d7a2018-01-18 15:52:10 +00001228 RemoteInput remoteInputWithChoices = null;
1229 PendingIntent pendingIntentWithChoices = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001230
1231 Notification.Action[] actions = entry.notification.getNotification().actions;
1232 if (actions != null) {
1233 for (Notification.Action a : actions) {
1234 if (a.getRemoteInputs() != null) {
1235 for (RemoteInput ri : a.getRemoteInputs()) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001236 boolean showRemoteInputView = ri.getAllowFreeFormInput();
1237 boolean showSmartReplyView = enableSmartReplies && ri.getChoices() != null
1238 && ri.getChoices().length > 0;
1239 if (showRemoteInputView) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001240 hasRemoteInput = true;
Petr Cermak7997d7a2018-01-18 15:52:10 +00001241 }
1242 if (showSmartReplyView) {
1243 remoteInputWithChoices = ri;
1244 pendingIntentWithChoices = a.actionIntent;
1245 }
1246 if (showRemoteInputView || showSmartReplyView) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001247 break;
1248 }
1249 }
1250 }
1251 }
1252 }
1253
Petr Cermak7997d7a2018-01-18 15:52:10 +00001254 applyRemoteInput(entry, hasRemoteInput);
Kenny Guy23991102018-04-05 21:18:38 +01001255 applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001256 }
1257
1258 private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001259 View bigContentView = mExpandedChild;
1260 if (bigContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001261 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001262 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1263 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001264 } else {
1265 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001266 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001267 if (mCachedExpandedRemoteInput != null
1268 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1269 // We had a cached remote input but didn't reuse it. Clean up required.
1270 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1271 }
1272 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001273
Adrian Roosb88b1a12015-12-09 18:51:05 -08001274 View headsUpContentView = mHeadsUpChild;
1275 if (headsUpContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001276 mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001277 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001278 } else {
1279 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001280 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001281 if (mCachedHeadsUpRemoteInput != null
1282 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1283 // We had a cached remote input but didn't reuse it. Clean up required.
1284 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1285 }
1286 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001287 }
1288
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001289 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001290 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001291 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001292 View actionContainerCandidate = view.findViewById(
1293 com.android.internal.R.id.actions_container);
1294 if (actionContainerCandidate instanceof FrameLayout) {
1295 RemoteInputView existing = (RemoteInputView)
1296 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1297
1298 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001299 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001300 }
1301
1302 if (existing == null && hasRemoteInput) {
1303 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001304 if (cachedView == null) {
1305 RemoteInputView riv = RemoteInputView.inflate(
1306 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001307
Adrian Roos7813dd72016-09-23 17:12:17 -07001308 riv.setVisibility(View.INVISIBLE);
1309 actionContainer.addView(riv, new LayoutParams(
1310 ViewGroup.LayoutParams.MATCH_PARENT,
1311 ViewGroup.LayoutParams.MATCH_PARENT)
1312 );
1313 existing = riv;
1314 } else {
1315 actionContainer.addView(cachedView);
1316 cachedView.dispatchFinishTemporaryDetach();
1317 cachedView.requestFocus();
1318 existing = cachedView;
1319 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001320 }
1321 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001322 int color = entry.notification.getNotification().color;
1323 if (color == Notification.COLOR_DEFAULT) {
1324 color = mContext.getColor(R.color.default_remote_input_background);
1325 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001326 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001327 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001328 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001329
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001330 existing.setWrapper(wrapper);
Selim Cineked64a142018-02-06 18:06:01 -08001331 existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001332
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001333 if (existingPendingIntent != null || existing.isActive()) {
1334 // The current action could be gone, or the pending intent no longer valid.
1335 // If we find a matching action in the new notification, focus, otherwise close.
1336 Notification.Action[] actions = entry.notification.getNotification().actions;
1337 if (existingPendingIntent != null) {
1338 existing.setPendingIntent(existingPendingIntent);
1339 }
1340 if (existing.updatePendingIntentFromActions(actions)) {
1341 if (!existing.isActive()) {
1342 existing.focus();
1343 }
1344 } else {
1345 if (existing.isActive()) {
1346 existing.close();
1347 }
1348 }
1349 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001350 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001351 return existing;
1352 }
1353 return null;
1354 }
1355
Kenny Guy23991102018-04-05 21:18:38 +01001356 private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
1357 NotificationData.Entry entry) {
1358 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +01001359 mExpandedSmartReplyView =
Kenny Guy23991102018-04-05 21:18:38 +01001360 applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry);
Milo Sredkove7cf4982018-04-09 15:08:26 +01001361 if (mExpandedSmartReplyView != null && remoteInput != null
1362 && remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
Kenny Guya0f6de82018-04-06 16:20:16 +01001363 mSmartReplyController.smartRepliesAdded(entry, remoteInput.getChoices().length);
Kenny Guy23991102018-04-05 21:18:38 +01001364 }
1365 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001366 }
1367
1368 private SmartReplyView applySmartReplyView(
Kenny Guy23991102018-04-05 21:18:38 +01001369 View view, RemoteInput remoteInput, PendingIntent pendingIntent,
1370 NotificationData.Entry entry) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001371 View smartReplyContainerCandidate = view.findViewById(
1372 com.android.internal.R.id.smart_reply_container);
1373 if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
1374 return null;
1375 }
1376 LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
1377 if (remoteInput == null || pendingIntent == null) {
1378 smartReplyContainer.setVisibility(View.GONE);
1379 return null;
1380 }
Kenny Guya0f6de82018-04-06 16:20:16 +01001381 // If we are showing the spinner we don't want to add the buttons.
1382 boolean showingSpinner = entry.notification.getNotification()
1383 .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
1384 if (showingSpinner) {
1385 smartReplyContainer.setVisibility(View.GONE);
1386 return null;
1387 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001388 SmartReplyView smartReplyView = null;
1389 if (smartReplyContainer.getChildCount() == 0) {
1390 smartReplyView = SmartReplyView.inflate(mContext, smartReplyContainer);
1391 smartReplyContainer.addView(smartReplyView);
1392 } else if (smartReplyContainer.getChildCount() == 1) {
1393 View child = smartReplyContainer.getChildAt(0);
1394 if (child instanceof SmartReplyView) {
1395 smartReplyView = (SmartReplyView) child;
1396 }
1397 }
1398 if (smartReplyView != null) {
Kenny Guy23991102018-04-05 21:18:38 +01001399 smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
Kenny Guya0f6de82018-04-06 16:20:16 +01001400 mSmartReplyController, entry, smartReplyContainer);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001401 smartReplyContainer.setVisibility(View.VISIBLE);
1402 }
1403 return smartReplyView;
1404 }
1405
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001406 public void closeRemoteInput() {
1407 if (mHeadsUpRemoteInput != null) {
1408 mHeadsUpRemoteInput.close();
1409 }
1410 if (mExpandedRemoteInput != null) {
1411 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001412 }
1413 }
1414
Selim Cinek83bc7832015-10-22 13:26:54 -07001415 public void setGroupManager(NotificationGroupManager groupManager) {
1416 mGroupManager = groupManager;
1417 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001418
Adrian Roosb88b1a12015-12-09 18:51:05 -08001419 public void setRemoteInputController(RemoteInputController r) {
1420 mRemoteInputController = r;
1421 }
1422
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001423 public void setExpandClickListener(OnClickListener expandClickListener) {
1424 mExpandClickListener = expandClickListener;
1425 }
1426
1427 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001428 mExpandable = expandable;
1429 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001430 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001431 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1432 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001433 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001434 expandable = false;
1435 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001436 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001437 expandable = false;
1438 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001439 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001440 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001441 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001442 }
1443 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001444 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001445 }
1446 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001447 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001448 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001449 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001450 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001451
1452 public NotificationHeaderView getNotificationHeader() {
1453 NotificationHeaderView header = null;
1454 if (mContractedChild != null) {
1455 header = mContractedWrapper.getNotificationHeader();
1456 }
1457 if (header == null && mExpandedChild != null) {
1458 header = mExpandedWrapper.getNotificationHeader();
1459 }
1460 if (header == null && mHeadsUpChild != null) {
1461 header = mHeadsUpWrapper.getNotificationHeader();
1462 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001463 if (header == null && mAmbientChild != null) {
1464 header = mAmbientWrapper.getNotificationHeader();
1465 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001466 return header;
1467 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001468
Julia Reynoldsfc640012018-02-21 12:25:27 -05001469 public void showAppOpsIcons(ArraySet<Integer> activeOps) {
1470 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
1471 mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1472 }
1473 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
1474 mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1475 }
1476 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
1477 mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1478 }
1479 }
Selim Cinekb26afa32017-06-29 10:28:17 +02001480
1481 public NotificationHeaderView getContractedNotificationHeader() {
1482 if (mContractedChild != null) {
1483 return mContractedWrapper.getNotificationHeader();
1484 }
1485 return null;
1486 }
1487
Selim Cinek34eda5e2016-02-18 17:10:43 -08001488 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001489 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001490 return wrapper == null ? null : wrapper.getNotificationHeader();
1491 }
1492
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001493 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1494 mContainingNotification = containingNotification;
1495 }
1496
1497 public void requestSelectLayout(boolean needsAnimation) {
1498 selectLayout(needsAnimation, false);
1499 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001500
1501 public void reInflateViews() {
1502 if (mIsChildInGroup && mSingleLineView != null) {
1503 removeView(mSingleLineView);
1504 mSingleLineView = null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001505 updateAllSingleLineViews();
Selim Cinekde33a4a2016-02-11 16:43:41 -08001506 }
1507 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001508
1509 public void setUserExpanding(boolean userExpanding) {
1510 mUserExpanding = userExpanding;
1511 if (userExpanding) {
1512 mTransformationStartVisibleType = mVisibleType;
1513 } else {
1514 mTransformationStartVisibleType = UNDEFINED;
1515 mVisibleType = calculateVisibleType();
1516 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001517 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001518 }
1519 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001520
1521 /**
1522 * Set by how much the single line view should be indented. Used when a overflow indicator is
1523 * present and only during measuring
1524 */
1525 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1526 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1527 mSingleLineWidthIndention = singleLineWidthIndention;
1528 mContainingNotification.forceLayout();
1529 forceLayout();
1530 }
1531 }
1532
1533 public HybridNotificationView getSingleLineView() {
1534 return mSingleLineView;
1535 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001536
1537 public void setRemoved() {
1538 if (mExpandedRemoteInput != null) {
1539 mExpandedRemoteInput.setRemoved();
1540 }
1541 if (mHeadsUpRemoteInput != null) {
1542 mHeadsUpRemoteInput.setRemoved();
1543 }
1544 }
Adrian Roos599be342016-06-13 14:54:39 -07001545
1546 public void setContentHeightAnimating(boolean animating) {
1547 if (!animating) {
1548 mContentHeightAtAnimationStart = UNDEFINED;
1549 }
1550 }
Selim Cineke9bad242016-06-15 11:46:37 -07001551
Adrian Roose18033c2017-01-17 15:22:49 -08001552 @VisibleForTesting
1553 boolean isAnimatingVisibleType() {
1554 return mAnimationStartVisibleType != UNDEFINED;
1555 }
1556
Selim Cinekcafa87f2016-10-26 17:00:17 -07001557 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1558 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001559 selectLayout(false /* animate */, true /* force */);
1560 }
1561
Selim Cineke9bad242016-06-15 11:46:37 -07001562 public void setFocusOnVisibilityChange() {
1563 mFocusOnVisibilityChange = true;
1564 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001565
1566 public void setIconsVisible(boolean iconsVisible) {
1567 mIconsVisible = iconsVisible;
1568 updateIconVisibilities();
1569 }
1570
1571 private void updateIconVisibilities() {
1572 if (mContractedWrapper != null) {
1573 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1574 if (header != null) {
1575 header.getIcon().setForceHidden(!mIconsVisible);
1576 }
1577 }
1578 if (mHeadsUpWrapper != null) {
1579 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1580 if (header != null) {
1581 header.getIcon().setForceHidden(!mIconsVisible);
1582 }
1583 }
1584 if (mExpandedWrapper != null) {
1585 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1586 if (header != null) {
1587 header.getIcon().setForceHidden(!mIconsVisible);
1588 }
1589 }
1590 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001591
Adrian Roos4320e892017-01-24 12:50:34 -08001592 @Override
1593 public void onVisibilityAggregated(boolean isVisible) {
1594 super.onVisibilityAggregated(isVisible);
1595 if (isVisible) {
1596 fireExpandedVisibleListenerIfVisible();
1597 }
1598 }
1599
1600 /**
1601 * Sets a one-shot listener for when the expanded view becomes visible.
1602 *
1603 * This will fire the listener immediately if the expanded view is already visible.
1604 */
1605 public void setOnExpandedVisibleListener(Runnable r) {
1606 mExpandedVisibleListener = r;
1607 fireExpandedVisibleListenerIfVisible();
1608 }
1609
Selim Cinek6743c0b2017-01-18 18:24:01 -08001610 public void setIsLowPriority(boolean isLowPriority) {
1611 mIsLowPriority = isLowPriority;
1612 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001613
1614 public boolean isDimmable() {
1615 if (!mContractedWrapper.isDimmable()) {
1616 return false;
1617 }
1618 return true;
1619 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001620
1621 /**
1622 * Should a single click be disallowed on this view when on the keyguard?
1623 */
1624 public boolean disallowSingleClick(float x, float y) {
1625 NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
1626 if (visibleWrapper != null) {
1627 return visibleWrapper.disallowSingleClick(x, y);
1628 }
1629 return false;
1630 }
Selim Cinek515b2032017-11-15 10:20:19 -08001631
Selim Cinek86bfcee2018-01-17 11:00:47 -08001632 public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
1633 boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001634 if (mUserExpanding) {
Selim Cinek86bfcee2018-01-17 11:00:47 -08001635 needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
1636 bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001637 }
1638 return needsPaddings;
1639 }
1640
Selim Cinek86bfcee2018-01-17 11:00:47 -08001641 private boolean shouldClipToRounding(int visibleType, boolean topRounded,
1642 boolean bottomRounded) {
Selim Cinek515b2032017-11-15 10:20:19 -08001643 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
1644 if (visibleWrapper == null) {
1645 return false;
1646 }
Selim Cinek86bfcee2018-01-17 11:00:47 -08001647 return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001648 }
Selim Cinekde4de0e2018-01-24 16:21:07 -08001649
1650 public CharSequence getActiveRemoteInputText() {
1651 if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
1652 return mExpandedRemoteInput.getText();
1653 }
1654 if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
1655 return mHeadsUpRemoteInput.getText();
1656 }
1657 return null;
1658 }
Selim Cineked64a142018-02-06 18:06:01 -08001659
Selim Cineka77d2c52018-04-09 10:44:52 -07001660 @Override
1661 public boolean dispatchTouchEvent(MotionEvent ev) {
1662 float y = ev.getY();
1663 // We still want to distribute touch events to the remote input even if it's outside the
1664 // view boundary. We're therefore manually dispatching these events to the remote view
1665 RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
1666 if (riv != null && riv.getVisibility() == VISIBLE) {
1667 int inputStart = mUnrestrictedContentHeight - riv.getHeight();
1668 if (y <= mUnrestrictedContentHeight && y >= inputStart) {
1669 ev.offsetLocation(0, -inputStart);
1670 return riv.dispatchTouchEvent(ev);
1671 }
1672 }
1673 return super.dispatchTouchEvent(ev);
1674 }
1675
1676 /**
1677 * Overridden to make sure touches to the reply action bar actually go through to this view
1678 */
1679 @Override
1680 public boolean pointInView(float localX, float localY, float slop) {
1681 float top = mClipTopAmount;
1682 float bottom = mUnrestrictedContentHeight;
1683 return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
1684 localY < (bottom + slop);
1685 }
1686
1687 private RemoteInputView getRemoteInputForView(View child) {
1688 if (child == mExpandedChild) {
1689 return mExpandedRemoteInput;
1690 } else if (child == mHeadsUpChild) {
1691 return mHeadsUpRemoteInput;
1692 }
1693 return null;
1694 }
1695
Selim Cineked64a142018-02-06 18:06:01 -08001696 public int getExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001697 int viewType = VISIBLE_TYPE_EXPANDED;
1698 if (mExpandedChild == null) {
1699 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001700 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001701 return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001702 }
1703
1704 public int getHeadsUpHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001705 int viewType = VISIBLE_TYPE_HEADSUP;
1706 if (mHeadsUpChild == null) {
1707 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001708 }
Selim Cinek09b442e2018-05-04 15:46:18 -07001709 // The headsUp remote input quickly switches to the expanded one, so lets also include that
1710 // one
1711 return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
1712 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001713 }
1714
1715 public void setRemoteInputVisible(boolean remoteInputVisible) {
1716 mRemoteInputVisible = remoteInputVisible;
1717 setClipChildren(!remoteInputVisible);
1718 }
1719
1720 @Override
1721 public void setClipChildren(boolean clipChildren) {
1722 clipChildren = clipChildren && !mRemoteInputVisible;
1723 super.setClipChildren(clipChildren);
1724 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001725
1726 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1727 if (mContractedWrapper != null) {
1728 mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1729 }
1730 if (mHeadsUpWrapper != null) {
1731 mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1732 }
1733 if (mExpandedWrapper != null) {
1734 mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1735 }
1736 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001737}