HeadsUpNotificationView.java revision 4222d9a7fb87d73e1443ec1a2de9782b05741af6
1/*
2 * Copyright (C) 2011 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.policy;
18
19import android.content.Context;
20import android.content.res.Configuration;
21import android.graphics.Rect;
22import android.util.AttributeSet;
23import android.util.Log;
24import android.view.MotionEvent;
25import android.view.View;
26import android.view.ViewConfiguration;
27import android.view.ViewGroup;
28import android.widget.FrameLayout;
29
30import com.android.systemui.ExpandHelper;
31import com.android.systemui.Gefingerpoken;
32import com.android.systemui.R;
33import com.android.systemui.SwipeHelper;
34import com.android.systemui.statusbar.BaseStatusBar;
35import com.android.systemui.statusbar.ExpandableView;
36import com.android.systemui.statusbar.NotificationData;
37
38public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback {
39    private static final String TAG = "HeadsUpNotificationView";
40    private static final boolean DEBUG = false;
41    private static final boolean SPEW = DEBUG;
42
43    Rect mTmpRect = new Rect();
44
45    private final int mTouchSensitivityDelay;
46    private SwipeHelper mSwipeHelper;
47    private EdgeSwipeHelper mEdgeSwipeHelper;
48
49    private BaseStatusBar mBar;
50    private ExpandHelper mExpandHelper;
51
52    private long mStartTouchTime;
53    private ViewGroup mContentHolder;
54
55    private NotificationData.Entry mHeadsUp;
56
57    public HeadsUpNotificationView(Context context, AttributeSet attrs) {
58        this(context, attrs, 0);
59    }
60
61    public HeadsUpNotificationView(Context context, AttributeSet attrs, int defStyle) {
62        super(context, attrs, defStyle);
63        mTouchSensitivityDelay = getResources().getInteger(R.integer.heads_up_sensitivity_delay);
64        if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
65    }
66
67    public void setBar(BaseStatusBar bar) {
68        mBar = bar;
69    }
70
71    public ViewGroup getHolder() {
72        return mContentHolder;
73    }
74
75    public boolean setNotification(NotificationData.Entry headsUp) {
76        mHeadsUp = headsUp;
77        if (mContentHolder != null) {
78            mContentHolder.removeAllViews();
79        }
80
81        if (mHeadsUp != null) {
82            mHeadsUp.row.setSystemExpanded(true);
83            mHeadsUp.row.setShowingPublic(false);
84            if (mContentHolder == null) {
85                // too soon!
86                return false;
87            }
88            mContentHolder.setX(0);
89            mContentHolder.setVisibility(View.VISIBLE);
90            mContentHolder.setAlpha(1f);
91            mContentHolder.addView(mHeadsUp.row);
92            mSwipeHelper.snapChild(mContentHolder, 1f);
93            mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay;
94        }
95        return true;
96    }
97
98    public boolean isClearable() {
99        return mHeadsUp == null || mHeadsUp.notification.isClearable();
100    }
101
102    public void setMargin(int notificationPanelMarginPx) {
103        if (SPEW) Log.v(TAG, "setMargin() " + notificationPanelMarginPx);
104        if (mContentHolder != null &&
105                mContentHolder.getLayoutParams() instanceof FrameLayout.LayoutParams) {
106            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mContentHolder.getLayoutParams();
107            lp.setMarginStart(notificationPanelMarginPx);
108            mContentHolder.setLayoutParams(lp);
109        }
110    }
111
112    // LinearLayout methods
113
114    @Override
115    public void onDraw(android.graphics.Canvas c) {
116        super.onDraw(c);
117        if (DEBUG) {
118            //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
119            //        + getMeasuredHeight() + "px");
120            c.save();
121            c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
122                    android.graphics.Region.Op.DIFFERENCE);
123            c.drawColor(0xFFcc00cc);
124            c.restore();
125        }
126    }
127
128    // ViewGroup methods
129
130    @Override
131    public void onAttachedToWindow() {
132        float densityScale = getResources().getDisplayMetrics().density;
133        final ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
134        float pagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
135        float touchSlop = viewConfiguration.getScaledTouchSlop();
136        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
137        mEdgeSwipeHelper = new EdgeSwipeHelper(touchSlop);
138
139        int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
140        int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
141        mExpandHelper = new ExpandHelper(getContext(), this, minHeight, maxHeight);
142
143        mContentHolder = (ViewGroup) findViewById(R.id.content_holder);
144
145        if (mHeadsUp != null) {
146            // whoops, we're on already!
147            setNotification(mHeadsUp);
148        }
149    }
150
151    @Override
152    public boolean onInterceptTouchEvent(MotionEvent ev) {
153        if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
154        if (System.currentTimeMillis() < mStartTouchTime) {
155            return true;
156        }
157        return mEdgeSwipeHelper.onInterceptTouchEvent(ev)
158                || mSwipeHelper.onInterceptTouchEvent(ev)
159                || mExpandHelper.onInterceptTouchEvent(ev)
160                || super.onInterceptTouchEvent(ev);
161    }
162
163    // View methods
164
165    @Override
166    public boolean onTouchEvent(MotionEvent ev) {
167        if (System.currentTimeMillis() < mStartTouchTime) {
168            return false;
169        }
170        mBar.resetHeadsUpDecayTimer();
171        return mEdgeSwipeHelper.onTouchEvent(ev)
172                || mSwipeHelper.onTouchEvent(ev)
173                || mExpandHelper.onTouchEvent(ev)
174                || super.onTouchEvent(ev);
175    }
176
177    @Override
178    protected void onConfigurationChanged(Configuration newConfig) {
179        super.onConfigurationChanged(newConfig);
180        float densityScale = getResources().getDisplayMetrics().density;
181        mSwipeHelper.setDensityScale(densityScale);
182        float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
183        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
184    }
185
186    // ExpandHelper.Callback methods
187
188    @Override
189    public ExpandableView getChildAtRawPosition(float x, float y) {
190        return getChildAtPosition(x, y);
191    }
192
193    @Override
194    public ExpandableView getChildAtPosition(float x, float y) {
195        return mHeadsUp == null ? null : mHeadsUp.row;
196    }
197
198    @Override
199    public boolean canChildBeExpanded(View v) {
200        return mHeadsUp != null && mHeadsUp.row == v && mHeadsUp.row.isExpandable();
201    }
202
203    @Override
204    public void setUserExpandedChild(View v, boolean userExpanded) {
205        if (mHeadsUp != null && mHeadsUp.row == v) {
206            mHeadsUp.row.setUserExpanded(userExpanded);
207        }
208    }
209
210    @Override
211    public void setUserLockedChild(View v, boolean userLocked) {
212        if (mHeadsUp != null && mHeadsUp.row == v) {
213            mHeadsUp.row.setUserLocked(userLocked);
214        }
215    }
216
217    // SwipeHelper.Callback methods
218
219    @Override
220    public boolean canChildBeDismissed(View v) {
221        return true;
222    }
223
224    @Override
225    public void onChildDismissed(View v) {
226        Log.v(TAG, "User swiped heads up to dismiss");
227        mBar.onHeadsUpDismissed();
228    }
229
230    @Override
231    public void onBeginDrag(View v) {
232    }
233
234    @Override
235    public void onDragCancelled(View v) {
236        mContentHolder.setAlpha(1f); // sometimes this isn't quite reset
237    }
238
239    @Override
240    public View getChildAtPosition(MotionEvent ev) {
241        return mContentHolder;
242    }
243
244    @Override
245    public View getChildContentView(View v) {
246        return mContentHolder;
247    }
248
249    private class EdgeSwipeHelper implements Gefingerpoken {
250        private static final boolean DEBUG_EDGE_SWIPE = false;
251        private final float mTouchSlop;
252        private boolean mConsuming;
253        private float mFirstY;
254        private float mFirstX;
255
256        public EdgeSwipeHelper(float touchSlop) {
257            mTouchSlop = touchSlop;
258        }
259
260        @Override
261        public boolean onInterceptTouchEvent(MotionEvent ev) {
262            switch (ev.getActionMasked()) {
263                case MotionEvent.ACTION_DOWN:
264                    if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action down " + ev.getY());
265                    mFirstX = ev.getX();
266                    mFirstY = ev.getY();
267                    mConsuming = false;
268                    break;
269
270                case MotionEvent.ACTION_MOVE:
271                    if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action move " + ev.getY());
272                    final float dY = ev.getY() - mFirstY;
273                    final float daX = Math.abs(ev.getX() - mFirstX);
274                    final float daY = Math.abs(dY);
275                    if (!mConsuming && (4f * daX) < daY && daY > mTouchSlop) {
276                        if (dY > 0) {
277                            if (DEBUG_EDGE_SWIPE) Log.d(TAG, "found an open");
278                            mBar.animateExpandNotificationsPanel();
279                        }
280                        if (dY < 0) {
281                            if (DEBUG_EDGE_SWIPE) Log.d(TAG, "found a close");
282                            mBar.onHeadsUpDismissed();
283                        }
284                        mConsuming = true;
285                    }
286                    break;
287
288                case MotionEvent.ACTION_UP:
289                case MotionEvent.ACTION_CANCEL:
290                    if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action done" );
291                    mConsuming = false;
292                    break;
293            }
294            return mConsuming;
295        }
296
297        @Override
298        public boolean onTouchEvent(MotionEvent ev) {
299            return mConsuming;
300        }
301    }
302}