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 
17 package com.android.camera.ui;
18 
19 import java.util.Locale;
20 
21 import android.content.Context;
22 import android.graphics.RectF;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.util.AttributeSet;
26 import android.view.View;
27 import android.widget.FrameLayout;
28 import android.widget.TextView;
29 
30 import com.android.camera.debug.Log;
31 import com.android.camera2.R;
32 
33 /**
34  * This class manages the looks of the countdown.
35  */
36 public 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          */
onRemainingSecondsChanged(int remainingSeconds)58         public void onRemainingSecondsChanged(int remainingSeconds);
59 
60         /**
61          * Gets called when countdown is finished.
62          */
onCountDownFinished()63         public void onCountDownFinished();
64     }
65 
CountDownView(Context context, AttributeSet attrs)66     public CountDownView(Context context, AttributeSet attrs) {
67         super(context, attrs);
68     }
69 
70     /**
71      * Returns whether countdown is on-going.
72      */
isCountingDown()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      */
onPreviewAreaChanged(RectF previewArea)81     public void onPreviewAreaChanged(RectF previewArea) {
82         mPreviewArea.set(previewArea);
83     }
84 
remainingSecondsChanged(int newVal)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 
startFadeOutAnimation()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
onFinishInflate()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      */
setCountDownStatusListener(OnCountDownStatusListener listener)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      */
startCountDown(int sec)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      */
cancelCountDown()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
handleMessage(Message message)166         public void handleMessage(Message message) {
167             if (message.what == SET_TIMER_TEXT) {
168                 remainingSecondsChanged(mRemainingSecs -1);
169             }
170         }
171     }
172 }