Merge changes I68379740,I9053218f
* changes: MediaFile: Add support for some popular non-media file types. Media scanner support for tracking files of arbitrary type.
This commit is contained in:
committed by
Android (Google) Code Review
commit
0c7d077e9a
@@ -339,6 +339,18 @@ public final class MediaStore {
|
||||
*/
|
||||
public static final String MEDIA_ID = "media_id";
|
||||
}
|
||||
|
||||
/**
|
||||
* The MIME type of the file
|
||||
* <P>Type: TEXT</P>
|
||||
*/
|
||||
public static final String MIME_TYPE = "mime_type";
|
||||
|
||||
/**
|
||||
* The title of the content
|
||||
* <P>Type: TEXT</P>
|
||||
*/
|
||||
public static final String TITLE = "title";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,8 +38,7 @@ struct MediaScanner {
|
||||
|
||||
typedef bool (*ExceptionCheck)(void* env);
|
||||
virtual status_t processDirectory(
|
||||
const char *path, const char *extensions,
|
||||
MediaScannerClient &client,
|
||||
const char *path, MediaScannerClient &client,
|
||||
ExceptionCheck exceptionCheck, void *exceptionEnv);
|
||||
|
||||
void setLocale(const char *locale);
|
||||
@@ -55,9 +54,8 @@ private:
|
||||
char *mLocale;
|
||||
|
||||
status_t doProcessDirectory(
|
||||
char *path, int pathRemaining, const char *extensions,
|
||||
MediaScannerClient &client, ExceptionCheck exceptionCheck,
|
||||
void *exceptionEnv);
|
||||
char *path, int pathRemaining, MediaScannerClient &client,
|
||||
ExceptionCheck exceptionCheck, void *exceptionEnv);
|
||||
|
||||
MediaScanner(const MediaScanner &);
|
||||
MediaScanner &operator=(const MediaScanner &);
|
||||
|
||||
@@ -34,8 +34,6 @@ import java.util.List;
|
||||
* {@hide}
|
||||
*/
|
||||
public class MediaFile {
|
||||
// comma separated list of all file extensions supported by the media scanner
|
||||
public final static String sFileExtensions;
|
||||
|
||||
// Audio file types
|
||||
public static final int FILE_TYPE_MP3 = 1;
|
||||
@@ -84,6 +82,17 @@ public class MediaFile {
|
||||
public static final int FILE_TYPE_WPL = 43;
|
||||
private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U;
|
||||
private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_WPL;
|
||||
|
||||
// Other popular file types
|
||||
public static final int FILE_TYPE_TEXT = 100;
|
||||
public static final int FILE_TYPE_HTML = 101;
|
||||
public static final int FILE_TYPE_PDF = 102;
|
||||
public static final int FILE_TYPE_XML = 103;
|
||||
public static final int FILE_TYPE_MS_WORD = 104;
|
||||
public static final int FILE_TYPE_MS_EXCEL = 105;
|
||||
public static final int FILE_TYPE_MS_POWERPOINT = 106;
|
||||
public static final int FILE_TYPE_FLAC = 107;
|
||||
public static final int FILE_TYPE_ZIP = 108;
|
||||
|
||||
static class MediaFileType {
|
||||
|
||||
@@ -132,16 +141,6 @@ public class MediaFile {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isWMVEnabled() {
|
||||
List<VideoDecoder> decoders = DecoderCapabilities.getVideoDecoders();
|
||||
for (VideoDecoder decoder: decoders) {
|
||||
if (decoder == VideoDecoder.VIDEO_DECODER_WMV) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static {
|
||||
addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3);
|
||||
addFileType("M4A", FILE_TYPE_M4A, "audio/mp4", MtpConstants.FORMAT_MPEG);
|
||||
@@ -176,10 +175,8 @@ public class MediaFile {
|
||||
addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska");
|
||||
addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts");
|
||||
|
||||
if (isWMVEnabled()) {
|
||||
addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", MtpConstants.FORMAT_WMV);
|
||||
addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
|
||||
}
|
||||
addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", MtpConstants.FORMAT_WMV);
|
||||
addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
|
||||
|
||||
addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg", MtpConstants.FORMAT_EXIF_JPEG);
|
||||
addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg", MtpConstants.FORMAT_EXIF_JPEG);
|
||||
@@ -192,53 +189,74 @@ public class MediaFile {
|
||||
addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST);
|
||||
addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST);
|
||||
|
||||
// compute file extensions list for native Media Scanner
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Iterator<String> iterator = sFileTypeMap.keySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
if (builder.length() > 0) {
|
||||
builder.append(',');
|
||||
}
|
||||
builder.append(iterator.next());
|
||||
}
|
||||
sFileExtensions = builder.toString();
|
||||
addFileType("TXT", FILE_TYPE_TEXT, "text/plain", MtpConstants.FORMAT_TEXT);
|
||||
addFileType("HTM", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML);
|
||||
addFileType("HTML", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML);
|
||||
addFileType("PDF", FILE_TYPE_PDF, "application/pdf");
|
||||
addFileType("DOC", FILE_TYPE_MS_WORD, "application/msword", MtpConstants.FORMAT_MS_WORD_DOCUMENT);
|
||||
addFileType("XLS", FILE_TYPE_MS_EXCEL, "application/vnd.ms-excel", MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET);
|
||||
addFileType("PPT", FILE_TYPE_MS_POWERPOINT, "application/mspowerpoint", MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION);
|
||||
addFileType("FLAC", FILE_TYPE_FLAC, "audio/flac", MtpConstants.FORMAT_FLAC);
|
||||
addFileType("ZIP", FILE_TYPE_ZIP, "application/zip");
|
||||
}
|
||||
|
||||
|
||||
public static boolean isAudioFileType(int fileType) {
|
||||
return ((fileType >= FIRST_AUDIO_FILE_TYPE &&
|
||||
fileType <= LAST_AUDIO_FILE_TYPE) ||
|
||||
(fileType >= FIRST_MIDI_FILE_TYPE &&
|
||||
fileType <= LAST_MIDI_FILE_TYPE));
|
||||
}
|
||||
|
||||
|
||||
public static boolean isVideoFileType(int fileType) {
|
||||
return (fileType >= FIRST_VIDEO_FILE_TYPE &&
|
||||
fileType <= LAST_VIDEO_FILE_TYPE);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isImageFileType(int fileType) {
|
||||
return (fileType >= FIRST_IMAGE_FILE_TYPE &&
|
||||
fileType <= LAST_IMAGE_FILE_TYPE);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isPlayListFileType(int fileType) {
|
||||
return (fileType >= FIRST_PLAYLIST_FILE_TYPE &&
|
||||
fileType <= LAST_PLAYLIST_FILE_TYPE);
|
||||
}
|
||||
|
||||
|
||||
public static MediaFileType getFileType(String path) {
|
||||
int lastDot = path.lastIndexOf(".");
|
||||
if (lastDot < 0)
|
||||
return null;
|
||||
return sFileTypeMap.get(path.substring(lastDot + 1).toUpperCase());
|
||||
}
|
||||
|
||||
|
||||
// generates a title based on file name
|
||||
public static String getFileTitle(String path) {
|
||||
// extract file name after last slash
|
||||
int lastSlash = path.lastIndexOf('/');
|
||||
if (lastSlash >= 0) {
|
||||
lastSlash++;
|
||||
if (lastSlash < path.length()) {
|
||||
path = path.substring(lastSlash);
|
||||
}
|
||||
}
|
||||
// truncate the file extension (if any)
|
||||
int lastDot = path.lastIndexOf('.');
|
||||
if (lastDot > 0) {
|
||||
path = path.substring(0, lastDot);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static int getFileTypeForMimeType(String mimeType) {
|
||||
Integer value = sMimeTypeMap.get(mimeType);
|
||||
return (value == null ? 0 : value.intValue());
|
||||
}
|
||||
|
||||
public static String getMimeTypeForFile(String path) {
|
||||
MediaFileType mediaFileType = getFileType(path);
|
||||
return (mediaFileType == null ? null : mediaFileType.mimeType);
|
||||
}
|
||||
|
||||
public static int getFormatCode(String fileName, String mimeType) {
|
||||
if (mimeType != null) {
|
||||
Integer value = sMimeTypeToFormatMap.get(mimeType);
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.os.SystemProperties;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.Settings;
|
||||
import android.provider.MediaStore.Audio;
|
||||
import android.provider.MediaStore.Files;
|
||||
import android.provider.MediaStore.Images;
|
||||
import android.provider.MediaStore.Video;
|
||||
import android.provider.MediaStore.Audio.Genres;
|
||||
@@ -109,41 +110,15 @@ public class MediaScanner
|
||||
|
||||
private final static String TAG = "MediaScanner";
|
||||
|
||||
private static final String[] AUDIO_PROJECTION = new String[] {
|
||||
Audio.Media._ID, // 0
|
||||
Audio.Media.DATA, // 1
|
||||
Audio.Media.DATE_MODIFIED, // 2
|
||||
private static final String[] PRESCAN_PROJECTION = new String[] {
|
||||
Files.FileColumns._ID, // 0
|
||||
Files.FileColumns.DATA, // 1
|
||||
Files.FileColumns.DATE_MODIFIED, // 2
|
||||
};
|
||||
|
||||
private static final int ID_AUDIO_COLUMN_INDEX = 0;
|
||||
private static final int PATH_AUDIO_COLUMN_INDEX = 1;
|
||||
private static final int DATE_MODIFIED_AUDIO_COLUMN_INDEX = 2;
|
||||
|
||||
private static final String[] VIDEO_PROJECTION = new String[] {
|
||||
Video.Media._ID, // 0
|
||||
Video.Media.DATA, // 1
|
||||
Video.Media.DATE_MODIFIED, // 2
|
||||
};
|
||||
|
||||
private static final int ID_VIDEO_COLUMN_INDEX = 0;
|
||||
private static final int PATH_VIDEO_COLUMN_INDEX = 1;
|
||||
private static final int DATE_MODIFIED_VIDEO_COLUMN_INDEX = 2;
|
||||
|
||||
private static final String[] IMAGES_PROJECTION = new String[] {
|
||||
Images.Media._ID, // 0
|
||||
Images.Media.DATA, // 1
|
||||
Images.Media.DATE_MODIFIED, // 2
|
||||
};
|
||||
|
||||
private static final int ID_IMAGES_COLUMN_INDEX = 0;
|
||||
private static final int PATH_IMAGES_COLUMN_INDEX = 1;
|
||||
private static final int DATE_MODIFIED_IMAGES_COLUMN_INDEX = 2;
|
||||
|
||||
private static final String[] PLAYLISTS_PROJECTION = new String[] {
|
||||
Audio.Playlists._ID, // 0
|
||||
Audio.Playlists.DATA, // 1
|
||||
Audio.Playlists.DATE_MODIFIED, // 2
|
||||
};
|
||||
private static final int PRESCAN_ID_COLUMN_INDEX = 0;
|
||||
private static final int PRESCAN_PATH_COLUMN_INDEX = 1;
|
||||
private static final int PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;
|
||||
|
||||
private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
|
||||
Audio.Playlists.Members.PLAYLIST_ID, // 0
|
||||
@@ -304,6 +279,7 @@ public class MediaScanner
|
||||
private Uri mThumbsUri;
|
||||
private Uri mGenresUri;
|
||||
private Uri mPlaylistsUri;
|
||||
private Uri mFilesUri;
|
||||
private boolean mProcessPlaylists, mProcessGenres;
|
||||
private int mMtpObjectHandle;
|
||||
|
||||
@@ -354,7 +330,7 @@ public class MediaScanner
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mPath;
|
||||
return mPath + " mTableUri: " + mTableUri + " mRowId: " + mRowId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,6 +408,9 @@ public class MediaScanner
|
||||
}
|
||||
|
||||
mMimeType = null;
|
||||
mFileType = 0;
|
||||
mFileSize = fileSize;
|
||||
|
||||
// try mimeType first, if it is specified
|
||||
if (mimeType != null) {
|
||||
mFileType = MediaFile.getFileTypeForMimeType(mimeType);
|
||||
@@ -439,7 +418,6 @@ public class MediaScanner
|
||||
mMimeType = mimeType;
|
||||
}
|
||||
}
|
||||
mFileSize = fileSize;
|
||||
|
||||
// if mimeType was not specified, compute file type based on file extension.
|
||||
if (mMimeType == null) {
|
||||
@@ -456,7 +434,17 @@ public class MediaScanner
|
||||
}
|
||||
FileCacheEntry entry = mFileCache.get(key);
|
||||
if (entry == null) {
|
||||
entry = new FileCacheEntry(null, 0, path, 0);
|
||||
Uri tableUri;
|
||||
if (MediaFile.isVideoFileType(mFileType)) {
|
||||
tableUri = mVideoUri;
|
||||
} else if (MediaFile.isImageFileType(mFileType)) {
|
||||
tableUri = mImagesUri;
|
||||
} else if (MediaFile.isAudioFileType(mFileType)) {
|
||||
tableUri = mAudioUri;
|
||||
} else {
|
||||
tableUri = mFilesUri;
|
||||
}
|
||||
entry = new FileCacheEntry(tableUri, 0, path, 0);
|
||||
mFileCache.put(key, entry);
|
||||
}
|
||||
entry.mSeenInFileSystem = true;
|
||||
@@ -501,7 +489,8 @@ public class MediaScanner
|
||||
doScanFile(path, mimeType, lastModified, fileSize, false);
|
||||
}
|
||||
|
||||
public Uri doScanFile(String path, String mimeType, long lastModified, long fileSize, boolean scanAlways) {
|
||||
public Uri doScanFile(String path, String mimeType, long lastModified,
|
||||
long fileSize, boolean scanAlways) {
|
||||
Uri result = null;
|
||||
// long t1 = System.currentTimeMillis();
|
||||
try {
|
||||
@@ -516,7 +505,9 @@ public class MediaScanner
|
||||
boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||
|
||||
(!ringtones && !notifications && !alarms && !podcasts);
|
||||
|
||||
if (!MediaFile.isImageFileType(mFileType)) {
|
||||
// we only extract metadata for audio and video files
|
||||
if (MediaFile.isAudioFileType(mFileType)
|
||||
|| MediaFile.isVideoFileType(mFileType)) {
|
||||
processFile(path, mimeType, this);
|
||||
}
|
||||
|
||||
@@ -627,9 +618,6 @@ public class MediaScanner
|
||||
map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
|
||||
map.put(MediaStore.MediaColumns.SIZE, mFileSize);
|
||||
map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
|
||||
if (mMtpObjectHandle != 0) {
|
||||
map.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
|
||||
}
|
||||
|
||||
if (MediaFile.isVideoFileType(mFileType)) {
|
||||
map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING));
|
||||
@@ -659,21 +647,6 @@ public class MediaScanner
|
||||
boolean alarms, boolean music, boolean podcasts)
|
||||
throws RemoteException {
|
||||
// update database
|
||||
Uri tableUri;
|
||||
boolean isAudio = MediaFile.isAudioFileType(mFileType);
|
||||
boolean isVideo = MediaFile.isVideoFileType(mFileType);
|
||||
boolean isImage = MediaFile.isImageFileType(mFileType);
|
||||
if (isVideo) {
|
||||
tableUri = mVideoUri;
|
||||
} else if (isImage) {
|
||||
tableUri = mImagesUri;
|
||||
} else if (isAudio) {
|
||||
tableUri = mAudioUri;
|
||||
} else {
|
||||
// don't add file to database if not audio, video or image
|
||||
return null;
|
||||
}
|
||||
entry.mTableUri = tableUri;
|
||||
|
||||
// use album artist if artist is missing
|
||||
if (mArtist == null || mArtist.length() == 0) {
|
||||
@@ -683,20 +656,7 @@ public class MediaScanner
|
||||
ContentValues values = toValues();
|
||||
String title = values.getAsString(MediaStore.MediaColumns.TITLE);
|
||||
if (title == null || TextUtils.isEmpty(title.trim())) {
|
||||
title = values.getAsString(MediaStore.MediaColumns.DATA);
|
||||
// extract file name after last slash
|
||||
int lastSlash = title.lastIndexOf('/');
|
||||
if (lastSlash >= 0) {
|
||||
lastSlash++;
|
||||
if (lastSlash < title.length()) {
|
||||
title = title.substring(lastSlash);
|
||||
}
|
||||
}
|
||||
// truncate the file extension (if any)
|
||||
int lastDot = title.lastIndexOf('.');
|
||||
if (lastDot > 0) {
|
||||
title = title.substring(0, lastDot);
|
||||
}
|
||||
title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
|
||||
values.put(MediaStore.MediaColumns.TITLE, title);
|
||||
}
|
||||
String album = values.getAsString(Audio.Media.ALBUM);
|
||||
@@ -720,7 +680,7 @@ public class MediaScanner
|
||||
}
|
||||
}
|
||||
long rowId = entry.mRowId;
|
||||
if (isAudio && rowId == 0) {
|
||||
if (MediaFile.isAudioFileType(mFileType) && rowId == 0) {
|
||||
// Only set these for new entries. For existing entries, they
|
||||
// may have been modified later, and we want to keep the current
|
||||
// values so that custom ringtones still show up in the ringtone
|
||||
@@ -773,8 +733,15 @@ public class MediaScanner
|
||||
}
|
||||
}
|
||||
|
||||
Uri tableUri = entry.mTableUri;
|
||||
Uri result = null;
|
||||
if (rowId == 0) {
|
||||
if (mMtpObjectHandle != 0) {
|
||||
values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
|
||||
}
|
||||
if (tableUri == mFilesUri) {
|
||||
values.put(Files.FileColumns.FORMAT, MediaFile.getFormatCode(entry.mPath, mMimeType));
|
||||
}
|
||||
// new file, insert it
|
||||
result = mMediaProvider.insert(tableUri, values);
|
||||
if (result != null) {
|
||||
@@ -890,7 +857,7 @@ public class MediaScanner
|
||||
|
||||
}; // end of anonymous MediaScannerClient instance
|
||||
|
||||
private void prescan(String filePath) throws RemoteException {
|
||||
private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
|
||||
Cursor c = null;
|
||||
String where = null;
|
||||
String[] selectionArgs = null;
|
||||
@@ -906,21 +873,24 @@ public class MediaScanner
|
||||
mPlayLists.clear();
|
||||
}
|
||||
|
||||
if (filePath != null) {
|
||||
// query for only one file
|
||||
where = Files.FileColumns.DATA + "=?";
|
||||
selectionArgs = new String[] { filePath };
|
||||
}
|
||||
|
||||
// Build the list of files from the content provider
|
||||
try {
|
||||
// Read existing files from the audio table
|
||||
if (filePath != null) {
|
||||
where = MediaStore.Audio.Media.DATA + "=?";
|
||||
selectionArgs = new String[] { filePath };
|
||||
}
|
||||
c = mMediaProvider.query(mAudioUri, AUDIO_PROJECTION, where, selectionArgs, null);
|
||||
if (prescanFiles) {
|
||||
// First read existing files from the files table
|
||||
|
||||
if (c != null) {
|
||||
try {
|
||||
c = mMediaProvider.query(mFilesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
|
||||
|
||||
if (c != null) {
|
||||
while (c.moveToNext()) {
|
||||
long rowId = c.getLong(ID_AUDIO_COLUMN_INDEX);
|
||||
String path = c.getString(PATH_AUDIO_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(DATE_MODIFIED_AUDIO_COLUMN_INDEX);
|
||||
long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
|
||||
String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
|
||||
|
||||
// Only consider entries with absolute path names.
|
||||
// This allows storing URIs in the database without the
|
||||
@@ -930,114 +900,141 @@ public class MediaScanner
|
||||
if (mCaseInsensitivePaths) {
|
||||
key = path.toLowerCase();
|
||||
}
|
||||
|
||||
FileCacheEntry entry = new FileCacheEntry(mFilesUri, rowId, path,
|
||||
lastModified);
|
||||
mFileCache.put(key, entry);
|
||||
}
|
||||
}
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Read existing files from the audio table and update FileCacheEntry
|
||||
c = mMediaProvider.query(mAudioUri, PRESCAN_PROJECTION, where, selectionArgs, null);
|
||||
if (c != null) {
|
||||
while (c.moveToNext()) {
|
||||
long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
|
||||
String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
|
||||
|
||||
// Only consider entries with absolute path names.
|
||||
// This allows storing URIs in the database without the
|
||||
// media scanner removing them.
|
||||
if (path.startsWith("/")) {
|
||||
String key = path;
|
||||
if (mCaseInsensitivePaths) {
|
||||
key = path.toLowerCase();
|
||||
}
|
||||
FileCacheEntry entry = mFileCache.get(path);
|
||||
if (entry == null) {
|
||||
mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
|
||||
lastModified));
|
||||
} else {
|
||||
// update the entry
|
||||
entry.mTableUri = mAudioUri;
|
||||
entry.mRowId = rowId;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
|
||||
// Read existing files from the video table
|
||||
if (filePath != null) {
|
||||
where = MediaStore.Video.Media.DATA + "=?";
|
||||
} else {
|
||||
where = null;
|
||||
}
|
||||
c = mMediaProvider.query(mVideoUri, VIDEO_PROJECTION, where, selectionArgs, null);
|
||||
|
||||
// Read existing files from the video table and update FileCacheEntry
|
||||
c = mMediaProvider.query(mVideoUri, PRESCAN_PROJECTION, where, selectionArgs, null);
|
||||
if (c != null) {
|
||||
try {
|
||||
while (c.moveToNext()) {
|
||||
long rowId = c.getLong(ID_VIDEO_COLUMN_INDEX);
|
||||
String path = c.getString(PATH_VIDEO_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(DATE_MODIFIED_VIDEO_COLUMN_INDEX);
|
||||
while (c.moveToNext()) {
|
||||
long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
|
||||
String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
|
||||
|
||||
// Only consider entries with absolute path names.
|
||||
// This allows storing URIs in the database without the
|
||||
// media scanner removing them.
|
||||
if (path.startsWith("/")) {
|
||||
String key = path;
|
||||
if (mCaseInsensitivePaths) {
|
||||
key = path.toLowerCase();
|
||||
}
|
||||
// Only consider entries with absolute path names.
|
||||
// This allows storing URIs in the database without the
|
||||
// media scanner removing them.
|
||||
if (path.startsWith("/")) {
|
||||
String key = path;
|
||||
if (mCaseInsensitivePaths) {
|
||||
key = path.toLowerCase();
|
||||
}
|
||||
FileCacheEntry entry = mFileCache.get(path);
|
||||
if (entry == null) {
|
||||
mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
|
||||
lastModified));
|
||||
} else {
|
||||
// update the entry
|
||||
entry.mTableUri = mVideoUri;
|
||||
entry.mRowId = rowId;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
|
||||
// Read existing files from the images table
|
||||
if (filePath != null) {
|
||||
where = MediaStore.Images.Media.DATA + "=?";
|
||||
} else {
|
||||
where = null;
|
||||
}
|
||||
mOriginalCount = 0;
|
||||
c = mMediaProvider.query(mImagesUri, IMAGES_PROJECTION, where, selectionArgs, null);
|
||||
|
||||
// Read existing files from the video table and update FileCacheEntry
|
||||
c = mMediaProvider.query(mImagesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
|
||||
if (c != null) {
|
||||
try {
|
||||
mOriginalCount = c.getCount();
|
||||
while (c.moveToNext()) {
|
||||
long rowId = c.getLong(ID_IMAGES_COLUMN_INDEX);
|
||||
String path = c.getString(PATH_IMAGES_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(DATE_MODIFIED_IMAGES_COLUMN_INDEX);
|
||||
while (c.moveToNext()) {
|
||||
long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
|
||||
String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
|
||||
|
||||
// Only consider entries with absolute path names.
|
||||
// This allows storing URIs in the database without the
|
||||
// media scanner removing them.
|
||||
if (path.startsWith("/")) {
|
||||
String key = path;
|
||||
if (mCaseInsensitivePaths) {
|
||||
key = path.toLowerCase();
|
||||
}
|
||||
mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
|
||||
lastModified));
|
||||
}
|
||||
// Only consider entries with absolute path names.
|
||||
// This allows storing URIs in the database without the
|
||||
// media scanner removing them.
|
||||
if (path.startsWith("/")) {
|
||||
String key = path;
|
||||
if (mCaseInsensitivePaths) {
|
||||
key = path.toLowerCase();
|
||||
}
|
||||
FileCacheEntry entry = mFileCache.get(path);
|
||||
if (entry == null) {
|
||||
mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
|
||||
lastModified));
|
||||
} else {
|
||||
// update the entry
|
||||
entry.mTableUri = mImagesUri;
|
||||
entry.mRowId = rowId;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
|
||||
if (mProcessPlaylists) {
|
||||
// Read existing files from the playlists table
|
||||
if (filePath != null) {
|
||||
where = MediaStore.Audio.Playlists.DATA + "=?";
|
||||
} else {
|
||||
where = null;
|
||||
}
|
||||
c = mMediaProvider.query(mPlaylistsUri, PLAYLISTS_PROJECTION, where, selectionArgs, null);
|
||||
|
||||
// Read existing files from the playlists table and update FileCacheEntry
|
||||
c = mMediaProvider.query(mPlaylistsUri, PRESCAN_PROJECTION, where,
|
||||
selectionArgs, null);
|
||||
if (c != null) {
|
||||
try {
|
||||
while (c.moveToNext()) {
|
||||
String path = c.getString(PATH_PLAYLISTS_COLUMN_INDEX);
|
||||
while (c.moveToNext()) {
|
||||
long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
|
||||
String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
|
||||
|
||||
if (path != null && path.length() > 0) {
|
||||
long rowId = c.getLong(ID_PLAYLISTS_COLUMN_INDEX);
|
||||
long lastModified = c.getLong(DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX);
|
||||
|
||||
String key = path;
|
||||
if (mCaseInsensitivePaths) {
|
||||
key = path.toLowerCase();
|
||||
}
|
||||
// Only consider entries with absolute path names.
|
||||
// This allows storing URIs in the database without the
|
||||
// media scanner removing them.
|
||||
if (path.startsWith("/")) {
|
||||
String key = path;
|
||||
if (mCaseInsensitivePaths) {
|
||||
key = path.toLowerCase();
|
||||
}
|
||||
FileCacheEntry entry = mFileCache.get(path);
|
||||
if (entry == null) {
|
||||
mFileCache.put(key, new FileCacheEntry(mPlaylistsUri, rowId, path,
|
||||
lastModified));
|
||||
} else {
|
||||
// update the entry
|
||||
entry.mTableUri = mPlaylistsUri;
|
||||
entry.mRowId = rowId;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
c.close();
|
||||
c = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +1136,7 @@ public class MediaScanner
|
||||
values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, 0);
|
||||
mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId), values, null, null);
|
||||
} else {
|
||||
mMediaProvider.delete(ContentUris.withAppendedId(entry.mTableUri, entry.mRowId), null, null);
|
||||
mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId), null, null);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
@@ -1167,6 +1164,7 @@ public class MediaScanner
|
||||
mVideoUri = Video.Media.getContentUri(volumeName);
|
||||
mImagesUri = Images.Media.getContentUri(volumeName);
|
||||
mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
|
||||
mFilesUri = Files.getContentUri(volumeName);
|
||||
|
||||
if (!volumeName.equals("internal")) {
|
||||
// we only support playlists on external media
|
||||
@@ -1189,11 +1187,11 @@ public class MediaScanner
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
initialize(volumeName);
|
||||
prescan(null);
|
||||
prescan(null, true);
|
||||
long prescan = System.currentTimeMillis();
|
||||
|
||||
for (int i = 0; i < directories.length; i++) {
|
||||
processDirectory(directories[i], MediaFile.sFileExtensions, mClient);
|
||||
processDirectory(directories[i], mClient);
|
||||
}
|
||||
long scan = System.currentTimeMillis();
|
||||
postscan(directories);
|
||||
@@ -1220,7 +1218,7 @@ public class MediaScanner
|
||||
public Uri scanSingleFile(String path, String volumeName, String mimeType) {
|
||||
try {
|
||||
initialize(volumeName);
|
||||
prescan(path);
|
||||
prescan(path, true);
|
||||
|
||||
File file = new File(path);
|
||||
|
||||
@@ -1235,12 +1233,34 @@ public class MediaScanner
|
||||
}
|
||||
}
|
||||
|
||||
public Uri scanMtpFile(String path, String volumeName, int objectHandle, int format) {
|
||||
String mimeType = MediaFile.getMimeTypeForFormatCode(format);
|
||||
public void scanMtpFile(String path, String volumeName, int objectHandle, int format) {
|
||||
MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
|
||||
int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
|
||||
|
||||
if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
|
||||
!MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType)) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
mMtpObjectHandle = objectHandle;
|
||||
Uri result = scanSingleFile(path, volumeName, mimeType);
|
||||
mMtpObjectHandle = 0;
|
||||
return result;
|
||||
try {
|
||||
initialize(volumeName);
|
||||
// MTP will create a file entry for us so we don't want to do it in prescan
|
||||
prescan(path, false);
|
||||
|
||||
File file = new File(path);
|
||||
|
||||
// lastModified is in milliseconds on Files.
|
||||
long lastModifiedSeconds = file.lastModified() / 1000;
|
||||
|
||||
// always scan the file, so we can return the content://media Uri for existing files
|
||||
mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), true);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
|
||||
} finally {
|
||||
mMtpObjectHandle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the number of matching file/directory names, starting from the right
|
||||
@@ -1522,7 +1542,7 @@ public class MediaScanner
|
||||
}
|
||||
}
|
||||
|
||||
private native void processDirectory(String path, String extensions, MediaScannerClient client);
|
||||
private native void processDirectory(String path, MediaScannerClient client);
|
||||
private native void processFile(String path, String mimeType, MediaScannerClient client);
|
||||
public native void setLocale(String locale);
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ public class MtpDatabase {
|
||||
Log.e(TAG, "RemoteException in endSendObject", e);
|
||||
}
|
||||
} else {
|
||||
Uri uri = mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
|
||||
mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
|
||||
}
|
||||
} else {
|
||||
deleteFile(handle);
|
||||
|
||||
@@ -146,7 +146,7 @@ static bool ExceptionCheck(void* env)
|
||||
}
|
||||
|
||||
static void
|
||||
android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client)
|
||||
android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jobject client)
|
||||
{
|
||||
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
|
||||
|
||||
@@ -154,27 +154,16 @@ android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring p
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
||||
return;
|
||||
}
|
||||
if (extensions == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const char *pathStr = env->GetStringUTFChars(path, NULL);
|
||||
if (pathStr == NULL) { // Out of memory
|
||||
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
||||
return;
|
||||
}
|
||||
const char *extensionsStr = env->GetStringUTFChars(extensions, NULL);
|
||||
if (extensionsStr == NULL) { // Out of memory
|
||||
env->ReleaseStringUTFChars(path, pathStr);
|
||||
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
MyMediaScannerClient myClient(env, client);
|
||||
mp->processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env);
|
||||
mp->processDirectory(pathStr, myClient, ExceptionCheck, env);
|
||||
env->ReleaseStringUTFChars(path, pathStr);
|
||||
env->ReleaseStringUTFChars(extensions, extensionsStr);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -309,9 +298,9 @@ android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
{"processDirectory", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
|
||||
{"processDirectory", "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
|
||||
(void *)android_media_MediaScanner_processDirectory},
|
||||
{"processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
|
||||
{"processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
|
||||
(void *)android_media_MediaScanner_processFile},
|
||||
{"setLocale", "(Ljava/lang/String;)V", (void *)android_media_MediaScanner_setLocale},
|
||||
{"extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt},
|
||||
|
||||
@@ -48,8 +48,7 @@ const char *MediaScanner::locale() const {
|
||||
}
|
||||
|
||||
status_t MediaScanner::processDirectory(
|
||||
const char *path, const char *extensions,
|
||||
MediaScannerClient &client,
|
||||
const char *path, MediaScannerClient &client,
|
||||
ExceptionCheck exceptionCheck, void *exceptionEnv) {
|
||||
int pathLength = strlen(path);
|
||||
if (pathLength >= PATH_MAX) {
|
||||
@@ -72,35 +71,16 @@ status_t MediaScanner::processDirectory(
|
||||
|
||||
status_t result =
|
||||
doProcessDirectory(
|
||||
pathBuffer, pathRemaining, extensions, client,
|
||||
exceptionCheck, exceptionEnv);
|
||||
pathBuffer, pathRemaining, client, exceptionCheck, exceptionEnv);
|
||||
|
||||
free(pathBuffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool fileMatchesExtension(const char* path, const char* extensions) {
|
||||
const char* extension = strrchr(path, '.');
|
||||
if (!extension) return false;
|
||||
++extension; // skip the dot
|
||||
if (extension[0] == 0) return false;
|
||||
|
||||
while (extensions[0]) {
|
||||
const char* comma = strchr(extensions, ',');
|
||||
size_t length = (comma ? comma - extensions : strlen(extensions));
|
||||
if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true;
|
||||
extensions += length;
|
||||
if (extensions[0] == ',') ++extensions;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
status_t MediaScanner::doProcessDirectory(
|
||||
char *path, int pathRemaining, const char *extensions,
|
||||
MediaScannerClient &client, ExceptionCheck exceptionCheck,
|
||||
void *exceptionEnv) {
|
||||
char *path, int pathRemaining, MediaScannerClient &client,
|
||||
ExceptionCheck exceptionCheck, void *exceptionEnv) {
|
||||
// place to copy file or directory name
|
||||
char* fileSpot = path + strlen(path);
|
||||
struct dirent* entry;
|
||||
@@ -163,14 +143,14 @@ status_t MediaScanner::doProcessDirectory(
|
||||
if (name[0] == '.') continue;
|
||||
|
||||
strcat(fileSpot, "/");
|
||||
int err = doProcessDirectory(path, pathRemaining - nameLength - 1, extensions, client, exceptionCheck, exceptionEnv);
|
||||
int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client, exceptionCheck, exceptionEnv);
|
||||
if (err) {
|
||||
// pass exceptions up - ignore other errors
|
||||
if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
|
||||
LOGE("Error processing '%s' - skipping\n", path);
|
||||
continue;
|
||||
}
|
||||
} else if (fileMatchesExtension(path, extensions)) {
|
||||
} else {
|
||||
struct stat statbuf;
|
||||
stat(path, &statbuf);
|
||||
if (statbuf.st_size > 0) {
|
||||
|
||||
Reference in New Issue
Block a user