ExpandableNotificationRow.java revision 22f2ee567dd1b1a42432251229bcb2f05c1c4700
1/*
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;
21import android.view.View;
22import android.view.accessibility.AccessibilityEvent;
23
24import com.android.systemui.R;
25
26public class ExpandableNotificationRow extends ActivatableNotificationView {
27    private int mRowMinHeight;
28    private int mRowMaxHeight;
29
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
41    /**
42     * Is this notification expanded by the system. The expansion state can be overridden by the
43     * user expansion.
44     */
45    private boolean mIsSystemExpanded;
46
47    /**
48     * Whether the notification expansion is disabled. This is the case on Keyguard.
49     */
50    private boolean mExpansionDisabled;
51
52    private NotificationContentView mPublicLayout;
53    private NotificationContentView mPrivateLayout;
54    private int mMaxExpandHeight;
55    private View mVetoButton;
56    private boolean mClearable;
57
58    public ExpandableNotificationRow(Context context, AttributeSet attrs) {
59        super(context, attrs);
60    }
61
62    /**
63     * Resets this view so it can be re-used for an updated notification.
64     */
65    @Override
66    public void reset() {
67        super.reset();
68        mRowMinHeight = 0;
69        mRowMaxHeight = 0;
70        mExpandable = false;
71        mHasUserChangedExpansion = false;
72        mUserLocked = false;
73        mShowingPublic = false;
74        mIsSystemExpanded = false;
75        mExpansionDisabled = false;
76        mPublicLayout.reset();
77        mPrivateLayout.reset();
78        mMaxExpandHeight = 0;
79    }
80
81    @Override
82    protected void onFinishInflate() {
83        super.onFinishInflate();
84        mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
85        mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
86        mVetoButton = findViewById(R.id.veto);
87    }
88
89    @Override
90    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
91        if (super.onRequestSendAccessibilityEvent(child, event)) {
92            // Add a record for the entire layout since its content is somehow small.
93            // The event comes from a leaf view that is interacted with.
94            AccessibilityEvent record = AccessibilityEvent.obtain();
95            onInitializeAccessibilityEvent(record);
96            dispatchPopulateAccessibilityEvent(record);
97            event.appendRecord(record);
98            return true;
99        }
100        return false;
101    }
102
103    public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
104        mRowMinHeight = rowMinHeight;
105        mRowMaxHeight = rowMaxHeight;
106    }
107
108    public boolean isExpandable() {
109        return mExpandable;
110    }
111
112    public void setExpandable(boolean expandable) {
113        mExpandable = expandable;
114    }
115
116    /**
117     * @return whether the user has changed the expansion state
118     */
119    public boolean hasUserChangedExpansion() {
120        return mHasUserChangedExpansion;
121    }
122
123    public boolean isUserExpanded() {
124        return mUserExpanded;
125    }
126
127    /**
128     * Set this notification to be expanded by the user
129     *
130     * @param userExpanded whether the user wants this notification to be expanded
131     */
132    public void setUserExpanded(boolean userExpanded) {
133        if (userExpanded && !mExpandable) return;
134        mHasUserChangedExpansion = true;
135        mUserExpanded = userExpanded;
136    }
137
138    public boolean isUserLocked() {
139        return mUserLocked;
140    }
141
142    public void setUserLocked(boolean userLocked) {
143        mUserLocked = userLocked;
144    }
145
146    /**
147     * @return has the system set this notification to be expanded
148     */
149    public boolean isSystemExpanded() {
150        return mIsSystemExpanded;
151    }
152
153    /**
154     * Set this notification to be expanded by the system.
155     *
156     * @param expand whether the system wants this notification to be expanded.
157     */
158    public void setSystemExpanded(boolean expand) {
159        mIsSystemExpanded = expand;
160        notifyHeightChanged();
161    }
162
163    /**
164     * @param expansionDisabled whether to prevent notification expansion
165     */
166    public void setExpansionDisabled(boolean expansionDisabled) {
167        mExpansionDisabled = expansionDisabled;
168        notifyHeightChanged();
169    }
170
171    /**
172     * @return Can the underlying notification be cleared?
173     */
174    public boolean isClearable() {
175        return mClearable;
176    }
177
178    /**
179     * Set whether the notification can be cleared.
180     *
181     * @param clearable
182     */
183    public void setClearable(boolean clearable) {
184        mClearable = clearable;
185        updateVetoButton();
186    }
187
188    /**
189     * Apply an expansion state to the layout.
190     */
191    public void applyExpansionToLayout() {
192        boolean expand = isExpanded();
193        if (expand && mExpandable) {
194            setActualHeight(mMaxExpandHeight);
195        } else {
196            setActualHeight(mRowMinHeight);
197        }
198    }
199
200    @Override
201    public int getIntrinsicHeight() {
202        if (isUserLocked()) {
203            return getActualHeight();
204        }
205        boolean inExpansionState = isExpanded();
206        if (!inExpansionState) {
207            // not expanded, so we return the collapsed size
208            return mRowMinHeight;
209        }
210
211        return mShowingPublic ? mRowMinHeight : getMaxExpandHeight();
212    }
213
214    /**
215     * Check whether the view state is currently expanded. This is given by the system in {@link
216     * #setSystemExpanded(boolean)} and can be overridden by user expansion or
217     * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
218     * view can differ from this state, if layout params are modified from outside.
219     *
220     * @return whether the view state is currently expanded.
221     */
222    private boolean isExpanded() {
223        return !mExpansionDisabled
224                && (!hasUserChangedExpansion() && isSystemExpanded() || isUserExpanded());
225    }
226
227    @Override
228    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
229        super.onLayout(changed, left, top, right, bottom);
230        boolean updateExpandHeight = mMaxExpandHeight == 0;
231        mMaxExpandHeight = mPrivateLayout.getMaxHeight();
232        if (updateExpandHeight) {
233            applyExpansionToLayout();
234        }
235    }
236
237    public void setShowingPublic(boolean show) {
238        mShowingPublic = show;
239
240        // bail out if no public version
241        if (mPublicLayout.getChildCount() == 0) return;
242
243        // TODO: animation?
244        mPublicLayout.setVisibility(show ? View.VISIBLE : View.GONE);
245        mPrivateLayout.setVisibility(show ? View.GONE : View.VISIBLE);
246
247        updateVetoButton();
248    }
249
250    private void updateVetoButton() {
251        // public versions cannot be dismissed
252        mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
253    }
254
255    public int getMaxExpandHeight() {
256        return mShowingPublic ? mRowMinHeight : mMaxExpandHeight;
257    }
258
259    @Override
260    public boolean isContentExpandable() {
261        NotificationContentView showingLayout = getShowingLayout();
262        return showingLayout.isContentExpandable();
263    }
264
265    @Override
266    public void setActualHeight(int height, boolean notifyListeners) {
267        mPrivateLayout.setActualHeight(height);
268        mPublicLayout.setActualHeight(height);
269        invalidate();
270        super.setActualHeight(height, notifyListeners);
271    }
272
273    @Override
274    public int getMaxHeight() {
275        NotificationContentView showingLayout = getShowingLayout();
276        return showingLayout.getMaxHeight();
277    }
278
279    @Override
280    public int getMinHeight() {
281        NotificationContentView showingLayout = getShowingLayout();
282        return showingLayout.getMinHeight();
283    }
284
285    @Override
286    public void setClipTopAmount(int clipTopAmount) {
287        super.setClipTopAmount(clipTopAmount);
288        mPrivateLayout.setClipTopAmount(clipTopAmount);
289        mPublicLayout.setClipTopAmount(clipTopAmount);
290    }
291
292    public void notifyContentUpdated() {
293        mPublicLayout.notifyContentUpdated();
294        mPrivateLayout.notifyContentUpdated();
295    }
296
297    public boolean isShowingLayoutLayouted() {
298        NotificationContentView showingLayout = getShowingLayout();
299        return showingLayout.getWidth() != 0;
300    }
301
302    private NotificationContentView getShowingLayout() {
303        return mShowingPublic ? mPublicLayout : mPrivateLayout;
304    }
305}
306