blob: 7d6432564d10e0e01f1e4fce048ea17c4821bc49 [file] [log] [blame]
Chris Wren51c75102013-07-16 20:49:17 -04001/*
2 * Copyright (C) 2013 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
19import android.content.Context;
20import android.util.AttributeSet;
Dan Sandlera5e0f412014-01-23 15:11:54 -050021import android.view.View;
Jorim Jaggife40f7d2014-04-28 15:20:04 +020022import android.view.accessibility.AccessibilityEvent;
Chris Wren51c75102013-07-16 20:49:17 -040023
Dan Sandlera5e0f412014-01-23 15:11:54 -050024import com.android.systemui.R;
25
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020026public class ExpandableNotificationRow extends ActivatableNotificationView {
Selim Cinek1685e632014-04-08 02:27:49 +020027 private int mRowMinHeight;
28 private int mRowMaxHeight;
Chris Wren51c75102013-07-16 20:49:17 -040029
Selim Cinek1685e632014-04-08 02:27:49 +020030 /** Does this row contain layouts that can adapt to row expansion */
Chris Wren51c75102013-07-16 20:49:17 -040031 private boolean mExpandable;
Selim Cinek1685e632014-04-08 02:27:49 +020032 /** Has the user actively changed the expansion state of this row */
33 private boolean mHasUserChangedExpansion;
34 /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
Chris Wren51c75102013-07-16 20:49:17 -040035 private boolean mUserExpanded;
Selim Cinek1685e632014-04-08 02:27:49 +020036 /** Is the user touching this row */
Chris Wren51c75102013-07-16 20:49:17 -040037 private boolean mUserLocked;
Selim Cinek1685e632014-04-08 02:27:49 +020038 /** Are we showing the "public" version */
Dan Sandlera5e0f412014-01-23 15:11:54 -050039 private boolean mShowingPublic;
Jorim Jaggiae441282014-08-01 02:45:18 +020040 private boolean mSensitive;
41 private boolean mShowingPublicInitialized;
42 private boolean mShowingPublicForIntrinsicHeight;
Chris Wren51c75102013-07-16 20:49:17 -040043
Selim Cinek1685e632014-04-08 02:27:49 +020044 /**
45 * Is this notification expanded by the system. The expansion state can be overridden by the
46 * user expansion.
47 */
48 private boolean mIsSystemExpanded;
Jorim Jaggidce3c4c2014-04-29 23:12:24 +020049
50 /**
51 * Whether the notification expansion is disabled. This is the case on Keyguard.
52 */
53 private boolean mExpansionDisabled;
54
Jorim Jaggibe565df2014-04-28 17:51:23 +020055 private NotificationContentView mPublicLayout;
56 private NotificationContentView mPrivateLayout;
Selim Cinek1685e632014-04-08 02:27:49 +020057 private int mMaxExpandHeight;
Selim Cinek863834b2014-05-20 04:20:25 +020058 private View mVetoButton;
Dan Sandler0d3e62f2014-07-14 17:13:50 -040059 private boolean mClearable;
Chris Wren78403d72014-07-28 10:23:24 +010060 private ExpansionLogger mLogger;
61 private String mLoggingKey;
Selim Cineka5e211b2014-08-11 17:35:48 +020062 private boolean mWasReset;
Chris Wren78403d72014-07-28 10:23:24 +010063
64 public interface ExpansionLogger {
65 public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
66 }
Selim Cinek1685e632014-04-08 02:27:49 +020067
Chris Wren51c75102013-07-16 20:49:17 -040068 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
69 super(context, attrs);
70 }
71
Christoph Studera7fe6312014-06-27 19:32:44 +020072 /**
73 * Resets this view so it can be re-used for an updated notification.
74 */
Christoph Studer22f2ee52014-07-29 22:57:21 +020075 @Override
Christoph Studera7fe6312014-06-27 19:32:44 +020076 public void reset() {
Christoph Studer22f2ee52014-07-29 22:57:21 +020077 super.reset();
Christoph Studera7fe6312014-06-27 19:32:44 +020078 mRowMinHeight = 0;
Chris Wren78403d72014-07-28 10:23:24 +010079 final boolean wasExpanded = isExpanded();
Christoph Studera7fe6312014-06-27 19:32:44 +020080 mRowMaxHeight = 0;
81 mExpandable = false;
82 mHasUserChangedExpansion = false;
83 mUserLocked = false;
84 mShowingPublic = false;
Jorim Jaggiae441282014-08-01 02:45:18 +020085 mSensitive = false;
86 mShowingPublicInitialized = false;
Christoph Studera7fe6312014-06-27 19:32:44 +020087 mIsSystemExpanded = false;
88 mExpansionDisabled = false;
89 mPublicLayout.reset();
90 mPrivateLayout.reset();
Selim Cinek31094df2014-08-14 19:28:15 +020091 resetHeight();
92 logExpansionEvent(false, wasExpanded);
93 }
94
95 public void resetHeight() {
Christoph Studera7fe6312014-06-27 19:32:44 +020096 mMaxExpandHeight = 0;
Selim Cineka5e211b2014-08-11 17:35:48 +020097 mWasReset = true;
Selim Cinek31094df2014-08-14 19:28:15 +020098 onHeightReset();
Christoph Studera7fe6312014-06-27 19:32:44 +020099 }
100
Jorim Jaggi251957d2014-04-09 04:24:09 +0200101 @Override
102 protected void onFinishInflate() {
103 super.onFinishInflate();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200104 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
105 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
Selim Cinek863834b2014-05-20 04:20:25 +0200106 mVetoButton = findViewById(R.id.veto);
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200107 }
108
109 @Override
110 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
111 if (super.onRequestSendAccessibilityEvent(child, event)) {
112 // Add a record for the entire layout since its content is somehow small.
113 // The event comes from a leaf view that is interacted with.
114 AccessibilityEvent record = AccessibilityEvent.obtain();
115 onInitializeAccessibilityEvent(record);
116 dispatchPopulateAccessibilityEvent(record);
117 event.appendRecord(record);
118 return true;
119 }
120 return false;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200121 }
Chris Wren51c75102013-07-16 20:49:17 -0400122
John Spurlocke15452b2014-08-21 09:44:39 -0400123 @Override
124 public void setDark(boolean dark, boolean fade) {
125 super.setDark(dark, fade);
126 final NotificationContentView showing = getShowingLayout();
127 if (showing != null) {
128 showing.setDark(dark, fade);
129 }
130 }
131
Selim Cinek1685e632014-04-08 02:27:49 +0200132 public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
133 mRowMinHeight = rowMinHeight;
134 mRowMaxHeight = rowMaxHeight;
Chris Wren51c75102013-07-16 20:49:17 -0400135 }
136
137 public boolean isExpandable() {
138 return mExpandable;
139 }
140
141 public void setExpandable(boolean expandable) {
142 mExpandable = expandable;
143 }
144
Selim Cinek1685e632014-04-08 02:27:49 +0200145 /**
146 * @return whether the user has changed the expansion state
147 */
148 public boolean hasUserChangedExpansion() {
149 return mHasUserChangedExpansion;
150 }
151
Chris Wren51c75102013-07-16 20:49:17 -0400152 public boolean isUserExpanded() {
153 return mUserExpanded;
154 }
155
Selim Cinek1685e632014-04-08 02:27:49 +0200156 /**
157 * Set this notification to be expanded by the user
158 *
159 * @param userExpanded whether the user wants this notification to be expanded
160 */
Chris Wren51c75102013-07-16 20:49:17 -0400161 public void setUserExpanded(boolean userExpanded) {
Christoph Studera7fe6312014-06-27 19:32:44 +0200162 if (userExpanded && !mExpandable) return;
Chris Wren78403d72014-07-28 10:23:24 +0100163 final boolean wasExpanded = isExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +0200164 mHasUserChangedExpansion = true;
Chris Wren51c75102013-07-16 20:49:17 -0400165 mUserExpanded = userExpanded;
Chris Wren78403d72014-07-28 10:23:24 +0100166 logExpansionEvent(true, wasExpanded);
Chris Wren51c75102013-07-16 20:49:17 -0400167 }
168
Selim Cinekccd14fb2014-08-12 18:53:24 +0200169 public void resetUserExpansion() {
170 mHasUserChangedExpansion = false;
171 mUserExpanded = false;
172 }
173
Chris Wren51c75102013-07-16 20:49:17 -0400174 public boolean isUserLocked() {
175 return mUserLocked;
176 }
177
178 public void setUserLocked(boolean userLocked) {
179 mUserLocked = userLocked;
180 }
181
Selim Cinek1685e632014-04-08 02:27:49 +0200182 /**
183 * @return has the system set this notification to be expanded
184 */
185 public boolean isSystemExpanded() {
186 return mIsSystemExpanded;
187 }
188
189 /**
190 * Set this notification to be expanded by the system.
191 *
192 * @param expand whether the system wants this notification to be expanded.
193 */
194 public void setSystemExpanded(boolean expand) {
Selim Cinek31094df2014-08-14 19:28:15 +0200195 if (expand != mIsSystemExpanded) {
196 final boolean wasExpanded = isExpanded();
197 mIsSystemExpanded = expand;
198 notifyHeightChanged();
199 logExpansionEvent(false, wasExpanded);
200 }
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200201 }
202
203 /**
204 * @param expansionDisabled whether to prevent notification expansion
205 */
206 public void setExpansionDisabled(boolean expansionDisabled) {
Selim Cinek31094df2014-08-14 19:28:15 +0200207 if (expansionDisabled != mExpansionDisabled) {
208 final boolean wasExpanded = isExpanded();
209 mExpansionDisabled = expansionDisabled;
210 logExpansionEvent(false, wasExpanded);
211 if (wasExpanded != isExpanded()) {
212 notifyHeightChanged();
213 }
214 }
Selim Cinek1685e632014-04-08 02:27:49 +0200215 }
216
217 /**
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400218 * @return Can the underlying notification be cleared?
219 */
220 public boolean isClearable() {
221 return mClearable;
222 }
223
224 /**
225 * Set whether the notification can be cleared.
226 *
227 * @param clearable
228 */
229 public void setClearable(boolean clearable) {
230 mClearable = clearable;
231 updateVetoButton();
232 }
233
234 /**
Selim Cinek1685e632014-04-08 02:27:49 +0200235 * Apply an expansion state to the layout.
Selim Cinek1685e632014-04-08 02:27:49 +0200236 */
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200237 public void applyExpansionToLayout() {
238 boolean expand = isExpanded();
Chris Wren51c75102013-07-16 20:49:17 -0400239 if (expand && mExpandable) {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200240 setActualHeight(mMaxExpandHeight);
Chris Wren51c75102013-07-16 20:49:17 -0400241 } else {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200242 setActualHeight(mRowMinHeight);
Chris Wren51c75102013-07-16 20:49:17 -0400243 }
Chris Wren51c75102013-07-16 20:49:17 -0400244 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500245
Jorim Jaggi9cbadd32014-05-01 20:18:31 +0200246 @Override
247 public int getIntrinsicHeight() {
Jorim Jaggibe565df2014-04-28 17:51:23 +0200248 if (isUserLocked()) {
249 return getActualHeight();
250 }
Selim Cinek1685e632014-04-08 02:27:49 +0200251 boolean inExpansionState = isExpanded();
252 if (!inExpansionState) {
253 // not expanded, so we return the collapsed size
254 return mRowMinHeight;
255 }
256
Jorim Jaggiae441282014-08-01 02:45:18 +0200257 return mShowingPublicForIntrinsicHeight ? mRowMinHeight : getMaxExpandHeight();
Selim Cinek1685e632014-04-08 02:27:49 +0200258 }
259
Selim Cinek1685e632014-04-08 02:27:49 +0200260 /**
261 * Check whether the view state is currently expanded. This is given by the system in {@link
262 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
263 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
264 * view can differ from this state, if layout params are modified from outside.
265 *
266 * @return whether the view state is currently expanded.
267 */
268 private boolean isExpanded() {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200269 return !mExpansionDisabled
270 && (!hasUserChangedExpansion() && isSystemExpanded() || isUserExpanded());
Selim Cinek1685e632014-04-08 02:27:49 +0200271 }
272
273 @Override
274 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
275 super.onLayout(changed, left, top, right, bottom);
Selim Cineka5e211b2014-08-11 17:35:48 +0200276 boolean updateExpandHeight = mMaxExpandHeight == 0 && !mWasReset;
Jorim Jaggibe565df2014-04-28 17:51:23 +0200277 mMaxExpandHeight = mPrivateLayout.getMaxHeight();
278 if (updateExpandHeight) {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +0200279 applyExpansionToLayout();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200280 }
Selim Cineka5e211b2014-08-11 17:35:48 +0200281 mWasReset = false;
Selim Cinek1685e632014-04-08 02:27:49 +0200282 }
283
Jorim Jaggiae441282014-08-01 02:45:18 +0200284 public void setSensitive(boolean sensitive) {
285 mSensitive = sensitive;
286 }
287
288 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
289 mShowingPublicForIntrinsicHeight = mSensitive && hideSensitive;
290 }
291
292 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
293 long duration) {
294 boolean oldShowingPublic = mShowingPublic;
295 mShowingPublic = mSensitive && hideSensitive;
296 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
297 return;
298 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500299
300 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +0200301 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -0500302
Jorim Jaggiae441282014-08-01 02:45:18 +0200303 if (!animated) {
304 mPublicLayout.animate().cancel();
305 mPrivateLayout.animate().cancel();
306 mPublicLayout.setAlpha(1f);
307 mPrivateLayout.setAlpha(1f);
308 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
309 mPrivateLayout.setVisibility(mShowingPublic ? View.INVISIBLE : View.VISIBLE);
310 } else {
311 animateShowingPublic(delay, duration);
312 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400313
314 updateVetoButton();
Jorim Jaggiae441282014-08-01 02:45:18 +0200315 mShowingPublicInitialized = true;
316 }
317
318 private void animateShowingPublic(long delay, long duration) {
319 final View source = mShowingPublic ? mPrivateLayout : mPublicLayout;
320 View target = mShowingPublic ? mPublicLayout : mPrivateLayout;
321 source.setVisibility(View.VISIBLE);
322 target.setVisibility(View.VISIBLE);
323 target.setAlpha(0f);
324 source.animate().cancel();
325 target.animate().cancel();
326 source.animate()
327 .alpha(0f)
328 .withLayer()
329 .setStartDelay(delay)
330 .setDuration(duration)
331 .withEndAction(new Runnable() {
332 @Override
333 public void run() {
334 source.setVisibility(View.INVISIBLE);
335 }
336 });
337 target.animate()
338 .alpha(1f)
339 .withLayer()
340 .setStartDelay(delay)
341 .setDuration(duration);
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400342 }
343
344 private void updateVetoButton() {
345 // public versions cannot be dismissed
346 mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
Dan Sandlera5e0f412014-01-23 15:11:54 -0500347 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200348
Selim Cinek1685e632014-04-08 02:27:49 +0200349 public int getMaxExpandHeight() {
Jorim Jaggiae441282014-08-01 02:45:18 +0200350 return mShowingPublicForIntrinsicHeight ? mRowMinHeight : mMaxExpandHeight;
Chris Wren51c75102013-07-16 20:49:17 -0400351 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +0200352
Jorim Jaggibe565df2014-04-28 17:51:23 +0200353 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200354 public boolean isContentExpandable() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200355 NotificationContentView showingLayout = getShowingLayout();
356 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200357 }
358
359 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200360 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200361 mPrivateLayout.setActualHeight(height);
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200362 mPublicLayout.setActualHeight(height);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200363 invalidate();
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200364 super.setActualHeight(height, notifyListeners);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200365 }
366
367 @Override
368 public int getMaxHeight() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200369 NotificationContentView showingLayout = getShowingLayout();
370 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200371 }
372
373 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200374 public int getMinHeight() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200375 NotificationContentView showingLayout = getShowingLayout();
376 return showingLayout.getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200377 }
378
379 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200380 public void setClipTopAmount(int clipTopAmount) {
381 super.setClipTopAmount(clipTopAmount);
382 mPrivateLayout.setClipTopAmount(clipTopAmount);
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200383 mPublicLayout.setClipTopAmount(clipTopAmount);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200384 }
385
386 public void notifyContentUpdated() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200387 mPublicLayout.notifyContentUpdated();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200388 mPrivateLayout.notifyContentUpdated();
Selim Cinek343e6e22014-04-11 21:23:30 +0200389 }
Selim Cinek7d447722014-06-10 15:51:59 +0200390
Selim Cinek31094df2014-08-14 19:28:15 +0200391 public boolean isMaxExpandHeightInitialized() {
392 return mMaxExpandHeight != 0;
Selim Cinek7d447722014-06-10 15:51:59 +0200393 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200394
395 private NotificationContentView getShowingLayout() {
396 return mShowingPublic ? mPublicLayout : mPrivateLayout;
397 }
Chris Wren78403d72014-07-28 10:23:24 +0100398
399 public void setExpansionLogger(ExpansionLogger logger, String key) {
400 mLogger = logger;
401 mLoggingKey = key;
402 }
403
404
405 private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
406 final boolean nowExpanded = isExpanded();
407 if (wasExpanded != nowExpanded && mLogger != null) {
408 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;
409 }
410 }
Chris Wren51c75102013-07-16 20:49:17 -0400411}