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.app.OrientationManager; 31 import com.android.camera.debug.Log; 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.ui.focus.FocusRing; 36 import com.android.camera2.R; 37 import com.android.ex.camera2.portability.CameraCapabilities; 38 import com.android.ex.camera2.portability.CameraSettings; 39 40 public class VideoUI implements PreviewStatusListener { 41 private static final Log.Tag TAG = new Log.Tag("VideoUI"); 42 43 private final static float UNSET = 0f; 44 private final PreviewOverlay mPreviewOverlay; 45 // module fields 46 private final CameraActivity mActivity; 47 private final View mRootView; 48 private final FocusRing mFocusRing; 49 // An review image having same size as preview. It is displayed when 50 // recording is stopped in capture intent. 51 private ImageView mReviewImage; 52 private TextView mRecordingTimeView; 53 private LinearLayout mLabelsLinearLayout; 54 private RotateLayout mRecordingTimeRect; 55 private boolean mRecordingStarted = false; 56 private final VideoController mController; 57 private float mZoomMax; 58 59 private float mAspectRatio = UNSET; 60 private final AnimationManager mAnimationManager; 61 62 @Override 63 public void onPreviewLayoutChanged(View v, int left, int top, int right, 64 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 65 } 66 67 @Override 68 public boolean shouldAutoAdjustTransformMatrixOnLayout() { 69 return true; 70 } 71 72 @Override 73 public void onPreviewFlipped() { 74 mController.updateCameraOrientation(); 75 } 76 77 private final GestureDetector.OnGestureListener mPreviewGestureListener 78 = new GestureDetector.SimpleOnGestureListener() { 79 @Override 80 public boolean onSingleTapUp(MotionEvent ev) { 81 mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY()); 82 return true; 83 } 84 }; 85 86 public VideoUI(CameraActivity activity, VideoController controller, View parent) { 87 mActivity = activity; 88 mController = controller; 89 mRootView = parent; 90 ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout); 91 mActivity.getLayoutInflater().inflate(R.layout.video_module, 92 moduleRoot, true); 93 94 mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay); 95 96 initializeMiscControls(); 97 mAnimationManager = new AnimationManager(); 98 mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring); 99 } 100 101 public void setPreviewSize(int width, int height) { 102 if (width == 0 || height == 0) { 103 Log.w(TAG, "Preview size should not be 0."); 104 return; 105 } 106 float aspectRatio; 107 if (width > height) { 108 aspectRatio = (float) width / height; 109 } else { 110 aspectRatio = (float) height / width; 111 } 112 setAspectRatio(aspectRatio); 113 } 114 115 public FocusRing getFocusRing() { 116 return mFocusRing; 117 } 118 119 /** 120 * Cancels on-going animations 121 */ 122 public void cancelAnimations() { 123 mAnimationManager.cancelAnimations(); 124 } 125 126 public void setOrientationIndicator(int orientation, boolean animation) { 127 // We change the orientation of the linearlayout only for phone UI 128 // because when in portrait the width is not enough. 129 if (mLabelsLinearLayout != null) { 130 if (((orientation / 90) & 1) == 0) { 131 mLabelsLinearLayout.setOrientation(LinearLayout.VERTICAL); 132 } else { 133 mLabelsLinearLayout.setOrientation(LinearLayout.HORIZONTAL); 134 } 135 } 136 mRecordingTimeRect.setOrientation(0, animation); 137 } 138 139 private void initializeMiscControls() { 140 mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image); 141 mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time); 142 mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect); 143 // The R.id.labels can only be found in phone layout. 144 // That is, mLabelsLinearLayout should be null in tablet layout. 145 mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels); 146 } 147 148 public void updateOnScreenIndicators(CameraSettings settings) { 149 } 150 151 public void setAspectRatio(float ratio) { 152 if (ratio <= 0) { 153 return; 154 } 155 float aspectRatio = ratio > 1 ? ratio : 1 / ratio; 156 if (aspectRatio != mAspectRatio) { 157 mAspectRatio = aspectRatio; 158 mController.updatePreviewAspectRatio(mAspectRatio); 159 } 160 } 161 162 public void setSwipingEnabled(boolean enable) { 163 mActivity.setSwipingEnabled(enable); 164 } 165 166 public void showPreviewBorder(boolean enable) { 167 // TODO: mPreviewFrameLayout.showBorder(enable); 168 } 169 170 public void showRecordingUI(boolean recording) { 171 mRecordingStarted = recording; 172 if (recording) { 173 mRecordingTimeView.setText(""); 174 mRecordingTimeView.setVisibility(View.VISIBLE); 175 mRecordingTimeView.announceForAccessibility( 176 mActivity.getResources().getString(R.string.video_recording_started)); 177 } else { 178 mRecordingTimeView.announceForAccessibility( 179 mActivity.getResources().getString(R.string.video_recording_stopped)); 180 mRecordingTimeView.setVisibility(View.GONE); 181 } 182 } 183 184 public void showReviewImage(Bitmap bitmap) { 185 mReviewImage.setImageBitmap(bitmap); 186 mReviewImage.setVisibility(View.VISIBLE); 187 } 188 189 public void showReviewControls() { 190 mActivity.getCameraAppUI().transitionToIntentReviewLayout(); 191 mReviewImage.setVisibility(View.VISIBLE); 192 } 193 194 public void initializeZoom(CameraSettings settings, CameraCapabilities capabilities) { 195 mZoomMax = capabilities.getMaxZoomRatio(); 196 // Currently we use immediate zoom for fast zooming to get better UX and 197 // there is no plan to take advantage of the smooth zoom. 198 // TODO: setup zoom through App UI. 199 mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomRatio(), 200 new ZoomChangeListener()); 201 } 202 203 public void setRecordingTime(String text) { 204 mRecordingTimeView.setText(text); 205 } 206 207 public void setRecordingTimeTextColor(int color) { 208 mRecordingTimeView.setTextColor(color); 209 } 210 211 public boolean isVisible() { 212 return false; 213 } 214 215 @Override 216 public GestureDetector.OnGestureListener getGestureListener() { 217 return mPreviewGestureListener; 218 } 219 220 @Override 221 public View.OnTouchListener getTouchListener() { 222 return null; 223 } 224 225 /** 226 * Hide the focus indicator. 227 */ 228 public void hidePassiveFocusIndicator() { 229 if (mFocusRing != null) { 230 Log.v(TAG, "mFocusRing.stopFocusAnimations()"); 231 mFocusRing.stopFocusAnimations(); 232 } 233 } 234 235 /** 236 * Show the passive focus indicator. 237 */ 238 public void showPassiveFocusIndicator() { 239 if (mFocusRing != null) { 240 mFocusRing.startPassiveFocus(); 241 } 242 } 243 244 245 /** 246 * @return The size of the available preview area. 247 */ 248 public Point getPreviewScreenSize() { 249 return new Point(mRootView.getMeasuredWidth(), mRootView.getMeasuredHeight()); 250 } 251 252 /** 253 * Adjust UI to an orientation change if necessary. 254 */ 255 public void onOrientationChanged(OrientationManager orientationManager, 256 OrientationManager.DeviceOrientation deviceOrientation) { 257 // do nothing. 258 } 259 260 private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener { 261 @Override 262 public void onZoomValueChanged(float ratio) { 263 mController.onZoomChanged(ratio); 264 } 265 266 @Override 267 public void onZoomStart() { 268 } 269 270 @Override 271 public void onZoomEnd() { 272 } 273 } 274 275 // SurfaceTexture callbacks 276 @Override 277 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 278 mController.onPreviewUIReady(); 279 } 280 281 @Override 282 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 283 mController.onPreviewUIDestroyed(); 284 Log.d(TAG, "surfaceTexture is destroyed"); 285 return true; 286 } 287 288 @Override 289 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 290 } 291 292 @Override 293 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 294 } 295 296 public void onPause() { 297 // recalculate aspect ratio when restarting. 298 mAspectRatio = 0.0f; 299 } 300 } 301