Add onContentDirty notification to MediaSet.

Change-Id: Id89f73fb468463565cade75a6b3628318dbaf597
diff --git a/new3d/src/com/android/gallery3d/data/ComboAlbumSet.java b/new3d/src/com/android/gallery3d/data/ComboAlbumSet.java
index 5918a69..f4a4c0c 100644
--- a/new3d/src/com/android/gallery3d/data/ComboAlbumSet.java
+++ b/new3d/src/com/android/gallery3d/data/ComboAlbumSet.java
@@ -32,6 +32,7 @@
         }
     }
 
+    @Override
     public long getUniqueId() {
         return mUniqueId;
     }
@@ -55,6 +56,7 @@
         return count;
     }
 
+    @Override
     public String getName() {
         return TAG;
     }
@@ -67,6 +69,7 @@
         return count;
     }
 
+    @Override
     public void reload() {
         for (MediaSet set : mSets) {
             set.reload();
@@ -78,4 +81,10 @@
             mListener.onContentChanged();
         }
     }
+
+    public void onContentDirty() {
+        if (mListener != null) {
+            mListener.onContentDirty();
+        }
+    }
 }
diff --git a/new3d/src/com/android/gallery3d/data/DatabaseMediaSet.java b/new3d/src/com/android/gallery3d/data/DatabaseMediaSet.java
index 707ecb3..6d05df0 100644
--- a/new3d/src/com/android/gallery3d/data/DatabaseMediaSet.java
+++ b/new3d/src/com/android/gallery3d/data/DatabaseMediaSet.java
@@ -42,6 +42,7 @@
     protected final Handler mDbHandler;
     protected final GalleryContext mContext;
     protected final ContentResolver mResolver;
+    protected boolean mIsDirty = true;
 
     // How many times do we need to reload: 1 means we are reloading,
     // 2 means after current reloading, we need to do another one.
@@ -77,7 +78,10 @@
         };
     }
 
+    @Override
     public synchronized void reload() {
+        if (!mIsDirty) return;
+        mIsDirty = false;
         // If we already have reload pending, just return.
         if (mReloadCount >= 2) return;
         // If this is the first reload, start it.
@@ -86,6 +90,11 @@
         }
     }
 
+    protected void notifyContentDirty() {
+        mIsDirty = true;
+        if (mListener != null) mListener.onContentDirty();
+    }
+
     abstract protected void onLoadFromDatabase();
     abstract protected void onUpdateContent();
 }
diff --git a/new3d/src/com/android/gallery3d/data/LocalAlbum.java b/new3d/src/com/android/gallery3d/data/LocalAlbum.java
index 4e56750..ed76a92 100644
--- a/new3d/src/com/android/gallery3d/data/LocalAlbum.java
+++ b/new3d/src/com/android/gallery3d/data/LocalAlbum.java
@@ -17,8 +17,10 @@
 package com.android.gallery3d.data;
 
 import android.content.ContentResolver;
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Handler;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
 import android.provider.MediaStore.Images.ImageColumns;
@@ -50,6 +52,7 @@
     private final String mBucketName;
     private boolean mIsImage;
     private long mUniqueId;
+    private boolean mIsDirty = true;
 
     public LocalAlbum(GalleryContext context, int bucketId, String name, boolean isImage) {
         mContext = context;
@@ -75,12 +78,16 @@
             mUniqueId = DataManager.makeId(
                     DataManager.ID_LOCAL_VIDEO_ALBUM, bucketId);
         }
+
+        mResolver.registerContentObserver(mBaseUri, true, new MyContentObserver());
     }
 
+    @Override
     public long getUniqueId() {
         return mUniqueId;
     }
 
+    @Override
     public ArrayList<MediaItem> getMediaItem(int start, int count) {
         ImageService imageService = mContext.getImageService();
         DataManager dataManager = mContext.getDataManager();
@@ -107,6 +114,7 @@
         return list;
     }
 
+    @Override
     public int getMediaItemCount() {
         Cursor cursor = mResolver.query(
                 mBaseUri, COUNT_PROJECTION, mWhereClause,
@@ -119,10 +127,12 @@
         }
     }
 
+    @Override
     public String getName() {
         return mBucketName;
     }
 
+    @Override
     public int getTotalMediaItemCount() {
         return getMediaItemCount();
     }
@@ -155,7 +165,23 @@
         }
     }
 
+    @Override
     public void reload() {
-        // do nothing
+        if (mIsDirty) {
+            mIsDirty = false;
+            if (mListener != null) mListener.onContentChanged();
+        }
+    }
+
+    private class MyContentObserver extends ContentObserver {
+        public MyContentObserver() {
+            super(new Handler(mContext.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mIsDirty = true;
+            if (mListener != null) mListener.onContentDirty();
+        }
     }
 }
diff --git a/new3d/src/com/android/gallery3d/data/LocalAlbumSet.java b/new3d/src/com/android/gallery3d/data/LocalAlbumSet.java
index e18cd4c..9485fc9 100644
--- a/new3d/src/com/android/gallery3d/data/LocalAlbumSet.java
+++ b/new3d/src/com/android/gallery3d/data/LocalAlbumSet.java
@@ -16,8 +16,10 @@
 
 package com.android.gallery3d.data;
 
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Handler;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
 import android.provider.MediaStore.Images.ImageColumns;
@@ -66,8 +68,11 @@
             mBaseUri = Video.Media.EXTERNAL_CONTENT_URI;
             mUniqueId = DataManager.makeId(DataManager.ID_LOCAL_VIDEO_ALBUM_SET, 0);
         }
+        context.getContentResolver().registerContentObserver(
+                mBaseUri, true, new MyContentObserver());
     }
 
+    @Override
     public long getUniqueId() {
         return mUniqueId;
     }
@@ -80,6 +85,7 @@
         return mAlbums.size();
     }
 
+    @Override
     public String getName() {
         return TAG;
     }
@@ -125,4 +131,15 @@
 
         Collections.sort(mAlbums, LocalAlbum.sBucketNameComparator);
     }
+
+    private class MyContentObserver extends ContentObserver {
+        public MyContentObserver() {
+            super(new Handler(mContext.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            notifyContentDirty();
+        }
+    }
 }
diff --git a/new3d/src/com/android/gallery3d/data/MediaSet.java b/new3d/src/com/android/gallery3d/data/MediaSet.java
index b51cf27..282e00c 100644
--- a/new3d/src/com/android/gallery3d/data/MediaSet.java
+++ b/new3d/src/com/android/gallery3d/data/MediaSet.java
@@ -30,6 +30,7 @@
 public abstract class MediaSet {
 
     public interface MediaSetListener {
+        public void onContentDirty();
         public void onContentChanged();
     }
 
diff --git a/new3d/src/com/android/gallery3d/data/MergeAlbum.java b/new3d/src/com/android/gallery3d/data/MergeAlbum.java
index c4e4b3b..b4cf364 100644
--- a/new3d/src/com/android/gallery3d/data/MergeAlbum.java
+++ b/new3d/src/com/android/gallery3d/data/MergeAlbum.java
@@ -18,10 +18,10 @@
 
 import android.util.Log;
 
-import java.util.Comparator;
-import java.util.TreeMap;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.SortedMap;
+import java.util.TreeMap;
 
 // MergeAlbum merges items from two or more MediaSets. It uses a Comparator to
 // determine the order of items. The items are assumed to be sorted in the input
@@ -99,24 +99,28 @@
         mIndex.put(0, new int[mSize]);
     }
 
+    @Override
     public long getUniqueId() {
         return mUniqueId;
     }
 
+    @Override
     public String getName() {
         return TAG;
     }
 
+    @Override
     public int getMediaItemCount() {
         return getTotalMediaItemCount();
     }
 
+    @Override
     public ArrayList<MediaItem> getMediaItem(int start, int count) {
 
         // First find the nearest mark position <= start.
         SortedMap<Integer, int[]> head = mIndex.headMap(start + 1);
         int markPos = head.lastKey();
-        int[] subPos = (int []) head.get(markPos).clone();
+        int[] subPos = head.get(markPos).clone();
         MediaItem[] slot = new MediaItem[mSize];
 
         // fill all slots
@@ -148,13 +152,14 @@
 
             // Periodically leave a mark in the index, so we can come back later.
             if ((i + 1) % mPageSize == 0) {
-                mIndex.put(i + 1, (int[]) subPos.clone());
+                mIndex.put(i + 1, subPos.clone());
             }
         }
 
         return result;
     }
 
+    @Override
     public int getTotalMediaItemCount() {
         int count = 0;
         for (MediaSet set : mSets) {
@@ -163,6 +168,7 @@
         return count;
     }
 
+    @Override
     public void reload() {
         for (MediaSet set : mSets) {
             set.reload();
@@ -176,6 +182,10 @@
             mListener.onContentChanged();
         }
     }
+
+    public void onContentDirty() {
+        if (mListener != null) mListener.onContentDirty();
+    }
 }
 
 class FetchCache {
diff --git a/new3d/src/com/android/gallery3d/data/MergeAlbumSet.java b/new3d/src/com/android/gallery3d/data/MergeAlbumSet.java
index 052df14..d04e29b 100644
--- a/new3d/src/com/android/gallery3d/data/MergeAlbumSet.java
+++ b/new3d/src/com/android/gallery3d/data/MergeAlbumSet.java
@@ -16,8 +16,6 @@
 
 package com.android.gallery3d.data;
 
-import android.util.Log;
-
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.Map;
@@ -46,6 +44,7 @@
         }
     }
 
+    @Override
     public long getUniqueId() {
         return mUniqueId;
     }
@@ -78,18 +77,22 @@
         }
     }
 
+    @Override
     public MediaSet getSubMediaSet(int index) {
         return mAlbums.get(index);
     }
 
+    @Override
     public int getSubMediaSetCount() {
         return mAlbums.size();
     }
 
+    @Override
     public String getName() {
         return TAG;
     }
 
+    @Override
     public int getTotalMediaItemCount() {
         int count = 0;
         for (MediaSet set : mSets) {
@@ -98,6 +101,7 @@
         return count;
     }
 
+    @Override
     public void reload() {
         for (MediaSet set : mSets) {
             set.reload();
@@ -110,4 +114,8 @@
             mListener.onContentChanged();
         }
     }
+
+    public void onContentDirty() {
+        if (mListener != null) mListener.onContentDirty();
+    }
 }
diff --git a/new3d/src/com/android/gallery3d/data/PicasaAlbum.java b/new3d/src/com/android/gallery3d/data/PicasaAlbum.java
index f82c923..625e474 100644
--- a/new3d/src/com/android/gallery3d/data/PicasaAlbum.java
+++ b/new3d/src/com/android/gallery3d/data/PicasaAlbum.java
@@ -17,8 +17,10 @@
 package com.android.gallery3d.data;
 
 import android.content.ContentResolver;
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Handler;
 
 import com.android.gallery3d.app.GalleryContext;
 import com.android.gallery3d.picasa.AlbumEntry;
@@ -41,6 +43,7 @@
     private final ContentResolver mResolver;
     private long mUniqueId;
     private GalleryContext mContext;
+    private boolean mIsDirty = true;
 
     public PicasaAlbum(GalleryContext context, AlbumEntry entry) {
         mContext = context;
@@ -48,12 +51,16 @@
         mData = entry;
         mUniqueId = DataManager.makeId(
                 DataManager.ID_PICASA_ALBUM, (int) entry.id);
+        mResolver.registerContentObserver(
+                PicasaContentProvider.PHOTOS_URI, true, new MyContentObserver());
     }
 
+    @Override
     public long getUniqueId() {
         return mUniqueId;
     }
 
+    @Override
     public ArrayList<MediaItem> getMediaItem(int start, int count) {
         Uri uri = PicasaContentProvider.PHOTOS_URI.buildUpon()
                 .appendQueryParameter("limit", start + "," + count).build();
@@ -83,6 +90,7 @@
         return list;
     }
 
+    @Override
     public int getMediaItemCount() {
         Cursor cursor = mResolver.query(
                 PicasaContentProvider.PHOTOS_URI,
@@ -96,15 +104,33 @@
         }
     }
 
+    @Override
     public String getName() {
         return TAG;
     }
 
+    @Override
     public int getTotalMediaItemCount() {
         return getMediaItemCount();
     }
 
+    @Override
     public void reload() {
-        // do nothing
+        if (mIsDirty) {
+            mIsDirty = false;
+            if (mListener != null) mListener.onContentChanged();
+        }
+    }
+
+    private class MyContentObserver extends ContentObserver {
+        public MyContentObserver() {
+            super(new Handler(mContext.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mIsDirty = true;
+            if (mListener != null) mListener.onContentDirty();
+        }
     }
 }
diff --git a/new3d/src/com/android/gallery3d/data/PicasaAlbumSet.java b/new3d/src/com/android/gallery3d/data/PicasaAlbumSet.java
index d7c839e..ec6b4b9 100644
--- a/new3d/src/com/android/gallery3d/data/PicasaAlbumSet.java
+++ b/new3d/src/com/android/gallery3d/data/PicasaAlbumSet.java
@@ -16,7 +16,9 @@
 
 package com.android.gallery3d.data;
 
+import android.database.ContentObserver;
 import android.database.Cursor;
+import android.os.Handler;
 
 import com.android.gallery3d.app.GalleryContext;
 import com.android.gallery3d.picasa.AlbumEntry;
@@ -35,8 +37,11 @@
 
     public PicasaAlbumSet(GalleryContext context) {
         super(context);
+        context.getContentResolver().registerContentObserver(
+                PicasaContentProvider.ALBUMS_URI, true, new MyContentObserver());
     }
 
+    @Override
     public MediaSet getSubMediaSet(int index) {
         return mAlbums.get(index);
     }
@@ -45,10 +50,12 @@
         return mAlbums.size();
     }
 
+    @Override
     public String getName() {
         return TAG;
     }
 
+    @Override
     public long getUniqueId() {
         return DataManager.makeId(DataManager.ID_PICASA_ALBUM_SET, 0);
     }
@@ -83,4 +90,15 @@
         mAlbums.addAll(mLoadBuffer);
         mLoadBuffer.clear();
     }
+
+    private class MyContentObserver extends ContentObserver {
+        public MyContentObserver() {
+            super(new Handler(mContext.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            notifyContentDirty();
+        }
+    }
 }
diff --git a/new3d/src/com/android/gallery3d/ui/GridSlotAdapter.java b/new3d/src/com/android/gallery3d/ui/GridSlotAdapter.java
index 9b771cf..7347e92 100644
--- a/new3d/src/com/android/gallery3d/ui/GridSlotAdapter.java
+++ b/new3d/src/com/android/gallery3d/ui/GridSlotAdapter.java
@@ -192,6 +192,10 @@
         public void onContentChanged() {
             GridSlotAdapter.this.onContentChanged();
         }
+
+        public void onContentDirty() {
+            //TODO: reload the album
+        }
     }
 
     public void updateVisibleRange(int start, int end) {
diff --git a/new3d/src/com/android/gallery3d/ui/MediaSetSlotAdapter.java b/new3d/src/com/android/gallery3d/ui/MediaSetSlotAdapter.java
index 23ed738..de01dce 100644
--- a/new3d/src/com/android/gallery3d/ui/MediaSetSlotAdapter.java
+++ b/new3d/src/com/android/gallery3d/ui/MediaSetSlotAdapter.java
@@ -298,6 +298,10 @@
                 mSlotView.invalidate();
             }
         }
+
+        public void onContentDirty() {
+            // TODO Reload the album set
+        }
     }
 
     private class MyContentListener implements MediaSet.MediaSetListener {
@@ -305,6 +309,10 @@
         public void onContentChanged() {
             MediaSetSlotAdapter.this.onContentChanged();
         }
+
+        public void onContentDirty() {
+            // TODO Reload the album set
+        }
     }
 
     public void updateVisibleRange(int start, int end) {
diff --git a/new3d/tests/src/com/android/gallery3d/data/GalleryContextMock.java b/new3d/tests/src/com/android/gallery3d/data/GalleryContextMock.java
index dc4774e..534aa7c 100644
--- a/new3d/tests/src/com/android/gallery3d/data/GalleryContextMock.java
+++ b/new3d/tests/src/com/android/gallery3d/data/GalleryContextMock.java
@@ -1,24 +1,34 @@
 package com.android.gallery3d.data;
 
-import com.android.gallery3d.ui.GLRoot;
-import com.android.gallery3d.ui.GLRootStub;
-
 import android.content.ContentResolver;
 import android.content.Context;
+import android.os.Looper;
+
+import com.android.gallery3d.ui.GLRoot;
+import com.android.gallery3d.ui.GLRootStub;
 
 class GalleryContextMock extends GalleryContextStub {
     GLRoot mGLRoot = new GLRootStub();
     DataManager mDataManager = new DataManager(this);
     ContentResolver mResolver;
     Context mContext;
+    Looper mMainLooper;
 
-    GalleryContextMock(Context context, ContentResolver resolver) {
+    GalleryContextMock(Context context,
+            ContentResolver resolver, Looper mainLooper) {
         mContext = context;
         mResolver = resolver;
+        mMainLooper = mainLooper;
     }
 
+    @Override
     public GLRoot getGLRoot() { return mGLRoot; }
+    @Override
     public DataManager getDataManager() { return mDataManager; }
+    @Override
     public Context getAndroidContext() { return mContext; }
+    @Override
     public ContentResolver getContentResolver() { return mResolver; }
+    @Override
+    public Looper getMainLooper() { return mMainLooper; }
 }
diff --git a/new3d/tests/src/com/android/gallery3d/data/LocalDataTest.java b/new3d/tests/src/com/android/gallery3d/data/LocalDataTest.java
index 4d551df..f3a0425 100644
--- a/new3d/tests/src/com/android/gallery3d/data/LocalDataTest.java
+++ b/new3d/tests/src/com/android/gallery3d/data/LocalDataTest.java
@@ -17,9 +17,6 @@
 package com.android.gallery3d.data;
 
 import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.IContentProvider;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
@@ -28,13 +25,9 @@
 import android.test.AndroidTestCase;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
-import java.util.ArrayList;
-
 public class LocalDataTest extends AndroidTestCase {
     private static final String TAG = "LocalDataTest";
 
@@ -64,13 +57,14 @@
             mIsImage = isImage;
         }
 
+        @Override
         public void run() {
             Looper.prepare();
             SQLiteDatabase db = SQLiteDatabase.create(null);
 
             prepareData(db);
 
-            GalleryContextStub context = newGalleryContext(db);
+            GalleryContextStub context = newGalleryContext(db, Looper.myLooper());
             LocalAlbumSet albumSet = new LocalAlbumSet(context, mIsImage);
             MyListener listener = new MyListener();
             albumSet.setContentListener(listener);
@@ -98,10 +92,12 @@
     }
 
     class TestZeroImage extends TestLocalImageAlbum {
+        @Override
         public void prepareData(SQLiteDatabase db) {
             createImageTable(db);
         }
 
+        @Override
         public void verifyResult(LocalAlbumSet albumSet) {
             assertEquals(0, albumSet.getMediaItemCount());
             assertEquals(0, albumSet.getSubMediaSetCount());
@@ -112,11 +108,13 @@
     }
 
     class TestOneImage extends TestLocalImageAlbum {
+        @Override
         public void prepareData(SQLiteDatabase db) {
             createImageTable(db);
             insertImageData(db);
         }
 
+        @Override
         public void verifyResult(LocalAlbumSet albumSet) {
             assertEquals(0, albumSet.getMediaItemCount());
             assertEquals(1, albumSet.getSubMediaSetCount());
@@ -142,6 +140,7 @@
     }
 
     class TestMoreImages extends TestLocalImageAlbum {
+        @Override
         public void prepareData(SQLiteDatabase db) {
             // Albums are sorted by names, and items are sorted by
             // dateTimeTaken (descending)
@@ -153,6 +152,7 @@
             insertImageData(db, 3000, 0xB001, "first");   // id 3
         }
 
+        @Override
         public void verifyResult(LocalAlbumSet albumSet) {
             assertEquals(0, albumSet.getMediaItemCount());
             assertEquals(2, albumSet.getSubMediaSetCount());
@@ -220,10 +220,12 @@
     }
 
     class TestZeroVideo extends TestLocalVideoAlbum {
+        @Override
         public void prepareData(SQLiteDatabase db) {
             createVideoTable(db);
         }
 
+        @Override
         public void verifyResult(LocalAlbumSet albumSet) {
             assertEquals(0, albumSet.getMediaItemCount());
             assertEquals(0, albumSet.getSubMediaSetCount());
@@ -234,11 +236,13 @@
     }
 
     class TestOneVideo extends TestLocalVideoAlbum {
+        @Override
         public void prepareData(SQLiteDatabase db) {
             createVideoTable(db);
             insertVideoData(db);
         }
 
+        @Override
         public void verifyResult(LocalAlbumSet albumSet) {
             assertEquals(0, albumSet.getMediaItemCount());
             assertEquals(1, albumSet.getSubMediaSetCount());
@@ -265,6 +269,7 @@
     }
 
     class TestMoreVideos extends TestLocalVideoAlbum {
+        @Override
         public void prepareData(SQLiteDatabase db) {
             // Albums are sorted by names, and items are sorted by
             // dateTimeTaken (descending)
@@ -276,6 +281,7 @@
             insertVideoData(db, 3000, 0xB001, "first");   // id 3
         }
 
+        @Override
         public void verifyResult(LocalAlbumSet albumSet) {
             assertEquals(0, albumSet.getMediaItemCount());
             assertEquals(2, albumSet.getSubMediaSetCount());
@@ -347,19 +353,23 @@
                 + "'/mnt/sdcard/DCIM/Camera/VID_20100811_051413.3gp', 2964)");
     }
 
-    static GalleryContextStub newGalleryContext(SQLiteDatabase db) {
+    static GalleryContextStub newGalleryContext(SQLiteDatabase db, Looper mainLooper) {
         ContentProvider cp = new DbContentProvider(db);
         MockContentResolver cr = new MockContentResolver();
         cr.addProvider("media", cp);
-        return new GalleryContextMock(null, cr);
+        return new GalleryContextMock(null, cr, mainLooper);
     }
 
     static class MyListener implements MediaSet.MediaSetListener {
         int count;
+
         public void onContentChanged() {
             count++;
             Looper.myLooper().quit();
         }
+
+        public void onContentDirty() {
+        }
     }
 }
 
diff --git a/new3d/tests/src/com/android/gallery3d/data/RealDataTest.java b/new3d/tests/src/com/android/gallery3d/data/RealDataTest.java
index 93ffef5..fff82a0 100644
--- a/new3d/tests/src/com/android/gallery3d/data/RealDataTest.java
+++ b/new3d/tests/src/com/android/gallery3d/data/RealDataTest.java
@@ -16,18 +16,13 @@
 
 package com.android.gallery3d.data;
 
-import com.android.gallery3d.app.GalleryContext;
-import com.android.gallery3d.ui.GLRoot;
-import com.android.gallery3d.ui.GLRootStub;
-
-import android.content.ContentResolver;
 import android.os.Looper;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
+import com.android.gallery3d.app.GalleryContext;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 
@@ -54,11 +49,13 @@
     }
 
     class TestLocalImageThread extends Thread {
+        @Override
         public void run() {
             Looper.prepare();
             GalleryContext context = new GalleryContextMock(
                     mContext,
-                    mContext.getContentResolver());
+                    mContext.getContentResolver(),
+                    Looper.myLooper());
 
             LocalAlbumSet albumSet = new LocalAlbumSet(context, true);
             MyListener listener = new MyListener();
@@ -73,11 +70,14 @@
     }
 
     class TestLocalVideoThread extends Thread {
+        @Override
         public void run() {
             Looper.prepare();
+
             GalleryContext context = new GalleryContextMock(
                     mContext,
-                    mContext.getContentResolver());
+                    mContext.getContentResolver(),
+                    Looper.myLooper());
 
             LocalAlbumSet albumSet = new LocalAlbumSet(context, false);
             MyListener listener = new MyListener();
@@ -92,11 +92,13 @@
     }
 
     class TestPicasaThread extends Thread {
+        @Override
         public void run() {
             Looper.prepare();
             GalleryContext context = new GalleryContextMock(
                     mContext,
-                    mContext.getContentResolver());
+                    mContext.getContentResolver(),
+                    Looper.myLooper());
 
             PicasaAlbumSet albumSet = new PicasaAlbumSet(context);
             MyListener listener = new MyListener();
@@ -144,9 +146,13 @@
 
     static class MyListener implements MediaSet.MediaSetListener {
         int count;
+
         public void onContentChanged() {
             count++;
             Looper.myLooper().quit();
         }
+
+        public void onContentDirty() {
+        }
     }
 }