blob: 334d3fa02a07804e1cb5e285f7b0ddb0748b71d1 [file] [log] [blame]
Jorim Jaggibe565df2014-04-28 17:51:23 +02001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar;
18
Adrian Roosb88b1a12015-12-09 18:51:05 -080019import android.app.Notification;
Adrian Roos4c1fcc82016-03-31 14:39:39 -070020import android.app.PendingIntent;
Adrian Roosb88b1a12015-12-09 18:51:05 -080021import android.app.RemoteInput;
Jorim Jaggibe565df2014-04-28 17:51:23 +020022import android.content.Context;
23import android.graphics.Rect;
Selim Cinek860b6da2015-12-16 19:02:19 -080024import android.os.Build;
Selim Cinek83bc7832015-10-22 13:26:54 -070025import android.service.notification.StatusBarNotification;
Jorim Jaggibe565df2014-04-28 17:51:23 +020026import android.util.AttributeSet;
Selim Cinekeaa29ca2015-11-23 13:51:13 -080027import android.view.NotificationHeaderView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020028import android.view.View;
Selim Cinek8d490d42015-04-10 00:05:50 -070029import android.view.ViewGroup;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010030import android.view.ViewTreeObserver;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020031import android.widget.FrameLayout;
Selim Cinek8d490d42015-04-10 00:05:50 -070032
Adrian Roos4ff3b122016-02-01 12:26:13 -080033import com.android.internal.util.NotificationColorUtil;
Jorim Jaggibe565df2014-04-28 17:51:23 +020034import com.android.systemui.R;
Selim Cinek83bc7832015-10-22 13:26:54 -070035import com.android.systemui.statusbar.notification.HybridNotificationView;
Selim Cinekc897bd32016-03-18 17:32:31 -070036import com.android.systemui.statusbar.notification.HybridGroupManager;
Selim Cinek0ffbda62016-01-01 20:29:12 +010037import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
Selim Cinekc3179332016-03-04 14:44:56 -080038import com.android.systemui.statusbar.notification.NotificationUtils;
Selim Cinek0ffbda62016-01-01 20:29:12 +010039import com.android.systemui.statusbar.notification.NotificationViewWrapper;
Selim Cinek83bc7832015-10-22 13:26:54 -070040import com.android.systemui.statusbar.phone.NotificationGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080041import com.android.systemui.statusbar.policy.RemoteInputView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020042
43/**
Selim Cinek684a4422015-04-15 16:18:39 -070044 * A frame layout containing the actual payload of the notification, including the contracted,
45 * expanded and heads up layout. This class is responsible for clipping the content and and
46 * switching between the expanded, contracted and the heads up view depending on its clipped size.
Jorim Jaggibe565df2014-04-28 17:51:23 +020047 */
Selim Cinekc9c00ae2014-05-20 03:33:40 +020048public class NotificationContentView extends FrameLayout {
Jorim Jaggibe565df2014-04-28 17:51:23 +020049
Selim Cinek684a4422015-04-15 16:18:39 -070050 private static final int VISIBLE_TYPE_CONTRACTED = 0;
51 private static final int VISIBLE_TYPE_EXPANDED = 1;
52 private static final int VISIBLE_TYPE_HEADSUP = 2;
Selim Cinek83bc7832015-10-22 13:26:54 -070053 private static final int VISIBLE_TYPE_SINGLELINE = 3;
Selim Cinekc3179332016-03-04 14:44:56 -080054 public static final int UNDEFINED = -1;
Jorim Jaggi11298832014-05-24 16:18:38 +020055
Jorim Jaggibe565df2014-04-28 17:51:23 +020056 private final Rect mClipBounds = new Rect();
Selim Cinek860b6da2015-12-16 19:02:19 -080057 private final int mMinContractedHeight;
Selim Cinek6ecc8102016-01-26 18:26:19 -080058 private final int mNotificationContentMarginEnd;
Jorim Jaggibe565df2014-04-28 17:51:23 +020059
60 private View mContractedChild;
61 private View mExpandedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -070062 private View mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -070063 private HybridNotificationView mSingleLineView;
Jorim Jaggibe565df2014-04-28 17:51:23 +020064
Adrian Roos0bd8a4b2016-03-14 16:21:44 -070065 private RemoteInputView mExpandedRemoteInput;
66 private RemoteInputView mHeadsUpRemoteInput;
67
Jorim Jaggi4e857f42014-11-17 19:14:04 +010068 private NotificationViewWrapper mContractedWrapper;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070069 private NotificationViewWrapper mExpandedWrapper;
70 private NotificationViewWrapper mHeadsUpWrapper;
Selim Cinekc897bd32016-03-18 17:32:31 -070071 private HybridGroupManager mHybridGroupManager;
Selim Cinekc9c00ae2014-05-20 03:33:40 +020072 private int mClipTopAmount;
Selim Cinekb5605e52015-02-20 18:21:41 +010073 private int mContentHeight;
Jorim Jaggibe4116a2015-05-20 20:04:08 -070074 private int mUnrestrictedContentHeight;
Selim Cinek684a4422015-04-15 16:18:39 -070075 private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
John Spurlocke15452b2014-08-21 09:44:39 -040076 private boolean mDark;
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010077 private boolean mAnimate;
Selim Cinek684a4422015-04-15 16:18:39 -070078 private boolean mIsHeadsUp;
Jorim Jaggi59ec3042015-06-05 15:18:43 -070079 private boolean mShowingLegacyBackground;
Selim Cinek83bc7832015-10-22 13:26:54 -070080 private boolean mIsChildInGroup;
Selim Cinek816c8e42015-11-19 12:00:45 -080081 private int mSmallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -080082 private int mHeadsUpHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -080083 private int mNotificationMaxHeight;
Selim Cinek83bc7832015-10-22 13:26:54 -070084 private StatusBarNotification mStatusBarNotification;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070085 private NotificationGroupManager mGroupManager;
Adrian Roosb88b1a12015-12-09 18:51:05 -080086 private RemoteInputController mRemoteInputController;
Jorim Jaggi59ec3042015-06-05 15:18:43 -070087
Jorim Jaggibe4116a2015-05-20 20:04:08 -070088 private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
Selim Cinekbb3d1cf2014-10-31 00:12:56 +010089 = new ViewTreeObserver.OnPreDrawListener() {
90 @Override
91 public boolean onPreDraw() {
Selim Cinekd1ad9ab2016-03-01 17:52:20 -080092 // We need to post since we don't want the notification to animate on the very first
93 // frame
94 post(new Runnable() {
95 @Override
96 public void run() {
97 mAnimate = true;
98 }
99 });
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100100 getViewTreeObserver().removeOnPreDrawListener(this);
101 return true;
102 }
103 };
Jorim Jaggi11298832014-05-24 16:18:38 +0200104
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800105 private OnClickListener mExpandClickListener;
Selim Cinek860b6da2015-12-16 19:02:19 -0800106 private boolean mBeforeN;
Selim Cinek98be3f32015-12-17 16:39:06 -0800107 private boolean mExpandable;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100108 private boolean mClipToActualHeight = true;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800109 private ExpandableNotificationRow mContainingNotification;
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800110 private int mTransformationStartVisibleType;
111 private boolean mUserExpanding;
Selim Cinekc897bd32016-03-18 17:32:31 -0700112 private int mSingleLineWidthIndention;
Selim Cinek589fd3e2016-04-26 18:17:57 -0700113 private boolean mForceSelectNextLayout = true;
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700114 private PendingIntent mPreviousExpandedRemoteInputIntent;
115 private PendingIntent mPreviousHeadsUpRemoteInputIntent;
Selim Cinek98be3f32015-12-17 16:39:06 -0800116
Jorim Jaggibe565df2014-04-28 17:51:23 +0200117 public NotificationContentView(Context context, AttributeSet attrs) {
118 super(context, attrs);
Selim Cinekc897bd32016-03-18 17:32:31 -0700119 mHybridGroupManager = new HybridGroupManager(getContext(), this);
Selim Cinek860b6da2015-12-16 19:02:19 -0800120 mMinContractedHeight = getResources().getDimensionPixelSize(
121 R.dimen.min_notification_layout_height);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800122 mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
123 com.android.internal.R.dimen.notification_content_margin_end);
Selim Cinek51d94912016-03-02 15:34:28 -0800124 reset();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200125 }
126
Selim Cinekd84a5932015-12-15 11:45:36 -0800127 public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
Selim Cinek816c8e42015-11-19 12:00:45 -0800128 mSmallHeight = smallHeight;
Selim Cinek77019c72015-12-09 10:18:02 -0800129 mHeadsUpHeight = headsUpMaxHeight;
Selim Cinekd84a5932015-12-15 11:45:36 -0800130 mNotificationMaxHeight = maxHeight;
Selim Cinek816c8e42015-11-19 12:00:45 -0800131 }
132
Jorim Jaggibe565df2014-04-28 17:51:23 +0200133 @Override
Selim Cinek8d490d42015-04-10 00:05:50 -0700134 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
135 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
136 boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
137 boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
138 int maxSize = Integer.MAX_VALUE;
Selim Cinekc897bd32016-03-18 17:32:31 -0700139 int width = MeasureSpec.getSize(widthMeasureSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700140 if (hasFixedHeight || isHeightLimited) {
141 maxSize = MeasureSpec.getSize(heightMeasureSpec);
142 }
143 int maxChildHeight = 0;
Selim Cinek6ecc8102016-01-26 18:26:19 -0800144 if (mExpandedChild != null) {
145 int size = Math.min(maxSize, mNotificationMaxHeight);
146 ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
147 if (layoutParams.height >= 0) {
148 // An actual height is set
149 size = Math.min(maxSize, layoutParams.height);
150 }
151 int spec = size == Integer.MAX_VALUE
152 ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
153 : MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
154 mExpandedChild.measure(widthMeasureSpec, spec);
155 maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
156 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700157 if (mContractedChild != null) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800158 int heightSpec;
Selim Cinekf619ffc2016-02-17 14:53:05 -0800159 int size = Math.min(maxSize, mSmallHeight);
Selim Cinek860b6da2015-12-16 19:02:19 -0800160 if (shouldContractedBeFixedSize()) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800161 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
162 } else {
Selim Cinekf619ffc2016-02-17 14:53:05 -0800163 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
Selim Cinek860b6da2015-12-16 19:02:19 -0800164 }
165 mContractedChild.measure(widthMeasureSpec, heightSpec);
166 int measuredHeight = mContractedChild.getMeasuredHeight();
167 if (measuredHeight < mMinContractedHeight) {
168 heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
169 mContractedChild.measure(widthMeasureSpec, heightSpec);
170 }
171 maxChildHeight = Math.max(maxChildHeight, measuredHeight);
Selim Cinek6ecc8102016-01-26 18:26:19 -0800172 if (updateContractedHeaderWidth()) {
173 mContractedChild.measure(widthMeasureSpec, heightSpec);
Selim Cinek8d490d42015-04-10 00:05:50 -0700174 }
Selim Cinek1ba41d12016-04-21 16:14:46 -0700175 if (mExpandedChild != null
176 && mContractedChild.getMeasuredHeight() > mExpandedChild.getMeasuredHeight()) {
177 // the Expanded child is smaller then the collapsed. Let's remeasure it.
178 heightSpec = MeasureSpec.makeMeasureSpec(mContractedChild.getMeasuredHeight(),
179 MeasureSpec.EXACTLY);
180 mExpandedChild.measure(widthMeasureSpec, heightSpec);
181 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700182 }
183 if (mHeadsUpChild != null) {
184 int size = Math.min(maxSize, mHeadsUpHeight);
185 ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
186 if (layoutParams.height >= 0) {
187 // An actual height is set
Selim Cinek77019c72015-12-09 10:18:02 -0800188 size = Math.min(size, layoutParams.height);
Selim Cinek8d490d42015-04-10 00:05:50 -0700189 }
190 mHeadsUpChild.measure(widthMeasureSpec,
191 MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
192 maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
193 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700194 if (mSingleLineView != null) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700195 int singleLineWidthSpec = widthMeasureSpec;
196 if (mSingleLineWidthIndention != 0
197 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
198 singleLineWidthSpec = MeasureSpec.makeMeasureSpec(
199 width - mSingleLineWidthIndention + mSingleLineView.getPaddingEnd(),
200 MeasureSpec.AT_MOST);
201 }
202 mSingleLineView.measure(singleLineWidthSpec,
Selim Cinek7b836392015-12-04 20:02:59 -0800203 MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
Selim Cinek83bc7832015-10-22 13:26:54 -0700204 maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
205 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700206 int ownHeight = Math.min(maxChildHeight, maxSize);
Selim Cinek8d490d42015-04-10 00:05:50 -0700207 setMeasuredDimension(width, ownHeight);
208 }
209
Selim Cinek6ecc8102016-01-26 18:26:19 -0800210 private boolean updateContractedHeaderWidth() {
211 // We need to update the expanded and the collapsed header to have exactly the same with to
212 // have the expand buttons laid out at the same location.
213 NotificationHeaderView contractedHeader = mContractedWrapper.getNotificationHeader();
214 if (contractedHeader != null) {
215 if (mExpandedChild != null
216 && mExpandedWrapper.getNotificationHeader() != null) {
217 NotificationHeaderView expandedHeader = mExpandedWrapper.getNotificationHeader();
218 int expandedSize = expandedHeader.getMeasuredWidth()
219 - expandedHeader.getPaddingEnd();
220 int collapsedSize = contractedHeader.getMeasuredWidth()
221 - expandedHeader.getPaddingEnd();
222 if (expandedSize != collapsedSize) {
223 int paddingEnd = contractedHeader.getMeasuredWidth() - expandedSize;
224 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800225 contractedHeader.isLayoutRtl()
226 ? paddingEnd
227 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800228 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800229 contractedHeader.isLayoutRtl()
230 ? contractedHeader.getPaddingLeft()
231 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800232 contractedHeader.getPaddingBottom());
233 contractedHeader.setShowWorkBadgeAtEnd(true);
234 return true;
235 }
236 } else {
237 int paddingEnd = mNotificationContentMarginEnd;
238 if (contractedHeader.getPaddingEnd() != paddingEnd) {
239 contractedHeader.setPadding(
Selim Cinekcb445682016-01-29 16:13:12 -0800240 contractedHeader.isLayoutRtl()
241 ? paddingEnd
242 : contractedHeader.getPaddingLeft(),
Selim Cinek6ecc8102016-01-26 18:26:19 -0800243 contractedHeader.getPaddingTop(),
Selim Cinekcb445682016-01-29 16:13:12 -0800244 contractedHeader.isLayoutRtl()
245 ? contractedHeader.getPaddingLeft()
246 : paddingEnd,
Selim Cinek6ecc8102016-01-26 18:26:19 -0800247 contractedHeader.getPaddingBottom());
248 contractedHeader.setShowWorkBadgeAtEnd(false);
249 return true;
250 }
251 }
252 }
253 return false;
254 }
255
Selim Cinek860b6da2015-12-16 19:02:19 -0800256 private boolean shouldContractedBeFixedSize() {
257 return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
258 }
259
Selim Cinek8d490d42015-04-10 00:05:50 -0700260 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200261 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
262 super.onLayout(changed, left, top, right, bottom);
263 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700264 invalidateOutline();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700265 selectLayout(false /* animate */, mForceSelectNextLayout /* force */);
266 mForceSelectNextLayout = false;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200267 }
268
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100269 @Override
Selim Cinek7b8157e2014-11-20 16:00:32 +0100270 protected void onAttachedToWindow() {
271 super.onAttachedToWindow();
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100272 updateVisibility();
273 }
274
Selim Cinek51d94912016-03-02 15:34:28 -0800275 public void reset() {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200276 if (mContractedChild != null) {
277 mContractedChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800278 removeView(mContractedChild);
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200279 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700280 mPreviousExpandedRemoteInputIntent =
281 mExpandedRemoteInput != null ? mExpandedRemoteInput.getPendingIntent() : null;
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200282 if (mExpandedChild != null) {
283 mExpandedChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800284 removeView(mExpandedChild);
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700285 mExpandedRemoteInput = null;
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200286 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700287 mPreviousHeadsUpRemoteInputIntent =
288 mHeadsUpRemoteInput != null ? mHeadsUpRemoteInput.getPendingIntent() : null;
Selim Cinek8d490d42015-04-10 00:05:50 -0700289 if (mHeadsUpChild != null) {
290 mHeadsUpChild.animate().cancel();
Selim Cinekde33a4a2016-02-11 16:43:41 -0800291 removeView(mHeadsUpChild);
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700292 mHeadsUpRemoteInput = null;
Selim Cinek8d490d42015-04-10 00:05:50 -0700293 }
Christoph Studera7fe6312014-06-27 19:32:44 +0200294 mContractedChild = null;
295 mExpandedChild = null;
Selim Cinek1f3f5442015-04-10 17:54:46 -0700296 mHeadsUpChild = null;
Christoph Studera7fe6312014-06-27 19:32:44 +0200297 }
298
Selim Cinekcab4a602014-09-03 14:47:57 +0200299 public View getContractedChild() {
300 return mContractedChild;
301 }
302
303 public View getExpandedChild() {
304 return mExpandedChild;
305 }
306
Selim Cinek8d490d42015-04-10 00:05:50 -0700307 public View getHeadsUpChild() {
308 return mHeadsUpChild;
309 }
310
Jorim Jaggibe565df2014-04-28 17:51:23 +0200311 public void setContractedChild(View child) {
312 if (mContractedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200313 mContractedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200314 removeView(mContractedChild);
315 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200316 addView(child);
317 mContractedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700318 mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
319 mContainingNotification);
Jorim Jaggi10ad7612014-12-08 18:41:11 +0100320 mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200321 }
322
323 public void setExpandedChild(View child) {
324 if (mExpandedChild != null) {
Jorim Jaggi0d9f35d2014-08-20 17:06:55 +0200325 mExpandedChild.animate().cancel();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200326 removeView(mExpandedChild);
327 }
328 addView(child);
329 mExpandedChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700330 mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
331 mContainingNotification);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200332 }
333
Selim Cinek8d490d42015-04-10 00:05:50 -0700334 public void setHeadsUpChild(View child) {
335 if (mHeadsUpChild != null) {
336 mHeadsUpChild.animate().cancel();
337 removeView(mHeadsUpChild);
338 }
339 addView(child);
340 mHeadsUpChild = child;
Selim Cinek7d1c63e2016-04-21 15:26:10 -0700341 mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
342 mContainingNotification);
Selim Cinek8d490d42015-04-10 00:05:50 -0700343 }
344
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100345 @Override
346 protected void onVisibilityChanged(View changedView, int visibility) {
347 super.onVisibilityChanged(changedView, visibility);
348 updateVisibility();
349 }
350
351 private void updateVisibility() {
352 setVisible(isShown());
353 }
354
Jorim Jaggi38b5ec92016-04-12 01:39:49 -0700355 @Override
356 protected void onDetachedFromWindow() {
357 super.onDetachedFromWindow();
358 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
359 }
360
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100361 private void setVisible(final boolean isVisible) {
362 if (isVisible) {
363
364 // We only animate if we are drawn at least once, otherwise the view might animate when
365 // it's shown the first time
366 getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
367 } else {
368 getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
369 mAnimate = false;
370 }
371 }
372
Selim Cinekb5605e52015-02-20 18:21:41 +0100373 public void setContentHeight(int contentHeight) {
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700374 mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());;
375 mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
Selim Cinekbb3d1cf2014-10-31 00:12:56 +0100376 selectLayout(mAnimate /* animate */, false /* force */);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200377 updateClipping();
Jorim Jaggibe4116a2015-05-20 20:04:08 -0700378 invalidateOutline();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200379 }
380
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800381 private void updateContentTransformation() {
382 int visibleType = calculateVisibleType();
383 if (visibleType != mVisibleType) {
384 // A new transformation starts
385 mTransformationStartVisibleType = mVisibleType;
386 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
387 final TransformableView hiddenView = getTransformableViewForVisibleType(
388 mTransformationStartVisibleType);
389 shownView.transformFrom(hiddenView, 0.0f);
390 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
391 hiddenView.transformTo(shownView, 0.0f);
392 mVisibleType = visibleType;
Selim Cinekc3179332016-03-04 14:44:56 -0800393 updateBackgroundColor(true /* animate */);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800394 }
Selim Cineke8578872016-05-03 16:42:50 -0700395 if (mForceSelectNextLayout) {
396 forceUpdateVisibilities();
397 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800398 if (mTransformationStartVisibleType != UNDEFINED
Selim Cinek589fd3e2016-04-26 18:17:57 -0700399 && mVisibleType != mTransformationStartVisibleType
400 && getViewForVisibleType(mTransformationStartVisibleType) != null) {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800401 final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
402 final TransformableView hiddenView = getTransformableViewForVisibleType(
403 mTransformationStartVisibleType);
404 float transformationAmount = calculateTransformationAmount();
405 shownView.transformFrom(hiddenView, transformationAmount);
406 hiddenView.transformTo(shownView, transformationAmount);
Selim Cinekc3179332016-03-04 14:44:56 -0800407 updateBackgroundTransformation(transformationAmount);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800408 } else {
409 updateViewVisibilities(visibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800410 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800411 }
412 }
413
Selim Cinekc3179332016-03-04 14:44:56 -0800414 private void updateBackgroundTransformation(float transformationAmount) {
415 int endColor = getBackgroundColor(mVisibleType);
416 int startColor = getBackgroundColor(mTransformationStartVisibleType);
417 if (endColor != startColor) {
418 if (startColor == 0) {
419 startColor = mContainingNotification.getBackgroundColorWithoutTint();
420 }
421 if (endColor == 0) {
422 endColor = mContainingNotification.getBackgroundColorWithoutTint();
423 }
424 endColor = NotificationUtils.interpolateColors(startColor, endColor,
425 transformationAmount);
426 }
427 mContainingNotification.setContentBackground(endColor, false, this);
428 }
429
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800430 private float calculateTransformationAmount() {
431 int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
432 int endHeight = getViewForVisibleType(mVisibleType).getHeight();
433 int progress = Math.abs(mContentHeight - startHeight);
434 int totalDistance = Math.abs(endHeight - startHeight);
435 float amount = (float) progress / (float) totalDistance;
436 return Math.min(1.0f, amount);
437 }
438
Selim Cinekb5605e52015-02-20 18:21:41 +0100439 public int getContentHeight() {
440 return mContentHeight;
441 }
442
Jorim Jaggibe565df2014-04-28 17:51:23 +0200443 public int getMaxHeight() {
Selim Cinek77019c72015-12-09 10:18:02 -0800444 if (mExpandedChild != null) {
Selim Cinek8d490d42015-04-10 00:05:50 -0700445 return mExpandedChild.getHeight();
Selim Cinek77019c72015-12-09 10:18:02 -0800446 } else if (mIsHeadsUp && mHeadsUpChild != null) {
447 return mHeadsUpChild.getHeight();
Selim Cinek8d490d42015-04-10 00:05:50 -0700448 }
Selim Cinek860b6da2015-12-16 19:02:19 -0800449 return mContractedChild.getHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200450 }
451
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200452 public int getMinHeight() {
Selim Cinek42357e02016-02-24 18:48:01 -0800453 return getMinHeight(false /* likeGroupExpanded */);
454 }
455
456 public int getMinHeight(boolean likeGroupExpanded) {
457 if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
Selim Cinek860b6da2015-12-16 19:02:19 -0800458 return mContractedChild.getHeight();
Selim Cinek42357e02016-02-24 18:48:01 -0800459 } else {
460 return mSingleLineView.getHeight();
Selim Cinek83bc7832015-10-22 13:26:54 -0700461 }
462 }
463
464 private boolean isGroupExpanded() {
465 return mGroupManager.isGroupExpanded(mStatusBarNotification);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200466 }
467
Jorim Jaggibe565df2014-04-28 17:51:23 +0200468 public void setClipTopAmount(int clipTopAmount) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200469 mClipTopAmount = clipTopAmount;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200470 updateClipping();
471 }
472
Jorim Jaggibe565df2014-04-28 17:51:23 +0200473 private void updateClipping() {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100474 if (mClipToActualHeight) {
475 mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
476 setClipBounds(mClipBounds);
477 } else {
478 setClipBounds(null);
479 }
480 }
481
482 public void setClipToActualHeight(boolean clipToActualHeight) {
483 mClipToActualHeight = clipToActualHeight;
484 updateClipping();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200485 }
486
Jorim Jaggi11298832014-05-24 16:18:38 +0200487 private void selectLayout(boolean animate, boolean force) {
488 if (mContractedChild == null) {
489 return;
490 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800491 if (mUserExpanding) {
492 updateContentTransformation();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700493 } else {
494 int visibleType = calculateVisibleType();
495 if (visibleType != mVisibleType || force) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700496 View visibleView = getViewForVisibleType(visibleType);
497 if (visibleView != null) {
498 visibleView.setVisibility(VISIBLE);
499 transferRemoteInputFocus(visibleType);
500 }
501
Selim Cinek589fd3e2016-04-26 18:17:57 -0700502 if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
503 || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
504 || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
505 || visibleType == VISIBLE_TYPE_CONTRACTED)) {
506 animateToVisibleType(visibleType);
507 } else {
508 updateViewVisibilities(visibleType);
509 }
510 mVisibleType = visibleType;
511 updateBackgroundColor(animate);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200512 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700513 }
Selim Cinek589fd3e2016-04-26 18:17:57 -0700514 }
515
516 private void forceUpdateVisibilities() {
517 boolean contractedVisible = mVisibleType == VISIBLE_TYPE_CONTRACTED
518 || mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED;
519 boolean expandedVisible = mVisibleType == VISIBLE_TYPE_EXPANDED
520 || mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED;
521 boolean headsUpVisible = mVisibleType == VISIBLE_TYPE_HEADSUP
522 || mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP;
523 boolean singleLineVisible = mVisibleType == VISIBLE_TYPE_SINGLELINE
524 || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE;
525 if (!contractedVisible) {
526 mContractedChild.setVisibility(View.INVISIBLE);
527 } else {
528 mContractedWrapper.setVisible(true);
529 }
530 if (mExpandedChild != null) {
531 if (!expandedVisible) {
532 mExpandedChild.setVisibility(View.INVISIBLE);
533 } else {
534 mExpandedWrapper.setVisible(true);
535 }
536 }
537 if (mHeadsUpChild != null) {
538 if (!headsUpVisible) {
539 mHeadsUpChild.setVisibility(View.INVISIBLE);
540 } else {
541 mHeadsUpWrapper.setVisible(true);
542 }
543 }
544 if (mSingleLineView != null) {
545 if (!singleLineVisible) {
546 mSingleLineView.setVisibility(View.INVISIBLE);
547 } else {
548 mSingleLineView.setVisible(true);
549 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200550 }
Jorim Jaggi11298832014-05-24 16:18:38 +0200551 }
552
Selim Cinekc3179332016-03-04 14:44:56 -0800553 public void updateBackgroundColor(boolean animate) {
554 int customBackgroundColor = getBackgroundColor(mVisibleType);
555 mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
556 }
557
558 private int getBackgroundColor(int visibleType) {
559 NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
560 int customBackgroundColor = 0;
561 if (currentVisibleWrapper != null) {
562 customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
563 }
564 return customBackgroundColor;
565 }
566
Selim Cinek684a4422015-04-15 16:18:39 -0700567 private void updateViewVisibilities(int visibleType) {
568 boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100569 mContractedWrapper.setVisible(contractedVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700570 if (mExpandedChild != null) {
Selim Cinek684a4422015-04-15 16:18:39 -0700571 boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100572 mExpandedWrapper.setVisible(expandedVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700573 }
574 if (mHeadsUpChild != null) {
Selim Cinek684a4422015-04-15 16:18:39 -0700575 boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100576 mHeadsUpWrapper.setVisible(headsUpVisible);
Selim Cinek8d490d42015-04-10 00:05:50 -0700577 }
Selim Cinek83bc7832015-10-22 13:26:54 -0700578 if (mSingleLineView != null) {
579 boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
Selim Cinek4ffd6362015-12-29 15:12:23 +0100580 mSingleLineView.setVisible(singleLineVisible);
Selim Cinek83bc7832015-10-22 13:26:54 -0700581 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700582 }
583
Selim Cinek4ffd6362015-12-29 15:12:23 +0100584 private void animateToVisibleType(int visibleType) {
585 final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
586 final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700587 if (shownView == hiddenView || hiddenView == null) {
Selim Cinek51d94912016-03-02 15:34:28 -0800588 shownView.setVisible(true);
589 return;
590 }
Selim Cinek4ffd6362015-12-29 15:12:23 +0100591 shownView.transformFrom(hiddenView);
592 getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
593 hiddenView.transformTo(shownView, new Runnable() {
594 @Override
595 public void run() {
Selim Cinek51d94912016-03-02 15:34:28 -0800596 if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
597 hiddenView.setVisible(false);
598 }
Selim Cinek4ffd6362015-12-29 15:12:23 +0100599 }
600 });
Jorim Jaggi11298832014-05-24 16:18:38 +0200601 }
602
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700603 private void transferRemoteInputFocus(int visibleType) {
604 if (visibleType == VISIBLE_TYPE_HEADSUP
605 && mHeadsUpRemoteInput != null
606 && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
607 mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
608 }
609 if (visibleType == VISIBLE_TYPE_EXPANDED
610 && mExpandedRemoteInput != null
611 && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
612 mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
613 }
614 }
615
Selim Cinek684a4422015-04-15 16:18:39 -0700616 /**
617 * @param visibleType one of the static enum types in this view
Selim Cinek4ffd6362015-12-29 15:12:23 +0100618 * @return the corresponding transformable view according to the given visible type
619 */
620 private TransformableView getTransformableViewForVisibleType(int visibleType) {
621 switch (visibleType) {
622 case VISIBLE_TYPE_EXPANDED:
623 return mExpandedWrapper;
624 case VISIBLE_TYPE_HEADSUP:
625 return mHeadsUpWrapper;
626 case VISIBLE_TYPE_SINGLELINE:
627 return mSingleLineView;
628 default:
629 return mContractedWrapper;
630 }
631 }
632
633 /**
634 * @param visibleType one of the static enum types in this view
Selim Cinek684a4422015-04-15 16:18:39 -0700635 * @return the corresponding view according to the given visible type
636 */
637 private View getViewForVisibleType(int visibleType) {
638 switch (visibleType) {
639 case VISIBLE_TYPE_EXPANDED:
Selim Cinek8d490d42015-04-10 00:05:50 -0700640 return mExpandedChild;
Selim Cinek684a4422015-04-15 16:18:39 -0700641 case VISIBLE_TYPE_HEADSUP:
Selim Cinek8d490d42015-04-10 00:05:50 -0700642 return mHeadsUpChild;
Selim Cinek83bc7832015-10-22 13:26:54 -0700643 case VISIBLE_TYPE_SINGLELINE:
644 return mSingleLineView;
Selim Cinek684a4422015-04-15 16:18:39 -0700645 default:
646 return mContractedChild;
Selim Cinek8d490d42015-04-10 00:05:50 -0700647 }
Selim Cinek8d490d42015-04-10 00:05:50 -0700648 }
649
Selim Cinekc3179332016-03-04 14:44:56 -0800650 private NotificationViewWrapper getVisibleWrapper(int visibleType) {
651 switch (visibleType) {
Selim Cinek34eda5e2016-02-18 17:10:43 -0800652 case VISIBLE_TYPE_EXPANDED:
653 return mExpandedWrapper;
654 case VISIBLE_TYPE_HEADSUP:
655 return mHeadsUpWrapper;
656 case VISIBLE_TYPE_CONTRACTED:
657 return mContractedWrapper;
658 default:
659 return null;
660 }
661 }
662
Selim Cinek684a4422015-04-15 16:18:39 -0700663 /**
664 * @return one of the static enum types in this view, calculated form the current state
665 */
666 private int calculateVisibleType() {
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800667 if (mUserExpanding) {
Selim Cinek42357e02016-02-24 18:48:01 -0800668 int height = !mIsChildInGroup || isGroupExpanded()
Selim Cineke81b82b2016-03-04 11:22:28 -0800669 || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
Selim Cinek42357e02016-02-24 18:48:01 -0800670 ? mContainingNotification.getMaxContentHeight()
671 : mContainingNotification.getShowingLayout().getMinHeight();
Selim Cinek51d94912016-03-02 15:34:28 -0800672 if (height == 0) {
673 height = mContentHeight;
674 }
Selim Cinek42357e02016-02-24 18:48:01 -0800675 int expandedVisualType = getVisualTypeForHeight(height);
Selim Cinek589fd3e2016-04-26 18:17:57 -0700676 int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
677 ? VISIBLE_TYPE_SINGLELINE
678 : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800679 return mTransformationStartVisibleType == collapsedVisualType
680 ? expandedVisualType
681 : collapsedVisualType;
682 }
Selim Cinek51d94912016-03-02 15:34:28 -0800683 int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
684 int viewHeight = mContentHeight;
685 if (intrinsicHeight != 0) {
686 // the intrinsicHeight might be 0 because it was just reset.
687 viewHeight = Math.min(mContentHeight, intrinsicHeight);
688 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800689 return getVisualTypeForHeight(viewHeight);
690 }
691
692 private int getVisualTypeForHeight(float viewHeight) {
693 boolean noExpandedChild = mExpandedChild == null;
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800694 if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
Adrian Roos777ef562015-12-01 17:37:14 -0800695 return VISIBLE_TYPE_EXPANDED;
696 }
Selim Cinek42357e02016-02-24 18:48:01 -0800697 if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
Selim Cinekd84a5932015-12-15 11:45:36 -0800698 return VISIBLE_TYPE_SINGLELINE;
699 }
Adrian Roos777ef562015-12-01 17:37:14 -0800700
Selim Cinek1f3f5442015-04-10 17:54:46 -0700701 if (mIsHeadsUp && mHeadsUpChild != null) {
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800702 if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
Selim Cinek684a4422015-04-15 16:18:39 -0700703 return VISIBLE_TYPE_HEADSUP;
Selim Cinek8d490d42015-04-10 00:05:50 -0700704 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700705 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700706 }
707 } else {
Selim Cineka4091502016-02-11 17:21:16 -0800708 if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
Selim Cineke81b82b2016-03-04 11:22:28 -0800709 && (!mIsChildInGroup
710 || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
Selim Cinek684a4422015-04-15 16:18:39 -0700711 return VISIBLE_TYPE_CONTRACTED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700712 } else {
Selim Cinek684a4422015-04-15 16:18:39 -0700713 return VISIBLE_TYPE_EXPANDED;
Selim Cinek8d490d42015-04-10 00:05:50 -0700714 }
715 }
Jorim Jaggibe565df2014-04-28 17:51:23 +0200716 }
717
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200718 public boolean isContentExpandable() {
719 return mExpandedChild != null;
720 }
John Spurlocke15452b2014-08-21 09:44:39 -0400721
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100722 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -0800723 if (mContractedChild == null) {
Selim Cinek0934bd92016-02-19 12:22:33 -0800724 return;
725 }
John Spurlocke15452b2014-08-21 09:44:39 -0400726 mDark = dark;
Selim Cinek19ba7052016-01-27 20:04:27 -0800727 if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800728 mContractedWrapper.setDark(dark, fade, delay);
729 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800730 if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800731 mExpandedWrapper.setDark(dark, fade, delay);
732 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800733 if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
Selim Cinek30e387d2016-01-11 18:01:47 -0800734 mHeadsUpWrapper.setDark(dark, fade, delay);
735 }
Selim Cinek19ba7052016-01-27 20:04:27 -0800736 if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
Selim Cinek9c7712d2015-12-08 19:19:48 -0800737 mSingleLineView.setDark(dark, fade, delay);
738 }
John Spurlocke15452b2014-08-21 09:44:39 -0400739 }
740
Selim Cinek8d490d42015-04-10 00:05:50 -0700741 public void setHeadsUp(boolean headsUp) {
742 mIsHeadsUp = headsUp;
743 selectLayout(false /* animate */, true /* force */);
Selim Cinek98be3f32015-12-17 16:39:06 -0800744 updateExpandButtons(mExpandable);
Selim Cinek8d490d42015-04-10 00:05:50 -0700745 }
746
Jorim Jaggiaa92ffb2014-09-10 23:29:28 +0200747 @Override
748 public boolean hasOverlappingRendering() {
749
750 // This is not really true, but good enough when fading from the contracted to the expanded
751 // layout, and saves us some layers.
752 return false;
753 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700754
755 public void setShowingLegacyBackground(boolean showing) {
756 mShowingLegacyBackground = showing;
Selim Cinekc3179332016-03-04 14:44:56 -0800757 updateShowingLegacyBackground();
758 }
759
760 private void updateShowingLegacyBackground() {
761 if (mContractedChild != null) {
762 mContractedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
763 }
764 if (mExpandedChild != null) {
765 mExpandedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
766 }
767 if (mHeadsUpChild != null) {
768 mHeadsUpWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
769 }
Jorim Jaggi59ec3042015-06-05 15:18:43 -0700770 }
Selim Cinek8d6440d2015-10-22 13:00:05 -0700771
Selim Cinek83bc7832015-10-22 13:26:54 -0700772 public void setIsChildInGroup(boolean isChildInGroup) {
773 mIsChildInGroup = isChildInGroup;
774 updateSingleLineView();
775 }
776
Adrian Roosb88b1a12015-12-09 18:51:05 -0800777 public void onNotificationUpdated(NotificationData.Entry entry) {
778 mStatusBarNotification = entry.notification;
Selim Cinek860b6da2015-12-16 19:02:19 -0800779 mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
Selim Cinek83bc7832015-10-22 13:26:54 -0700780 updateSingleLineView();
Adrian Roosb88b1a12015-12-09 18:51:05 -0800781 applyRemoteInput(entry);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800782 if (mContractedChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100783 mContractedWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800784 }
785 if (mExpandedChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100786 mExpandedWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800787 }
788 if (mHeadsUpChild != null) {
Selim Cinek4ffd6362015-12-29 15:12:23 +0100789 mHeadsUpWrapper.notifyContentUpdated(entry.notification);
Selim Cinek8fc93c92015-11-23 17:48:07 -0800790 }
Selim Cinekc3179332016-03-04 14:44:56 -0800791 updateShowingLegacyBackground();
Selim Cinek589fd3e2016-04-26 18:17:57 -0700792 mForceSelectNextLayout = true;
Selim Cinekb88b9842016-02-26 09:25:33 -0800793 setDark(mDark, false /* animate */, 0 /* delay */);
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700794 mPreviousExpandedRemoteInputIntent = null;
795 mPreviousHeadsUpRemoteInputIntent = null;
Selim Cinek83bc7832015-10-22 13:26:54 -0700796 }
797
798 private void updateSingleLineView() {
799 if (mIsChildInGroup) {
Selim Cinekc897bd32016-03-18 17:32:31 -0700800 mSingleLineView = mHybridGroupManager.bindFromNotification(
Selim Cinek83bc7832015-10-22 13:26:54 -0700801 mSingleLineView, mStatusBarNotification.getNotification());
Selim Cinekde33a4a2016-02-11 16:43:41 -0800802 } else if (mSingleLineView != null) {
803 removeView(mSingleLineView);
804 mSingleLineView = null;
Selim Cinek83bc7832015-10-22 13:26:54 -0700805 }
806 }
807
Adrian Roosb88b1a12015-12-09 18:51:05 -0800808 private void applyRemoteInput(final NotificationData.Entry entry) {
809 if (mRemoteInputController == null) {
810 return;
811 }
812
813 boolean hasRemoteInput = false;
814
815 Notification.Action[] actions = entry.notification.getNotification().actions;
816 if (actions != null) {
817 for (Notification.Action a : actions) {
818 if (a.getRemoteInputs() != null) {
819 for (RemoteInput ri : a.getRemoteInputs()) {
820 if (ri.getAllowFreeFormInput()) {
821 hasRemoteInput = true;
822 break;
823 }
824 }
825 }
826 }
827 }
828
829 View bigContentView = mExpandedChild;
830 if (bigContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700831 mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
832 mPreviousExpandedRemoteInputIntent);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700833 } else {
834 mExpandedRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800835 }
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700836
Adrian Roosb88b1a12015-12-09 18:51:05 -0800837 View headsUpContentView = mHeadsUpChild;
838 if (headsUpContentView != null) {
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700839 mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
840 mPreviousHeadsUpRemoteInputIntent);
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700841 } else {
842 mHeadsUpRemoteInput = null;
Adrian Roosb88b1a12015-12-09 18:51:05 -0800843 }
844 }
845
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700846 private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
847 boolean hasRemoteInput, PendingIntent existingPendingIntent) {
Adrian Roosb88b1a12015-12-09 18:51:05 -0800848 View actionContainerCandidate = view.findViewById(
849 com.android.internal.R.id.actions_container);
850 if (actionContainerCandidate instanceof FrameLayout) {
851 RemoteInputView existing = (RemoteInputView)
852 view.findViewWithTag(RemoteInputView.VIEW_TAG);
853
854 if (existing != null) {
855 existing.onNotificationUpdate();
856 }
857
858 if (existing == null && hasRemoteInput) {
859 ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
860 RemoteInputView riv = RemoteInputView.inflate(
861 mContext, actionContainer, entry, mRemoteInputController);
862
863 riv.setVisibility(View.INVISIBLE);
864 actionContainer.addView(riv, new LayoutParams(
865 ViewGroup.LayoutParams.MATCH_PARENT,
866 ViewGroup.LayoutParams.MATCH_PARENT)
867 );
Adrian Roos4ff3b122016-02-01 12:26:13 -0800868 existing = riv;
869 }
870 if (hasRemoteInput) {
Adrian Roosb88b1a12015-12-09 18:51:05 -0800871 int color = entry.notification.getNotification().color;
872 if (color == Notification.COLOR_DEFAULT) {
873 color = mContext.getColor(R.color.default_remote_input_background);
874 }
Adrian Roos4ff3b122016-02-01 12:26:13 -0800875 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
876 mContext.getColor(R.color.remote_input_text),
877 mContext.getColor(R.color.remote_input_hint)));
Adrian Roos4c1fcc82016-03-31 14:39:39 -0700878
879 if (existingPendingIntent != null || existing.isActive()) {
880 // The current action could be gone, or the pending intent no longer valid.
881 // If we find a matching action in the new notification, focus, otherwise close.
882 Notification.Action[] actions = entry.notification.getNotification().actions;
883 if (existingPendingIntent != null) {
884 existing.setPendingIntent(existingPendingIntent);
885 }
886 if (existing.updatePendingIntentFromActions(actions)) {
887 if (!existing.isActive()) {
888 existing.focus();
889 }
890 } else {
891 if (existing.isActive()) {
892 existing.close();
893 }
894 }
895 }
Adrian Roosb88b1a12015-12-09 18:51:05 -0800896 }
Adrian Roos0bd8a4b2016-03-14 16:21:44 -0700897 return existing;
898 }
899 return null;
900 }
901
902 public void closeRemoteInput() {
903 if (mHeadsUpRemoteInput != null) {
904 mHeadsUpRemoteInput.close();
905 }
906 if (mExpandedRemoteInput != null) {
907 mExpandedRemoteInput.close();
Adrian Roosb88b1a12015-12-09 18:51:05 -0800908 }
909 }
910
Selim Cinek83bc7832015-10-22 13:26:54 -0700911 public void setGroupManager(NotificationGroupManager groupManager) {
912 mGroupManager = groupManager;
913 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700914
Adrian Roosb88b1a12015-12-09 18:51:05 -0800915 public void setRemoteInputController(RemoteInputController r) {
916 mRemoteInputController = r;
917 }
918
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800919 public void setExpandClickListener(OnClickListener expandClickListener) {
920 mExpandClickListener = expandClickListener;
921 }
922
923 public void updateExpandButtons(boolean expandable) {
Selim Cinek98be3f32015-12-17 16:39:06 -0800924 mExpandable = expandable;
925 // if the expanded child has the same height as the collapsed one we hide it.
Selim Cinek8ea2d5f2016-04-20 17:10:40 -0700926 if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
927 if ((!mIsHeadsUp || mHeadsUpChild == null)) {
928 if (mExpandedChild.getHeight() == mContractedChild.getHeight()) {
929 expandable = false;
930 }
931 } else if (mExpandedChild.getHeight() == mHeadsUpChild.getHeight()) {
932 expandable = false;
933 }
Selim Cinek98be3f32015-12-17 16:39:06 -0800934 }
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700935 if (mExpandedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800936 mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700937 }
938 if (mContractedChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800939 mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700940 }
941 if (mHeadsUpChild != null) {
Selim Cinekeaa29ca2015-11-23 13:51:13 -0800942 mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700943 }
944 }
Selim Cinekea4bef72015-12-02 15:51:10 -0800945
946 public NotificationHeaderView getNotificationHeader() {
947 NotificationHeaderView header = null;
948 if (mContractedChild != null) {
949 header = mContractedWrapper.getNotificationHeader();
950 }
951 if (header == null && mExpandedChild != null) {
952 header = mExpandedWrapper.getNotificationHeader();
953 }
954 if (header == null && mHeadsUpChild != null) {
955 header = mHeadsUpWrapper.getNotificationHeader();
956 }
957 return header;
958 }
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800959
Selim Cinek34eda5e2016-02-18 17:10:43 -0800960 public NotificationHeaderView getVisibleNotificationHeader() {
Selim Cinekc3179332016-03-04 14:44:56 -0800961 NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
Selim Cinek34eda5e2016-02-18 17:10:43 -0800962 return wrapper == null ? null : wrapper.getNotificationHeader();
963 }
964
Selim Cinekfa0a2d32016-01-14 13:02:21 -0800965 public void setContainingNotification(ExpandableNotificationRow containingNotification) {
966 mContainingNotification = containingNotification;
967 }
968
969 public void requestSelectLayout(boolean needsAnimation) {
970 selectLayout(needsAnimation, false);
971 }
Selim Cinekde33a4a2016-02-11 16:43:41 -0800972
973 public void reInflateViews() {
974 if (mIsChildInGroup && mSingleLineView != null) {
975 removeView(mSingleLineView);
976 mSingleLineView = null;
977 updateSingleLineView();
978 }
979 }
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800980
981 public void setUserExpanding(boolean userExpanding) {
982 mUserExpanding = userExpanding;
983 if (userExpanding) {
984 mTransformationStartVisibleType = mVisibleType;
985 } else {
986 mTransformationStartVisibleType = UNDEFINED;
987 mVisibleType = calculateVisibleType();
988 updateViewVisibilities(mVisibleType);
Selim Cinekc3179332016-03-04 14:44:56 -0800989 updateBackgroundColor(false);
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800990 }
991 }
Selim Cinekc897bd32016-03-18 17:32:31 -0700992
993 /**
994 * Set by how much the single line view should be indented. Used when a overflow indicator is
995 * present and only during measuring
996 */
997 public void setSingleLineWidthIndention(int singleLineWidthIndention) {
998 if (singleLineWidthIndention != mSingleLineWidthIndention) {
999 mSingleLineWidthIndention = singleLineWidthIndention;
1000 mContainingNotification.forceLayout();
1001 forceLayout();
1002 }
1003 }
1004
1005 public HybridNotificationView getSingleLineView() {
1006 return mSingleLineView;
1007 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001008}