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