blob: 161e0510d525d8babceb15a12a7bb7324c34e40b [file] [log] [blame]
Doris Liu6827ce22013-03-12 19:24:28 -07001/*
Doris Liu6432cd62013-06-13 17:20:31 -07002 * Copyright (C) 2012 The Android Open Source Project
Doris Liu6827ce22013-03-12 19:24:28 -07003 *
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;
Angus Kong5f8c30e2014-03-06 17:15:08 -080020import android.graphics.Point;
Doris Liu6432cd62013-06-13 17:20:31 -070021import android.graphics.SurfaceTexture;
Doris Liu6827ce22013-03-12 19:24:28 -070022import android.hardware.Camera.Parameters;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080023import android.view.GestureDetector;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080024import android.view.MotionEvent;
Doris Liu6827ce22013-03-12 19:24:28 -070025import android.view.View;
Doris Liu6827ce22013-03-12 19:24:28 -070026import android.view.ViewGroup;
Doris Liu6827ce22013-03-12 19:24:28 -070027import android.widget.ImageView;
28import android.widget.LinearLayout;
29import android.widget.TextView;
30
Angus Kong5596b4c2014-03-11 16:27:30 -070031import com.android.camera.debug.Log;
Doris Liu38c6bc32014-01-16 18:03:18 -080032import com.android.camera.ui.FocusOverlay;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080033import com.android.camera.ui.PreviewOverlay;
Doris Liu06db7422013-12-09 19:36:25 -080034import com.android.camera.ui.PreviewStatusListener;
Doris Liu6827ce22013-03-12 19:24:28 -070035import com.android.camera.ui.RotateLayout;
Doris Liu8dc878f2014-05-05 14:10:34 -070036import com.android.camera.widget.VideoRecordingHints;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070037import com.android.camera2.R;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070038
39import java.util.List;
Doris Liu6827ce22013-03-12 19:24:28 -070040
Erin Dahlgrend8de0772014-02-03 10:12:27 -080041public class VideoUI implements PreviewStatusListener {
Angus Kong5596b4c2014-03-11 16:27:30 -070042 private static final Log.Tag TAG = new Log.Tag("VideoUI");
Doris Liue038c162013-12-13 23:06:11 -080043
Doris Liu70da9182013-12-17 18:41:15 -080044 private final static float UNSET = 0f;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080045 private final PreviewOverlay mPreviewOverlay;
Doris Liu6827ce22013-03-12 19:24:28 -070046 // module fields
Sascha Haeberling67019c72013-12-14 01:08:38 -080047 private final CameraActivity mActivity;
48 private final View mRootView;
Doris Liu38c6bc32014-01-16 18:03:18 -080049 private final FocusOverlay mFocusUI;
Doris Liu6827ce22013-03-12 19:24:28 -070050 // An review image having same size as preview. It is displayed when
51 // recording is stopped in capture intent.
52 private ImageView mReviewImage;
Doris Liu8dc878f2014-05-05 14:10:34 -070053 private VideoRecordingHints mVideoHints;
Doris Liu6827ce22013-03-12 19:24:28 -070054 private TextView mRecordingTimeView;
55 private LinearLayout mLabelsLinearLayout;
56 private View mTimeLapseLabel;
Doris Liu6827ce22013-03-12 19:24:28 -070057 private RotateLayout mRecordingTimeRect;
Doris Liufe6596c2013-10-08 11:03:37 -070058 private boolean mRecordingStarted = false;
Sascha Haeberling67019c72013-12-14 01:08:38 -080059 private final VideoController mController;
Doris Liu6827ce22013-03-12 19:24:28 -070060 private int mZoomMax;
61 private List<Integer> mZoomRatios;
62
Doris Liu70da9182013-12-17 18:41:15 -080063 private float mAspectRatio = UNSET;
Sascha Haeberling37f36112013-08-06 14:31:52 -070064 private final AnimationManager mAnimationManager;
Doris Liue038c162013-12-13 23:06:11 -080065
Doris Liu4a010db2013-12-16 18:44:49 -080066 @Override
67 public void onPreviewLayoutChanged(View v, int left, int top, int right,
68 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
Doris Liu70da9182013-12-17 18:41:15 -080069 }
70
71 @Override
72 public boolean shouldAutoAdjustTransformMatrixOnLayout() {
73 return true;
74 }
75
76 @Override
77 public boolean shouldAutoAdjustBottomBar() {
78 return true;
Doris Liu4a010db2013-12-16 18:44:49 -080079 }
Doris Liu6432cd62013-06-13 17:20:31 -070080
Doris Liu11ddd732014-01-07 10:52:11 -080081 @Override
82 public void onPreviewFlipped() {
83 mController.updateCameraOrientation();
84 }
85
Doris Liu06db7422013-12-09 19:36:25 -080086 private final GestureDetector.OnGestureListener mPreviewGestureListener
87 = new GestureDetector.SimpleOnGestureListener() {
88 @Override
89 public boolean onSingleTapUp(MotionEvent ev) {
Doris Liud6487c92014-02-28 10:35:45 -080090 if (mVideoHints.getVisibility() == View.VISIBLE) {
91 mVideoHints.setVisibility(View.INVISIBLE);
92 } else {
93 mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY());
94 }
Doris Liu06db7422013-12-09 19:36:25 -080095 return true;
96 }
97 };
Doris Liu3a45c332013-10-15 19:10:28 -070098
Doris Liu6827ce22013-03-12 19:24:28 -070099 public VideoUI(CameraActivity activity, VideoController controller, View parent) {
100 mActivity = activity;
101 mController = controller;
102 mRootView = parent;
Doris Liuf55f3c42013-11-20 00:24:46 -0800103 ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout);
104 mActivity.getLayoutInflater().inflate(R.layout.video_module,
Sascha Haeberling67019c72013-12-14 01:08:38 -0800105 moduleRoot, true);
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000106
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800107 mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay);
Doris Liue038c162013-12-13 23:06:11 -0800108
Doris Liu6827ce22013-03-12 19:24:28 -0700109 initializeMiscControls();
Sascha Haeberling37f36112013-08-06 14:31:52 -0700110 mAnimationManager = new AnimationManager();
Doris Liu38c6bc32014-01-16 18:03:18 -0800111 mFocusUI = (FocusOverlay) mRootView.findViewById(R.id.focus_overlay);
Doris Liu8dc878f2014-05-05 14:10:34 -0700112 mVideoHints = (VideoRecordingHints) mRootView.findViewById(R.id.video_shooting_hints);
Doris Liu6827ce22013-03-12 19:24:28 -0700113 }
114
Doris Liu6432cd62013-06-13 17:20:31 -0700115 public void setPreviewSize(int width, int height) {
116 if (width == 0 || height == 0) {
117 Log.w(TAG, "Preview size should not be 0.");
118 return;
119 }
Doris Liue038c162013-12-13 23:06:11 -0800120 float aspectRatio;
Doris Liu6432cd62013-06-13 17:20:31 -0700121 if (width > height) {
Doris Liue038c162013-12-13 23:06:11 -0800122 aspectRatio = (float) width / height;
Doris Liu6432cd62013-06-13 17:20:31 -0700123 } else {
Doris Liue038c162013-12-13 23:06:11 -0800124 aspectRatio = (float) height / width;
Doris Liu6432cd62013-06-13 17:20:31 -0700125 }
Doris Liue038c162013-12-13 23:06:11 -0800126 setAspectRatio(aspectRatio);
Doris Liu6432cd62013-06-13 17:20:31 -0700127 }
128
Doris Liua1ec04a2014-01-13 17:29:40 -0800129 public FocusOverlayManager.FocusUI getFocusUI() {
130 return mFocusUI;
131 }
132
Sascha Haeberling37f36112013-08-06 14:31:52 -0700133 /**
134 * Starts a flash animation
135 */
136 public void animateFlash() {
Doris Liu1dfe7822013-12-12 00:02:08 -0800137 mController.startPreCaptureAnimation();
Sascha Haeberling37f36112013-08-06 14:31:52 -0700138 }
139
140 /**
Sascha Haeberling37f36112013-08-06 14:31:52 -0700141 * Cancels on-going animations
142 */
143 public void cancelAnimations() {
144 mAnimationManager.cancelAnimations();
145 }
146
Doris Liu6827ce22013-03-12 19:24:28 -0700147 public void setOrientationIndicator(int orientation, boolean animation) {
Doris Liu6827ce22013-03-12 19:24:28 -0700148 // We change the orientation of the linearlayout only for phone UI
149 // because when in portrait the width is not enough.
150 if (mLabelsLinearLayout != null) {
151 if (((orientation / 90) & 1) == 0) {
152 mLabelsLinearLayout.setOrientation(LinearLayout.VERTICAL);
153 } else {
154 mLabelsLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
155 }
156 }
157 mRecordingTimeRect.setOrientation(0, animation);
158 }
159
Doris Liu6827ce22013-03-12 19:24:28 -0700160 private void initializeMiscControls() {
Doris Liu6827ce22013-03-12 19:24:28 -0700161 mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
Doris Liu6827ce22013-03-12 19:24:28 -0700162 mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time);
163 mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect);
164 mTimeLapseLabel = mRootView.findViewById(R.id.time_lapse_label);
165 // The R.id.labels can only be found in phone layout.
166 // That is, mLabelsLinearLayout should be null in tablet layout.
167 mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels);
168 }
169
Erin Dahlgrenbd3da262013-12-02 11:48:13 -0800170 public void updateOnScreenIndicators(Parameters param) {
Doris Liu6827ce22013-03-12 19:24:28 -0700171 }
172
Doris Liue038c162013-12-13 23:06:11 -0800173 public void setAspectRatio(float ratio) {
174 if (ratio <= 0) {
175 return;
176 }
177 float aspectRatio = ratio > 1 ? ratio : 1 / ratio;
178 if (aspectRatio != mAspectRatio) {
179 mAspectRatio = aspectRatio;
Doris Liu70da9182013-12-17 18:41:15 -0800180 mController.updatePreviewAspectRatio(mAspectRatio);
Doris Liue038c162013-12-13 23:06:11 -0800181 }
Doris Liu6827ce22013-03-12 19:24:28 -0700182 }
183
184 public void showTimeLapseUI(boolean enable) {
185 if (mTimeLapseLabel != null) {
186 mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE);
187 }
188 }
189
Doris Liu6432cd62013-06-13 17:20:31 -0700190 public void setSwipingEnabled(boolean enable) {
191 mActivity.setSwipingEnabled(enable);
Doris Liu6827ce22013-03-12 19:24:28 -0700192 }
193
194 public void showPreviewBorder(boolean enable) {
Doris Liu6432cd62013-06-13 17:20:31 -0700195 // TODO: mPreviewFrameLayout.showBorder(enable);
Doris Liu6827ce22013-03-12 19:24:28 -0700196 }
197
Doris Liufe6596c2013-10-08 11:03:37 -0700198 public void showRecordingUI(boolean recording) {
199 mRecordingStarted = recording;
Doris Liu6827ce22013-03-12 19:24:28 -0700200 if (recording) {
Doris Liu6827ce22013-03-12 19:24:28 -0700201 mRecordingTimeView.setText("");
202 mRecordingTimeView.setVisibility(View.VISIBLE);
Alan Newbergerf2711922014-03-26 11:28:50 -0700203 mRecordingTimeView.announceForAccessibility(
204 mActivity.getResources().getString(R.string.video_recording_started));
Doris Liu6827ce22013-03-12 19:24:28 -0700205 } else {
Alan Newbergerf2711922014-03-26 11:28:50 -0700206 mRecordingTimeView.announceForAccessibility(
207 mActivity.getResources().getString(R.string.video_recording_stopped));
Doris Liu6827ce22013-03-12 19:24:28 -0700208 mRecordingTimeView.setVisibility(View.GONE);
Doris Liu6827ce22013-03-12 19:24:28 -0700209 }
210 }
211
212 public void showReviewImage(Bitmap bitmap) {
213 mReviewImage.setImageBitmap(bitmap);
214 mReviewImage.setVisibility(View.VISIBLE);
215 }
216
217 public void showReviewControls() {
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800218 mActivity.getCameraAppUI().transitionToIntentReviewLayout();
Doris Liu6827ce22013-03-12 19:24:28 -0700219 mReviewImage.setVisibility(View.VISIBLE);
Doris Liu6827ce22013-03-12 19:24:28 -0700220 }
221
Doris Liu6827ce22013-03-12 19:24:28 -0700222 public void initializeZoom(Parameters param) {
Doris Liu6827ce22013-03-12 19:24:28 -0700223 mZoomMax = param.getMaxZoom();
224 mZoomRatios = param.getZoomRatios();
225 // Currently we use immediate zoom for fast zooming to get better UX and
226 // there is no plan to take advantage of the smooth zoom.
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800227 // TODO: setup zoom through App UI.
228 mPreviewOverlay.setupZoom(mZoomMax, param.getZoom(), mZoomRatios, new ZoomChangeListener());
Doris Liu6827ce22013-03-12 19:24:28 -0700229 }
230
Doris Liu6827ce22013-03-12 19:24:28 -0700231 public void setRecordingTime(String text) {
232 mRecordingTimeView.setText(text);
233 }
234
235 public void setRecordingTimeTextColor(int color) {
236 mRecordingTimeView.setTextColor(color);
237 }
238
Doris Liu6432cd62013-06-13 17:20:31 -0700239 public boolean isVisible() {
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000240 return false;
Doris Liu6432cd62013-06-13 17:20:31 -0700241 }
242
Doris Liu06db7422013-12-09 19:36:25 -0800243 @Override
244 public GestureDetector.OnGestureListener getGestureListener() {
245 return mPreviewGestureListener;
246 }
247
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800248 @Override
249 public View.OnTouchListener getTouchListener() {
250 return null;
251 }
252
Doris Liu38c6bc32014-01-16 18:03:18 -0800253 /**
254 * Shows or hides focus UI.
255 *
256 * @param show shows focus UI when true, hides it otherwise
257 */
258 public void showFocusUI(boolean show) {
259 if (mFocusUI != null) {
260 mFocusUI.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
261 }
262 }
263
Doris Liud6487c92014-02-28 10:35:45 -0800264 /**
265 * Shows or hides video recording hints.
266 *
267 * @param show shows video recording hints when true, hides it otherwise.
268 */
269 public void showVideoRecordingHints(boolean show) {
270 mVideoHints.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
271 }
272
Angus Kong5f8c30e2014-03-06 17:15:08 -0800273 /**
274 * @return The size of the available preview area.
275 */
276 public Point getPreviewScreenSize() {
277 return new Point(mRootView.getMeasuredWidth(), mRootView.getMeasuredHeight());
278 }
279
Doris Liu8dc878f2014-05-05 14:10:34 -0700280 public void onOrientationChanged(int orientation) {
281 mVideoHints.onOrientationChanged(orientation);
282 }
283
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800284 private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener {
Doris Liu6827ce22013-03-12 19:24:28 -0700285 @Override
286 public void onZoomValueChanged(int index) {
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800287 mController.onZoomChanged(index);
Doris Liu6827ce22013-03-12 19:24:28 -0700288 }
289
290 @Override
291 public void onZoomStart() {
292 }
293
294 @Override
295 public void onZoomEnd() {
296 }
297 }
Michael Kolb3bc96b22013-03-12 10:24:42 -0700298
Doris Liu6432cd62013-06-13 17:20:31 -0700299 // SurfaceTexture callbacks
Michael Kolb3bc96b22013-03-12 10:24:42 -0700300 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700301 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Angus Kong395ee2d2013-07-15 12:42:41 -0700302 mController.onPreviewUIReady();
Michael Kolb3bc96b22013-03-12 10:24:42 -0700303 }
304
Doris Liu6432cd62013-06-13 17:20:31 -0700305 @Override
306 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Angus Kong395ee2d2013-07-15 12:42:41 -0700307 mController.onPreviewUIDestroyed();
Doris Liu6432cd62013-06-13 17:20:31 -0700308 Log.d(TAG, "surfaceTexture is destroyed");
309 return true;
310 }
311
312 @Override
313 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
314 }
315
316 @Override
317 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
318 }
Doris Liu6827ce22013-03-12 19:24:28 -0700319}