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