Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7b1f89e authored by Felipe Leme's avatar Felipe Leme
Browse files

Created a new API to grant scoped directory access to applications.

The Storage Access Framework currently provides an
API (OPEN_DOCUMENT_TREE) that lets the user pick a directory to be used
by the application. Although this APIs is very powerful (for example, it
allows the user to create sub-directories, delete and share files,
etc...), it's too complex for simpler use cases like a camera
application trying to have access to the standard Pictures directory.

The new API (OPEN_EXTERNAL_DIRECTORY) simplifies this scenario by
letting the application select a standard, pre-defined directory (as
defined by android.os.Environment). The user still needs to grant access
to the requested directory, but the resulting UI will be simpler and
more straightforward.

Usage example:

  // ...
  int requestCode = 42;
  Intent intent = new Intent(Intent.ACTION_OPEN_EXTERNAL_DIRECTORY);
  intent.setData(Uri.fromFile(
    new File(Environment.getExternalStorageDirectory(),
             Environment.DIRECTORY_PICTURES)));
  startActivityForResult(intent, requestCode);
  // ...

  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode != Activity.RESULT_OK) {
      return;
    }
    Uri uri = data.getData();
    int modeFlags = data.getFlags()
        & (Intent.FLAG_GRANT_READ_URI_PERMISSION
        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    getContentResolver().takePersistableUriPermission(uri, modeFlags);
  }

BUG: 23011462
Change-Id: Ie832026532f74e681ef220b761667eb66ff3c8b0
parent 390c9d2d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8426,6 +8426,7 @@ package android.content {
    field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
    field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
    field public static final java.lang.String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
    field public static final java.lang.String ACTION_OPEN_EXTERNAL_DIRECTORY = "android.intent.action.OPEN_EXTERNAL_DIRECTORY";
    field public static final java.lang.String ACTION_PACKAGES_SUSPENDED = "android.intent.action.PACKAGES_SUSPENDED";
    field public static final java.lang.String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
    field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
+1 −0
Original line number Diff line number Diff line
@@ -8698,6 +8698,7 @@ package android.content {
    field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
    field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
    field public static final java.lang.String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
    field public static final java.lang.String ACTION_OPEN_EXTERNAL_DIRECTORY = "android.intent.action.OPEN_EXTERNAL_DIRECTORY";
    field public static final java.lang.String ACTION_PACKAGES_SUSPENDED = "android.intent.action.PACKAGES_SUSPENDED";
    field public static final java.lang.String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
    field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
+1 −0
Original line number Diff line number Diff line
@@ -8431,6 +8431,7 @@ package android.content {
    field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
    field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
    field public static final java.lang.String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
    field public static final java.lang.String ACTION_OPEN_EXTERNAL_DIRECTORY = "android.intent.action.OPEN_EXTERNAL_DIRECTORY";
    field public static final java.lang.String ACTION_PACKAGES_SUSPENDED = "android.intent.action.PACKAGES_SUSPENDED";
    field public static final java.lang.String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
    field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
+33 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.content.res.TypedArray;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -3186,6 +3187,38 @@ public class Intent implements Parcelable, Cloneable {
    public static final String
            ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";

    /**
     * Activity Action: Give access to a standard storage directory after obtaining the user's
     * approval.
     * <p>
     * When invoked, the system will ask the user to grant access to the requested directory (and
     * its descendants).
     * <p>
     * To gain access to descendant (child, grandchild, etc) documents, use
     * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)} and
     * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)} with the returned URI.
     * <p>
     * Input: full path to a standard directory, in the form of
     * {@code STORAGE_ROOT + STANDARD_DIRECTORY}, where {@code STORAGE_ROOT} is the physical path of
     * a storage container, and {@code STANDARD_DIRECTORY} is one of
     * {@link Environment#DIRECTORY_MUSIC}, {@link Environment#DIRECTORY_PODCASTS},
     * {@link Environment#DIRECTORY_RINGTONES}, {@link Environment#DIRECTORY_ALARMS},
     * {@link Environment#DIRECTORY_NOTIFICATIONS}, {@link Environment#DIRECTORY_PICTURES},
     * {@link Environment#DIRECTORY_MOVIES}, {@link Environment#DIRECTORY_DOWNLOADS},
     * {@link Environment#DIRECTORY_DCIM}, or {@link Environment#DIRECTORY_DOCUMENTS}
     * <p>
     * For example, to open the "Pictures" folder in the default external storage, the intent's data
     * would be: {@code Uri.fromFile(new File(Environment.getExternalStorageDirectory(),
     * Environment.DIRECTORY_PICTURES))}.
     * <p>
     * Output: The URI representing the requested directory tree.
     *
     * @see DocumentsContract
     */
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    public static final String
            ACTION_OPEN_EXTERNAL_DIRECTORY = "android.intent.action.OPEN_EXTERNAL_DIRECTORY";

    /** {@hide} */
    public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";

+36 −5
Original line number Diff line number Diff line
@@ -478,6 +478,37 @@ public class Environment {
     */
    public static String DIRECTORY_DOCUMENTS = "Documents";

    /**
     * List of standard storage directories.
     * <p>
     * Each of its values have its own constant:
     * <ul>
     *   <li>{@link #DIRECTORY_MUSIC}
     *   <li>{@link #DIRECTORY_PODCASTS}
     *   <li>{@link #DIRECTORY_ALARMS}
     *   <li>{@link #DIRECTORY_RINGTONES}
     *   <li>{@link #DIRECTORY_NOTIFICATIONS}
     *   <li>{@link #DIRECTORY_PICTURES}
     *   <li>{@link #DIRECTORY_MOVIES}
     *   <li>{@link #DIRECTORY_DOWNLOADS}
     *   <li>{@link #DIRECTORY_DCIM}
     *   <li>{@link #DIRECTORY_DOCUMENTS}
     * </ul>
     * @hide
     */
    public static final String[] STANDARD_DIRECTORIES = {
            DIRECTORY_MUSIC,
            DIRECTORY_PODCASTS,
            DIRECTORY_RINGTONES,
            DIRECTORY_ALARMS,
            DIRECTORY_NOTIFICATIONS,
            DIRECTORY_PICTURES,
            DIRECTORY_MOVIES,
            DIRECTORY_DOWNLOADS,
            DIRECTORY_DCIM,
            DIRECTORY_DOCUMENTS
    };

    /**
     * Get a top-level shared/external storage directory for placing files of a
     * particular type. This is where the user will typically place and manage
@@ -500,8 +531,8 @@ public class Environment {
     *            {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
     *            {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
     *            {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
     *            {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
     *            {@link #DIRECTORY_DCIM}. May not be null.
     *            {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS},
     *            {@link #DIRECTORY_DCIM}, or {@link #DIRECTORY_DOCUMENTS}. May not be null.
     * @return Returns the File path for the directory. Note that this directory
     *         may not yet exist, so you must make sure it exists before using
     *         it such as with {@link File#mkdirs File.mkdirs()}.
Loading