/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.SurfaceTexture; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.android.camera.debug.Log; import com.android.camera.ui.FocusOverlay; import com.android.camera.ui.PreviewOverlay; import com.android.camera.ui.PreviewStatusListener; import com.android.camera.ui.RotateLayout; import com.android.camera.widget.VideoRecordingHints; import com.android.camera2.R; import com.android.ex.camera2.portability.CameraCapabilities; import com.android.ex.camera2.portability.CameraSettings; import java.util.List; public class VideoUI implements PreviewStatusListener { private static final Log.Tag TAG = new Log.Tag("VideoUI"); private final static float UNSET = 0f; private final PreviewOverlay mPreviewOverlay; // module fields private final CameraActivity mActivity; private final View mRootView; private final FocusOverlay mFocusUI; // An review image having same size as preview. It is displayed when // recording is stopped in capture intent. private ImageView mReviewImage; private VideoRecordingHints mVideoHints; private TextView mRecordingTimeView; private LinearLayout mLabelsLinearLayout; private RotateLayout mRecordingTimeRect; private boolean mRecordingStarted = false; private final VideoController mController; private float mZoomMax; private float mAspectRatio = UNSET; private final AnimationManager mAnimationManager; @Override public void onPreviewLayoutChanged(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { } @Override public boolean shouldAutoAdjustTransformMatrixOnLayout() { return true; } @Override public boolean shouldAutoAdjustBottomBar() { return true; } @Override public void onPreviewFlipped() { mController.updateCameraOrientation(); } private final GestureDetector.OnGestureListener mPreviewGestureListener = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent ev) { if (mVideoHints.getVisibility() == View.VISIBLE) { mVideoHints.setVisibility(View.INVISIBLE); } else { mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY()); } return true; } }; public VideoUI(CameraActivity activity, VideoController controller, View parent) { mActivity = activity; mController = controller; mRootView = parent; ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout); mActivity.getLayoutInflater().inflate(R.layout.video_module, moduleRoot, true); mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay); initializeMiscControls(); mAnimationManager = new AnimationManager(); mFocusUI = (FocusOverlay) mRootView.findViewById(R.id.focus_overlay); mVideoHints = (VideoRecordingHints) mRootView.findViewById(R.id.video_shooting_hints); } public void setPreviewSize(int width, int height) { if (width == 0 || height == 0) { Log.w(TAG, "Preview size should not be 0."); return; } float aspectRatio; if (width > height) { aspectRatio = (float) width / height; } else { aspectRatio = (float) height / width; } setAspectRatio(aspectRatio); } public FocusOverlayManager.FocusUI getFocusUI() { return mFocusUI; } /** * Starts a flash animation */ public void animateFlash() { mController.startPreCaptureAnimation(); } /** * Cancels on-going animations */ public void cancelAnimations() { mAnimationManager.cancelAnimations(); } public void setOrientationIndicator(int orientation, boolean animation) { // We change the orientation of the linearlayout only for phone UI // because when in portrait the width is not enough. if (mLabelsLinearLayout != null) { if (((orientation / 90) & 1) == 0) { mLabelsLinearLayout.setOrientation(LinearLayout.VERTICAL); } else { mLabelsLinearLayout.setOrientation(LinearLayout.HORIZONTAL); } } mRecordingTimeRect.setOrientation(0, animation); } private void initializeMiscControls() { mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image); mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time); mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect); // The R.id.labels can only be found in phone layout. // That is, mLabelsLinearLayout should be null in tablet layout. mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels); } public void updateOnScreenIndicators(CameraSettings settings) { } public void setAspectRatio(float ratio) { if (ratio <= 0) { return; } float aspectRatio = ratio > 1 ? ratio : 1 / ratio; if (aspectRatio != mAspectRatio) { mAspectRatio = aspectRatio; mController.updatePreviewAspectRatio(mAspectRatio); } } public void setSwipingEnabled(boolean enable) { mActivity.setSwipingEnabled(enable); } public void showPreviewBorder(boolean enable) { // TODO: mPreviewFrameLayout.showBorder(enable); } public void showRecordingUI(boolean recording) { mRecordingStarted = recording; if (recording) { mRecordingTimeView.setText(""); mRecordingTimeView.setVisibility(View.VISIBLE); mRecordingTimeView.announceForAccessibility( mActivity.getResources().getString(R.string.video_recording_started)); } else { mRecordingTimeView.announceForAccessibility( mActivity.getResources().getString(R.string.video_recording_stopped)); mRecordingTimeView.setVisibility(View.GONE); } } public void showReviewImage(Bitmap bitmap) { mReviewImage.setImageBitmap(bitmap); mReviewImage.setVisibility(View.VISIBLE); } public void showReviewControls() { mActivity.getCameraAppUI().transitionToIntentReviewLayout(); mReviewImage.setVisibility(View.VISIBLE); } public void initializeZoom(CameraSettings settings, CameraCapabilities capabilities) { mZoomMax = capabilities.getMaxZoomRatio(); // Currently we use immediate zoom for fast zooming to get better UX and // there is no plan to take advantage of the smooth zoom. // TODO: setup zoom through App UI. mPreviewOverlay.setupZoom(mZoomMax, settings.getCurrentZoomRatio(), new ZoomChangeListener()); } public void setRecordingTime(String text) { mRecordingTimeView.setText(text); } public void setRecordingTimeTextColor(int color) { mRecordingTimeView.setTextColor(color); } public boolean isVisible() { return false; } @Override public GestureDetector.OnGestureListener getGestureListener() { return mPreviewGestureListener; } @Override public View.OnTouchListener getTouchListener() { return null; } /** * Shows or hides focus UI. * * @param show shows focus UI when true, hides it otherwise */ public void showFocusUI(boolean show) { if (mFocusUI != null) { mFocusUI.setVisibility(show ? View.VISIBLE : View.INVISIBLE); } } /** * Shows or hides video recording hints. * * @param show shows video recording hints when true, hides it otherwise. */ public void showVideoRecordingHints(boolean show) { mVideoHints.setVisibility(show ? View.VISIBLE : View.INVISIBLE); } /** * @return The size of the available preview area. */ public Point getPreviewScreenSize() { return new Point(mRootView.getMeasuredWidth(), mRootView.getMeasuredHeight()); } public void onOrientationChanged(int orientation) { mVideoHints.onOrientationChanged(orientation); } private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener { @Override public void onZoomValueChanged(float ratio) { mController.onZoomChanged(ratio); } @Override public void onZoomStart() { } @Override public void onZoomEnd() { } } // SurfaceTexture callbacks @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mController.onPreviewUIReady(); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { mController.onPreviewUIDestroyed(); Log.d(TAG, "surfaceTexture is destroyed"); return true; } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } public void onPause() { // recalculate aspect ratio when restarting. mAspectRatio = 0.0f; } }