diff options
Diffstat (limited to 'java/com/android/dialer/callcomposer/camera/CameraPreview.java')
-rw-r--r-- | java/com/android/dialer/callcomposer/camera/CameraPreview.java | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/java/com/android/dialer/callcomposer/camera/CameraPreview.java b/java/com/android/dialer/callcomposer/camera/CameraPreview.java new file mode 100644 index 000000000..6581ad67b --- /dev/null +++ b/java/com/android/dialer/callcomposer/camera/CameraPreview.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2016 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.dialer.callcomposer.camera; + +import android.content.Context; +import android.content.res.Configuration; +import android.hardware.Camera; +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.View.OnTouchListener; +import com.android.dialer.common.Assert; +import com.android.dialer.util.PermissionsUtil; +import java.io.IOException; + +/** + * Contains shared code for SoftwareCameraPreview and HardwareCameraPreview, cannot use inheritance + * because those classes must inherit from separate Views, so those classes delegate calls to this + * helper class. Specifics for each implementation are in CameraPreviewHost + */ +public class CameraPreview { + /** Implemented by the camera for rendering. */ + public interface CameraPreviewHost { + View getView(); + + boolean isValid(); + + void startPreview(final Camera camera) throws IOException; + + void onCameraPermissionGranted(); + + void setShown(); + } + + private int mCameraWidth = -1; + private int mCameraHeight = -1; + private boolean mTabHasBeenShown = false; + private OnTouchListener mListener; + + private final CameraPreviewHost mHost; + + public CameraPreview(final CameraPreviewHost host) { + Assert.isNotNull(host); + Assert.isNotNull(host.getView()); + mHost = host; + } + + // This is set when the tab is actually selected. + public void setShown() { + mTabHasBeenShown = true; + maybeOpenCamera(); + } + + // Opening camera is very expensive. Most of the ANR reports seem to be related to the camera. + // So we delay until the camera is actually needed. See b/23287938 + private void maybeOpenCamera() { + boolean visible = mHost.getView().getVisibility() == View.VISIBLE; + if (mTabHasBeenShown && visible && PermissionsUtil.hasCameraPermissions(getContext())) { + CameraManager.get().openCamera(); + } + } + + public void setSize(final Camera.Size size, final int orientation) { + switch (orientation) { + case 0: + case 180: + mCameraWidth = size.width; + mCameraHeight = size.height; + break; + case 90: + case 270: + default: + mCameraWidth = size.height; + mCameraHeight = size.width; + } + mHost.getView().requestLayout(); + } + + public int getWidthMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec) { + if (mCameraHeight >= 0) { + final int width = View.MeasureSpec.getSize(widthMeasureSpec); + return MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); + } else { + return widthMeasureSpec; + } + } + + public int getHeightMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec) { + if (mCameraHeight >= 0) { + final int orientation = getContext().getResources().getConfiguration().orientation; + final int width = View.MeasureSpec.getSize(widthMeasureSpec); + final float aspectRatio = (float) mCameraWidth / (float) mCameraHeight; + int height; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + height = (int) (width * aspectRatio); + } else { + height = (int) (width / aspectRatio); + } + return View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + } else { + return heightMeasureSpec; + } + } + + // onVisibilityChanged is set to Visible when the tab is _created_, + // which may be when the user is viewing a different tab. + public void onVisibilityChanged(final int visibility) { + if (PermissionsUtil.hasCameraPermissions(getContext())) { + if (visibility == View.VISIBLE) { + maybeOpenCamera(); + } else { + CameraManager.get().closeCamera(); + } + } + } + + public Context getContext() { + return mHost.getView().getContext(); + } + + public void setOnTouchListener(final View.OnTouchListener listener) { + mListener = listener; + mHost.getView().setOnTouchListener(listener); + } + + public void setFocusable(boolean focusable) { + mHost.getView().setOnTouchListener(focusable ? mListener : null); + } + + public int getHeight() { + return mHost.getView().getHeight(); + } + + public void onAttachedToWindow() { + maybeOpenCamera(); + } + + public void onDetachedFromWindow() { + CameraManager.get().closeCamera(); + } + + public void onRestoreInstanceState() { + maybeOpenCamera(); + } + + public void onCameraPermissionGranted() { + maybeOpenCamera(); + } + + /** @return True if the view is valid and prepared for the camera to start showing the preview */ + public boolean isValid() { + return mHost.isValid(); + } + + /** + * Starts the camera preview on the current surface. Abstracts out the differences in API from the + * CameraManager + * + * @throws IOException Which is caught by the CameraManager to display an error + */ + public void startPreview(final Camera camera) throws IOException { + mHost.startPreview(camera); + } +} |