blob: fe754a902d80e608649701b0cc38b9ae9dda839f [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;
Selim Cinekd2319fb2014-09-01 19:41:54 +0200277 updateMaxExpandHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200278 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
Selim Cinekd2319fb2014-09-01 19:41:54 +0200284 private void updateMaxExpandHeight() {
285 int intrinsicBefore = getIntrinsicHeight();
286 mMaxExpandHeight = mPrivateLayout.getMaxHeight();
287 if (intrinsicBefore != getIntrinsicHeight()) {
288 notifyHeightChanged();
289 }
290 }
291
Jorim Jaggiae441282014-08-01 02:45:18 +0200292 public void setSensitive(boolean sensitive) {
293 mSensitive = sensitive;
294 }
295
296 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
297 mShowingPublicForIntrinsicHeight = mSensitive && hideSensitive;
298 }
299
300 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
301 long duration) {
302 boolean oldShowingPublic = mShowingPublic;
303 mShowingPublic = mSensitive && hideSensitive;
304 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
305 return;
306 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500307
308 // bail out if no public version
Selim Cinek1685e632014-04-08 02:27:49 +0200309 if (mPublicLayout.getChildCount() == 0) return;
Dan Sandlera5e0f412014-01-23 15:11:54 -0500310
Jorim Jaggiae441282014-08-01 02:45:18 +0200311 if (!animated) {
312 mPublicLayout.animate().cancel();
313 mPrivateLayout.animate().cancel();
314 mPublicLayout.setAlpha(1f);
315 mPrivateLayout.setAlpha(1f);
316 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
317 mPrivateLayout.setVisibility(mShowingPublic ? View.INVISIBLE : View.VISIBLE);
318 } else {
319 animateShowingPublic(delay, duration);
320 }
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400321
322 updateVetoButton();
Jorim Jaggiae441282014-08-01 02:45:18 +0200323 mShowingPublicInitialized = true;
324 }
325
326 private void animateShowingPublic(long delay, long duration) {
327 final View source = mShowingPublic ? mPrivateLayout : mPublicLayout;
328 View target = mShowingPublic ? mPublicLayout : mPrivateLayout;
329 source.setVisibility(View.VISIBLE);
330 target.setVisibility(View.VISIBLE);
331 target.setAlpha(0f);
332 source.animate().cancel();
333 target.animate().cancel();
334 source.animate()
335 .alpha(0f)
336 .withLayer()
337 .setStartDelay(delay)
338 .setDuration(duration)
339 .withEndAction(new Runnable() {
340 @Override
341 public void run() {
342 source.setVisibility(View.INVISIBLE);
343 }
344 });
345 target.animate()
346 .alpha(1f)
347 .withLayer()
348 .setStartDelay(delay)
349 .setDuration(duration);
Dan Sandler0d3e62f2014-07-14 17:13:50 -0400350 }
351
352 private void updateVetoButton() {
353 // public versions cannot be dismissed
354 mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
Dan Sandlera5e0f412014-01-23 15:11:54 -0500355 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200356
Selim Cinek1685e632014-04-08 02:27:49 +0200357 public int getMaxExpandHeight() {
Jorim Jaggiae441282014-08-01 02:45:18 +0200358 return mShowingPublicForIntrinsicHeight ? mRowMinHeight : mMaxExpandHeight;
Chris Wren51c75102013-07-16 20:49:17 -0400359 }
Jorim Jaggi584a7aa2014-04-10 23:26:13 +0200360
Jorim Jaggibe565df2014-04-28 17:51:23 +0200361 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200362 public boolean isContentExpandable() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200363 NotificationContentView showingLayout = getShowingLayout();
364 return showingLayout.isContentExpandable();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200365 }
366
367 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200368 public void setActualHeight(int height, boolean notifyListeners) {
Selim Cinekc9c00ae2014-05-20 03:33:40 +0200369 mPrivateLayout.setActualHeight(height);
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200370 mPublicLayout.setActualHeight(height);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200371 invalidate();
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200372 super.setActualHeight(height, notifyListeners);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200373 }
374
375 @Override
376 public int getMaxHeight() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200377 NotificationContentView showingLayout = getShowingLayout();
378 return showingLayout.getMaxHeight();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200379 }
380
381 @Override
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200382 public int getMinHeight() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200383 NotificationContentView showingLayout = getShowingLayout();
384 return showingLayout.getMinHeight();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200385 }
386
387 @Override
Jorim Jaggibe565df2014-04-28 17:51:23 +0200388 public void setClipTopAmount(int clipTopAmount) {
389 super.setClipTopAmount(clipTopAmount);
390 mPrivateLayout.setClipTopAmount(clipTopAmount);
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200391 mPublicLayout.setClipTopAmount(clipTopAmount);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200392 }
393
394 public void notifyContentUpdated() {
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200395 mPublicLayout.notifyContentUpdated();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200396 mPrivateLayout.notifyContentUpdated();
Selim Cinek343e6e22014-04-11 21:23:30 +0200397 }
Selim Cinek7d447722014-06-10 15:51:59 +0200398
Selim Cinek31094df2014-08-14 19:28:15 +0200399 public boolean isMaxExpandHeightInitialized() {
400 return mMaxExpandHeight != 0;
Selim Cinek7d447722014-06-10 15:51:59 +0200401 }
Selim Cinek2f0df8a2014-06-10 17:40:42 +0200402
403 private NotificationContentView getShowingLayout() {
404 return mShowingPublic ? mPublicLayout : mPrivateLayout;
405 }
Chris Wren78403d72014-07-28 10:23:24 +0100406
407 public void setExpansionLogger(ExpansionLogger logger, String key) {
408 mLogger = logger;
409 mLoggingKey = key;
410 }
411
412
413 private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
414 final boolean nowExpanded = isExpanded();
415 if (wasExpanded != nowExpanded && mLogger != null) {
416 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;
417 }
418 }
Chris Wren51c75102013-07-16 20:49:17 -0400419}