summaryrefslogtreecommitdiff
path: root/java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java
diff options
context:
space:
mode:
authorEric Erfanian <erfanian@google.com>2017-03-20 08:50:25 -0700
committerEric Erfanian <erfanian@google.com>2017-03-20 09:03:10 -0700
commit06b6b56e9eaa91ebf757ea641e38a9c885fa40bd (patch)
tree6a0f9f0809cc02004f4be6ef9d20cbced397c3d1 /java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java
parent9a090c89e7a0c43a668bd6e2ac5e5e680f323aed (diff)
Update AOSP Dialer source from internal google3 repository at
cl/150622237 Test: make, treehugger, on device testing. This CL updates the AOSP Dialer source with all the changes that have gone into the private google3 repository. This includes all the changes from cl/150392808 (3/16/2017) to cl/150622237 (3/20/2017). This goal of these drops is to keep the AOSP source in sync with the internal google3 repository. Currently these sync are done by hand with very minor modifications to the internal source code. See the Android.mk file for list of modifications. Our current goal is to do frequent drops (daily if possible) and eventually switched to an automated process. Change-Id: Id53e0e580a4ef73760a8afb7bb8c265ee27ad535
Diffstat (limited to 'java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java')
-rw-r--r--java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java268
1 files changed, 268 insertions, 0 deletions
diff --git a/java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java b/java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java
new file mode 100644
index 000000000..522d77235
--- /dev/null
+++ b/java/com/android/incallui/answer/impl/SelfManagedAnswerVideoCallScreen.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2017 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.incallui.answer.impl;
+
+import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.StateCallback;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.Size;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.video.protocol.VideoCallScreen;
+import java.util.Arrays;
+
+/**
+ * Shows the local preview for the incoming video call or video upgrade request. This class is used
+ * for RCS Video Share where we need to open the camera preview ourselves. For IMS Video the camera
+ * is managed by the modem, see {@link AnswerVideoCallScreen}.
+ */
+public class SelfManagedAnswerVideoCallScreen extends StateCallback implements VideoCallScreen {
+
+ private static final int MAX_WIDTH = 1920;
+ private static final float ASPECT_TOLERANCE = 0.1f;
+ private static final float TARGET_ASPECT = 16.f / 9.f;
+
+ @NonNull private final String callId;
+ @NonNull private final Fragment fragment;
+ @NonNull private final FixedAspectSurfaceView surfaceView;
+ private final Context context;
+
+ private String cameraId;
+ private CameraDevice camera;
+ private CaptureRequest.Builder captureRequestBuilder;
+
+ public SelfManagedAnswerVideoCallScreen(
+ @NonNull String callId, @NonNull Fragment fragment, @NonNull View view) {
+ this.callId = Assert.isNotNull(callId);
+ this.fragment = Assert.isNotNull(fragment);
+ this.context = Assert.isNotNull(fragment.getContext());
+
+ surfaceView =
+ Assert.isNotNull(
+ (FixedAspectSurfaceView) view.findViewById(R.id.incoming_preview_surface_view));
+ surfaceView.setVisibility(View.VISIBLE);
+ view.findViewById(R.id.incoming_preview_texture_view_overlay).setVisibility(View.VISIBLE);
+ view.setBackgroundColor(0xff000000);
+ }
+
+ @Override
+ public void onVideoScreenStart() {
+ openCamera();
+ }
+
+ @Override
+ public void onVideoScreenStop() {
+ closeCamera();
+ }
+
+ @Override
+ public void showVideoViews(
+ boolean shouldShowPreview, boolean shouldShowRemote, boolean isRemotelyHeld) {}
+
+ @Override
+ public void onLocalVideoDimensionsChanged() {}
+
+ @Override
+ public void onLocalVideoOrientationChanged() {}
+
+ @Override
+ public void onRemoteVideoDimensionsChanged() {}
+
+ @Override
+ public void updateFullscreenAndGreenScreenMode(
+ boolean shouldShowFullscreen, boolean shouldShowGreenScreen) {}
+
+ @Override
+ public Fragment getVideoCallScreenFragment() {
+ return fragment;
+ }
+
+ @Override
+ public String getCallId() {
+ return callId;
+ }
+
+ /**
+ * Opens the first front facing camera on the device into a {@link SurfaceView} while preserving
+ * aspect ratio.
+ */
+ private void openCamera() {
+ CameraManager manager = context.getSystemService(CameraManager.class);
+
+ StreamConfigurationMap configMap = getFrontFacingCameraSizes(manager);
+ if (configMap == null) {
+ return;
+ }
+
+ Size previewSize = getOptimalSize(configMap.getOutputSizes(SurfaceHolder.class));
+ LogUtil.i("SelfManagedAnswerVideoCallScreen.openCamera", "Optimal size: " + previewSize);
+ float outputAspect = (float) previewSize.getWidth() / previewSize.getHeight();
+ surfaceView.setAspectRatio(outputAspect);
+ surfaceView.getHolder().setFixedSize(previewSize.getWidth(), previewSize.getHeight());
+
+ try {
+ manager.openCamera(cameraId, this, null);
+ } catch (CameraAccessException e) {
+ LogUtil.e("SelfManagedAnswerVideoCallScreen.openCamera", "failed to open camera", e);
+ }
+ }
+
+ @Nullable
+ private StreamConfigurationMap getFrontFacingCameraSizes(CameraManager manager) {
+ String[] cameraIds;
+ try {
+ cameraIds = manager.getCameraIdList();
+ } catch (CameraAccessException e) {
+ LogUtil.e(
+ "SelfManagedAnswerVideoCallScreen.getFrontFacingCameraSizes",
+ "failed to get camera ids",
+ e);
+ return null;
+ }
+
+ for (String cameraId : cameraIds) {
+ CameraCharacteristics characteristics;
+ try {
+ characteristics = manager.getCameraCharacteristics(cameraId);
+ } catch (CameraAccessException e) {
+ LogUtil.e(
+ "SelfManagedAnswerVideoCallScreen.getFrontFacingCameraSizes",
+ "failed to get camera characteristics",
+ e);
+ continue;
+ }
+
+ if (characteristics.get(CameraCharacteristics.LENS_FACING)
+ != CameraCharacteristics.LENS_FACING_FRONT) {
+ continue;
+ }
+
+ StreamConfigurationMap configMap =
+ characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ if (configMap == null) {
+ continue;
+ }
+
+ this.cameraId = cameraId;
+ return configMap;
+ }
+ LogUtil.e(
+ "SelfManagedAnswerVideoCallScreen.getFrontFacingCameraSizes", "No valid configurations.");
+ return null;
+ }
+
+ /**
+ * Given an array of {@link Size}s, tries to find the largest Size such that the aspect ratio of
+ * the returned size is within {@code ASPECT_TOLERANCE} of {@code TARGET_ASPECT}. This is useful
+ * because it provides us with an adequate size/camera resolution that will experience the least
+ * stretching from our fullscreen UI that doesn't match any of the camera sizes.
+ */
+ private static Size getOptimalSize(Size[] outputSizes) {
+ Size bestCandidateSize = outputSizes[0];
+ float bestCandidateAspect =
+ (float) bestCandidateSize.getWidth() / bestCandidateSize.getHeight();
+
+ for (Size candidateSize : outputSizes) {
+ if (candidateSize.getWidth() < MAX_WIDTH) {
+ float candidateAspect = (float) candidateSize.getWidth() / candidateSize.getHeight();
+ boolean isGoodCandidateAspect =
+ Math.abs(candidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
+ boolean isGoodOutputAspect =
+ Math.abs(bestCandidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
+
+ if ((isGoodCandidateAspect && !isGoodOutputAspect)
+ || candidateSize.getWidth() > bestCandidateSize.getWidth()) {
+ bestCandidateSize = candidateSize;
+ bestCandidateAspect = candidateAspect;
+ }
+ }
+ }
+ return bestCandidateSize;
+ }
+
+ @Override
+ public void onOpened(CameraDevice camera) {
+ LogUtil.i("SelfManagedAnswerVideoCallScreen.opOpened", "camera opened.");
+ this.camera = camera;
+ Surface surface = surfaceView.getHolder().getSurface();
+ try {
+ captureRequestBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ captureRequestBuilder.addTarget(surface);
+ camera.createCaptureSession(Arrays.asList(surface), new CaptureSessionCallback(), null);
+ } catch (CameraAccessException e) {
+ LogUtil.e(
+ "SelfManagedAnswerVideoCallScreen.createCameraPreview", "failed to create preview", e);
+ }
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ closeCamera();
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ closeCamera();
+ }
+
+ private void closeCamera() {
+ if (camera != null) {
+ camera.close();
+ camera = null;
+ }
+ }
+
+ private class CaptureSessionCallback extends CameraCaptureSession.StateCallback {
+
+ @Override
+ public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
+ LogUtil.i(
+ "SelfManagedAnswerVideoCallScreen.onConfigured", "camera capture session configured.");
+ // The camera is already closed.
+ if (camera == null) {
+ return;
+ }
+
+ // When the session is ready, we start displaying the preview.
+ captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
+ try {
+ cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
+ } catch (CameraAccessException e) {
+ LogUtil.e("CaptureSessionCallback.onConfigured", "failed to configure", e);
+ }
+ }
+
+ @Override
+ public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
+ LogUtil.e("CaptureSessionCallback.onConfigureFailed", "failed to configure");
+ }
+ }
+}