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 { getView()37 View getView(); 38 isValid()39 boolean isValid(); 40 startPreview(final Camera camera)41 void startPreview(final Camera camera) throws IOException; 42 onCameraPermissionGranted()43 void onCameraPermissionGranted(); 44 setShown()45 void setShown(); 46 } 47 48 private int cameraWidth = -1; 49 private int cameraHeight = -1; 50 private boolean tabHasBeenShown = false; 51 private OnTouchListener listener; 52 53 private final CameraPreviewHost host; 54 CameraPreview(final CameraPreviewHost host)55 public CameraPreview(final CameraPreviewHost host) { 56 Assert.isNotNull(host); 57 Assert.isNotNull(host.getView()); 58 this.host = host; 59 } 60 61 // This is set when the tab is actually selected. setShown()62 public void setShown() { 63 tabHasBeenShown = 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 a bug maybeOpenCamera()69 private void maybeOpenCamera() { 70 boolean visible = host.getView().getVisibility() == View.VISIBLE; 71 if (tabHasBeenShown && visible && PermissionsUtil.hasCameraPermissions(getContext())) { 72 CameraManager.get().openCamera(); 73 } 74 } 75 setSize(final Camera.Size size, final int orientation)76 public void setSize(final Camera.Size size, final int orientation) { 77 switch (orientation) { 78 case 0: 79 case 180: 80 cameraWidth = size.width; 81 cameraHeight = size.height; 82 break; 83 case 90: 84 case 270: 85 default: 86 cameraWidth = size.height; 87 cameraHeight = size.width; 88 } 89 host.getView().requestLayout(); 90 } 91 getWidthMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec)92 public int getWidthMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec) { 93 if (cameraHeight >= 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 getHeightMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec)101 public int getHeightMeasureSpec(final int widthMeasureSpec, final int heightMeasureSpec) { 102 if (cameraHeight >= 0) { 103 final int orientation = getContext().getResources().getConfiguration().orientation; 104 final int width = View.MeasureSpec.getSize(widthMeasureSpec); 105 final float aspectRatio = (float) cameraWidth / (float) cameraHeight; 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. onVisibilityChanged(final int visibility)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 getContext()130 public Context getContext() { 131 return host.getView().getContext(); 132 } 133 setOnTouchListener(final View.OnTouchListener listener)134 public void setOnTouchListener(final View.OnTouchListener listener) { 135 this.listener = listener; 136 host.getView().setOnTouchListener(listener); 137 } 138 setFocusable(boolean focusable)139 public void setFocusable(boolean focusable) { 140 host.getView().setOnTouchListener(focusable ? listener : null); 141 } 142 getHeight()143 public int getHeight() { 144 return host.getView().getHeight(); 145 } 146 onAttachedToWindow()147 public void onAttachedToWindow() { 148 maybeOpenCamera(); 149 } 150 onDetachedFromWindow()151 public void onDetachedFromWindow() { 152 CameraManager.get().closeCamera(); 153 } 154 onRestoreInstanceState()155 public void onRestoreInstanceState() { 156 maybeOpenCamera(); 157 } 158 onCameraPermissionGranted()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 */ isValid()164 public boolean isValid() { 165 return host.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 */ startPreview(final Camera camera)174 public void startPreview(final Camera camera) throws IOException { 175 host.startPreview(camera); 176 } 177 } 178