blob: 703500696959c304aaf91ea650227d90411e8e98 [file] [log] [blame]
Daniel Sandler50a53132012-10-24 15:02:27 -04001/*
2 * Copyright (C) 2012 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
Daniel Sandler08d05e32012-08-08 16:39:54 -040017package com.android.systemui.statusbar.phone;
18
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040019import android.animation.ObjectAnimator;
Daniel Sandler08d05e32012-08-08 16:39:54 -040020import android.animation.TimeAnimator;
21import android.animation.TimeAnimator.TimeListener;
22import android.content.Context;
23import android.content.res.Resources;
24import android.util.AttributeSet;
Daniel Sandlerbf526d12012-09-04 22:56:44 -040025import android.util.Slog;
Daniel Sandler08d05e32012-08-08 16:39:54 -040026import android.view.MotionEvent;
27import android.view.VelocityTracker;
28import android.view.View;
29import android.widget.FrameLayout;
30
31import com.android.systemui.R;
32
33public class PanelView extends FrameLayout {
Daniel Sandler198a0302012-08-17 16:04:31 -040034 public static final boolean DEBUG = PanelBar.DEBUG;
Daniel Sandler08d05e32012-08-08 16:39:54 -040035 public static final String TAG = PanelView.class.getSimpleName();
Daniel Sandler978f8532012-08-15 15:48:16 -040036 public final void LOG(String fmt, Object... args) {
Daniel Sandler08d05e32012-08-08 16:39:54 -040037 if (!DEBUG) return;
Daniel Sandlerbf526d12012-09-04 22:56:44 -040038 Slog.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
Daniel Sandler08d05e32012-08-08 16:39:54 -040039 }
40
41 public static final boolean BRAKES = false;
Daniel Sandlerefb0faf2012-10-10 14:15:34 -070042 private boolean mRubberbandingEnabled = true;
Daniel Sandler08d05e32012-08-08 16:39:54 -040043
44 private float mSelfExpandVelocityPx; // classic value: 2000px/s
45 private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
46 private float mFlingExpandMinVelocityPx; // classic value: 200px/s
47 private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
48 private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
49 private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
50 private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
51
Daniel Sandler173bae22012-09-25 14:37:42 -040052 private float mFlingGestureMinDistPx;
53
Daniel Sandler08d05e32012-08-08 16:39:54 -040054 private float mExpandAccelPx; // classic value: 2000px/s/s
55 private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
56
57 private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
58 // faster than mSelfCollapseVelocityPx)
59
60 private float mCollapseBrakingDistancePx = 200; // XXX Resource
61 private float mExpandBrakingDistancePx = 150; // XXX Resource
62 private float mBrakingSpeedPx = 150; // XXX Resource
63
64 private View mHandleView;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040065 private float mPeekHeight;
Daniel Sandler08d05e32012-08-08 16:39:54 -040066 private float mTouchOffset;
67 private float mExpandedFraction = 0;
68 private float mExpandedHeight = 0;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040069 private boolean mJustPeeked;
Daniel Sandler50508132012-08-16 14:10:53 -040070 private boolean mClosing;
71 private boolean mRubberbanding;
72 private boolean mTracking;
Daniel Sandler08d05e32012-08-08 16:39:54 -040073
74 private TimeAnimator mTimeAnimator;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040075 private ObjectAnimator mPeekAnimator;
Daniel Sandler08d05e32012-08-08 16:39:54 -040076 private VelocityTracker mVelocityTracker;
77
78 private int[] mAbsPos = new int[2];
79 PanelBar mBar;
80
81 private final TimeListener mAnimationCallback = new TimeListener() {
82 @Override
83 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
84 animationTick(deltaTime);
85 }
86 };
87
Daniel Sandler67eab792012-10-02 17:08:23 -040088 private final Runnable mStopAnimator = new Runnable() {
89 @Override
90 public void run() {
Daniel Sandler0c1b75c2012-10-04 12:08:54 -040091 if (mTimeAnimator != null && mTimeAnimator.isStarted()) {
Daniel Sandler67eab792012-10-02 17:08:23 -040092 mTimeAnimator.end();
93 mRubberbanding = false;
94 mClosing = false;
95 }
Daniel Sandler50508132012-08-16 14:10:53 -040096 }
Daniel Sandler67eab792012-10-02 17:08:23 -040097 };
Daniel Sandler5a35a0d2012-08-16 13:50:40 -040098
Daniel Sandler08d05e32012-08-08 16:39:54 -040099 private float mVel, mAccel;
100 private int mFullHeight = 0;
Daniel Sandler50508132012-08-16 14:10:53 -0400101 private String mViewName;
Daniel Sandler173bae22012-09-25 14:37:42 -0400102 protected float mInitialTouchY;
103 protected float mFinalTouchY;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400104
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700105 public void setRubberbandingEnabled(boolean enable) {
106 mRubberbandingEnabled = enable;
107 }
108
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400109 private void runPeekAnimation() {
110 if (DEBUG) LOG("peek to height=%.1f", mPeekHeight);
111 if (mTimeAnimator.isStarted()) {
112 return;
113 }
114 if (mPeekAnimator == null) {
115 mPeekAnimator = ObjectAnimator.ofFloat(this,
116 "expandedHeight", mPeekHeight)
Daniel Sandler3679bf52012-10-16 21:30:28 -0400117 .setDuration(250);
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400118 }
119 mPeekAnimator.start();
120 }
121
Daniel Sandler08d05e32012-08-08 16:39:54 -0400122 private void animationTick(long dtms) {
123 if (!mTimeAnimator.isStarted()) {
124 // XXX HAX to work around bug in TimeAnimator.end() not resetting its last time
125 mTimeAnimator = new TimeAnimator();
126 mTimeAnimator.setTimeListener(mAnimationCallback);
127
Daniel Sandlera801f682012-10-05 11:01:05 -0400128 if (mPeekAnimator != null) mPeekAnimator.cancel();
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400129
Daniel Sandler08d05e32012-08-08 16:39:54 -0400130 mTimeAnimator.start();
Daniel Sandler67eab792012-10-02 17:08:23 -0400131
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700132 mRubberbanding = mRubberbandingEnabled // is it enabled at all?
Daniel Sandler67eab792012-10-02 17:08:23 -0400133 && mExpandedHeight > getFullHeight() // are we past the end?
134 && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture?
Daniel Sandler173bae22012-09-25 14:37:42 -0400135 if (mRubberbanding) {
136 mClosing = true;
137 } else if (mVel == 0) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400138 // if the panel is less than halfway open, close it
Daniel Sandler173bae22012-09-25 14:37:42 -0400139 mClosing = (mFinalTouchY / getFullHeight()) < 0.5f;
140 } else {
141 mClosing = mExpandedHeight > 0 && mVel < 0;
142 }
Daniel Sandler978f8532012-08-15 15:48:16 -0400143 } else if (dtms > 0) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400144 final float dt = dtms * 0.001f; // ms -> s
Daniel Sandler67eab792012-10-02 17:08:23 -0400145 if (DEBUG) LOG("tick: v=%.2fpx/s dt=%.4fs", mVel, dt);
146 if (DEBUG) LOG("tick: before: h=%d", (int) mExpandedHeight);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400147
148 final float fh = getFullHeight();
Daniel Sandler08d05e32012-08-08 16:39:54 -0400149 boolean braking = false;
150 if (BRAKES) {
Daniel Sandler50508132012-08-16 14:10:53 -0400151 if (mClosing) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400152 braking = mExpandedHeight <= mCollapseBrakingDistancePx;
153 mAccel = braking ? 10*mCollapseAccelPx : -mCollapseAccelPx;
154 } else {
155 braking = mExpandedHeight >= (fh-mExpandBrakingDistancePx);
156 mAccel = braking ? 10*-mExpandAccelPx : mExpandAccelPx;
157 }
158 } else {
Daniel Sandler50508132012-08-16 14:10:53 -0400159 mAccel = mClosing ? -mCollapseAccelPx : mExpandAccelPx;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400160 }
161
162 mVel += mAccel * dt;
163
164 if (braking) {
Daniel Sandler50508132012-08-16 14:10:53 -0400165 if (mClosing && mVel > -mBrakingSpeedPx) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400166 mVel = -mBrakingSpeedPx;
Daniel Sandler50508132012-08-16 14:10:53 -0400167 } else if (!mClosing && mVel < mBrakingSpeedPx) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400168 mVel = mBrakingSpeedPx;
169 }
170 } else {
Daniel Sandler50508132012-08-16 14:10:53 -0400171 if (mClosing && mVel > -mFlingCollapseMinVelocityPx) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400172 mVel = -mFlingCollapseMinVelocityPx;
Daniel Sandler50508132012-08-16 14:10:53 -0400173 } else if (!mClosing && mVel > mFlingGestureMaxOutputVelocityPx) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400174 mVel = mFlingGestureMaxOutputVelocityPx;
175 }
176 }
177
178 float h = mExpandedHeight + mVel * dt;
Daniel Sandler67eab792012-10-02 17:08:23 -0400179
Daniel Sandler50508132012-08-16 14:10:53 -0400180 if (mRubberbanding && h < fh) {
181 h = fh;
182 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400183
Daniel Sandler67eab792012-10-02 17:08:23 -0400184 if (DEBUG) LOG("tick: new h=%d closing=%s", (int) h, mClosing?"true":"false");
Daniel Sandler08d05e32012-08-08 16:39:54 -0400185
186 setExpandedHeightInternal(h);
187
188 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
189
190 if (mVel == 0
Daniel Sandler50508132012-08-16 14:10:53 -0400191 || (mClosing && mExpandedHeight == 0)
192 || ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) {
Daniel Sandler5a35a0d2012-08-16 13:50:40 -0400193 post(mStopAnimator);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400194 }
195 }
196 }
197
198 public PanelView(Context context, AttributeSet attrs) {
199 super(context, attrs);
200
201 mTimeAnimator = new TimeAnimator();
202 mTimeAnimator.setTimeListener(mAnimationCallback);
203 }
204
205 private void loadDimens() {
206 final Resources res = getContext().getResources();
207
208 mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
209 mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
210 mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
211 mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
212
Daniel Sandler173bae22012-09-25 14:37:42 -0400213 mFlingGestureMinDistPx = res.getDimension(R.dimen.fling_gesture_min_dist);
214
Daniel Sandler08d05e32012-08-08 16:39:54 -0400215 mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
216 mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
217
218 mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
219 mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
220
221 mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
222
223 mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400224
Daniel Sandler3679bf52012-10-16 21:30:28 -0400225 mPeekHeight = res.getDimension(R.dimen.peek_height)
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400226 + getPaddingBottom() // our window might have a dropshadow
227 - (mHandleView == null ? 0 : mHandleView.getPaddingTop()); // the handle might have a topshadow
Daniel Sandler08d05e32012-08-08 16:39:54 -0400228 }
229
230 private void trackMovement(MotionEvent event) {
231 // Add movement to velocity tracker using raw screen X and Y coordinates instead
232 // of window coordinates because the window frame may be moving at the same time.
233 float deltaX = event.getRawX() - event.getX();
234 float deltaY = event.getRawY() - event.getY();
235 event.offsetLocation(deltaX, deltaY);
Daniel Sandlerb17a7262012-10-05 14:32:50 -0400236 if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400237 event.offsetLocation(-deltaX, -deltaY);
238 }
239
Daniel Sandlerbf526d12012-09-04 22:56:44 -0400240 // Pass all touches along to the handle, allowing the user to drag the panel closed from its interior
241 @Override
242 public boolean onTouchEvent(MotionEvent event) {
243 return mHandleView.dispatchTouchEvent(event);
244 }
245
Daniel Sandler08d05e32012-08-08 16:39:54 -0400246 @Override
247 protected void onFinishInflate() {
248 super.onFinishInflate();
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400249 mHandleView = findViewById(R.id.handle);
250
Daniel Sandler08d05e32012-08-08 16:39:54 -0400251 loadDimens();
252
Daniel Sandler67eab792012-10-02 17:08:23 -0400253 if (DEBUG) LOG("handle view: " + mHandleView);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400254 if (mHandleView != null) {
255 mHandleView.setOnTouchListener(new View.OnTouchListener() {
256 @Override
257 public boolean onTouch(View v, MotionEvent event) {
258 final float y = event.getY();
259 final float rawY = event.getRawY();
Daniel Sandler67eab792012-10-02 17:08:23 -0400260 if (DEBUG) LOG("handle.onTouch: a=%s y=%.1f rawY=%.1f off=%.1f",
Daniel Sandler978f8532012-08-15 15:48:16 -0400261 MotionEvent.actionToString(event.getAction()),
262 y, rawY, mTouchOffset);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400263 PanelView.this.getLocationOnScreen(mAbsPos);
264
265 switch (event.getAction()) {
266 case MotionEvent.ACTION_DOWN:
Daniel Sandler50508132012-08-16 14:10:53 -0400267 mTracking = true;
Daniel Sandler13522a22012-09-27 14:46:58 -0400268 mHandleView.setPressed(true);
Daniel Sandler040c2e42012-10-17 00:56:33 -0400269 postInvalidate(); // catch the press state change
Daniel Sandler173bae22012-09-25 14:37:42 -0400270 mInitialTouchY = y;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400271 mVelocityTracker = VelocityTracker.obtain();
272 trackMovement(event);
Daniel Sandler67eab792012-10-02 17:08:23 -0400273 mTimeAnimator.cancel(); // end any outstanding animations
Daniel Sandler978f8532012-08-15 15:48:16 -0400274 mBar.onTrackingStarted(PanelView.this);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400275 mTouchOffset = (rawY - mAbsPos[1]) - PanelView.this.getExpandedHeight();
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400276 if (mExpandedHeight == 0) {
277 mJustPeeked = true;
278 runPeekAnimation();
279 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400280 break;
281
282 case MotionEvent.ACTION_MOVE:
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400283 final float h = rawY - mAbsPos[1] - mTouchOffset;
284 if (h > mPeekHeight) {
Daniel Sandlera801f682012-10-05 11:01:05 -0400285 if (mPeekAnimator != null && mPeekAnimator.isRunning()) {
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400286 mPeekAnimator.cancel();
287 }
288 mJustPeeked = false;
289 }
290 if (!mJustPeeked) {
291 PanelView.this.setExpandedHeightInternal(h);
292 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
293 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400294
295 trackMovement(event);
296 break;
297
298 case MotionEvent.ACTION_UP:
299 case MotionEvent.ACTION_CANCEL:
Daniel Sandler173bae22012-09-25 14:37:42 -0400300 mFinalTouchY = y;
Daniel Sandler50508132012-08-16 14:10:53 -0400301 mTracking = false;
Daniel Sandler13522a22012-09-27 14:46:58 -0400302 mHandleView.setPressed(false);
Daniel Sandler040c2e42012-10-17 00:56:33 -0400303 postInvalidate(); // catch the press state change
Daniel Sandler978f8532012-08-15 15:48:16 -0400304 mBar.onTrackingStopped(PanelView.this);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400305 trackMovement(event);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400306
Daniel Sandler9d098242012-10-07 23:27:30 -0400307 float vel = 0, yVel = 0, xVel = 0;
308 boolean negative = false;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400309
Daniel Sandler9d098242012-10-07 23:27:30 -0400310 if (mVelocityTracker != null) {
311 // the velocitytracker might be null if we got a bad input stream
312 mVelocityTracker.computeCurrentVelocity(1000);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400313
Daniel Sandler9d098242012-10-07 23:27:30 -0400314 yVel = mVelocityTracker.getYVelocity();
315 negative = yVel < 0;
316
317 xVel = mVelocityTracker.getXVelocity();
318 if (xVel < 0) {
319 xVel = -xVel;
320 }
321 if (xVel > mFlingGestureMaxXVelocityPx) {
322 xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
323 }
324
325 vel = (float)Math.hypot(yVel, xVel);
326 if (vel > mFlingGestureMaxOutputVelocityPx) {
327 vel = mFlingGestureMaxOutputVelocityPx;
328 }
329
330 mVelocityTracker.recycle();
331 mVelocityTracker = null;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400332 }
Daniel Sandler173bae22012-09-25 14:37:42 -0400333
334 // if you've barely moved your finger, we treat the velocity as 0
335 // preventing spurious flings due to touch screen jitter
Daniel Sandler67eab792012-10-02 17:08:23 -0400336 final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY);
Daniel Sandler173bae22012-09-25 14:37:42 -0400337 if (deltaY < mFlingGestureMinDistPx
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400338 || vel < mFlingExpandMinVelocityPx
Daniel Sandlerbe2cf322012-10-24 15:23:42 -0400339 ) {
Daniel Sandler173bae22012-09-25 14:37:42 -0400340 vel = 0;
341 }
342
Daniel Sandler08d05e32012-08-08 16:39:54 -0400343 if (negative) {
344 vel = -vel;
345 }
346
Daniel Sandlerbe2cf322012-10-24 15:23:42 -0400347 if (DEBUG) LOG("gesture: dy=%f vel=(%f,%f) vlinear=%f",
Daniel Sandler173bae22012-09-25 14:37:42 -0400348 deltaY,
Daniel Sandler08d05e32012-08-08 16:39:54 -0400349 xVel, yVel,
350 vel);
351
Daniel Sandlercf591db2012-08-15 16:11:55 -0400352 fling(vel, true);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400353
Daniel Sandler08d05e32012-08-08 16:39:54 -0400354 break;
355 }
356 return true;
357 }});
358 }
359 }
360
361 public void fling(float vel, boolean always) {
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400362 if (DEBUG) LOG("fling: vel=%.3f, this=%s", vel, this);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400363 mVel = vel;
364
Daniel Sandlercf591db2012-08-15 16:11:55 -0400365 if (always||mVel != 0) {
Daniel Sandler08d05e32012-08-08 16:39:54 -0400366 animationTick(0); // begin the animation
367 }
368 }
369
370 @Override
371 protected void onAttachedToWindow() {
372 super.onAttachedToWindow();
Daniel Sandler978f8532012-08-15 15:48:16 -0400373 mViewName = getResources().getResourceName(getId());
374 }
375
376 public String getName() {
377 return mViewName;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400378 }
379
380 @Override
381 protected void onViewAdded(View child) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400382 if (DEBUG) LOG("onViewAdded: " + child);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400383 }
384
385 public View getHandle() {
386 return mHandleView;
387 }
388
389 // Rubberbands the panel to hold its contents.
390 @Override
391 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
392 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
393
Daniel Sandler67eab792012-10-02 17:08:23 -0400394 if (DEBUG) LOG("onMeasure(%d, %d) -> (%d, %d)",
Daniel Sandler08d05e32012-08-08 16:39:54 -0400395 widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight());
Daniel Sandler198a0302012-08-17 16:04:31 -0400396
397 // Did one of our children change size?
398 int newHeight = getMeasuredHeight();
399 if (newHeight != mFullHeight) {
400 mFullHeight = newHeight;
401 // If the user isn't actively poking us, let's rubberband to the content
Daniel Sandler67eab792012-10-02 17:08:23 -0400402 if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
Daniel Sandler198a0302012-08-17 16:04:31 -0400403 && mExpandedHeight > 0 && mExpandedHeight != mFullHeight) {
404 mExpandedHeight = mFullHeight;
405 }
Daniel Sandler50508132012-08-16 14:10:53 -0400406 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400407 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
408 (int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec));
409 setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
410 }
411
412
413 public void setExpandedHeight(float height) {
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400414 if (DEBUG) LOG("setExpandedHeight(%.1f)", height);
Daniel Sandlera801f682012-10-05 11:01:05 -0400415 mRubberbanding = false;
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400416 if (mTimeAnimator.isRunning()) {
417 post(mStopAnimator);
418 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400419 setExpandedHeightInternal(height);
Daniel Sandler0c1b75c2012-10-04 12:08:54 -0400420 mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400421 }
422
Daniel Sandler50508132012-08-16 14:10:53 -0400423 @Override
424 protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400425 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 -0400426 super.onLayout(changed, left, top, right, bottom);
427 }
428
Daniel Sandler08d05e32012-08-08 16:39:54 -0400429 public void setExpandedHeightInternal(float h) {
430 float fh = getFullHeight();
431 if (fh == 0) {
432 // Hmm, full height hasn't been computed yet
433 }
434
Daniel Sandler08d05e32012-08-08 16:39:54 -0400435 if (h < 0) h = 0;
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700436 if (!(mRubberbandingEnabled && (mTracking || mRubberbanding)) && h > fh) h = fh;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400437 mExpandedHeight = h;
438
Daniel Sandler67eab792012-10-02 17:08:23 -0400439 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 -0400440
Daniel Sandler08d05e32012-08-08 16:39:54 -0400441 requestLayout();
442// FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
443// lp.height = (int) mExpandedHeight;
444// setLayoutParams(lp);
445
Daniel Sandler198a0302012-08-17 16:04:31 -0400446 mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400447 }
448
449 private float getFullHeight() {
Daniel Sandler198a0302012-08-17 16:04:31 -0400450 if (mFullHeight <= 0) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400451 if (DEBUG) LOG("Forcing measure() since fullHeight=" + mFullHeight);
452 measure(MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY),
453 MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY));
Daniel Sandler198a0302012-08-17 16:04:31 -0400454 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400455 return mFullHeight;
456 }
457
458 public void setExpandedFraction(float frac) {
459 setExpandedHeight(getFullHeight() * frac);
460 }
461
462 public float getExpandedHeight() {
463 return mExpandedHeight;
464 }
465
466 public float getExpandedFraction() {
467 return mExpandedFraction;
468 }
469
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700470 public boolean isFullyExpanded() {
Daniel Sandler67eab792012-10-02 17:08:23 -0400471 return mExpandedHeight >= getFullHeight();
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700472 }
473
474 public boolean isFullyCollapsed() {
Daniel Sandler67eab792012-10-02 17:08:23 -0400475 return mExpandedHeight <= 0;
476 }
477
478 public boolean isCollapsing() {
479 return mClosing;
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700480 }
481
Daniel Sandler08d05e32012-08-08 16:39:54 -0400482 public void setBar(PanelBar panelBar) {
483 mBar = panelBar;
484 }
485
Daniel Sandler08d05e32012-08-08 16:39:54 -0400486 public void collapse() {
487 // TODO: abort animation or ongoing touch
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400488 if (DEBUG) LOG("collapse: " + this);
Daniel Sandlerb4e56ed2012-09-12 23:07:44 -0700489 if (!isFullyCollapsed()) {
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400490 mTimeAnimator.cancel();
491 mClosing = true;
Daniel Sandler67eab792012-10-02 17:08:23 -0400492 // collapse() should never be a rubberband, even if an animation is already running
493 mRubberbanding = false;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400494 fling(-mSelfCollapseVelocityPx, /*always=*/ true);
495 }
496 }
497
498 public void expand() {
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400499 if (DEBUG) LOG("expand: " + this);
Daniel Sandler198a0302012-08-17 16:04:31 -0400500 if (isFullyCollapsed()) {
501 mBar.startOpeningPanel(this);
Daniel Sandler750bb9b2012-10-03 16:24:00 -0400502 fling(mSelfExpandVelocityPx, /*always=*/ true);
Daniel Sandler198a0302012-08-17 16:04:31 -0400503 } else if (DEBUG) {
Daniel Sandler67eab792012-10-02 17:08:23 -0400504 if (DEBUG) LOG("skipping expansion: is expanded");
Daniel Sandler08d05e32012-08-08 16:39:54 -0400505 }
506 }
507}