ExpandableNotificationRow.java revision 343e6e258ab6a9f647eabebaed05ce3acafd2ff1
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.ViewGroup;
23import android.widget.FrameLayout;
24
25import com.android.internal.widget.SizeAdaptiveLayout;
26import com.android.systemui.R;
27
28public class ExpandableNotificationRow extends FrameLayout
29        implements LatestItemView.OnActivatedListener {
30    private int mRowMinHeight;
31    private int mRowMaxHeight;
32
33    /** Does this row contain layouts that can adapt to row expansion */
34    private boolean mExpandable;
35    /** Has the user actively changed the expansion state of this row */
36    private boolean mHasUserChangedExpansion;
37    /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
38    private boolean mUserExpanded;
39    /** Is the user touching this row */
40    private boolean mUserLocked;
41    /** Are we showing the "public" version */
42    private boolean mShowingPublic;
43
44    private LatestItemView mLatestItemView;
45
46    /**
47     * Is this notification expanded by the system. The expansion state can be overridden by the
48     * user expansion.
49     */
50    private boolean mIsSystemExpanded;
51    private SizeAdaptiveLayout mPublicLayout;
52    private SizeAdaptiveLayout mPrivateLayout;
53    private int mMaxExpandHeight;
54    private boolean mMaxHeightNeedsUpdate;
55    private NotificationActivator mActivator;
56    private LatestItemView.OnActivatedListener mOnActivatedListener;
57    private boolean mSelfInitiatedLayout;
58
59    public ExpandableNotificationRow(Context context, AttributeSet attrs) {
60        super(context, attrs);
61    }
62
63    @Override
64    protected void onFinishInflate() {
65        super.onFinishInflate();
66        mPublicLayout = (SizeAdaptiveLayout) findViewById(R.id.expandedPublic);
67        mPrivateLayout = (SizeAdaptiveLayout) findViewById(R.id.expanded);
68        mLatestItemView = (LatestItemView) findViewById(R.id.container);
69
70        mActivator = new NotificationActivator(this);
71        mLatestItemView.setOnActivatedListener(this);
72    }
73
74    public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
75        mRowMinHeight = rowMinHeight;
76        mRowMaxHeight = rowMaxHeight;
77        mMaxHeightNeedsUpdate = true;
78    }
79
80    public boolean isExpandable() {
81        return mExpandable;
82    }
83
84    public void setExpandable(boolean expandable) {
85        mExpandable = expandable;
86    }
87
88    /**
89     * @return whether the user has changed the expansion state
90     */
91    public boolean hasUserChangedExpansion() {
92        return mHasUserChangedExpansion;
93    }
94
95    public boolean isUserExpanded() {
96        return mUserExpanded;
97    }
98
99    /**
100     * Set this notification to be expanded by the user
101     *
102     * @param userExpanded whether the user wants this notification to be expanded
103     */
104    public void setUserExpanded(boolean userExpanded) {
105        mHasUserChangedExpansion = true;
106        mUserExpanded = userExpanded;
107    }
108
109    public boolean isUserLocked() {
110        return mUserLocked;
111    }
112
113    public void setUserLocked(boolean userLocked) {
114        mUserLocked = userLocked;
115    }
116
117    /**
118     * @return has the system set this notification to be expanded
119     */
120    public boolean isSystemExpanded() {
121        return mIsSystemExpanded;
122    }
123
124    /**
125     * Set this notification to be expanded by the system.
126     *
127     * @param expand whether the system wants this notification to be expanded.
128     */
129    public void setSystemExpanded(boolean expand) {
130        mIsSystemExpanded = expand;
131        applyExpansionToLayout(expand);
132    }
133
134    /**
135     * Apply an expansion state to the layout.
136     *
137     * @param expand should the layout be in the expanded state
138     */
139    public void applyExpansionToLayout(boolean expand) {
140        ViewGroup.LayoutParams lp = getLayoutParams();
141        if (expand && mExpandable) {
142            lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
143        } else {
144            lp.height = mRowMinHeight;
145        }
146        setLayoutParams(lp);
147    }
148
149    /**
150     * If {@link #isExpanded()} then this is the greatest possible height this view can
151     * get and otherwise it is {@link #mRowMinHeight}.
152     *
153     * @return the maximum allowed expansion height of this view.
154     */
155    public int getMaximumAllowedExpandHeight() {
156        boolean inExpansionState = isExpanded();
157        if (!inExpansionState) {
158            // not expanded, so we return the collapsed size
159            return mRowMinHeight;
160        }
161
162        return mShowingPublic ? mRowMinHeight : getMaxExpandHeight();
163    }
164
165    private void updateMaxExpandHeight() {
166
167        // We don't want this method to trigger a layout of the whole view hierarchy,
168        // as the layout parameters in the end are the same which they were in the beginning.
169        // Otherwise a loop may occur if this method is called on the layout of a parent.
170        mSelfInitiatedLayout = true;
171        ViewGroup.LayoutParams lp = getLayoutParams();
172        int oldHeight = lp.height;
173        lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
174        setLayoutParams(lp);
175        measure(View.MeasureSpec.makeMeasureSpec(getMeasuredWidth(), View.MeasureSpec.EXACTLY),
176                View.MeasureSpec.makeMeasureSpec(mRowMaxHeight, View.MeasureSpec.AT_MOST));
177        lp.height = oldHeight;
178        setLayoutParams(lp);
179        mMaxExpandHeight = getMeasuredHeight();
180        mSelfInitiatedLayout = false;
181    }
182
183    @Override
184    public void requestLayout() {
185        if (!mSelfInitiatedLayout) {
186            super.requestLayout();
187        }
188    }
189
190    /**
191     * Check whether the view state is currently expanded. This is given by the system in {@link
192     * #setSystemExpanded(boolean)} and can be overridden by user expansion or
193     * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
194     * view can differ from this state, if layout params are modified from outside.
195     *
196     * @return whether the view state is currently expanded.
197     */
198    private boolean isExpanded() {
199        return !hasUserChangedExpansion() && isSystemExpanded() || isUserExpanded();
200    }
201
202    @Override
203    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
204        super.onLayout(changed, left, top, right, bottom);
205        mMaxHeightNeedsUpdate = true;
206    }
207
208    public void setShowingPublic(boolean show) {
209        mShowingPublic = show;
210
211        // bail out if no public version
212        if (mPublicLayout.getChildCount() == 0) return;
213
214        // TODO: animation?
215        mPublicLayout.setVisibility(show ? View.VISIBLE : View.GONE);
216        mPrivateLayout.setVisibility(show ? View.GONE : View.VISIBLE);
217    }
218
219    /**
220     * Sets the notification as dimmed, meaning that it will appear in a more gray variant.
221     */
222    public void setDimmed(boolean dimmed) {
223        mLatestItemView.setDimmed(dimmed);
224        mActivator.setDimmed(dimmed);
225    }
226
227    public int getMaxExpandHeight() {
228        if (mMaxHeightNeedsUpdate) {
229            updateMaxExpandHeight();
230            mMaxHeightNeedsUpdate = false;
231        }
232        return mMaxExpandHeight;
233    }
234
235    /**
236     * Sets the notification as locked. In the locked state, the first tap will produce a quantum
237     * ripple to make the notification brighter and only the second tap will cause a click.
238     */
239    public void setLocked(boolean locked) {
240        mLatestItemView.setLocked(locked);
241    }
242
243    public void setOnActivatedListener(LatestItemView.OnActivatedListener listener) {
244        mOnActivatedListener = listener;
245    }
246
247    public NotificationActivator getActivator() {
248        return mActivator;
249    }
250
251    @Override
252    public void onActivated(View view) {
253        if (mOnActivatedListener != null) {
254            mOnActivatedListener.onActivated(this);
255        }
256    }
257
258    @Override
259    public void onReset(View view) {
260        if (mOnActivatedListener != null) {
261            mOnActivatedListener.onReset(this);
262        }
263    }
264
265    /**
266     * Sets the resource id for the background of this notification.
267     *
268     * @param bgResId The background resource to use in normal state.
269     * @param dimmedBgResId The background resource to use in dimmed state.
270     */
271    public void setBackgroundResourceIds(int bgResId, int dimmedBgResId) {
272        mLatestItemView.setBackgroundResourceIds(bgResId, dimmedBgResId);
273    }
274
275    /**
276     * @return the potential height this view could expand in addition.
277     */
278    public int getExpandPotential() {
279        return getMaximumAllowedExpandHeight() - getHeight();
280    }
281}
282