2 * Copyright (C) 2013 The CyanogenMod Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.cyanogenmod.filemanager.util;
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.net.Uri;
23 import android.os.UserHandle;
24 import android.provider.BaseColumns;
25 import android.provider.MediaStore;
26 import android.provider.MediaStore.MediaColumns;
27 import android.text.TextUtils;
30 import java.util.HashMap;
34 * A helper class with useful methods to extract media data.
36 public final class MediaHelper {
38 private static final String EMULATED_STORAGE_SOURCE = System.getenv("EMULATED_STORAGE_SOURCE");
39 private static final String EMULATED_STORAGE_TARGET = System.getenv("EMULATED_STORAGE_TARGET");
40 private static final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");
42 private static final String INTERNAL_VOLUME = "internal";
43 private static final String EXTERNAL_VOLUME = "external";
46 * URIs that are relevant for determining album art;
47 * useful for content observer registration
49 public static final Uri[] RELEVANT_URIS = new Uri[] {
50 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
51 MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI
55 * Method that returns an array with all the unique albums paths and ids.
57 * @param cr The ContentResolver
58 * @return Map<String, Long> The albums map
60 public static Map<String, Long> getAllAlbums(ContentResolver cr) {
61 Map<String, Long> albums = new HashMap<String, Long>();
62 final String[] projection =
64 "distinct " + MediaStore.Audio.Media.ALBUM_ID,
65 "substr(" + MediaStore.Audio.Media.DATA + ", 0, length(" +
66 MediaStore.Audio.Media.DATA + ") - length(" +
67 MediaStore.Audio.Media.DISPLAY_NAME + "))"
69 final String where = MediaStore.Audio.Media.IS_MUSIC + " = ?";
70 Cursor c = cr.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
71 projection, where, new String[]{"1"}, null);
74 while (c.moveToNext()) {
75 long albumId = c.getLong(0);
76 String albumPath = c.getString(1);
77 albums.put(albumPath, albumId);
87 * Method that returns the album thumbnail path by its identifier.
89 * @param cr The ContentResolver
90 * @param albumId The album identifier to search
91 * @return String The album thumbnail path
93 public static String getAlbumThumbnailPath(ContentResolver cr, long albumId) {
94 final String[] projection = {MediaStore.Audio.Albums.ALBUM_ART};
95 final String where = BaseColumns._ID + " = ?";
96 Cursor c = cr.query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
97 projection, where, new String[]{String.valueOf(albumId)}, null);
99 if (c != null && c.moveToNext()) {
100 return c.getString(0);
111 * Method that converts a file reference to a content uri reference
113 * @param cr A content resolver
114 * @param file The file reference
115 * @return Uri The content uri or null if file not exists in the media database
117 public static Uri fileToContentUri(Context context, File file) {
118 // Normalize the path to ensure media search
119 final String normalizedPath = normalizeMediaPath(file.getAbsolutePath());
121 // Check in external and internal storages
122 Uri uri = fileToContentUri(context, normalizedPath, EXTERNAL_VOLUME);
126 uri = fileToContentUri(context, normalizedPath, INTERNAL_VOLUME);
134 * Method that converts a file reference to a content uri reference
136 * @param cr A content resolver
137 * @param path The path to search
138 * @param volume The volume
139 * @return Uri The content uri or null if file not exists in the media database
141 private static Uri fileToContentUri(Context context, String path, String volume) {
142 String[] projection = null;
143 final String where = MediaColumns.DATA + " = ?";
144 File file = new File(path);
145 Uri baseUri = MediaStore.Files.getContentUri(volume);
146 boolean isMimeTypeImage = false, isMimeTypeVideo = false, isMimeTypeAudio = false;
147 isMimeTypeImage = MimeTypeHelper.KnownMimeTypeResolver.isImage(context, file);
148 if (!isMimeTypeImage) {
149 isMimeTypeVideo = MimeTypeHelper.KnownMimeTypeResolver.isVideo(context, file);
150 if (!isMimeTypeVideo) {
151 isMimeTypeAudio = MimeTypeHelper.KnownMimeTypeResolver.isAudio(context, file);
154 if (isMimeTypeImage || isMimeTypeVideo || isMimeTypeAudio) {
155 projection = new String[]{BaseColumns._ID};
156 if (isMimeTypeImage) {
157 baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
158 } else if (isMimeTypeVideo) {
159 baseUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
160 } else if (isMimeTypeAudio) {
161 baseUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
164 projection = new String[]{BaseColumns._ID, MediaStore.Files.FileColumns.MEDIA_TYPE};
166 ContentResolver cr = context.getContentResolver();
167 Cursor c = cr.query(baseUri, projection, where, new String[]{path}, null);
169 if (c != null && c.moveToNext()) {
170 boolean isValid = false;
171 if (isMimeTypeImage || isMimeTypeVideo || isMimeTypeAudio) {
174 int type = c.getInt(c.getColumnIndexOrThrow(
175 MediaStore.Files.FileColumns.MEDIA_TYPE));
179 // Do not force to use content uri for no media files
180 long id = c.getLong(c.getColumnIndexOrThrow(BaseColumns._ID));
181 return Uri.withAppendedPath(baseUri, String.valueOf(id));
193 * Method that converts a content uri to a file system path
195 * @param cr The content resolver
196 * @param uri The content uri
197 * @return File The file reference
199 public static File contentUriToFile(ContentResolver cr, Uri uri) {
201 if (uri == null || uri.getScheme() == null || uri.getScheme().compareTo("content") != 0) {
205 // Retrieve the request id
208 id = Long.parseLong(new File(uri.getPath()).getName());
209 } catch (NumberFormatException nfex) {
213 // Check in external and internal storages
214 File file = mediaIdToFile(cr, id, EXTERNAL_VOLUME);
218 file = mediaIdToFile(cr, id, INTERNAL_VOLUME);
226 * Method that converts a content uri to a file system path
228 * @param cr The content resolver
229 * @param id The media database id
230 * @param volume The volume
231 * @return File The file reference
233 private static File mediaIdToFile(ContentResolver cr, long id, String volume) {
234 final String[] projection = {MediaColumns.DATA};
235 final String where = MediaColumns._ID + " = ?";
236 Uri baseUri = MediaStore.Files.getContentUri(volume);
237 Cursor c = cr.query(baseUri, projection, where, new String[]{String.valueOf(id)}, null);
239 if (c != null && c.moveToNext()) {
240 return new File(c.getString(c.getColumnIndexOrThrow(MediaColumns.DATA)));
251 * Method that converts a not standard media mount path to a standard media path
253 * @param path The path to normalize
254 * @return String The normalized media path
256 public static String normalizeMediaPath(String path) {
257 // Retrieve all the paths and check that we have this environment vars
258 if (TextUtils.isEmpty(EMULATED_STORAGE_SOURCE) ||
259 TextUtils.isEmpty(EMULATED_STORAGE_TARGET) ||
260 TextUtils.isEmpty(EXTERNAL_STORAGE)) {
264 // We need to convert EMULATED_STORAGE_SOURCE -> EMULATED_STORAGE_TARGET
265 if (path.startsWith(EMULATED_STORAGE_SOURCE)) {
266 path = path.replace(EMULATED_STORAGE_SOURCE, EMULATED_STORAGE_TARGET);
268 // We need to convert EXTERNAL_STORAGE -> EMULATED_STORAGE_TARGET / userId
269 if (path.startsWith(EXTERNAL_STORAGE)) {
270 final String userId = String.valueOf(UserHandle.myUserId());
271 final String target = new File(EMULATED_STORAGE_TARGET, userId).getAbsolutePath();
272 path = path.replace(EXTERNAL_STORAGE, target);