1/*
2 * Copyright (C) 2017 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 androidx.wear.widget;
18
19import android.os.CountDownTimer;
20
21import androidx.annotation.Nullable;
22import androidx.annotation.VisibleForTesting;
23
24/**
25 * Controller for {@link CircularProgressLayout}.
26 */
27class CircularProgressLayoutController {
28
29    private final CircularProgressLayout mLayout;
30    @VisibleForTesting CountDownTimer mTimer;
31    private boolean mIsIndeterminate;
32    private boolean mIsTimerRunning;
33
34    /**
35     * Called when the timer is finished.
36     */
37    @Nullable
38    private CircularProgressLayout.OnTimerFinishedListener mOnTimerFinishedListener;
39
40    CircularProgressLayoutController(CircularProgressLayout layout) {
41        mLayout = layout;
42    }
43
44    /**
45     * Returns the registered {@link CircularProgressLayout.OnTimerFinishedListener}.
46     */
47    @Nullable
48    public CircularProgressLayout.OnTimerFinishedListener getOnTimerFinishedListener() {
49        return mOnTimerFinishedListener;
50    }
51
52    /**
53     * Sets the {@link CircularProgressLayout.OnTimerFinishedListener} to be notified when timer
54     * countdown is finished.
55     */
56    public void setOnTimerFinishedListener(
57            @Nullable CircularProgressLayout.OnTimerFinishedListener listener) {
58        mOnTimerFinishedListener = listener;
59    }
60
61    /** Returns true if the progress is shown as an indeterminate spinner. */
62    boolean isIndeterminate() {
63        return mIsIndeterminate;
64    }
65
66    /** Returns true if timer is running. */
67    boolean isTimerRunning() {
68        return mIsTimerRunning;
69    }
70
71    /** Sets if the progress should be shown as an indeterminate spinner. */
72    void setIndeterminate(boolean indeterminate) {
73        if (mIsIndeterminate == indeterminate) {
74            return;
75        }
76        mIsIndeterminate = indeterminate;
77        if (mIsIndeterminate) {
78            if (mIsTimerRunning) {
79                stopTimer();
80            }
81            mLayout.getProgressDrawable().start();
82        } else {
83            mLayout.getProgressDrawable().stop();
84        }
85    }
86
87    void startTimer(long totalTime, long updateInterval) {
88        reset();
89        mIsTimerRunning = true;
90        mTimer = new CircularProgressTimer(totalTime, updateInterval);
91        mTimer.start();
92    }
93
94    void stopTimer() {
95        if (mIsTimerRunning) {
96            mTimer.cancel();
97            mIsTimerRunning = false;
98            mLayout.getProgressDrawable().setStartEndTrim(0f, 0f); // Reset the progress
99        }
100    }
101
102    /**
103     * Resets everything.
104     */
105    void reset() {
106        setIndeterminate(false); // If showing indeterminate progress, stop it
107        stopTimer(); // Stop the previous timer if there is one
108        mLayout.getProgressDrawable().setStartEndTrim(0f, 0f); // Reset the progress
109    }
110
111    /**
112     * Class to handle timing for {@link CircularProgressLayout}.
113     */
114    private class CircularProgressTimer extends CountDownTimer {
115
116        private final long mTotalTime;
117
118        CircularProgressTimer(long totalTime, long updateInterval) {
119            super(totalTime, updateInterval);
120            mTotalTime = totalTime;
121        }
122
123        @Override
124        public void onTick(long millisUntilFinished) {
125            mLayout.getProgressDrawable()
126                    .setStartEndTrim(0f, 1f - (float) millisUntilFinished / (float) mTotalTime);
127            mLayout.invalidate();
128        }
129
130        @Override
131        public void onFinish() {
132            mLayout.getProgressDrawable().setStartEndTrim(0f, 1f);
133            if (mOnTimerFinishedListener != null) {
134                mOnTimerFinishedListener.onTimerFinished(mLayout);
135            }
136            mIsTimerRunning = false;
137        }
138    }
139}
140