PanelView.java revision 08d05e3d1d6ade6924266296033981a96b47d5fb
1package com.android.systemui.statusbar.phone; 2 3import android.animation.TimeAnimator; 4import android.animation.TimeAnimator.TimeListener; 5import android.content.Context; 6import android.content.res.Resources; 7import android.util.AttributeSet; 8import android.util.Log; 9import android.view.MotionEvent; 10import android.view.VelocityTracker; 11import android.view.View; 12import android.widget.FrameLayout; 13 14import com.android.systemui.R; 15 16public class PanelView extends FrameLayout { 17 public static final boolean DEBUG = true; 18 public static final String TAG = PanelView.class.getSimpleName(); 19 public static final void LOG(String fmt, Object... args) { 20 if (!DEBUG) return; 21 Log.v(TAG, String.format(fmt, args)); 22 } 23 24 public static final boolean BRAKES = false; 25 26 private float mSelfExpandVelocityPx; // classic value: 2000px/s 27 private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up") 28 private float mFlingExpandMinVelocityPx; // classic value: 200px/s 29 private float mFlingCollapseMinVelocityPx; // classic value: 200px/s 30 private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1) 31 private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand) 32 private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s 33 34 private float mExpandAccelPx; // classic value: 2000px/s/s 35 private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up") 36 37 private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little 38 // faster than mSelfCollapseVelocityPx) 39 40 private float mCollapseBrakingDistancePx = 200; // XXX Resource 41 private float mExpandBrakingDistancePx = 150; // XXX Resource 42 private float mBrakingSpeedPx = 150; // XXX Resource 43 44 private View mHandleView; 45 private float mTouchOffset; 46 private float mExpandedFraction = 0; 47 private float mExpandedHeight = 0; 48 49 private TimeAnimator mTimeAnimator; 50 private VelocityTracker mVelocityTracker; 51 52 private int[] mAbsPos = new int[2]; 53 PanelBar mBar; 54 55 private final TimeListener mAnimationCallback = new TimeListener() { 56 @Override 57 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { 58 animationTick(deltaTime); 59 } 60 }; 61 62 private float mVel, mAccel; 63 private int mFullHeight = 0; 64 65 private void animationTick(long dtms) { 66 if (!mTimeAnimator.isStarted()) { 67 // XXX HAX to work around bug in TimeAnimator.end() not resetting its last time 68 mTimeAnimator = new TimeAnimator(); 69 mTimeAnimator.setTimeListener(mAnimationCallback); 70 71 mTimeAnimator.start(); 72 } else { 73 final float dt = dtms * 0.001f; // ms -> s 74 LOG("tick: v=%.2fpx/s dt=%.4fs", mVel, dt); 75 LOG("tick: before: h=%d", (int) mExpandedHeight); 76 77 final float fh = getFullHeight(); 78 final boolean closing = mExpandedHeight > 0 && mVel < 0; 79 boolean braking = false; 80 if (BRAKES) { 81 if (closing) { 82 braking = mExpandedHeight <= mCollapseBrakingDistancePx; 83 mAccel = braking ? 10*mCollapseAccelPx : -mCollapseAccelPx; 84 } else { 85 braking = mExpandedHeight >= (fh-mExpandBrakingDistancePx); 86 mAccel = braking ? 10*-mExpandAccelPx : mExpandAccelPx; 87 } 88 } else { 89 mAccel = closing ? -mCollapseAccelPx : mExpandAccelPx; 90 } 91 92 mVel += mAccel * dt; 93 94 if (braking) { 95 if (closing && mVel > -mBrakingSpeedPx) { 96 mVel = -mBrakingSpeedPx; 97 } else if (!closing && mVel < mBrakingSpeedPx) { 98 mVel = mBrakingSpeedPx; 99 } 100 } else { 101 if (closing && mVel > -mFlingCollapseMinVelocityPx) { 102 mVel = -mFlingCollapseMinVelocityPx; 103 } else if (!closing && mVel > mFlingGestureMaxOutputVelocityPx) { 104 mVel = mFlingGestureMaxOutputVelocityPx; 105 } 106 } 107 108 float h = mExpandedHeight + mVel * dt; 109 110 LOG("tick: new h=%d closing=%s", (int) h, closing?"true":"false"); 111 112 setExpandedHeightInternal(h); 113 114 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); 115 116 if (mVel == 0 117 || (closing && mExpandedHeight == 0) 118 || (!closing && mExpandedHeight == getFullHeight())) { 119 mTimeAnimator.end(); 120 } 121 } 122 } 123 124 public PanelView(Context context, AttributeSet attrs) { 125 super(context, attrs); 126 127 mTimeAnimator = new TimeAnimator(); 128 mTimeAnimator.setTimeListener(mAnimationCallback); 129 } 130 131 private void loadDimens() { 132 final Resources res = getContext().getResources(); 133 134 mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity); 135 mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity); 136 mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity); 137 mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity); 138 139 mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1); 140 mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1); 141 142 mExpandAccelPx = res.getDimension(R.dimen.expand_accel); 143 mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel); 144 145 mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity); 146 147 mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity); 148 } 149 150 private void trackMovement(MotionEvent event) { 151 // Add movement to velocity tracker using raw screen X and Y coordinates instead 152 // of window coordinates because the window frame may be moving at the same time. 153 float deltaX = event.getRawX() - event.getX(); 154 float deltaY = event.getRawY() - event.getY(); 155 event.offsetLocation(deltaX, deltaY); 156 mVelocityTracker.addMovement(event); 157 event.offsetLocation(-deltaX, -deltaY); 158 } 159 160 @Override 161 protected void onFinishInflate() { 162 super.onFinishInflate(); 163 loadDimens(); 164 165 mHandleView = findViewById(R.id.handle); 166 LOG("handle view: " + mHandleView); 167 if (mHandleView != null) { 168 mHandleView.setOnTouchListener(new View.OnTouchListener() { 169 @Override 170 public boolean onTouch(View v, MotionEvent event) { 171 final float y = event.getY(); 172 final float rawY = event.getRawY(); 173 LOG("handle.onTouch: y=%.1f rawY=%.1f off=%.1f", y, rawY, mTouchOffset); 174 PanelView.this.getLocationOnScreen(mAbsPos); 175 176 switch (event.getAction()) { 177 case MotionEvent.ACTION_DOWN: 178 mVelocityTracker = VelocityTracker.obtain(); 179 trackMovement(event); 180 mTouchOffset = (rawY - mAbsPos[1]) - PanelView.this.getExpandedHeight(); 181 break; 182 183 case MotionEvent.ACTION_MOVE: 184 PanelView.this.setExpandedHeight(rawY - mAbsPos[1] - mTouchOffset); 185 186 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); 187 188 trackMovement(event); 189 break; 190 191 case MotionEvent.ACTION_UP: 192 case MotionEvent.ACTION_CANCEL: 193 trackMovement(event); 194 mVelocityTracker.computeCurrentVelocity(1000); 195 196 float yVel = mVelocityTracker.getYVelocity(); 197 boolean negative = yVel < 0; 198 199 float xVel = mVelocityTracker.getXVelocity(); 200 if (xVel < 0) { 201 xVel = -xVel; 202 } 203 if (xVel > mFlingGestureMaxXVelocityPx) { 204 xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis 205 } 206 207 float vel = (float)Math.hypot(yVel, xVel); 208 if (vel > mFlingGestureMaxOutputVelocityPx) { 209 vel = mFlingGestureMaxOutputVelocityPx; 210 } 211 if (negative) { 212 vel = -vel; 213 } 214 215 LOG("gesture: vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f", 216 mVelocityTracker.getXVelocity(), 217 mVelocityTracker.getYVelocity(), 218 xVel, yVel, 219 vel); 220 221 fling(vel, false); 222 223 mVelocityTracker.recycle(); 224 mVelocityTracker = null; 225 226 break; 227 } 228 return true; 229 }}); 230 } 231 } 232 233 public void fling(float vel, boolean always) { 234 mVel = vel; 235 236 if (mVel != 0) { 237 animationTick(0); // begin the animation 238 } 239 } 240 241 @Override 242 protected void onAttachedToWindow() { 243 super.onAttachedToWindow(); 244 } 245 246 @Override 247 protected void onViewAdded(View child) { 248 LOG("onViewAdded: " + child); 249 } 250 251 public View getHandle() { 252 return mHandleView; 253 } 254 255 // Rubberbands the panel to hold its contents. 256 @Override 257 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 258 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 259 260 LOG("onMeasure(%d, %d) -> (%d, %d)", 261 widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight()); 262 mFullHeight = getMeasuredHeight(); 263 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 264 (int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec)); 265 setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); 266 } 267 268 269 public void setExpandedHeight(float height) { 270 mTimeAnimator.end(); 271 setExpandedHeightInternal(height); 272 } 273 274 public void setExpandedHeightInternal(float h) { 275 float fh = getFullHeight(); 276 if (fh == 0) { 277 // Hmm, full height hasn't been computed yet 278 } 279 280 LOG("setExpansion: height=%.1f fh=%.1f", h, fh); 281 282 if (h < 0) h = 0; 283 else if (h > fh) h = fh; 284 285 mExpandedHeight = h; 286 287 requestLayout(); 288// FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); 289// lp.height = (int) mExpandedHeight; 290// setLayoutParams(lp); 291 292 mExpandedFraction = Math.min(1f, h / fh); 293 } 294 295 private float getFullHeight() { 296 return mFullHeight; 297 } 298 299 public void setExpandedFraction(float frac) { 300 setExpandedHeight(getFullHeight() * frac); 301 } 302 303 public float getExpandedHeight() { 304 return mExpandedHeight; 305 } 306 307 public float getExpandedFraction() { 308 return mExpandedFraction; 309 } 310 311 public void setBar(PanelBar panelBar) { 312 mBar = panelBar; 313 } 314 315 public void collapse() { 316 // TODO: abort animation or ongoing touch 317 if (mExpandedHeight > 0) { 318 fling(-mSelfCollapseVelocityPx, /*always=*/ true); 319 } 320 } 321 322 public void expand() { 323 if (mExpandedHeight < getFullHeight()) { 324 fling (mSelfExpandVelocityPx, /*always=*/ true); 325 } 326 } 327} 328