blob: 76daf3aecd8347005170f3067d43d9531d47accc [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 Liuf9e4f8f2013-12-04 18:04:22 -080022import android.view.GestureDetector;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080023import android.view.MotionEvent;
Doris Liu6827ce22013-03-12 19:24:28 -070024import android.view.View;
Doris Liu6827ce22013-03-12 19:24:28 -070025import android.view.ViewGroup;
Doris Liu6827ce22013-03-12 19:24:28 -070026import android.widget.ImageView;
27import android.widget.LinearLayout;
28import android.widget.TextView;
29
Senpo Hu0bdc4b52014-11-17 22:56:31 -080030import com.android.camera.app.OrientationManager;
Angus Kong5596b4c2014-03-11 16:27:30 -070031import com.android.camera.debug.Log;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080032import com.android.camera.ui.PreviewOverlay;
Doris Liu06db7422013-12-09 19:36:25 -080033import com.android.camera.ui.PreviewStatusListener;
Doris Liu6827ce22013-03-12 19:24:28 -070034import com.android.camera.ui.RotateLayout;
Paul Rohde987ee642014-12-05 12:17:15 -080035import com.android.camera.ui.focus.FocusRing;
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;
Sol Boucher5a344962014-06-17 14:05:08 -070038import com.android.ex.camera2.portability.CameraCapabilities;
39import com.android.ex.camera2.portability.CameraSettings;
Sascha Haeberling638e6f02013-09-18 14:28:51 -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;
Paul Rohde987ee642014-12-05 12:17:15 -080049 private final FocusRing mFocusRing;
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;
Doris Liu6827ce22013-03-12 19:24:28 -070056 private RotateLayout mRecordingTimeRect;
Doris Liufe6596c2013-10-08 11:03:37 -070057 private boolean mRecordingStarted = false;
Sascha Haeberling67019c72013-12-14 01:08:38 -080058 private final VideoController mController;
Sol Boucher2192fba2014-08-19 17:24:07 -070059 private float mZoomMax;
Doris Liu6827ce22013-03-12 19:24:28 -070060
Doris Liu70da9182013-12-17 18:41:15 -080061 private float mAspectRatio = UNSET;
Sascha Haeberling37f36112013-08-06 14:31:52 -070062 private final AnimationManager mAnimationManager;
Doris Liue038c162013-12-13 23:06:11 -080063
Doris Liu4a010db2013-12-16 18:44:49 -080064 @Override
65 public void onPreviewLayoutChanged(View v, int left, int top, int right,
66 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
Doris Liu70da9182013-12-17 18:41:15 -080067 }
68
69 @Override
70 public boolean shouldAutoAdjustTransformMatrixOnLayout() {
71 return true;
72 }
73
74 @Override
75 public boolean shouldAutoAdjustBottomBar() {
76 return true;
Doris Liu4a010db2013-12-16 18:44:49 -080077 }
Doris Liu6432cd62013-06-13 17:20:31 -070078
Doris Liu11ddd732014-01-07 10:52:11 -080079 @Override
80 public void onPreviewFlipped() {
81 mController.updateCameraOrientation();
82 }
83
Doris Liu06db7422013-12-09 19:36:25 -080084 private final GestureDetector.OnGestureListener mPreviewGestureListener
85 = new GestureDetector.SimpleOnGestureListener() {
86 @Override
87 public boolean onSingleTapUp(MotionEvent ev) {
Doris Liud6487c92014-02-28 10:35:45 -080088 if (mVideoHints.getVisibility() == View.VISIBLE) {
89 mVideoHints.setVisibility(View.INVISIBLE);
90 } else {
91 mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY());
92 }
Doris Liu06db7422013-12-09 19:36:25 -080093 return true;
94 }
95 };
Doris Liu3a45c332013-10-15 19:10:28 -070096
Doris Liu6827ce22013-03-12 19:24:28 -070097 public VideoUI(CameraActivity activity, VideoController controller, View parent) {
98 mActivity = activity;
99 mController = controller;
100 mRootView = parent;
Doris Liuf55f3c42013-11-20 00:24:46 -0800101 ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout);
102 mActivity.getLayoutInflater().inflate(R.layout.video_module,
Sascha Haeberling67019c72013-12-14 01:08:38 -0800103 moduleRoot, true);
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000104
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800105 mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay);
Doris Liue038c162013-12-13 23:06:11 -0800106
Doris Liu6827ce22013-03-12 19:24:28 -0700107 initializeMiscControls();
Sascha Haeberling37f36112013-08-06 14:31:52 -0700108 mAnimationManager = new AnimationManager();
Paul Rohde987ee642014-12-05 12:17:15 -0800109 mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring);
Doris Liu8dc878f2014-05-05 14:10:34 -0700110 mVideoHints = (VideoRecordingHints) mRootView.findViewById(R.id.video_shooting_hints);
Doris Liu6827ce22013-03-12 19:24:28 -0700111 }
112
Doris Liu6432cd62013-06-13 17:20:31 -0700113 public void setPreviewSize(int width, int height) {
114 if (width == 0 || height == 0) {
115 Log.w(TAG, "Preview size should not be 0.");
116 return;
117 }
Doris Liue038c162013-12-13 23:06:11 -0800118 float aspectRatio;
Doris Liu6432cd62013-06-13 17:20:31 -0700119 if (width > height) {
Doris Liue038c162013-12-13 23:06:11 -0800120 aspectRatio = (float) width / height;
Doris Liu6432cd62013-06-13 17:20:31 -0700121 } else {
Doris Liue038c162013-12-13 23:06:11 -0800122 aspectRatio = (float) height / width;
Doris Liu6432cd62013-06-13 17:20:31 -0700123 }
Doris Liue038c162013-12-13 23:06:11 -0800124 setAspectRatio(aspectRatio);
Doris Liu6432cd62013-06-13 17:20:31 -0700125 }
126
Paul Rohde987ee642014-12-05 12:17:15 -0800127 public FocusRing getFocusRing() {
128 return mFocusRing;
Doris Liua1ec04a2014-01-13 17:29:40 -0800129 }
130
Sascha Haeberling37f36112013-08-06 14:31:52 -0700131 /**
Sascha Haeberling37f36112013-08-06 14:31:52 -0700132 * Cancels on-going animations
133 */
134 public void cancelAnimations() {
135 mAnimationManager.cancelAnimations();
136 }
137
Doris Liu6827ce22013-03-12 19:24:28 -0700138 public void setOrientationIndicator(int orientation, boolean animation) {
Doris Liu6827ce22013-03-12 19:24:28 -0700139 // We change the orientation of the linearlayout only for phone UI
140 // because when in portrait the width is not enough.
141 if (mLabelsLinearLayout != null) {
142 if (((orientation / 90) & 1) == 0) {
143 mLabelsLinearLayout.setOrientation(LinearLayout.VERTICAL);
144 } else {
145 mLabelsLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
146 }
147 }
148 mRecordingTimeRect.setOrientation(0, animation);
149 }
150
Doris Liu6827ce22013-03-12 19:24:28 -0700151 private void initializeMiscControls() {
Doris Liu6827ce22013-03-12 19:24:28 -0700152 mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
Doris Liu6827ce22013-03-12 19:24:28 -0700153 mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time);
154 mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect);
Doris Liu6827ce22013-03-12 19:24:28 -0700155 // The R.id.labels can only be found in phone layout.
156 // That is, mLabelsLinearLayout should be null in tablet layout.
157 mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels);
158 }
159
Angus Kong6607dae2014-06-10 16:07:45 -0700160 public void updateOnScreenIndicators(CameraSettings settings) {
Doris Liu6827ce22013-03-12 19:24:28 -0700161 }
162
Doris Liue038c162013-12-13 23:06:11 -0800163 public void setAspectRatio(float ratio) {
164 if (ratio <= 0) {
165 return;
166 }
167 float aspectRatio = ratio > 1 ? ratio : 1 / ratio;
168 if (aspectRatio != mAspectRatio) {
169 mAspectRatio = aspectRatio;
Doris Liu70da9182013-12-17 18:41:15 -0800170 mController.updatePreviewAspectRatio(mAspectRatio);
Doris Liue038c162013-12-13 23:06:11 -0800171 }
Doris Liu6827ce22013-03-12 19:24:28 -0700172 }
173
Doris Liu6432cd62013-06-13 17:20:31 -0700174 public void setSwipingEnabled(boolean enable) {
175 mActivity.setSwipingEnabled(enable);
Doris Liu6827ce22013-03-12 19:24:28 -0700176 }
177
178 public void showPreviewBorder(boolean enable) {
Doris Liu6432cd62013-06-13 17:20:31 -0700179 // TODO: mPreviewFrameLayout.showBorder(enable);
Doris Liu6827ce22013-03-12 19:24:28 -0700180 }
181
Doris Liufe6596c2013-10-08 11:03:37 -0700182 public void showRecordingUI(boolean recording) {
183 mRecordingStarted = recording;
Doris Liu6827ce22013-03-12 19:24:28 -0700184 if (recording) {
Doris Liu6827ce22013-03-12 19:24:28 -0700185 mRecordingTimeView.setText("");
186 mRecordingTimeView.setVisibility(View.VISIBLE);
Alan Newbergerf2711922014-03-26 11:28:50 -0700187 mRecordingTimeView.announceForAccessibility(
188 mActivity.getResources().getString(R.string.video_recording_started));
Doris Liu6827ce22013-03-12 19:24:28 -0700189 } else {
Alan Newbergerf2711922014-03-26 11:28:50 -0700190 mRecordingTimeView.announceForAccessibility(
191 mActivity.getResources().getString(R.string.video_recording_stopped));
Doris Liu6827ce22013-03-12 19:24:28 -0700192 mRecordingTimeView.setVisibility(View.GONE);
Doris Liu6827ce22013-03-12 19:24:28 -0700193 }
194 }
195
196 public void showReviewImage(Bitmap bitmap) {
197 mReviewImage.setImageBitmap(bitmap);
198 mReviewImage.setVisibility(View.VISIBLE);
199 }
200
201 public void showReviewControls() {
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800202 mActivity.getCameraAppUI().transitionToIntentReviewLayout();
Doris Liu6827ce22013-03-12 19:24:28 -0700203 mReviewImage.setVisibility(View.VISIBLE);
Doris Liu6827ce22013-03-12 19:24:28 -0700204 }
205
Angus Kong6607dae2014-06-10 16:07:45 -0700206 public void initializeZoom(CameraSettings settings, CameraCapabilities capabilities) {
Sol Boucher2192fba2014-08-19 17:24:07 -0700207 mZoomMax = capabilities.getMaxZoomRatio();
Doris Liu6827ce22013-03-12 19:24:28 -0700208 // Currently we use immediate zoom for fast zooming to get better UX and
209 // there is no plan to take advantage of the smooth zoom.
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800210 // TODO: setup zoom through App UI.
Sol Boucher2192fba2014-08-19 17:24:07 -0700211 mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomRatio(),
Angus Kong6607dae2014-06-10 16:07:45 -0700212 new ZoomChangeListener());
Doris Liu6827ce22013-03-12 19:24:28 -0700213 }
214
Doris Liu6827ce22013-03-12 19:24:28 -0700215 public void setRecordingTime(String text) {
216 mRecordingTimeView.setText(text);
217 }
218
219 public void setRecordingTimeTextColor(int color) {
220 mRecordingTimeView.setTextColor(color);
221 }
222
Doris Liu6432cd62013-06-13 17:20:31 -0700223 public boolean isVisible() {
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000224 return false;
Doris Liu6432cd62013-06-13 17:20:31 -0700225 }
226
Doris Liu06db7422013-12-09 19:36:25 -0800227 @Override
228 public GestureDetector.OnGestureListener getGestureListener() {
229 return mPreviewGestureListener;
230 }
231
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800232 @Override
233 public View.OnTouchListener getTouchListener() {
234 return null;
235 }
236
Doris Liu38c6bc32014-01-16 18:03:18 -0800237 /**
Paul Rohde987ee642014-12-05 12:17:15 -0800238 * Hide the focus indicator.
Doris Liu38c6bc32014-01-16 18:03:18 -0800239 */
Paul Rohde987ee642014-12-05 12:17:15 -0800240 public void hidePassiveFocusIndicator() {
241 if (mFocusRing != null) {
242 Log.v(TAG, "mFocusRing.stopFocusAnimations()");
243 mFocusRing.stopFocusAnimations();
Doris Liu38c6bc32014-01-16 18:03:18 -0800244 }
245 }
246
Doris Liud6487c92014-02-28 10:35:45 -0800247 /**
Paul Rohde987ee642014-12-05 12:17:15 -0800248 * Show the passive focus indicator.
249 */
250 public void showPassiveFocusIndicator() {
251 if (mFocusRing != null) {
252 mFocusRing.startPassiveFocus();
253 }
254 }
255
256
257 /**
Doris Liud6487c92014-02-28 10:35:45 -0800258 * Shows or hides video recording hints.
259 *
260 * @param show shows video recording hints when true, hides it otherwise.
261 */
262 public void showVideoRecordingHints(boolean show) {
263 mVideoHints.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
264 }
265
Angus Kong5f8c30e2014-03-06 17:15:08 -0800266 /**
267 * @return The size of the available preview area.
268 */
269 public Point getPreviewScreenSize() {
270 return new Point(mRootView.getMeasuredWidth(), mRootView.getMeasuredHeight());
271 }
272
Senpo Hu0bdc4b52014-11-17 22:56:31 -0800273 public void onOrientationChanged(OrientationManager orientationManager,
274 OrientationManager.DeviceOrientation deviceOrientation) {
275 mVideoHints.onOrientationChanged(orientationManager, deviceOrientation);
Doris Liu8dc878f2014-05-05 14:10:34 -0700276 }
277
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800278 private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener {
Doris Liu6827ce22013-03-12 19:24:28 -0700279 @Override
Sol Boucher2192fba2014-08-19 17:24:07 -0700280 public void onZoomValueChanged(float ratio) {
281 mController.onZoomChanged(ratio);
Doris Liu6827ce22013-03-12 19:24:28 -0700282 }
283
284 @Override
285 public void onZoomStart() {
286 }
287
288 @Override
289 public void onZoomEnd() {
290 }
291 }
Michael Kolb3bc96b22013-03-12 10:24:42 -0700292
Doris Liu6432cd62013-06-13 17:20:31 -0700293 // SurfaceTexture callbacks
Michael Kolb3bc96b22013-03-12 10:24:42 -0700294 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700295 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Angus Kong395ee2d2013-07-15 12:42:41 -0700296 mController.onPreviewUIReady();
Michael Kolb3bc96b22013-03-12 10:24:42 -0700297 }
298
Doris Liu6432cd62013-06-13 17:20:31 -0700299 @Override
300 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Angus Kong395ee2d2013-07-15 12:42:41 -0700301 mController.onPreviewUIDestroyed();
Doris Liu6432cd62013-06-13 17:20:31 -0700302 Log.d(TAG, "surfaceTexture is destroyed");
303 return true;
304 }
305
306 @Override
307 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
308 }
309
310 @Override
311 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
312 }
Alan Newberger73514152014-09-10 15:03:27 -0700313
314 public void onPause() {
315 // recalculate aspect ratio when restarting.
316 mAspectRatio = 0.0f;
317 }
Doris Liu6827ce22013-03-12 19:24:28 -0700318}