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