Merge "Fix the recorded video contains inconsistent video/audio timestamp" into androidx-main
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/OutputOptionsTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/OutputOptionsTest.kt
index 93c3a8e..3b4791e 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/OutputOptionsTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/OutputOptionsTest.kt
@@ -42,8 +42,7 @@
val savedFile = File.createTempFile("CameraX", ".tmp")
savedFile.deleteOnExit()
- val fileOutputOptions = FileOutputOptions.builder()
- .setFile(savedFile)
+ val fileOutputOptions = FileOutputOptions.Builder(savedFile)
.setFileSizeLimit(FILE_SIZE_LIMIT)
.build()
@@ -65,12 +64,10 @@
put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
}
- val mediaStoreOutputOptions = MediaStoreOutputOptions.builder()
- .setContentResolver(contentResolver)
- .setFileSizeLimit(FILE_SIZE_LIMIT)
- .setCollection(MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
- .setContentValues(contentValues)
- .build()
+ val mediaStoreOutputOptions = MediaStoreOutputOptions.Builder(
+ contentResolver,
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+ ).setContentValues(contentValues).setFileSizeLimit(FILE_SIZE_LIMIT).build()
assertThat(mediaStoreOutputOptions.contentResolver).isEqualTo(contentResolver)
assertThat(mediaStoreOutputOptions.collection).isEqualTo(
@@ -89,8 +86,7 @@
savedFile,
ParcelFileDescriptor.MODE_READ_WRITE
).use { pfd ->
- val fdOutputOptions = FileDescriptorOutputOptions.builder()
- .setParcelFileDescriptor(pfd)
+ val fdOutputOptions = FileDescriptorOutputOptions.Builder(pfd)
.setFileSizeLimit(FILE_SIZE_LIMIT)
.build()
@@ -106,9 +102,7 @@
fun file_builderContainsCorrectDefaults() {
val savedFile = File.createTempFile("CameraX", ".tmp")
savedFile.deleteOnExit()
- val fileOutputOptions = FileOutputOptions.builder()
- .setFile(savedFile)
- .build()
+ val fileOutputOptions = FileOutputOptions.Builder(savedFile).build()
assertThat(fileOutputOptions.fileSizeLimit).isEqualTo(OutputOptions.FILE_SIZE_UNLIMITED)
savedFile.delete()
@@ -118,19 +112,14 @@
fun mediaStore_builderContainsCorrectDefaults() {
val context: Context = ApplicationProvider.getApplicationContext()
val contentResolver: ContentResolver = context.contentResolver
- val fileName = "OutputOptionTest"
- val contentValues = ContentValues().apply {
- put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
- put(MediaStore.Video.Media.TITLE, fileName)
- put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
- }
- val mediaStoreOutputOptions = MediaStoreOutputOptions.builder()
- .setContentResolver(contentResolver)
- .setCollection(MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
- .setContentValues(contentValues)
- .build()
+ val mediaStoreOutputOptions = MediaStoreOutputOptions.Builder(
+ contentResolver,
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+ ).build()
+ assertThat(mediaStoreOutputOptions.contentValues)
+ .isEqualTo(MediaStoreOutputOptions.EMPTY_CONTENT_VALUES)
assertThat(mediaStoreOutputOptions.fileSizeLimit)
.isEqualTo(OutputOptions.FILE_SIZE_UNLIMITED)
}
@@ -142,9 +131,7 @@
savedFile,
ParcelFileDescriptor.MODE_READ_WRITE
).use { pfd ->
- val fdOutputOptions = FileDescriptorOutputOptions.builder()
- .setParcelFileDescriptor(pfd)
- .build()
+ val fdOutputOptions = FileDescriptorOutputOptions.Builder(pfd).build()
assertThat(fdOutputOptions.fileSizeLimit).isEqualTo(OutputOptions.FILE_SIZE_UNLIMITED)
}
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
index d946668..c8cd073 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/RecorderTest.kt
@@ -179,9 +179,8 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -220,11 +219,10 @@
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
}
- val outputOptions = MediaStoreOutputOptions.builder()
- .setContentResolver(contentResolver)
- .setCollection(MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
- .setContentValues(contentValues)
- .build()
+ val outputOptions = MediaStoreOutputOptions.Builder(
+ contentResolver,
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+ ).setContentValues(contentValues).build()
var uri: Uri = Uri.EMPTY
val activeRecording = recorder.prepareRecording(outputOptions)
@@ -264,11 +262,8 @@
file,
ParcelFileDescriptor.MODE_READ_WRITE
).use { pfd ->
- val outputOptions = FileDescriptorOutputOptions.builder()
- .setParcelFileDescriptor(pfd)
- .build()
-
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder
+ .prepareRecording(FileDescriptorOutputOptions.Builder(pfd).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -299,12 +294,8 @@
file,
ParcelFileDescriptor.MODE_READ_WRITE
).use { pfd ->
- val outputOptions = FileDescriptorOutputOptions.builder()
- .setParcelFileDescriptor(pfd)
- .build()
-
assertThrows(IllegalStateException::class.java) {
- recorder.prepareRecording(outputOptions)
+ recorder.prepareRecording(FileDescriptorOutputOptions.Builder(pfd).build())
}
}
@@ -317,9 +308,8 @@
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -354,9 +344,8 @@
clearInvocations(videoRecordEventListener)
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -387,14 +376,11 @@
invokeSurfaceRequest()
val file1 = File.createTempFile("CameraX1", ".tmp").apply { deleteOnExit() }
- val outputOptions1 = FileOutputOptions.builder().setFile(file1).build()
-
val file2 = File.createTempFile("CameraX2", ".tmp").apply { deleteOnExit() }
- val outputOptions2 = FileOutputOptions.builder().setFile(file2).build()
// Start and stop a recording to ensure recorder is idling
val inOrder = inOrder(videoRecordEventListener)
- val activeRecording1 = recorder.prepareRecording(outputOptions1)
+ val activeRecording1 = recorder.prepareRecording(FileOutputOptions.Builder(file1).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -411,7 +397,7 @@
.accept(any(VideoRecordEvent.Finalize::class.java))
// First recording is now finalized. Try starting second recording paused.
- val activeRecording2 = recorder.prepareRecording(outputOptions2)
+ val activeRecording2 = recorder.prepareRecording(FileOutputOptions.Builder(file2).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -436,11 +422,10 @@
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
val inOrder = inOrder(videoRecordEventListener)
// Start
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -524,7 +509,6 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
@Suppress("UNCHECKED_CAST")
val streamStateObserver =
@@ -538,7 +522,7 @@
)
// Start
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -561,7 +545,7 @@
fun start_throwsExceptionWhenActive() {
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
+ val outputOptions = FileOutputOptions.Builder(file).build()
val activeRecording = recorder.prepareRecording(outputOptions).start()
@@ -578,9 +562,8 @@
fun start_beforeSurfaceRequested() {
clearInvocations(videoRecordEventListener)
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -605,11 +588,10 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
recorder.onSourceStateChanged(VideoOutput.SourceState.INACTIVE)
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -630,9 +612,8 @@
fun pause_beforeSurfaceRequested() {
clearInvocations(videoRecordEventListener)
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -659,9 +640,8 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -697,9 +677,8 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -723,9 +702,8 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -754,9 +732,8 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -779,9 +756,8 @@
fun stop_beforeSurfaceRequested() {
clearInvocations(videoRecordEventListener)
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -801,11 +777,10 @@
fun stop_fromAutoCloseable() {
clearInvocations(videoRecordEventListener)
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
val inOrder = inOrder(videoRecordEventListener)
// Recording will be stopped by AutoCloseable.close() upon exiting use{} block
- val pendingRecording = recorder.prepareRecording(outputOptions)
+ val pendingRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
pendingRecording.withEventListener(
CameraXExecutors.directExecutor(),
videoRecordEventListener
@@ -828,10 +803,8 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder
- .prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -858,10 +831,10 @@
fun stop_whenActiveRecordingIsGarbageCollected() {
clearInvocations(videoRecordEventListener)
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
val inOrder = inOrder(videoRecordEventListener)
- var activeRecording: ActiveRecording? = recorder.prepareRecording(outputOptions)
+ var activeRecording: ActiveRecording? = recorder
+ .prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -893,9 +866,8 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.withAudioEnabled()
.start()
@@ -948,9 +920,8 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
- val activeRecording = recorder.prepareRecording(outputOptions)
+ val activeRecording = recorder.prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(CameraXExecutors.directExecutor(), videoRecordEventListener)
.start()
@@ -982,14 +953,12 @@
@Test
fun cannotStartMultiplePendingRecordingsWhileInitializing() {
val file1 = File.createTempFile("CameraX1", ".tmp").apply { deleteOnExit() }
- val outputOptions1 = FileOutputOptions.builder().setFile(file1).build()
val file2 = File.createTempFile("CameraX2", ".tmp").apply { deleteOnExit() }
- val outputOptions2 = FileOutputOptions.builder().setFile(file2).build()
try {
// We explicitly do not invoke the surface request so the recorder is initializing.
- recorder.prepareRecording(outputOptions1).start().use {
+ recorder.prepareRecording(FileOutputOptions.Builder(file1).build()).start().use {
assertThrows<IllegalStateException> {
- recorder.prepareRecording(outputOptions2).start()
+ recorder.prepareRecording(FileOutputOptions.Builder(file2).build()).start()
}
}
} finally {
@@ -1005,13 +974,11 @@
clearInvocations(videoRecordEventListener)
invokeSurfaceRequest()
val file1 = File.createTempFile("CameraX1", ".tmp").apply { deleteOnExit() }
- val outputOptions1 = FileOutputOptions.builder().setFile(file1).build()
val file2 = File.createTempFile("CameraX2", ".tmp").apply { deleteOnExit() }
- val outputOptions2 = FileOutputOptions.builder().setFile(file2).build()
val inOrder = inOrder(videoRecordEventListener)
try {
- recorder.prepareRecording(outputOptions1)
+ recorder.prepareRecording(FileOutputOptions.Builder(file1).build())
.withEventListener(
CameraXExecutors.directExecutor(),
videoRecordEventListener
@@ -1022,7 +989,7 @@
.accept(any(VideoRecordEvent.Status::class.java))
}
- recorder.prepareRecording(outputOptions2)
+ recorder.prepareRecording(FileOutputOptions.Builder(file2).build())
.withEventListener(
CameraXExecutors.directExecutor(),
videoRecordEventListener
@@ -1108,8 +1075,7 @@
private fun runFileSizeLimitTest(fileSizeLimit: Long) {
invokeSurfaceRequest()
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder()
- .setFile(file)
+ val outputOptions = FileOutputOptions.Builder(file)
.setFileSizeLimit(fileSizeLimit)
.build()
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index a172cc5..7ee8a9c 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -297,7 +297,6 @@
// Arrange.
val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
@Suppress("UNCHECKED_CAST")
val mockListener = mock(Consumer::class.java) as Consumer<VideoRecordEvent>
instrumentation.runOnMainSync {
@@ -307,7 +306,7 @@
// Act.
videoCapture.output
- .prepareRecording(outputOptions)
+ .prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(
CameraXExecutors.directExecutor(),
mockListener
@@ -403,10 +402,8 @@
private fun startVideoRecording(videoCapture: VideoCapture<Recorder>, file: File):
ActiveRecording {
- val outputOptions = FileOutputOptions.builder().setFile(file).build()
-
val activeRecording = videoCapture.output
- .prepareRecording(outputOptions)
+ .prepareRecording(FileOutputOptions.Builder(file).build())
.withEventListener(
CameraXExecutors.directExecutor(),
videoRecordEventListener
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/FileDescriptorOutputOptions.java b/camera/camera-video/src/main/java/androidx/camera/video/FileDescriptorOutputOptions.java
index 55f022f..b293823 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/FileDescriptorOutputOptions.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/FileDescriptorOutputOptions.java
@@ -19,6 +19,7 @@
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
+import androidx.core.util.Preconditions;
import com.google.auto.value.AutoValue;
@@ -31,49 +32,71 @@
* <p>To use a {@link java.io.File} as an output destination instead of a file descriptor, use
* {@link FileOutputOptions}.
*/
-@AutoValue
-public abstract class FileDescriptorOutputOptions extends OutputOptions {
+public final class FileDescriptorOutputOptions extends OutputOptions {
- FileDescriptorOutputOptions() {
+ private final FileDescriptorOutputOptionsInternal mFileDescriptorOutputOptionsInternal;
+
+ FileDescriptorOutputOptions(
+ @NonNull FileDescriptorOutputOptionsInternal fileDescriptorOutputOptionsInternal) {
super(OPTIONS_TYPE_FILE_DESCRIPTOR);
+ Preconditions.checkNotNull(fileDescriptorOutputOptionsInternal,
+ "FileDescriptorOutputOptionsInternal can't be null.");
+ mFileDescriptorOutputOptionsInternal = fileDescriptorOutputOptionsInternal;
}
- /** Returns a builder for this FileDescriptorOutputOptions. */
+ /**
+ * Gets the file descriptor instance.
+ *
+ * @return the file descriptor used as the output destination.
+ */
@NonNull
- public static Builder builder() {
- return new AutoValue_FileDescriptorOutputOptions.Builder()
- .setFileSizeLimit(FILE_SIZE_UNLIMITED);
+ public ParcelFileDescriptor getParcelFileDescriptor() {
+ return mFileDescriptorOutputOptionsInternal.getParcelFileDescriptor();
}
/**
* Gets the limit for the file length in bytes.
*/
@Override
- public abstract long getFileSizeLimit();
+ public long getFileSizeLimit() {
+ return mFileDescriptorOutputOptionsInternal.getFileSizeLimit();
+ }
- /**
- * Gets the file descriptor instance.
- * @return the file descriptor used as the output destination.
- */
+ @Override
@NonNull
- public abstract ParcelFileDescriptor getParcelFileDescriptor();
+ public String toString() {
+ return mFileDescriptorOutputOptionsInternal.toString().replaceFirst(
+ mFileDescriptorOutputOptionsInternal.getClass().getSuperclass().getSimpleName(),
+ getClass().getSimpleName());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return mFileDescriptorOutputOptionsInternal.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return mFileDescriptorOutputOptionsInternal.hashCode();
+ }
/** The builder of the {@link FileDescriptorOutputOptions}. */
- @AutoValue.Builder
- @SuppressWarnings("StaticFinalBuilder")
- public abstract static class Builder implements
+ public static final class Builder implements
OutputOptions.Builder<FileDescriptorOutputOptions, Builder> {
- Builder() {
- }
+ private final FileDescriptorOutputOptionsInternal.Builder mInternalBuilder =
+ new AutoValue_FileDescriptorOutputOptions_FileDescriptorOutputOptionsInternal
+ .Builder()
+ .setFileSizeLimit(FILE_SIZE_UNLIMITED);
/**
- * Defines the file descriptor used to store the result.
+ * Creates a builder of the {@link FileDescriptorOutputOptions} with a file descriptor.
*
* @param fileDescriptor the file descriptor to use as the output destination.
*/
- @NonNull
- public abstract Builder setParcelFileDescriptor(
- @NonNull ParcelFileDescriptor fileDescriptor);
+ public Builder(@NonNull ParcelFileDescriptor fileDescriptor) {
+ Preconditions.checkNotNull(fileDescriptor, "File descriptor can't be null.");
+ mInternalBuilder.setParcelFileDescriptor(fileDescriptor);
+ }
/**
* Sets the limit for the file length in bytes. Zero or negative values are considered
@@ -89,11 +112,34 @@
*/
@Override
@NonNull
- public abstract Builder setFileSizeLimit(long bytes);
+ public Builder setFileSizeLimit(long fileSizeLimitBytes) {
+ mInternalBuilder.setFileSizeLimit(fileSizeLimitBytes);
+ return this;
+ }
/** Builds the {@link FileDescriptorOutputOptions} instance. */
@Override
@NonNull
- public abstract FileDescriptorOutputOptions build();
+ public FileDescriptorOutputOptions build() {
+ return new FileDescriptorOutputOptions(mInternalBuilder.build());
+ }
+ }
+
+ @AutoValue
+ abstract static class FileDescriptorOutputOptionsInternal {
+ @NonNull
+ abstract ParcelFileDescriptor getParcelFileDescriptor();
+ abstract long getFileSizeLimit();
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ @NonNull
+ abstract Builder setParcelFileDescriptor(
+ @NonNull ParcelFileDescriptor parcelFileDescriptor);
+ @NonNull
+ abstract Builder setFileSizeLimit(long fileSizeLimitBytes);
+ @NonNull
+ abstract FileDescriptorOutputOptionsInternal build();
+ }
}
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/FileOutputOptions.java b/camera/camera-video/src/main/java/androidx/camera/video/FileOutputOptions.java
index 0268b38..610f235 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/FileOutputOptions.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/FileOutputOptions.java
@@ -17,6 +17,7 @@
package androidx.camera.video;
import androidx.annotation.NonNull;
+import androidx.core.util.Preconditions;
import com.google.auto.value.AutoValue;
@@ -30,42 +31,68 @@
* <p>To use a {@link android.os.ParcelFileDescriptor} as an output desination instead of a
* {@link File}, use {@link FileDescriptorOutputOptions}.
*/
-@AutoValue
-public abstract class FileOutputOptions extends OutputOptions {
+public final class FileOutputOptions extends OutputOptions {
- FileOutputOptions() {
+ private final FileOutputOptionsInternal mFileOutputOptionsInternal;
+
+ FileOutputOptions(@NonNull FileOutputOptionsInternal fileOutputOptionsInternal) {
super(OPTIONS_TYPE_FILE);
+ Preconditions.checkNotNull(fileOutputOptionsInternal,
+ "FileOutputOptionsInternal can't be null.");
+ mFileOutputOptionsInternal = fileOutputOptionsInternal;
}
- /** Returns a builder for this FileOutputOptions. */
+ /** Gets the File instance */
@NonNull
- public static Builder builder() {
- return new AutoValue_FileOutputOptions.Builder()
- .setFileSizeLimit(FILE_SIZE_UNLIMITED);
+ public File getFile() {
+ return mFileOutputOptionsInternal.getFile();
}
/**
* Gets the limit for the file length in bytes.
*/
@Override
- public abstract long getFileSizeLimit();
+ public long getFileSizeLimit() {
+ return mFileOutputOptionsInternal.getFileSizeLimit();
+ }
- /** Gets the File instance */
+ @Override
@NonNull
- public abstract File getFile();
+ public String toString() {
+ return mFileOutputOptionsInternal.toString().replaceFirst(
+ mFileOutputOptionsInternal.getClass().getSuperclass().getSimpleName(),
+ getClass().getSimpleName());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return mFileOutputOptionsInternal.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return mFileOutputOptionsInternal.hashCode();
+ }
/** The builder of the {@link FileOutputOptions}. */
- @AutoValue.Builder
- @SuppressWarnings("StaticFinalBuilder")
- public abstract static class Builder implements
- OutputOptions.Builder<FileOutputOptions, Builder> {
- Builder() {
- }
+ public static final class Builder implements OutputOptions.Builder<FileOutputOptions, Builder> {
+ private final FileOutputOptionsInternal.Builder mInternalBuilder =
+ new AutoValue_FileOutputOptions_FileOutputOptionsInternal.Builder()
+ .setFileSizeLimit(OutputOptions.FILE_SIZE_UNLIMITED);
- /** Defines the file used to store the result. */
- @SuppressWarnings("StreamFiles") // FileDescriptor API is in FileDescriptorOutputOptions
- @NonNull
- public abstract Builder setFile(@NonNull File file);
+ /**
+ * Creates a builder of the {@link FileOutputOptions} with a file object.
+ *
+ * <p>The file object can be created with a path using the {@link File} APIs. The path
+ * must be seekable and writable.
+ *
+ * @param file the file object.
+ * @see File
+ */
+ public Builder(@NonNull File file) {
+ Preconditions.checkNotNull(file, "File can't be null.");
+ mInternalBuilder.setFile(file);
+ }
/**
* Sets the limit for the file length in bytes. Zero or negative values are considered
@@ -81,11 +108,36 @@
*/
@Override
@NonNull
- public abstract Builder setFileSizeLimit(long bytes);
+ public Builder setFileSizeLimit(long fileSizeLimitBytes) {
+ mInternalBuilder.setFileSizeLimit(fileSizeLimitBytes);
+ return this;
+ }
/** Builds the {@link FileOutputOptions} instance. */
@Override
@NonNull
- public abstract FileOutputOptions build();
+ public FileOutputOptions build() {
+ return new FileOutputOptions(mInternalBuilder.build());
+ }
+ }
+
+ @AutoValue
+ abstract static class FileOutputOptionsInternal {
+ @NonNull
+ abstract File getFile();
+
+ abstract long getFileSizeLimit();
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ @NonNull
+ abstract Builder setFile(@NonNull File file);
+
+ @NonNull
+ abstract Builder setFileSizeLimit(long fileSizeLimitBytes);
+
+ @NonNull
+ abstract FileOutputOptionsInternal build();
+ }
}
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/MediaStoreOutputOptions.java b/camera/camera-video/src/main/java/androidx/camera/video/MediaStoreOutputOptions.java
index 1248749..e2dda51 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/MediaStoreOutputOptions.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/MediaStoreOutputOptions.java
@@ -21,6 +21,7 @@
import android.net.Uri;
import androidx.annotation.NonNull;
+import androidx.core.util.Preconditions;
import com.google.auto.value.AutoValue;
@@ -46,65 +47,106 @@
*
* }</pre>
*/
-@AutoValue
-public abstract class MediaStoreOutputOptions extends OutputOptions {
+public final class MediaStoreOutputOptions extends OutputOptions {
- MediaStoreOutputOptions() {
+ /**
+ * An empty {@link ContentValues}.
+ */
+ public static final ContentValues EMPTY_CONTENT_VALUES = new ContentValues();
+
+ private final MediaStoreOutputOptionsInternal mMediaStoreOutputOptionsInternal;
+
+ MediaStoreOutputOptions(
+ @NonNull MediaStoreOutputOptionsInternal mediaStoreOutputOptionsInternal) {
super(OPTIONS_TYPE_MEDIA_STORE);
+ Preconditions.checkNotNull(mediaStoreOutputOptionsInternal,
+ "MediaStoreOutputOptionsInternal can't be null.");
+ mMediaStoreOutputOptionsInternal = mediaStoreOutputOptionsInternal;
}
/**
- * Returns a builder for this MediaStoreOutputOptions.
+ * Gets the ContentResolver instance in order to convert URI to a file path.
*/
@NonNull
- public static Builder builder() {
- return new AutoValue_MediaStoreOutputOptions.Builder()
- .setFileSizeLimit(FILE_SIZE_UNLIMITED);
+ public ContentResolver getContentResolver() {
+ return mMediaStoreOutputOptionsInternal.getContentResolver();
}
/**
- * Gets the ContentResolver instance in order to convert Uri to a file path.
- */
- @NonNull
- public abstract ContentResolver getContentResolver();
-
- /**
* Gets the URL of the table to insert into.
*/
@NonNull
- public abstract Uri getCollection();
+ public Uri getCollection() {
+ return mMediaStoreOutputOptionsInternal.getCollection();
+ }
/**
* Gets the content values to be included in the created file.
*/
@NonNull
- public abstract ContentValues getContentValues();
+ public ContentValues getContentValues() {
+ return mMediaStoreOutputOptionsInternal.getContentValues();
+ }
/**
* Gets the limit for the file length in bytes.
*/
@Override
- public abstract long getFileSizeLimit();
+ public long getFileSizeLimit() {
+ return mMediaStoreOutputOptionsInternal.getFileSizeLimit();
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return mMediaStoreOutputOptionsInternal.toString().replaceFirst(
+ mMediaStoreOutputOptionsInternal.getClass().getSuperclass().getSimpleName(),
+ getClass().getSimpleName());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return mMediaStoreOutputOptionsInternal.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return mMediaStoreOutputOptionsInternal.hashCode();
+ }
/** The builder of the {@link MediaStoreOutputOptions}. */
- @AutoValue.Builder
- @SuppressWarnings("StaticFinalBuilder")
- public abstract static class Builder implements
+ public static final class Builder implements
OutputOptions.Builder<MediaStoreOutputOptions, Builder> {
- Builder() {
+ private final MediaStoreOutputOptionsInternal.Builder mInternalBuilder =
+ new AutoValue_MediaStoreOutputOptions_MediaStoreOutputOptionsInternal.Builder()
+ .setContentValues(EMPTY_CONTENT_VALUES)
+ .setFileSizeLimit(FILE_SIZE_UNLIMITED);
+
+ /**
+ * Creates a builder of the {@link MediaStoreOutputOptions} with media store options.
+ *
+ * @param contentResolver the content resolver instance.
+ * @param collectionUri the URI of the table to insert into.
+ */
+ public Builder(@NonNull ContentResolver contentResolver, @NonNull Uri collectionUri) {
+ Preconditions.checkNotNull(contentResolver, "Content resolver can't be null.");
+ Preconditions.checkNotNull(collectionUri, "Collection Uri can't be null.");
+ mInternalBuilder.setContentResolver(contentResolver).setCollection(collectionUri);
}
- /** Sets the ContentResolver instance. */
+ /**
+ * Sets the content values to be included in the created file.
+ *
+ * <p>If not set, defaults to {@link #EMPTY_CONTENT_VALUES}.
+ *
+ * @param contentValues the content values to be inserted.
+ */
@NonNull
- public abstract Builder setContentResolver(@NonNull ContentResolver contentResolver);
-
- /** Sets the URL of the table to insert into. */
- @NonNull
- public abstract Builder setCollection(@NonNull Uri collectionUri);
-
- /** Sets the content values to be included in the created file. */
- @NonNull
- public abstract Builder setContentValues(@NonNull ContentValues contentValues);
+ public Builder setContentValues(@NonNull ContentValues contentValues) {
+ Preconditions.checkNotNull(contentValues, "Content values can't be null.");
+ mInternalBuilder.setContentValues(contentValues);
+ return this;
+ }
/**
* Sets the limit for the file length in bytes. Zero or negative values are considered
@@ -120,11 +162,41 @@
*/
@Override
@NonNull
- public abstract Builder setFileSizeLimit(long bytes);
+ public Builder setFileSizeLimit(long fileSizeLimitBytes) {
+ mInternalBuilder.setFileSizeLimit(fileSizeLimitBytes);
+ return this;
+ }
/** Builds the {@link MediaStoreOutputOptions} instance. */
@Override
@NonNull
- public abstract MediaStoreOutputOptions build();
+ public MediaStoreOutputOptions build() {
+ return new MediaStoreOutputOptions(mInternalBuilder.build());
+ }
+ }
+
+ @AutoValue
+ abstract static class MediaStoreOutputOptionsInternal {
+ @NonNull
+ abstract ContentResolver getContentResolver();
+ @NonNull
+ abstract Uri getCollection();
+ @NonNull
+ abstract ContentValues getContentValues();
+ abstract long getFileSizeLimit();
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ @NonNull
+ abstract Builder setContentResolver(@NonNull ContentResolver contentResolver);
+ @NonNull
+ abstract Builder setCollection(@NonNull Uri collectionUri);
+ @NonNull
+ abstract Builder setContentValues(@NonNull ContentValues contentValues);
+ @NonNull
+ abstract Builder setFileSizeLimit(long fileSizeLimitBytes);
+ @NonNull
+ abstract MediaStoreOutputOptionsInternal build();
+ }
}
}
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/VideoRecordEventTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/VideoRecordEventTest.kt
index 749e4f0..71c4d17 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/VideoRecordEventTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/VideoRecordEventTest.kt
@@ -30,8 +30,7 @@
import java.io.File
private const val INVALID_FILE_PATH = "/invalid/file/path"
-private val TEST_OUTPUT_OPTION =
- FileOutputOptions.builder().setFile(File(INVALID_FILE_PATH)).build()
+private val TEST_OUTPUT_OPTION = FileOutputOptions.Builder(File(INVALID_FILE_PATH)).build()
private val TEST_RECORDING_STATE =
RecordingStats.of(0, 0, AudioStats.of(AudioStats.AUDIO_STATE_ACTIVE, null))
private val TEST_OUTPUT_RESULT = OutputResults.of(Uri.EMPTY)
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index d69c8f8..25d0175 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -546,9 +546,8 @@
contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, videoFileName);
contentValues.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
contentValues.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
- return MediaStoreOutputOptions.builder()
- .setContentResolver(getContentResolver())
- .setCollection(MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
+ return new MediaStoreOutputOptions.Builder(getContentResolver(),
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
.setContentValues(contentValues)
.build();
}
@@ -560,7 +559,7 @@
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
videoFileName + ".mp4");
Log.d(TAG, "VideoOutputFileOptions file: " + videoFile.getAbsolutePath());
- return FileOutputOptions.builder().setFile(videoFile).build();
+ return new FileOutputOptions.Builder(videoFile).build();
}
@NonNull
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/CoroutineBroadcastReceiverTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/CoroutineBroadcastReceiverTest.kt
index d1d5003..3085e20 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/CoroutineBroadcastReceiverTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/CoroutineBroadcastReceiverTest.kt
@@ -21,8 +21,10 @@
import android.content.Intent
import android.content.IntentFilter
import android.os.Looper.getMainLooper
+import android.util.Log
import androidx.glance.GlanceInternalApi
import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.FlakyTest
import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -55,10 +57,12 @@
try {
awaitCancellation()
} catch (ex: CancellationException) {
+ Log.i("CoroutineBRTest", "Scope cancelled")
scopeCancelled.countDown()
throw ex
}
}
+ Log.i("CoroutineBRTest", "Broadcast executed")
broadcastExecuted.countDown()
}
}
@@ -66,6 +70,7 @@
@MediumTest
@Test
+ @FlakyTest
fun onReceive() {
val broadcastReceiver = TestBroadcast()
context.registerReceiver(