| /* |
| * 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.app.AlertDialog; |
| import android.graphics.Bitmap; |
| import android.graphics.Matrix; |
| import android.graphics.SurfaceTexture; |
| import android.hardware.Camera; |
| import android.hardware.Camera.Face; |
| import android.os.AsyncTask; |
| import android.view.GestureDetector; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| |
| import com.android.camera.FocusOverlayManager.FocusUI; |
| import com.android.camera.app.CameraManager; |
| import com.android.camera.debug.Log; |
| import com.android.camera.ui.FaceView; |
| import com.android.camera.ui.PreviewOverlay; |
| import com.android.camera.ui.PreviewStatusListener; |
| import com.android.camera.util.CameraUtil; |
| import com.android.camera2.R; |
| |
| import java.util.List; |
| |
| public class PhotoUI implements PreviewStatusListener, |
| CameraManager.CameraFaceDetectionCallback { |
| |
| private static final Log.Tag TAG = new Log.Tag("PhotoUI"); |
| private static final int DOWN_SAMPLE_FACTOR = 4; |
| private static final float UNSET = 0f; |
| |
| private final PreviewOverlay mPreviewOverlay; |
| private final FocusUI mFocusUI; |
| private final CameraActivity mActivity; |
| private final PhotoController mController; |
| |
| private final View mRootView; |
| |
| // TODO: Remove face view logic if UX does not bring it back within a month. |
| private FaceView mFaceView = null; |
| private DecodeImageForReview mDecodeTaskForReview = null; |
| |
| private int mZoomMax; |
| private List<Integer> mZoomRatios; |
| |
| private int mPreviewWidth = 0; |
| private int mPreviewHeight = 0; |
| private float mAspectRatio = UNSET; |
| |
| private final GestureDetector.OnGestureListener mPreviewGestureListener |
| = new GestureDetector.SimpleOnGestureListener() { |
| @Override |
| public boolean onSingleTapUp(MotionEvent ev) { |
| mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY()); |
| return true; |
| } |
| }; |
| |
| @Override |
| public GestureDetector.OnGestureListener getGestureListener() { |
| return mPreviewGestureListener; |
| } |
| |
| @Override |
| public View.OnTouchListener getTouchListener() { |
| return null; |
| } |
| |
| @Override |
| public void onPreviewLayoutChanged(View v, int left, int top, int right, |
| int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { |
| int width = right - left; |
| int height = bottom - top; |
| if (mPreviewWidth != width || mPreviewHeight != height) { |
| mPreviewWidth = width; |
| mPreviewHeight = height; |
| } |
| } |
| |
| @Override |
| public boolean shouldAutoAdjustTransformMatrixOnLayout() { |
| return true; |
| } |
| |
| @Override |
| public boolean shouldAutoAdjustBottomBar() { |
| return true; |
| } |
| |
| @Override |
| public void onPreviewFlipped() { |
| mController.updateCameraOrientation(); |
| } |
| |
| private class DecodeTask extends AsyncTask<Void, Void, Bitmap> { |
| private final byte [] mData; |
| private final int mOrientation; |
| private final boolean mMirror; |
| |
| public DecodeTask(byte[] data, int orientation, boolean mirror) { |
| mData = data; |
| mOrientation = orientation; |
| mMirror = mirror; |
| } |
| |
| @Override |
| protected Bitmap doInBackground(Void... params) { |
| // Decode image in background. |
| Bitmap bitmap = CameraUtil.downSample(mData, DOWN_SAMPLE_FACTOR); |
| if (mOrientation != 0 || mMirror) { |
| Matrix m = new Matrix(); |
| if (mMirror) { |
| // Flip horizontally |
| m.setScale(-1f, 1f); |
| } |
| m.preRotate(mOrientation); |
| return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, |
| false); |
| } |
| return bitmap; |
| } |
| } |
| |
| private class DecodeImageForReview extends DecodeTask { |
| public DecodeImageForReview(byte[] data, int orientation, boolean mirror) { |
| super(data, orientation, mirror); |
| } |
| |
| @Override |
| protected void onPostExecute(Bitmap bitmap) { |
| if (isCancelled()) { |
| return; |
| } |
| mDecodeTaskForReview = null; |
| } |
| } |
| |
| public PhotoUI(CameraActivity activity, PhotoController controller, View parent) { |
| mActivity = activity; |
| mController = controller; |
| mRootView = parent; |
| |
| ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout); |
| mActivity.getLayoutInflater().inflate(R.layout.photo_module, |
| moduleRoot, true); |
| initIndicators(); |
| mFocusUI = (FocusUI) mRootView.findViewById(R.id.focus_overlay); |
| mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay); |
| } |
| |
| public FocusUI getFocusUI() { |
| return mFocusUI; |
| } |
| |
| public void updatePreviewAspectRatio(float aspectRatio) { |
| if (aspectRatio <= 0) { |
| Log.e(TAG, "Invalid aspect ratio: " + aspectRatio); |
| return; |
| } |
| if (aspectRatio < 1f) { |
| aspectRatio = 1f / aspectRatio; |
| } |
| |
| if (mAspectRatio != aspectRatio) { |
| mAspectRatio = aspectRatio; |
| // Update transform matrix with the new aspect ratio. |
| mController.updatePreviewAspectRatio(mAspectRatio); |
| } |
| } |
| |
| @Override |
| public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { |
| mController.onPreviewUIReady(); |
| } |
| |
| @Override |
| public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { |
| // Ignored, Camera does all the work for us |
| } |
| |
| @Override |
| public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { |
| mController.onPreviewUIDestroyed(); |
| return true; |
| } |
| |
| @Override |
| public void onSurfaceTextureUpdated(SurfaceTexture surface) { |
| } |
| |
| public View getRootView() { |
| return mRootView; |
| } |
| |
| private void initIndicators() { |
| // TODO init toggle buttons on bottom bar here |
| } |
| |
| public void onCameraOpened(Camera.Parameters params) { |
| initializeZoom(params); |
| } |
| |
| public void animateCapture(final byte[] jpegData, int orientation, boolean mirror) { |
| // Decode jpeg byte array and then animate the jpeg |
| DecodeTask task = new DecodeTask(jpegData, orientation, mirror); |
| task.execute(); |
| } |
| |
| // called from onResume but only the first time |
| public void initializeFirstTime() { |
| |
| } |
| |
| // called from onResume every other time |
| public void initializeSecondTime(Camera.Parameters params) { |
| initializeZoom(params); |
| if (mController.isImageCaptureIntent()) { |
| hidePostCaptureAlert(); |
| } |
| // Removes pie menu. |
| } |
| |
| public void showLocationDialog() { |
| AlertDialog alert = mActivity.getFirstTimeLocationAlert(); |
| alert.show(); |
| } |
| |
| public void initializeZoom(Camera.Parameters params) { |
| if ((params == null) || !params.isZoomSupported()) return; |
| mZoomMax = params.getMaxZoom(); |
| mZoomRatios = params.getZoomRatios(); |
| // 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: Need to setup a path to AppUI to do this |
| mPreviewOverlay.setupZoom(mZoomMax, params.getZoom(), mZoomRatios, new ZoomChangeListener()); |
| } |
| |
| public void animateFlash() { |
| mController.startPreCaptureAnimation(); |
| } |
| |
| public boolean onBackPressed() { |
| // In image capture mode, back button should: |
| // 1) if there is any popup, dismiss them, 2) otherwise, get out of |
| // image capture |
| if (mController.isImageCaptureIntent()) { |
| mController.onCaptureCancelled(); |
| return true; |
| } else if (!mController.isCameraIdle()) { |
| // ignore backs while we're taking a picture |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| protected void showCapturedImageForReview(byte[] jpegData, int orientation, boolean mirror) { |
| mDecodeTaskForReview = new DecodeImageForReview(jpegData, orientation, mirror); |
| mDecodeTaskForReview.execute(); |
| |
| mActivity.getCameraAppUI().transitionToIntentReviewLayout(); |
| pauseFaceDetection(); |
| } |
| |
| protected void hidePostCaptureAlert() { |
| if (mDecodeTaskForReview != null) { |
| mDecodeTaskForReview.cancel(true); |
| } |
| resumeFaceDetection(); |
| } |
| |
| public void setDisplayOrientation(int orientation) { |
| if (mFaceView != null) { |
| mFaceView.setDisplayOrientation(orientation); |
| } |
| } |
| |
| // shutter button handling |
| |
| public boolean isShutterPressed() { |
| return false; |
| } |
| |
| /** |
| * Enables or disables the shutter button. |
| */ |
| public void enableShutter(boolean enabled) { |
| |
| } |
| |
| public void pressShutterButton() { |
| |
| } |
| |
| private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener { |
| @Override |
| public void onZoomValueChanged(int index) { |
| mController.onZoomChanged(index); |
| } |
| |
| @Override |
| public void onZoomStart() { |
| } |
| |
| @Override |
| public void onZoomEnd() { |
| } |
| } |
| |
| public void setSwipingEnabled(boolean enable) { |
| mActivity.setSwipingEnabled(enable); |
| } |
| |
| public void onPause() { |
| if (mFaceView != null) mFaceView.clear(); |
| } |
| |
| public void clearFaces() { |
| if (mFaceView != null) { |
| mFaceView.clear(); |
| } |
| } |
| |
| public void pauseFaceDetection() { |
| if (mFaceView != null) mFaceView.pause(); |
| } |
| |
| public void resumeFaceDetection() { |
| if (mFaceView != null) mFaceView.resume(); |
| } |
| |
| public void onStartFaceDetection(int orientation, boolean mirror) { |
| if (mFaceView != null) { |
| mFaceView.clear(); |
| mFaceView.setVisibility(View.VISIBLE); |
| mFaceView.setDisplayOrientation(orientation); |
| mFaceView.setMirror(mirror); |
| mFaceView.resume(); |
| } |
| } |
| |
| @Override |
| public void onFaceDetection(Face[] faces, CameraManager.CameraProxy camera) { |
| if (mFaceView != null) { |
| mFaceView.setFaces(faces); |
| } |
| } |
| |
| /** |
| * Returns a {@link com.android.camera.ui.PreviewStatusListener.PreviewAreaChangedListener} |
| * that should be registered to listen to preview area change. |
| */ |
| public PreviewAreaChangedListener getPreviewAreaSizeChangedListener() { |
| return mFaceView; |
| } |
| |
| } |