public SoundLevels(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); // Safe source, replaced with system one when attached. mLevelSource = new AudioLevelSource(); mLevelSource.setSpeechLevel(0); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SoundLevels, defStyle, 0); mMaximumLevelSize = a.getDimensionPixelOffset(R.styleable.SoundLevels_maxLevelRadius, 0); mMinimumLevelSize = a.getDimensionPixelOffset(R.styleable.SoundLevels_minLevelRadius, 0); mMinimumLevel = mMinimumLevelSize / mMaximumLevelSize; mPrimaryLevelPaint = new Paint(); mPrimaryLevelPaint.setColor(a.getColor(R.styleable.SoundLevels_primaryColor, Color.BLACK)); mPrimaryLevelPaint.setFlags(Paint.ANTI_ALIAS_FLAG); a.recycle(); // This animator generates ticks that invalidate the // view so that the animation is synced with the global animation loop. // TODO: We could probably remove this in favor of using postInvalidateOnAnimation // which might improve things further. mSpeechLevelsAnimator = new TimeAnimator(); mSpeechLevelsAnimator.setRepeatCount(ObjectAnimator.INFINITE); mSpeechLevelsAnimator.setTimeListener( new TimeListener() { @Override public void onTimeUpdate( final TimeAnimator animation, final long totalTime, final long deltaTime) { invalidate(); } }); }
private void startSpeechLevelsAnimator() { if (DEBUG) { Log.d(TAG, "startAnimator()"); } if (!mSpeechLevelsAnimator.isStarted()) { mSpeechLevelsAnimator.start(); } }
private void stopSpeechLevelsAnimator() { if (DEBUG) { Log.d(TAG, "stopAnimator()"); } if (mSpeechLevelsAnimator.isStarted()) { mSpeechLevelsAnimator.end(); } }
@Override public void run() { if (mTimeAnimator != null && mTimeAnimator.isStarted()) { mTimeAnimator.end(); mRubberbanding = false; mClosing = false; } }
// Rubberbands the panel to hold its contents. @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (DEBUG) LOG( "onMeasure(%d, %d) -> (%d, %d)", widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight()); // Did one of our children change size? int newHeight = getMeasuredHeight(); if (newHeight != mFullHeight) { mFullHeight = newHeight; // If the user isn't actively poking us, let's rubberband to the content if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted() && mExpandedHeight > 0 && mExpandedHeight != mFullHeight) { mExpandedHeight = mFullHeight; } } heightMeasureSpec = MeasureSpec.makeMeasureSpec( (int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec)); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); }
public void setExpandedHeight(float height) { if (DEBUG) LOG("setExpandedHeight(%.1f)", height); mRubberbanding = false; if (mTimeAnimator.isRunning()) { post(mStopAnimator); } setExpandedHeightInternal(height); mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); }
private void runPeekAnimation() { if (DEBUG) LOG("peek to height=%.1f", mPeekHeight); if (mTimeAnimator.isStarted()) { return; } if (mPeekAnimator == null) { mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight).setDuration(250); } mPeekAnimator.start(); }
public void collapse() { // TODO: abort animation or ongoing touch if (DEBUG) LOG("collapse: " + this); if (!isFullyCollapsed()) { mTimeAnimator.cancel(); mClosing = true; // collapse() should never be a rubberband, even if an animation is already running mRubberbanding = false; fling(-mSelfCollapseVelocityPx, /*always=*/ true); } }
public PanelView(Context context, AttributeSet attrs) { super(context, attrs); mTimeAnimator = new TimeAnimator(); mTimeAnimator.setTimeListener(mAnimationCallback); }
private void animationTick(long dtms) { if (!mTimeAnimator.isStarted()) { // XXX HAX to work around bug in TimeAnimator.end() not resetting its last time mTimeAnimator = new TimeAnimator(); mTimeAnimator.setTimeListener(mAnimationCallback); if (mPeekAnimator != null) mPeekAnimator.cancel(); mTimeAnimator.start(); mRubberbanding = mRubberbandingEnabled // is it enabled at all? && mExpandedHeight > getFullHeight() // are we past the end? && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture? if (mRubberbanding) { mClosing = true; } else if (mVel == 0) { // if the panel is less than halfway open, close it mClosing = (mFinalTouchY / getFullHeight()) < 0.5f; } else { mClosing = mExpandedHeight > 0 && mVel < 0; } } else if (dtms > 0) { final float dt = dtms * 0.001f; // ms -> s if (DEBUG) LOG("tick: v=%.2fpx/s dt=%.4fs", mVel, dt); if (DEBUG) LOG("tick: before: h=%d", (int) mExpandedHeight); final float fh = getFullHeight(); boolean braking = false; if (BRAKES) { if (mClosing) { braking = mExpandedHeight <= mCollapseBrakingDistancePx; mAccel = braking ? 10 * mCollapseAccelPx : -mCollapseAccelPx; } else { braking = mExpandedHeight >= (fh - mExpandBrakingDistancePx); mAccel = braking ? 10 * -mExpandAccelPx : mExpandAccelPx; } } else { mAccel = mClosing ? -mCollapseAccelPx : mExpandAccelPx; } mVel += mAccel * dt; if (braking) { if (mClosing && mVel > -mBrakingSpeedPx) { mVel = -mBrakingSpeedPx; } else if (!mClosing && mVel < mBrakingSpeedPx) { mVel = mBrakingSpeedPx; } } else { if (mClosing && mVel > -mFlingCollapseMinVelocityPx) { mVel = -mFlingCollapseMinVelocityPx; } else if (!mClosing && mVel > mFlingGestureMaxOutputVelocityPx) { mVel = mFlingGestureMaxOutputVelocityPx; } } float h = mExpandedHeight + mVel * dt; if (mRubberbanding && h < fh) { h = fh; } if (DEBUG) LOG("tick: new h=%d closing=%s", (int) h, mClosing ? "true" : "false"); setExpandedHeightInternal(h); mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); if (mVel == 0 || (mClosing && mExpandedHeight == 0) || ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) { post(mStopAnimator); } } }