diff options
Diffstat (limited to 'src/com/android/camera/ui/focus/FocusRingRenderer.java')
-rw-r--r-- | src/com/android/camera/ui/focus/FocusRingRenderer.java | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/com/android/camera/ui/focus/FocusRingRenderer.java b/src/com/android/camera/ui/focus/FocusRingRenderer.java new file mode 100644 index 000000000..264af2ace --- /dev/null +++ b/src/com/android/camera/ui/focus/FocusRingRenderer.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.graphics.Paint; +import android.util.Log; + +import com.android.camera.ui.motion.DampedSpring; +import com.android.camera.ui.motion.DynamicAnimation; +import com.android.camera.ui.motion.Invalidator; +import com.android.camera.ui.motion.UnitCurve; +import com.android.camera.ui.motion.UnitCurves; + +/** + * Base class for defining the focus ring states, enter and exit durations, and + * positioning logic. + */ +abstract class FocusRingRenderer implements DynamicAnimation { + private static final String TAG = "FocusRingRenderer"; + + /** + * Primary focus states that a focus ring renderer can go through. + */ + protected static enum FocusState { + STATE_INACTIVE, + STATE_ENTER, + STATE_ACTIVE, + STATE_FADE_OUT, + STATE_HARD_STOP, + } + + protected final Invalidator mInvalidator; + protected final Paint mRingPaint; + protected final DampedSpring mRingRadius; + protected final UnitCurve mEnterOpacityCurve; + protected final UnitCurve mExitOpacityCurve; + protected final UnitCurve mHardExitOpacityCurve; + protected final float mEnterDurationMillis; + protected final float mExitDurationMillis; + protected final float mHardExitDurationMillis = 64; + + private int mCenterX; + private int mCenterY; + protected long mEnterStartMillis = 0; + protected long mExitStartMillis = 0; + protected long mHardExitStartMillis = 0; + + protected FocusState mFocusState = FocusState.STATE_INACTIVE; + + /** + * A dynamic, configurable, self contained ring render that will inform + * via invalidation if it should continue to be receive updates + * and re-draws. + * + * @param invalidator the object to inform if it requires more draw calls. + * @param ringPaint the paint to use to draw the ring. + * @param enterDurationMillis the fade in duration in milliseconds + * @param exitDurationMillis the fade out duration in milliseconds. + */ + FocusRingRenderer(Invalidator invalidator, Paint ringPaint, float enterDurationMillis, + float exitDurationMillis) { + mInvalidator = invalidator; + mRingPaint = ringPaint; + mEnterDurationMillis = enterDurationMillis; + mExitDurationMillis = exitDurationMillis; + + mEnterOpacityCurve = UnitCurves.FAST_OUT_SLOW_IN; + mExitOpacityCurve = UnitCurves.FAST_OUT_LINEAR_IN; + mHardExitOpacityCurve = UnitCurves.FAST_OUT_LINEAR_IN; + + mRingRadius = new DampedSpring(); + } + + /** + * Set the centerX position for this focus ring renderer. + * + * @param value the x position + */ + public void setCenterX(int value) { + mCenterX = value; + } + + protected int getCenterX() { + return mCenterX; + } + + /** + * Set the centerY position for this focus ring renderer. + * + * @param value the y position + */ + public void setCenterY(int value) { + mCenterY = value; + } + + protected int getCenterY() { + return mCenterY; + } + + /** + * Set the physical radius of this ring. + * + * @param value the radius of the ring. + */ + public void setRadius(long tMs, float value) { + if (mFocusState == FocusState.STATE_FADE_OUT + && Math.abs(mRingRadius.getTarget() - value) > 0.1) { + Log.v(TAG, "FOCUS STATE ENTER VIA setRadius(" + tMs + ", " + value + ")"); + mFocusState = FocusState.STATE_ENTER; + mEnterStartMillis = computeEnterStartTimeMillis(tMs, mEnterDurationMillis); + } + + mRingRadius.setTarget(value); + } + + /** + * returns true if the renderer is not in an inactive state. + */ + @Override + public boolean isActive() { + return mFocusState != FocusState.STATE_INACTIVE; + } + + /** + * returns true if the renderer is in an exit state. + */ + public boolean isExiting() { + return mFocusState == FocusState.STATE_FADE_OUT + || mFocusState == FocusState.STATE_HARD_STOP; + } + + /** + * returns true if the renderer is in an enter state. + */ + public boolean isEntering() { + return mFocusState == FocusState.STATE_ENTER; + } + + /** + * Initialize and start the animation with the given start and + * target radius. + */ + public void start(long startMs, float initialRadius, float targetRadius) { + if (mFocusState != FocusState.STATE_INACTIVE) { + Log.w(TAG, "start() called while the ring was still focusing!"); + } + mRingRadius.stop(); + mRingRadius.setValue(initialRadius); + mRingRadius.setTarget(targetRadius); + mEnterStartMillis = startMs; + + mFocusState = FocusState.STATE_ENTER; + mInvalidator.invalidate(); + } + + /** + * Put the animation in the exit state regardless of the current + * dynamic transition. If the animation is currently in an enter state + * this will compute an exit start time such that the exit time lines + * up with the enter time at the current transition value. + * + * @param t the current animation time. + */ + public void exit(long t) { + if (mRingRadius.isActive()) { + mRingRadius.stop(); + } + + mFocusState = FocusState.STATE_FADE_OUT; + mExitStartMillis = computeExitStartTimeMs(t, mExitDurationMillis); + } + + /** + * Put the animation in the hard stop state regardless of the current + * dynamic transition. If the animation is currently in an enter state + * this will compute an exit start time such that the exit time lines + * up with the enter time at the current transition value. + * + * @param tMillis the current animation time in milliseconds. + */ + public void stop(long tMillis) { + if (mRingRadius.isActive()) { + mRingRadius.stop(); + } + + mFocusState = FocusState.STATE_HARD_STOP; + mHardExitStartMillis = computeExitStartTimeMs(tMillis, mHardExitDurationMillis); + } + + private long computeExitStartTimeMs(long tMillis, float exitDuration) { + if (mEnterStartMillis + mEnterDurationMillis <= tMillis) { + return tMillis; + } + + // Compute the current progress on the enter animation. + float enterT = (tMillis - mEnterStartMillis) / mEnterDurationMillis; + + // Find a time on the exit curve such that it will produce the same value. + float exitT = UnitCurves.mapEnterCurveToExitCurveAtT(mEnterOpacityCurve, mExitOpacityCurve, + enterT); + + // Compute the a start time before tMs such that the ratio of time completed + // equals the computed exit curve animation position. + return tMillis - (long) (exitT * exitDuration); + } + + private long computeEnterStartTimeMillis(long tMillis, float enterDuration) { + if (mExitStartMillis + mExitDurationMillis <= tMillis) { + return tMillis; + } + + // Compute the current progress on the enter animation. + float exitT = (tMillis - mExitStartMillis) / mExitDurationMillis; + + // Find a time on the exit curve such that it will produce the same value. + float enterT = UnitCurves.mapEnterCurveToExitCurveAtT(mExitOpacityCurve, mEnterOpacityCurve, + exitT); + + // Compute the a start time before tMs such that the ratio of time completed + // equals the computed exit curve animation position. + return tMillis - (long) (enterT * enterDuration); + } +} |