|
|
|
|
@@ -102,20 +102,40 @@ public final class MediaStore {
|
|
|
|
|
public static final @NonNull Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Volume name used for content on "internal" storage of device. This
|
|
|
|
|
* volume contains media distributed with the device, such as built-in
|
|
|
|
|
* ringtones and wallpapers.
|
|
|
|
|
* Synthetic volume name that provides a view of all content across the
|
|
|
|
|
* "internal" storage of the device.
|
|
|
|
|
* <p>
|
|
|
|
|
* This synthetic volume provides a merged view of all media distributed
|
|
|
|
|
* with the device, such as built-in ringtones and wallpapers.
|
|
|
|
|
* <p>
|
|
|
|
|
* Because this is a synthetic volume, you can't insert new content into
|
|
|
|
|
* this volume.
|
|
|
|
|
*/
|
|
|
|
|
public static final String VOLUME_INTERNAL = "internal";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Volume name used for content on "external" storage of device. This only
|
|
|
|
|
* includes media on the primary shared storage device; the contents of any
|
|
|
|
|
* secondary storage devices can be obtained using
|
|
|
|
|
* {@link #getAllVolumeNames(Context)}.
|
|
|
|
|
* Synthetic volume name that provides a view of all content across the
|
|
|
|
|
* "external" storage of the device.
|
|
|
|
|
* <p>
|
|
|
|
|
* This synthetic volume provides a merged view of all media across all
|
|
|
|
|
* currently attached external storage devices.
|
|
|
|
|
* <p>
|
|
|
|
|
* Because this is a synthetic volume, you can't insert new content into
|
|
|
|
|
* this volume. Instead, you can insert content into a specific storage
|
|
|
|
|
* volume obtained from {@link #getExternalVolumeNames(Context)}.
|
|
|
|
|
*/
|
|
|
|
|
public static final String VOLUME_EXTERNAL = "external";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Specific volume name that represents the primary external storage device
|
|
|
|
|
* at {@link Environment#getExternalStorageDirectory()}.
|
|
|
|
|
* <p>
|
|
|
|
|
* This volume may not always be available, such as when the user has
|
|
|
|
|
* ejected the device. You can find a list of all specific volume names
|
|
|
|
|
* using {@link #getExternalVolumeNames(Context)}.
|
|
|
|
|
*/
|
|
|
|
|
public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary";
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public static final String SCAN_FILE_CALL = "scan_file";
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
@@ -1036,6 +1056,16 @@ public final class MediaStore {
|
|
|
|
|
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
|
|
|
|
|
public static final String OWNER_PACKAGE_NAME = "owner_package_name";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Volume name of the specific storage device where this media item is
|
|
|
|
|
* persisted. The value is typically one of the volume names returned
|
|
|
|
|
* from {@link MediaStore#getExternalVolumeNames(Context)}.
|
|
|
|
|
* <p>
|
|
|
|
|
* This is a read-only column that is automatically computed.
|
|
|
|
|
*/
|
|
|
|
|
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
|
|
|
|
|
public static final String VOLUME_NAME = "volume_name";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Relative path of this media item within the storage device where it
|
|
|
|
|
* is persisted. For example, an item stored at
|
|
|
|
|
@@ -1408,7 +1438,7 @@ public final class MediaStore {
|
|
|
|
|
final StorageVolume sv = sm.getStorageVolume(path);
|
|
|
|
|
if (sv != null) {
|
|
|
|
|
if (sv.isPrimary()) {
|
|
|
|
|
return VOLUME_EXTERNAL;
|
|
|
|
|
return VOLUME_EXTERNAL_PRIMARY;
|
|
|
|
|
} else {
|
|
|
|
|
return checkArgumentVolumeName(sv.getNormalizedUuid());
|
|
|
|
|
}
|
|
|
|
|
@@ -1710,7 +1740,7 @@ public final class MediaStore {
|
|
|
|
|
String stringUrl = null; /* value to be returned */
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
url = cr.insert(EXTERNAL_CONTENT_URI, values);
|
|
|
|
|
url = cr.insert(getContentUri(VOLUME_EXTERNAL_PRIMARY), values);
|
|
|
|
|
|
|
|
|
|
if (source != null) {
|
|
|
|
|
try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
|
|
|
|
|
@@ -3224,22 +3254,29 @@ public final class MediaStore {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return list of all volume names currently available. This includes a
|
|
|
|
|
* unique name for each shared storage device that is currently mounted.
|
|
|
|
|
* <p>
|
|
|
|
|
* Each name can be passed to APIs like
|
|
|
|
|
* {@link MediaStore.Images.Media#getContentUri(String)} to query media at
|
|
|
|
|
* that location.
|
|
|
|
|
*/
|
|
|
|
|
/** @removed */
|
|
|
|
|
@Deprecated
|
|
|
|
|
public static @NonNull Set<String> getAllVolumeNames(@NonNull Context context) {
|
|
|
|
|
return getExternalVolumeNames(context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return list of all specific volume names that make up
|
|
|
|
|
* {@link #VOLUME_EXTERNAL}. This includes a unique volume name for each
|
|
|
|
|
* shared storage device that is currently attached, which typically
|
|
|
|
|
* includes {@link MediaStore#VOLUME_EXTERNAL_PRIMARY}.
|
|
|
|
|
* <p>
|
|
|
|
|
* Each specific volume name can be passed to APIs like
|
|
|
|
|
* {@link MediaStore.Images.Media#getContentUri(String)} to interact with
|
|
|
|
|
* media on that storage device.
|
|
|
|
|
*/
|
|
|
|
|
public static @NonNull Set<String> getExternalVolumeNames(@NonNull Context context) {
|
|
|
|
|
final StorageManager sm = context.getSystemService(StorageManager.class);
|
|
|
|
|
final Set<String> volumeNames = new ArraySet<>();
|
|
|
|
|
volumeNames.add(VOLUME_INTERNAL);
|
|
|
|
|
for (VolumeInfo vi : sm.getVolumes()) {
|
|
|
|
|
if (vi.isVisibleForUser(UserHandle.myUserId()) && vi.isMountedReadable()) {
|
|
|
|
|
if (vi.isPrimary()) {
|
|
|
|
|
volumeNames.add(VOLUME_EXTERNAL);
|
|
|
|
|
volumeNames.add(VOLUME_EXTERNAL_PRIMARY);
|
|
|
|
|
} else {
|
|
|
|
|
volumeNames.add(vi.getNormalizedFsUuid());
|
|
|
|
|
}
|
|
|
|
|
@@ -3270,6 +3307,8 @@ public final class MediaStore {
|
|
|
|
|
return volumeName;
|
|
|
|
|
} else if (VOLUME_EXTERNAL.equals(volumeName)) {
|
|
|
|
|
return volumeName;
|
|
|
|
|
} else if (VOLUME_EXTERNAL_PRIMARY.equals(volumeName)) {
|
|
|
|
|
return volumeName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When not one of the well-known values above, it must be a hex UUID
|
|
|
|
|
@@ -3285,8 +3324,9 @@ public final class MediaStore {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return path where the given volume is mounted. Not valid for
|
|
|
|
|
* {@link #VOLUME_INTERNAL}.
|
|
|
|
|
* Return path where the given specific volume is mounted. Not valid for
|
|
|
|
|
* {@link #VOLUME_INTERNAL} or {@link #VOLUME_EXTERNAL}, since those are
|
|
|
|
|
* broad collections that cover many paths.
|
|
|
|
|
*
|
|
|
|
|
* @hide
|
|
|
|
|
*/
|
|
|
|
|
@@ -3297,8 +3337,12 @@ public final class MediaStore {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (VOLUME_EXTERNAL.equals(volumeName)) {
|
|
|
|
|
return Environment.getExternalStorageDirectory();
|
|
|
|
|
switch (volumeName) {
|
|
|
|
|
case VOLUME_INTERNAL:
|
|
|
|
|
case VOLUME_EXTERNAL:
|
|
|
|
|
throw new FileNotFoundException(volumeName + " has no associated path");
|
|
|
|
|
case VOLUME_EXTERNAL_PRIMARY:
|
|
|
|
|
return Environment.getExternalStorageDirectory();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final StorageManager sm = AppGlobals.getInitialApplication()
|
|
|
|
|
@@ -3328,23 +3372,31 @@ public final class MediaStore {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final Context context = AppGlobals.getInitialApplication();
|
|
|
|
|
final UserManager um = context.getSystemService(UserManager.class);
|
|
|
|
|
|
|
|
|
|
final ArrayList<File> res = new ArrayList<>();
|
|
|
|
|
if (VOLUME_INTERNAL.equals(volumeName)) {
|
|
|
|
|
addCanoncialFile(res, new File(Environment.getRootDirectory(), "media"));
|
|
|
|
|
addCanoncialFile(res, new File(Environment.getOemDirectory(), "media"));
|
|
|
|
|
addCanoncialFile(res, new File(Environment.getProductDirectory(), "media"));
|
|
|
|
|
addCanonicalFile(res, new File(Environment.getRootDirectory(), "media"));
|
|
|
|
|
addCanonicalFile(res, new File(Environment.getOemDirectory(), "media"));
|
|
|
|
|
addCanonicalFile(res, new File(Environment.getProductDirectory(), "media"));
|
|
|
|
|
} else if (VOLUME_EXTERNAL.equals(volumeName)) {
|
|
|
|
|
for (String exactVolume : getExternalVolumeNames(context)) {
|
|
|
|
|
addCanonicalFile(res, getVolumePath(exactVolume));
|
|
|
|
|
}
|
|
|
|
|
if (um.isDemoUser()) {
|
|
|
|
|
addCanonicalFile(res, Environment.getDataPreloadsMediaDirectory());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
addCanoncialFile(res, getVolumePath(volumeName));
|
|
|
|
|
final UserManager um = AppGlobals.getInitialApplication()
|
|
|
|
|
.getSystemService(UserManager.class);
|
|
|
|
|
if (VOLUME_EXTERNAL.equals(volumeName) && um.isDemoUser()) {
|
|
|
|
|
addCanoncialFile(res, Environment.getDataPreloadsMediaDirectory());
|
|
|
|
|
addCanonicalFile(res, getVolumePath(volumeName));
|
|
|
|
|
if (VOLUME_EXTERNAL_PRIMARY.equals(volumeName) && um.isDemoUser()) {
|
|
|
|
|
addCanonicalFile(res, Environment.getDataPreloadsMediaDirectory());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void addCanoncialFile(List<File> list, File file) {
|
|
|
|
|
private static void addCanonicalFile(List<File> list, File file) {
|
|
|
|
|
try {
|
|
|
|
|
list.add(file.getCanonicalFile());
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
@@ -3382,12 +3434,12 @@ public final class MediaStore {
|
|
|
|
|
* <p>
|
|
|
|
|
* No other assumptions should be made about the meaning of the version.
|
|
|
|
|
* <p>
|
|
|
|
|
* This method returns the version for {@link MediaStore#VOLUME_EXTERNAL};
|
|
|
|
|
* to obtain a version for a different volume, use
|
|
|
|
|
* {@link #getVersion(Context, String)}.
|
|
|
|
|
* This method returns the version for
|
|
|
|
|
* {@link MediaStore#VOLUME_EXTERNAL_PRIMARY}; to obtain a version for a
|
|
|
|
|
* different volume, use {@link #getVersion(Context, String)}.
|
|
|
|
|
*/
|
|
|
|
|
public static @NonNull String getVersion(@NonNull Context context) {
|
|
|
|
|
return getVersion(context, VOLUME_EXTERNAL);
|
|
|
|
|
return getVersion(context, VOLUME_EXTERNAL_PRIMARY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@@ -3401,7 +3453,7 @@ public final class MediaStore {
|
|
|
|
|
*
|
|
|
|
|
* @param volumeName specific volume to obtain an opaque version string for.
|
|
|
|
|
* Must be one of the values returned from
|
|
|
|
|
* {@link #getAllVolumeNames(Context)}.
|
|
|
|
|
* {@link #getExternalVolumeNames(Context)}.
|
|
|
|
|
*/
|
|
|
|
|
public static @NonNull String getVersion(@NonNull Context context, @NonNull String volumeName) {
|
|
|
|
|
final ContentResolver resolver = context.getContentResolver();
|
|
|
|
|
|