1/*
2 * Copyright (C) 2012 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;
18
19import android.graphics.Bitmap;
20import android.graphics.Point;
21import android.graphics.SurfaceTexture;
22import android.view.GestureDetector;
23import android.view.MotionEvent;
24import android.view.View;
25import android.view.ViewGroup;
26import android.widget.ImageView;
27import android.widget.LinearLayout;
28import android.widget.TextView;
29
30import com.android.camera.debug.Log;
31import com.android.camera.ui.FocusOverlay;
32import com.android.camera.ui.PreviewOverlay;
33import com.android.camera.ui.PreviewStatusListener;
34import com.android.camera.ui.RotateLayout;
35import com.android.camera.widget.VideoRecordingHints;
36import com.android.camera2.R;
37import com.android.ex.camera2.portability.CameraCapabilities;
38import com.android.ex.camera2.portability.CameraSettings;
39
40import java.util.List;
41
42public class VideoUI implements PreviewStatusListener {
43    private static final Log.Tag TAG = new Log.Tag("VideoUI");
44
45    private final static float UNSET = 0f;
46    private final PreviewOverlay mPreviewOverlay;
47    // module fields
48    private final CameraActivity mActivity;
49    private final View mRootView;
50    private final FocusOverlay mFocusUI;
51    // An review image having same size as preview. It is displayed when
52    // recording is stopped in capture intent.
53    private ImageView mReviewImage;
54    private VideoRecordingHints mVideoHints;
55    private TextView mRecordingTimeView;
56    private LinearLayout mLabelsLinearLayout;
57    private RotateLayout mRecordingTimeRect;
58    private boolean mRecordingStarted = false;
59    private final VideoController mController;
60    private float mZoomMax;
61
62    private float mAspectRatio = UNSET;
63    private final AnimationManager mAnimationManager;
64
65    @Override
66    public void onPreviewLayoutChanged(View v, int left, int top, int right,
67            int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
68    }
69
70    @Override
71    public boolean shouldAutoAdjustTransformMatrixOnLayout() {
72        return true;
73    }
74
75    @Override
76    public boolean shouldAutoAdjustBottomBar() {
77        return true;
78    }
79
80    @Override
81    public void onPreviewFlipped() {
82        mController.updateCameraOrientation();
83    }
84
85    private final GestureDetector.OnGestureListener mPreviewGestureListener
86            = new GestureDetector.SimpleOnGestureListener() {
87        @Override
88        public boolean onSingleTapUp(MotionEvent ev) {
89            if (mVideoHints.getVisibility() == View.VISIBLE) {
90                mVideoHints.setVisibility(View.INVISIBLE);
91            } else {
92                mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY());
93            }
94            return true;
95        }
96    };
97
98    public VideoUI(CameraActivity activity, VideoController controller, View parent) {
99        mActivity = activity;
100        mController = controller;
101        mRootView = parent;
102        ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout);
103        mActivity.getLayoutInflater().inflate(R.layout.video_module,
104                moduleRoot, true);
105
106        mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay);
107
108        initializeMiscControls();
109        mAnimationManager = new AnimationManager();
110        mFocusUI = (FocusOverlay) mRootView.findViewById(R.id.focus_overlay);
111        mVideoHints = (VideoRecordingHints) mRootView.findViewById(R.id.video_shooting_hints);
112    }
113
114    public void setPreviewSize(int width, int height) {
115        if (width == 0 || height == 0) {
116            Log.w(TAG, "Preview size should not be 0.");
117            return;
118        }
119        float aspectRatio;
120        if (width > height) {
121            aspectRatio = (float) width / height;
122        } else {
123            aspectRatio = (float) height / width;
124        }
125        setAspectRatio(aspectRatio);
126    }
127
128    public FocusOverlayManager.FocusUI getFocusUI() {
129        return mFocusUI;
130    }
131
132    /**
133     * Starts a flash animation
134     */
135    public void animateFlash() {
136        mController.startPreCaptureAnimation();
137    }
138
139    /**
140     * Cancels on-going animations
141     */
142    public void cancelAnimations() {
143        mAnimationManager.cancelAnimations();
144    }
145
146    public void setOrientationIndicator(int orientation, boolean animation) {
147        // We change the orientation of the linearlayout only for phone UI
148        // because when in portrait the width is not enough.
149        if (mLabelsLinearLayout != null) {
150            if (((orientation / 90) & 1) == 0) {
151                mLabelsLinearLayout.setOrientation(LinearLayout.VERTICAL);
152            } else {
153                mLabelsLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
154            }
155        }
156        mRecordingTimeRect.setOrientation(0, animation);
157    }
158
159    private void initializeMiscControls() {
160        mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
161        mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time);
162        mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect);
163        // The R.id.labels can only be found in phone layout.
164        // That is, mLabelsLinearLayout should be null in tablet layout.
165        mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels);
166    }
167
168    public void updateOnScreenIndicators(CameraSettings settings) {
169    }
170
171    public void setAspectRatio(float ratio) {
172        if (ratio <= 0) {
173            return;
174        }
175        float aspectRatio = ratio > 1 ? ratio : 1 / ratio;
176        if (aspectRatio != mAspectRatio) {
177            mAspectRatio = aspectRatio;
178            mController.updatePreviewAspectRatio(mAspectRatio);
179        }
180    }
181
182    public void setSwipingEnabled(boolean enable) {
183        mActivity.setSwipingEnabled(enable);
184    }
185
186    public void showPreviewBorder(boolean enable) {
187       // TODO: mPreviewFrameLayout.showBorder(enable);
188    }
189
190    public void showRecordingUI(boolean recording) {
191        mRecordingStarted = recording;
192        if (recording) {
193            mRecordingTimeView.setText("");
194            mRecordingTimeView.setVisibility(View.VISIBLE);
195            mRecordingTimeView.announceForAccessibility(
196                    mActivity.getResources().getString(R.string.video_recording_started));
197        } else {
198            mRecordingTimeView.announceForAccessibility(
199                    mActivity.getResources().getString(R.string.video_recording_stopped));
200            mRecordingTimeView.setVisibility(View.GONE);
201        }
202    }
203
204    public void showReviewImage(Bitmap bitmap) {
205        mReviewImage.setImageBitmap(bitmap);
206        mReviewImage.setVisibility(View.VISIBLE);
207    }
208
209    public void showReviewControls() {
210        mActivity.getCameraAppUI().transitionToIntentReviewLayout();
211        mReviewImage.setVisibility(View.VISIBLE);
212    }
213
214    public void initializeZoom(CameraSettings settings, CameraCapabilities capabilities) {
215        mZoomMax = capabilities.getMaxZoomRatio();
216        // Currently we use immediate zoom for fast zooming to get better UX and
217        // there is no plan to take advantage of the smooth zoom.
218        // TODO: setup zoom through App UI.
219        mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomRatio(),
220                new ZoomChangeListener());
221    }
222
223    public void setRecordingTime(String text) {
224        mRecordingTimeView.setText(text);
225    }
226
227    public void setRecordingTimeTextColor(int color) {
228        mRecordingTimeView.setTextColor(color);
229    }
230
231    public boolean isVisible() {
232        return false;
233    }
234
235    @Override
236    public GestureDetector.OnGestureListener getGestureListener() {
237        return mPreviewGestureListener;
238    }
239
240    @Override
241    public View.OnTouchListener getTouchListener() {
242        return null;
243    }
244
245    /**
246     * Shows or hides focus UI.
247     *
248     * @param show shows focus UI when true, hides it otherwise
249     */
250    public void showFocusUI(boolean show) {
251        if (mFocusUI != null) {
252            mFocusUI.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
253        }
254    }
255
256    /**
257     * Shows or hides video recording hints.
258     *
259     * @param show shows video recording hints when true, hides it otherwise.
260     */
261    public void showVideoRecordingHints(boolean show) {
262        mVideoHints.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
263    }
264
265    /**
266     * @return The size of the available preview area.
267     */
268    public Point getPreviewScreenSize() {
269        return new Point(mRootView.getMeasuredWidth(), mRootView.getMeasuredHeight());
270    }
271
272    public void onOrientationChanged(int orientation) {
273        mVideoHints.onOrientationChanged(orientation);
274    }
275
276    private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener {
277        @Override
278        public void onZoomValueChanged(float ratio) {
279            mController.onZoomChanged(ratio);
280        }
281
282        @Override
283        public void onZoomStart() {
284        }
285
286        @Override
287        public void onZoomEnd() {
288        }
289    }
290
291    // SurfaceTexture callbacks
292    @Override
293    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
294        mController.onPreviewUIReady();
295    }
296
297    @Override
298    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
299        mController.onPreviewUIDestroyed();
300        Log.d(TAG, "surfaceTexture is destroyed");
301        return true;
302    }
303
304    @Override
305    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
306    }
307
308    @Override
309    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
310    }
311
312    public void onPause() {
313        // recalculate aspect ratio when restarting.
314        mAspectRatio = 0.0f;
315    }
316}
317