AlbumSetPage.java revision 20cb268c47ce382859c6b3245e71a73e05a629d4
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
17package com.android.gallery3d.app;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.graphics.Rect;
23import android.os.Bundle;
24import android.os.Vibrator;
25import android.provider.MediaStore;
26import android.view.ActionMode;
27import android.view.Menu;
28import android.view.MenuInflater;
29import android.view.MenuItem;
30import android.widget.Toast;
31
32import com.android.gallery3d.R;
33import com.android.gallery3d.common.Utils;
34import com.android.gallery3d.data.DataManager;
35import com.android.gallery3d.data.MediaDetails;
36import com.android.gallery3d.data.MediaObject;
37import com.android.gallery3d.data.MediaSet;
38import com.android.gallery3d.data.Path;
39import com.android.gallery3d.picasasource.PicasaSource;
40import com.android.gallery3d.settings.GallerySettings;
41import com.android.gallery3d.ui.ActionModeHandler;
42import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
43import com.android.gallery3d.ui.AlbumSetView;
44import com.android.gallery3d.ui.DetailsHelper;
45import com.android.gallery3d.ui.DetailsHelper.CloseListener;
46import com.android.gallery3d.ui.GLCanvas;
47import com.android.gallery3d.ui.GLView;
48import com.android.gallery3d.ui.GridDrawer;
49import com.android.gallery3d.ui.HighlightDrawer;
50import com.android.gallery3d.ui.PositionProvider;
51import com.android.gallery3d.ui.PositionRepository;
52import com.android.gallery3d.ui.PositionRepository.Position;
53import com.android.gallery3d.ui.SelectionManager;
54import com.android.gallery3d.ui.SlotView;
55import com.android.gallery3d.ui.StaticBackground;
56import com.android.gallery3d.util.GalleryUtils;
57
58public class AlbumSetPage extends ActivityState implements
59        SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner,
60        EyePosition.EyePositionListener {
61    @SuppressWarnings("unused")
62    private static final String TAG = "AlbumSetPage";
63
64    public static final String KEY_MEDIA_PATH = "media-path";
65    public static final String KEY_SET_TITLE = "set-title";
66    public static final String KEY_SET_SUBTITLE = "set-subtitle";
67    public static final String KEY_SELECTED_CLUSTER_TYPE = "selected-cluster";
68
69    private static final int DATA_CACHE_SIZE = 256;
70    private static final int REQUEST_DO_ANIMATION = 1;
71    private static final int MSG_GOTO_MANAGE_CACHE_PAGE = 1;
72
73    private boolean mIsActive = false;
74    private StaticBackground mStaticBackground;
75    private AlbumSetView mAlbumSetView;
76
77    private MediaSet mMediaSet;
78    private String mSubtitle;
79    private boolean mShowClusterMenu;
80    private int mSelectedAction;
81    private Vibrator mVibrator;
82
83    protected SelectionManager mSelectionManager;
84    private AlbumSetDataAdapter mAlbumSetDataAdapter;
85    private GridDrawer mGridDrawer;
86    private HighlightDrawer mHighlightDrawer;
87
88    private boolean mGetContent;
89    private boolean mGetAlbum;
90    private ActionMode mActionMode;
91    private ActionModeHandler mActionModeHandler;
92    private DetailsHelper mDetailsHelper;
93    private MyDetailsSource mDetailsSource;
94    private boolean mShowDetails;
95    private EyePosition mEyePosition;
96
97    // The eyes' position of the user, the origin is at the center of the
98    // device and the unit is in pixels.
99    private float mX;
100    private float mY;
101    private float mZ;
102
103    private GLView mRootPane = new GLView() {
104        private float mMatrix[] = new float[16];
105
106        @Override
107        protected void onLayout(
108                boolean changed, int left, int top, int right, int bottom) {
109            mStaticBackground.layout(0, 0, right - left, bottom - top);
110            mEyePosition.resetPosition();
111
112            int slotViewTop = GalleryActionBar.getHeight((Activity) mActivity);
113            int slotViewBottom = bottom - top;
114            int slotViewRight = right - left;
115
116            if (mShowDetails) {
117                mDetailsHelper.layout(left, slotViewTop, right, bottom);
118            } else {
119                mAlbumSetView.setSelectionDrawer(mGridDrawer);
120            }
121
122            mAlbumSetView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
123            PositionRepository.getInstance(mActivity).setOffset(
124                    0, slotViewTop);
125        }
126
127        @Override
128        protected void render(GLCanvas canvas) {
129            canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
130            GalleryUtils.setViewPointMatrix(mMatrix,
131                    getWidth() / 2 + mX, getHeight() / 2 + mY, mZ);
132            canvas.multiplyMatrix(mMatrix, 0);
133            super.render(canvas);
134            canvas.restore();
135        }
136    };
137
138    @Override
139    public void onEyePositionChanged(float x, float y, float z) {
140        mRootPane.lockRendering();
141        mX = x;
142        mY = y;
143        mZ = z;
144        mRootPane.unlockRendering();
145        mRootPane.invalidate();
146    }
147
148    @Override
149    public void onBackPressed() {
150        if (mShowDetails) {
151            hideDetails();
152        } else if (mSelectionManager.inSelectionMode()) {
153            mSelectionManager.leaveSelectionMode();
154        } else {
155            mAlbumSetView.savePositions(
156                    PositionRepository.getInstance(mActivity));
157            super.onBackPressed();
158        }
159    }
160
161    private void savePositions(int slotIndex, int center[]) {
162        Rect offset = new Rect();
163        mRootPane.getBoundsOf(mAlbumSetView, offset);
164        mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
165        Rect r = mAlbumSetView.getSlotRect(slotIndex);
166        int scrollX = mAlbumSetView.getScrollX();
167        int scrollY = mAlbumSetView.getScrollY();
168        center[0] = offset.left + (r.left + r.right) / 2 - scrollX;
169        center[1] = offset.top + (r.top + r.bottom) / 2 - scrollY;
170    }
171
172    public void onSingleTapUp(int slotIndex) {
173        MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
174        if (targetSet == null) return; // Content is dirty, we shall reload soon
175
176        if (mShowDetails) {
177            Path path = targetSet.getPath();
178            mHighlightDrawer.setHighlightItem(path);
179            mDetailsHelper.reloadDetails(slotIndex);
180        } else if (!mSelectionManager.inSelectionMode()) {
181            Bundle data = new Bundle(getData());
182            String mediaPath = targetSet.getPath().toString();
183            int[] center = new int[2];
184            savePositions(slotIndex, center);
185            data.putIntArray(AlbumPage.KEY_SET_CENTER, center);
186            if (mGetAlbum && targetSet.isLeafAlbum()) {
187                Activity activity = (Activity) mActivity;
188                Intent result = new Intent()
189                        .putExtra(AlbumPicker.KEY_ALBUM_PATH, targetSet.getPath().toString());
190                activity.setResult(Activity.RESULT_OK, result);
191                activity.finish();
192            } else if (targetSet.getSubMediaSetCount() > 0) {
193                data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
194                mActivity.getStateManager().startStateForResult(
195                        AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
196            } else {
197                if (!mGetContent && (targetSet.getSupportedOperations()
198                        & MediaObject.SUPPORT_IMPORT) != 0) {
199                    data.putBoolean(AlbumPage.KEY_AUTO_SELECT_ALL, true);
200                }
201                data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath);
202                boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
203                // We only show cluster menu in the first AlbumPage in stack
204                data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum);
205                mActivity.getStateManager().startStateForResult(
206                        AlbumPage.class, REQUEST_DO_ANIMATION, data);
207            }
208        } else {
209            mSelectionManager.toggle(targetSet.getPath());
210            mAlbumSetView.invalidate();
211        }
212    }
213
214    private void onDown(int index) {
215        MediaSet set = mAlbumSetDataAdapter.getMediaSet(index);
216        Path path = (set == null) ? null : set.getPath();
217        mSelectionManager.setPressedPath(path);
218        mAlbumSetView.invalidate();
219    }
220
221    private void onUp() {
222        mSelectionManager.setPressedPath(null);
223        mAlbumSetView.invalidate();
224    }
225
226    public void onLongTap(int slotIndex) {
227        if (mGetContent || mGetAlbum) return;
228        if (mShowDetails) {
229            onSingleTapUp(slotIndex);
230        } else {
231            MediaSet set = mAlbumSetDataAdapter.getMediaSet(slotIndex);
232            if (set == null) return;
233            mSelectionManager.setAutoLeaveSelectionMode(true);
234            mSelectionManager.toggle(set.getPath());
235            mDetailsSource.findIndex(slotIndex);
236            mAlbumSetView.invalidate();
237        }
238    }
239
240    public void doCluster(int clusterType) {
241        Log.v(TAG, "doCluster: " + clusterType);
242        String basePath = mMediaSet.getPath().toString();
243        String newPath = FilterUtils.switchClusterPath(basePath, clusterType);
244        Bundle data = new Bundle(getData());
245        data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
246        data.putInt(KEY_SELECTED_CLUSTER_TYPE, clusterType);
247        mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
248        mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
249    }
250
251    public void doFilter(int filterType) {
252        String basePath = mMediaSet.getPath().toString();
253        String newPath = FilterUtils.switchFilterPath(basePath, filterType);
254        Bundle data = new Bundle(getData());
255        data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
256        mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
257        mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
258    }
259
260    public void onOperationComplete() {
261        mAlbumSetView.invalidate();
262        // TODO: enable animation
263    }
264
265    @Override
266    public void onCreate(Bundle data, Bundle restoreState) {
267        initializeViews();
268        initializeData(data);
269        Context context = mActivity.getAndroidContext();
270        mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
271        mGetAlbum = data.getBoolean(Gallery.KEY_GET_ALBUM, false);
272        mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
273        mEyePosition = new EyePosition(context, this);
274        mDetailsSource = new MyDetailsSource();
275        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
276        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
277        if (actionBar != null) {
278            mSelectedAction = data.getInt(
279                    AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE, FilterUtils.CLUSTER_BY_ALBUM);
280        }
281        startTransition();
282    }
283
284    @Override
285    public void onPause() {
286        super.onPause();
287        Log.v(TAG, "onPause");
288        mIsActive = false;
289        mActionModeHandler.pause();
290        mAlbumSetDataAdapter.pause();
291        mAlbumSetView.pause();
292        mEyePosition.pause();
293        DetailsHelper.pause();
294        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
295        if (actionBar != null) actionBar.hideClusterMenu();
296    }
297
298    @Override
299    public void onResume() {
300        super.onResume();
301        Log.v(TAG, "onResume");
302        mIsActive = true;
303        setContentPane(mRootPane);
304        mAlbumSetDataAdapter.resume();
305        mAlbumSetView.resume();
306        mEyePosition.resume();
307        mActionModeHandler.resume();
308        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
309        if (mShowClusterMenu && actionBar != null) actionBar.showClusterMenu(mSelectedAction, this);
310    }
311
312    private void initializeData(Bundle data) {
313        String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);
314        mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
315        mSelectionManager.setSourceMediaSet(mMediaSet);
316        mAlbumSetDataAdapter = new AlbumSetDataAdapter(
317                mActivity, mMediaSet, DATA_CACHE_SIZE);
318        mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
319        mAlbumSetView.setModel(mAlbumSetDataAdapter);
320    }
321
322    private void initializeViews() {
323        mSelectionManager = new SelectionManager(mActivity, true);
324        mSelectionManager.setSelectionListener(this);
325        mStaticBackground = new StaticBackground(mActivity.getAndroidContext());
326        mRootPane.addComponent(mStaticBackground);
327
328        mGridDrawer = new GridDrawer((Context) mActivity, mSelectionManager);
329        Config.AlbumSetPage config = Config.AlbumSetPage.get((Context) mActivity);
330        mAlbumSetView = new AlbumSetView(mActivity, mGridDrawer,
331                config.slotViewSpec, config.labelSpec);
332        mAlbumSetView.setListener(new SlotView.SimpleListener() {
333            @Override
334            public void onDown(int index) {
335                AlbumSetPage.this.onDown(index);
336            }
337
338            @Override
339            public void onUp() {
340                AlbumSetPage.this.onUp();
341            }
342
343            @Override
344            public void onSingleTapUp(int slotIndex) {
345                AlbumSetPage.this.onSingleTapUp(slotIndex);
346            }
347
348            @Override
349            public void onLongTap(int slotIndex) {
350                AlbumSetPage.this.onLongTap(slotIndex);
351            }
352        });
353
354        mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
355        mActionModeHandler.setActionModeListener(new ActionModeListener() {
356            public boolean onActionItemClicked(MenuItem item) {
357                return onItemSelected(item);
358            }
359        });
360        mRootPane.addComponent(mAlbumSetView);
361
362        mStaticBackground.setImage(R.drawable.background,
363                R.drawable.background_portrait);
364    }
365
366    @Override
367    protected boolean onCreateActionBar(Menu menu) {
368        Activity activity = (Activity) mActivity;
369        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
370        MenuInflater inflater = activity.getMenuInflater();
371
372        final boolean inAlbum = mActivity.getStateManager().hasStateClass(
373                AlbumPage.class);
374
375        if (mGetContent) {
376            inflater.inflate(R.menu.pickup, menu);
377            int typeBits = mData.getInt(
378                    Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE);
379            int id = R.string.select_image;
380            if ((typeBits & DataManager.INCLUDE_VIDEO) != 0) {
381                id = (typeBits & DataManager.INCLUDE_IMAGE) == 0
382                        ? R.string.select_video
383                        : R.string.select_item;
384            }
385            actionBar.setTitle(id);
386        } else  if (mGetAlbum) {
387            inflater.inflate(R.menu.pickup, menu);
388            actionBar.setTitle(R.string.select_album);
389        } else {
390            mShowClusterMenu = !inAlbum;
391            inflater.inflate(R.menu.albumset, menu);
392            actionBar.setTitle(null);
393            MenuItem selectItem = menu.findItem(R.id.action_select);
394
395            if (selectItem != null) {
396                boolean selectAlbums = !inAlbum &&
397                        actionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM;
398                if (selectAlbums) {
399                    selectItem.setTitle(R.string.select_album);
400                } else {
401                    selectItem.setTitle(R.string.select_group);
402                }
403            }
404
405            MenuItem switchCamera = menu.findItem(R.id.action_camera);
406            if (switchCamera != null) {
407                switchCamera.setVisible(GalleryUtils.isCameraAvailable(activity));
408            }
409
410            actionBar.setSubtitle(mSubtitle);
411        }
412        return true;
413    }
414
415    @Override
416    protected boolean onItemSelected(MenuItem item) {
417        Activity activity = (Activity) mActivity;
418        switch (item.getItemId()) {
419            case R.id.action_cancel:
420                activity.setResult(Activity.RESULT_CANCELED);
421                activity.finish();
422                return true;
423            case R.id.action_select:
424                mSelectionManager.setAutoLeaveSelectionMode(false);
425                mSelectionManager.enterSelectionMode();
426                return true;
427            case R.id.action_details:
428                if (mAlbumSetDataAdapter.size() != 0) {
429                    if (mShowDetails) {
430                        hideDetails();
431                    } else {
432                        showDetails();
433                    }
434                } else {
435                    Toast.makeText(activity,
436                            activity.getText(R.string.no_albums_alert),
437                            Toast.LENGTH_SHORT).show();
438                }
439                return true;
440            case R.id.action_camera: {
441                Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
442                        .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
443                        | Intent.FLAG_ACTIVITY_NEW_TASK);
444                activity.startActivity(intent);
445                return true;
446            }
447            case R.id.action_manage_offline: {
448                Bundle data = new Bundle();
449                String mediaPath = mActivity.getDataManager().getTopSetPath(
450                    DataManager.INCLUDE_ALL);
451                data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
452                mActivity.getStateManager().startState(ManageCachePage.class, data);
453                return true;
454            }
455            case R.id.action_sync_picasa_albums: {
456                PicasaSource.requestSync(activity);
457                return true;
458            }
459            case R.id.action_settings: {
460                activity.startActivity(new Intent(activity, GallerySettings.class));
461                return true;
462            }
463            default:
464                return false;
465        }
466    }
467
468    @Override
469    protected void onStateResult(int requestCode, int resultCode, Intent data) {
470        switch (requestCode) {
471            case REQUEST_DO_ANIMATION: {
472                startTransition();
473            }
474        }
475    }
476
477    private void startTransition() {
478        final PositionRepository repository =
479                PositionRepository.getInstance(mActivity);
480        mAlbumSetView.startTransition(new PositionProvider() {
481            private Position mTempPosition = new Position();
482            public Position getPosition(long identity, Position target) {
483                Position p = repository.get(identity);
484                if (p == null) {
485                    p = mTempPosition;
486                    p.set(target.x, target.y, 128, target.theta, 1);
487                }
488                return p;
489            }
490        });
491    }
492
493    private String getSelectedString() {
494        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
495        int count = mSelectionManager.getSelectedCount();
496        int action = actionBar.getClusterTypeAction();
497        int string = action == FilterUtils.CLUSTER_BY_ALBUM
498                ? R.plurals.number_of_albums_selected
499                : R.plurals.number_of_groups_selected;
500        String format = mActivity.getResources().getQuantityString(string, count);
501        return String.format(format, count);
502    }
503
504    public void onSelectionModeChange(int mode) {
505
506        switch (mode) {
507            case SelectionManager.ENTER_SELECTION_MODE: {
508                mActivity.getGalleryActionBar().hideClusterMenu();
509                mActionMode = mActionModeHandler.startActionMode();
510                mVibrator.vibrate(100);
511                break;
512            }
513            case SelectionManager.LEAVE_SELECTION_MODE: {
514                mActionMode.finish();
515                mActivity.getGalleryActionBar().showClusterMenu(mSelectedAction, this);
516                mRootPane.invalidate();
517                break;
518            }
519            case SelectionManager.SELECT_ALL_MODE: {
520                mActionModeHandler.setTitle(getSelectedString());
521                mRootPane.invalidate();
522                break;
523            }
524        }
525    }
526
527    public void onSelectionChange(Path path, boolean selected) {
528        Utils.assertTrue(mActionMode != null);
529        mActionModeHandler.setTitle(getSelectedString());
530        mActionModeHandler.updateSupportedOperation(path, selected);
531    }
532
533    private void hideDetails() {
534        mShowDetails = false;
535        mDetailsHelper.hide();
536        mAlbumSetView.setSelectionDrawer(mGridDrawer);
537        mAlbumSetView.invalidate();
538    }
539
540    private void showDetails() {
541        mShowDetails = true;
542        if (mDetailsHelper == null) {
543            mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext(),
544                    mSelectionManager);
545            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
546            mDetailsHelper.setCloseListener(new CloseListener() {
547                public void onClose() {
548                    hideDetails();
549                }
550            });
551        }
552        mAlbumSetView.setSelectionDrawer(mHighlightDrawer);
553        mDetailsHelper.show();
554    }
555
556    private class MyLoadingListener implements LoadingListener {
557        public void onLoadingStarted() {
558            GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
559        }
560
561        public void onLoadingFinished() {
562            if (!mIsActive) return;
563            GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
564            if (mAlbumSetDataAdapter.size() == 0) {
565                Toast.makeText((Context) mActivity,
566                        R.string.empty_album, Toast.LENGTH_LONG).show();
567                if (mActivity.getStateManager().getStateCount() > 1) {
568                    mActivity.getStateManager().finishState(AlbumSetPage.this);
569                }
570            }
571        }
572    }
573
574    private class MyDetailsSource implements DetailsHelper.DetailsSource {
575        private int mIndex;
576        public int size() {
577            return mAlbumSetDataAdapter.size();
578        }
579
580        public int getIndex() {
581            return mIndex;
582        }
583
584        // If requested index is out of active window, suggest a valid index.
585        // If there is no valid index available, return -1.
586        public int findIndex(int indexHint) {
587            if (mAlbumSetDataAdapter.isActive(indexHint)) {
588                mIndex = indexHint;
589            } else {
590                mIndex = mAlbumSetDataAdapter.getActiveStart();
591                if (!mAlbumSetDataAdapter.isActive(mIndex)) {
592                    return -1;
593                }
594            }
595            return mIndex;
596        }
597
598        public MediaDetails getDetails() {
599            MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex);
600            if (item != null) {
601                mHighlightDrawer.setHighlightItem(item.getPath());
602                return item.getDetails();
603            } else {
604                return null;
605            }
606        }
607    }
608}
609