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 17 package com.android.camera; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Point; 21 import android.graphics.SurfaceTexture; 22 import android.view.GestureDetector; 23 import android.view.MotionEvent; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.widget.ImageView; 27 import android.widget.LinearLayout; 28 import android.widget.TextView; 29 30 import com.android.camera.debug.Log; 31 import com.android.camera.ui.FocusOverlay; 32 import com.android.camera.ui.PreviewOverlay; 33 import com.android.camera.ui.PreviewStatusListener; 34 import com.android.camera.ui.RotateLayout; 35 import com.android.camera.widget.VideoRecordingHints; 36 import com.android.camera2.R; 37 import com.android.ex.camera2.portability.CameraCapabilities; 38 import com.android.ex.camera2.portability.CameraSettings; 39 40 import java.util.List; 41 42 public 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 onPreviewLayoutChanged(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)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 shouldAutoAdjustTransformMatrixOnLayout()71 public boolean shouldAutoAdjustTransformMatrixOnLayout() { 72 return true; 73 } 74 75 @Override shouldAutoAdjustBottomBar()76 public boolean shouldAutoAdjustBottomBar() { 77 return true; 78 } 79 80 @Override onPreviewFlipped()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 VideoUI(CameraActivity activity, VideoController controller, View parent)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 setPreviewSize(int width, int height)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 getFocusUI()128 public FocusOverlayManager.FocusUI getFocusUI() { 129 return mFocusUI; 130 } 131 132 /** 133 * Starts a flash animation 134 */ animateFlash()135 public void animateFlash() { 136 mController.startPreCaptureAnimation(); 137 } 138 139 /** 140 * Cancels on-going animations 141 */ cancelAnimations()142 public void cancelAnimations() { 143 mAnimationManager.cancelAnimations(); 144 } 145 setOrientationIndicator(int orientation, boolean animation)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 initializeMiscControls()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 updateOnScreenIndicators(CameraSettings settings)168 public void updateOnScreenIndicators(CameraSettings settings) { 169 } 170 setAspectRatio(float ratio)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 setSwipingEnabled(boolean enable)182 public void setSwipingEnabled(boolean enable) { 183 mActivity.setSwipingEnabled(enable); 184 } 185 showPreviewBorder(boolean enable)186 public void showPreviewBorder(boolean enable) { 187 // TODO: mPreviewFrameLayout.showBorder(enable); 188 } 189 showRecordingUI(boolean recording)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 showReviewImage(Bitmap bitmap)204 public void showReviewImage(Bitmap bitmap) { 205 mReviewImage.setImageBitmap(bitmap); 206 mReviewImage.setVisibility(View.VISIBLE); 207 } 208 showReviewControls()209 public void showReviewControls() { 210 mActivity.getCameraAppUI().transitionToIntentReviewLayout(); 211 mReviewImage.setVisibility(View.VISIBLE); 212 } 213 initializeZoom(CameraSettings settings, CameraCapabilities capabilities)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 setRecordingTime(String text)223 public void setRecordingTime(String text) { 224 mRecordingTimeView.setText(text); 225 } 226 setRecordingTimeTextColor(int color)227 public void setRecordingTimeTextColor(int color) { 228 mRecordingTimeView.setTextColor(color); 229 } 230 isVisible()231 public boolean isVisible() { 232 return false; 233 } 234 235 @Override getGestureListener()236 public GestureDetector.OnGestureListener getGestureListener() { 237 return mPreviewGestureListener; 238 } 239 240 @Override getTouchListener()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 */ showFocusUI(boolean show)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 */ showVideoRecordingHints(boolean show)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 */ getPreviewScreenSize()268 public Point getPreviewScreenSize() { 269 return new Point(mRootView.getMeasuredWidth(), mRootView.getMeasuredHeight()); 270 } 271 onOrientationChanged(int orientation)272 public void onOrientationChanged(int orientation) { 273 mVideoHints.onOrientationChanged(orientation); 274 } 275 276 private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener { 277 @Override onZoomValueChanged(float ratio)278 public void onZoomValueChanged(float ratio) { 279 mController.onZoomChanged(ratio); 280 } 281 282 @Override onZoomStart()283 public void onZoomStart() { 284 } 285 286 @Override onZoomEnd()287 public void onZoomEnd() { 288 } 289 } 290 291 // SurfaceTexture callbacks 292 @Override onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)293 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 294 mController.onPreviewUIReady(); 295 } 296 297 @Override onSurfaceTextureDestroyed(SurfaceTexture surface)298 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 299 mController.onPreviewUIDestroyed(); 300 Log.d(TAG, "surfaceTexture is destroyed"); 301 return true; 302 } 303 304 @Override onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)305 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 306 } 307 308 @Override onSurfaceTextureUpdated(SurfaceTexture surface)309 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 310 } 311 onPause()312 public void onPause() { 313 // recalculate aspect ratio when restarting. 314 mAspectRatio = 0.0f; 315 } 316 } 317