1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
|
/*
* Copyright (C) 2013 The CyanogenMod 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 com.cyanogenmod.filemanager.util;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.text.TextUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* A helper class with useful methods to extract media data.
*/
public final class MediaHelper {
private static final String EMULATED_STORAGE_SOURCE = System.getenv("EMULATED_STORAGE_SOURCE");
private static final String EMULATED_STORAGE_TARGET = System.getenv("EMULATED_STORAGE_TARGET");
private static final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");
private static final String INTERNAL_VOLUME = "internal";
public static final String EXTERNAL_VOLUME = "external";
/**
* URIs that are relevant for determining album art;
* useful for content observer registration
*/
public static final Uri[] RELEVANT_URIS = new Uri[] {
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI
};
/**
* Method that returns an array with all the unique albums paths and ids.
*
* @param cr The ContentResolver
* @return Map<String, Long> The albums map
*/
public static Map<String, Long> getAllAlbums(ContentResolver cr) {
Map<String, Long> albums = new HashMap<String, Long>();
final String[] projection =
{
"distinct " + MediaStore.Audio.Media.ALBUM_ID,
"substr(" + MediaStore.Audio.Media.DATA + ", 0, length(" +
MediaStore.Audio.Media.DATA + ") - length(" +
MediaStore.Audio.Media.DISPLAY_NAME + "))"
};
final String where = MediaStore.Audio.Media.IS_MUSIC + " = ?";
Cursor c = cr.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
projection, where, new String[]{"1"}, null);
if (c != null) {
try {
while (c.moveToNext()) {
long albumId = c.getLong(0);
String albumPath = c.getString(1);
albums.put(albumPath, albumId);
}
} finally {
c.close();
}
}
return albums;
}
/**
* Method that returns the album thumbnail path by its identifier.
*
* @param cr The ContentResolver
* @param albumId The album identifier to search
* @return String The album thumbnail path
*/
public static String getAlbumThumbnailPath(ContentResolver cr, long albumId) {
final String[] projection = {MediaStore.Audio.Albums.ALBUM_ART};
final String where = BaseColumns._ID + " = ?";
Cursor c = cr.query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
projection, where, new String[]{String.valueOf(albumId)}, null);
try {
if (c != null && c.moveToNext()) {
return c.getString(0);
}
} finally {
if (c != null) {
c.close();
}
}
return null;
}
/**
* Method that converts a file reference to a content uri reference
*
* @param cr A content resolver
* @param file The file reference
* @return Uri The content uri or null if file not exists in the media database
*/
public static Uri fileToContentUri(Context context, File file) {
// Normalize the path to ensure media search
final String normalizedPath = normalizeMediaPath(file.getAbsolutePath());
// Check in external and internal storages
Uri uri = fileToContentUri(context, normalizedPath, EXTERNAL_VOLUME);
if (uri != null) {
return uri;
}
uri = fileToContentUri(context, normalizedPath, INTERNAL_VOLUME);
if (uri != null) {
return uri;
}
return null;
}
/**
* Method that converts a file reference to a content uri reference
*
* @param cr A content resolver
* @param path The path to search
* @param volume The volume
* @return Uri The content uri or null if file not exists in the media database
*/
private static Uri fileToContentUri(Context context, String path, String volume) {
String[] projection = null;
final String where = MediaColumns.DATA + " = ?";
File file = new File(path);
Uri baseUri = MediaStore.Files.getContentUri(volume);
boolean isMimeTypeImage = false, isMimeTypeVideo = false, isMimeTypeAudio = false;
isMimeTypeImage = MimeTypeHelper.KnownMimeTypeResolver.isImage(context, file);
if (!isMimeTypeImage) {
isMimeTypeVideo = MimeTypeHelper.KnownMimeTypeResolver.isVideo(context, file);
if (!isMimeTypeVideo) {
isMimeTypeAudio = MimeTypeHelper.KnownMimeTypeResolver.isAudio(context, file);
}
}
if (isMimeTypeImage || isMimeTypeVideo || isMimeTypeAudio) {
projection = new String[]{BaseColumns._ID};
if (isMimeTypeImage) {
baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if (isMimeTypeVideo) {
baseUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if (isMimeTypeAudio) {
baseUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
} else {
projection = new String[]{BaseColumns._ID, MediaStore.Files.FileColumns.MEDIA_TYPE};
}
ContentResolver cr = context.getContentResolver();
Cursor c = cr.query(baseUri, projection, where, new String[]{path}, null);
try {
if (c != null && c.moveToNext()) {
boolean isValid = false;
if (isMimeTypeImage || isMimeTypeVideo || isMimeTypeAudio) {
isValid = true;
} else {
int type = c.getInt(c.getColumnIndexOrThrow(
MediaStore.Files.FileColumns.MEDIA_TYPE));
isValid = type != 0;
}
if (isValid) {
// Do not force to use content uri for no media files
long id = c.getLong(c.getColumnIndexOrThrow(BaseColumns._ID));
return Uri.withAppendedPath(baseUri, String.valueOf(id));
}
}
} finally {
if (c != null) {
c.close();
}
}
return null;
}
/**
* Method that converts a content uri to a file system path
*
* @param cr The content resolver
* @param uri The content uri
* @return File The file reference
*/
public static File contentUriToFile(ContentResolver cr, Uri uri) {
// Sanity checks
if (uri == null || uri.getScheme() == null || uri.getScheme().compareTo("content") != 0) {
return null;
}
// Retrieve the request id
long id = 0;
try {
id = Long.parseLong(new File(uri.getPath()).getName());
} catch (NumberFormatException nfex) {
return null;
}
// Check in external and internal storages
File file = mediaIdToFile(cr, id, EXTERNAL_VOLUME);
if (file != null) {
return file;
}
file = mediaIdToFile(cr, id, INTERNAL_VOLUME);
if (file != null) {
return file;
}
return null;
}
/**
* Method that converts a content uri to a file system path
*
* @param cr The content resolver
* @param id The media database id
* @param volume The volume
* @return File The file reference
*/
private static File mediaIdToFile(ContentResolver cr, long id, String volume) {
final String[] projection = {MediaColumns.DATA};
final String where = MediaColumns._ID + " = ?";
Uri baseUri = MediaStore.Files.getContentUri(volume);
Cursor c = cr.query(baseUri, projection, where, new String[]{String.valueOf(id)}, null);
try {
if (c != null && c.moveToNext()) {
return new File(c.getString(c.getColumnIndexOrThrow(MediaColumns.DATA)));
}
} finally {
if (c != null) {
c.close();
}
}
return null;
}
/**
* Method that converts a not standard media mount path to a standard media path
*
* @param path The path to normalize
* @return String The normalized media path
*/
public static String normalizeMediaPath(String path) {
// Retrieve all the paths and check that we have this environment vars
if (TextUtils.isEmpty(EMULATED_STORAGE_SOURCE) ||
TextUtils.isEmpty(EMULATED_STORAGE_TARGET) ||
TextUtils.isEmpty(EXTERNAL_STORAGE)) {
return path;
}
// We need to convert EMULATED_STORAGE_SOURCE -> EMULATED_STORAGE_TARGET
if (path.startsWith(EMULATED_STORAGE_SOURCE)) {
path = path.replace(EMULATED_STORAGE_SOURCE, EMULATED_STORAGE_TARGET);
}
// We need to convert EXTERNAL_STORAGE -> EMULATED_STORAGE_TARGET / userId
if (path.startsWith(EXTERNAL_STORAGE)) {
final String userId = String.valueOf(UserHandle.myUserId());
final String target = new File(EMULATED_STORAGE_TARGET, userId).getAbsolutePath();
path = path.replace(EXTERNAL_STORAGE, target);
}
return path;
}
}
|