blob: 78500357f41f32b54aba29edc54ee211cce901cc [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;
Selim Cinekc3179332016-03-04 14:44:56 -080075 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020076
Jorim Jaggibe565df2014-04-28 17:51:23 +020077 private final Rect mClipBounds = new Rect();
78
Selim Cinek9b49f6d2017-11-13 18:19:35 -080079 private int mMinContractedHeight;
80 private int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020081 private View mContractedChild;
82 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070083 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070084 private HybridNotificationView mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -080085 private View mAmbientChild;
Jorim Jaggibe565df2014-04-28 17:51:23 +020086
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070087 private RemoteInputView mExpandedRemoteInput;
88 private RemoteInputView mHeadsUpRemoteInput;
Petr Cermak10011fa2018-02-05 19:00:54 +000089
90 private SmartReplyConstants mSmartReplyConstants;
Milo Sredkove7cf4982018-04-09 15:08:26 +010091 private SmartReplyView mExpandedSmartReplyView;
Gustav Senntond0e84532018-12-03 16:48:36 +000092 private SmartReplyView mHeadsUpSmartReplyView;
Kenny Guya0f6de82018-04-06 16:20:16 +010093 private SmartReplyController mSmartReplyController;
Gustav Sennton5759f872019-02-13 17:25:26 +000094 private InflatedSmartReplies mExpandedInflatedSmartReplies;
95 private InflatedSmartReplies mHeadsUpInflatedSmartReplies;
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070096
Jorim Jaggi4e857f42014-11-17 19:14:04 +010097 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070098 private NotificationViewWrapper mExpandedWrapper;
99 private NotificationViewWrapper mHeadsUpWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800100 private NotificationViewWrapper mAmbientWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -0700101 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200102 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +0100103 private int mContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -0700104 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -0400105 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100106 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -0700107 private boolean mIsHeadsUp;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800108 private boolean mLegacy;
Selim Cinek83bc7832015-10-22 13:26:54 -0700109 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -0800110 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800111 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800112 private int mNotificationMaxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800113 private int mNotificationAmbientHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -0700114 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700115 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800116 private RemoteInputController mRemoteInputController;
Adrian Roos4320e892017-01-24 12:50:34 -0800117 private Runnable mExpandedVisibleListener;
Kevind5022f92018-10-08 18:30:26 -0700118 /**
119 * List of listeners for when content views become inactive (i.e. not the showing view).
120 */
121 private final ArrayMap<View, Runnable> mOnContentViewInactiveListeners = new ArrayMap<>();
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700122
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700123 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100124 = new ViewTreeObserver.OnPreDrawListener() {
125 @Override
126 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -0800127 // We need to post since we don't want the notification to animate on the very first
128 // frame
129 post(new Runnable() {
130 @Override
131 public void run() {
132 mAnimate = true;
133 }
134 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100135 getViewTreeObserver().removeOnPreDrawListener(this);
136 return true;
137 }
138 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200139
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800140 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800141 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800142 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100143 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800144 private ExpandableNotificationRow mContainingNotification;
Adrian Roos599be342016-06-13 14:54:39 -0700145 /** The visible type at the start of a touch driven transformation */
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800146 private int mTransformationStartVisibleType;
Adrian Roos599be342016-06-13 14:54:39 -0700147 /** The visible type at the start of an animation driven transformation */
148 private int mAnimationStartVisibleType = UNDEFINED;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800149 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700150 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700151 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700152 private PendingIntent mPreviousExpandedRemoteInputIntent;
153 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Adrian Roos7813dd72016-09-23 17:12:17 -0700154 private RemoteInputView mCachedExpandedRemoteInput;
155 private RemoteInputView mCachedHeadsUpRemoteInput;
Selim Cinek98be3f32015-12-17 16:39:06 -0800156
Adrian Roos599be342016-06-13 14:54:39 -0700157 private int mContentHeightAtAnimationStart = UNDEFINED;
Selim Cineke9bad242016-06-15 11:46:37 -0700158 private boolean mFocusOnVisibilityChange;
Selim Cinekcafa87f2016-10-26 17:00:17 -0700159 private boolean mHeadsUpAnimatingAway;
Selim Cinek0242fbb2016-10-19 13:38:32 -0700160 private boolean mIconsVisible;
Selim Cineka686b2c2016-10-26 13:58:27 -0700161 private int mClipBottomAmount;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800162 private boolean mIsLowPriority;
Selim Cinekc0ac4af2017-03-03 15:13:48 -0800163 private boolean mIsContentExpandable;
Selim Cineked64a142018-02-06 18:06:01 -0800164 private boolean mRemoteInputVisible;
165 private int mUnrestrictedContentHeight;
linanson2bcd4032019-01-14 15:22:04 +0800166 private MediaTransferManager mMediaTransferManager;
Adrian Roos599be342016-06-13 14:54:39 -0700167
168
Jorim Jaggibe565df2014-04-28 17:51:23 +0200169 public NotificationContentView(Context context, AttributeSet attrs) {
170 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700171 mHybridGroupManager = new HybridGroupManager(getContext(), this);
linanson2bcd4032019-01-14 15:22:04 +0800172 mMediaTransferManager = new MediaTransferManager(getContext());
Petr Cermak10011fa2018-02-05 19:00:54 +0000173 mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
Kenny Guya0f6de82018-04-06 16:20:16 +0100174 mSmartReplyController = Dependency.get(SmartReplyController.class);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800175 initView();
176 }
177
178 public void initView() {
Selim Cinek860b6da2015-12-16 19:02:19 -0800179 mMinContractedHeight = getResources().getDimensionPixelSize(
180 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800181 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
182 com.android.internal.R.dimen.notification_content_margin_end);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200183 }
184
Adrian Roos0aac04f2016-12-08 15:59:29 -0800185 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
186 int ambientHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800187 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800188 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800189 mNotificationMaxHeight = maxHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800190 mNotificationAmbientHeight = ambientHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800191 }
192
Jorim Jaggibe565df2014-04-28 17:51:23 +0200193 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700194 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
195 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
196 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
197 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700198 int maxSize = Integer.MAX_VALUE / 2;
Selim Cinekc897bd32016-03-18 17:32:31 -0700199 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700200 if (hasFixedHeight || isHeightLimited) {
201 maxSize = MeasureSpec.getSize(heightMeasureSpec);
202 }
203 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800204 if (mExpandedChild != null) {
Milo Sredkove7cf4982018-04-09 15:08:26 +0100205 int notificationMaxHeight = mNotificationMaxHeight;
206 if (mExpandedSmartReplyView != null) {
207 notificationMaxHeight += mExpandedSmartReplyView.getHeightUpperLimit();
208 }
Selim Cinekbee4e072018-05-21 22:06:43 -0700209 notificationMaxHeight += mExpandedWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700210 int size = notificationMaxHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800211 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800212 boolean useExactly = false;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800213 if (layoutParams.height >= 0) {
214 // An actual height is set
Selim Cinekbe570ce2018-05-11 12:29:52 -0700215 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800216 useExactly = true;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800217 }
Selim Cinekbe570ce2018-05-11 12:29:52 -0700218 int spec = MeasureSpec.makeMeasureSpec(size, useExactly
Selim Cinek6743c0b2017-01-18 18:24:01 -0800219 ? MeasureSpec.EXACTLY
220 : MeasureSpec.AT_MOST);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800221 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800222 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
223 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700224 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800225 int heightSpec;
Selim Cinekbe570ce2018-05-11 12:29:52 -0700226 int size = mSmallHeight;
Selim Cinek6743c0b2017-01-18 18:24:01 -0800227 ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
228 boolean useExactly = false;
229 if (layoutParams.height >= 0) {
230 // An actual height is set
231 size = Math.min(size, layoutParams.height);
232 useExactly = true;
233 }
234 if (shouldContractedBeFixedSize() || useExactly) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800235 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
236 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800237 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800238 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800239 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800240 int measuredHeight = mContractedChild.getMeasuredHeight();
241 if (measuredHeight < mMinContractedHeight) {
242 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800243 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek860b6da2015-12-16 19:02:19 -0800244 }
245 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800246 if (updateContractedHeaderWidth()) {
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800247 measureChildWithMargins(mContractedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700248 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700249 if (mExpandedChild != null
250 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
251 // the Expanded child is smaller then the collapsed. Let's remeasure it.
252 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
253 MeasureSpec.EXACTLY);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800254 measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, heightSpec, 0);
Selim Cinek1ba41d12016-04-21 16:14:46 -0700255 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700256 }
257 if (mHeadsUpChild != null) {
Selim Cinek396caca2018-04-10 17:46:46 -0700258 int maxHeight = mHeadsUpHeight;
Gustav Senntond0e84532018-12-03 16:48:36 +0000259 if (mHeadsUpSmartReplyView != null) {
260 maxHeight += mHeadsUpSmartReplyView.getHeightUpperLimit();
261 }
Selim Cinek396caca2018-04-10 17:46:46 -0700262 maxHeight += mHeadsUpWrapper.getExtraMeasureHeight();
Selim Cinekbe570ce2018-05-11 12:29:52 -0700263 int size = maxHeight;
Selim Cinek8d490d42015-04-10 00:05:50 -0700264 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800265 boolean useExactly = false;
Selim Cinek8d490d42015-04-10 00:05:50 -0700266 if (layoutParams.height >= 0) {
267 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800268 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800269 useExactly = true;
Selim Cinek8d490d42015-04-10 00:05:50 -0700270 }
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800271 measureChildWithMargins(mHeadsUpChild, widthMeasureSpec, 0,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800272 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800273 : MeasureSpec.AT_MOST), 0);
Selim Cinek8d490d42015-04-10 00:05:50 -0700274 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
275 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700276 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700277 int singleLineWidthSpec = widthMeasureSpec;
278 if (mSingleLineWidthIndention != 0
279 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
280 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
281 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
Selim Cinek09b7dea2016-08-30 11:28:19 -0700282 MeasureSpec.EXACTLY);
Selim Cinekc897bd32016-03-18 17:32:31 -0700283 }
284 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinekbe570ce2018-05-11 12:29:52 -0700285 MeasureSpec.makeMeasureSpec(mNotificationMaxHeight, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700286 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
287 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800288 if (mAmbientChild != null) {
Selim Cinekbe570ce2018-05-11 12:29:52 -0700289 int size = mNotificationAmbientHeight;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800290 ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
Selim Cinek6743c0b2017-01-18 18:24:01 -0800291 boolean useExactly = false;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800292 if (layoutParams.height >= 0) {
293 // An actual height is set
294 size = Math.min(size, layoutParams.height);
Selim Cinek6743c0b2017-01-18 18:24:01 -0800295 useExactly = true;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800296 }
297 mAmbientChild.measure(widthMeasureSpec,
Selim Cinek6743c0b2017-01-18 18:24:01 -0800298 MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
299 : MeasureSpec.AT_MOST));
Adrian Roos0aac04f2016-12-08 15:59:29 -0800300 maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
301 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700302 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700303 setMeasuredDimension(width, ownHeight);
304 }
305
Selim Cineked64a142018-02-06 18:06:01 -0800306 /**
307 * Get the extra height that needs to be added to the notification height for a given
308 * {@link RemoteInputView}.
309 * This is needed when the user is inline replying in order to ensure that the reply bar has
310 * enough padding.
311 *
312 * @param remoteInput The remote input to check.
313 * @return The extra height needed.
314 */
315 private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
Selim Cinek09b442e2018-05-04 15:46:18 -0700316 if (remoteInput != null && (remoteInput.isActive() || remoteInput.isSending())) {
Selim Cineked64a142018-02-06 18:06:01 -0800317 return getResources().getDimensionPixelSize(
318 com.android.internal.R.dimen.notification_content_margin);
319 }
320 return 0;
321 }
322
Selim Cinek6ecc8102016-01-26 18:26:19 -0800323 private boolean updateContractedHeaderWidth() {
324 // We need to update the expanded and the collapsed header to have exactly the same with to
325 // have the expand buttons laid out at the same location.
326 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
327 if (contractedHeader != null) {
328 if (mExpandedChild != null
329 && mExpandedWrapper.getNotificationHeader() != null) {
330 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
Beth Thibodeau837bfc22019-02-19 12:26:53 -0500331
332 int headerTextMargin = expandedHeader.getHeaderTextMarginEnd();
333 if (headerTextMargin != contractedHeader.getHeaderTextMarginEnd()) {
334 contractedHeader.setHeaderTextMarginEnd(headerTextMargin);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800335 return true;
336 }
337 } else {
338 int paddingEnd = mNotificationContentMarginEnd;
339 if (contractedHeader.getPaddingEnd() != paddingEnd) {
340 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800341 contractedHeader.isLayoutRtl()
342 ? paddingEnd
343 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800344 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800345 contractedHeader.isLayoutRtl()
346 ? contractedHeader.getPaddingLeft()
347 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800348 contractedHeader.getPaddingBottom());
349 contractedHeader.setShowWorkBadgeAtEnd(false);
350 return true;
351 }
352 }
353 }
354 return false;
355 }
356
Selim Cinek860b6da2015-12-16 19:02:19 -0800357 private boolean shouldContractedBeFixedSize() {
358 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
359 }
360
Selim Cinek8d490d42015-04-10 00:05:50 -0700361 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200362 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Adrian Roos599be342016-06-13 14:54:39 -0700363 int previousHeight = 0;
364 if (mExpandedChild != null) {
365 previousHeight = mExpandedChild.getHeight();
366 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200367 super.onLayout(changed, left, top, right, bottom);
Adrian Roos599be342016-06-13 14:54:39 -0700368 if (previousHeight != 0 && mExpandedChild.getHeight() != previousHeight) {
369 mContentHeightAtAnimationStart = previousHeight;
370 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200371 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700372 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700373 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
374 mForceSelectNextLayout = false;
Selim Cinek5f56d852016-05-24 16:55:13 -0700375 updateExpandButtons(mExpandable);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200376 }
377
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100378 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100379 protected void onAttachedToWindow() {
380 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100381 updateVisibility();
382 }
383
Selim Cinekcab4a602014-09-03 14:47:57 +0200384 public View getContractedChild() {
385 return mContractedChild;
386 }
387
388 public View getExpandedChild() {
389 return mExpandedChild;
390 }
391
Selim Cinek8d490d42015-04-10 00:05:50 -0700392 public View getHeadsUpChild() {
393 return mHeadsUpChild;
394 }
395
Adrian Roos0aac04f2016-12-08 15:59:29 -0800396 public View getAmbientChild() {
397 return mAmbientChild;
398 }
399
Kevin38ce6fa2018-10-17 16:00:14 -0700400 /**
401 * Sets the contracted view. Child may be null to remove the content view.
402 *
403 * @param child contracted content view to set
404 */
405 public void setContractedChild(@Nullable View child) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200406 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200407 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200408 removeView(mContractedChild);
409 }
Kevin38ce6fa2018-10-17 16:00:14 -0700410 if (child == null) {
411 mContractedChild = null;
412 mContractedWrapper = null;
413 if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
414 mTransformationStartVisibleType = UNDEFINED;
415 }
416 return;
417 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200418 addView(child);
419 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700420 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
421 mContainingNotification);
Selim Cinek9b49f6d2017-11-13 18:19:35 -0800422 }
423
424 private NotificationViewWrapper getWrapperForView(View child) {
425 if (child == mContractedChild) {
426 return mContractedWrapper;
427 }
428 if (child == mExpandedChild) {
429 return mExpandedWrapper;
430 }
431 if (child == mHeadsUpChild) {
432 return mHeadsUpWrapper;
433 }
434 if (child == mAmbientChild) {
435 return mAmbientWrapper;
436 }
437 return null;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200438 }
439
Kevin38ce6fa2018-10-17 16:00:14 -0700440 /**
441 * Sets the expanded view. Child may be null to remove the content view.
442 *
443 * @param child expanded content view to set
444 */
445 public void setExpandedChild(@Nullable View child) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200446 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800447 mPreviousExpandedRemoteInputIntent = null;
448 if (mExpandedRemoteInput != null) {
449 mExpandedRemoteInput.onNotificationUpdateOrReset();
450 if (mExpandedRemoteInput.isActive()) {
451 mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
452 mCachedExpandedRemoteInput = mExpandedRemoteInput;
453 mExpandedRemoteInput.dispatchStartTemporaryDetach();
454 ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
455 }
456 }
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200457 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200458 removeView(mExpandedChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800459 mExpandedRemoteInput = null;
460 }
461 if (child == null) {
462 mExpandedChild = null;
463 mExpandedWrapper = null;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800464 if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
465 mTransformationStartVisibleType = UNDEFINED;
466 }
Selim Cinek9f186642018-11-29 15:47:40 -0800467 if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
468 selectLayout(false /* animate */, true /* force */);
469 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800470 return;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200471 }
472 addView(child);
473 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700474 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
475 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200476 }
477
Kevin38ce6fa2018-10-17 16:00:14 -0700478 /**
479 * Sets the heads up view. Child may be null to remove the content view.
480 *
481 * @param child heads up content view to set
482 */
483 public void setHeadsUpChild(@Nullable View child) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700484 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800485 mPreviousHeadsUpRemoteInputIntent = null;
486 if (mHeadsUpRemoteInput != null) {
487 mHeadsUpRemoteInput.onNotificationUpdateOrReset();
488 if (mHeadsUpRemoteInput.isActive()) {
489 mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
490 mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
491 mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
492 ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
493 }
494 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700495 mHeadsUpChild.animate().cancel();
496 removeView(mHeadsUpChild);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800497 mHeadsUpRemoteInput = null;
498 }
499 if (child == null) {
500 mHeadsUpChild = null;
501 mHeadsUpWrapper = null;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800502 if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
503 mTransformationStartVisibleType = UNDEFINED;
504 }
Selim Cinek9f186642018-11-29 15:47:40 -0800505 if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
506 selectLayout(false /* animate */, true /* force */);
507 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800508 return;
Selim Cinek8d490d42015-04-10 00:05:50 -0700509 }
510 addView(child);
511 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700512 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
513 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700514 }
515
Kevin38ce6fa2018-10-17 16:00:14 -0700516 /**
517 * Sets the ambient view. Child may be null to remove the content view.
518 *
519 * @param child ambient content view to set
520 */
521 public void setAmbientChild(@Nullable View child) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800522 if (mAmbientChild != null) {
523 mAmbientChild.animate().cancel();
524 removeView(mAmbientChild);
525 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700526 if (child == null) {
Kevind4660b22018-09-27 10:57:35 -0700527 mAmbientChild = null;
528 mAmbientWrapper = null;
Kevind4660b22018-09-27 10:57:35 -0700529 if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
530 mTransformationStartVisibleType = UNDEFINED;
531 }
Selim Cinek9f186642018-11-29 15:47:40 -0800532 if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
533 selectLayout(false /* animate */, true /* force */);
534 }
Adrian Roos1a1ecfc2017-04-17 11:17:59 -0700535 return;
536 }
Adrian Roos0aac04f2016-12-08 15:59:29 -0800537 addView(child);
538 mAmbientChild = child;
539 mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
540 mContainingNotification);
541 }
542
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100543 @Override
544 protected void onVisibilityChanged(View changedView, int visibility) {
545 super.onVisibilityChanged(changedView, visibility);
546 updateVisibility();
Kevin38ce6fa2018-10-17 16:00:14 -0700547 if (visibility != VISIBLE) {
548 // View is no longer visible so all content views are inactive.
549 for (Runnable r : mOnContentViewInactiveListeners.values()) {
550 r.run();
551 }
552 mOnContentViewInactiveListeners.clear();
553 }
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100554 }
555
556 private void updateVisibility() {
557 setVisible(isShown());
558 }
559
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700560 @Override
561 protected void onDetachedFromWindow() {
562 super.onDetachedFromWindow();
563 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
564 }
565
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100566 private void setVisible(final boolean isVisible) {
567 if (isVisible) {
Selim Cinekf1614a62016-05-26 16:57:20 -0700568 // This call can happen multiple times, but removing only removes a single one.
569 // We therefore need to remove the old one.
570 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100571 // We only animate if we are drawn at least once, otherwise the view might animate when
572 // it's shown the first time
573 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
574 } else {
575 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
576 mAnimate = false;
577 }
578 }
579
Selim Cineke9bad242016-06-15 11:46:37 -0700580 private void focusExpandButtonIfNecessary() {
581 if (mFocusOnVisibilityChange) {
582 NotificationHeaderView header = getVisibleNotificationHeader();
583 if (header != null) {
584 ImageView expandButton = header.getExpandButton();
585 if (expandButton != null) {
586 expandButton.requestAccessibilityFocus();
587 }
588 }
589 mFocusOnVisibilityChange = false;
590 }
591 }
592
Selim Cinekb5605e52015-02-20 18:21:41 +0100593 public void setContentHeight(int contentHeight) {
Selim Cineked64a142018-02-06 18:06:01 -0800594 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
595 int maxContentHeight = mContainingNotification.getIntrinsicHeight()
596 - getExtraRemoteInputHeight(mExpandedRemoteInput)
597 - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
598 mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100599 selectLayout(mAnimate /* animate */, false /* force */);
Adrian Roos181385c2016-05-05 17:45:44 -0400600
Kevin38ce6fa2018-10-17 16:00:14 -0700601 if (mContractedChild == null) {
602 // Contracted child may be null if this is the public content view and we don't need to
603 // show it.
604 return;
605 }
606
Adrian Roos181385c2016-05-05 17:45:44 -0400607 int minHeightHint = getMinContentHeightHint();
608
609 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
610 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800611 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400612 }
613
614 wrapper = getVisibleWrapper(mTransformationStartVisibleType);
615 if (wrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800616 wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
Adrian Roos181385c2016-05-05 17:45:44 -0400617 }
618
Jorim Jaggibe565df2014-04-28 17:51:23 +0200619 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700620 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200621 }
622
Adrian Roos181385c2016-05-05 17:45:44 -0400623 /**
624 * @return the minimum apparent height that the wrapper should allow for the purpose
625 * of aligning elements at the bottom edge. If this is larger than the content
626 * height, the notification is clipped instead of being further shrunk.
627 */
628 private int getMinContentHeightHint() {
Adrian Roos599be342016-06-13 14:54:39 -0700629 if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
Adrian Roos181385c2016-05-05 17:45:44 -0400630 return mContext.getResources().getDimensionPixelSize(
631 com.android.internal.R.dimen.notification_action_list_height);
632 }
Adrian Roos599be342016-06-13 14:54:39 -0700633
634 // Transition between heads-up & expanded, or pinned.
635 if (mHeadsUpChild != null && mExpandedChild != null) {
636 boolean transitioningBetweenHunAndExpanded =
637 isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
638 isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
Selim Cinek73cf02a2016-06-17 13:08:00 -0700639 boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
Selim Cinekc494e382017-01-31 16:09:23 -0800640 && (mIsHeadsUp || mHeadsUpAnimatingAway)
641 && !mContainingNotification.isOnKeyguard();
Adrian Roos599be342016-06-13 14:54:39 -0700642 if (transitioningBetweenHunAndExpanded || pinned) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800643 return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
644 getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700645 }
646 }
647
648 // Size change of the expanded version
649 if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
650 && mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800651 return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roos599be342016-06-13 14:54:39 -0700652 }
653
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700654 int hint;
Adrian Roos05d3d002017-01-26 15:28:07 -0800655 if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
656 hint = mAmbientChild.getHeight();
Selim Cinek7927c732019-03-06 15:21:39 -0800657 } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800658 hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
Adrian Roos599be342016-06-13 14:54:39 -0700659 } else if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800660 hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
Adrian Roos181385c2016-05-05 17:45:44 -0400661 } else {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800662 hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
663 + mContext.getResources().getDimensionPixelSize(
664 com.android.internal.R.dimen.notification_action_list_height);
Adrian Roos181385c2016-05-05 17:45:44 -0400665 }
Adrian Roos599be342016-06-13 14:54:39 -0700666
667 if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800668 hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
Adrian Roosbb73e9c62016-05-26 12:06:40 -0700669 }
670 return hint;
Adrian Roos181385c2016-05-05 17:45:44 -0400671 }
672
Adrian Roos599be342016-06-13 14:54:39 -0700673 private boolean isTransitioningFromTo(int from, int to) {
674 return (mTransformationStartVisibleType == from || mAnimationStartVisibleType == from)
675 && mVisibleType == to;
676 }
677
678 private boolean isVisibleOrTransitioning(int type) {
679 return mVisibleType == type || mTransformationStartVisibleType == type
680 || mAnimationStartVisibleType == type;
681 }
682
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800683 private void updateContentTransformation() {
684 int visibleType = calculateVisibleType();
685 if (visibleType != mVisibleType) {
686 // A new transformation starts
687 mTransformationStartVisibleType = mVisibleType;
688 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
689 final TransformableView hiddenView = getTransformableViewForVisibleType(
690 mTransformationStartVisibleType);
691 shownView.transformFrom(hiddenView, 0.0f);
692 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
693 hiddenView.transformTo(shownView, 0.0f);
694 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800695 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800696 }
Selim Cineke8578872016-05-03 16:42:50 -0700697 if (mForceSelectNextLayout) {
698 forceUpdateVisibilities();
699 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800700 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700701 && mVisibleType != mTransformationStartVisibleType
702 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800703 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
704 final TransformableView hiddenView = getTransformableViewForVisibleType(
705 mTransformationStartVisibleType);
706 float transformationAmount = calculateTransformationAmount();
707 shownView.transformFrom(hiddenView, transformationAmount);
708 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800709 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800710 } else {
711 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800712 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800713 }
714 }
715
Selim Cinekc3179332016-03-04 14:44:56 -0800716 private void updateBackgroundTransformation(float transformationAmount) {
717 int endColor = getBackgroundColor(mVisibleType);
718 int startColor = getBackgroundColor(mTransformationStartVisibleType);
719 if (endColor != startColor) {
720 if (startColor == 0) {
721 startColor = mContainingNotification.getBackgroundColorWithoutTint();
722 }
723 if (endColor == 0) {
724 endColor = mContainingNotification.getBackgroundColorWithoutTint();
725 }
726 endColor = NotificationUtils.interpolateColors(startColor, endColor,
727 transformationAmount);
728 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400729 mContainingNotification.updateBackgroundAlpha(transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800730 mContainingNotification.setContentBackground(endColor, false, this);
731 }
732
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800733 private float calculateTransformationAmount() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800734 int startHeight = getViewHeight(mTransformationStartVisibleType);
735 int endHeight = getViewHeight(mVisibleType);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800736 int progress = Math.abs(mContentHeight - startHeight);
737 int totalDistance = Math.abs(endHeight - startHeight);
Selim Cinek011bde92017-11-22 05:53:38 -0800738 if (totalDistance == 0) {
739 Log.wtf(TAG, "the total transformation distance is 0"
740 + "\n StartType: " + mTransformationStartVisibleType + " height: " + startHeight
741 + "\n VisibleType: " + mVisibleType + " height: " + endHeight
742 + "\n mContentHeight: " + mContentHeight);
743 return 1.0f;
744 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800745 float amount = (float) progress / (float) totalDistance;
746 return Math.min(1.0f, amount);
747 }
748
Selim Cinekb5605e52015-02-20 18:21:41 +0100749 public int getContentHeight() {
750 return mContentHeight;
751 }
752
Jorim Jaggibe565df2014-04-28 17:51:23 +0200753 public int getMaxHeight() {
Selim Cinek3d6ae232019-01-04 14:14:33 -0800754 if (mExpandedChild != null) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800755 return getViewHeight(VISIBLE_TYPE_EXPANDED)
756 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cinek3d6ae232019-01-04 14:14:33 -0800757 } else if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
758 return getShowingAmbientView().getHeight();
Selim Cinekc494e382017-01-31 16:09:23 -0800759 } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800760 return getViewHeight(VISIBLE_TYPE_HEADSUP)
761 + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
Kevin38ce6fa2018-10-17 16:00:14 -0700762 } else if (mContractedChild != null) {
763 return getViewHeight(VISIBLE_TYPE_CONTRACTED);
Selim Cinek8d490d42015-04-10 00:05:50 -0700764 }
Kevin38ce6fa2018-10-17 16:00:14 -0700765 return mNotificationMaxHeight;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800766 }
767
768 private int getViewHeight(int visibleType) {
769 View view = getViewForVisibleType(visibleType);
770 int height = view.getHeight();
771 NotificationViewWrapper viewWrapper = getWrapperForView(view);
772 if (viewWrapper != null) {
773 height += viewWrapper.getHeaderTranslation();
774 }
775 return height;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200776 }
777
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200778 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800779 return getMinHeight(false /* likeGroupExpanded */);
780 }
781
782 public int getMinHeight(boolean likeGroupExpanded) {
Kevin38ce6fa2018-10-17 16:00:14 -0700783 if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
Adrian Roos6f6e1592017-05-02 16:22:53 -0700784 return getShowingAmbientView().getHeight();
785 } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Kevin38ce6fa2018-10-17 16:00:14 -0700786 return mContractedChild != null
787 ? getViewHeight(VISIBLE_TYPE_CONTRACTED) : mMinContractedHeight;
Selim Cinek42357e02016-02-24 18:48:01 -0800788 } else {
789 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700790 }
791 }
792
Adrian Roos6f6e1592017-05-02 16:22:53 -0700793 public View getShowingAmbientView() {
Selim Cinek7927c732019-03-06 15:21:39 -0800794 View v = mIsChildInGroup ? mSingleLineView : mAmbientChild;
Adrian Roos6f6e1592017-05-02 16:22:53 -0700795 if (v != null) {
796 return v;
797 } else {
798 return mContractedChild;
799 }
800 }
801
Selim Cinek83bc7832015-10-22 13:26:54 -0700802 private boolean isGroupExpanded() {
803 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200804 }
805
Jorim Jaggibe565df2014-04-28 17:51:23 +0200806 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200807 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200808 updateClipping();
809 }
810
Selim Cineka686b2c2016-10-26 13:58:27 -0700811
812 public void setClipBottomAmount(int clipBottomAmount) {
813 mClipBottomAmount = clipBottomAmount;
814 updateClipping();
815 }
816
Selim Cinek875a3a12016-11-18 17:52:16 -0800817 @Override
818 public void setTranslationY(float translationY) {
819 super.setTranslationY(translationY);
820 updateClipping();
821 }
822
Jorim Jaggibe565df2014-04-28 17:51:23 +0200823 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100824 if (mClipToActualHeight) {
Selim Cinek875a3a12016-11-18 17:52:16 -0800825 int top = (int) (mClipTopAmount - getTranslationY());
Selim Cineked64a142018-02-06 18:06:01 -0800826 int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
Selim Cineka686b2c2016-10-26 13:58:27 -0700827 bottom = Math.max(top, bottom);
828 mClipBounds.set(0, top, getWidth(), bottom);
Selim Cinek4ffd6362015-12-29 15:12:23 +0100829 setClipBounds(mClipBounds);
830 } else {
831 setClipBounds(null);
832 }
833 }
834
835 public void setClipToActualHeight(boolean clipToActualHeight) {
836 mClipToActualHeight = clipToActualHeight;
837 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200838 }
839
Jorim Jaggi11298832014-05-24 16:18:38 +0200840 private void selectLayout(boolean animate, boolean force) {
841 if (mContractedChild == null) {
842 return;
843 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800844 if (mUserExpanding) {
845 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700846 } else {
847 int visibleType = calculateVisibleType();
Selim Cineke9bad242016-06-15 11:46:37 -0700848 boolean changedType = visibleType != mVisibleType;
849 if (changedType || force) {
Adrian Roos181385c2016-05-05 17:45:44 -0400850 View visibleView = getViewForVisibleType(visibleType);
851 if (visibleView != null) {
852 visibleView.setVisibility(VISIBLE);
853 transferRemoteInputFocus(visibleType);
854 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700855
Selim Cinek589fd3e2016-04-26 18:17:57 -0700856 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
857 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
858 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
859 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
860 animateToVisibleType(visibleType);
861 } else {
862 updateViewVisibilities(visibleType);
863 }
864 mVisibleType = visibleType;
Selim Cineke9bad242016-06-15 11:46:37 -0700865 if (changedType) {
866 focusExpandButtonIfNecessary();
867 }
Adrian Roos05d3d002017-01-26 15:28:07 -0800868 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
869 if (visibleWrapper != null) {
Selim Cineked64a142018-02-06 18:06:01 -0800870 visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
871 getMinContentHeightHint());
Adrian Roos05d3d002017-01-26 15:28:07 -0800872 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700873 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200874 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700875 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700876 }
877
878 private void forceUpdateVisibilities() {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800879 forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
880 forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
881 forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
882 forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
883 forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
Adrian Roos4320e892017-01-24 12:50:34 -0800884 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800885 // forceUpdateVisibilities cancels outstanding animations without updating the
886 // mAnimationStartVisibleType. Do so here instead.
887 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800888 }
889
Adrian Roos4320e892017-01-24 12:50:34 -0800890 private void fireExpandedVisibleListenerIfVisible() {
891 if (mExpandedVisibleListener != null && mExpandedChild != null && isShown()
892 && mExpandedChild.getVisibility() == VISIBLE) {
893 Runnable listener = mExpandedVisibleListener;
894 mExpandedVisibleListener = null;
895 listener.run();
896 }
897 }
898
Adrian Roos0aac04f2016-12-08 15:59:29 -0800899 private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
900 if (view == null) {
901 return;
902 }
903 boolean visible = mVisibleType == type
904 || mTransformationStartVisibleType == type;
905 if (!visible) {
906 view.setVisibility(INVISIBLE);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700907 } else {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800908 wrapper.setVisible(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200909 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200910 }
911
Selim Cinekc3179332016-03-04 14:44:56 -0800912 public void updateBackgroundColor(boolean animate) {
913 int customBackgroundColor = getBackgroundColor(mVisibleType);
Mady Mellorc7d65b42016-05-04 11:44:57 -0400914 mContainingNotification.resetBackgroundAlpha();
Selim Cinekc3179332016-03-04 14:44:56 -0800915 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
916 }
917
Kenny Guy14d035c2018-05-02 19:10:36 +0100918 public void setBackgroundTintColor(int color) {
919 if (mExpandedSmartReplyView != null) {
920 mExpandedSmartReplyView.setBackgroundTintColor(color);
921 }
Gustav Senntond0e84532018-12-03 16:48:36 +0000922 if (mHeadsUpSmartReplyView != null) {
923 mHeadsUpSmartReplyView.setBackgroundTintColor(color);
924 }
Kenny Guy14d035c2018-05-02 19:10:36 +0100925 }
926
Mady Mellorb0a82462016-04-30 17:31:02 -0700927 public int getVisibleType() {
928 return mVisibleType;
929 }
930
931 public int getBackgroundColorForExpansionState() {
932 // When expanding or user locked we want the new type, when collapsing we want
933 // the original type
934 final int visibleType = (mContainingNotification.isGroupExpanded()
935 || mContainingNotification.isUserLocked())
936 ? calculateVisibleType()
937 : getVisibleType();
938 return getBackgroundColor(visibleType);
939 }
940
941 public int getBackgroundColor(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -0800942 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
943 int customBackgroundColor = 0;
944 if (currentVisibleWrapper != null) {
945 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
946 }
947 return customBackgroundColor;
948 }
949
Selim Cinek684a4422015-04-15 16:18:39 -0700950 private void updateViewVisibilities(int visibleType) {
Adrian Roos0aac04f2016-12-08 15:59:29 -0800951 updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
952 mContractedChild, mContractedWrapper);
953 updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
954 mExpandedChild, mExpandedWrapper);
955 updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
956 mHeadsUpChild, mHeadsUpWrapper);
957 updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
958 mSingleLineView, mSingleLineView);
959 updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
960 mAmbientChild, mAmbientWrapper);
Adrian Roos4320e892017-01-24 12:50:34 -0800961 fireExpandedVisibleListenerIfVisible();
Adrian Roose18033c2017-01-17 15:22:49 -0800962 // updateViewVisibilities cancels outstanding animations without updating the
963 // mAnimationStartVisibleType. Do so here instead.
964 mAnimationStartVisibleType = UNDEFINED;
Adrian Roos0aac04f2016-12-08 15:59:29 -0800965 }
966
967 private void updateViewVisibility(int visibleType, int type, View view,
968 TransformableView wrapper) {
969 if (view != null) {
970 wrapper.setVisible(visibleType == type);
Selim Cinek83bc7832015-10-22 13:26:54 -0700971 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700972 }
973
Selim Cinek4ffd6362015-12-29 15:12:23 +0100974 private void animateToVisibleType(int visibleType) {
975 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
976 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700977 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800978 shownView.setVisible(true);
979 return;
980 }
Adrian Roos599be342016-06-13 14:54:39 -0700981 mAnimationStartVisibleType = mVisibleType;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100982 shownView.transformFrom(hiddenView);
983 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
984 hiddenView.transformTo(shownView, new Runnable() {
985 @Override
986 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800987 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
988 hiddenView.setVisible(false);
989 }
Adrian Roos599be342016-06-13 14:54:39 -0700990 mAnimationStartVisibleType = UNDEFINED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100991 }
992 });
Adrian Roos4320e892017-01-24 12:50:34 -0800993 fireExpandedVisibleListenerIfVisible();
Jorim Jaggi11298832014-05-24 16:18:38 +0200994 }
995
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700996 private void transferRemoteInputFocus(int visibleType) {
997 if (visibleType == VISIBLE_TYPE_HEADSUP
998 && mHeadsUpRemoteInput != null
999 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
1000 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
1001 }
1002 if (visibleType == VISIBLE_TYPE_EXPANDED
1003 && mExpandedRemoteInput != null
1004 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
1005 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
1006 }
1007 }
1008
Selim Cinek684a4422015-04-15 16:18:39 -07001009 /**
1010 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +01001011 * @return the corresponding transformable view according to the given visible type
1012 */
1013 private TransformableView getTransformableViewForVisibleType(int visibleType) {
1014 switch (visibleType) {
1015 case VISIBLE_TYPE_EXPANDED:
1016 return mExpandedWrapper;
1017 case VISIBLE_TYPE_HEADSUP:
1018 return mHeadsUpWrapper;
1019 case VISIBLE_TYPE_SINGLELINE:
1020 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001021 case VISIBLE_TYPE_AMBIENT:
1022 return mAmbientWrapper;
Selim Cinek4ffd6362015-12-29 15:12:23 +01001023 default:
1024 return mContractedWrapper;
1025 }
1026 }
1027
1028 /**
1029 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -07001030 * @return the corresponding view according to the given visible type
1031 */
1032 private View getViewForVisibleType(int visibleType) {
1033 switch (visibleType) {
1034 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -07001035 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001036 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -07001037 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -07001038 case VISIBLE_TYPE_SINGLELINE:
1039 return mSingleLineView;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001040 case VISIBLE_TYPE_AMBIENT:
1041 return mAmbientChild;
Selim Cinek684a4422015-04-15 16:18:39 -07001042 default:
1043 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -07001044 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001045 }
1046
Selim Cinek131f1a42017-06-05 17:50:19 -07001047 public NotificationViewWrapper getVisibleWrapper(int visibleType) {
Selim Cinekc3179332016-03-04 14:44:56 -08001048 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -08001049 case VISIBLE_TYPE_EXPANDED:
1050 return mExpandedWrapper;
1051 case VISIBLE_TYPE_HEADSUP:
1052 return mHeadsUpWrapper;
1053 case VISIBLE_TYPE_CONTRACTED:
1054 return mContractedWrapper;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001055 case VISIBLE_TYPE_AMBIENT:
1056 return mAmbientWrapper;
Selim Cinek34eda5e2016-02-18 17:10:43 -08001057 default:
1058 return null;
1059 }
1060 }
1061
Selim Cinek684a4422015-04-15 16:18:39 -07001062 /**
1063 * @return one of the static enum types in this view, calculated form the current state
1064 */
Mady Mellorb0a82462016-04-30 17:31:02 -07001065 public int calculateVisibleType() {
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001066 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -08001067 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001068 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -08001069 ? mContainingNotification.getMaxContentHeight()
1070 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -08001071 if (height == 0) {
1072 height = mContentHeight;
1073 }
Selim Cinek42357e02016-02-24 18:48:01 -08001074 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek414ad332017-02-24 19:06:12 -08001075 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
Selim Cinek589fd3e2016-04-26 18:17:57 -07001076 ? VISIBLE_TYPE_SINGLELINE
1077 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001078 return mTransformationStartVisibleType == collapsedVisualType
1079 ? expandedVisualType
1080 : collapsedVisualType;
1081 }
Selim Cinek51d94912016-03-02 15:34:28 -08001082 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
1083 int viewHeight = mContentHeight;
1084 if (intrinsicHeight != 0) {
1085 // the intrinsicHeight might be 0 because it was just reset.
1086 viewHeight = Math.min(mContentHeight, intrinsicHeight);
1087 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001088 return getVisualTypeForHeight(viewHeight);
1089 }
1090
1091 private int getVisualTypeForHeight(float viewHeight) {
1092 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001093 if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
Adrian Roos777ef562015-12-01 17:37:14 -08001094 return VISIBLE_TYPE_EXPANDED;
1095 }
Selim Cinek3d6ae232019-01-04 14:14:33 -08001096 boolean onAmbient = mContainingNotification.isOnAmbient();
Selim Cinek414ad332017-02-24 19:06:12 -08001097 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinek7927c732019-03-06 15:21:39 -08001098 return VISIBLE_TYPE_SINGLELINE;
Selim Cinekd84a5932015-12-15 11:45:36 -08001099 }
Adrian Roos777ef562015-12-01 17:37:14 -08001100
Selim Cinekc494e382017-01-31 16:09:23 -08001101 if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
1102 && !mContainingNotification.isOnKeyguard()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001103 if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -07001104 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -07001105 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001106 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001107 }
1108 } else {
Selim Cinek3d6ae232019-01-04 14:14:33 -08001109 int collapsedType = onAmbient && mAmbientChild != null ? VISIBLE_TYPE_AMBIENT :
1110 VISIBLE_TYPE_CONTRACTED;
Kevin38ce6fa2018-10-17 16:00:14 -07001111 if (noExpandedChild || (mContractedChild != null
Selim Cinek3d6ae232019-01-04 14:14:33 -08001112 && viewHeight <= getViewHeight(collapsedType)
Selim Cinek6ac3fa82016-05-10 17:11:54 -04001113 && (!mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -08001114 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek3d6ae232019-01-04 14:14:33 -08001115 return collapsedType;
Selim Cinek8d490d42015-04-10 00:05:50 -07001116 } else {
Selim Cinek684a4422015-04-15 16:18:39 -07001117 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -07001118 }
1119 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001120 }
1121
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001122 public boolean isContentExpandable() {
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001123 return mIsContentExpandable;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02001124 }
John Spurlocke15452b2014-08-21 09:44:39 -04001125
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001126 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -08001127 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -08001128 return;
1129 }
John Spurlocke15452b2014-08-21 09:44:39 -04001130 mDark = dark;
Adrian Roos0aac04f2016-12-08 15:59:29 -08001131 selectLayout(!dark && fade /* animate */, false /* force */);
John Spurlocke15452b2014-08-21 09:44:39 -04001132 }
1133
Selim Cinek8d490d42015-04-10 00:05:50 -07001134 public void setHeadsUp(boolean headsUp) {
1135 mIsHeadsUp = headsUp;
1136 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -08001137 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -07001138 }
1139
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +02001140 @Override
1141 public boolean hasOverlappingRendering() {
1142
1143 // This is not really true, but good enough when fading from the contracted to the expanded
1144 // layout, and saves us some layers.
1145 return false;
1146 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001147
Selim Cinek1a48bab2017-02-17 19:38:40 -08001148 public void setLegacy(boolean legacy) {
1149 mLegacy = legacy;
1150 updateLegacy();
Selim Cinekc3179332016-03-04 14:44:56 -08001151 }
1152
Selim Cinek1a48bab2017-02-17 19:38:40 -08001153 private void updateLegacy() {
Selim Cinekc3179332016-03-04 14:44:56 -08001154 if (mContractedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001155 mContractedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001156 }
1157 if (mExpandedChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001158 mExpandedWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001159 }
1160 if (mHeadsUpChild != null) {
Selim Cinek1a48bab2017-02-17 19:38:40 -08001161 mHeadsUpWrapper.setLegacy(mLegacy);
Selim Cinekc3179332016-03-04 14:44:56 -08001162 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -07001163 }
Selim Cinek8d6440d2015-10-22 13:00:05 -07001164
Selim Cinek83bc7832015-10-22 13:26:54 -07001165 public void setIsChildInGroup(boolean isChildInGroup) {
1166 mIsChildInGroup = isChildInGroup;
Selim Cinek414ad332017-02-24 19:06:12 -08001167 if (mContractedChild != null) {
1168 mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
1169 }
1170 if (mExpandedChild != null) {
1171 mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
1172 }
1173 if (mHeadsUpChild != null) {
1174 mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
1175 }
1176 if (mAmbientChild != null) {
1177 mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
1178 }
Adrian Roos6f6e1592017-05-02 16:22:53 -07001179 updateAllSingleLineViews();
Selim Cinek83bc7832015-10-22 13:26:54 -07001180 }
1181
Ned Burnsf81c4c42019-01-07 14:10:43 -05001182 public void onNotificationUpdated(NotificationEntry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001183 mStatusBarNotification = entry.notification;
Kevind5022f92018-10-08 18:30:26 -07001184 mOnContentViewInactiveListeners.clear();
Selim Cinek860b6da2015-12-16 19:02:19 -08001185 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001186 updateAllSingleLineViews();
Evan Laird94492852018-10-25 13:43:01 -04001187 ExpandableNotificationRow row = entry.getRow();
Selim Cinek8fc93c92015-11-23 17:48:07 -08001188 if (mContractedChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001189 mContractedWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001190 }
1191 if (mExpandedChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001192 mExpandedWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001193 }
1194 if (mHeadsUpChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001195 mHeadsUpWrapper.onContentUpdated(row);
Selim Cinek8fc93c92015-11-23 17:48:07 -08001196 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001197 if (mAmbientChild != null) {
Evan Laird94492852018-10-25 13:43:01 -04001198 mAmbientWrapper.onContentUpdated(row);
Adrian Roos0aac04f2016-12-08 15:59:29 -08001199 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001200 applyRemoteInputAndSmartReply(entry);
linanson2bcd4032019-01-14 15:22:04 +08001201 applyMediaTransfer(entry);
Selim Cinek1a48bab2017-02-17 19:38:40 -08001202 updateLegacy();
Selim Cinek589fd3e2016-04-26 18:17:57 -07001203 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -08001204 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001205 mPreviousExpandedRemoteInputIntent = null;
1206 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001207 }
1208
Adrian Roos6f6e1592017-05-02 16:22:53 -07001209 private void updateAllSingleLineViews() {
1210 updateSingleLineView();
Adrian Roos6f6e1592017-05-02 16:22:53 -07001211 }
Tony Mak638430e2018-10-08 19:19:10 +01001212
Selim Cinek83bc7832015-10-22 13:26:54 -07001213 private void updateSingleLineView() {
1214 if (mIsChildInGroup) {
Selim Cinekc5926c32018-05-15 11:17:07 -07001215 boolean isNewView = mSingleLineView == null;
Selim Cinekc897bd32016-03-18 17:32:31 -07001216 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -07001217 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekc5926c32018-05-15 11:17:07 -07001218 if (isNewView) {
1219 updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
1220 mSingleLineView, mSingleLineView);
1221 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001222 } else if (mSingleLineView != null) {
1223 removeView(mSingleLineView);
1224 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -07001225 }
1226 }
1227
linanson2bcd4032019-01-14 15:22:04 +08001228 private void applyMediaTransfer(final NotificationEntry entry) {
1229 View bigContentView = mExpandedChild;
1230 if (bigContentView == null || !entry.isMediaNotification()) {
1231 return;
1232 }
1233
1234 View mediaActionContainer = bigContentView.findViewById(
1235 com.android.internal.R.id.media_actions);
1236 if (!(mediaActionContainer instanceof LinearLayout)) {
1237 return;
1238 }
1239
1240 mMediaTransferManager.applyMediaTransferView((ViewGroup) mediaActionContainer,
1241 entry);
1242 }
1243
Ned Burnsf81c4c42019-01-07 14:10:43 -05001244 private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001245 if (mRemoteInputController == null) {
1246 return;
1247 }
1248
Gustav Sennton5759f872019-02-13 17:25:26 +00001249 applyRemoteInput(entry, InflatedSmartReplies.hasFreeformRemoteInput(entry));
1250
1251 if (mExpandedInflatedSmartReplies == null && mHeadsUpInflatedSmartReplies == null) {
1252 if (DEBUG) {
1253 Log.d(TAG, "Both expanded, and heads-up InflatedSmartReplies are null, "
1254 + "don't add smart replies.");
1255 }
1256 return;
1257 }
1258 // The inflated smart-reply objects for the expanded view and the heads-up view both contain
1259 // the same SmartRepliesAndActions to avoid discrepancies between the two views. We here
1260 // reuse that object for our local SmartRepliesAndActions to avoid discrepancies between
1261 // this class and the InflatedSmartReplies classes.
1262 SmartRepliesAndActions smartRepliesAndActions = mExpandedInflatedSmartReplies != null
1263 ? mExpandedInflatedSmartReplies.getSmartRepliesAndActions()
1264 : mHeadsUpInflatedSmartReplies.getSmartRepliesAndActions();
Gustav Sennton2d66b512019-01-31 17:48:17 +00001265 if (DEBUG) {
1266 Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.",
1267 entry.notification.getKey(),
1268 smartRepliesAndActions.smartActions == null ? 0 :
1269 smartRepliesAndActions.smartActions.actions.size(),
1270 smartRepliesAndActions.smartReplies == null ? 0 :
1271 smartRepliesAndActions.smartReplies.choices.length));
1272 }
Gustav Senntoneab53682018-11-01 16:30:23 +00001273 applySmartReplyView(smartRepliesAndActions, entry);
1274 }
Tony Mak638430e2018-10-08 19:19:10 +01001275
Ned Burnsf81c4c42019-01-07 14:10:43 -05001276 private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001277 View bigContentView = mExpandedChild;
1278 if (bigContentView != null) {
Tony Mak638430e2018-10-08 19:19:10 +01001279 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001280 mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
1281 mExpandedWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001282 } else {
1283 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001284 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001285 if (mCachedExpandedRemoteInput != null
1286 && mCachedExpandedRemoteInput != mExpandedRemoteInput) {
1287 // We had a cached remote input but didn't reuse it. Clean up required.
1288 mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
1289 }
1290 mCachedExpandedRemoteInput = null;
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001291
Adrian Roosb88b1a12015-12-09 18:51:05 -08001292 View headsUpContentView = mHeadsUpChild;
1293 if (headsUpContentView != null) {
Tony Mak638430e2018-10-08 19:19:10 +01001294 mHeadsUpRemoteInput = applyRemoteInput(
1295 headsUpContentView, entry, hasFreeformRemoteInput,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001296 mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001297 } else {
1298 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001299 }
Adrian Roos7813dd72016-09-23 17:12:17 -07001300 if (mCachedHeadsUpRemoteInput != null
1301 && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
1302 // We had a cached remote input but didn't reuse it. Clean up required.
1303 mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
1304 }
1305 mCachedHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -08001306 }
1307
Ned Burnsf81c4c42019-01-07 14:10:43 -05001308 private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
Adrian Roos7813dd72016-09-23 17:12:17 -07001309 boolean hasRemoteInput, PendingIntent existingPendingIntent,
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001310 RemoteInputView cachedView, NotificationViewWrapper wrapper) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001311 View actionContainerCandidate = view.findViewById(
1312 com.android.internal.R.id.actions_container);
1313 if (actionContainerCandidate instanceof FrameLayout) {
1314 RemoteInputView existing = (RemoteInputView)
1315 view.findViewWithTag(RemoteInputView.VIEW_TAG);
1316
1317 if (existing != null) {
Adrian Roos0789ee02016-06-01 11:34:58 -07001318 existing.onNotificationUpdateOrReset();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001319 }
1320
1321 if (existing == null && hasRemoteInput) {
1322 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
Adrian Roos7813dd72016-09-23 17:12:17 -07001323 if (cachedView == null) {
1324 RemoteInputView riv = RemoteInputView.inflate(
1325 mContext, actionContainer, entry, mRemoteInputController);
Adrian Roosb88b1a12015-12-09 18:51:05 -08001326
Adrian Roos7813dd72016-09-23 17:12:17 -07001327 riv.setVisibility(View.INVISIBLE);
1328 actionContainer.addView(riv, new LayoutParams(
1329 ViewGroup.LayoutParams.MATCH_PARENT,
1330 ViewGroup.LayoutParams.MATCH_PARENT)
1331 );
1332 existing = riv;
1333 } else {
1334 actionContainer.addView(cachedView);
1335 cachedView.dispatchFinishTemporaryDetach();
1336 cachedView.requestFocus();
1337 existing = cachedView;
1338 }
Adrian Roos4ff3b122016-02-01 12:26:13 -08001339 }
1340 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -08001341 int color = entry.notification.getNotification().color;
1342 if (color == Notification.COLOR_DEFAULT) {
1343 color = mContext.getColor(R.color.default_remote_input_background);
1344 }
Lucas Dupina291d192018-06-07 13:59:42 -07001345 existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
Adrian Roos25ea5b82016-04-27 16:34:35 -07001346 mContext.getColor(R.color.remote_input_text_enabled),
Adrian Roos4ff3b122016-02-01 12:26:13 -08001347 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001348
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001349 existing.setWrapper(wrapper);
Selim Cineked64a142018-02-06 18:06:01 -08001350 existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
Adrian Roos7b9ed0d2017-01-24 15:55:18 -08001351
Adrian Roos4c1fcc82016-03-31 14:39:39 -07001352 if (existingPendingIntent != null || existing.isActive()) {
1353 // The current action could be gone, or the pending intent no longer valid.
1354 // If we find a matching action in the new notification, focus, otherwise close.
1355 Notification.Action[] actions = entry.notification.getNotification().actions;
1356 if (existingPendingIntent != null) {
1357 existing.setPendingIntent(existingPendingIntent);
1358 }
1359 if (existing.updatePendingIntentFromActions(actions)) {
1360 if (!existing.isActive()) {
1361 existing.focus();
1362 }
1363 } else {
1364 if (existing.isActive()) {
1365 existing.close();
1366 }
1367 }
1368 }
Adrian Roosb88b1a12015-12-09 18:51:05 -08001369 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001370 return existing;
1371 }
1372 return null;
1373 }
1374
Gustav Sennton5759f872019-02-13 17:25:26 +00001375 private void applySmartReplyView(
1376 SmartRepliesAndActions smartRepliesAndActions,
Ned Burnsf81c4c42019-01-07 14:10:43 -05001377 NotificationEntry entry) {
Kenny Guy23991102018-04-05 21:18:38 +01001378 if (mExpandedChild != null) {
Gustav Sennton5759f872019-02-13 17:25:26 +00001379 mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, smartRepliesAndActions,
1380 entry, mExpandedInflatedSmartReplies);
Gustav Senntond25a64d2018-12-07 10:58:39 +00001381 if (mExpandedSmartReplyView != null) {
1382 if (smartRepliesAndActions.smartReplies != null
1383 || smartRepliesAndActions.smartActions != null) {
1384 int numSmartReplies = smartRepliesAndActions.smartReplies == null
1385 ? 0 : smartRepliesAndActions.smartReplies.choices.length;
1386 int numSmartActions = smartRepliesAndActions.smartActions == null
1387 ? 0 : smartRepliesAndActions.smartActions.actions.size();
1388 boolean fromAssistant = smartRepliesAndActions.smartReplies == null
1389 ? smartRepliesAndActions.smartActions.fromAssistant
1390 : smartRepliesAndActions.smartReplies.fromAssistant;
Milo Sredkov13d88112019-02-01 12:23:24 +00001391 boolean editBeforeSending = smartRepliesAndActions.smartReplies != null
1392 && mSmartReplyConstants.getEffectiveEditChoicesBeforeSending(
1393 smartRepliesAndActions.smartReplies.remoteInput
1394 .getEditChoicesBeforeSending());
1395
Gustav Senntond25a64d2018-12-07 10:58:39 +00001396 mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
Milo Sredkov13d88112019-02-01 12:23:24 +00001397 numSmartActions, fromAssistant, editBeforeSending);
Gustav Senntond25a64d2018-12-07 10:58:39 +00001398 }
Kenny Guy23991102018-04-05 21:18:38 +01001399 }
1400 }
Gustav Sennton3f3eaff2019-01-08 09:39:51 +00001401 if (mHeadsUpChild != null && mSmartReplyConstants.getShowInHeadsUp()) {
Gustav Sennton5759f872019-02-13 17:25:26 +00001402 mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, smartRepliesAndActions,
1403 entry, mHeadsUpInflatedSmartReplies);
Gustav Senntond0e84532018-12-03 16:48:36 +00001404 }
Petr Cermak7997d7a2018-01-18 15:52:10 +00001405 }
1406
Gustav Sennton5759f872019-02-13 17:25:26 +00001407 @Nullable
Gustav Senntoneab53682018-11-01 16:30:23 +00001408 private SmartReplyView applySmartReplyView(View view,
Gustav Sennton5759f872019-02-13 17:25:26 +00001409 SmartRepliesAndActions smartRepliesAndActions,
1410 NotificationEntry entry, InflatedSmartReplies inflatedSmartReplyView) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001411 View smartReplyContainerCandidate = view.findViewById(
1412 com.android.internal.R.id.smart_reply_container);
1413 if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
1414 return null;
1415 }
Gustav Sennton5759f872019-02-13 17:25:26 +00001416
Petr Cermak7997d7a2018-01-18 15:52:10 +00001417 LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
Gustav Senntonb944ce52019-02-25 18:52:43 +00001418 if (!InflatedSmartReplies.shouldShowSmartReplyView(entry, smartRepliesAndActions)) {
Petr Cermak7997d7a2018-01-18 15:52:10 +00001419 smartReplyContainer.setVisibility(View.GONE);
1420 return null;
1421 }
Gustav Senntonb944ce52019-02-25 18:52:43 +00001422
Petr Cermak7997d7a2018-01-18 15:52:10 +00001423 SmartReplyView smartReplyView = null;
Gustav Sennton5759f872019-02-13 17:25:26 +00001424 if (smartReplyContainer.getChildCount() == 1
1425 && smartReplyContainer.getChildAt(0) instanceof SmartReplyView) {
1426 // If we already have a SmartReplyView - replace it with the newly inflated one. The
1427 // newly inflated one is connected to the new inflated smart reply/action buttons.
1428 smartReplyContainer.removeAllViews();
1429 }
1430 if (smartReplyContainer.getChildCount() == 0
1431 && inflatedSmartReplyView != null
1432 && inflatedSmartReplyView.getSmartReplyView() != null) {
1433 smartReplyView = inflatedSmartReplyView.getSmartReplyView();
Petr Cermak7997d7a2018-01-18 15:52:10 +00001434 smartReplyContainer.addView(smartReplyView);
Petr Cermak7997d7a2018-01-18 15:52:10 +00001435 }
1436 if (smartReplyView != null) {
Gustav Senntoneab53682018-11-01 16:30:23 +00001437 smartReplyView.resetSmartSuggestions(smartReplyContainer);
Gustav Sennton5759f872019-02-13 17:25:26 +00001438 smartReplyView.addPreInflatedButtons(
1439 inflatedSmartReplyView.getSmartSuggestionButtons());
Petr Cermak7997d7a2018-01-18 15:52:10 +00001440 smartReplyContainer.setVisibility(View.VISIBLE);
1441 }
1442 return smartReplyView;
1443 }
1444
Gustav Sennton5759f872019-02-13 17:25:26 +00001445 /**
1446 * Set pre-inflated views necessary to display smart replies and actions in the expanded
1447 * notification state.
1448 *
1449 * @param inflatedSmartReplies the pre-inflated state to add to this view. If null the existing
1450 * {@link SmartReplyView} related to the expanded notification state is cleared.
1451 */
1452 public void setExpandedInflatedSmartReplies(
1453 @Nullable InflatedSmartReplies inflatedSmartReplies) {
1454 mExpandedInflatedSmartReplies = inflatedSmartReplies;
1455 if (inflatedSmartReplies == null) {
1456 mExpandedSmartReplyView = null;
1457 }
1458 }
1459
1460 /**
1461 * Set pre-inflated views necessary to display smart replies and actions in the heads-up
1462 * notification state.
1463 *
1464 * @param inflatedSmartReplies the pre-inflated state to add to this view. If null the existing
1465 * {@link SmartReplyView} related to the heads-up notification state is cleared.
1466 */
1467 public void setHeadsUpInflatedSmartReplies(
1468 @Nullable InflatedSmartReplies inflatedSmartReplies) {
1469 mHeadsUpInflatedSmartReplies = inflatedSmartReplies;
1470 if (inflatedSmartReplies == null) {
1471 mHeadsUpSmartReplyView = null;
1472 }
1473 }
1474
Adrian Roos0bd8a4b2016-03-14 16:21:44 -07001475 public void closeRemoteInput() {
1476 if (mHeadsUpRemoteInput != null) {
1477 mHeadsUpRemoteInput.close();
1478 }
1479 if (mExpandedRemoteInput != null) {
1480 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -08001481 }
1482 }
1483
Selim Cinek83bc7832015-10-22 13:26:54 -07001484 public void setGroupManager(NotificationGroupManager groupManager) {
1485 mGroupManager = groupManager;
1486 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001487
Adrian Roosb88b1a12015-12-09 18:51:05 -08001488 public void setRemoteInputController(RemoteInputController r) {
1489 mRemoteInputController = r;
1490 }
1491
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001492 public void setExpandClickListener(OnClickListener expandClickListener) {
1493 mExpandClickListener = expandClickListener;
1494 }
1495
1496 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -08001497 mExpandable = expandable;
1498 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001499 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
Selim Cinek4ca6c632017-02-23 18:03:37 -08001500 if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
1501 || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
Selim Cinek9635cf32017-03-03 16:59:03 -08001502 if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001503 expandable = false;
1504 }
Selim Cinek9635cf32017-03-03 16:59:03 -08001505 } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
Selim Cinek8ea2d5f2016-04-20 17:10:40 -07001506 expandable = false;
1507 }
Selim Cinek98be3f32015-12-17 16:39:06 -08001508 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001509 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001510 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001511 }
1512 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001513 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001514 }
1515 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -08001516 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001517 }
Selim Cinekc0ac4af2017-03-03 15:13:48 -08001518 mIsContentExpandable = expandable;
Selim Cinek65b2e7c2015-10-26 14:11:31 -07001519 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001520
1521 public NotificationHeaderView getNotificationHeader() {
1522 NotificationHeaderView header = null;
1523 if (mContractedChild != null) {
1524 header = mContractedWrapper.getNotificationHeader();
1525 }
1526 if (header == null && mExpandedChild != null) {
1527 header = mExpandedWrapper.getNotificationHeader();
1528 }
1529 if (header == null && mHeadsUpChild != null) {
1530 header = mHeadsUpWrapper.getNotificationHeader();
1531 }
Adrian Roos0aac04f2016-12-08 15:59:29 -08001532 if (header == null && mAmbientChild != null) {
1533 header = mAmbientWrapper.getNotificationHeader();
1534 }
Selim Cinekea4bef72015-12-02 15:51:10 -08001535 return header;
1536 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001537
Julia Reynoldsfc640012018-02-21 12:25:27 -05001538 public void showAppOpsIcons(ArraySet<Integer> activeOps) {
1539 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
1540 mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1541 }
1542 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
1543 mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1544 }
1545 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
1546 mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps);
1547 }
1548 }
Selim Cinekb26afa32017-06-29 10:28:17 +02001549
Gus Prevasa3226492018-10-23 11:10:09 -04001550 /** Sets whether the notification being displayed audibly alerted the user. */
Gus Prevas7306b902018-12-11 10:57:06 -05001551 public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
Gus Prevasa3226492018-10-23 11:10:09 -04001552 if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001553 mContractedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001554 }
1555 if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001556 mExpandedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001557 }
1558 if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) {
Gus Prevas7306b902018-12-11 10:57:06 -05001559 mHeadsUpWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted);
Gus Prevasa3226492018-10-23 11:10:09 -04001560 }
1561 }
1562
Selim Cinekb26afa32017-06-29 10:28:17 +02001563 public NotificationHeaderView getContractedNotificationHeader() {
1564 if (mContractedChild != null) {
1565 return mContractedWrapper.getNotificationHeader();
1566 }
1567 return null;
1568 }
1569
Selim Cinek34eda5e2016-02-18 17:10:43 -08001570 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -08001571 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -08001572 return wrapper == null ? null : wrapper.getNotificationHeader();
1573 }
1574
Selim Cinekfa0a2d32016-01-14 13:02:21 -08001575 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
1576 mContainingNotification = containingNotification;
1577 }
1578
1579 public void requestSelectLayout(boolean needsAnimation) {
1580 selectLayout(needsAnimation, false);
1581 }
Selim Cinekde33a4a2016-02-11 16:43:41 -08001582
1583 public void reInflateViews() {
1584 if (mIsChildInGroup && mSingleLineView != null) {
1585 removeView(mSingleLineView);
1586 mSingleLineView = null;
Adrian Roos6f6e1592017-05-02 16:22:53 -07001587 updateAllSingleLineViews();
Selim Cinekde33a4a2016-02-11 16:43:41 -08001588 }
1589 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001590
1591 public void setUserExpanding(boolean userExpanding) {
1592 mUserExpanding = userExpanding;
1593 if (userExpanding) {
1594 mTransformationStartVisibleType = mVisibleType;
1595 } else {
1596 mTransformationStartVisibleType = UNDEFINED;
1597 mVisibleType = calculateVisibleType();
1598 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -08001599 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -08001600 }
1601 }
Selim Cinekc897bd32016-03-18 17:32:31 -07001602
1603 /**
1604 * Set by how much the single line view should be indented. Used when a overflow indicator is
1605 * present and only during measuring
1606 */
1607 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
1608 if (singleLineWidthIndention != mSingleLineWidthIndention) {
1609 mSingleLineWidthIndention = singleLineWidthIndention;
1610 mContainingNotification.forceLayout();
1611 forceLayout();
1612 }
1613 }
1614
1615 public HybridNotificationView getSingleLineView() {
1616 return mSingleLineView;
1617 }
Adrian Roosd009ab12016-05-20 17:58:53 -07001618
1619 public void setRemoved() {
1620 if (mExpandedRemoteInput != null) {
1621 mExpandedRemoteInput.setRemoved();
1622 }
1623 if (mHeadsUpRemoteInput != null) {
1624 mHeadsUpRemoteInput.setRemoved();
1625 }
1626 }
Adrian Roos599be342016-06-13 14:54:39 -07001627
1628 public void setContentHeightAnimating(boolean animating) {
1629 if (!animating) {
1630 mContentHeightAtAnimationStart = UNDEFINED;
1631 }
1632 }
Selim Cineke9bad242016-06-15 11:46:37 -07001633
Adrian Roose18033c2017-01-17 15:22:49 -08001634 @VisibleForTesting
1635 boolean isAnimatingVisibleType() {
1636 return mAnimationStartVisibleType != UNDEFINED;
1637 }
1638
Selim Cinekcafa87f2016-10-26 17:00:17 -07001639 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
1640 mHeadsUpAnimatingAway = headsUpAnimatingAway;
Selim Cinek73cf02a2016-06-17 13:08:00 -07001641 selectLayout(false /* animate */, true /* force */);
1642 }
1643
Selim Cineke9bad242016-06-15 11:46:37 -07001644 public void setFocusOnVisibilityChange() {
1645 mFocusOnVisibilityChange = true;
1646 }
Selim Cinek0242fbb2016-10-19 13:38:32 -07001647
1648 public void setIconsVisible(boolean iconsVisible) {
1649 mIconsVisible = iconsVisible;
1650 updateIconVisibilities();
1651 }
1652
1653 private void updateIconVisibilities() {
1654 if (mContractedWrapper != null) {
1655 NotificationHeaderView header = mContractedWrapper.getNotificationHeader();
1656 if (header != null) {
1657 header.getIcon().setForceHidden(!mIconsVisible);
1658 }
1659 }
1660 if (mHeadsUpWrapper != null) {
1661 NotificationHeaderView header = mHeadsUpWrapper.getNotificationHeader();
1662 if (header != null) {
1663 header.getIcon().setForceHidden(!mIconsVisible);
1664 }
1665 }
1666 if (mExpandedWrapper != null) {
1667 NotificationHeaderView header = mExpandedWrapper.getNotificationHeader();
1668 if (header != null) {
1669 header.getIcon().setForceHidden(!mIconsVisible);
1670 }
1671 }
1672 }
Selim Cinek6743c0b2017-01-18 18:24:01 -08001673
Adrian Roos4320e892017-01-24 12:50:34 -08001674 @Override
1675 public void onVisibilityAggregated(boolean isVisible) {
1676 super.onVisibilityAggregated(isVisible);
1677 if (isVisible) {
1678 fireExpandedVisibleListenerIfVisible();
1679 }
1680 }
1681
1682 /**
1683 * Sets a one-shot listener for when the expanded view becomes visible.
1684 *
1685 * This will fire the listener immediately if the expanded view is already visible.
1686 */
1687 public void setOnExpandedVisibleListener(Runnable r) {
1688 mExpandedVisibleListener = r;
1689 fireExpandedVisibleListenerIfVisible();
1690 }
1691
Kevind5022f92018-10-08 18:30:26 -07001692 /**
1693 * Set a one-shot listener to run when a given content view becomes inactive.
1694 *
1695 * @param visibleType visible type corresponding to the content view to listen
1696 * @param listener runnable to run once when the content view becomes inactive
1697 */
1698 public void performWhenContentInactive(int visibleType, Runnable listener) {
1699 View view = getViewForVisibleType(visibleType);
1700 // View is already inactive
1701 if (view == null || isContentViewInactive(visibleType)) {
1702 listener.run();
1703 return;
1704 }
1705 mOnContentViewInactiveListeners.put(view, listener);
1706 }
1707
1708 /**
1709 * Whether or not the content view is inactive. This means it should not be visible
1710 * or the showing content as removing it would cause visual jank.
1711 *
1712 * @param visibleType visible type corresponding to the content view to be removed
1713 * @return true if the content view is inactive, false otherwise
1714 */
1715 public boolean isContentViewInactive(int visibleType) {
1716 View view = getViewForVisibleType(visibleType);
1717 return isContentViewInactive(view);
1718 }
1719
1720 /**
1721 * Whether or not the content view is inactive.
1722 *
1723 * @param view view to see if its inactive
1724 * @return true if the view is inactive, false o/w
1725 */
1726 private boolean isContentViewInactive(View view) {
1727 if (view == null) {
1728 return true;
1729 }
Kevin38ce6fa2018-10-17 16:00:14 -07001730 return !isShown()
1731 || (view.getVisibility() != VISIBLE && getViewForVisibleType(mVisibleType) != view);
Kevind5022f92018-10-08 18:30:26 -07001732 }
1733
1734 @Override
1735 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
1736 super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
1737 if (isContentViewInactive(child)) {
1738 Runnable listener = mOnContentViewInactiveListeners.remove(child);
1739 if (listener != null) {
1740 listener.run();
1741 }
1742 }
1743 }
1744
Selim Cinek6743c0b2017-01-18 18:24:01 -08001745 public void setIsLowPriority(boolean isLowPriority) {
1746 mIsLowPriority = isLowPriority;
1747 }
Selim Cinek63edaf22017-04-24 22:18:48 -07001748
1749 public boolean isDimmable() {
Kevin38ce6fa2018-10-17 16:00:14 -07001750 return mContractedWrapper != null && mContractedWrapper.isDimmable();
Selim Cinek63edaf22017-04-24 22:18:48 -07001751 }
Selim Cinek5d6ef8d2017-05-18 22:16:00 -07001752
1753 /**
1754 * Should a single click be disallowed on this view when on the keyguard?
1755 */
1756 public boolean disallowSingleClick(float x, float y) {
1757 NotificationViewWrapper visibleWrapper = getVisibleWrapper(getVisibleType());
1758 if (visibleWrapper != null) {
1759 return visibleWrapper.disallowSingleClick(x, y);
1760 }
1761 return false;
1762 }
Selim Cinek515b2032017-11-15 10:20:19 -08001763
Selim Cinek86bfcee2018-01-17 11:00:47 -08001764 public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
1765 boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001766 if (mUserExpanding) {
Selim Cinek86bfcee2018-01-17 11:00:47 -08001767 needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
1768 bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001769 }
1770 return needsPaddings;
1771 }
1772
Selim Cinek86bfcee2018-01-17 11:00:47 -08001773 private boolean shouldClipToRounding(int visibleType, boolean topRounded,
1774 boolean bottomRounded) {
Selim Cinek515b2032017-11-15 10:20:19 -08001775 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
1776 if (visibleWrapper == null) {
1777 return false;
1778 }
Selim Cinek86bfcee2018-01-17 11:00:47 -08001779 return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded);
Selim Cinek515b2032017-11-15 10:20:19 -08001780 }
Selim Cinekde4de0e2018-01-24 16:21:07 -08001781
1782 public CharSequence getActiveRemoteInputText() {
1783 if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
1784 return mExpandedRemoteInput.getText();
1785 }
1786 if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
1787 return mHeadsUpRemoteInput.getText();
1788 }
1789 return null;
1790 }
Selim Cineked64a142018-02-06 18:06:01 -08001791
Selim Cineka77d2c52018-04-09 10:44:52 -07001792 @Override
1793 public boolean dispatchTouchEvent(MotionEvent ev) {
1794 float y = ev.getY();
1795 // We still want to distribute touch events to the remote input even if it's outside the
1796 // view boundary. We're therefore manually dispatching these events to the remote view
1797 RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
1798 if (riv != null && riv.getVisibility() == VISIBLE) {
1799 int inputStart = mUnrestrictedContentHeight - riv.getHeight();
1800 if (y <= mUnrestrictedContentHeight && y >= inputStart) {
1801 ev.offsetLocation(0, -inputStart);
1802 return riv.dispatchTouchEvent(ev);
1803 }
1804 }
1805 return super.dispatchTouchEvent(ev);
1806 }
1807
1808 /**
1809 * Overridden to make sure touches to the reply action bar actually go through to this view
1810 */
1811 @Override
1812 public boolean pointInView(float localX, float localY, float slop) {
1813 float top = mClipTopAmount;
1814 float bottom = mUnrestrictedContentHeight;
1815 return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
1816 localY < (bottom + slop);
1817 }
1818
1819 private RemoteInputView getRemoteInputForView(View child) {
1820 if (child == mExpandedChild) {
1821 return mExpandedRemoteInput;
1822 } else if (child == mHeadsUpChild) {
1823 return mHeadsUpRemoteInput;
1824 }
1825 return null;
1826 }
1827
Selim Cineked64a142018-02-06 18:06:01 -08001828 public int getExpandHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001829 int viewType = VISIBLE_TYPE_EXPANDED;
1830 if (mExpandedChild == null) {
1831 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001832 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001833 return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001834 }
1835
1836 public int getHeadsUpHeight() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001837 int viewType = VISIBLE_TYPE_HEADSUP;
1838 if (mHeadsUpChild == null) {
1839 viewType = VISIBLE_TYPE_CONTRACTED;
Selim Cineked64a142018-02-06 18:06:01 -08001840 }
Selim Cinek09b442e2018-05-04 15:46:18 -07001841 // The headsUp remote input quickly switches to the expanded one, so lets also include that
1842 // one
1843 return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput)
1844 + getExtraRemoteInputHeight(mExpandedRemoteInput);
Selim Cineked64a142018-02-06 18:06:01 -08001845 }
1846
1847 public void setRemoteInputVisible(boolean remoteInputVisible) {
1848 mRemoteInputVisible = remoteInputVisible;
1849 setClipChildren(!remoteInputVisible);
1850 }
1851
1852 @Override
1853 public void setClipChildren(boolean clipChildren) {
1854 clipChildren = clipChildren && !mRemoteInputVisible;
1855 super.setClipChildren(clipChildren);
1856 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001857
1858 public void setHeaderVisibleAmount(float headerVisibleAmount) {
1859 if (mContractedWrapper != null) {
1860 mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1861 }
1862 if (mHeadsUpWrapper != null) {
1863 mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1864 }
1865 if (mExpandedWrapper != null) {
1866 mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
1867 }
1868 }
Selim Cinek85a8f9f2018-11-21 13:58:27 -08001869
1870 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1871 pw.print(" ");
1872 pw.print("contentView visibility: " + getVisibility());
1873 pw.print(", alpha: " + getAlpha());
1874 pw.print(", clipBounds: " + getClipBounds());
1875 pw.print(", contentHeight: " + mContentHeight);
1876 pw.print(", visibleType: " + mVisibleType);
1877 View view = getViewForVisibleType(mVisibleType);
1878 pw.print(", visibleView ");
1879 if (view != null) {
1880 pw.print(" visibility: " + view.getVisibility());
1881 pw.print(", alpha: " + view.getAlpha());
1882 pw.print(", clipBounds: " + view.getClipBounds());
1883 } else {
1884 pw.print("null");
1885 }
1886 pw.println();
1887 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001888}