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}