From: csagan5 <32685696+csagan5@users.noreply.github.com> Date: Wed, 11 Mar 2020 19:56:49 +0100 Subject: Add missing android_provider --- third_party/android_provider/BUILD.gn | 14 ++ .../android/provider/MediaStoreUtils.java | 228 ++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 third_party/android_provider/BUILD.gn create mode 100644 third_party/android_provider/java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java diff --git a/third_party/android_provider/BUILD.gn b/third_party/android_provider/BUILD.gn new file mode 100644 --- /dev/null +++ b/third_party/android_provider/BUILD.gn @@ -0,0 +1,14 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +assert(is_android) + +android_library("android_provider_java") { + sources = [ + "java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java", + ] + deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ] +} diff --git a/third_party/android_provider/java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java b/third_party/android_provider/java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java new file mode 100644 --- /dev/null +++ b/third_party/android_provider/java/src/org/chromium/third_party/android/provider/MediaStoreUtils.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package org.chromium.third_party.android.provider; + +import android.content.ContentValues; +import android.content.Context; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.provider.MediaStore; +import android.provider.MediaStore.DownloadColumns; +import android.provider.MediaStore.MediaColumns; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.io.FileNotFoundException; +import java.io.OutputStream; +import java.util.Objects; + +/** + * Utility class to contribute download to the public download collection using + * MediaStore API from Q. + */ +public class MediaStoreUtils { + private static final String TAG = "MediaStoreUtils"; + + /** + * Creates a new pending media item using the given parameters. Pending items + * are expected to have a short lifetime, and owners should either + * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a + * pending item within a few hours after first creating it. + * + * @param context Application context. + * @param params Parameters used to configure the item. + * @return token which can be passed to {@link #openPending(Context, Uri)} + * to work with this pending item. + */ + public static @NonNull Uri createPending( + @NonNull Context context, @NonNull PendingParams params) { + return context.getContentResolver().insert(params.mInsertUri, params.mInsertValues); + } + + /** + * Opens a pending media item to make progress on it. You can open a pending + * item multiple times before finally calling either + * {@link PendingSession#publish()} or {@link PendingSession#abandon()}. + * + * @param uri token which was previously returned from + * {@link #createPending(Context, PendingParams)}. + * @return pending session that was opened. + */ + public static @NonNull PendingSession openPending(@NonNull Context context, @NonNull Uri uri) { + return new PendingSession(context, uri); + } + + /** + * Parameters that describe a pending media item. + */ + public static class PendingParams { + final Uri mInsertUri; + final ContentValues mInsertValues; + + /** + * Creates parameters that describe a pending media item. + * + * @param insertUri the {@code content://} Uri where this pending item + * should be inserted when finally published. For example, to + * publish an image, use + * {@link MediaStore.Images.Media#getContentUri(String)}. + * @param displayName Display name of the item. + * @param mimeType MIME type of the item. + */ + public PendingParams( + @NonNull Uri insertUri, @NonNull String displayName, @NonNull String mimeType) { + mInsertUri = Objects.requireNonNull(insertUri); + final long now = System.currentTimeMillis() / 1000; + mInsertValues = new ContentValues(); + mInsertValues.put(MediaColumns.DISPLAY_NAME, Objects.requireNonNull(displayName)); + mInsertValues.put(MediaColumns.MIME_TYPE, Objects.requireNonNull(mimeType)); + mInsertValues.put(MediaColumns.DATE_ADDED, now); + mInsertValues.put(MediaColumns.DATE_MODIFIED, now); + try { + setPendingContentValues(this.mInsertValues, true); + } catch (Exception e) { + Log.e(TAG, "Unable to set pending content values.", e); + } + } + + /** + * Optionally sets the Uri from where the file has been downloaded. This is used + * for files being added to {@link Downloads} table. + * + * @see DownloadColumns#DOWNLOAD_URI + */ + public void setDownloadUri(@Nullable Uri downloadUri) { + if (downloadUri == null) { + mInsertValues.remove(DownloadColumns.DOWNLOAD_URI); + } else { + mInsertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString()); + } + } + + /** + * Optionally set the Uri indicating HTTP referer of the file. This is used for + * files being added to {@link Downloads} table. + * + * @see DownloadColumns#REFERER_URI + */ + public void setRefererUri(@Nullable Uri refererUri) { + if (refererUri == null) { + mInsertValues.remove(DownloadColumns.REFERER_URI); + } else { + mInsertValues.put(DownloadColumns.REFERER_URI, refererUri.toString()); + } + } + + /** + * Sets the expiration time of the download. + * + * @time Epoch time in seconds. + */ + public void setExpirationTime(long time) { + mInsertValues.put("date_expires", time); + } + } + + /** + * Session actively working on a pending media item. Pending items are + * expected to have a short lifetime, and owners should either + * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a + * pending item within a few hours after first creating it. + */ + public static class PendingSession implements AutoCloseable { + private final Context mContext; + private final Uri mUri; + + /** + * Create a new pending session item to be published. + * @param context Contexxt of the application. + * @param uri Token which was previously returned from + * {@link #createPending(Context, PendingParams)}. + */ + PendingSession(Context context, Uri uri) { + mContext = Objects.requireNonNull(context); + mUri = Objects.requireNonNull(uri); + } + + /** + * Open the underlying file representing this media item. When a media + * item is successfully completed, you should + * {@link ParcelFileDescriptor#close()} and then {@link #publish()} it. + * + * @return ParcelFileDescriptor to be written into. + */ + public @NonNull ParcelFileDescriptor open() throws FileNotFoundException { + return mContext.getContentResolver().openFileDescriptor(mUri, "rw"); + } + + /** + * Open the underlying file representing this media item. When a media + * item is successfully completed, you should + * {@link OutputStream#close()} and then {@link #publish()} it. + * + * @return OutputStream to be written into. + */ + public @NonNull OutputStream openOutputStream() throws FileNotFoundException { + return mContext.getContentResolver().openOutputStream(mUri); + } + + /** + * When this media item is successfully completed, call this method to + * publish and make the final item visible to the user. + * + * @return the final {@code content://} Uri representing the newly + * published media. + */ + public @NonNull Uri publish() { + try { + final ContentValues values = new ContentValues(); + setPendingContentValues(values, false); + values.putNull("date_expires"); + mContext.getContentResolver().update(mUri, values, null, null); + } catch (Exception e) { + Log.e(TAG, "Unable to publish pending session.", e); + } + return mUri; + } + + /** + * When this media item has failed to be completed, call this method to + * destroy the pending item record and any data related to it. + */ + public void abandon() { + mContext.getContentResolver().delete(mUri, null, null); + } + + @Override + public void close() { + // No resources to close, but at least we can inform people that no + // progress is being actively made. + } + } + + /** + * Helper method to set the ContentValues to pending or non-pending. + * @param values ContentValues to be set. + * @param isPending Whether the item is pending. + */ + private static void setPendingContentValues(ContentValues values, boolean isPending) + throws Exception { + values.put(MediaColumns.IS_PENDING, isPending ? 1 : 0); + } +} -- 2.17.1