From c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 10 Sep 2010 14:47:36 -0400 Subject: [PATCH 1/2] Media scanner support for tracking files of arbitrary type. The native media scanner no longer filters files based on file extension. Audio, video, image and playlist files are handled as before, but non-media files are now inserted into the "files" table, which was originally added to support MTP. Change-Id: I9053218fb6d2671a3bb181405c34442b94678afc Signed-off-by: Mike Lockwood --- core/java/android/provider/MediaStore.java | 12 + include/media/mediascanner.h | 8 +- media/java/android/media/MediaFile.java | 49 +-- media/java/android/media/MediaScanner.java | 362 +++++++++++---------- media/java/android/media/MtpDatabase.java | 2 +- media/jni/android_media_MediaScanner.cpp | 21 +- media/libmedia/MediaScanner.cpp | 32 +- 7 files changed, 247 insertions(+), 239 deletions(-) diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index d3718f8365acc..1417ef5503ca8 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -339,6 +339,18 @@ public final class MediaStore { */ public static final String MEDIA_ID = "media_id"; } + + /** + * The MIME type of the file + *

Type: TEXT

+ */ + public static final String MIME_TYPE = "mime_type"; + + /** + * The title of the content + *

Type: TEXT

+ */ + public static final String TITLE = "title"; } /** diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h index 0d397acc91d8a..74c9d5dd47621 100644 --- a/include/media/mediascanner.h +++ b/include/media/mediascanner.h @@ -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 &); diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index fb2480e716104..bdb3c5c504ca8 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -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; @@ -191,54 +189,65 @@ public class MediaFile { addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST); 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 iterator = sFileTypeMap.keySet().iterator(); - - while (iterator.hasNext()) { - if (builder.length() > 0) { - builder.append(','); - } - builder.append(iterator.next()); - } - sFileExtensions = builder.toString(); } - + 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); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 8ca62375238ed..9f2a49c9983d9 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -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); diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java index ad029a6b7e36c..6b0b7611d1bfe 100644 --- a/media/java/android/media/MtpDatabase.java +++ b/media/java/android/media/MtpDatabase.java @@ -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); diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index 273f1aff9930d..fd0b2332863e6 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -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}, diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp index ba98f04e76bb4..c31b622c353e0 100644 --- a/media/libmedia/MediaScanner.cpp +++ b/media/libmedia/MediaScanner.cpp @@ -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) { From fde2ca180ea63ebd314770ec55b97e0a3c5a0788 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 11 Sep 2010 12:07:24 -0400 Subject: [PATCH 2/2] MediaFile: Add support for some popular non-media file types. Change-Id: I68379740b535b08e707158f357e4e58a9c4d74ec Signed-off-by: Mike Lockwood --- media/java/android/media/MediaFile.java | 37 +++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index bdb3c5c504ca8..66a93f04518e3 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -82,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 { @@ -130,16 +141,6 @@ public class MediaFile { return false; } - private static boolean isWMVEnabled() { - List 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); @@ -174,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); @@ -189,6 +188,16 @@ public class MediaFile { addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST); 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); + + 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) {