From b19aedd17c2e90d16b7c8755c1d5289e22456e68 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 6 Sep 2023 15:52:21 -0400 Subject: [PATCH] Upgrade CameraX to 1.3.0-rc01 --- .../securesms/mediasend/CameraFragment.java | 3 - .../securesms/mediasend/CameraXFragment.java | 7 +- .../mediasend/CameraXVideoCaptureHelper.java | 79 +++++++++++++------ .../mediasend/camerax/CameraXModePolicy.kt | 2 - .../mediasend/camerax/FastCameraModels.java | 3 +- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 58 ++++++++++++++ 7 files changed, 118 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraFragment.java index dc38b1a960..b6bc6c4681 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraFragment.java @@ -3,12 +3,9 @@ package org.thoughtcrime.securesms.mediasend; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Configuration; -import android.view.Window; -import android.view.WindowManager; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.camera.view.video.ExperimentalVideo; import androidx.fragment.app.Fragment; import org.thoughtcrime.securesms.R; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java index 9774c46c06..fbba84d920 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXFragment.java @@ -30,10 +30,12 @@ import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageCapture; import androidx.camera.core.ImageCaptureException; import androidx.camera.core.ImageProxy; +import androidx.camera.video.FallbackStrategy; +import androidx.camera.video.Quality; +import androidx.camera.video.QualitySelector; import androidx.camera.view.CameraController; import androidx.camera.view.LifecycleCameraController; import androidx.camera.view.PreviewView; -import androidx.camera.view.video.ExperimentalVideo; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; import androidx.core.content.ContextCompat; @@ -71,7 +73,6 @@ import io.reactivex.rxjava3.disposables.Disposable; * Camera captured implemented using the CameraX SDK, which uses Camera2 under the hood. Should be * preferred whenever possible. */ -@ExperimentalVideo public class CameraXFragment extends LoggingFragment implements CameraFragment { private static final String TAG = Log.tag(CameraXFragment.class); @@ -239,7 +240,7 @@ public class CameraXFragment extends LoggingFragment implements CameraFragment { CameraController.OutputSize outputSize = new CameraController.OutputSize(size); cameraController.setImageCaptureTargetSize(outputSize); - cameraController.setVideoCaptureTargetSize(new CameraController.OutputSize(VideoUtil.getVideoRecordingSize())); + cameraController.setVideoCaptureQualitySelector(QualitySelector.from(Quality.HD, FallbackStrategy.lowerQualityThan(Quality.HD))); controlsContainer.removeAllViews(); controlsContainer.addView(LayoutInflater.from(getContext()).inflate(layout, controlsContainer, false)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXVideoCaptureHelper.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXVideoCaptureHelper.java index c5268cc553..9f6663b97c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXVideoCaptureHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/CameraXVideoCaptureHelper.java @@ -14,12 +14,13 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.camera.core.ZoomState; +import androidx.camera.video.FileDescriptorOutputOptions; +import androidx.camera.video.Recording; +import androidx.camera.video.VideoRecordEvent; import androidx.camera.view.CameraController; import androidx.camera.view.PreviewView; -import androidx.camera.view.video.ExperimentalVideo; -import androidx.camera.view.video.OnVideoSavedCallback; -import androidx.camera.view.video.OutputFileOptions; -import androidx.camera.view.video.OutputFileResults; +import androidx.camera.view.video.AudioConfig; +import androidx.core.util.Consumer; import androidx.fragment.app.Fragment; import com.bumptech.glide.util.Executors; @@ -39,7 +40,6 @@ import java.util.Objects; import java.util.concurrent.TimeUnit; @RequiresApi(26) -@ExperimentalVideo class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener { private static final String TAG = CameraXVideoCaptureHelper.class.getName(); @@ -57,25 +57,31 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener private ValueAnimator cameraMetricsAnimator; - private final OnVideoSavedCallback videoSavedListener = new OnVideoSavedCallback() { - @SuppressLint("RestrictedApi") - @Override - public void onVideoSaved(@NonNull OutputFileResults outputFileResults) { - try { - debouncer.clear(); - cameraController.setZoomRatio(Objects.requireNonNull(cameraController.getZoomState().getValue()).getMinZoomRatio()); - memoryFileDescriptor.seek(0); - callback.onVideoSaved(memoryFileDescriptor.getFileDescriptor()); - } catch (IOException e) { - callback.onVideoError(e); - } - } + private @Nullable Recording activeRecording = null; - @SuppressLint("RestrictedApi") + private final Consumer videoSavedListener = new Consumer<>() { @Override - public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) { - debouncer.clear(); - callback.onVideoError(cause); + public void accept(VideoRecordEvent videoRecordEvent) { + Log.d(TAG, "Received recording event: " + videoRecordEvent.getClass().getSimpleName()); + + if (videoRecordEvent instanceof VideoRecordEvent.Finalize) { + VideoRecordEvent.Finalize event = (VideoRecordEvent.Finalize) videoRecordEvent; + + if (event.hasError()) { + Log.w(TAG, "Hit an error while recording! Error code: " + event.getError(), event.getCause()); + debouncer.clear(); + callback.onVideoError(event.getCause()); + } else { + try { + debouncer.clear(); + cameraController.setZoomRatio(Objects.requireNonNull(cameraController.getZoomState().getValue()).getMinZoomRatio()); + memoryFileDescriptor.seek(0); + callback.onVideoSaved(memoryFileDescriptor.getFileDescriptor()); + } catch (IOException e) { + callback.onVideoError(e); + } + } + } } }; @@ -140,9 +146,11 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener callback.onVideoRecordStarted(); shrinkCaptureArea(); - OutputFileOptions options = OutputFileOptions.builder(memoryFileDescriptor.getParcelFileDescriptor()).build(); + FileDescriptorOutputOptions outputOptions = new FileDescriptorOutputOptions.Builder(memoryFileDescriptor.getParcelFileDescriptor()).build(); + AudioConfig audioConfig = AudioConfig.create(true); + + activeRecording = cameraController.startRecording(outputOptions, audioConfig, Executors.mainThreadExecutor(), videoSavedListener); - cameraController.startRecording(options, Executors.mainThreadExecutor(), videoSavedListener); updateProgressAnimator.start(); debouncer.publish(this::onVideoCaptureComplete); } @@ -198,10 +206,19 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener @Override public void onVideoCaptureComplete() { - if (!canRecordAudio()) return; + if (!canRecordAudio()) { + Log.w(TAG, "Can't record audio!"); + return; + } + + if (activeRecording == null) { + Log.w(TAG, "No active recording!"); + return; + } Log.d(TAG, "onVideoCaptureComplete"); - cameraController.stopRecording(); + activeRecording.close(); + activeRecording = null; if (cameraMetricsAnimator != null && cameraMetricsAnimator.isRunning()) { cameraMetricsAnimator.reverse(); @@ -219,6 +236,16 @@ class CameraXVideoCaptureHelper implements CameraButtonView.VideoCaptureListener cameraController.setZoomRatio((range * increment) + zoomState.getMinZoomRatio()); } + @Override + protected void finalize() throws Throwable { + if (activeRecording != null) { + Log.w(TAG, "Dangling recording left open in finalize()! Attempting to close."); + activeRecording.close(); + } + + super.finalize(); + } + static MemoryFileDescriptor createFileDescriptor(@NonNull Context context) throws MemoryFileDescriptor.MemoryFileException { return MemoryFileDescriptor.newMemoryFileDescriptor( context, diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/camerax/CameraXModePolicy.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/camerax/CameraXModePolicy.kt index 2cac197218..35badb798f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/camerax/CameraXModePolicy.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/camerax/CameraXModePolicy.kt @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.mediasend.camerax import android.content.Context import android.os.Build import androidx.camera.view.CameraController -import androidx.camera.view.video.ExperimentalVideo import org.signal.core.util.asListContains import org.thoughtcrime.securesms.mms.MediaConstraints import org.thoughtcrime.securesms.util.FeatureFlags @@ -12,7 +11,6 @@ import org.thoughtcrime.securesms.video.VideoUtil /** * Describes device capabilities */ -@ExperimentalVideo sealed class CameraXModePolicy { abstract val isVideoSupported: Boolean diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/camerax/FastCameraModels.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/camerax/FastCameraModels.java index d0bb6e39f2..bf2f36b61a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/camerax/FastCameraModels.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/camerax/FastCameraModels.java @@ -7,7 +7,7 @@ import java.util.Set; /** * A set of {@link android.os.Build#MODEL} that are known to both benefit from - * {@link androidx.camera.core.ImageCapture.CaptureMode#MAX_QUALITY} and execute it quickly. + * {@link androidx.camera.core.ImageCapture.CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY} and execute it quickly. * */ public class FastCameraModels { @@ -19,6 +19,7 @@ public class FastCameraModels { add("Pixel 3 XL"); add("Pixel 3a"); add("Pixel 3a XL"); + add("SM-S911U1"); }}; /** diff --git a/dependencies.gradle b/dependencies.gradle index 2d924e8944..5c85b93647 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -6,7 +6,7 @@ dependencyResolutionManagement { libs { version('androidx-appcompat', '1.6.1') version('androidx-activity', '1.7.2') - version('androidx-camera', '1.2.3') + version('androidx-camera', '1.3.0-rc01') version('androidx-fragment', '1.6.1') version('androidx-lifecycle', '2.6.1') version('androidx-media3', '1.1.0') diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index e4737354b2..edbea2401a 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -144,6 +144,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -289,6 +297,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -297,6 +313,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -305,6 +329,22 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + + + + + + + + + @@ -313,6 +353,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -4653,6 +4701,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + @@ -4693,6 +4746,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + +