OSDN Git Service

Remove FilmStripView.
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / app / AlbumPage.java
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.gallery3d.app;
18
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.graphics.Rect;
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.os.Vibrator;
26 import android.provider.MediaStore;
27 import android.view.ActionMode;
28 import android.view.Menu;
29 import android.view.MenuInflater;
30 import android.view.MenuItem;
31 import android.widget.Toast;
32
33 import com.android.gallery3d.R;
34 import com.android.gallery3d.common.Utils;
35 import com.android.gallery3d.data.DataManager;
36 import com.android.gallery3d.data.MediaDetails;
37 import com.android.gallery3d.data.MediaItem;
38 import com.android.gallery3d.data.MediaObject;
39 import com.android.gallery3d.data.MediaSet;
40 import com.android.gallery3d.data.MtpDevice;
41 import com.android.gallery3d.data.Path;
42 import com.android.gallery3d.ui.ActionModeHandler;
43 import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
44 import com.android.gallery3d.ui.AlbumView;
45 import com.android.gallery3d.ui.DetailsHelper;
46 import com.android.gallery3d.ui.DetailsHelper.CloseListener;
47 import com.android.gallery3d.ui.GLCanvas;
48 import com.android.gallery3d.ui.GLView;
49 import com.android.gallery3d.ui.GridDrawer;
50 import com.android.gallery3d.ui.HighlightDrawer;
51 import com.android.gallery3d.ui.RelativePosition;
52 import com.android.gallery3d.ui.ScreenNailHolder;
53 import com.android.gallery3d.ui.SelectionManager;
54 import com.android.gallery3d.ui.SlotView;
55 import com.android.gallery3d.util.Future;
56 import com.android.gallery3d.util.GalleryUtils;
57
58 public class AlbumPage extends ActivityState implements GalleryActionBar.ClusterRunner,
59         SelectionManager.SelectionListener, MediaSet.SyncListener {
60     @SuppressWarnings("unused")
61     private static final String TAG = "AlbumPage";
62
63     public static final String KEY_MEDIA_PATH = "media-path";
64     public static final String KEY_PARENT_MEDIA_PATH = "parent-media-path";
65     public static final String KEY_SET_CENTER = "set-center";
66     public static final String KEY_AUTO_SELECT_ALL = "auto-select-all";
67     public static final String KEY_SHOW_CLUSTER_MENU = "cluster-menu";
68
69     private static final int REQUEST_SLIDESHOW = 1;
70     private static final int REQUEST_PHOTO = 2;
71     private static final int REQUEST_DO_ANIMATION = 3;
72
73     private static final int BIT_LOADING_RELOAD = 1;
74     private static final int BIT_LOADING_SYNC = 2;
75
76     private static final float USER_DISTANCE_METER = 0.3f;
77     private static final boolean TEST_CAMERA_PREVIEW = false;
78
79     private boolean mIsActive = false;
80     private AlbumView mAlbumView;
81     private Path mMediaSetPath;
82     private String mParentMediaSetString;
83     private SlotView mSlotView;
84
85     private AlbumDataAdapter mAlbumDataAdapter;
86
87     protected SelectionManager mSelectionManager;
88     private Vibrator mVibrator;
89     private GridDrawer mGridDrawer;
90     private HighlightDrawer mHighlightDrawer;
91
92     private boolean mGetContent;
93     private boolean mShowClusterMenu;
94
95     private ActionMode mActionMode;
96     private ActionModeHandler mActionModeHandler;
97     private int mFocusIndex = 0;
98     private DetailsHelper mDetailsHelper;
99     private MyDetailsSource mDetailsSource;
100     private MediaSet mMediaSet;
101     private boolean mShowDetails;
102     private float mUserDistance; // in pixel
103
104     private Future<Integer> mSyncTask = null;
105
106     private int mLoadingBits = 0;
107     private boolean mInitialSynced = false;
108     private RelativePosition mOpenCenter = new RelativePosition();
109
110     private final GLView mRootPane = new GLView() {
111         private final float mMatrix[] = new float[16];
112
113         @Override
114         protected void renderBackground(GLCanvas view) {
115             view.clearBuffer();
116         }
117
118         @Override
119         protected void onLayout(
120                 boolean changed, int left, int top, int right, int bottom) {
121
122             int slotViewTop = mActivity.getGalleryActionBar().getHeight();
123             int slotViewBottom = bottom - top;
124             int slotViewRight = right - left;
125
126             if (mShowDetails) {
127                 mDetailsHelper.layout(left, slotViewTop, right, bottom);
128             } else {
129                 mAlbumView.setSelectionDrawer(mGridDrawer);
130             }
131
132             // Set the mSlotView as a reference point to the open animation
133             mOpenCenter.setReferencePosition(0, slotViewTop);
134             mSlotView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
135             GalleryUtils.setViewPointMatrix(mMatrix,
136                     (right - left) / 2, (bottom - top) / 2, -mUserDistance);
137         }
138
139         @Override
140         protected void render(GLCanvas canvas) {
141             canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
142             canvas.multiplyMatrix(mMatrix, 0);
143             super.render(canvas);
144             canvas.restore();
145         }
146     };
147
148     @Override
149     protected void onBackPressed() {
150         if (mShowDetails) {
151             hideDetails();
152         } else if (mSelectionManager.inSelectionMode()) {
153             mSelectionManager.leaveSelectionMode();
154         } else {
155             // TODO: fix this regression
156             // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
157             super.onBackPressed();
158         }
159     }
160
161     private void onDown(int index) {
162         MediaItem item = mAlbumDataAdapter.get(index);
163         Path path = (item == null) ? null : item.getPath();
164         mSelectionManager.setPressedPath(path);
165         mSlotView.invalidate();
166     }
167
168     private void onUp() {
169         mSelectionManager.setPressedPath(null);
170         mSlotView.invalidate();
171     }
172
173     private void onSingleTapUp(int slotIndex) {
174         MediaItem item = mAlbumDataAdapter.get(slotIndex);
175         if (item == null) {
176             Log.w(TAG, "item not ready yet, ignore the click");
177             return;
178         }
179         if (mShowDetails) {
180             mHighlightDrawer.setHighlightItem(item.getPath());
181             mDetailsHelper.reloadDetails(slotIndex);
182         } else if (!mSelectionManager.inSelectionMode()) {
183             if (mGetContent) {
184                 onGetContent(item);
185             } else {
186                 // Get into the PhotoPage.
187                 Bundle data = new Bundle();
188
189                 // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
190                 data.putInt(PhotoPage.KEY_INDEX_HINT, slotIndex);
191                 data.putParcelable(PhotoPage.KEY_OPEN_ANIMATION_RECT,
192                         getSlotRect(slotIndex));
193                 data.putString(PhotoPage.KEY_MEDIA_SET_PATH,
194                         mMediaSetPath.toString());
195                 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH,
196                         item.getPath().toString());
197                 if (TEST_CAMERA_PREVIEW) {
198                     ScreenNailHolder holder = new CameraScreenNailHolder(mActivity);
199                     data.putParcelable(PhotoPage.KEY_SCREENNAIL_HOLDER, holder);
200                 }
201                 mActivity.getStateManager().startStateForResult(
202                         PhotoPage.class, REQUEST_PHOTO, data);
203             }
204         } else {
205             mSelectionManager.toggle(item.getPath());
206             mDetailsSource.findIndex(slotIndex);
207             mSlotView.invalidate();
208         }
209     }
210
211     private Rect getSlotRect(int slotIndex) {
212         // Get slot rectangle relative to this root pane.
213         Rect offset = new Rect();
214         mRootPane.getBoundsOf(mSlotView, offset);
215         Rect r = mSlotView.getSlotRect(slotIndex);
216         r.offset(offset.left - mSlotView.getScrollX(),
217                 offset.top - mSlotView.getScrollY());
218         return r;
219     }
220
221     private void onGetContent(final MediaItem item) {
222         DataManager dm = mActivity.getDataManager();
223         Activity activity = (Activity) mActivity;
224         if (mData.getString(Gallery.EXTRA_CROP) != null) {
225             // TODO: Handle MtpImagew
226             Uri uri = dm.getContentUri(item.getPath());
227             Intent intent = new Intent(CropImage.ACTION_CROP, uri)
228                     .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
229                     .putExtras(getData());
230             if (mData.getParcelable(MediaStore.EXTRA_OUTPUT) == null) {
231                 intent.putExtra(CropImage.KEY_RETURN_DATA, true);
232             }
233             activity.startActivity(intent);
234             activity.finish();
235         } else {
236             activity.setResult(Activity.RESULT_OK,
237                     new Intent(null, item.getContentUri()));
238             activity.finish();
239         }
240     }
241
242     public void onLongTap(int slotIndex) {
243         if (mGetContent) return;
244         if (mShowDetails) {
245             onSingleTapUp(slotIndex);
246         } else {
247             MediaItem item = mAlbumDataAdapter.get(slotIndex);
248             if (item == null) return;
249             mSelectionManager.setAutoLeaveSelectionMode(true);
250             mSelectionManager.toggle(item.getPath());
251             mDetailsSource.findIndex(slotIndex);
252             mSlotView.invalidate();
253         }
254     }
255
256     @Override
257     public void doCluster(int clusterType) {
258         String basePath = mMediaSet.getPath().toString();
259         String newPath = FilterUtils.newClusterPath(basePath, clusterType);
260         Bundle data = new Bundle(getData());
261         data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
262         if (mShowClusterMenu) {
263             Context context = mActivity.getAndroidContext();
264             data.putString(AlbumSetPage.KEY_SET_TITLE, mMediaSet.getName());
265             data.putString(AlbumSetPage.KEY_SET_SUBTITLE,
266                     GalleryActionBar.getClusterByTypeString(context, clusterType));
267         }
268
269         // mAlbumView.savePositions(PositionRepository.getInstance(mActivity));
270         mActivity.getStateManager().startStateForResult(
271                 AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
272     }
273
274     @Override
275     protected void onCreate(Bundle data, Bundle restoreState) {
276         mUserDistance = GalleryUtils.meterToPixel(USER_DISTANCE_METER);
277         initializeViews();
278         initializeData(data);
279         mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
280         mShowClusterMenu = data.getBoolean(KEY_SHOW_CLUSTER_MENU, false);
281         mDetailsSource = new MyDetailsSource();
282         Context context = mActivity.getAndroidContext();
283         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
284
285         // Enable auto-select-all for mtp album
286         if (data.getBoolean(KEY_AUTO_SELECT_ALL)) {
287             mSelectionManager.selectAll();
288         }
289
290         // Don't show animation if it is restored
291         if (restoreState == null && data != null) {
292             int[] center = data.getIntArray(KEY_SET_CENTER);
293             if (center != null) {
294                 mOpenCenter.setAbsolutePosition(center[0], center[1]);
295                 mSlotView.startScatteringAnimation(mOpenCenter);
296             }
297         }
298     }
299
300     @Override
301     protected void onResume() {
302         super.onResume();
303         mIsActive = true;
304         setContentPane(mRootPane);
305
306         Path path = mMediaSet.getPath();
307         boolean enableHomeButton = (mActivity.getStateManager().getStateCount() > 1) |
308                 mParentMediaSetString != null;
309         mActivity.getGalleryActionBar().setDisplayOptions(enableHomeButton, true);
310
311         // Set the reload bit here to prevent it exit this page in clearLoadingBit().
312         setLoadingBit(BIT_LOADING_RELOAD);
313         mAlbumDataAdapter.resume();
314
315         mAlbumView.resume();
316         mActionModeHandler.resume();
317         if (!mInitialSynced) {
318             setLoadingBit(BIT_LOADING_SYNC);
319             mSyncTask = mMediaSet.requestSync(this);
320         }
321     }
322
323     @Override
324     protected void onPause() {
325         super.onPause();
326         mIsActive = false;
327         mAlbumDataAdapter.pause();
328         mAlbumView.pause();
329         DetailsHelper.pause();
330
331         if (mSyncTask != null) {
332             mSyncTask.cancel();
333             mSyncTask = null;
334             clearLoadingBit(BIT_LOADING_SYNC);
335         }
336         mActionModeHandler.pause();
337         GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
338     }
339
340     @Override
341     protected void onDestroy() {
342         super.onDestroy();
343         if (mAlbumDataAdapter != null) {
344             mAlbumDataAdapter.setLoadingListener(null);
345         }
346     }
347
348     private void initializeViews() {
349         mSelectionManager = new SelectionManager(mActivity, false);
350         mSelectionManager.setSelectionListener(this);
351         mGridDrawer = new GridDrawer((Context) mActivity, mSelectionManager);
352         Config.AlbumPage config = Config.AlbumPage.get((Context) mActivity);
353         mSlotView = new SlotView((Context) mActivity, config.slotViewSpec);
354         mAlbumView = new AlbumView(mActivity, mSlotView);
355         mSlotView.setSlotRenderer(mAlbumView);
356         mAlbumView.setSelectionDrawer(mGridDrawer);
357         mRootPane.addComponent(mSlotView);
358         mSlotView.setListener(new SlotView.SimpleListener() {
359             @Override
360             public void onDown(int index) {
361                 AlbumPage.this.onDown(index);
362             }
363
364             @Override
365             public void onUp() {
366                 AlbumPage.this.onUp();
367             }
368
369             @Override
370             public void onSingleTapUp(int slotIndex) {
371                 AlbumPage.this.onSingleTapUp(slotIndex);
372             }
373
374             @Override
375             public void onLongTap(int slotIndex) {
376                 AlbumPage.this.onLongTap(slotIndex);
377             }
378         });
379         mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
380         mActionModeHandler.setActionModeListener(new ActionModeListener() {
381             public boolean onActionItemClicked(MenuItem item) {
382                 return onItemSelected(item);
383             }
384         });
385     }
386
387     private void initializeData(Bundle data) {
388         mMediaSetPath = Path.fromString(data.getString(KEY_MEDIA_PATH));
389         mParentMediaSetString = data.getString(KEY_PARENT_MEDIA_PATH);
390         mMediaSet = mActivity.getDataManager().getMediaSet(mMediaSetPath);
391         if (mMediaSet == null) {
392             Utils.fail("MediaSet is null. Path = %s", mMediaSetPath);
393         }
394         mSelectionManager.setSourceMediaSet(mMediaSet);
395         mAlbumDataAdapter = new AlbumDataAdapter(mActivity, mMediaSet);
396         mAlbumDataAdapter.setLoadingListener(new MyLoadingListener());
397         mAlbumView.setModel(mAlbumDataAdapter);
398     }
399
400     private void showDetails() {
401         mShowDetails = true;
402         if (mDetailsHelper == null) {
403             mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext(),
404                     mSelectionManager);
405             mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
406             mDetailsHelper.setCloseListener(new CloseListener() {
407                 public void onClose() {
408                     hideDetails();
409                 }
410             });
411         }
412         mAlbumView.setSelectionDrawer(mHighlightDrawer);
413         mDetailsHelper.show();
414     }
415
416     private void hideDetails() {
417         mShowDetails = false;
418         mDetailsHelper.hide();
419         mAlbumView.setSelectionDrawer(mGridDrawer);
420         mSlotView.invalidate();
421     }
422
423     @Override
424     protected boolean onCreateActionBar(Menu menu) {
425         Activity activity = (Activity) mActivity;
426         GalleryActionBar actionBar = mActivity.getGalleryActionBar();
427         MenuInflater inflater = activity.getMenuInflater();
428
429         if (mGetContent) {
430             inflater.inflate(R.menu.pickup, menu);
431             int typeBits = mData.getInt(Gallery.KEY_TYPE_BITS,
432                     DataManager.INCLUDE_IMAGE);
433
434             actionBar.setTitle(GalleryUtils.getSelectionModePrompt(typeBits));
435         } else {
436             inflater.inflate(R.menu.album, menu);
437             actionBar.setTitle(mMediaSet.getName());
438             if (mMediaSet instanceof MtpDevice) {
439                 menu.findItem(R.id.action_slideshow).setVisible(false);
440             } else {
441                 menu.findItem(R.id.action_slideshow).setVisible(true);
442             }
443
444             MenuItem groupBy = menu.findItem(R.id.action_group_by);
445             FilterUtils.setupMenuItems(actionBar, mMediaSetPath, true);
446
447             if (groupBy != null) {
448                 groupBy.setVisible(mShowClusterMenu);
449             }
450
451             actionBar.setTitle(mMediaSet.getName());
452         }
453         actionBar.setSubtitle(null);
454
455         return true;
456     }
457
458     @Override
459     protected boolean onItemSelected(MenuItem item) {
460         switch (item.getItemId()) {
461             case android.R.id.home: {
462                 if (mActivity.getStateManager().getStateCount() > 1) {
463                     onBackPressed();
464                 } else if (mParentMediaSetString != null) {
465                     Activity a = (Activity) mActivity;
466                     int flags = Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK;
467                     Intent intent = new Intent()
468                             .setClass(a, Gallery.class)
469                             .setFlags(flags);
470                     a.startActivity(intent);
471                 }
472                 return true;
473             }
474             case R.id.action_cancel:
475                 mActivity.getStateManager().finishState(this);
476                 return true;
477             case R.id.action_select:
478                 mSelectionManager.setAutoLeaveSelectionMode(false);
479                 mSelectionManager.enterSelectionMode();
480                 return true;
481             case R.id.action_group_by: {
482                 mActivity.getGalleryActionBar().showClusterDialog(this);
483                 return true;
484             }
485             case R.id.action_slideshow: {
486                 Bundle data = new Bundle();
487                 data.putString(SlideshowPage.KEY_SET_PATH,
488                         mMediaSetPath.toString());
489                 data.putBoolean(SlideshowPage.KEY_REPEAT, true);
490                 mActivity.getStateManager().startStateForResult(
491                         SlideshowPage.class, REQUEST_SLIDESHOW, data);
492                 return true;
493             }
494             case R.id.action_details: {
495                 if (mShowDetails) {
496                     hideDetails();
497                 } else {
498                     showDetails();
499                 }
500                 return true;
501             }
502             default:
503                 return false;
504         }
505     }
506
507     @Override
508     protected void onStateResult(int request, int result, Intent data) {
509         switch (request) {
510             case REQUEST_SLIDESHOW: {
511                 // data could be null, if there is no images in the album
512                 if (data == null) return;
513                 mFocusIndex = data.getIntExtra(SlideshowPage.KEY_PHOTO_INDEX, 0);
514                 mSlotView.setCenterIndex(mFocusIndex);
515                 break;
516             }
517             case REQUEST_PHOTO: {
518                 if (data == null) return;
519                 mFocusIndex = data.getIntExtra(PhotoPage.KEY_INDEX_HINT, 0);
520                 mSlotView.setCenterIndex(mFocusIndex);
521                 mSlotView.startRestoringAnimation(mFocusIndex);
522                 break;
523             }
524             case REQUEST_DO_ANIMATION: {
525                 mSlotView.startRisingAnimation();
526                 break;
527             }
528         }
529     }
530
531     public void onSelectionModeChange(int mode) {
532         switch (mode) {
533             case SelectionManager.ENTER_SELECTION_MODE: {
534                 mActionMode = mActionModeHandler.startActionMode();
535                 mVibrator.vibrate(100);
536                 break;
537             }
538             case SelectionManager.LEAVE_SELECTION_MODE: {
539                 mActionMode.finish();
540                 mRootPane.invalidate();
541                 break;
542             }
543             case SelectionManager.SELECT_ALL_MODE: {
544                 mActionModeHandler.updateSupportedOperation();
545                 mRootPane.invalidate();
546                 break;
547             }
548         }
549     }
550
551     public void onSelectionChange(Path path, boolean selected) {
552         Utils.assertTrue(mActionMode != null);
553         int count = mSelectionManager.getSelectedCount();
554         String format = mActivity.getResources().getQuantityString(
555                 R.plurals.number_of_items_selected, count);
556         mActionModeHandler.setTitle(String.format(format, count));
557         mActionModeHandler.updateSupportedOperation(path, selected);
558     }
559
560     @Override
561     public void onSyncDone(final MediaSet mediaSet, final int resultCode) {
562         Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) + " result="
563                 + resultCode);
564         ((Activity) mActivity).runOnUiThread(new Runnable() {
565             @Override
566             public void run() {
567                 if (resultCode == MediaSet.SYNC_RESULT_SUCCESS) {
568                     mInitialSynced = true;
569                 }
570                 clearLoadingBit(BIT_LOADING_SYNC);
571                 if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive) {
572                     Toast.makeText((Context) mActivity, R.string.sync_album_error,
573                             Toast.LENGTH_LONG).show();
574                 }
575             }
576         });
577     }
578
579     private void setLoadingBit(int loadTaskBit) {
580         if (mLoadingBits == 0 && mIsActive) {
581             GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
582         }
583         mLoadingBits |= loadTaskBit;
584     }
585
586     private void clearLoadingBit(int loadTaskBit) {
587         mLoadingBits &= ~loadTaskBit;
588         if (mLoadingBits == 0 && mIsActive) {
589             GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
590
591             if (mAlbumDataAdapter.size() == 0) {
592                 Toast.makeText((Context) mActivity,
593                         R.string.empty_album, Toast.LENGTH_LONG).show();
594                 mActivity.getStateManager().finishState(AlbumPage.this);
595             }
596         }
597     }
598
599     private class MyLoadingListener implements LoadingListener {
600         @Override
601         public void onLoadingStarted() {
602             setLoadingBit(BIT_LOADING_RELOAD);
603         }
604
605         @Override
606         public void onLoadingFinished() {
607             clearLoadingBit(BIT_LOADING_RELOAD);
608         }
609     }
610
611     private class MyDetailsSource implements DetailsHelper.DetailsSource {
612         private int mIndex;
613
614         public int size() {
615             return mAlbumDataAdapter.size();
616         }
617
618         public int getIndex() {
619             return mIndex;
620         }
621
622         // If requested index is out of active window, suggest a valid index.
623         // If there is no valid index available, return -1.
624         public int findIndex(int indexHint) {
625             if (mAlbumDataAdapter.isActive(indexHint)) {
626                 mIndex = indexHint;
627             } else {
628                 mIndex = mAlbumDataAdapter.getActiveStart();
629                 if (!mAlbumDataAdapter.isActive(mIndex)) {
630                     return -1;
631                 }
632             }
633             return mIndex;
634         }
635
636         public MediaDetails getDetails() {
637             MediaObject item = mAlbumDataAdapter.get(mIndex);
638             if (item != null) {
639                 mHighlightDrawer.setHighlightItem(item.getPath());
640                 return item.getDetails();
641             } else {
642                 return null;
643             }
644         }
645     }
646 }