|
|
@@ -0,0 +1,238 @@
|
|
|
+package com.atmob.voiceai.utils;
|
|
|
+
|
|
|
+import android.content.ContentResolver;
|
|
|
+import android.content.ContentValues;
|
|
|
+import android.content.Context;
|
|
|
+import android.net.Uri;
|
|
|
+import android.os.Build;
|
|
|
+import android.os.ParcelFileDescriptor;
|
|
|
+import android.provider.MediaStore;
|
|
|
+
|
|
|
+import androidx.annotation.IntDef;
|
|
|
+import androidx.annotation.RequiresApi;
|
|
|
+
|
|
|
+import com.atmob.common.runtime.ContextUtil;
|
|
|
+
|
|
|
+import java.io.File;
|
|
|
+import java.io.FileInputStream;
|
|
|
+import java.io.FileOutputStream;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.lang.annotation.Retention;
|
|
|
+import java.lang.annotation.RetentionPolicy;
|
|
|
+
|
|
|
+public class MediaStoreHelper {
|
|
|
+
|
|
|
+
|
|
|
+ public static final int TYPE_IMAGE = 1;
|
|
|
+
|
|
|
+ public static final int TYPE_VIDEO = 2;
|
|
|
+
|
|
|
+ public static final int TYPE_AUDIO = 3;
|
|
|
+
|
|
|
+ public static final int TYPE_FILES = 4;
|
|
|
+
|
|
|
+ public static final int TYPE_DOWNLOADS = 5;
|
|
|
+
|
|
|
+ @Retention(RetentionPolicy.SOURCE)
|
|
|
+ @IntDef({TYPE_IMAGE, TYPE_VIDEO, TYPE_AUDIO, TYPE_FILES, TYPE_DOWNLOADS})
|
|
|
+ @interface MediaType {
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static void saveToSharedStorage(@MediaType int mediaType, File file, String fileName) throws Exception {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ saveToSharedStorage(mediaType, java.nio.file.Files.newInputStream(file.toPath()), fileName);
|
|
|
+ } else {
|
|
|
+ saveToSharedStorage(mediaType, new FileInputStream(file), fileName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void saveToSharedStorage(@MediaType int mediaType, InputStream inputStream, String fileName) throws Exception {
|
|
|
+ // Add a media item that other apps don't see until the item is
|
|
|
+ // fully written to the media store.
|
|
|
+ Context applicationContext = ContextUtil.getContext().getApplicationContext();
|
|
|
+
|
|
|
+ ContentResolver resolver = applicationContext.getContentResolver();
|
|
|
+
|
|
|
+ Media targetMedia = getTargetMedia(mediaType);
|
|
|
+
|
|
|
+ // Find all media files on the primary external storage device.
|
|
|
+ Uri mediaCollection = targetMedia.getMediaCollection();
|
|
|
+
|
|
|
+ ContentValues mediaDetails = new ContentValues();
|
|
|
+ mediaDetails.put(targetMedia.displayName(), fileName);
|
|
|
+
|
|
|
+ // lock media file until it's fully written.
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ mediaDetails.put(targetMedia.isPending(), 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ Uri mediaContentUri = resolver.insert(mediaCollection, mediaDetails);
|
|
|
+
|
|
|
+ if (mediaContentUri == null) {
|
|
|
+ throw new IllegalStateException("Failed to create new media item.");
|
|
|
+ }
|
|
|
+
|
|
|
+ // "w" for write.
|
|
|
+ try (ParcelFileDescriptor pfd =
|
|
|
+ resolver.openFileDescriptor(mediaContentUri, "w", null);
|
|
|
+ InputStream is = inputStream;
|
|
|
+ FileOutputStream fos = pfd == null ? null : new FileOutputStream(pfd.getFileDescriptor())
|
|
|
+ ) {
|
|
|
+ if (fos == null) {
|
|
|
+ throw new IllegalStateException("Failed to open new media item.");
|
|
|
+ }
|
|
|
+ // Write data into the pending media file.
|
|
|
+ byte[] buf = new byte[8192];
|
|
|
+ int len;
|
|
|
+ while ((len = is.read(buf)) > 0) {
|
|
|
+ fos.write(buf, 0, len);
|
|
|
+ }
|
|
|
+ fos.flush();
|
|
|
+ } finally {
|
|
|
+ // finished, release the "pending" status.
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ mediaDetails.clear();
|
|
|
+ mediaDetails.put(targetMedia.isPending(), 0);
|
|
|
+ resolver.update(mediaContentUri, mediaDetails, null, null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Media getTargetMedia(int mediaType) {
|
|
|
+ switch (mediaType) {
|
|
|
+ case TYPE_IMAGE:
|
|
|
+ return new Image();
|
|
|
+ case TYPE_VIDEO:
|
|
|
+ return new Video();
|
|
|
+ case TYPE_AUDIO:
|
|
|
+ return new Audio();
|
|
|
+ case TYPE_FILES:
|
|
|
+ return new Files();
|
|
|
+ case TYPE_DOWNLOADS:
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ return new Downloads();
|
|
|
+ }
|
|
|
+ return new Files();
|
|
|
+ default:
|
|
|
+ throw new IllegalArgumentException("Unknown media type: " + mediaType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Image implements Media {
|
|
|
+ @Override
|
|
|
+ public Uri getMediaCollection() {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ return MediaStore.Images.Media
|
|
|
+ .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
|
|
|
+ } else {
|
|
|
+ return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String displayName() {
|
|
|
+ return MediaStore.Images.Media.DISPLAY_NAME;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String isPending() {
|
|
|
+ return MediaStore.Images.Media.IS_PENDING;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Video implements Media {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Uri getMediaCollection() {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ return MediaStore.Video.Media
|
|
|
+ .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
|
|
|
+ } else {
|
|
|
+ return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String displayName() {
|
|
|
+ return MediaStore.Video.Media.DISPLAY_NAME;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String isPending() {
|
|
|
+ return MediaStore.Video.Media.IS_PENDING;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Audio implements Media {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Uri getMediaCollection() {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ return MediaStore.Audio.Media
|
|
|
+ .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
|
|
|
+ } else {
|
|
|
+ return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String displayName() {
|
|
|
+ return MediaStore.Audio.Media.DISPLAY_NAME;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String isPending() {
|
|
|
+ return MediaStore.Audio.Media.IS_PENDING;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Files implements Media {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Uri getMediaCollection() {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ return MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
|
|
|
+ } else {
|
|
|
+ return MediaStore.Files.getContentUri("external");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String displayName() {
|
|
|
+ return MediaStore.Files.FileColumns.DISPLAY_NAME;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String isPending() {
|
|
|
+ return MediaStore.Files.FileColumns.IS_PENDING;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Downloads implements Media {
|
|
|
+
|
|
|
+ @RequiresApi(api = Build.VERSION_CODES.Q)
|
|
|
+ @Override
|
|
|
+ public Uri getMediaCollection() {
|
|
|
+ return MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String displayName() {
|
|
|
+ return MediaStore.Downloads.DISPLAY_NAME;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String isPending() {
|
|
|
+ return MediaStore.Downloads.IS_PENDING;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private interface Media {
|
|
|
+ Uri getMediaCollection();
|
|
|
+
|
|
|
+ String displayName();
|
|
|
+
|
|
|
+ String isPending();
|
|
|
+ }
|
|
|
+}
|