Fix crash related to unsupported camera mode.

Fixes #9106
This commit is contained in:
Alex Hart 2019-10-21 18:54:06 -03:00 committed by Greyson Parrelli
parent 097f97b5e4
commit 03dc220cea
3 changed files with 99 additions and 2 deletions

View File

@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.util.MemoryFileDescriptor;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.whispersystems.libsignal.util.guava.Optional;
@ -236,7 +237,14 @@ public class CameraXFragment extends Fragment implements CameraFragment {
Animation inAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.fade_in);
Animation outAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.fade_out);
if (CameraXUtil.isMixedModeSupported(requireContext())) {
Log.i(TAG, "Device supports mixed mode recording. [" + CameraXUtil.getLowestSupportedHardwareLevel(requireContext()) + "]");
camera.setCaptureMode(CameraXView.CaptureMode.MIXED);
} else {
Log.i(TAG, "Device does not support mixed mode recording, falling back to IMAGE [" + CameraXUtil.getLowestSupportedHardwareLevel(requireContext()) + "]");
camera.setCaptureMode(CameraXView.CaptureMode.IMAGE);
}
captureButton.setVideoCaptureListener(new CameraXVideoCaptureHelper(
this,
captureButton,
@ -245,6 +253,9 @@ public class CameraXFragment extends Fragment implements CameraFragment {
new CameraXVideoCaptureHelper.Callback() {
@Override
public void onVideoRecordStarted() {
if (camera.getCaptureMode() == CameraXView.CaptureMode.IMAGE) {
camera.setCaptureMode(CameraXView.CaptureMode.VIDEO);
}
hideAndDisableControlsForVideoRecording(captureButton, flashButton, flipButton, outAnimation);
}
@ -320,6 +331,12 @@ public class CameraXFragment extends Fragment implements CameraFragment {
}
private void onCaptureClicked() {
if (camera.getCaptureMode() == CameraXView.CaptureMode.VIDEO) {
camera.setCaptureMode(CameraXView.CaptureMode.IMAGE);
Util.runOnMainDelayed(this::onCaptureClicked, 100);
return;
}
Stopwatch stopwatch = new Stopwatch("Capture");
CameraXSelfieFlashHelper flashHelper = new CameraXSelfieFlashHelper(

View File

@ -50,6 +50,7 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
@Override
public void onVideoSaved(@NonNull FileDescriptor fileDescriptor) {
try {
isRecording = false;
camera.setZoomLevel(0f);
memoryFileDescriptor.seek(0);
callback.onVideoSaved(fileDescriptor);
@ -63,6 +64,7 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
@NonNull String message,
@Nullable Throwable cause)
{
isRecording = false;
callback.onVideoError(cause);
Util.runOnMain(() -> resetCameraSizing());
}
@ -202,7 +204,6 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener
Log.d(TAG, "onVideoCaptureComplete");
camera.stopRecording();
if (cameraMetricsAnimator != null && cameraMetricsAnimator.isRunning()) {
cameraMetricsAnimator.reverse();
}

View File

@ -1,12 +1,17 @@
package org.thoughtcrime.securesms.mediasend.camerax;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.os.Build;
import android.util.Rational;
import android.util.Size;
@ -14,6 +19,7 @@ import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.camera2.impl.compat.CameraManagerCompat;
import androidx.camera.core.CameraX;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageProxy;
@ -29,6 +35,24 @@ public class CameraXUtil {
private static final String TAG = Log.tag(CameraXUtil.class);
@RequiresApi(21)
private static final int[] CAMERA_HARDWARE_LEVEL_ORDERING = new int[]{CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL};
@RequiresApi(24)
private static final int[] CAMERA_HARDWARE_LEVEL_ORDERING_24 = new int[]{CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3};
@RequiresApi(28)
private static final int[] CAMERA_HARDWARE_LEVEL_ORDERING_28 = new int[]{CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3};
@SuppressWarnings("SuspiciousNameCombination")
@RequiresApi(21)
public static ImageResult toJpeg(@NonNull ImageProxy image, int rotation, boolean flip) throws IOException {
@ -144,6 +168,61 @@ public class CameraXUtil {
return out.toByteArray();
}
@RequiresApi(21)
public static boolean isMixedModeSupported(@NonNull Context context) {
return getLowestSupportedHardwareLevel(context) != CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
}
@RequiresApi(21)
public static int getLowestSupportedHardwareLevel(@NonNull Context context) {
CameraManager cameraManager = CameraManagerCompat.from(context).unwrap();
try {
int supported = maxHardwareLevel();
for (String cameraId : cameraManager.getCameraIdList()) {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
Integer hwLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (hwLevel == null || hwLevel == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
}
supported = smallerHardwareLevel(supported, hwLevel);
}
return supported;
} catch (CameraAccessException e) {
Log.w(TAG, "Failed to enumerate cameras", e);
return CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
}
}
@RequiresApi(21)
private static int maxHardwareLevel() {
if (Build.VERSION.SDK_INT >= 24) return CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3;
else return CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
}
@RequiresApi(21)
private static int smallerHardwareLevel(int levelA, int levelB) {
int[] hardwareInfoOrdering = getHardwareInfoOrdering();
for (int hwInfo : hardwareInfoOrdering) {
if (levelA == hwInfo || levelB == hwInfo) return hwInfo;
}
return CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
}
@RequiresApi(21)
private static int[] getHardwareInfoOrdering() {
if (Build.VERSION.SDK_INT >= 28) return CAMERA_HARDWARE_LEVEL_ORDERING_28;
else if (Build.VERSION.SDK_INT >= 24) return CAMERA_HARDWARE_LEVEL_ORDERING_24;
else return CAMERA_HARDWARE_LEVEL_ORDERING;
}
public static class ImageResult {
private final byte[] data;
private final int width;