blob: a8a92ecd14aa1140feefa7c2fea8706318da60e4 [file] [log] [blame]
Daniel Sandler08d05e32012-08-08 16:39:54 -04001package com.android.systemui.statusbar.phone;
2
Daniel Sandler0c1b75c2012-10-04 12:08:54 -04003import android.animation.ObjectAnimator;
Daniel Sandler08d05e32012-08-08 16:39:54 -04004import android.animation.TimeAnimator;
5import android.animation.TimeAnimator.TimeListener;
6import android.content.Context;
7import android.content.res.Resources;
8import android.util.AttributeSet;
Daniel Sandlerbf526d12012-09-04 22:56:44 -04009import android.util.Slog;
Daniel Sandler08d05e32012-08-08 16:39:54 -040010import android.view.MotionEvent;
11import android.view.VelocityTracker;
12import android.view.View;
13import android.widget.FrameLayout;
14
15import com.android.systemui.R;
16
17public class PanelView extends FrameLayout {
Daniel Sandler198a0302012-08-17 16:04:31 -040018 public static final boolean DEBUG = PanelBar.DEBUG;
Daniel Sandler08d05e32012-08-08 16:39:54 -040019 public static final String TAG = PanelView.class.getSimpleName();
Daniel Sandler978f8532012-08-15 15:48:16 -040020 public final void LOG(String fmt, Object... args) {
Daniel Sandler08d05e32012-08-08 16:39:54 -040021 if (!DEBUG) return;
Daniel Sandlerbf526d12012-09-04 22:56:44 -040022 Slog.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
Daniel Sandler08d05e32012-08-08 16:39:54 -040023 }
24
25 public static final boolean BRAKES = false;
Daniel Sandler50508132012-08-16 14:10:53 -040026 private static final boolean STRETCH_PAST_CONTENTS = true;
Daniel Sandler08d05e32012-08-08 16:39:54 -040027
28 private float mSelfExpandVelocityPx; // classic value: 2000px/s
29 private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
30 private float mFlingExpandMinVelocityPx; // classic value: 200px/s
31 private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
32 private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
33 private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
34 private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
35
Daniel Sandler173bae22012-09-25 14:37:42 -040036 private float mFlingGestureMinDistPx;
37
Daniel Sandler08d05e32012-08-08 16:39:54 -040038 private float mExpandAccelPx; // classic value: 2000px/s/s
39 private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
40
41 private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
42 // faster than mSelfCollapseVelocityPx)
43
44 private float mCollapseBrakingDistancePx = 200; // XXX Resource
45 private float mExpandBrakingDistancePx = 150; // XXX Resource
46 private float mBrakingSpeedPx = 150; // XXX Resource
47
48 private View mHandleView;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040049 private float mPeekHeight;
Daniel Sandler08d05e32012-08-08 16:39:54 -040050 private float mTouchOffset;
51 private float mExpandedFraction = 0;
52 private float mExpandedHeight = 0;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040053 private boolean mJustPeeked;
Daniel Sandler50508132012-08-16 14:10:53 -040054 private boolean mClosing;
55 private boolean mRubberbanding;
56 private boolean mTracking;
Daniel Sandler08d05e32012-08-08 16:39:54 -040057
58 private TimeAnimator mTimeAnimator;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040059 private ObjectAnimator mPeekAnimator;
Daniel Sandler08d05e32012-08-08 16:39:54 -040060 private VelocityTracker mVelocityTracker;
61
62 private int[] mAbsPos = new int[2];
63 PanelBar mBar;
64
65 private final TimeListener mAnimationCallback = new TimeListener() {
66 @Override
67 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
68 animationTick(deltaTime);
69 }
70 };
71
Daniel Sandler67eab792012-10-02 17:08:23 -040072 private final Runnable mStopAnimator = new Runnable() {
73 @Override
74 public void run() {
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040075 if (mTimeAnimator != null && mTimeAnimator.isStarted()) {
Daniel Sandler67eab792012-10-02 17:08:23 -040076 mTimeAnimator.end();
77 mRubberbanding = false;
78 mClosing = false;
79 }
Daniel Sandler50508132012-08-16 14:10:53 -040080 }
Daniel Sandler67eab792012-10-02 17:08:23 -040081 };
Daniel Sandler5a35a0d2012-08-16 13:50:40 -040082
Daniel Sandler08d05e32012-08-08 16:39:54 -040083 private float mVel, mAccel;
84 private int mFullHeight = 0;
Daniel Sandler50508132012-08-16 14:10:53 -040085 private String mViewName;
Daniel Sandler173bae22012-09-25 14:37:42 -040086 protected float mInitialTouchY;
87 protected float mFinalTouchY;
Daniel Sandler08d05e32012-08-08 16:39:54 -040088
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040089 private void runPeekAnimation() {
90 if (DEBUG) LOG("peek to height=%.1f", mPeekHeight);
91 if (mTimeAnimator.isStarted()) {
92 return;
93 }
94 if (mPeekAnimator == null) {
95 mPeekAnimator = ObjectAnimator.ofFloat(this,
96 "expandedHeight", mPeekHeight)
97 .setDuration(300);
98 }
99 mPeekAnimator.start();
100 }
101
Daniel Sandler08d05e32012-08-08 16:39:54 -0400102 private void animationTick(long dtms) {
103 if (!mTimeAnimator.isStarted()) {
104 // XXX HAX to work around bug in TimeAnimator.end() not resetting its last time
105 mTimeAnimator = new TimeAnimator();
106 mTimeAnimator.setTimeListener(mAnimationCallback);
107
Daniel Sandlera801f682012-10-05 11:01:05 -0400108 if (mPeekAnimator != null) mPeekAnimator.cancel();
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400109
Daniel Sandler08d05e32012-08-08 16:39:54 -0400110 mTimeAnimator.start();
Daniel Sandler67eab792012-10-02 17:08:23 -0400111
112 mRubberbanding = STRETCH_PAST_CONTENTS // is it enabled at all?
113 && mExpandedHeight > getFullHeight() // are we past the end?
114 && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture?
Daniel Sandler173bae22012-09-25 14:37:42 -0400115 if (mRubberbanding) {
116 mClosing = true;
117 } else if (mVel == 0) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400118 // if the panel is less than halfway open, close it
Daniel Sandler173bae22012-09-25 14:37:42 -0400119 mClosing = (mFinalTouchY / getFullHeight()) < 0.5f;
120 } else {
121 mClosing = mExpandedHeight > 0 && mVel < 0;
122 }
Daniel Sandler978f8532012-08-15 15:48:16 -0400123 } else if (dtms > 0) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400124 final float dt = dtms * 0.001f; // ms -> s
Daniel Sandler67eab792012-10-02 17:08:23 -0400125 if (DEBUG) LOG("tick: v=%.2fpx/s dt=%.4fs", mVel, dt);
126 if (DEBUG) LOG("tick: before: h=%d", (int) mExpandedHeight);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400127
128 final float fh = getFullHeight();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400129 boolean braking = false;
130 if (BRAKES) {
Daniel Sandler50508132012-08-16 14:10:53 -0400131 if (mClosing) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400132 braking = mExpandedHeight <= mCollapseBrakingDistancePx;
133 mAccel = braking ? 10*mCollapseAccelPx : -mCollapseAccelPx;
134 } else {
135 braking = mExpandedHeight >= (fh-mExpandBrakingDistancePx);
136 mAccel = braking ? 10*-mExpandAccelPx : mExpandAccelPx;
137 }
138 } else {
Daniel Sandler50508132012-08-16 14:10:53 -0400139 mAccel = mClosing ? -mCollapseAccelPx : mExpandAccelPx;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400140 }
141
142 mVel += mAccel * dt;
143
144 if (braking) {
Daniel Sandler50508132012-08-16 14:10:53 -0400145 if (mClosing && mVel > -mBrakingSpeedPx) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400146 mVel = -mBrakingSpeedPx;
Daniel Sandler50508132012-08-16 14:10:53 -0400147 } else if (!mClosing && mVel < mBrakingSpeedPx) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400148 mVel = mBrakingSpeedPx;
149 }
150 } else {
Daniel Sandler50508132012-08-16 14:10:53 -0400151 if (mClosing && mVel > -mFlingCollapseMinVelocityPx) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400152 mVel = -mFlingCollapseMinVelocityPx;
Daniel Sandler50508132012-08-16 14:10:53 -0400153 } else if (!mClosing && mVel > mFlingGestureMaxOutputVelocityPx) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400154 mVel = mFlingGestureMaxOutputVelocityPx;
155 }
156 }
157
158 float h = mExpandedHeight + mVel * dt;
Daniel Sandler67eab792012-10-02 17:08:23 -0400159
Daniel Sandler50508132012-08-16 14:10:53 -0400160 if (mRubberbanding && h < fh) {
161 h = fh;
162 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400163
Daniel Sandler67eab792012-10-02 17:08:23 -0400164 if (DEBUG) LOG("tick: new h=%d closing=%s", (int) h, mClosing?"true":"false");
Daniel Sandler08d05e32012-08-08 16:39:54 -0400165
166 setExpandedHeightInternal(h);
167
168 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
169
170 if (mVel == 0
Daniel Sandler50508132012-08-16 14:10:53 -0400171 || (mClosing && mExpandedHeight == 0)
172 || ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) {
Daniel Sandler5a35a0d2012-08-16 13:50:40 -0400173 post(mStopAnimator);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400174 }
175 }
176 }
177
178 public PanelView(Context context, AttributeSet attrs) {
179 super(context, attrs);
180
181 mTimeAnimator = new TimeAnimator();
182 mTimeAnimator.setTimeListener(mAnimationCallback);
183 }
184
185 private void loadDimens() {
186 final Resources res = getContext().getResources();
187
188 mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
189 mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
190 mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
191 mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
192
Daniel Sandler173bae22012-09-25 14:37:42 -0400193 mFlingGestureMinDistPx = res.getDimension(R.dimen.fling_gesture_min_dist);
194
Daniel Sandler08d05e32012-08-08 16:39:54 -0400195 mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
196 mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
197
198 mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
199 mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
200
201 mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
202
203 mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400204
205 mPeekHeight = res.getDimension(R.dimen.close_handle_height)
206 + getPaddingBottom() // our window might have a dropshadow
207 - (mHandleView == null ? 0 : mHandleView.getPaddingTop()); // the handle might have a topshadow
Daniel Sandler08d05e32012-08-08 16:39:54 -0400208 }
209
210 private void trackMovement(MotionEvent event) {
211 // Add movement to velocity tracker using raw screen X and Y coordinates instead
212 // of window coordinates because the window frame may be moving at the same time.
213 float deltaX = event.getRawX() - event.getX();
214 float deltaY = event.getRawY() - event.getY();
215 event.offsetLocation(deltaX, deltaY);
216 mVelocityTracker.addMovement(event);
217 event.offsetLocation(-deltaX, -deltaY);
218 }
219
Daniel Sandlerbf526d12012-09-04 22:56:44 -0400220 // Pass all touches along to the handle, allowing the user to drag the panel closed from its interior
221 @Override
222 public boolean onTouchEvent(MotionEvent event) {
223 return mHandleView.dispatchTouchEvent(event);
224 }
225
Daniel Sandler08d05e32012-08-08 16:39:54 -0400226 @Override
227 protected void onFinishInflate() {
228 super.onFinishInflate();
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400229 mHandleView = findViewById(R.id.handle);
230
Daniel Sandler08d05e32012-08-08 16:39:54 -0400231 loadDimens();
232
Daniel Sandler67eab792012-10-02 17:08:23 -0400233 if (DEBUG) LOG("handle view: " + mHandleView);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400234 if (mHandleView != null) {
235 mHandleView.setOnTouchListener(new View.OnTouchListener() {
236 @Override
237 public boolean onTouch(View v, MotionEvent event) {
238 final float y = event.getY();
239 final float rawY = event.getRawY();
Daniel Sandler67eab792012-10-02 17:08:23 -0400240 if (DEBUG) LOG("handle.onTouch: a=%s y=%.1f rawY=%.1f off=%.1f",
Daniel Sandler978f8532012-08-15 15:48:16 -0400241 MotionEvent.actionToString(event.getAction()),
242 y, rawY, mTouchOffset);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400243 PanelView.this.getLocationOnScreen(mAbsPos);
244
245 switch (event.getAction()) {
246 case MotionEvent.ACTION_DOWN:
Daniel Sandler50508132012-08-16 14:10:53 -0400247 mTracking = true;
Daniel Sandler13522a22012-09-27 14:46:58 -0400248 mHandleView.setPressed(true);
Daniel Sandler173bae22012-09-25 14:37:42 -0400249 mInitialTouchY = y;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400250 mVelocityTracker = VelocityTracker.obtain();
251 trackMovement(event);
Daniel Sandler67eab792012-10-02 17:08:23 -0400252 mTimeAnimator.cancel(); // end any outstanding animations
Daniel Sandler978f8532012-08-15 15:48:16 -0400253 mBar.onTrackingStarted(PanelView.this);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400254 mTouchOffset = (rawY - mAbsPos[1]) - PanelView.this.getExpandedHeight();
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400255 if (mExpandedHeight == 0) {
256 mJustPeeked = true;
257 runPeekAnimation();
258 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400259 break;
260
261 case MotionEvent.ACTION_MOVE:
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400262 final float h = rawY - mAbsPos[1] - mTouchOffset;
263 if (h > mPeekHeight) {
Daniel Sandlera801f682012-10-05 11:01:05 -0400264 if (mPeekAnimator != null && mPeekAnimator.isRunning()) {
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400265 mPeekAnimator.cancel();
266 }
267 mJustPeeked = false;
268 }
269 if (!mJustPeeked) {
270 PanelView.this.setExpandedHeightInternal(h);
271 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
272 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400273
274 trackMovement(event);
275 break;
276
277 case MotionEvent.ACTION_UP:
278 case MotionEvent.ACTION_CANCEL:
Daniel Sandler173bae22012-09-25 14:37:42 -0400279 mFinalTouchY = y;
Daniel Sandler50508132012-08-16 14:10:53 -0400280 mTracking = false;
Daniel Sandler13522a22012-09-27 14:46:58 -0400281 mHandleView.setPressed(false);
Daniel Sandler978f8532012-08-15 15:48:16 -0400282 mBar.onTrackingStopped(PanelView.this);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400283 trackMovement(event);
284 mVelocityTracker.computeCurrentVelocity(1000);
285
286 float yVel = mVelocityTracker.getYVelocity();
287 boolean negative = yVel < 0;
288
289 float xVel = mVelocityTracker.getXVelocity();
290 if (xVel < 0) {
291 xVel = -xVel;
292 }
293 if (xVel > mFlingGestureMaxXVelocityPx) {
294 xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
295 }
296
297 float vel = (float)Math.hypot(yVel, xVel);
298 if (vel > mFlingGestureMaxOutputVelocityPx) {
299 vel = mFlingGestureMaxOutputVelocityPx;
300 }
Daniel Sandler173bae22012-09-25 14:37:42 -0400301
302 // if you've barely moved your finger, we treat the velocity as 0
303 // preventing spurious flings due to touch screen jitter
Daniel Sandler67eab792012-10-02 17:08:23 -0400304 final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY);
Daniel Sandler173bae22012-09-25 14:37:42 -0400305 if (deltaY < mFlingGestureMinDistPx
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400306 || vel < mFlingExpandMinVelocityPx
307 || mJustPeeked) {
Daniel Sandler173bae22012-09-25 14:37:42 -0400308 vel = 0;
309 }
310
Daniel Sandler08d05e32012-08-08 16:39:54 -0400311 if (negative) {
312 vel = -vel;
313 }
314
Daniel Sandler67eab792012-10-02 17:08:23 -0400315 if (DEBUG) LOG("gesture: dy=%f vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
Daniel Sandler173bae22012-09-25 14:37:42 -0400316 deltaY,
Daniel Sandler08d05e32012-08-08 16:39:54 -0400317 mVelocityTracker.getXVelocity(),
318 mVelocityTracker.getYVelocity(),
319 xVel, yVel,
320 vel);
321
Daniel Sandlercf591db2012-08-15 16:11:55 -0400322 fling(vel, true);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400323
324 mVelocityTracker.recycle();
325 mVelocityTracker = null;
326
327 break;
328 }
329 return true;
330 }});
331 }
332 }
333
334 public void fling(float vel, boolean always) {
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400335 if (DEBUG) LOG("fling: vel=%.3f, this=%s", vel, this);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400336 mVel = vel;
337
Daniel Sandlercf591db2012-08-15 16:11:55 -0400338 if (always||mVel != 0) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400339 animationTick(0); // begin the animation
340 }
341 }
342
343 @Override
344 protected void onAttachedToWindow() {
345 super.onAttachedToWindow();
Daniel Sandler978f8532012-08-15 15:48:16 -0400346 mViewName = getResources().getResourceName(getId());
347 }
348
349 public String getName() {
350 return mViewName;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400351 }
352
353 @Override
354 protected void onViewAdded(View child) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400355 if (DEBUG) LOG("onViewAdded: " + child);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400356 }
357
358 public View getHandle() {
359 return mHandleView;
360 }
361
362 // Rubberbands the panel to hold its contents.
363 @Override
364 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
365 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
366
Daniel Sandler67eab792012-10-02 17:08:23 -0400367 if (DEBUG) LOG("onMeasure(%d, %d) -> (%d, %d)",
Daniel Sandler08d05e32012-08-08 16:39:54 -0400368 widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight());
Daniel Sandler198a0302012-08-17 16:04:31 -0400369
370 // Did one of our children change size?
371 int newHeight = getMeasuredHeight();
372 if (newHeight != mFullHeight) {
373 mFullHeight = newHeight;
374 // If the user isn't actively poking us, let's rubberband to the content
Daniel Sandler67eab792012-10-02 17:08:23 -0400375 if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
Daniel Sandler198a0302012-08-17 16:04:31 -0400376 && mExpandedHeight > 0 && mExpandedHeight != mFullHeight) {
377 mExpandedHeight = mFullHeight;
378 }
Daniel Sandler50508132012-08-16 14:10:53 -0400379 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400380 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
381 (int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec));
382 setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
383 }
384
385
386 public void setExpandedHeight(float height) {
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400387 if (DEBUG) LOG("setExpandedHeight(%.1f)", height);
Daniel Sandlera801f682012-10-05 11:01:05 -0400388 mRubberbanding = false;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400389 if (mTimeAnimator.isRunning()) {
390 post(mStopAnimator);
391 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400392 setExpandedHeightInternal(height);
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400393 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400394 }
395
Daniel Sandler50508132012-08-16 14:10:53 -0400396 @Override
397 protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400398 if (DEBUG) LOG("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, (int)mExpandedHeight, mFullHeight);
Daniel Sandler50508132012-08-16 14:10:53 -0400399 super.onLayout(changed, left, top, right, bottom);
400 }
401
Daniel Sandler08d05e32012-08-08 16:39:54 -0400402 public void setExpandedHeightInternal(float h) {
403 float fh = getFullHeight();
404 if (fh == 0) {
405 // Hmm, full height hasn't been computed yet
406 }
407
Daniel Sandler08d05e32012-08-08 16:39:54 -0400408 if (h < 0) h = 0;
Daniel Sandler50508132012-08-16 14:10:53 -0400409 if (!(STRETCH_PAST_CONTENTS && (mTracking || mRubberbanding)) && h > fh) h = fh;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400410 mExpandedHeight = h;
411
Daniel Sandler67eab792012-10-02 17:08:23 -0400412 if (DEBUG) LOG("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");
Daniel Sandler198a0302012-08-17 16:04:31 -0400413
Daniel Sandler08d05e32012-08-08 16:39:54 -0400414 requestLayout();
415// FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
416// lp.height = (int) mExpandedHeight;
417// setLayoutParams(lp);
418
Daniel Sandler198a0302012-08-17 16:04:31 -0400419 mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400420 }
421
422 private float getFullHeight() {
Daniel Sandler198a0302012-08-17 16:04:31 -0400423 if (mFullHeight <= 0) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400424 if (DEBUG) LOG("Forcing measure() since fullHeight=" + mFullHeight);
425 measure(MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY),
426 MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY));
Daniel Sandler198a0302012-08-17 16:04:31 -0400427 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400428 return mFullHeight;
429 }
430
431 public void setExpandedFraction(float frac) {
432 setExpandedHeight(getFullHeight() * frac);
433 }
434
435 public float getExpandedHeight() {
436 return mExpandedHeight;
437 }
438
439 public float getExpandedFraction() {
440 return mExpandedFraction;
441 }
442
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700443 public boolean isFullyExpanded() {
Daniel Sandler67eab792012-10-02 17:08:23 -0400444 return mExpandedHeight >= getFullHeight();
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700445 }
446
447 public boolean isFullyCollapsed() {
Daniel Sandler67eab792012-10-02 17:08:23 -0400448 return mExpandedHeight <= 0;
449 }
450
451 public boolean isCollapsing() {
452 return mClosing;
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700453 }
454
Daniel Sandler08d05e32012-08-08 16:39:54 -0400455 public void setBar(PanelBar panelBar) {
456 mBar = panelBar;
457 }
458
Daniel Sandler08d05e32012-08-08 16:39:54 -0400459 public void collapse() {
460 // TODO: abort animation or ongoing touch
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400461 if (DEBUG) LOG("collapse: " + this);
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700462 if (!isFullyCollapsed()) {
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400463 mTimeAnimator.cancel();
464 mClosing = true;
Daniel Sandler67eab792012-10-02 17:08:23 -0400465 // collapse() should never be a rubberband, even if an animation is already running
466 mRubberbanding = false;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400467 fling(-mSelfCollapseVelocityPx, /*always=*/ true);
468 }
469 }
470
471 public void expand() {
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400472 if (DEBUG) LOG("expand: " + this);
Daniel Sandler198a0302012-08-17 16:04:31 -0400473 if (isFullyCollapsed()) {
474 mBar.startOpeningPanel(this);
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400475 fling(mSelfExpandVelocityPx, /*always=*/ true);
Daniel Sandler198a0302012-08-17 16:04:31 -0400476 } else if (DEBUG) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400477 if (DEBUG) LOG("skipping expansion: is expanded");
Daniel Sandler08d05e32012-08-08 16:39:54 -0400478 }
479 }
480}