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;
18
19import java.util.Locale;
20
21import android.content.Context;
22import android.graphics.RectF;
23import android.os.Handler;
24import android.os.Message;
25import android.util.AttributeSet;
26import android.view.View;
27import android.widget.FrameLayout;
28import android.widget.TextView;
29
30import com.android.camera.debug.Log;
31import com.android.camera2.R;
32
33/**
34 * This class manages the looks of the countdown.
35 */
36public class CountDownView extends FrameLayout {
37
38    private static final Log.Tag TAG = new Log.Tag("CountDownView");
39    private static final int SET_TIMER_TEXT = 1;
40    private static final long ANIMATION_DURATION_MS = 800;
41    private TextView mRemainingSecondsView;
42    private int mRemainingSecs = 0;
43    private OnCountDownStatusListener mListener;
44    private final Handler mHandler = new MainHandler();
45    private final RectF mPreviewArea = new RectF();
46
47    /**
48     * Listener that gets notified when the countdown status has
49     * been updated (i.e. remaining seconds changed, or finished).
50     */
51    public interface OnCountDownStatusListener {
52        /**
53         * Gets notified when the remaining seconds for the countdown
54         * has changed.
55         *
56         * @param remainingSeconds seconds remained for countdown
57         */
58        public void onRemainingSecondsChanged(int remainingSeconds);
59
60        /**
61         * Gets called when countdown is finished.
62         */
63        public void onCountDownFinished();
64    }
65
66    public CountDownView(Context context, AttributeSet attrs) {
67        super(context, attrs);
68    }
69
70    /**
71     * Returns whether countdown is on-going.
72     */
73    public boolean isCountingDown() {
74        return mRemainingSecs > 0;
75    };
76
77    /**
78     * Responds to preview area change by centering th countdown UI in the new
79     * preview area.
80     */
81    public void onPreviewAreaChanged(RectF previewArea) {
82        mPreviewArea.set(previewArea);
83    }
84
85    private void remainingSecondsChanged(int newVal) {
86        mRemainingSecs = newVal;
87        if (mListener != null) {
88            mListener.onRemainingSecondsChanged(mRemainingSecs);
89        }
90
91        if (newVal == 0) {
92            // Countdown has finished.
93            setVisibility(View.INVISIBLE);
94            if (mListener != null) {
95                mListener.onCountDownFinished();
96            }
97        } else {
98            Locale locale = getResources().getConfiguration().locale;
99            String localizedValue = String.format(locale, "%d", newVal);
100            mRemainingSecondsView.setText(localizedValue);
101            // Fade-out animation.
102            startFadeOutAnimation();
103            // Schedule the next remainingSecondsChanged() call in 1 second
104            mHandler.sendEmptyMessageDelayed(SET_TIMER_TEXT, 1000);
105        }
106    }
107
108    private void startFadeOutAnimation() {
109        int textWidth = mRemainingSecondsView.getMeasuredWidth();
110        int textHeight = mRemainingSecondsView.getMeasuredHeight();
111        mRemainingSecondsView.setScaleX(1f);
112        mRemainingSecondsView.setScaleY(1f);
113        mRemainingSecondsView.setTranslationX(mPreviewArea.centerX() - textWidth / 2);
114        mRemainingSecondsView.setTranslationY(mPreviewArea.centerY() - textHeight / 2);
115        mRemainingSecondsView.setPivotX(textWidth / 2);
116        mRemainingSecondsView.setPivotY(textHeight / 2);
117        mRemainingSecondsView.setAlpha(1f);
118        float endScale = 2.5f;
119        mRemainingSecondsView.animate().scaleX(endScale).scaleY(endScale)
120                .alpha(0f).setDuration(ANIMATION_DURATION_MS).start();
121    }
122
123    @Override
124    protected void onFinishInflate() {
125        super.onFinishInflate();
126        mRemainingSecondsView = (TextView) findViewById(R.id.remaining_seconds);
127    }
128
129    /**
130     * Sets a listener that gets notified when the status of countdown has changed.
131     */
132    public void setCountDownStatusListener(OnCountDownStatusListener listener) {
133        mListener = listener;
134    }
135
136    /**
137     * Starts showing countdown in the UI.
138     *
139     * @param sec duration of the countdown, in seconds
140     */
141    public void startCountDown(int sec) {
142        if (sec <= 0) {
143            Log.w(TAG, "Invalid input for countdown timer: " + sec + " seconds");
144            return;
145        }
146        if (isCountingDown()) {
147            cancelCountDown();
148        }
149        setVisibility(View.VISIBLE);
150        remainingSecondsChanged(sec);
151    }
152
153    /**
154     * Cancels the on-going countdown in the UI, if any.
155     */
156    public void cancelCountDown() {
157        if (mRemainingSecs > 0) {
158            mRemainingSecs = 0;
159            mHandler.removeMessages(SET_TIMER_TEXT);
160            setVisibility(View.INVISIBLE);
161        }
162    }
163
164    private class MainHandler extends Handler {
165        @Override
166        public void handleMessage(Message message) {
167            if (message.what == SET_TIMER_TEXT) {
168                remainingSecondsChanged(mRemainingSecs -1);
169            }
170        }
171    }
172}