/* * 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); } }