Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2016 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.dialer.callcomposer.camera;
     18 
     19 import android.content.Context;
     20 import android.content.res.Configuration;
     21 import android.hardware.Camera;
     22 import android.view.View;
     23 import android.view.View.MeasureSpec;
     24 import android.view.View.OnTouchListener;
     25 import com.android.dialer.common.Assert;
     26 import com.android.dialer.util.PermissionsUtil;
     27 import java.io.IOException;
     28 
     29 /**
     30  * Contains shared code for SoftwareCameraPreview and HardwareCameraPreview, cannot use inheritance
     31  * because those classes must inherit from separate Views, so those classes delegate calls to this
     32  * helper class. Specifics for each implementation are in CameraPreviewHost
     33  */
     34 public class CameraPreview {
     35   /** Implemented by the camera for rendering. */
     36   public interface CameraPreviewHost {
     37     View getView();
     38 
     39     boolean isValid();
     40 
     41     void startPreview(final Camera camera) throws IOException;
     42 
     43     void onCameraPermissionGranted();
     44 
     45     void setShown();
     46   }
     47 
     48   private int mCameraWidth = -1;
     49   private int mCameraHeight = -1;
     50   private boolean mTabHasBeenShown = false;
     51   private OnTouchListener mListener;
     52 
     53   private final CameraPreviewHost mHost;
     54 
     55   public CameraPreview(final CameraPreviewHost host) {
     56     Assert.isNotNull(host);
     57     Assert.isNotNull(host.getView());
     58     mHost = host;
     59   }
     60 
     61   // This is set when the tab is actually selected.
     62   public void setShown() {
     63     mTabHasBeenShown = true;
     64     maybeOpenCamera();
     65   }
     66 
     67   // Opening camera is very expensive. Most of the ANR reports seem to be related to the camera.
     68   // So we delay until the camera is actually needed.  See b/23287938
     69   private void maybeOpenCamera() {
     70     boolean visible = mHost.getView().getVisibility() == View.VISIBLE;
     71     if (mTabHasBeenShown && visible && PermissionsUtil.hasCameraPermissions(getContext())) {
     72       CameraManager.get().openCamera();
     73     }
     74   }
     75 
     76   public void setSize(final Camera.Size size, final int orientation) {
     77     switch (orientation) {
     78       case 0:
     79       case 180:
     80         mCameraWidth = size.width;
     81         mCameraHeight = size.height;
     82         break;
     83       case 90:
     84       case 270:
     85       default:
     86         mCameraWidth = size.height;
     87         mCameraHeight = size.width;
     88     }
     89     mHost.getView().requestLayout();
     90   }
     91 
     92   public int getWidthMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec) {
     93     if (mCameraHeight >= 0) {
     94       final int width = View.MeasureSpec.getSize(widthMeasureSpec);
     95       return MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
     96     } else {
     97       return widthMeasureSpec;
     98     }
     99   }
    100 
    101   public int getHeightMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec) {
    102     if (mCameraHeight >= 0) {
    103       final int orientation = getContext().getResources().getConfiguration().orientation;
    104       final int width = View.MeasureSpec.getSize(widthMeasureSpec);
    105       final float aspectRatio = (float) mCameraWidth / (float) mCameraHeight;
    106       int height;
    107       if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
    108         height = (int) (width * aspectRatio);
    109       } else {
    110         height = (int) (width / aspectRatio);
    111       }
    112       return View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
    113     } else {
    114       return heightMeasureSpec;
    115     }
    116   }
    117 
    118   // onVisibilityChanged is set to Visible when the tab is _created_,
    119   //   which may be when the user is viewing a different tab.
    120   public void onVisibilityChanged(final int visibility) {
    121     if (PermissionsUtil.hasCameraPermissions(getContext())) {
    122       if (visibility == View.VISIBLE) {
    123         maybeOpenCamera();
    124       } else {
    125         CameraManager.get().closeCamera();
    126       }
    127     }
    128   }
    129 
    130   public Context getContext() {
    131     return mHost.getView().getContext();
    132   }
    133 
    134   public void setOnTouchListener(final View.OnTouchListener listener) {
    135     mListener = listener;
    136     mHost.getView().setOnTouchListener(listener);
    137   }
    138 
    139   public void setFocusable(boolean focusable) {
    140     mHost.getView().setOnTouchListener(focusable ? mListener : null);
    141   }
    142 
    143   public int getHeight() {
    144     return mHost.getView().getHeight();
    145   }
    146 
    147   public void onAttachedToWindow() {
    148     maybeOpenCamera();
    149   }
    150 
    151   public void onDetachedFromWindow() {
    152     CameraManager.get().closeCamera();
    153   }
    154 
    155   public void onRestoreInstanceState() {
    156     maybeOpenCamera();
    157   }
    158 
    159   public void onCameraPermissionGranted() {
    160     maybeOpenCamera();
    161   }
    162 
    163   /** @return True if the view is valid and prepared for the camera to start showing the preview */
    164   public boolean isValid() {
    165     return mHost.isValid();
    166   }
    167 
    168   /**
    169    * Starts the camera preview on the current surface. Abstracts out the differences in API from the
    170    * CameraManager
    171    *
    172    * @throws IOException Which is caught by the CameraManager to display an error
    173    */
    174   public void startPreview(final Camera camera) throws IOException {
    175     mHost.startPreview(camera);
    176   }
    177 }
    178