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