diff options
-rw-r--r-- | include/media/mediascanner.h | 5 | ||||
-rw-r--r-- | media/java/android/media/MediaScanner.java | 192 | ||||
-rw-r--r-- | media/java/android/media/MediaScannerClient.java | 5 | ||||
-rw-r--r-- | media/jni/android_media_MediaScanner.cpp | 29 | ||||
-rw-r--r-- | media/libmedia/MediaScanner.cpp | 24 |
5 files changed, 124 insertions, 131 deletions
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h index df5be32..765c039 100644 --- a/include/media/mediascanner.h +++ b/include/media/mediascanner.h @@ -55,7 +55,7 @@ private: status_t doProcessDirectory( char *path, int pathRemaining, MediaScannerClient &client, - ExceptionCheck exceptionCheck, void *exceptionEnv); + bool noMedia, ExceptionCheck exceptionCheck, void *exceptionEnv); MediaScanner(const MediaScanner &); MediaScanner &operator=(const MediaScanner &); @@ -72,10 +72,9 @@ public: void endFile(); virtual bool scanFile(const char* path, long long lastModified, - long long fileSize, bool isDirectory) = 0; + long long fileSize, bool isDirectory, bool noMedia) = 0; virtual bool handleStringTag(const char* name, const char* value) = 0; virtual bool setMimeType(const char* mimeType) = 0; - virtual bool addNoMediaFolder(const char* path) = 0; protected: void convertValues(uint32_t encoding); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index a7ac9ed..80cc94e 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -422,9 +422,10 @@ public class MediaScanner private long mFileSize; private String mWriter; private int mCompilation; + private boolean mNoMedia; // flag to suppress file from appearing in media tables public FileCacheEntry beginFile(String path, String mimeType, long lastModified, - long fileSize, boolean isDirectory) { + long fileSize, boolean isDirectory, boolean noMedia) { mMimeType = mimeType; mFileType = 0; mFileSize = fileSize; @@ -435,28 +436,31 @@ public class MediaScanner // to avoid memory allocation int lastSlash = path.lastIndexOf('/'); if (lastSlash >= 0 && lastSlash + 2 < path.length()) { - // ignore those ._* files created by MacOS - if (path.regionMatches(lastSlash + 1, "._", 0, 2)) { - return null; - } - - // ignore album art files created by Windows Media Player: - // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg - // and AlbumArt_{...}_Small.jpg - if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) { - if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) || - path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) { - return null; + if (!noMedia) { + // ignore those ._* files created by MacOS + if (path.regionMatches(lastSlash + 1, "._", 0, 2)) { + noMedia = true; } - int length = path.length() - lastSlash - 1; - if ((length == 17 && path.regionMatches( - true, lastSlash + 1, "AlbumArtSmall", 0, 13)) || - (length == 10 - && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) { - return null; + + // ignore album art files created by Windows Media Player: + // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg + // and AlbumArt_{...}_Small.jpg + if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) { + if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) || + path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) { + noMedia = true; + } + int length = path.length() - lastSlash - 1; + if ((length == 17 && path.regionMatches( + true, lastSlash + 1, "AlbumArtSmall", 0, 13)) || + (length == 10 + && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) { + noMedia = true; + } } } } + mNoMedia = noMedia; // try mimeType first, if it is specified if (mimeType != null) { @@ -523,36 +527,41 @@ public class MediaScanner return entry; } - public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory) { + public void scanFile(String path, long lastModified, long fileSize, + boolean isDirectory, boolean noMedia) { // This is the callback funtion from native codes. // Log.v(TAG, "scanFile: "+path); - doScanFile(path, null, lastModified, fileSize, isDirectory, false); + doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia); } public Uri doScanFile(String path, String mimeType, long lastModified, - long fileSize, boolean isDirectory, boolean scanAlways) { + long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) { Uri result = null; // long t1 = System.currentTimeMillis(); try { FileCacheEntry entry = beginFile(path, mimeType, lastModified, - fileSize, isDirectory); + fileSize, isDirectory, noMedia); // rescan for metadata if file was modified since last scan if (entry != null && (entry.mLastModifiedChanged || scanAlways)) { - String lowpath = path.toLowerCase(); - boolean ringtones = (lowpath.indexOf(RINGTONES_DIR) > 0); - boolean notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > 0); - boolean alarms = (lowpath.indexOf(ALARMS_DIR) > 0); - boolean podcasts = (lowpath.indexOf(PODCAST_DIR) > 0); - boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) || - (!ringtones && !notifications && !alarms && !podcasts); - - // we only extract metadata for audio and video files - if (MediaFile.isAudioFileType(mFileType) - || MediaFile.isVideoFileType(mFileType)) { - processFile(path, mimeType, this); - } + if (noMedia) { + result = endFile(entry, false, false, false, false, false); + } else { + String lowpath = path.toLowerCase(); + boolean ringtones = (lowpath.indexOf(RINGTONES_DIR) > 0); + boolean notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > 0); + boolean alarms = (lowpath.indexOf(ALARMS_DIR) > 0); + boolean podcasts = (lowpath.indexOf(PODCAST_DIR) > 0); + boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) || + (!ringtones && !notifications && !alarms && !podcasts); + + // we only extract metadata for audio and video files + if (MediaFile.isAudioFileType(mFileType) + || MediaFile.isVideoFileType(mFileType)) { + processFile(path, mimeType, this); + } - result = endFile(entry, ringtones, notifications, alarms, music, podcasts); + result = endFile(entry, ringtones, notifications, alarms, music, podcasts); + } } } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); @@ -689,27 +698,31 @@ public class MediaScanner map.put(MediaStore.MediaColumns.SIZE, mFileSize); map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType); - if (MediaFile.isVideoFileType(mFileType)) { - map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING)); - map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaStore.UNKNOWN_STRING)); - map.put(Video.Media.DURATION, mDuration); - // FIXME - add RESOLUTION - } else if (MediaFile.isImageFileType(mFileType)) { - // FIXME - add DESCRIPTION - } else if (MediaFile.isAudioFileType(mFileType)) { - map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ? - mArtist : MediaStore.UNKNOWN_STRING); - map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null && - mAlbumArtist.length() > 0) ? mAlbumArtist : null); - map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ? - mAlbum : MediaStore.UNKNOWN_STRING); - map.put(Audio.Media.COMPOSER, mComposer); - if (mYear != 0) { - map.put(Audio.Media.YEAR, mYear); + if (!mNoMedia) { + if (MediaFile.isVideoFileType(mFileType)) { + map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 + ? mArtist : MediaStore.UNKNOWN_STRING)); + map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 + ? mAlbum : MediaStore.UNKNOWN_STRING)); + map.put(Video.Media.DURATION, mDuration); + // FIXME - add RESOLUTION + } else if (MediaFile.isImageFileType(mFileType)) { + // FIXME - add DESCRIPTION + } else if (MediaFile.isAudioFileType(mFileType)) { + map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ? + mArtist : MediaStore.UNKNOWN_STRING); + map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null && + mAlbumArtist.length() > 0) ? mAlbumArtist : null); + map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ? + mAlbum : MediaStore.UNKNOWN_STRING); + map.put(Audio.Media.COMPOSER, mComposer); + if (mYear != 0) { + map.put(Audio.Media.YEAR, mYear); + } + map.put(Audio.Media.TRACK, mTrack); + map.put(Audio.Media.DURATION, mDuration); + map.put(Audio.Media.COMPILATION, mCompilation); } - map.put(Audio.Media.TRACK, mTrack); - map.put(Audio.Media.DURATION, mDuration); - map.put(Audio.Media.COMPILATION, mCompilation); } return map; } @@ -719,7 +732,7 @@ public class MediaScanner throws RemoteException { // update database - // use album artist if artist is missing + // use album artist if artist is missing if (mArtist == null || mArtist.length() == 0) { mArtist = mAlbumArtist; } @@ -761,7 +774,7 @@ public class MediaScanner values.put(Audio.Media.IS_ALARM, alarms); values.put(Audio.Media.IS_MUSIC, music); values.put(Audio.Media.IS_PODCAST, podcasts); - } else if (mFileType == MediaFile.FILE_TYPE_JPEG) { + } else if (mFileType == MediaFile.FILE_TYPE_JPEG && !mNoMedia) { ExifInterface exif = null; try { exif = new ExifInterface(entry.mPath); @@ -814,12 +827,14 @@ public class MediaScanner } Uri tableUri = mFilesUri; - if (MediaFile.isVideoFileType(mFileType)) { - tableUri = mVideoUri; - } else if (MediaFile.isImageFileType(mFileType)) { - tableUri = mImagesUri; - } else if (MediaFile.isAudioFileType(mFileType)) { - tableUri = mAudioUri; + if (!mNoMedia) { + if (MediaFile.isVideoFileType(mFileType)) { + tableUri = mVideoUri; + } else if (MediaFile.isImageFileType(mFileType)) { + tableUri = mImagesUri; + } else if (MediaFile.isAudioFileType(mFileType)) { + tableUri = mAudioUri; + } } Uri result = null; if (rowId == 0) { @@ -930,25 +945,6 @@ public class MediaScanner } } - public void addNoMediaFolder(String path) { - ContentValues values = new ContentValues(); - values.put(MediaStore.Images.ImageColumns.DATA, ""); - String [] pathSpec = new String[] {path + '%'}; - try { - // These tables have DELETE_FILE triggers that delete the file from the - // sd card when deleting the database entry. We don't want to do this in - // this case, since it would cause those files to be removed if a .nomedia - // file was added after the fact, when in that case we only want the database - // entries to be removed. - mMediaProvider.update(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values, - MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec); - mMediaProvider.update(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values, - MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec); - } catch (RemoteException e) { - throw new RuntimeException(); - } - } - private int getFileTypeFromDrm(String path) { if (!isDrmEnabled()) { return 0; @@ -1228,13 +1224,37 @@ public class MediaScanner // always scan the file, so we can return the content://media Uri for existing files return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), - false, true); + false, true, false); } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); return null; } } + public static boolean isNoMediaPath(String path) { + if (path == null) return false; + + // return true if file or any parent directory has name starting with a dot + if (path.indexOf("/.") >= 0) return true; + + // now check to see if any parent directories have a ".nomedia" file + // start from 1 so we don't bother checking in the root directory + int offset = 1; + while (offset >= 0) { + int slashIndex = path.indexOf('/', offset); + if (slashIndex > offset) { + slashIndex++; // move past slash + File file = new File(path.substring(0, slashIndex) + ".nomedia"); + if (file.exists()) { + // we have a .nomedia in one of the parent directories + return true; + } + } + offset = slashIndex; + } + return false; + } + public void scanMtpFile(String path, String volumeName, int objectHandle, int format) { initialize(volumeName); MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); @@ -1279,7 +1299,7 @@ public class MediaScanner // always scan the file, so we can return the content://media Uri for existing files mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), - (format == MtpConstants.FORMAT_ASSOCIATION), true); + (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path)); } } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e); diff --git a/media/java/android/media/MediaScannerClient.java b/media/java/android/media/MediaScannerClient.java index ac326ef..b326671 100644 --- a/media/java/android/media/MediaScannerClient.java +++ b/media/java/android/media/MediaScannerClient.java @@ -21,9 +21,8 @@ package android.media; */ public interface MediaScannerClient { - public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory); - - public void addNoMediaFolder(String path); + public void scanFile(String path, long lastModified, long fileSize, + boolean isDirectory, boolean noMedia); /** * Called by native code to return metadata extracted from media files. diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index a3dd136..9151799 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -67,7 +67,7 @@ public: mScanFileMethodID = env->GetMethodID( mediaScannerClientInterface, "scanFile", - "(Ljava/lang/String;JJZ)V"); + "(Ljava/lang/String;JJZZ)V"); mHandleStringTagMethodID = env->GetMethodID( mediaScannerClientInterface, @@ -78,11 +78,6 @@ public: mediaScannerClientInterface, "setMimeType", "(Ljava/lang/String;)V"); - - mAddNoMediaFolderMethodID = env->GetMethodID( - mediaScannerClientInterface, - "addNoMediaFolder", - "(Ljava/lang/String;)V"); } } @@ -95,7 +90,7 @@ public: // Returns true if it succeeded, false if an exception occured // in the Java code virtual bool scanFile(const char* path, long long lastModified, - long long fileSize, bool isDirectory) + long long fileSize, bool isDirectory, bool noMedia) { LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)", path, lastModified, fileSize, isDirectory); @@ -106,7 +101,7 @@ public: } mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, - fileSize, isDirectory); + fileSize, isDirectory, noMedia); mEnv->DeleteLocalRef(pathStr); return (!mEnv->ExceptionCheck()); @@ -149,30 +144,12 @@ public: return (!mEnv->ExceptionCheck()); } - // Returns true if it succeeded, false if an exception occured - // in the Java code - virtual bool addNoMediaFolder(const char* path) - { - LOGV("addNoMediaFolder: path(%s)", path); - jstring pathStr; - if ((pathStr = mEnv->NewStringUTF(path)) == NULL) { - return false; - } - - mEnv->CallVoidMethod(mClient, mAddNoMediaFolderMethodID, pathStr); - - mEnv->DeleteLocalRef(pathStr); - return (!mEnv->ExceptionCheck()); - } - - private: JNIEnv *mEnv; jobject mClient; jmethodID mScanFileMethodID; jmethodID mHandleStringTagMethodID; jmethodID mSetMimeTypeMethodID; - jmethodID mAddNoMediaFolderMethodID; }; diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp index 5ec573e..4e22175 100644 --- a/media/libmedia/MediaScanner.cpp +++ b/media/libmedia/MediaScanner.cpp @@ -70,8 +70,7 @@ status_t MediaScanner::processDirectory( client.setLocale(locale()); status_t result = - doProcessDirectory( - pathBuffer, pathRemaining, client, exceptionCheck, exceptionEnv); + doProcessDirectory(pathBuffer, pathRemaining, client, false, exceptionCheck, exceptionEnv); free(pathBuffer); @@ -80,20 +79,18 @@ status_t MediaScanner::processDirectory( status_t MediaScanner::doProcessDirectory( char *path, int pathRemaining, MediaScannerClient &client, - ExceptionCheck exceptionCheck, void *exceptionEnv) { + bool noMedia, ExceptionCheck exceptionCheck, void *exceptionEnv) { // place to copy file or directory name char* fileSpot = path + strlen(path); struct dirent* entry; struct stat statbuf; - // ignore directories that contain a ".nomedia" file + // Treat all files as non-media in directories that contain a ".nomedia" file if (pathRemaining >= 8 /* strlen(".nomedia") */ ) { strcpy(fileSpot, ".nomedia"); if (access(path, F_OK) == 0) { - LOGD("found .nomedia, skipping directory\n"); - fileSpot[0] = 0; - client.addNoMediaFolder(path); - return OK; + LOGD("found .nomedia, setting noMedia flag\n"); + noMedia = true; } // restore path @@ -138,19 +135,20 @@ status_t MediaScanner::doProcessDirectory( } if (type == DT_REG || type == DT_DIR) { if (type == DT_DIR) { - // ignore directories with a name that starts with '.' + // set noMedia flag on directories with a name that starts with '.' // for example, the Mac ".Trashes" directory - if (name[0] == '.') continue; + if (name[0] == '.') + noMedia = true; // report the directory to the client if (stat(path, &statbuf) == 0) { - client.scanFile(path, statbuf.st_mtime, 0, true); + client.scanFile(path, statbuf.st_mtime, 0, true, noMedia); } // and now process its contents strcat(fileSpot, "/"); int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client, - exceptionCheck, exceptionEnv); + noMedia, exceptionCheck, exceptionEnv); if (err) { // pass exceptions up - ignore other errors if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure; @@ -159,7 +157,7 @@ status_t MediaScanner::doProcessDirectory( } } else { stat(path, &statbuf); - client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false); + client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false, noMedia); if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure; } } |