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