1/*
2 * Copyright (C) 2014 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
17package com.android.camera.ui.motion;
18
19import android.graphics.Canvas;
20
21import java.util.ArrayList;
22import java.util.List;
23
24/**
25 * Designed to handle the lifecycle of a view that needs a continuous update /
26 * redraw cycle that does not have a defined start / end time.
27 *
28 * Fixed length animations should NOT use this class.
29 */
30public class DynamicAnimator implements Invalidator {
31
32    public final List<DynamicAnimation> animations = new ArrayList<>();
33
34    private final Invalidator mInvalidator;
35    private final AnimationClock mClock;
36
37    private boolean mUpdateRequested = false;
38    private boolean mIsDrawing = false;
39    private long mLastDrawTimeMillis = 0;
40    private long mDrawTimeMillis = 0;
41
42    public DynamicAnimator(Invalidator invalidator, AnimationClock clock) {
43        mInvalidator = invalidator;
44        mClock = clock;
45    }
46
47    public void draw(Canvas canvas) {
48        mIsDrawing = true;
49        mUpdateRequested = false;
50
51        mDrawTimeMillis = mClock.getTimeMillis();
52
53        if (mLastDrawTimeMillis <= 0) {
54            mLastDrawTimeMillis = mDrawTimeMillis; // On the initial draw, dt is zero.
55        }
56
57        long dt = mDrawTimeMillis - mLastDrawTimeMillis;
58        mLastDrawTimeMillis = mDrawTimeMillis;
59
60        // Run the animation
61        for (DynamicAnimation renderer : animations) {
62            if (renderer.isActive()) {
63                renderer.draw(mDrawTimeMillis, dt, canvas);
64            }
65        }
66
67        // If either the update or the draw methods requested new frames, then
68        // invalidate the view which should give us another frame to work with.
69        // Otherwise, stopAt the last update time.
70        if (mUpdateRequested) {
71            mInvalidator.invalidate();
72        } else {
73            mLastDrawTimeMillis = -1;
74        }
75
76        mIsDrawing = false;
77    }
78
79    /**
80     * If a scheduleNewFrame request comes in outside of the animation loop,
81     * and we didn't schedule a frame after the previous loop (or it's the
82     * first time we've used this instance), invalidate the view and set the
83     * last update time to the current time. Theoretically, a few milliseconds
84     * have elapsed before the view gets updated.
85     */
86    @Override
87    public void invalidate() {
88        if (!mIsDrawing && !mUpdateRequested) {
89            mInvalidator.invalidate();
90            mLastDrawTimeMillis = mClock.getTimeMillis();
91        }
92
93        mUpdateRequested = true;
94    }
95
96    /**
97     * This will return the "best guess" for the most current animation frame
98     * time.  If the loop is currently drawing, then it will return the time the
99     * draw began, and if an update is currently requested it will return the
100     * time that the update was requested at, and if neither of these are true
101     * it will return the current system clock time.
102     *
103     * This method will not trigger a new update.
104     */
105    public long getTimeMillis() {
106        if (mIsDrawing) {
107            return mDrawTimeMillis;
108        }
109
110        if (mUpdateRequested) {
111            return mLastDrawTimeMillis;
112        }
113
114        return mClock.getTimeMillis();
115    }
116}
117