diff options
author | Hung-ying Tyan <tyanh@google.com> | 2011-09-30 14:42:50 +0800 |
---|---|---|
committer | Hung-ying Tyan <tyanh@google.com> | 2011-10-07 18:57:06 +0800 |
commit | 57cbaa1ef40d2215c6400f5ae4af3a09e67abb2d (patch) | |
tree | 3ea6a3f73ce7ff4aa309cc79a109fd6860f08300 | |
parent | eaa750f62a064e142605c3531539699469948983 (diff) | |
download | android_packages_apps_Gallery2-57cbaa1ef40d2215c6400f5ae4af3a09e67abb2d.tar.gz android_packages_apps_Gallery2-57cbaa1ef40d2215c6400f5ae4af3a09e67abb2d.tar.bz2 android_packages_apps_Gallery2-57cbaa1ef40d2215c6400f5ae4af3a09e67abb2d.zip |
Request sync when there's no mediaItem in a mediaSet.
This is to fix the problem where media items haven't been sync'ed when the album
set or album is viewed for the first time.
+ Add MediaSet.SyncListener.
+ Make AlbumPage and AlbumSetPage implement SyncListener.
+ Implement requestSync() for ComboAlbum and ComboAlbumSet.
+ add ActivityState.isDestroyed(). This also fixes the problem where
StateManager.finishState() may be called twice.
Bug: 5337899
Change-Id: I25364c3ac25721a2650701c5d7931bfb6daa9303
-rw-r--r-- | res/values/strings.xml | 5 | ||||
-rw-r--r-- | src/com/android/gallery3d/app/ActivityState.java | 7 | ||||
-rw-r--r-- | src/com/android/gallery3d/app/AlbumDataAdapter.java | 4 | ||||
-rw-r--r-- | src/com/android/gallery3d/app/AlbumPage.java | 25 | ||||
-rw-r--r-- | src/com/android/gallery3d/app/AlbumSetPage.java | 50 | ||||
-rw-r--r-- | src/com/android/gallery3d/app/StateManager.java | 11 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/ComboAlbum.java | 6 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/ComboAlbumSet.java | 6 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/MediaSet.java | 126 |
9 files changed, 220 insertions, 20 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml index 26d80e7a2..74ba1f31b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -199,6 +199,11 @@ <!-- This toast message is shown when network connection is lost while doing clustering --> <string name="no_connectivity">Some locations couldn\'t be identified due to network problems.</string> + <!-- This toast message is shown when failed to load the album data. [CHAR LIMIT=NONE] --> + <string name="sync_album_error">Failed to download the photos in this album. Please retry later.</string> + <!-- This toast message is shown when failed to load the album list data. [CHAR LIMIT=NONE] --> + <string name="sync_album_set_error">Failed to download the list of albums. Please retry later.</string> + <!-- The title of the menu item to let user choose the which portion of the media items the user wants to see. When pressed, a submenu will appear and user can choose one of "show images only", diff --git a/src/com/android/gallery3d/app/ActivityState.java b/src/com/android/gallery3d/app/ActivityState.java index 6a0c72c52..519eaff58 100644 --- a/src/com/android/gallery3d/app/ActivityState.java +++ b/src/com/android/gallery3d/app/ActivityState.java @@ -46,6 +46,8 @@ abstract public class ActivityState { ResultEntry next; } + private boolean mDestroyed = false; + protected ActivityState() { } @@ -139,5 +141,10 @@ abstract public class ActivityState { } protected void onDestroy() { + mDestroyed = true; + } + + boolean isDestroyed() { + return mDestroyed; } } diff --git a/src/com/android/gallery3d/app/AlbumDataAdapter.java b/src/com/android/gallery3d/app/AlbumDataAdapter.java index 9934cf88c..42388ead7 100644 --- a/src/com/android/gallery3d/app/AlbumDataAdapter.java +++ b/src/com/android/gallery3d/app/AlbumDataAdapter.java @@ -147,7 +147,6 @@ public class AlbumDataAdapter implements AlbumView.Model { mContentStart = contentStart; mContentEnd = contentEnd; } - MediaItem[] data = mData; long[] itemVersion = mItemVersion; long[] setVersion = mSetVersion; if (contentStart >= end || start >= contentEnd) { @@ -168,9 +167,6 @@ public class AlbumDataAdapter implements AlbumView.Model { public void setActiveWindow(int start, int end) { if (start == mActiveStart && end == mActiveEnd) return; - mActiveStart = start; - mActiveEnd = end; - Utils.assertTrue(start <= end && end - start <= mData.length && end <= mSize); diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java index 1f2c2910c..faaa800b5 100644 --- a/src/com/android/gallery3d/app/AlbumPage.java +++ b/src/com/android/gallery3d/app/AlbumPage.java @@ -60,7 +60,7 @@ import com.android.gallery3d.util.GalleryUtils; import java.util.Random; public class AlbumPage extends ActivityState implements GalleryActionBar.ClusterRunner, - SelectionManager.SelectionListener { + SelectionManager.SelectionListener, MediaSet.SyncListener { @SuppressWarnings("unused") private static final String TAG = "AlbumPage"; @@ -102,7 +102,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster private ProgressDialog mProgressDialog; private Future<?> mPendingTask; - private Future<Void> mSyncTask = null; + private Future<Integer> mSyncTask = null; private GLView mRootPane = new GLView() { private float mMatrix[] = new float[16]; @@ -360,6 +360,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster @Override protected void onDestroy() { + super.onDestroy(); if (mAlbumDataAdapter != null) { mAlbumDataAdapter.setLoadingListener(null); } @@ -566,6 +567,24 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster mActionModeHandler.updateSupportedOperation(path, selected); } + @Override + public void onSyncDone(final MediaSet mediaSet, final int resultCode) { + Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result=" + + resultCode); + ((Activity) mActivity).runOnUiThread(new Runnable() { + @Override + public void run() { + if (!mIsActive) return; + mediaSet.notifyContentChanged(); // force reload to handle spinner + + if (resultCode == MediaSet.SYNC_RESULT_ERROR) { + Toast.makeText((Context) mActivity, R.string.sync_album_error, + Toast.LENGTH_LONG).show(); + } + } + }); + } + private class MyLoadingListener implements LoadingListener { @Override public void onLoadingStarted() { @@ -577,7 +596,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster if (!mIsActive) return; if (mAlbumDataAdapter.size() == 0) { if (mSyncTask == null) { - mSyncTask = mMediaSet.requestSync(); + mSyncTask = mMediaSet.requestSync(AlbumPage.this); } if (mSyncTask.isDone()){ Toast.makeText((Context) mActivity, diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java index 74d60c402..6951fa37e 100644 --- a/src/com/android/gallery3d/app/AlbumSetPage.java +++ b/src/com/android/gallery3d/app/AlbumSetPage.java @@ -54,10 +54,12 @@ import com.android.gallery3d.ui.PositionRepository.Position; import com.android.gallery3d.ui.SelectionManager; import com.android.gallery3d.ui.SlotView; import com.android.gallery3d.ui.StaticBackground; +import com.android.gallery3d.util.Future; +import com.android.gallery3d.util.GalleryUtils; public class AlbumSetPage extends ActivityState implements SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner, - EyePosition.EyePositionListener { + EyePosition.EyePositionListener, MediaSet.SyncListener { @SuppressWarnings("unused") private static final String TAG = "AlbumSetPage"; @@ -100,6 +102,8 @@ public class AlbumSetPage extends ActivityState implements private float mY; private float mZ; + private Future<Integer> mSyncTask = null; + private final GLView mRootPane = new GLView() { private final float mMatrix[] = new float[16]; @@ -291,6 +295,10 @@ public class AlbumSetPage extends ActivityState implements DetailsHelper.pause(); GalleryActionBar actionBar = mActivity.getGalleryActionBar(); if (actionBar != null) actionBar.hideClusterMenu(); + if (mSyncTask != null) { + mSyncTask.cancel(); + mSyncTask = null; + } } @Override @@ -552,6 +560,26 @@ public class AlbumSetPage extends ActivityState implements mDetailsHelper.show(); } + @Override + public void onSyncDone(final MediaSet mediaSet, final int resultCode) { + if (resultCode == MediaSet.SYNC_RESULT_ERROR) { + Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result=" + + resultCode); + } + ((Activity) mActivity).runOnUiThread(new Runnable() { + @Override + public void run() { + if (!mIsActive) return; + mediaSet.notifyContentChanged(); // force reload to handle spinner + + if (resultCode == MediaSet.SYNC_RESULT_ERROR) { + Toast.makeText((Context) mActivity, R.string.sync_album_set_error, + Toast.LENGTH_LONG).show(); + } + } + }); + } + private class MyLoadingListener implements LoadingListener { public void onLoadingStarted() { GalleryUtils.setSpinnerVisibility((Activity) mActivity, true); @@ -559,11 +587,21 @@ public class AlbumSetPage extends ActivityState implements public void onLoadingFinished() { if (!mIsActive) return; - GalleryUtils.setSpinnerVisibility((Activity) mActivity, false); - if (mAlbumSetDataAdapter.size() == 0) { - Toast.makeText((Context) mActivity, - R.string.empty_album, Toast.LENGTH_LONG).show(); - if (mActivity.getStateManager().getStateCount() > 1) { + + if (mSyncTask == null) { + // Request sync in case the mediaSet hasn't been sync'ed before. + mSyncTask = mMediaSet.requestSync(AlbumSetPage.this); + } + if (mSyncTask.isDone()){ + // The mediaSet is in sync. Turn off the loading indicator. + GalleryUtils.setSpinnerVisibility((Activity) mActivity, false); + + // Only show toast when there's no album and we are going to finish + // the page. Toast is redundant if we are going to stay on this page. + if ((mAlbumSetDataAdapter.size() == 0) + && (mActivity.getStateManager().getStateCount() > 1)) { + Toast.makeText((Context) mActivity, + R.string.empty_album, Toast.LENGTH_LONG).show(); mActivity.getStateManager().finishState(AlbumSetPage.this); } } diff --git a/src/com/android/gallery3d/app/StateManager.java b/src/com/android/gallery3d/app/StateManager.java index 9c55fbd15..556a06aa4 100644 --- a/src/com/android/gallery3d/app/StateManager.java +++ b/src/com/android/gallery3d/app/StateManager.java @@ -161,9 +161,14 @@ public class StateManager { void finishState(ActivityState state) { Log.v(TAG, "finishState " + state.getClass()); if (state != mStack.peek().activityState) { - throw new IllegalArgumentException("The stateview to be finished" - + " is not at the top of the stack: " + state + ", " - + mStack.peek().activityState); + if (state.isDestroyed()) { + Log.d(TAG, "The state is already destroyed"); + return; + } else { + throw new IllegalArgumentException("The stateview to be finished" + + " is not at the top of the stack: " + state + ", " + + mStack.peek().activityState); + } } // Remove the top state. diff --git a/src/com/android/gallery3d/data/ComboAlbum.java b/src/com/android/gallery3d/data/ComboAlbum.java index 8ca2077a4..69ab62e53 100644 --- a/src/com/android/gallery3d/data/ComboAlbum.java +++ b/src/com/android/gallery3d/data/ComboAlbum.java @@ -17,6 +17,7 @@ package com.android.gallery3d.data; import com.android.gallery3d.app.GalleryApp; +import com.android.gallery3d.util.Future; import java.util.ArrayList; @@ -84,4 +85,9 @@ public class ComboAlbum extends MediaSet implements ContentListener { public void onContentDirty() { notifyContentChanged(); } + + @Override + public Future<Integer> requestSync(SyncListener listener) { + return requestSyncOnEmptySets(mSets, listener); + } } diff --git a/src/com/android/gallery3d/data/ComboAlbumSet.java b/src/com/android/gallery3d/data/ComboAlbumSet.java index aa196039d..16adc12f4 100644 --- a/src/com/android/gallery3d/data/ComboAlbumSet.java +++ b/src/com/android/gallery3d/data/ComboAlbumSet.java @@ -18,6 +18,7 @@ package com.android.gallery3d.data; import com.android.gallery3d.R; import com.android.gallery3d.app.GalleryApp; +import com.android.gallery3d.util.Future; // ComboAlbumSet combines multiple media sets into one. It lists all sub // media sets from the input album sets. @@ -77,4 +78,9 @@ public class ComboAlbumSet extends MediaSet implements ContentListener { public void onContentDirty() { notifyContentChanged(); } + + @Override + public Future<Integer> requestSync(SyncListener listener) { + return requestSyncOnEmptySets(mSets, listener); + } } diff --git a/src/com/android/gallery3d/data/MediaSet.java b/src/com/android/gallery3d/data/MediaSet.java index 99f00a0dd..a54067efc 100644 --- a/src/com/android/gallery3d/data/MediaSet.java +++ b/src/com/android/gallery3d/data/MediaSet.java @@ -16,9 +16,11 @@ package com.android.gallery3d.data; +import com.android.gallery3d.common.Utils; import com.android.gallery3d.util.Future; import java.util.ArrayList; +import java.util.HashMap; import java.util.WeakHashMap; // MediaSet is a directory-like data structure. @@ -34,6 +36,22 @@ public abstract class MediaSet extends MediaObject { public static final int MEDIAITEM_BATCH_FETCH_COUNT = 500; public static final int INDEX_NOT_FOUND = -1; + public static final int SYNC_RESULT_SUCCESS = 0; + public static final int SYNC_RESULT_CANCELLED = 1; + public static final int SYNC_RESULT_ERROR = 2; + + /** Listener to be used with requestSync(SyncListener). */ + public static interface SyncListener { + /** + * Called when the sync task completed. Completion may be due to normal termination, + * an exception, or cancellation. + * + * @param mediaSet the MediaSet that's done with sync + * @param resultCode one of the SYNC_RESULT_* constants + */ + void onSyncDone(MediaSet mediaSet, int resultCode); + } + public MediaSet(Path path, long version) { super(path, version); } @@ -190,11 +208,21 @@ public abstract class MediaSet extends MediaObject { return start; } - public Future<Void> requestSync() { + /** + * Requests sync on this MediaSet. It returns a Future object that can be used by the caller + * to query the status of the sync. The sync result code is one of the SYNC_RESULT_* constants + * defined in this class and can be obtained by Future.get(). + * + * Subclasses should perform sync on a different thread. + * + * The default implementation here returns a Future stub that does nothing and returns + * SYNC_RESULT_SUCCESS by get(). + */ + public Future<Integer> requestSync(SyncListener listener) { return FUTURE_STUB; } - private static final Future<Void> FUTURE_STUB = new Future<Void>() { + private static final Future<Integer> FUTURE_STUB = new Future<Integer>() { @Override public void cancel() {} @@ -209,11 +237,101 @@ public abstract class MediaSet extends MediaObject { } @Override - public Void get() { - return null; + public Integer get() { + return SYNC_RESULT_SUCCESS; } @Override public void waitDone() {} }; + + protected Future<Integer> requestSyncOnEmptySets(MediaSet[] sets, SyncListener listener) { + MultiSetSyncFuture future = new MultiSetSyncFuture(listener); + future.requestSyncOnEmptySets(sets); + return future; + } + + private class MultiSetSyncFuture implements Future<Integer>, SyncListener { + private static final String TAG = "Gallery.MultiSetSync"; + + private final HashMap<MediaSet, Future<Integer>> mMediaSetMap = + new HashMap<MediaSet, Future<Integer>>(); + private final SyncListener mListener; + + private boolean mIsCancelled = false; + private int mResult = -1; + + MultiSetSyncFuture(SyncListener listener) { + mListener = listener; + } + + synchronized void requestSyncOnEmptySets(MediaSet[] sets) { + for (MediaSet set : sets) { + if ((set.getMediaItemCount() == 0) && !mMediaSetMap.containsKey(set)) { + // Sync results are handled in this.onSyncDone(). + Future<Integer> future = set.requestSync(this); + if (!future.isDone()) { + mMediaSetMap.put(set, future); + Log.d(TAG, " request sync: " + Utils.maskDebugInfo(set.getName())); + } + } + } + Log.d(TAG, "requestSyncOnEmptySets actual=" + mMediaSetMap.size()); + } + + @Override + public synchronized void cancel() { + if (mIsCancelled) return; + mIsCancelled = true; + for (Future<Integer> future : mMediaSetMap.values()) future.cancel(); + mMediaSetMap.clear(); + if (mResult < 0) mResult = SYNC_RESULT_CANCELLED; + } + + @Override + public synchronized boolean isCancelled() { + return mIsCancelled; + } + + @Override + public synchronized boolean isDone() { + return mMediaSetMap.isEmpty(); + } + + @Override + public synchronized Integer get() { + waitDone(); + return mResult; + } + + @Override + public synchronized void waitDone() { + try { + while (!isDone()) wait(); + } catch (InterruptedException e) { + Log.d(TAG, "waitDone() interrupted"); + } + } + + // SyncListener callback + @Override + public void onSyncDone(MediaSet mediaSet, int resultCode) { + SyncListener listener = null; + synchronized (this) { + if (mMediaSetMap.remove(mediaSet) != null) { + Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + + " #pending=" + mMediaSetMap.size()); + if (resultCode == SYNC_RESULT_ERROR) { + mResult = SYNC_RESULT_ERROR; + } + if (mMediaSetMap.isEmpty()) { + if (mResult < 0) mResult = SYNC_RESULT_SUCCESS; + notifyAll(); + listener = mListener; + } + } + } + if (listener != null) listener.onSyncDone(MediaSet.this, mResult); + } + } } |