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