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