OSDN Git Service

Music app unbundling prep: add SortCursor to the music app, make
authorMarco Nelissen <marcone@google.com>
Thu, 24 Dec 2009 00:18:45 +0000 (16:18 -0800)
committerMarco Nelissen <marcone@google.com>
Fri, 8 Jan 2010 17:24:14 +0000 (09:24 -0800)
it get the card id from the media provider, and clean up imports.
(http://b/2363099)

src/com/android/music/MediaPickerActivity.java
src/com/android/music/MediaPlaybackActivity.java
src/com/android/music/MediaPlaybackService.java
src/com/android/music/MusicUtils.java
src/com/android/music/SortCursor.java [new file with mode: 0644]
tests/src/com/android/music/stress/MusicPlaybackStress.java

index 3261701..c06ea29 100644 (file)
@@ -16,8 +16,6 @@
 
 package com.android.music;
 
-import com.android.internal.database.SortCursor;
-
 import android.app.ListActivity;
 import android.content.Context;
 import android.content.Intent;
index 6a2689f..baf1442 100644 (file)
@@ -30,17 +30,16 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.MediaStore;
 import android.text.Layout;
@@ -54,7 +53,6 @@ import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.Window;
-import android.view.WindowManager;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
index 77226d1..ed94634 100644 (file)
@@ -34,8 +34,6 @@ import android.database.sqlite.SQLiteException;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.net.Uri;
-import android.os.Environment;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -268,7 +266,7 @@ public class MediaPlaybackService extends Service {
         super.onCreate();
         
         mPreferences = getSharedPreferences("Music", MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE);
-        mCardId = FileUtils.getFatVolumeId(Environment.getExternalStorageDirectory().getPath());
+        mCardId = MusicUtils.getCardId(this);
         
         registerExternalStorageListener();
 
@@ -676,7 +674,7 @@ public class MediaPlaybackService extends Service {
                         closeExternalStorageFiles(intent.getData().getPath());
                     } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
                         mMediaMountedCount++;
-                        mCardId = FileUtils.getFatVolumeId(intent.getData().getPath());
+                        mCardId = MusicUtils.getCardId(MediaPlaybackService.this);
                         reloadQueue();
                         notifyChange(QUEUE_CHANGED);
                         notifyChange(META_CHANGED);
index 46c4eae..2262d03 100644 (file)
@@ -1232,4 +1232,15 @@ public class MusicUtils {
         v.setBackgroundDrawable(new BitmapDrawable(bg));
     }
 
+    static int getCardId(Context context) {
+        ContentResolver res = context.getContentResolver();
+        Cursor c = res.query(Uri.parse("content://media/external/fs_id"), null, null, null, null);
+        int id = -1;
+        if (c != null) {
+            c.moveToFirst();
+            id = c.getInt(0);
+            c.close();
+        }
+        return id;
+    }
 }
diff --git a/src/com/android/music/SortCursor.java b/src/com/android/music/SortCursor.java
new file mode 100644 (file)
index 0000000..9b17294
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.music;
+
+import android.database.AbstractCursor;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.util.Log;
+
+/**
+ * A variant of MergeCursor that sorts the cursors being merged. If decent
+ * performance is ever obtained, it can be put back under android.database.
+ */
+public class SortCursor extends AbstractCursor
+{
+    private static final String TAG = "SortCursor";
+    private Cursor mCursor; // updated in onMove
+    private Cursor[] mCursors;
+    private int [] mSortColumns;
+    private final int ROWCACHESIZE = 64;
+    private int mRowNumCache[] = new int[ROWCACHESIZE];
+    private int mCursorCache[] = new int[ROWCACHESIZE];
+    private int mCurRowNumCache[][];
+    private int mLastCacheHit = -1;
+
+    private DataSetObserver mObserver = new DataSetObserver() {
+
+        @Override
+        public void onChanged() {
+            // Reset our position so the optimizations in move-related code
+            // don't screw us over
+            mPos = -1;
+        }
+
+        @Override
+        public void onInvalidated() {
+            mPos = -1;
+        }
+    };
+
+    public SortCursor(Cursor[] cursors, String sortcolumn)
+    {
+        mCursors = cursors;
+
+        int length = mCursors.length;
+        mSortColumns = new int[length];
+        for (int i = 0 ; i < length ; i++) {
+            if (mCursors[i] == null) continue;
+
+            // Register ourself as a data set observer
+            mCursors[i].registerDataSetObserver(mObserver);
+
+            mCursors[i].moveToFirst();
+
+            // We don't catch the exception
+            mSortColumns[i] = mCursors[i].getColumnIndexOrThrow(sortcolumn);
+        }
+        mCursor = null;
+        String smallest = "";
+        for (int j = 0 ; j < length; j++) {
+            if (mCursors[j] == null || mCursors[j].isAfterLast())
+                continue;
+            String current = mCursors[j].getString(mSortColumns[j]);
+            if (mCursor == null || current.compareToIgnoreCase(smallest) < 0) {
+                smallest = current;
+                mCursor = mCursors[j];
+            }
+        }
+
+        for (int i = mRowNumCache.length - 1; i >= 0; i--) {
+            mRowNumCache[i] = -2;
+        }
+        mCurRowNumCache = new int[ROWCACHESIZE][length];
+    }
+
+    @Override
+    public int getCount()
+    {
+        int count = 0;
+        int length = mCursors.length;
+        for (int i = 0 ; i < length ; i++) {
+            if (mCursors[i] != null) {
+                count += mCursors[i].getCount();
+            }
+        }
+        return count;
+    }
+
+    @Override
+    public boolean onMove(int oldPosition, int newPosition)
+    {
+        if (oldPosition == newPosition)
+            return true;
+
+        /* Find the right cursor
+         * Because the client of this cursor (the listadapter/view) tends
+         * to jump around in the cursor somewhat, a simple cache strategy
+         * is used to avoid having to search all cursors from the start.
+         * TODO: investigate strategies for optimizing random access and
+         * reverse-order access.
+         */
+
+        int cache_entry = newPosition % ROWCACHESIZE;
+
+        if (mRowNumCache[cache_entry] == newPosition) {
+            int which = mCursorCache[cache_entry];
+            mCursor = mCursors[which];
+            if (mCursor == null) {
+                Log.w(TAG, "onMove: cache results in a null cursor.");
+                return false;
+            }
+            mCursor.moveToPosition(mCurRowNumCache[cache_entry][which]);
+            mLastCacheHit = cache_entry;
+            return true;
+        }
+
+        mCursor = null;
+        int length = mCursors.length;
+
+        if (mLastCacheHit >= 0) {
+            for (int i = 0; i < length; i++) {
+                if (mCursors[i] == null) continue;
+                mCursors[i].moveToPosition(mCurRowNumCache[mLastCacheHit][i]);
+            }
+        }
+
+        if (newPosition < oldPosition || oldPosition == -1) {
+            for (int i = 0 ; i < length; i++) {
+                if (mCursors[i] == null) continue;
+                mCursors[i].moveToFirst();
+            }
+            oldPosition = 0;
+        }
+        if (oldPosition < 0) {
+            oldPosition = 0;
+        }
+
+        // search forward to the new position
+        int smallestIdx = -1;
+        for(int i = oldPosition; i <= newPosition; i++) {
+            String smallest = "";
+            smallestIdx = -1;
+            for (int j = 0 ; j < length; j++) {
+                if (mCursors[j] == null || mCursors[j].isAfterLast()) {
+                    continue;
+                }
+                String current = mCursors[j].getString(mSortColumns[j]);
+                if (smallestIdx < 0 || current.compareToIgnoreCase(smallest) < 0) {
+                    smallest = current;
+                    smallestIdx = j;
+                }
+            }
+            if (i == newPosition) break;
+            if (mCursors[smallestIdx] != null) {
+                mCursors[smallestIdx].moveToNext();
+            }
+        }
+        mCursor = mCursors[smallestIdx];
+        mRowNumCache[cache_entry] = newPosition;
+        mCursorCache[cache_entry] = smallestIdx;
+        for (int i = 0; i < length; i++) {
+            if (mCursors[i] != null) {
+                mCurRowNumCache[cache_entry][i] = mCursors[i].getPosition();
+            }
+        }
+        mLastCacheHit = -1;
+        return true;
+    }
+
+    @Override
+    public String getString(int column)
+    {
+        return mCursor.getString(column);
+    }
+
+    @Override
+    public short getShort(int column)
+    {
+        return mCursor.getShort(column);
+    }
+
+    @Override
+    public int getInt(int column)
+    {
+        return mCursor.getInt(column);
+    }
+
+    @Override
+    public long getLong(int column)
+    {
+        return mCursor.getLong(column);
+    }
+
+    @Override
+    public float getFloat(int column)
+    {
+        return mCursor.getFloat(column);
+    }
+
+    @Override
+    public double getDouble(int column)
+    {
+        return mCursor.getDouble(column);
+    }
+
+    @Override
+    public boolean isNull(int column)
+    {
+        return mCursor.isNull(column);
+    }
+
+    @Override
+    public byte[] getBlob(int column)
+    {
+        return mCursor.getBlob(column);
+    }
+
+    @Override
+    public String[] getColumnNames()
+    {
+        if (mCursor != null) {
+            return mCursor.getColumnNames();
+        } else {
+            // All of the cursors may be empty, but they can still return
+            // this information.
+            int length = mCursors.length;
+            for (int i = 0 ; i < length ; i++) {
+                if (mCursors[i] != null) {
+                    return mCursors[i].getColumnNames();
+                }
+            }
+            throw new IllegalStateException("No cursor that can return names");
+        }
+    }
+
+    @Override
+    public void deactivate()
+    {
+        int length = mCursors.length;
+        for (int i = 0 ; i < length ; i++) {
+            if (mCursors[i] == null) continue;
+            mCursors[i].deactivate();
+        }
+    }
+
+    @Override
+    public void close() {
+        int length = mCursors.length;
+        for (int i = 0 ; i < length ; i++) {
+            if (mCursors[i] == null) continue;
+            mCursors[i].close();
+        }
+    }
+
+    @Override
+    public void registerDataSetObserver(DataSetObserver observer) {
+        int length = mCursors.length;
+        for (int i = 0 ; i < length ; i++) {
+            if (mCursors[i] != null) {
+                mCursors[i].registerDataSetObserver(observer);
+            }
+        }
+    }
+
+    @Override
+    public void unregisterDataSetObserver(DataSetObserver observer) {
+        int length = mCursors.length;
+        for (int i = 0 ; i < length ; i++) {
+            if (mCursors[i] != null) {
+                mCursors[i].unregisterDataSetObserver(observer);
+            }
+        }
+    }
+
+    @Override
+    public boolean requery()
+    {
+        int length = mCursors.length;
+        for (int i = 0 ; i < length ; i++) {
+            if (mCursors[i] == null) continue;
+
+            if (mCursors[i].requery() == false) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
index 80661f2..9e7fd74 100644 (file)
@@ -27,7 +27,6 @@ import android.test.suitebuilder.annotation.LargeTest;
 import android.view.KeyEvent;
 import android.util.Log;
 import android.content.Context;
-import android.os.ServiceManager;
 
 
 import com.android.music.MusicBrowserActivity;