merge in nakasi-factoryrom-release history after reset to jb-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f369df9..529480c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -99,7 +99,7 @@
<!-- Details dialog "OK" button. Dismisses dialog. -->
<string name="delete">Delete</string>
- <!-- String Delete the selected media item(s) [CHAR LIMIT=30] -->
+ <!-- String Delete the selected media item(s) [CHAR LIMIT=50] -->
<plurals name="delete_selection">
<item quantity="one">Delete selected item?</item>
<item quantity="other">Delete selected items?</item>
@@ -211,8 +211,6 @@
<!-- This toast message is shown when failed to load the album data. [CHAR LIMIT=NONE] -->
<string name="sync_album_error">Couldn\'t download the photos in this album. 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">Couldn\'t download the list of albums. 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
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index 3033b93..e773691 100644
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -176,9 +176,11 @@
if (!more) {
mResumeEffect = null;
mAlbumView.setSlotFilter(null);
- } else {
- invalidate();
}
+ // We want to render one more time even when no more effect
+ // required. So that the animated thumbnails could be draw
+ // with declarations in super.render().
+ invalidate();
}
canvas.restore();
}
@@ -238,6 +240,7 @@
MediaItem item = mAlbumDataAdapter.get(slotIndex);
if (item == null) return; // Item not ready yet, ignore the click
mSelectionManager.toggle(item.getPath());
+ mDetailsSource.findIndex(slotIndex);
mSlotView.invalidate();
} else {
// Show pressed-up animation for the single-tap.
@@ -308,6 +311,7 @@
if (item == null) return;
mSelectionManager.setAutoLeaveSelectionMode(true);
mSelectionManager.toggle(item.getPath());
+ mDetailsSource.findIndex(slotIndex);
mSlotView.invalidate();
}
@@ -648,7 +652,9 @@
mInitialSynced = true;
}
clearLoadingBit(BIT_LOADING_SYNC);
- if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive) {
+ if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive
+ && (mAlbumDataAdapter.size() == 0)) {
+ // show error toast only if the album is empty
Toast.makeText((Context) mActivity, R.string.sync_album_error,
Toast.LENGTH_LONG).show();
}
diff --git a/src/com/android/gallery3d/app/AlbumSetDataLoader.java b/src/com/android/gallery3d/app/AlbumSetDataLoader.java
index 819adcc..39d4a8b 100644
--- a/src/com/android/gallery3d/app/AlbumSetDataLoader.java
+++ b/src/com/android/gallery3d/app/AlbumSetDataLoader.java
@@ -329,7 +329,7 @@
while (mActive) {
synchronized (this) {
if (mActive && !mDirty && updateComplete) {
- updateLoading(false);
+ if (!mSource.isLoading()) updateLoading(false);
Utils.waitWithoutInterrupt(this);
continue;
}
diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java
index 0a0b354..aa72eb8 100644
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -250,6 +250,7 @@
if (set == null) return;
mSelectionManager.setAutoLeaveSelectionMode(true);
mSelectionManager.toggle(set.getPath());
+ mDetailsSource.findIndex(slotIndex);
mSlotView.invalidate();
}
@@ -299,9 +300,9 @@
// 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)) {
- Toast.makeText((Context) mActivity,
- R.string.empty_album, Toast.LENGTH_LONG).show();
if (mActivity.getStateManager().getStateCount() > 1) {
+ Toast.makeText((Context) mActivity,
+ R.string.empty_album, Toast.LENGTH_LONG).show();
mActivity.getStateManager().finishState(this);
}
}
@@ -544,7 +545,7 @@
break;
}
case SelectionManager.SELECT_ALL_MODE: {
- mActionModeHandler.setTitle(getSelectedString());
+ mActionModeHandler.updateSupportedOperation();
mRootPane.invalidate();
break;
}
@@ -596,8 +597,7 @@
}
clearLoadingBit(BIT_LOADING_SYNC);
if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive) {
- Toast.makeText((Context) mActivity, R.string.sync_album_set_error,
- Toast.LENGTH_LONG).show();
+ Log.w(TAG, "failed to load album set");
}
} finally {
root.unlockRenderThread();
diff --git a/src/com/android/gallery3d/app/MovieActivity.java b/src/com/android/gallery3d/app/MovieActivity.java
index 629894a..8dd69e4 100644
--- a/src/com/android/gallery3d/app/MovieActivity.java
+++ b/src/com/android/gallery3d/app/MovieActivity.java
@@ -51,11 +51,13 @@
public class MovieActivity extends Activity {
@SuppressWarnings("unused")
private static final String TAG = "MovieActivity";
- private static final String KEY_LOGO_BITMAP = "logo-bitmap";
+ public static final String KEY_LOGO_BITMAP = "logo-bitmap";
+ public static final String KEY_TREAT_UP_AS_BACK = "treat-up-as-back";
private MoviePlayer mPlayer;
private boolean mFinishOnCompletion;
private Uri mUri;
+ private boolean mTreatUpAsBack;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -71,6 +73,7 @@
initializeActionBar(intent);
mFinishOnCompletion = intent.getBooleanExtra(
MediaStore.EXTRA_FINISH_ON_COMPLETION, true);
+ mTreatUpAsBack = intent.getBooleanExtra(KEY_TREAT_UP_AS_BACK, false);
mPlayer = new MoviePlayer(rootView, this, intent.getData(), savedInstanceState,
!mFinishOnCompletion) {
@Override
@@ -159,7 +162,12 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
- finish();
+ if (mTreatUpAsBack) {
+ finish();
+ } else {
+ startActivity(new Intent(this, Gallery.class));
+ finish();
+ }
return true;
}
return false;
diff --git a/src/com/android/gallery3d/app/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java
index 54c7115..6a4961e 100644
--- a/src/com/android/gallery3d/app/PhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java
@@ -23,6 +23,7 @@
import com.android.gallery3d.common.BitmapUtils;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.ContentListener;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.LocalMediaItem;
@@ -415,7 +416,7 @@
// Create a default ScreenNail if the real one is not available yet.
if (entry.screenNail == null) {
- entry.screenNail = newDefaultScreenNail(item);
+ entry.screenNail = newPlaceholderScreenNail(item);
if (offset == 0) updateTileProvider(entry);
}
@@ -490,8 +491,8 @@
}
public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize) {
- return mTileProvider.getTile(level, x, y, tileSize, borderSize);
+ int borderSize, BitmapPool pool) {
+ return mTileProvider.getTile(level, x, y, tileSize, borderSize, pool);
}
public boolean isEmpty() {
@@ -632,7 +633,7 @@
// If this is a temporary item, don't try to get its bitmap because
// it won't be available. We will get its bitmap after a data reload.
if (isTemporaryItem(mItem)) {
- return newDefaultScreenNail(mItem);
+ return newPlaceholderScreenNail(mItem);
}
Bitmap bitmap = mItem.requestImage(MediaItem.TYPE_THUMBNAIL).run(jc);
@@ -687,7 +688,7 @@
// Create a default ScreenNail when a ScreenNail is needed, but we don't yet
// have one available (because the image data is still being saved, or the
// Bitmap is still being loaded.
- private ScreenNail newDefaultScreenNail(MediaItem item) {
+ private ScreenNail newPlaceholderScreenNail(MediaItem item) {
int width = item.getWidth();
int height = item.getHeight();
return new BitmapScreenNail(width, height);
@@ -937,7 +938,6 @@
if (mCurrentIndex >= mSize) {
mCurrentIndex = mSize - 1;
}
- fireDataChange();
}
}
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index eee7197..2132d06 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -75,6 +75,7 @@
private static final int MSG_ON_FULL_SCREEN_CHANGED = 4;
private static final int MSG_UPDATE_ACTION_BAR = 5;
private static final int MSG_UNFREEZE_GLROOT = 6;
+ private static final int MSG_WANT_BARS = 7;
private static final int HIDE_BARS_TIMEOUT = 3500;
private static final int UNFREEZE_GLROOT_TIMEOUT = 250;
@@ -110,8 +111,6 @@
private int mCurrentIndex = 0;
private Handler mHandler;
private boolean mShowBars = true;
- // The value of canShowBars() last time the bar updates state.
- private boolean mCanShowBars = false;
private volatile boolean mActionBarAllowed = true;
private GalleryActionBar mActionBar;
private MyMenuVisibilityListener mMenuVisibilityListener;
@@ -212,6 +211,7 @@
}
mMediaSet = mActivity.getDataManager().getMediaSet(mSetPathString);
+ mSelectionManager.setSourceMediaSet(mMediaSet);
mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0);
if (mMediaSet == null) {
Log.w(TAG, "failed to restore " + mSetPathString);
@@ -268,12 +268,10 @@
}
case MSG_LOCK_ORIENTATION: {
mOrientationManager.lockOrientation();
- updateBars();
break;
}
case MSG_UNLOCK_ORIENTATION: {
mOrientationManager.unlockOrientation();
- updateBars();
break;
}
case MSG_ON_FULL_SCREEN_CHANGED: {
@@ -284,6 +282,10 @@
updateBars();
break;
}
+ case MSG_WANT_BARS: {
+ wantBars();
+ break;
+ }
case MSG_UNFREEZE_GLROOT: {
mActivity.getGLRoot().unfreeze();
break;
@@ -377,6 +379,7 @@
private void showBars() {
if (mShowBars) return;
mShowBars = true;
+ mOrientationManager.unlockOrientation();
mActionBar.show();
mActivity.getGLRoot().setLightsOutMode(false);
refreshHidingMessage();
@@ -402,29 +405,24 @@
if (mAppBridge != null && mCurrentIndex == 0) return false;
// No bars if it's not allowed.
if (!mActionBarAllowed) return false;
- // No bars if the orientation is locked.
- if (mOrientationManager.isOrientationLocked()) return false;
return true;
}
+ private void wantBars() {
+ if (canShowBars()) showBars();
+ }
+
private void toggleBars() {
- mCanShowBars = canShowBars();
if (mShowBars) {
hideBars();
} else {
- if (mCanShowBars) showBars();
+ if (canShowBars()) showBars();
}
}
private void updateBars() {
- boolean v = canShowBars();
- if (mCanShowBars == v) return;
- mCanShowBars = v;
-
- if (mCanShowBars) {
- showBars();
- } else {
+ if (!canShowBars()) {
hideBars();
}
}
@@ -686,6 +684,11 @@
}
@Override
+ public void onActionBarWanted() {
+ mHandler.sendEmptyMessage(MSG_WANT_BARS);
+ }
+
+ @Override
public void onFullScreenChanged(boolean full) {
Message m = mHandler.obtainMessage(
MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0);
@@ -695,8 +698,9 @@
public static void playVideo(Activity activity, Uri uri, String title) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW)
- .setDataAndType(uri, "video/*");
- intent.putExtra(Intent.EXTRA_TITLE, title);
+ .setDataAndType(uri, "video/*")
+ .putExtra(Intent.EXTRA_TITLE, title)
+ .putExtra(MovieActivity.KEY_TREAT_UP_AS_BACK, true);
activity.startActivityForResult(intent, REQUEST_PLAY_VIDEO);
} catch (ActivityNotFoundException e) {
Toast.makeText(activity, activity.getString(R.string.video_err),
diff --git a/src/com/android/gallery3d/data/ComboAlbumSet.java b/src/com/android/gallery3d/data/ComboAlbumSet.java
index 916b163..6d9a2a3 100644
--- a/src/com/android/gallery3d/data/ComboAlbumSet.java
+++ b/src/com/android/gallery3d/data/ComboAlbumSet.java
@@ -65,6 +65,14 @@
}
@Override
+ public boolean isLoading() {
+ for (int i = 0, n = mSets.length; i < n; ++i) {
+ if (mSets[i].isLoading()) return true;
+ }
+ return false;
+ }
+
+ @Override
public long reload() {
boolean changed = false;
for (int i = 0, n = mSets.length; i < n; ++i) {
diff --git a/src/com/android/gallery3d/data/LocalAlbumSet.java b/src/com/android/gallery3d/data/LocalAlbumSet.java
index dbb5189..07741ef 100644
--- a/src/com/android/gallery3d/data/LocalAlbumSet.java
+++ b/src/com/android/gallery3d/data/LocalAlbumSet.java
@@ -100,6 +100,7 @@
private final ChangeNotifier mNotifierVideo;
private final String mName;
private final Handler mHandler;
+ private boolean mIsLoading;
private Future<ArrayList<MediaSet>> mLoadTask;
private ArrayList<MediaSet> mLoadBuffer;
@@ -261,6 +262,11 @@
}
@Override
+ public synchronized boolean isLoading() {
+ return mIsLoading;
+ }
+
+ @Override
// synchronized on this function for
// 1. Prevent calling reload() concurrently.
// 2. Prevent calling onFutureDone() and reload() concurrently
@@ -268,6 +274,7 @@
// "|" is used instead of "||" because we want to clear both flags.
if (mNotifierImage.isDirty() | mNotifierVideo.isDirty()) {
if (mLoadTask != null) mLoadTask.cancel();
+ mIsLoading = true;
mLoadTask = mApplication.getThreadPool().submit(new AlbumsLoader(), this);
}
if (mLoadBuffer != null) {
@@ -285,6 +292,7 @@
public synchronized void onFutureDone(Future<ArrayList<MediaSet>> future) {
if (mLoadTask != future) return; // ignore, wait for the latest task
mLoadBuffer = future.get();
+ mIsLoading = false;
if (mLoadBuffer == null) mLoadBuffer = new ArrayList<MediaSet>();
mHandler.post(new Runnable() {
@Override
diff --git a/src/com/android/gallery3d/data/MediaSet.java b/src/com/android/gallery3d/data/MediaSet.java
index ff9b8c3..67525b1 100644
--- a/src/com/android/gallery3d/data/MediaSet.java
+++ b/src/com/android/gallery3d/data/MediaSet.java
@@ -94,6 +94,14 @@
return false;
}
+ /**
+ * Method {@link #reload()} may process the loading task in background, this method tells
+ * its client whether the loading is still in process or not.
+ */
+ public boolean isLoading() {
+ return false;
+ }
+
public int getTotalMediaItemCount() {
int total = getMediaItemCount();
for (int i = 0, n = getSubMediaSetCount(); i < n; i++) {
diff --git a/src/com/android/gallery3d/data/MtpDeviceSet.java b/src/com/android/gallery3d/data/MtpDeviceSet.java
index 6dcb0d2..1f26511 100644
--- a/src/com/android/gallery3d/data/MtpDeviceSet.java
+++ b/src/com/android/gallery3d/data/MtpDeviceSet.java
@@ -47,6 +47,7 @@
private Future<ArrayList<MediaSet>> mLoadTask;
private ArrayList<MediaSet> mDeviceSet = new ArrayList<MediaSet>();
private ArrayList<MediaSet> mLoadBuffer;
+ private boolean mIsLoading;
public MtpDeviceSet(Path path, GalleryApp application, MtpContext mtpContext) {
super(path, nextVersionNumber());
@@ -113,9 +114,15 @@
}
@Override
+ public synchronized boolean isLoading() {
+ return mIsLoading;
+ }
+
+ @Override
public synchronized long reload() {
if (mNotifier.isDirty()) {
if (mLoadTask != null) mLoadTask.cancel();
+ mIsLoading = true;
mLoadTask = mApplication.getThreadPool().submit(new DevicesLoader(), this);
}
if (mLoadBuffer != null) {
@@ -133,6 +140,7 @@
public synchronized void onFutureDone(Future<ArrayList<MediaSet>> future) {
if (future != mLoadTask) return;
mLoadBuffer = future.get();
+ mIsLoading = false;
if (mLoadBuffer == null) mLoadBuffer = new ArrayList<MediaSet>();
mHandler.post(new Runnable() {
diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java
index c5773ed..746d41d 100644
--- a/src/com/android/gallery3d/ui/ActionModeHandler.java
+++ b/src/com/android/gallery3d/ui/ActionModeHandler.java
@@ -283,6 +283,8 @@
mMenuTask.cancel();
}
+ updateSelectionMenu();
+
// Disable share action until share intent is in good shape
final MenuItem item = mShareActionProvider != null ?
mMenu.findItem(R.id.action_share) : null;
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
index cefb9cd..6561a23 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
@@ -70,7 +70,8 @@
public MediaSet album;
public MediaItem coverItem;
public Texture content;
- public Texture label;
+ public BitmapTexture labelTexture;
+ public BitmapTexture bitmapTexture;
public Path setPath;
public String title;
public int totalCount;
@@ -226,6 +227,8 @@
AlbumSetEntry entry = mData[slotIndex % mData.length];
if (entry.coverLoader != null) entry.coverLoader.recycle();
if (entry.labelLoader != null) entry.labelLoader.recycle();
+ if (entry.labelTexture != null) entry.labelTexture.recycle();
+ if (entry.bitmapTexture != null) entry.bitmapTexture.recycle();
mData[slotIndex % mData.length] = null;
}
@@ -256,7 +259,7 @@
if (entry.labelLoader != null) {
entry.labelLoader.recycle();
entry.labelLoader = null;
- entry.label = null;
+ entry.labelTexture = null;
}
if (album != null) {
entry.labelLoader = new AlbumLabelLoader(
@@ -273,6 +276,7 @@
if (entry.coverLoader != null) {
entry.coverLoader.recycle();
entry.coverLoader = null;
+ entry.bitmapTexture = null;
entry.content = null;
}
if (cover != null) {
@@ -296,11 +300,11 @@
private void uploadBackgroundTextureInSlot(int index) {
if (index < mContentStart || index >= mContentEnd) return;
AlbumSetEntry entry = mData[index % mData.length];
- if (entry.content instanceof BitmapTexture) {
- mTextureUploader.addBgTexture((BitmapTexture) entry.content);
+ if (entry.bitmapTexture != null) {
+ mTextureUploader.addBgTexture(entry.bitmapTexture);
}
- if (entry.label instanceof BitmapTexture) {
- mTextureUploader.addBgTexture((BitmapTexture) entry.label);
+ if (entry.labelTexture != null) {
+ mTextureUploader.addBgTexture(entry.labelTexture);
}
}
@@ -311,11 +315,11 @@
// Upload foreground texture
for (int i = mActiveStart, n = mActiveEnd; i < n; ++i) {
AlbumSetEntry entry = mData[i % mData.length];
- if (entry.content instanceof BitmapTexture) {
- mTextureUploader.addFgTexture((BitmapTexture) entry.content);
+ if (entry.bitmapTexture != null) {
+ mTextureUploader.addFgTexture(entry.bitmapTexture);
}
- if (entry.label instanceof BitmapTexture) {
- mTextureUploader.addFgTexture((BitmapTexture) entry.label);
+ if (entry.labelTexture != null) {
+ mTextureUploader.addFgTexture(entry.labelTexture);
}
}
@@ -440,6 +444,7 @@
AlbumSetEntry entry = mData[mSlotIndex % mData.length];
BitmapTexture texture = new BitmapTexture(bitmap);
+ entry.bitmapTexture = texture;
entry.content = texture;
if (isActiveSlot(mSlotIndex)) {
@@ -509,7 +514,7 @@
AlbumSetEntry entry = mData[mSlotIndex % mData.length];
BitmapTexture texture = new BitmapTexture(bitmap);
texture.setOpaque(false);
- entry.label = texture;
+ entry.labelTexture = texture;
if (isActiveSlot(mSlotIndex)) {
mTextureUploader.addFgTexture(texture);
@@ -536,7 +541,7 @@
if (entry.labelLoader != null) {
entry.labelLoader.recycle();
entry.labelLoader = null;
- entry.label = null;
+ entry.labelTexture = null;
}
if (entry.album != null) {
entry.labelLoader = new AlbumLabelLoader(i,
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
index b0e6153..7b4a866 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
@@ -99,9 +99,9 @@
}
}
- private static Texture checkTexture(GLCanvas canvas, Texture texture) {
- return ((texture == null) || ((texture instanceof UploadedTexture)
- && !((UploadedTexture) texture).isContentValid(canvas)))
+ private static Texture checkTexture(Texture texture) {
+ return ((texture instanceof UploadedTexture)
+ && ((UploadedTexture) texture).isUploading())
? null
: texture;
}
@@ -142,15 +142,14 @@
GLCanvas canvas, AlbumSetEntry entry, int width, int height) {
int renderRequestFlags = 0;
- Texture content = checkTexture(canvas, entry.content);
+ Texture content = checkTexture(entry.content);
if (content == null) {
content = mWaitLoadingTexture;
entry.isWaitLoadingDisplayed = true;
} else if (entry.isWaitLoadingDisplayed) {
entry.isWaitLoadingDisplayed = false;
- entry.content = new FadeInTexture(
- PLACEHOLDER_COLOR, (BitmapTexture) entry.content);
- content = entry.content;
+ content = new FadeInTexture(PLACEHOLDER_COLOR, entry.bitmapTexture);
+ entry.content = content;
}
drawContent(canvas, content, width, height, entry.rotation);
if ((content instanceof FadeInTexture) &&
@@ -173,7 +172,7 @@
GLCanvas canvas, AlbumSetEntry entry, int width, int height) {
// We show the loading message only when the album is still loading
// (Not when we are still preparing the label)
- Texture content = checkTexture(canvas, entry.label);
+ Texture content = checkTexture(entry.labelTexture);
if (entry.album == null) {
content = mDataWindow.getLoadingTexture();
}
diff --git a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
index 240cf34..deec171 100644
--- a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
+++ b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
@@ -48,6 +48,7 @@
public int rotation;
public int mediaType;
public boolean isWaitDisplayed;
+ public BitmapTexture bitmapTexture;
public Texture content;
private BitmapLoader contentLoader;
}
@@ -164,8 +165,8 @@
private void uploadBgTextureInSlot(int index) {
if (index < mContentEnd && index >= mContentStart) {
AlbumEntry entry = mData[index % mData.length];
- if (entry.content instanceof BitmapTexture) {
- mTextureUploader.addBgTexture((BitmapTexture) entry.content);
+ if (entry.bitmapTexture != null) {
+ mTextureUploader.addBgTexture(entry.bitmapTexture);
}
}
}
@@ -177,8 +178,8 @@
// add foreground textures
for (int i = mActiveStart, n = mActiveEnd; i < n; ++i) {
AlbumEntry entry = mData[i % mData.length];
- if (entry.content instanceof BitmapTexture) {
- mTextureUploader.addFgTexture((BitmapTexture) entry.content);
+ if (entry.bitmapTexture != null) {
+ mTextureUploader.addFgTexture(entry.bitmapTexture);
}
}
@@ -234,9 +235,8 @@
AlbumEntry data[] = mData;
int index = slotIndex % data.length;
AlbumEntry entry = data[index];
- if (entry.contentLoader != null) {
- entry.contentLoader.recycle();
- }
+ if (entry.contentLoader != null) entry.contentLoader.recycle();
+ if (entry.bitmapTexture != null) entry.bitmapTexture.recycle();
data[index] = null;
}
@@ -296,15 +296,16 @@
if (bitmap == null) return; // error or recycled
AlbumEntry entry = mData[mSlotIndex % mData.length];
- entry.content = new BitmapTexture(bitmap);
+ entry.bitmapTexture = new BitmapTexture(bitmap);
+ entry.content = entry.bitmapTexture;
if (isActiveSlot(mSlotIndex)) {
- mTextureUploader.addFgTexture((BitmapTexture) entry.content);
+ mTextureUploader.addFgTexture(entry.bitmapTexture);
--mActiveRequestCount;
if (mActiveRequestCount == 0) requestNonactiveImages();
if (mListener != null) mListener.onContentChanged();
} else {
- mTextureUploader.addBgTexture((BitmapTexture) entry.content);
+ mTextureUploader.addBgTexture(entry.bitmapTexture);
}
}
}
diff --git a/src/com/android/gallery3d/ui/AlbumSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSlotRenderer.java
index 5f1e3a1..ab99766 100644
--- a/src/com/android/gallery3d/ui/AlbumSlotRenderer.java
+++ b/src/com/android/gallery3d/ui/AlbumSlotRenderer.java
@@ -89,9 +89,9 @@
}
}
- private static Texture checkTexture(GLCanvas canvas, Texture texture) {
- return ((texture == null) || ((texture instanceof UploadedTexture)
- && !((UploadedTexture) texture).isContentValid(canvas)))
+ private static Texture checkTexture(Texture texture) {
+ return (texture instanceof UploadedTexture)
+ && ((UploadedTexture) texture).isUploading()
? null
: texture;
}
@@ -104,15 +104,14 @@
int renderRequestFlags = 0;
- Texture content = checkTexture(canvas, entry.content);
+ Texture content = checkTexture(entry.content);
if (content == null) {
content = mWaitLoadingTexture;
entry.isWaitDisplayed = true;
} else if (entry.isWaitDisplayed) {
entry.isWaitDisplayed = false;
- entry.content = new FadeInTexture(
- PLACEHOLDER_COLOR, (BitmapTexture) entry.content);
- content = entry.content;
+ content = new FadeInTexture(PLACEHOLDER_COLOR, entry.bitmapTexture);
+ entry.content = content;
}
drawContent(canvas, content, width, height, entry.rotation);
if ((content instanceof FadeInTexture) &&
diff --git a/src/com/android/gallery3d/ui/BasicTexture.java b/src/com/android/gallery3d/ui/BasicTexture.java
index 6a9a17d..7855927 100644
--- a/src/com/android/gallery3d/ui/BasicTexture.java
+++ b/src/com/android/gallery3d/ui/BasicTexture.java
@@ -33,6 +33,8 @@
protected static final int STATE_LOADED = 1;
protected static final int STATE_ERROR = -1;
+ private static final int MAX_TEXTURE_SIZE = 2048;
+
protected int mId;
protected int mState;
@@ -75,6 +77,10 @@
mHeight = height;
mTextureWidth = Utils.nextPowerOf2(width);
mTextureHeight = Utils.nextPowerOf2(height);
+ if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
+ Log.w(TAG, String.format("texture is too large: %d x %d",
+ mTextureWidth, mTextureHeight), new Exception());
+ }
}
public int getId() {
@@ -133,7 +139,7 @@
// Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
abstract protected int getTarget();
- public boolean isLoaded(GLCanvas canvas) {
+ public boolean isLoaded() {
return mState == STATE_LOADED;
}
@@ -154,10 +160,10 @@
private void freeResource() {
GLCanvas canvas = mCanvasRef;
- if (canvas != null && isLoaded(canvas)) {
+ if (canvas != null && isLoaded()) {
canvas.unloadTexture(this);
}
- mState = BasicTexture.STATE_UNLOADED;
+ mState = STATE_UNLOADED;
setAssociatedCanvas(null);
}
diff --git a/src/com/android/gallery3d/ui/BitmapScreenNail.java b/src/com/android/gallery3d/ui/BitmapScreenNail.java
index 14d3f19..5c91757 100644
--- a/src/com/android/gallery3d/ui/BitmapScreenNail.java
+++ b/src/com/android/gallery3d/ui/BitmapScreenNail.java
@@ -18,7 +18,6 @@
import android.graphics.Bitmap;
import android.graphics.RectF;
-import android.util.Log;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.MediaItem;
@@ -35,6 +34,9 @@
private static final int PLACEHOLDER_COLOR = 0xFF222222;
// The duration of the fading animation in milliseconds
private static final int DURATION = 180;
+
+ private static final int MAX_SIDE = 640;
+
// These are special values for mAnimationStartTime
private static final long ANIMATION_NOT_NEEDED = -1;
private static final long ANIMATION_NEEDED = -2;
@@ -44,7 +46,6 @@
private int mHeight;
private Bitmap mBitmap;
private BitmapTexture mTexture;
- private FadeInTexture mFadeInTexture;
private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
public BitmapScreenNail(Bitmap bitmap) {
@@ -56,12 +57,17 @@
}
public BitmapScreenNail(int width, int height) {
+ setSize(width, height);
+ }
+
+ private void setSize(int width, int height) {
if (width == 0 || height == 0) {
width = 640;
height = 480;
}
- mWidth = width;
- mHeight = height;
+ float scale = Math.min(1, (float) MAX_SIDE / Math.max(width, height));
+ mWidth = Math.round(scale * width);
+ mHeight = Math.round(scale * height);
}
// Combines the two ScreenNails.
@@ -101,8 +107,7 @@
public void updatePlaceholderSize(int width, int height) {
if (mBitmap != null) return;
if (width == 0 || height == 0) return;
- mWidth = width;
- mHeight = height;
+ setSize(width, height);
}
@Override
@@ -189,4 +194,8 @@
float r = (float)(now() - mAnimationStartTime) / DURATION;
return Utils.clamp(1.0f - r, 0.0f, 1.0f);
}
+
+ public boolean isShowingPlaceholder() {
+ return (mBitmap == null) || isAnimating();
+ }
}
diff --git a/src/com/android/gallery3d/ui/BitmapTileProvider.java b/src/com/android/gallery3d/ui/BitmapTileProvider.java
index 320118e..d4c9b1d 100644
--- a/src/com/android/gallery3d/ui/BitmapTileProvider.java
+++ b/src/com/android/gallery3d/ui/BitmapTileProvider.java
@@ -21,6 +21,7 @@
import android.graphics.Canvas;
import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.data.BitmapPool;
import java.util.ArrayList;
@@ -49,44 +50,46 @@
mConfig = Config.ARGB_8888;
}
+ @Override
public ScreenNail getScreenNail() {
return mScreenNail;
}
+ @Override
public int getImageHeight() {
return mImageHeight;
}
+ @Override
public int getImageWidth() {
return mImageWidth;
}
+ @Override
public int getLevelCount() {
return mMipmaps.length;
}
+ @Override
public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize) {
+ int borderSize, BitmapPool pool) {
x >>= level;
y >>= level;
int size = tileSize + 2 * borderSize;
- Bitmap result = Bitmap.createBitmap(size, size, mConfig);
+
+ Bitmap result = pool == null ? null : pool.getBitmap();
+ if (result == null) {
+ result = Bitmap.createBitmap(size, size, mConfig);
+ } else {
+ result.eraseColor(0);
+ }
+
Bitmap mipmap = mMipmaps[level];
Canvas canvas = new Canvas(result);
int offsetX = -x + borderSize;
int offsetY = -y + borderSize;
canvas.drawBitmap(mipmap, offsetX, offsetY, null);
-
- // If the valid region (covered by mipmap or border) is smaller than the
- // result bitmap, subset it.
- int endX = offsetX + mipmap.getWidth() + borderSize;
- int endY = offsetY + mipmap.getHeight() + borderSize;
- if (endX < size || endY < size) {
- return Bitmap.createBitmap(result, 0, 0, Math.min(size, endX),
- Math.min(size, endY));
- } else {
- return result;
- }
+ return result;
}
public void recycle() {
diff --git a/src/com/android/gallery3d/ui/ExtTexture.java b/src/com/android/gallery3d/ui/ExtTexture.java
index d8267d1..9319d8c 100644
--- a/src/com/android/gallery3d/ui/ExtTexture.java
+++ b/src/com/android/gallery3d/ui/ExtTexture.java
@@ -60,12 +60,12 @@
GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
setAssociatedCanvas(canvas);
- mState = UploadedTexture.STATE_LOADED;
+ mState = STATE_LOADED;
}
@Override
protected boolean onBind(GLCanvas canvas) {
- if (!isLoaded(canvas)) {
+ if (!isLoaded()) {
uploadToCanvas(canvas);
}
diff --git a/src/com/android/gallery3d/ui/FadeOutTexture.java b/src/com/android/gallery3d/ui/FadeOutTexture.java
index c438977..47e6acb 100644
--- a/src/com/android/gallery3d/ui/FadeOutTexture.java
+++ b/src/com/android/gallery3d/ui/FadeOutTexture.java
@@ -18,7 +18,7 @@
// FadeOutTexture is a texture which begins with a given texture, then gradually animates
// into fading out totally.
-public class FadeOutTexture extends FadeTexture implements Texture {
+public class FadeOutTexture extends FadeTexture {
@SuppressWarnings("unused")
private static final String TAG = "FadeOutTexture";
diff --git a/src/com/android/gallery3d/ui/FadeTexture.java b/src/com/android/gallery3d/ui/FadeTexture.java
index ad0d358..cbf5073 100644
--- a/src/com/android/gallery3d/ui/FadeTexture.java
+++ b/src/com/android/gallery3d/ui/FadeTexture.java
@@ -42,23 +42,22 @@
mIsAnimating = true;
}
+ @Override
public void draw(GLCanvas canvas, int x, int y) {
draw(canvas, x, y, mWidth, mHeight);
}
- /**
- * Subclasses should implement how to fade the texture.
- */
- public abstract void draw(GLCanvas canvas, int x, int y, int w, int h);
-
+ @Override
public boolean isOpaque() {
return mIsOpaque;
}
+ @Override
public int getWidth() {
return mWidth;
}
+ @Override
public int getHeight() {
return mHeight;
}
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
index 1efe5af..ff3e9e5 100644
--- a/src/com/android/gallery3d/ui/GLCanvasImpl.java
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -661,7 +661,7 @@
// so we synchronized on the mUnboundTextures object.
public boolean unloadTexture(BasicTexture t) {
synchronized (mUnboundTextures) {
- if (!t.isLoaded(this)) return false;
+ if (!t.isLoaded()) return false;
mUnboundTextures.add(t.mId);
return true;
}
@@ -786,7 +786,7 @@
} else {
setSize(texture.getWidth(), texture.getHeight());
- if (!texture.isLoaded(this)) texture.prepare(this);
+ if (!texture.isLoaded()) texture.prepare(this);
gl11ep.glFramebufferTexture2DOES(
GL11ExtensionPack.GL_FRAMEBUFFER_OES,
diff --git a/src/com/android/gallery3d/ui/GLRootView.java b/src/com/android/gallery3d/ui/GLRootView.java
index 987e9a2..f78e6e6 100644
--- a/src/com/android/gallery3d/ui/GLRootView.java
+++ b/src/com/android/gallery3d/ui/GLRootView.java
@@ -118,7 +118,7 @@
getHolder().setFormat(PixelFormat.RGB_565);
// Uncomment this to enable gl error check.
- //setDebugFlags(DEBUG_CHECK_GL_ERROR);
+ // setDebugFlags(DEBUG_CHECK_GL_ERROR);
}
@Override
@@ -252,8 +252,15 @@
// The GL Object has changed
Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
}
- mGL = gl;
- mCanvas = new GLCanvasImpl(gl);
+ mRenderLock.lock();
+ try {
+ mGL = gl;
+ mCanvas = new GLCanvasImpl(gl);
+ BasicTexture.invalidateAllTextures();
+ } finally {
+ mRenderLock.unlock();
+ }
+
if (DEBUG_FPS || DEBUG_PROFILE) {
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
} else {
@@ -272,7 +279,6 @@
+ ", gl10: " + gl1.toString());
Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
GalleryUtils.setRenderThread();
- BasicTexture.invalidateAllTextures();
if (DEBUG_PROFILE) {
Log.d(TAG, "Start profiling");
Profile.enable(20); // take a sample every 20ms
@@ -514,7 +520,11 @@
@Override
public void setLightsOutMode(boolean enabled) {
- int flags = enabled ? SYSTEM_UI_FLAG_LOW_PROFILE : 0;
+ int flags = enabled
+ ? SYSTEM_UI_FLAG_LOW_PROFILE
+ | SYSTEM_UI_FLAG_FULLSCREEN
+ | SYSTEM_UI_FLAG_LAYOUT_STABLE
+ : 0;
setSystemUiVisibility(flags);
}
diff --git a/src/com/android/gallery3d/ui/NinePatchTexture.java b/src/com/android/gallery3d/ui/NinePatchTexture.java
index 957229e..fa0e9cd 100644
--- a/src/com/android/gallery3d/ui/NinePatchTexture.java
+++ b/src/com/android/gallery3d/ui/NinePatchTexture.java
@@ -157,7 +157,7 @@
@Override
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
- if (!isLoaded(canvas)) {
+ if (!isLoaded()) {
mInstanceCache.clear();
}
diff --git a/src/com/android/gallery3d/ui/PhotoFallbackEffect.java b/src/com/android/gallery3d/ui/PhotoFallbackEffect.java
index cd930bd..3ca09ab 100644
--- a/src/com/android/gallery3d/ui/PhotoFallbackEffect.java
+++ b/src/com/android/gallery3d/ui/PhotoFallbackEffect.java
@@ -87,7 +87,7 @@
}
private void drawEntry(GLCanvas canvas, Entry entry) {
- if (!entry.texture.isLoaded(canvas)) return;
+ if (!entry.texture.isLoaded()) return;
int w = entry.texture.getWidth();
int h = entry.texture.getHeight();
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index e7ec3a9..4490569 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -90,33 +90,25 @@
public void unlockOrientation();
public void onFullScreenChanged(boolean full);
public void onActionBarAllowed(boolean allowed);
+ public void onActionBarWanted();
public void onCurrentImageUpdated();
}
- // Here is a graph showing the places we need to lock/unlock device
- // orientation:
+ // The rules about orientation locking:
//
- // +------------+ A +------------+
- // Page mode | Camera |<---| Photo |
- // | [locked] |--->| [unlocked] |
- // +------------+ B +------------+
- // ^ ^
- // | C | D
- // +------------+ +------------+
- // | Camera | | Photo |
- // Film mode | [*] | | [*] |
- // +------------+ +------------+
+ // (1) We need to lock the orientation if we are in page mode camera
+ // preview, so there is no (unwanted) rotation animation when the user
+ // rotates the device.
//
- // In Page mode, we want to lock in Camera because we don't want the system
- // rotation animation. We also want to unlock in Photo because we want to
- // show the system action bar in the right place.
+ // (2) We need to unlock the orientation if we want to show the action bar
+ // because the action bar follows the system orientation.
//
- // We don't show action bar in Film mode, so it's fine for it to be locked
- // or unlocked in Film mode.
+ // The rules about action bar:
//
- // There are four transitions we need to check if we need to
- // lock/unlock. Marked as A to D above and in the code.
-
+ // (1) If we are in film mode, we don't show action bar.
+ //
+ // (2) If we go from camera to gallery with capture animation, we show
+ // action bar.
private static final int MSG_CANCEL_EXTRA_SCALING = 2;
private static final int MSG_SWITCH_FOCUS = 3;
private static final int MSG_CAPTURE_ANIMATION_DONE = 4;
@@ -457,14 +449,9 @@
setFilmMode(false);
}
- if (isCenter && !mFilmMode) {
- if (mIsCamera) {
- // move into camera, lock
- mListener.lockOrientation(); // Transition A
- } else {
- // move out of camera, unlock
- mListener.unlockOrientation(); // Transition B
- }
+ if (isCenter && !mFilmMode && mIsCamera) {
+ // Move into camera in page mode, lock
+ mListener.lockOrientation();
}
mWasCameraCenter = isCameraCenter;
@@ -931,13 +918,9 @@
mModel.setNeedFullImage(!enabled);
mListener.onActionBarAllowed(!enabled);
- // If we leave filmstrip mode, we should lock/unlock
- if (!enabled) {
- if (mPictures.get(0).isCamera()) {
- mListener.lockOrientation(); // Transition C
- } else {
- mListener.unlockOrientation(); // Transition D
- }
+ // Move into camera in page mode, lock
+ if (!enabled && mPictures.get(0).isCamera()) {
+ mListener.lockOrientation();
}
}
@@ -973,21 +956,15 @@
@Override
protected void render(GLCanvas canvas) {
- float filmRatio = mPositionController.getFilmRatio();
+ // In page mode, we draw only one previous/next photo. But if we are
+ // doing capture animation, we want to draw all photos.
+ boolean inPageMode = (mPositionController.getFilmRatio() == 0f);
+ boolean inCaptureAnimation = ((mHolding & HOLD_CAPTURE_ANIMATION) != 0);
+ boolean drawOneNeighborOnly = inPageMode && !inCaptureAnimation;
+ int neighbors = drawOneNeighborOnly ? 1 : SCREEN_NAIL_MAX;
- // Draw next photos. In page mode, we draw only one next photo.
- int lastPhoto = (filmRatio == 0f) ? 1 : SCREEN_NAIL_MAX;
- for (int i = lastPhoto; i > 0; i--) {
- Rect r = mPositionController.getPosition(i);
- mPictures.get(i).draw(canvas, r);
- }
-
- // Draw current photo
- mPictures.get(0).draw(canvas, mPositionController.getPosition(0));
-
- // Draw previous photos. In page mode, we draw only one previous photo.
- lastPhoto = (filmRatio == 0f) ? -1: -SCREEN_NAIL_MAX;
- for (int i = -1; i >= lastPhoto; i--) {
+ // Draw photos from back to front
+ for (int i = neighbors; i >= -neighbors; i--) {
Rect r = mPositionController.getPosition(i);
mPictures.get(i).draw(canvas, r);
}
@@ -1191,6 +1168,17 @@
mPositionController.startCaptureAnimationSlide(-1);
} else if (offset == -1) {
if (mPrevBound >= 0) return false;
+ if (mFilmMode) setFilmMode(false);
+
+ // If we are too far away from the first image (so that we don't
+ // have all the ScreenNails in-between), we go directly without
+ // animation.
+ if (mModel.getCurrentIndex() > SCREEN_NAIL_MAX) {
+ switchToFirstImage();
+ mPositionController.skipAnimation();
+ return true;
+ }
+
switchToFirstImage();
mPositionController.startCaptureAnimationSlide(1);
} else {
@@ -1204,12 +1192,10 @@
private void captureAnimationDone(int offset) {
mHolding &= ~HOLD_CAPTURE_ANIMATION;
- if (offset == 1) {
- // move out of camera, unlock
- if (!mFilmMode) {
- // Now the capture animation is done, enable the action bar.
- mListener.onActionBarAllowed(true);
- }
+ if (offset == 1 && !mFilmMode) {
+ // Now the capture animation is done, enable the action bar.
+ mListener.onActionBarAllowed(true);
+ mListener.onActionBarWanted();
}
snapback();
}
@@ -1318,7 +1304,6 @@
return mPositionController.getPosition(index);
}
-
public PhotoFallbackEffect buildFallbackEffect(GLView root, GLCanvas canvas) {
Rect location = new Rect();
Utils.assertTrue(root.getBoundsOf(this, location));
@@ -1329,7 +1314,10 @@
MediaItem item = mModel.getMediaItem(i);
if (item == null) continue;
ScreenNail sc = mModel.getScreenNail(i);
- if (sc == null) continue;
+ if (!(sc instanceof BitmapScreenNail)
+ || ((BitmapScreenNail) sc).isShowingPlaceholder()) continue;
+
+ // Now, sc is BitmapScreenNail and is not showing placeholder
Rect rect = new Rect(getPhotoRect(i));
if (!Rect.intersects(fullRect, rect)) continue;
rect.offset(location.left, location.top);
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index 226826d..d085c4d 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -34,7 +34,7 @@
public static final int IMAGE_AT_TOP_EDGE = 4;
public static final int IMAGE_AT_BOTTOM_EDGE = 8;
- public static final int CAPTURE_ANIMATION_TIME = 600;
+ public static final int CAPTURE_ANIMATION_TIME = 700;
// Special values for animation time.
private static final long NO_ANIMATION = -1;
@@ -209,8 +209,13 @@
}
updateScaleAndGapLimit();
- startOpeningAnimationIfNeeded();
- snapAndRedraw();
+
+ // If we have the opening animation, do it. Otherwise go directly to the
+ // right position.
+ if (!startOpeningAnimationIfNeeded()) {
+ snapAndRedraw();
+ skipAnimation();
+ }
}
public void setConstrainedFrame(Rect f) {
@@ -280,10 +285,10 @@
return true;
}
- private void startOpeningAnimationIfNeeded() {
- if (mOpenAnimationRect == null) return;
+ private boolean startOpeningAnimationIfNeeded() {
+ if (mOpenAnimationRect == null) return false;
Box b = mBoxes.get(0);
- if (b.mUseViewSize) return;
+ if (b.mUseViewSize) return false;
// Start animation from the saved rectangle if we have one.
Rect r = mOpenAnimationRect;
@@ -303,6 +308,8 @@
g.mCurrentGap = mViewW;
g.doAnimation(g.mDefaultSize, ANIM_KIND_OPENING);
}
+
+ return true;
}
public void setFilmMode(boolean enabled) {
@@ -960,7 +967,27 @@
}
}
- // 8. offset the Platform position
+ // 8. calculate the new absolute X coordinates for those box before
+ // first or after last.
+ for (int i = first - 1; i >= -BOX_MAX; i--) {
+ Box a = mBoxes.get(i + 1);
+ Box b = mBoxes.get(i);
+ int wa = widthOf(a);
+ int wb = widthOf(b);
+ Gap g = mGaps.get(i);
+ b.mAbsoluteX = a.mAbsoluteX - wa / 2 - (wb - wb / 2) - g.mCurrentGap;
+ }
+
+ for (int i = last + 1; i <= BOX_MAX; i++) {
+ Box a = mBoxes.get(i - 1);
+ Box b = mBoxes.get(i);
+ int wa = widthOf(a);
+ int wb = widthOf(b);
+ Gap g = mGaps.get(i - 1);
+ b.mAbsoluteX = a.mAbsoluteX + (wa - wa / 2) + wb / 2 + g.mCurrentGap;
+ }
+
+ // 9. offset the Platform position
int dx = mBoxes.get(0).mAbsoluteX - mPlatform.mCurrentX;
mPlatform.mCurrentX += dx;
mPlatform.mFromX += dx;
diff --git a/src/com/android/gallery3d/ui/RawTexture.java b/src/com/android/gallery3d/ui/RawTexture.java
index e1af7d2..4c0d9d3 100644
--- a/src/com/android/gallery3d/ui/RawTexture.java
+++ b/src/com/android/gallery3d/ui/RawTexture.java
@@ -68,13 +68,13 @@
0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, null);
mId = sTextureId[0];
- mState = UploadedTexture.STATE_LOADED;
+ mState = STATE_LOADED;
setAssociatedCanvas(canvas);
}
@Override
protected boolean onBind(GLCanvas canvas) {
- if (isLoaded(canvas)) return true;
+ if (isLoaded()) return true;
Log.w(TAG, "lost the content due to context change");
return false;
}
diff --git a/src/com/android/gallery3d/ui/SelectionManager.java b/src/com/android/gallery3d/ui/SelectionManager.java
index 1783b11..86e92da 100644
--- a/src/com/android/gallery3d/ui/SelectionManager.java
+++ b/src/com/android/gallery3d/ui/SelectionManager.java
@@ -107,15 +107,21 @@
return mInverseSelection ^ mClickedSet.contains(itemId);
}
+ private int getTotalCount() {
+ if (mSourceMediaSet == null) return -1;
+
+ if (mTotal < 0) {
+ mTotal = mIsAlbumSet
+ ? mSourceMediaSet.getSubMediaSetCount()
+ : mSourceMediaSet.getMediaItemCount();
+ }
+ return mTotal;
+ }
+
public int getSelectedCount() {
int count = mClickedSet.size();
if (mInverseSelection) {
- if (mTotal < 0) {
- mTotal = mIsAlbumSet
- ? mSourceMediaSet.getSubMediaSetCount()
- : mSourceMediaSet.getMediaItemCount();
- }
- count = mTotal - count;
+ count = getTotalCount() - count;
}
return count;
}
@@ -128,8 +134,14 @@
mClickedSet.add(path);
}
+ // Convert to inverse selection mode if everything is selected.
+ int count = getSelectedCount();
+ if (count == getTotalCount()) {
+ selectAll();
+ }
+
if (mListener != null) mListener.onSelectionChange(path, isItemSelected(path));
- if (getSelectedCount() == 0 && mAutoLeave) {
+ if (count == 0 && mAutoLeave) {
leaveSelectionMode();
}
}
@@ -159,8 +171,8 @@
ArrayList<Path> selected = new ArrayList<Path>();
if (mIsAlbumSet) {
if (mInverseSelection) {
- int max = mSourceMediaSet.getSubMediaSetCount();
- for (int i = 0; i < max; i++) {
+ int total = getTotalCount();
+ for (int i = 0; i < total; i++) {
MediaSet set = mSourceMediaSet.getSubMediaSet(i);
Path id = set.getPath();
if (!mClickedSet.contains(id)) {
@@ -182,8 +194,7 @@
}
} else {
if (mInverseSelection) {
-
- int total = mSourceMediaSet.getMediaItemCount();
+ int total = getTotalCount();
int index = 0;
while (index < total) {
int count = Math.min(total - index, MediaSet.MEDIAITEM_BATCH_FETCH_COUNT);
diff --git a/src/com/android/gallery3d/ui/TextureUploader.java b/src/com/android/gallery3d/ui/TextureUploader.java
index ff7830f..714b097 100644
--- a/src/com/android/gallery3d/ui/TextureUploader.java
+++ b/src/com/android/gallery3d/ui/TextureUploader.java
@@ -36,8 +36,12 @@
}
public synchronized void clear() {
- mFgTextures.clear();
- mBgTextures.clear();
+ while (!mFgTextures.isEmpty()) {
+ mFgTextures.pop().setIsUploading(false);
+ }
+ while (!mBgTextures.isEmpty()) {
+ mBgTextures.pop().setIsUploading(false);
+ }
}
// caller should hold synchronized on "this"
@@ -48,12 +52,16 @@
}
public synchronized void addBgTexture(UploadedTexture t) {
+ if (t.isContentValid()) return;
mBgTextures.addLast(t);
+ t.setIsUploading(true);
queueSelfIfNeed();
}
public synchronized void addFgTexture(UploadedTexture t) {
+ if (t.isContentValid()) return;
mFgTextures.addLast(t);
+ t.setIsUploading(true);
queueSelfIfNeed();
}
@@ -64,7 +72,9 @@
synchronized (this) {
if (deque.isEmpty()) break;
t = deque.removeFirst();
- if (t.isContentValid(canvas)) continue;
+ t.setIsUploading(false);
+ if (t.isContentValid()) continue;
+
// this has to be protected by the synchronized block
// to prevent the inner bitmap get recycled
t.updateContent(canvas);
diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index fb0e333..0c6086d 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -25,6 +25,7 @@
import com.android.gallery3d.app.GalleryContext;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.DecodeUtils;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.ThreadPool;
@@ -43,8 +44,12 @@
// texture to avoid seams between tiles.
private static final int TILE_SIZE = 254;
private static final int TILE_BORDER = 1;
+ private static final int BITMAP_SIZE = TILE_SIZE + TILE_BORDER * 2;
private static final int UPLOAD_LIMIT = 1;
+ private static final BitmapPool sTilePool =
+ new BitmapPool(BITMAP_SIZE, BITMAP_SIZE, 128);
+
/*
* This is the tile state in the CPU side.
* Life of a Tile:
@@ -96,9 +101,9 @@
private final LongSparseArray<Tile> mActiveTiles = new LongSparseArray<Tile>();
// The following three queue is guarded by TileImageView.this
- private TileQueue mRecycledQueue = new TileQueue();
- private TileQueue mUploadQueue = new TileQueue();
- private TileQueue mDecodeQueue = new TileQueue();
+ private final TileQueue mRecycledQueue = new TileQueue();
+ private final TileQueue mUploadQueue = new TileQueue();
+ private final TileQueue mDecodeQueue = new TileQueue();
// The width and height of the full-sized bitmap
protected int mImageWidth = SIZE_UNKNOWN;
@@ -116,7 +121,7 @@
private final TileUploader mTileUploader = new TileUploader();
private boolean mIsTextureFreed;
private Future<Void> mTileDecoder;
- private ThreadPool mThreadPool;
+ private final ThreadPool mThreadPool;
private boolean mBackgroundTileUploaded;
public static interface Model {
@@ -138,7 +143,7 @@
//
// The method would be called in another thread.
public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize);
+ int borderSize, BitmapPool pool);
}
public TileImageView(GalleryContext context) {
@@ -373,6 +378,7 @@
}
}
setScreenNail(null);
+ sTilePool.clear();
}
public void prepareTextures() {
@@ -451,7 +457,7 @@
int n = mActiveTiles.size();
for (int i = 0; i < n; i++) {
Tile tile = mActiveTiles.valueAt(i);
- if (!tile.isContentValid(canvas)) queueForDecode(tile);
+ if (!tile.isContentValid()) queueForDecode(tile);
}
}
@@ -480,7 +486,10 @@
synchronized (this) {
if (tile.mTileState == STATE_RECYCLING) {
tile.mTileState = STATE_RECYCLED;
- tile.mDecodedTile = null;
+ if (tile.mDecodedTile != null) {
+ sTilePool.recycle(tile.mDecodedTile);
+ tile.mDecodedTile = null;
+ }
mRecycledQueue.push(tile);
return false;
}
@@ -505,7 +514,10 @@
return;
}
tile.mTileState = STATE_RECYCLED;
- tile.mDecodedTile = null;
+ if (tile.mDecodedTile != null) {
+ sTilePool.recycle(tile.mDecodedTile);
+ tile.mDecodedTile = null;
+ }
mRecycledQueue.push(tile);
}
@@ -546,7 +558,7 @@
tile = mUploadQueue.pop();
}
if (tile == null || quota <= 0) break;
- if (!tile.isContentValid(canvas)) {
+ if (!tile.isContentValid()) {
Utils.assertTrue(tile.mTileState == STATE_DECODED);
tile.updateContent(canvas);
--quota;
@@ -568,7 +580,7 @@
Tile tile = getTile(tx, ty, level);
if (tile != null) {
- if (!tile.isContentValid(canvas)) {
+ if (!tile.isContentValid()) {
if (tile.mTileState == STATE_DECODED) {
if (mUploadQuota > 0) {
--mUploadQuota;
@@ -597,7 +609,7 @@
static boolean drawTile(
Tile tile, GLCanvas canvas, RectF source, RectF target) {
while (true) {
- if (tile.isContentValid(canvas)) {
+ if (tile.isContentValid()) {
// offset source rectangle for the texture border.
source.offset(TILE_BORDER, TILE_BORDER);
canvas.drawTexture(tile, source, target);
@@ -626,12 +638,12 @@
}
private class Tile extends UploadedTexture {
- int mX;
- int mY;
- int mTileLevel;
- Tile mNext;
- Bitmap mDecodedTile;
- volatile int mTileState = STATE_ACTIVATED;
+ public int mX;
+ public int mY;
+ public int mTileLevel;
+ public Tile mNext;
+ public Bitmap mDecodedTile;
+ public volatile int mTileState = STATE_ACTIVATED;
public Tile(int x, int y, int level) {
mX = x;
@@ -641,7 +653,7 @@
@Override
protected void onFreeBitmap(Bitmap bitmap) {
- bitmap.recycle();
+ sTilePool.recycle(bitmap);
}
boolean decode() {
@@ -649,7 +661,7 @@
// by (1 << mTilelevel) from a region in the original image.
try {
mDecodedTile = DecodeUtils.ensureGLCompatibleBitmap(mModel.getTile(
- mTileLevel, mX, mY, TILE_SIZE, TILE_BORDER));
+ mTileLevel, mX, mY, TILE_SIZE, TILE_BORDER, sTilePool));
} catch (Throwable t) {
Log.w(TAG, "fail to decode tile", t);
}
@@ -659,6 +671,13 @@
@Override
protected Bitmap onGetBitmap() {
Utils.assertTrue(mTileState == STATE_DECODED);
+
+ // We need to override the width and height, so that we won't
+ // draw beyond the boundaries.
+ int rightEdge = ((mImageWidth - mX) >> mTileLevel) + TILE_BORDER;
+ int bottomEdge = ((mImageHeight - mY) >> mTileLevel) + TILE_BORDER;
+ setSize(Math.min(BITMAP_SIZE, rightEdge), Math.min(BITMAP_SIZE, bottomEdge));
+
Bitmap bitmap = mDecodedTile;
mDecodedTile = null;
mTileState = STATE_ACTIVATED;
diff --git a/src/com/android/gallery3d/ui/TileImageViewAdapter.java b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
index 5c92812..0b4ac03 100644
--- a/src/com/android/gallery3d/ui/TileImageViewAdapter.java
+++ b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
@@ -20,10 +20,10 @@
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
import android.graphics.Rect;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
public class TileImageViewAdapter implements TileImageView.Model {
private static final String TAG = "TileImageViewAdapter";
@@ -94,70 +94,68 @@
(float) mImageWidth / mScreenNail.getWidth()));
}
+ // Gets a sub image on a rectangle of the current photo. For example,
+ // getTile(1, 50, 50, 100, 3, pool) means to get the region located
+ // at (50, 50) with sample level 1 (ie, down sampled by 2^1) and the
+ // target tile size (after sampling) 100 with border 3.
+ //
+ // From this spec, we can infer the actual tile size to be
+ // 100 + 3x2 = 106, and the size of the region to be extracted from the
+ // photo to be 200 with border 6.
+ //
+ // As a result, we should decode region (50-6, 50-6, 250+6, 250+6) or
+ // (44, 44, 256, 256) from the original photo and down sample it to 106.
@Override
public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize) {
- // wantRegion is the rectangle on the original image we want. askRegion
- // is the rectangle on the original image that we will ask from
- // mRegionDecoder. Both are in the coordinates of the original image,
- // not the coordinates of the scaled-down images.
- Rect wantRegion = new Rect();
- Rect askRegion = new Rect();
-
+ int borderSize, BitmapPool pool) {
int b = borderSize << level;
- wantRegion.set(x - b, y - b, x + (tileSize << level) + b,
- y + (tileSize << level) + b);
+ int t = tileSize << level;
+ Rect wantRegion = new Rect(x - b, y - b, x + t + b, y + t + b);
+
+ boolean needClear;
BitmapRegionDecoder regionDecoder = null;
+
synchronized (this) {
regionDecoder = mRegionDecoder;
if (regionDecoder == null) return null;
- // askRegion is the intersection of wantRegion and the original image.
- askRegion.set(0, 0, mImageWidth, mImageHeight);
+
+ // We need to clear a reused bitmap, if wantRegion is not fully
+ // within the image.
+ needClear = !new Rect(0, 0, mImageWidth, mImageHeight)
+ .contains(wantRegion);
}
- Utils.assertTrue(askRegion.intersect(wantRegion));
+ Bitmap bitmap = pool == null ? null : pool.getBitmap();
+ if (bitmap != null) {
+ if (needClear) bitmap.eraseColor(0);
+ } else {
+ int s = tileSize + 2 * borderSize;
+ bitmap = Bitmap.createBitmap(s, s, Config.ARGB_8888);
+ }
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.ARGB_8888;
options.inPreferQualityOverSpeed = true;
options.inSampleSize = (1 << level);
+ options.inBitmap = bitmap;
- Bitmap bitmap;
-
- // In CropImage, we may call the decodeRegion() concurrently.
- synchronized (regionDecoder) {
- bitmap = regionDecoder.decodeRegion(askRegion, options);
+ try {
+ // In CropImage, we may call the decodeRegion() concurrently.
+ synchronized (regionDecoder) {
+ bitmap = regionDecoder.decodeRegion(wantRegion, options);
+ }
+ } finally {
+ if (options.inBitmap != bitmap && options.inBitmap != null) {
+ if (pool != null) pool.recycle(options.inBitmap);
+ options.inBitmap = null;
+ }
}
if (bitmap == null) {
Log.w(TAG, "fail in decoding region");
- return null;
}
-
- if (wantRegion.equals(askRegion)) return bitmap;
-
- // Now the wantRegion does not match the askRegion. This means we are at
- // a boundary tile, and we need to add paddings. Create a new Bitmap
- // and copy over.
- int size = tileSize + 2 * borderSize;
- Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- int offsetX = (askRegion.left - wantRegion.left) >> level;
- int offsetY = (askRegion.top - wantRegion.top) >> level;
- canvas.drawBitmap(bitmap, offsetX, offsetY, null);
-
- // If the valid region (covered by bitmap or border) is smaller than the
- // result bitmap, subset it.
- int endX = offsetX + bitmap.getWidth() + borderSize;
- int endY = offsetY + bitmap.getHeight() + borderSize;
- bitmap.recycle();
- if (endX < size || endY < size) {
- return Bitmap.createBitmap(result, 0, 0, Math.min(size, endX),
- Math.min(size, endY));
- } else {
- return result;
- }
+ return bitmap;
}
@Override
diff --git a/src/com/android/gallery3d/ui/UploadedTexture.java b/src/com/android/gallery3d/ui/UploadedTexture.java
index 85aa1c4..0fe5067 100644
--- a/src/com/android/gallery3d/ui/UploadedTexture.java
+++ b/src/com/android/gallery3d/ui/UploadedTexture.java
@@ -52,6 +52,9 @@
@SuppressWarnings("unused")
private static final String TAG = "Texture";
private boolean mContentValid = true;
+
+ // indicate this textures is being uploaded in background
+ private boolean mIsUploading = false;
private boolean mOpaque = true;
private boolean mThrottled = false;
private static int sUploadedCount;
@@ -72,6 +75,14 @@
}
}
+ protected void setIsUploading(boolean uploading) {
+ mIsUploading = uploading;
+ }
+
+ public boolean isUploading() {
+ return mIsUploading;
+ }
+
private static class BorderKey implements Cloneable {
public boolean vertical;
public Config config;
@@ -128,10 +139,6 @@
int h = mBitmap.getHeight() + mBorder * 2;
if (mWidth == UNSPECIFIED) {
setSize(w, h);
- } else if (mWidth != w || mHeight != h) {
- throw new IllegalStateException(String.format(
- "cannot change size: this = %s, orig = %sx%s, new = %sx%s",
- toString(), mWidth, mHeight, w, h));
}
}
return mBitmap;
@@ -169,8 +176,8 @@
/**
* Whether the content on GPU is valid.
*/
- public boolean isContentValid(GLCanvas canvas) {
- return isLoaded(canvas) && mContentValid;
+ public boolean isContentValid() {
+ return isLoaded() && mContentValid;
}
/**
@@ -178,7 +185,7 @@
* @param canvas
*/
public void updateContent(GLCanvas canvas) {
- if (!isLoaded(canvas)) {
+ if (!isLoaded()) {
if (mThrottled && ++sUploadedCount > UPLOAD_LIMIT) {
return;
}
@@ -218,6 +225,9 @@
int height = bHeight + mBorder * 2;
int texWidth = getTextureWidth();
int texHeight = getTextureHeight();
+
+ Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
+
// Define a vertically flipped crop rectangle for
// OES_draw_texture.
// The four values in sCropRect are: left, bottom, width, and
@@ -285,7 +295,7 @@
// Update texture state.
setAssociatedCanvas(canvas);
mId = sTextureId[0];
- mState = UploadedTexture.STATE_LOADED;
+ mState = STATE_LOADED;
mContentValid = true;
} else {
mState = STATE_ERROR;
@@ -296,7 +306,7 @@
@Override
protected boolean onBind(GLCanvas canvas) {
updateContent(canvas);
- return isContentValid(canvas);
+ return isContentValid();
}
@Override
diff --git a/tests/src/com/android/gallery3d/ui/TextureTest.java b/tests/src/com/android/gallery3d/ui/TextureTest.java
index be2356c..36446b3 100644
--- a/tests/src/com/android/gallery3d/ui/TextureTest.java
+++ b/tests/src/com/android/gallery3d/ui/TextureTest.java
@@ -20,10 +20,10 @@
import android.graphics.Bitmap.Config;
import android.test.suitebuilder.annotation.SmallTest;
-import javax.microedition.khronos.opengles.GL11;
-
import junit.framework.TestCase;
+import javax.microedition.khronos.opengles.GL11;
+
@SmallTest
public class TextureTest extends TestCase {
@SuppressWarnings("unused")
@@ -34,7 +34,7 @@
int mOpaqueCalled;
MyBasicTexture(GLCanvas canvas, int id) {
- super(canvas, id, BasicTexture.STATE_UNLOADED);
+ super(canvas, id, 0);
}
@Override
@@ -76,13 +76,13 @@
assertEquals(4, texture.getTextureWidth());
assertEquals(8, texture.getTextureHeight());
- assertFalse(texture.isLoaded(canvas));
+ assertFalse(texture.isLoaded());
texture.upload();
- assertTrue(texture.isLoaded(canvas));
+ assertTrue(texture.isLoaded());
// For a different GL, it's not loaded.
GLCanvas canvas2 = new GLCanvasImpl(new GLStub());
- assertFalse(texture.isLoaded(canvas2));
+ assertFalse(texture.isLoaded());
assertEquals(0, texture.mOnBindCalled);
assertEquals(0, texture.mOpaqueCalled);
@@ -143,20 +143,20 @@
assertEquals(0, texture.mGetCalled);
texture.draw(canvas, 0, 0);
assertEquals(1, texture.mGetCalled);
- assertTrue(texture.isLoaded(canvas));
- assertTrue(texture.isContentValid(canvas));
+ assertTrue(texture.isLoaded());
+ assertTrue(texture.isContentValid());
// invalidate content and it should be freed.
texture.invalidateContent();
- assertFalse(texture.isContentValid(canvas));
+ assertFalse(texture.isContentValid());
assertEquals(1, texture.mFreeCalled);
- assertTrue(texture.isLoaded(canvas)); // But it's still loaded
+ assertTrue(texture.isLoaded()); // But it's still loaded
// draw it again and the bitmap should be fetched again.
texture.draw(canvas, 0, 0);
assertEquals(2, texture.mGetCalled);
- assertTrue(texture.isLoaded(canvas));
- assertTrue(texture.isContentValid(canvas));
+ assertTrue(texture.isLoaded());
+ assertTrue(texture.isContentValid());
// recycle the texture and it should be freed again.
texture.recycle();
@@ -168,7 +168,7 @@
class MyTextureForMixed extends BasicTexture {
MyTextureForMixed(GLCanvas canvas, int id) {
- super(canvas, id, BasicTexture.STATE_UNLOADED);
+ super(canvas, id, 0);
}
@Override