2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.statusbar;
19 import android.content.Context;
20 import android.util.AttributeSet;
21 import android.view.View;
22 import android.view.accessibility.AccessibilityEvent;
24 import com.android.systemui.R;
26 public class ExpandableNotificationRow extends ActivatableNotificationView {
27 private int mRowMinHeight;
28 private int mRowMaxHeight;
30 /** Does this row contain layouts that can adapt to row expansion */
31 private boolean mExpandable;
32 /** 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 */
35 private boolean mUserExpanded;
36 /** Is the user touching this row */
37 private boolean mUserLocked;
38 /** Are we showing the "public" version */
39 private boolean mShowingPublic;
40 private boolean mSensitive;
41 private boolean mShowingPublicInitialized;
42 private boolean mShowingPublicForIntrinsicHeight;
45 * Is this notification expanded by the system. The expansion state can be overridden by the
48 private boolean mIsSystemExpanded;
51 * Whether the notification expansion is disabled. This is the case on Keyguard.
53 private boolean mExpansionDisabled;
55 private NotificationContentView mPublicLayout;
56 private NotificationContentView mPrivateLayout;
57 private int mMaxExpandHeight;
58 private View mVetoButton;
59 private boolean mClearable;
60 private ExpansionLogger mLogger;
61 private String mLoggingKey;
62 private boolean mWasReset;
64 public interface ExpansionLogger {
65 public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
68 public ExpandableNotificationRow(Context context, AttributeSet attrs) {
69 super(context, attrs);
73 * Resets this view so it can be re-used for an updated notification.
79 final boolean wasExpanded = isExpanded();
82 mHasUserChangedExpansion = false;
84 mShowingPublic = false;
86 mShowingPublicInitialized = false;
87 mIsSystemExpanded = false;
88 mExpansionDisabled = false;
89 mPublicLayout.reset();
90 mPrivateLayout.reset();
93 logExpansionEvent(false, wasExpanded);
97 protected void onFinishInflate() {
98 super.onFinishInflate();
99 mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
100 mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
101 mVetoButton = findViewById(R.id.veto);
105 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
106 if (super.onRequestSendAccessibilityEvent(child, event)) {
107 // Add a record for the entire layout since its content is somehow small.
108 // The event comes from a leaf view that is interacted with.
109 AccessibilityEvent record = AccessibilityEvent.obtain();
110 onInitializeAccessibilityEvent(record);
111 dispatchPopulateAccessibilityEvent(record);
112 event.appendRecord(record);
118 public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
119 mRowMinHeight = rowMinHeight;
120 mRowMaxHeight = rowMaxHeight;
123 public boolean isExpandable() {
127 public void setExpandable(boolean expandable) {
128 mExpandable = expandable;
132 * @return whether the user has changed the expansion state
134 public boolean hasUserChangedExpansion() {
135 return mHasUserChangedExpansion;
138 public boolean isUserExpanded() {
139 return mUserExpanded;
143 * Set this notification to be expanded by the user
145 * @param userExpanded whether the user wants this notification to be expanded
147 public void setUserExpanded(boolean userExpanded) {
148 if (userExpanded && !mExpandable) return;
149 final boolean wasExpanded = isExpanded();
150 mHasUserChangedExpansion = true;
151 mUserExpanded = userExpanded;
152 logExpansionEvent(true, wasExpanded);
155 public void resetUserExpansion() {
156 mHasUserChangedExpansion = false;
157 mUserExpanded = false;
160 public boolean isUserLocked() {
164 public void setUserLocked(boolean userLocked) {
165 mUserLocked = userLocked;
169 * @return has the system set this notification to be expanded
171 public boolean isSystemExpanded() {
172 return mIsSystemExpanded;
176 * Set this notification to be expanded by the system.
178 * @param expand whether the system wants this notification to be expanded.
180 public void setSystemExpanded(boolean expand) {
181 final boolean wasExpanded = isExpanded();
182 mIsSystemExpanded = expand;
183 notifyHeightChanged();
184 logExpansionEvent(false, wasExpanded);
188 * @param expansionDisabled whether to prevent notification expansion
190 public void setExpansionDisabled(boolean expansionDisabled) {
191 final boolean wasExpanded = isExpanded();
192 mExpansionDisabled = expansionDisabled;
193 logExpansionEvent(false, wasExpanded);
194 notifyHeightChanged();
198 * @return Can the underlying notification be cleared?
200 public boolean isClearable() {
205 * Set whether the notification can be cleared.
209 public void setClearable(boolean clearable) {
210 mClearable = clearable;
215 * Apply an expansion state to the layout.
217 public void applyExpansionToLayout() {
218 boolean expand = isExpanded();
219 if (expand && mExpandable) {
220 setActualHeight(mMaxExpandHeight);
222 setActualHeight(mRowMinHeight);
227 public int getIntrinsicHeight() {
228 if (isUserLocked()) {
229 return getActualHeight();
231 boolean inExpansionState = isExpanded();
232 if (!inExpansionState) {
233 // not expanded, so we return the collapsed size
234 return mRowMinHeight;
237 return mShowingPublicForIntrinsicHeight ? mRowMinHeight : getMaxExpandHeight();
241 * Check whether the view state is currently expanded. This is given by the system in {@link
242 * #setSystemExpanded(boolean)} and can be overridden by user expansion or
243 * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
244 * view can differ from this state, if layout params are modified from outside.
246 * @return whether the view state is currently expanded.
248 private boolean isExpanded() {
249 return !mExpansionDisabled
250 && (!hasUserChangedExpansion() && isSystemExpanded() || isUserExpanded());
254 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
255 super.onLayout(changed, left, top, right, bottom);
256 boolean updateExpandHeight = mMaxExpandHeight == 0 && !mWasReset;
257 mMaxExpandHeight = mPrivateLayout.getMaxHeight();
258 if (updateExpandHeight) {
259 applyExpansionToLayout();
264 public void setSensitive(boolean sensitive) {
265 mSensitive = sensitive;
268 public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
269 mShowingPublicForIntrinsicHeight = mSensitive && hideSensitive;
272 public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
274 boolean oldShowingPublic = mShowingPublic;
275 mShowingPublic = mSensitive && hideSensitive;
276 if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
280 // bail out if no public version
281 if (mPublicLayout.getChildCount() == 0) return;
284 mPublicLayout.animate().cancel();
285 mPrivateLayout.animate().cancel();
286 mPublicLayout.setAlpha(1f);
287 mPrivateLayout.setAlpha(1f);
288 mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
289 mPrivateLayout.setVisibility(mShowingPublic ? View.INVISIBLE : View.VISIBLE);
291 animateShowingPublic(delay, duration);
295 mShowingPublicInitialized = true;
298 private void animateShowingPublic(long delay, long duration) {
299 final View source = mShowingPublic ? mPrivateLayout : mPublicLayout;
300 View target = mShowingPublic ? mPublicLayout : mPrivateLayout;
301 source.setVisibility(View.VISIBLE);
302 target.setVisibility(View.VISIBLE);
304 source.animate().cancel();
305 target.animate().cancel();
309 .setStartDelay(delay)
310 .setDuration(duration)
311 .withEndAction(new Runnable() {
314 source.setVisibility(View.INVISIBLE);
320 .setStartDelay(delay)
321 .setDuration(duration);
324 private void updateVetoButton() {
325 // public versions cannot be dismissed
326 mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
329 public int getMaxExpandHeight() {
330 return mShowingPublicForIntrinsicHeight ? mRowMinHeight : mMaxExpandHeight;
334 public boolean isContentExpandable() {
335 NotificationContentView showingLayout = getShowingLayout();
336 return showingLayout.isContentExpandable();
340 public void setActualHeight(int height, boolean notifyListeners) {
341 mPrivateLayout.setActualHeight(height);
342 mPublicLayout.setActualHeight(height);
344 super.setActualHeight(height, notifyListeners);
348 public int getMaxHeight() {
349 NotificationContentView showingLayout = getShowingLayout();
350 return showingLayout.getMaxHeight();
354 public int getMinHeight() {
355 NotificationContentView showingLayout = getShowingLayout();
356 return showingLayout.getMinHeight();
360 public void setClipTopAmount(int clipTopAmount) {
361 super.setClipTopAmount(clipTopAmount);
362 mPrivateLayout.setClipTopAmount(clipTopAmount);
363 mPublicLayout.setClipTopAmount(clipTopAmount);
366 public void notifyContentUpdated() {
367 mPublicLayout.notifyContentUpdated();
368 mPrivateLayout.notifyContentUpdated();
371 public boolean isShowingLayoutLayouted() {
372 NotificationContentView showingLayout = getShowingLayout();
373 return showingLayout.getWidth() != 0;
376 private NotificationContentView getShowingLayout() {
377 return mShowingPublic ? mPublicLayout : mPrivateLayout;
380 public void setExpansionLogger(ExpansionLogger logger, String key) {
386 private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
387 final boolean nowExpanded = isExpanded();
388 if (wasExpanded != nowExpanded && mLogger != null) {
389 mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;