VideoUI.java revision 39f8a7647aa4051a1c260b9496c2db82f93f8667
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.SurfaceTexture;
21import android.hardware.Camera.Parameters;
22import android.util.Log;
23import android.view.GestureDetector;
24import android.view.MotionEvent;
25import android.view.SurfaceHolder;
26import android.view.SurfaceView;
27import android.view.TextureView;
28import android.view.View;
29import android.view.View.OnClickListener;
30import android.view.ViewGroup;
31import android.widget.ImageView;
32import android.widget.LinearLayout;
33import android.widget.TextView;
34
35import com.android.camera.app.CameraAppUI;
36import com.android.camera.ui.PreviewOverlay;
37import com.android.camera.ui.PreviewStatusListener;
38import com.android.camera.ui.RotateLayout;
39import com.android.camera.util.CameraUtil;
40import com.android.camera2.R;
41
42import java.util.List;
43
44public class VideoUI implements PreviewStatusListener, SurfaceHolder.Callback {
45    private static final String TAG = "VideoUI";
46
47    private final static float UNSET = 0f;
48    private final PreviewOverlay mPreviewOverlay;
49    // module fields
50    private final CameraActivity mActivity;
51    private final View mRootView;
52    private final TextureView mTextureView;
53    // An review image having same size as preview. It is displayed when
54    // recording is stopped in capture intent.
55    private ImageView mReviewImage;
56    private View mReviewCancelButton;
57    private View mReviewDoneButton;
58    private View mReviewPlayButton;
59    private TextView mRecordingTimeView;
60    private LinearLayout mLabelsLinearLayout;
61    private View mTimeLapseLabel;
62    private RotateLayout mRecordingTimeRect;
63    private boolean mRecordingStarted = false;
64    private SurfaceTexture mSurfaceTexture;
65    private final VideoController mController;
66    private int mZoomMax;
67    private List<Integer> mZoomRatios;
68
69    private SurfaceView mSurfaceView = null;
70    private float mSurfaceTextureUncroppedWidth;
71    private float mSurfaceTextureUncroppedHeight;
72
73    private ButtonManager.ButtonCallback mFlashCallback;
74    private ButtonManager.ButtonCallback mCameraCallback;
75
76    private final OnClickListener mCancelCallback = new OnClickListener() {
77        @Override
78        public void onClick(View v) {
79            mController.onReviewCancelClicked(v);
80        }
81    };
82    private final OnClickListener mDoneCallback = new OnClickListener() {
83        @Override
84        public void onClick(View v) {
85            mController.onReviewDoneClicked(v);
86        }
87    };
88    private final OnClickListener mReviewCallback = new OnClickListener() {
89        @Override
90        public void onClick(View v) {
91            customizeButtons(mActivity.getButtonManager(), mFlashCallback, mCameraCallback);
92            mActivity.getCameraAppUI().transitionToIntentLayout();
93            mController.onReviewPlayClicked(v);
94        }
95    };
96
97    private float mAspectRatio = UNSET;
98    private final AnimationManager mAnimationManager;
99
100    @Override
101    public void onPreviewLayoutChanged(View v, int left, int top, int right,
102            int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
103    }
104
105    @Override
106    public boolean shouldAutoAdjustTransformMatrixOnLayout() {
107        return true;
108    }
109
110    @Override
111    public boolean shouldAutoAdjustBottomBar() {
112        return true;
113    }
114
115    @Override
116    public void onPreviewFlipped() {
117        mController.updateCameraOrientation();
118    }
119
120    private final GestureDetector.OnGestureListener mPreviewGestureListener
121            = new GestureDetector.SimpleOnGestureListener() {
122        @Override
123        public boolean onSingleTapUp(MotionEvent ev) {
124            mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY());
125            return true;
126        }
127    };
128
129    public VideoUI(CameraActivity activity, VideoController controller, View parent) {
130        mActivity = activity;
131        mController = controller;
132        mRootView = parent;
133        ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout);
134        mActivity.getLayoutInflater().inflate(R.layout.video_module,
135                moduleRoot, true);
136
137        mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay);
138        mTextureView = (TextureView) mRootView.findViewById(R.id.preview_content);
139
140        mSurfaceTexture = mTextureView.getSurfaceTexture();
141
142        initializeMiscControls();
143        initializeControlByIntent();
144        mAnimationManager = new AnimationManager();
145    }
146
147    public void initializeSurfaceView() {
148        mSurfaceView = new SurfaceView(mActivity);
149        ((ViewGroup) mRootView).addView(mSurfaceView, 0);
150        mSurfaceView.getHolder().addCallback(this);
151    }
152
153    /**
154     * Customize the mode options such that flash and camera
155     * switching are enabled.
156     */
157    public void customizeButtons(ButtonManager buttonManager,
158                                   ButtonManager.ButtonCallback flashCallback,
159                                   ButtonManager.ButtonCallback cameraCallback) {
160
161        buttonManager.enableButton(ButtonManager.BUTTON_CAMERA,
162            cameraCallback, R.array.camera_id_icons);
163        buttonManager.enableButton(ButtonManager.BUTTON_TORCH,
164            flashCallback, R.array.video_flashmode_icons);
165        buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
166        buttonManager.hideButton(ButtonManager.BUTTON_REFOCUS);
167
168        mActivity.getCameraAppUI().setBottomBarShutterIcon(CameraAppUI.VIDEO_SHUTTER_ICON);
169
170        if (mController.isVideoCaptureIntent()) {
171            buttonManager.enablePushButton(ButtonManager.BUTTON_CANCEL,
172                mCancelCallback);
173            buttonManager.enablePushButton(ButtonManager.BUTTON_DONE,
174                mDoneCallback);
175            buttonManager.enablePushButton(ButtonManager.BUTTON_REVIEW,
176                mReviewCallback, R.drawable.ic_play);
177        }
178    }
179
180    private void initializeControlByIntent() {
181        if (mController.isVideoCaptureIntent()) {
182            customizeButtons(mActivity.getButtonManager(), mFlashCallback, mCameraCallback);
183            mActivity.getCameraAppUI().transitionToIntentLayout();
184        }
185    }
186
187    public void setPreviewSize(int width, int height) {
188        if (width == 0 || height == 0) {
189            Log.w(TAG, "Preview size should not be 0.");
190            return;
191        }
192        float aspectRatio;
193        if (width > height) {
194            aspectRatio = (float) width / height;
195        } else {
196            aspectRatio = (float) height / width;
197        }
198        setAspectRatio(aspectRatio);
199    }
200
201    /**
202     * Starts a flash animation
203     */
204    public void animateFlash() {
205        mController.startPreCaptureAnimation();
206    }
207
208    /**
209     * Starts a capture animation
210     */
211    public void animateCapture() {
212        Bitmap bitmap = null;
213        if (mTextureView != null) {
214            bitmap = mTextureView.getBitmap((int) mSurfaceTextureUncroppedWidth / 2,
215                    (int) mSurfaceTextureUncroppedHeight / 2);
216        }
217        animateCapture(bitmap);
218    }
219
220    /**
221     * Starts a capture animation
222     * @param bitmap the captured image that we shrink and slide in the animation
223     */
224    public void animateCapture(Bitmap bitmap) {
225        if (bitmap == null) {
226            Log.e(TAG, "No valid bitmap for capture animation.");
227            return;
228        }
229    }
230
231    /**
232     * Cancels on-going animations
233     */
234    public void cancelAnimations() {
235        mAnimationManager.cancelAnimations();
236    }
237
238    public void setOrientationIndicator(int orientation, boolean animation) {
239        // We change the orientation of the linearlayout only for phone UI
240        // because when in portrait the width is not enough.
241        if (mLabelsLinearLayout != null) {
242            if (((orientation / 90) & 1) == 0) {
243                mLabelsLinearLayout.setOrientation(LinearLayout.VERTICAL);
244            } else {
245                mLabelsLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
246            }
247        }
248        mRecordingTimeRect.setOrientation(0, animation);
249    }
250
251    public SurfaceHolder getSurfaceHolder() {
252        return mSurfaceView.getHolder();
253    }
254
255    public void hideSurfaceView() {
256        mSurfaceView.setVisibility(View.GONE);
257        mTextureView.setVisibility(View.VISIBLE);
258    }
259
260    public void showSurfaceView() {
261        mSurfaceView.setVisibility(View.VISIBLE);
262        mTextureView.setVisibility(View.GONE);
263    }
264
265    public void onCameraOpened(ButtonManager.ButtonCallback flashCallback,
266            ButtonManager.ButtonCallback cameraCallback) {
267        mFlashCallback = flashCallback;
268        mCameraCallback = cameraCallback;
269    }
270
271    private void initializeMiscControls() {
272        mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
273        mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time);
274        mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect);
275        mTimeLapseLabel = mRootView.findViewById(R.id.time_lapse_label);
276        // The R.id.labels can only be found in phone layout.
277        // That is, mLabelsLinearLayout should be null in tablet layout.
278        mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels);
279    }
280
281    public void updateOnScreenIndicators(Parameters param) {
282    }
283
284    public void setAspectRatio(float ratio) {
285        if (ratio <= 0) {
286            return;
287        }
288        float aspectRatio = ratio > 1 ? ratio : 1 / ratio;
289        if (aspectRatio != mAspectRatio) {
290            mAspectRatio = aspectRatio;
291            mController.updatePreviewAspectRatio(mAspectRatio);
292        }
293    }
294
295    public void showTimeLapseUI(boolean enable) {
296        if (mTimeLapseLabel != null) {
297            mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE);
298        }
299    }
300
301    public void enableShutter(boolean enable) {
302
303    }
304
305    public void setSwipingEnabled(boolean enable) {
306        mActivity.setSwipingEnabled(enable);
307    }
308
309    public void showPreviewBorder(boolean enable) {
310       // TODO: mPreviewFrameLayout.showBorder(enable);
311    }
312
313    public void showRecordingUI(boolean recording) {
314        mRecordingStarted = recording;
315        if (recording) {
316            mRecordingTimeView.setText("");
317            mRecordingTimeView.setVisibility(View.VISIBLE);
318        } else {
319            mRecordingTimeView.setVisibility(View.GONE);
320        }
321    }
322
323    public void showReviewImage(Bitmap bitmap) {
324        mReviewImage.setImageBitmap(bitmap);
325        mReviewImage.setVisibility(View.VISIBLE);
326    }
327
328    public void showReviewControls() {
329        customizeButtons(mActivity.getButtonManager(), mFlashCallback, mCameraCallback);
330        mActivity.getCameraAppUI().transitionToIntentReviewLayout();
331        mReviewImage.setVisibility(View.VISIBLE);
332    }
333
334    public void hideReviewUI() {
335        mReviewImage.setVisibility(View.GONE);
336        CameraUtil.fadeOut(mReviewDoneButton);
337        CameraUtil.fadeOut(mReviewPlayButton);
338    }
339
340    public void initializeZoom(Parameters param) {
341        mZoomMax = param.getMaxZoom();
342        mZoomRatios = param.getZoomRatios();
343        // Currently we use immediate zoom for fast zooming to get better UX and
344        // there is no plan to take advantage of the smooth zoom.
345        // TODO: setup zoom through App UI.
346        mPreviewOverlay.setupZoom(mZoomMax, param.getZoom(), mZoomRatios, new ZoomChangeListener());
347    }
348
349    public void clickShutter() {
350
351    }
352
353    public void pressShutter(boolean pressed) {
354
355    }
356
357    public View getShutterButton() {
358        return null;
359    }
360
361    public void setRecordingTime(String text) {
362        mRecordingTimeView.setText(text);
363    }
364
365    public void setRecordingTimeTextColor(int color) {
366        mRecordingTimeView.setTextColor(color);
367    }
368
369    public boolean isVisible() {
370        return false;
371    }
372
373    @Override
374    public GestureDetector.OnGestureListener getGestureListener() {
375        return mPreviewGestureListener;
376    }
377
378    private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener {
379        @Override
380        public void onZoomValueChanged(int index) {
381            mController.onZoomChanged(index);
382        }
383
384        @Override
385        public void onZoomStart() {
386        }
387
388        @Override
389        public void onZoomEnd() {
390        }
391    }
392
393    public SurfaceTexture getSurfaceTexture() {
394        return mSurfaceTexture;
395    }
396
397    // SurfaceTexture callbacks
398    @Override
399    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
400        mSurfaceTexture = surface;
401        mController.onPreviewUIReady();
402    }
403
404    @Override
405    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
406        mSurfaceTexture = null;
407        mController.onPreviewUIDestroyed();
408        Log.d(TAG, "surfaceTexture is destroyed");
409        return true;
410    }
411
412    @Override
413    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
414    }
415
416    @Override
417    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
418    }
419
420    // SurfaceHolder callbacks
421    @Override
422    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
423        Log.v(TAG, "Surface changed. width=" + width + ". height=" + height);
424    }
425
426    @Override
427    public void surfaceCreated(SurfaceHolder holder) {
428        Log.v(TAG, "Surface created");
429    }
430
431    @Override
432    public void surfaceDestroyed(SurfaceHolder holder) {
433        Log.v(TAG, "Surface destroyed");
434        mController.stopPreview();
435    }
436}
437