1 /* 2 * Copyright 2021 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 androidx.camera.video; 18 19 import android.content.ContentResolver; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.net.Uri; 23 import android.provider.MediaStore; 24 25 import androidx.core.util.Preconditions; 26 27 import com.google.auto.value.AutoValue; 28 29 import org.jspecify.annotations.NonNull; 30 import org.jspecify.annotations.Nullable; 31 32 /** 33 * A class providing options for storing output to MediaStore. 34 * 35 * <p>Example: 36 * 37 * <pre>{@code 38 * 39 * ContentValues contentValues = new ContentValues(); 40 * contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "NEW_VIDEO"); 41 * contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4"); 42 * 43 * MediaStoreOutputOptions options = 44 * new MediaStoreOutputOptions.Builder( 45 * contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI) 46 * .setContentValues(contentValues) 47 * .build(); 48 * 49 * }</pre> 50 * 51 * <p>The output {@link Uri} can be obtained via {@link OutputResults#getOutputUri()} from 52 * {@link VideoRecordEvent.Finalize#getOutputResults()}. 53 * 54 * <p>For more information about setting collections {@link Uri} and {@link ContentValues}, read 55 * the <a href="https://developer.android.com/training/data-storage/shared/media"> 56 * Access media files from shared storage</a> and 57 * <a href="https://developer.android.com/reference/android/provider/MediaStore">MediaStore</a> 58 * developer guide. 59 */ 60 public final class MediaStoreOutputOptions extends OutputOptions { 61 62 /** 63 * An empty {@link ContentValues}. 64 */ 65 public static final @NonNull ContentValues EMPTY_CONTENT_VALUES = new ContentValues(); 66 67 private final MediaStoreOutputOptionsInternal mMediaStoreOutputOptionsInternal; 68 MediaStoreOutputOptions( @onNull MediaStoreOutputOptionsInternal mediaStoreOutputOptionsInternal)69 MediaStoreOutputOptions( 70 @NonNull MediaStoreOutputOptionsInternal mediaStoreOutputOptionsInternal) { 71 super(mediaStoreOutputOptionsInternal); 72 mMediaStoreOutputOptionsInternal = mediaStoreOutputOptionsInternal; 73 } 74 75 /** 76 * Gets the ContentResolver instance. 77 * 78 * @see Builder#Builder(ContentResolver, Uri) 79 */ getContentResolver()80 public @NonNull ContentResolver getContentResolver() { 81 return mMediaStoreOutputOptionsInternal.getContentResolver(); 82 } 83 84 /** 85 * Gets the URI of the collection to insert into. 86 * 87 * @see Builder#Builder(ContentResolver, Uri) 88 */ getCollectionUri()89 public @NonNull Uri getCollectionUri() { 90 return mMediaStoreOutputOptionsInternal.getCollectionUri(); 91 } 92 93 /** 94 * Gets the content values to be included in the created video row. 95 * 96 * @see Builder#setContentValues(ContentValues) 97 */ getContentValues()98 public @NonNull ContentValues getContentValues() { 99 return mMediaStoreOutputOptionsInternal.getContentValues(); 100 } 101 102 @Override toString()103 public @NonNull String toString() { 104 // Don't use Class.getSimpleName(), class name will be changed by proguard obfuscation. 105 return mMediaStoreOutputOptionsInternal.toString().replaceFirst( 106 "MediaStoreOutputOptionsInternal", "MediaStoreOutputOptions"); 107 } 108 109 @Override equals(@ullable Object o)110 public boolean equals(@Nullable Object o) { 111 if (this == o) { 112 return true; 113 } 114 if (!(o instanceof MediaStoreOutputOptions)) { 115 return false; 116 } 117 return mMediaStoreOutputOptionsInternal.equals( 118 ((MediaStoreOutputOptions) o).mMediaStoreOutputOptionsInternal); 119 } 120 121 @Override hashCode()122 public int hashCode() { 123 return mMediaStoreOutputOptionsInternal.hashCode(); 124 } 125 126 /** The builder of the {@link MediaStoreOutputOptions} object. */ 127 public static final class Builder extends 128 OutputOptions.Builder<MediaStoreOutputOptions, Builder> { 129 130 private final MediaStoreOutputOptionsInternal.Builder mInternalBuilder; 131 132 /** 133 * Creates a builder of the {@link MediaStoreOutputOptions} with media store options. 134 * 135 * <p>The ContentResolver can be obtained by app {@link Context#getContentResolver() 136 * context} and is used to access to MediaStore. 137 * 138 * <p>{@link MediaStore} class provides APIs to obtain the collection URI. A collection 139 * URI corresponds to a storage volume on the device shared storage. A common collection 140 * URI used to access the primary external storage is 141 * {@link MediaStore.Video.Media#EXTERNAL_CONTENT_URI}. 142 * {@link MediaStore.Video.Media#getContentUri} can also be used to query different 143 * storage volumes. For more information, read 144 * <a href="https://developer.android.com/training/data-storage/shared/media"> 145 * Access media files from shared storage</a> developer guide. 146 * 147 * <p>When recording a video, a corresponding video row will be created in the input 148 * collection, and the content values set by {@link #setContentValues} will also be 149 * written to this row. 150 * 151 * @param contentResolver the ContentResolver instance. 152 * @param collectionUri the URI of the collection to insert into. 153 */ Builder(@onNull ContentResolver contentResolver, @NonNull Uri collectionUri)154 public Builder(@NonNull ContentResolver contentResolver, @NonNull Uri collectionUri) { 155 super(new AutoValue_MediaStoreOutputOptions_MediaStoreOutputOptionsInternal.Builder()); 156 Preconditions.checkNotNull(contentResolver, "Content resolver can't be null."); 157 Preconditions.checkNotNull(collectionUri, "Collection Uri can't be null."); 158 mInternalBuilder = (MediaStoreOutputOptionsInternal.Builder) mRootInternalBuilder; 159 mInternalBuilder.setContentResolver(contentResolver) 160 .setCollectionUri(collectionUri) 161 .setContentValues(EMPTY_CONTENT_VALUES); 162 } 163 164 /** 165 * Sets the content values to be included in the created video row. 166 * 167 * <p>The content values is a set of key/value paris used to store the metadata of a 168 * video item. The keys are defined in {@link MediaStore.MediaColumns} and 169 * {@link MediaStore.Video.VideoColumns}. 170 * When recording a video, a corresponding video row will be created in the input 171 * collection, and this content values will also be written to this row. If a key is not 172 * defined in the MediaStore, the corresponding value will be ignored. 173 * 174 * <p>If not set, defaults to {@link #EMPTY_CONTENT_VALUES}. 175 * 176 * @param contentValues the content values to be inserted. 177 */ setContentValues(@onNull ContentValues contentValues)178 public @NonNull Builder setContentValues(@NonNull ContentValues contentValues) { 179 Preconditions.checkNotNull(contentValues, "Content values can't be null."); 180 mInternalBuilder.setContentValues(contentValues); 181 return this; 182 } 183 184 /** Builds the {@link MediaStoreOutputOptions} instance. */ 185 @Override build()186 public @NonNull MediaStoreOutputOptions build() { 187 return new MediaStoreOutputOptions(mInternalBuilder.build()); 188 } 189 } 190 191 @AutoValue 192 abstract static class MediaStoreOutputOptionsInternal extends OutputOptionsInternal { getContentResolver()193 abstract @NonNull ContentResolver getContentResolver(); getCollectionUri()194 abstract @NonNull Uri getCollectionUri(); getContentValues()195 abstract @NonNull ContentValues getContentValues(); 196 197 @SuppressWarnings("NullableProblems") // Nullable problem in AutoValue generated class 198 @AutoValue.Builder 199 abstract static class Builder extends OutputOptionsInternal.Builder<Builder> { setContentResolver(@onNull ContentResolver contentResolver)200 abstract @NonNull Builder setContentResolver(@NonNull ContentResolver contentResolver); setCollectionUri(@onNull Uri collectionUri)201 abstract @NonNull Builder setCollectionUri(@NonNull Uri collectionUri); setContentValues(@onNull ContentValues contentValues)202 abstract @NonNull Builder setContentValues(@NonNull ContentValues contentValues); 203 @Override build()204 abstract @NonNull MediaStoreOutputOptionsInternal build(); 205 } 206 } 207 } 208