summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHung-ying Tyan <tyanh@google.com>2011-09-30 14:42:50 +0800
committerHung-ying Tyan <tyanh@google.com>2011-10-07 18:57:06 +0800
commit57cbaa1ef40d2215c6400f5ae4af3a09e67abb2d (patch)
tree3ea6a3f73ce7ff4aa309cc79a109fd6860f08300
parenteaa750f62a064e142605c3531539699469948983 (diff)
downloadandroid_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.xml5
-rw-r--r--src/com/android/gallery3d/app/ActivityState.java7
-rw-r--r--src/com/android/gallery3d/app/AlbumDataAdapter.java4
-rw-r--r--src/com/android/gallery3d/app/AlbumPage.java25
-rw-r--r--src/com/android/gallery3d/app/AlbumSetPage.java50
-rw-r--r--src/com/android/gallery3d/app/StateManager.java11
-rw-r--r--src/com/android/gallery3d/data/ComboAlbum.java6
-rw-r--r--src/com/android/gallery3d/data/ComboAlbumSet.java6
-rw-r--r--src/com/android/gallery3d/data/MediaSet.java126
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);
+ }
+ }
}