Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42f16f3d authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge "MediaScanner: reimplement the ".nomedia" feature for hiding files from the media provider"

parents e8fd99df 997354e4
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -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);
+106 −86
Original line number Diff line number Diff line
@@ -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,9 +436,10 @@ public class MediaScanner
                // to avoid memory allocation
                int lastSlash = path.lastIndexOf('/');
                if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
                    if (!noMedia) {
                        // ignore those ._* files created by MacOS
                        if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
                        return null;
                            noMedia = true;
                        }

                        // ignore album art files created by Windows Media Player:
@@ -446,17 +448,19 @@ public class MediaScanner
                        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;
                                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;
                                noMedia = true;
                            }
                        }
                    }
                }
                mNoMedia = noMedia;

                // try mimeType first, if it is specified
                if (mimeType != null) {
@@ -523,21 +527,25 @@ 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)) {
                    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);
@@ -554,6 +562,7 @@ public class MediaScanner

                        result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
                    }
                }
            } catch (RemoteException e) {
                Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
            }
@@ -689,9 +698,12 @@ public class MediaScanner
            map.put(MediaStore.MediaColumns.SIZE, mFileSize);
            map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);

            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.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)) {
@@ -711,6 +723,7 @@ public class MediaScanner
                    map.put(Audio.Media.DURATION, mDuration);
                    map.put(Audio.Media.COMPILATION, mCompilation);
                }
            }
            return map;
        }

@@ -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,6 +827,7 @@ public class MediaScanner
            }

            Uri tableUri = mFilesUri;
            if (!mNoMedia) {
                if (MediaFile.isVideoFileType(mFileType)) {
                    tableUri = mVideoUri;
                } else if (MediaFile.isImageFileType(mFileType)) {
@@ -821,6 +835,7 @@ public class MediaScanner
                } else if (MediaFile.isAudioFileType(mFileType)) {
                    tableUri = mAudioUri;
                }
            }
            Uri result = null;
            if (rowId == 0) {
                if (mMtpObjectHandle != 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);
+2 −3
Original line number Diff line number Diff line
@@ -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.
+3 −26
Original line number Diff line number Diff line
@@ -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;
};


+11 −13
Original line number Diff line number Diff line
@@ -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;
            }
        }