OSDN Git Service

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