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

Commit 83cab258 authored by Marco Nelissen's avatar Marco Nelissen Committed by Android (Google) Code Review
Browse files

Merge "Further speed up playlist processing" into jb-dev

parents 1fa1de52 0b718392
Loading
Loading
Loading
Loading
+85 −96
Original line number Diff line number Diff line
@@ -31,17 +31,16 @@ import android.graphics.BitmapFactory;
import android.mtp.MtpConstants;
import android.net.Uri;
import android.os.Environment;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.MediaStore;
import android.provider.MediaStore.Files.FileColumns;
import android.provider.Settings;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Audio.Playlists;
import android.provider.MediaStore.Files;
import android.provider.MediaStore.Files.FileColumns;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Audio.Playlists;
import android.provider.Settings;
import android.sax.Element;
import android.sax.ElementListener;
import android.sax.RootElement;
@@ -56,10 +55,8 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;

import libcore.io.ErrnoException;
@@ -372,6 +369,14 @@ public class MediaScanner
        }
    }

    private static class PlaylistEntry {
        String path;
        long bestmatchid;
        int bestmatchlevel;
    }

    private ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<PlaylistEntry>();

    private MediaInserter mMediaInserter;

    private ArrayList<FileEntry> mPlayLists;
@@ -1492,93 +1497,83 @@ public class MediaScanner
        return result;
    }

    private boolean addPlayListEntry(String entry, String playListDirectory,
            Uri uri, ContentValues values, int index, Cursor fileList) {
    private boolean matchEntries(long rowId, String data) {

        int len = mPlaylistEntries.size();
        boolean done = true;
        for (int i = 0; i < len; i++) {
            PlaylistEntry entry = mPlaylistEntries.get(i);
            if (entry.bestmatchlevel == Integer.MAX_VALUE) {
                continue; // this entry has been matched already
            }
            done = false;
            if (data.equalsIgnoreCase(entry.path)) {
                entry.bestmatchid = rowId;
                entry.bestmatchlevel = Integer.MAX_VALUE;
                continue; // no need for path matching
            }

            int matchLength = matchPaths(data, entry.path);
            if (matchLength > entry.bestmatchlevel) {
                entry.bestmatchid = rowId;
                entry.bestmatchlevel = matchLength;
            }
        }
        return done;
    }

    private void cachePlaylistEntry(String line, String playListDirectory) {
        PlaylistEntry entry = new PlaylistEntry();
        // watch for trailing whitespace
        int entryLength = entry.length();
        while (entryLength > 0 && Character.isWhitespace(entry.charAt(entryLength - 1))) entryLength--;
        int entryLength = line.length();
        while (entryLength > 0 && Character.isWhitespace(line.charAt(entryLength - 1))) entryLength--;
        // path should be longer than 3 characters.
        // avoid index out of bounds errors below by returning here.
        if (entryLength < 3) return false;
        if (entryLength < entry.length()) entry = entry.substring(0, entryLength);
        if (entryLength < 3) return;
        if (entryLength < line.length()) line = line.substring(0, entryLength);

        // does entry appear to be an absolute path?
        // look for Unix or DOS absolute paths
        char ch1 = entry.charAt(0);
        char ch1 = line.charAt(0);
        boolean fullPath = (ch1 == '/' ||
                (Character.isLetter(ch1) && entry.charAt(1) == ':' && entry.charAt(2) == '\\'));
                (Character.isLetter(ch1) && line.charAt(1) == ':' && line.charAt(2) == '\\'));
        // if we have a relative path, combine entry with playListDirectory
        if (!fullPath)
            entry = playListDirectory + entry;

            line = playListDirectory + line;
        entry.path = line;
        //FIXME - should we look for "../" within the path?

        // best matching MediaFile for the play list entry
        FileEntry bestMatch = null;

        // number of rightmost file/directory names for bestMatch
        int bestMatchLength = 0;

        if (fileList != null) {
            int count = fileList.getCount();
            // Backing up a little in the cursor helps when the files in the
            // playlist are not in the same order as they are in the database
            // but are still close.
            fileList.move(-1000);
            while(--count >= 0) {
                if (!fileList.moveToNext()) {
                    fileList.moveToFirst();
                }
                long rowId = fileList.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
                String path = fileList.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
                int format = fileList.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
                long lastModified = fileList.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);

                if (path.equalsIgnoreCase(entry)) {
                    bestMatch = new FileEntry(rowId, path, lastModified, format);
                    break;    // don't bother continuing search
        mPlaylistEntries.add(entry);
    }

                int matchLength = matchPaths(path, entry);
                if (matchLength > bestMatchLength) {
                    bestMatch = new FileEntry(rowId, path, lastModified, format);
                    bestMatchLength = matchLength;
                }
    private void processCachedPlaylist(Cursor fileList, ContentValues values, Uri playlistUri) {
        fileList.moveToPosition(-1);
        while (fileList.moveToNext()) {
            long rowId = fileList.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
            String data = fileList.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
            if (matchEntries(rowId, data)) {
                break;
            }
        }

        if (bestMatch == null) {
            return false;
        }

        int len = mPlaylistEntries.size();
        int index = 0;
        for (int i = 0; i < len; i++) {
            PlaylistEntry entry = mPlaylistEntries.get(i);
            if (entry.bestmatchlevel > 0) {
                try {
            // check rowid is set. Rowid may be missing if it is inserted by bulkInsert().
            if (bestMatch.mRowId == 0) {
                Cursor c = mMediaProvider.query(mAudioUri, ID_PROJECTION,
                        MediaStore.Files.FileColumns.DATA + "=?",
                        new String[] { bestMatch.mPath }, null, null);
                if (c != null) {
                    if (c.moveToNext()) {
                        bestMatch.mRowId = c.getLong(0);
                    }
                    c.close();
                }
                if (bestMatch.mRowId == 0) {
                    return false;
                }
            }
            // OK, now we are ready to add this to the database
                    values.clear();
                    values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
            values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(bestMatch.mRowId));
            mMediaProvider.insert(uri, values);
                    values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
                    mMediaProvider.insert(playlistUri, values);
                    index++;
                } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in MediaScanner.addPlayListEntry()", e);
            return false;
                    Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
                    return;
                }

        return true;
            }
        }
        mPlaylistEntries.clear();
    }

    private void processM3uPlayList(String path, String playListDirectory, Uri uri,
@@ -1590,16 +1585,16 @@ public class MediaScanner
                reader = new BufferedReader(
                        new InputStreamReader(new FileInputStream(f)), 8192);
                String line = reader.readLine();
                int index = 0;
                mPlaylistEntries.clear();
                while (line != null) {
                    // ignore comment lines, which begin with '#'
                    if (line.length() > 0 && line.charAt(0) != '#') {
                        values.clear();
                        if (addPlayListEntry(line, playListDirectory, uri, values, index, fileList))
                            index++;
                        cachePlaylistEntry(line, playListDirectory);
                    }
                    line = reader.readLine();
                }

                processCachedPlaylist(fileList, values, uri);
            }
        } catch (IOException e) {
            Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
@@ -1622,20 +1617,19 @@ public class MediaScanner
                reader = new BufferedReader(
                        new InputStreamReader(new FileInputStream(f)), 8192);
                String line = reader.readLine();
                int index = 0;
                mPlaylistEntries.clear();
                while (line != null) {
                    // ignore comment lines, which begin with '#'
                    if (line.startsWith("File")) {
                        int equals = line.indexOf('=');
                        if (equals > 0) {
                            values.clear();
                            if (addPlayListEntry(line.substring(equals + 1), playListDirectory,
                                    uri, values, index, fileList))
                                index++;
                            cachePlaylistEntry(line, playListDirectory);
                        }
                    }
                    line = reader.readLine();
                }

                processCachedPlaylist(fileList, values, uri);
            }
        } catch (IOException e) {
            Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
@@ -1653,15 +1647,9 @@ public class MediaScanner

        final ContentHandler handler;
        String playListDirectory;
        Uri uri;
        Cursor fileList;
        ContentValues values = new ContentValues();
        int index = 0;

        public WplHandler(String playListDirectory, Uri uri, Cursor fileList) {
            this.playListDirectory = playListDirectory;
            this.uri = uri;
            this.fileList = fileList;

            RootElement root = new RootElement("smil");
            Element body = root.getChild("body");
@@ -1676,13 +1664,11 @@ public class MediaScanner
        public void start(Attributes attributes) {
            String path = attributes.getValue("", "src");
            if (path != null) {
                values.clear();
                if (addPlayListEntry(path, playListDirectory, uri, values, index, fileList)) {
                    index++;
                }
                cachePlaylistEntry(path, playListDirectory);
            }
        }

       @Override
       public void end() {
       }

@@ -1692,15 +1678,18 @@ public class MediaScanner
    }

    private void processWplPlayList(String path, String playListDirectory, Uri uri,
            Cursor fileList) {
            ContentValues values, Cursor fileList) {
        FileInputStream fis = null;
        try {
            File f = new File(path);
            if (f.exists()) {
                fis = new FileInputStream(f);

                mPlaylistEntries.clear();
                Xml.parse(fis, Xml.findEncodingByName("UTF-8"),
                        new WplHandler(playListDirectory, uri, fileList).getContentHandler());

                processCachedPlaylist(fileList, values, uri);
            }
        } catch (SAXException e) {
            e.printStackTrace();
@@ -1762,7 +1751,7 @@ public class MediaScanner
        } else if (fileType == MediaFile.FILE_TYPE_PLS) {
            processPlsPlayList(path, playListDirectory, membersUri, values, fileList);
        } else if (fileType == MediaFile.FILE_TYPE_WPL) {
            processWplPlayList(path, playListDirectory, membersUri, fileList);
            processWplPlayList(path, playListDirectory, membersUri, values, fileList);
        }
    }

@@ -1800,7 +1789,7 @@ public class MediaScanner
    private native final void native_finalize();

    /**
     * Releases resouces associated with this MediaScanner object.
     * Releases resources associated with this MediaScanner object.
     * It is considered good practice to call this method when
     * one is done using the MediaScanner object. After this method
     * is called, the MediaScanner object can no longer be used.