AlbumSetPage.java revision e9ae772f3400d53d24ce3d2834bd5866de599406
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.slotWidth, config.slotHeight,
317                config.displayItemSize, config.labelFontSize,
318                config.labelOffsetY, config.labelMargin);
319        mAlbumSetView.setListener(new SlotView.SimpleListener() {
320            @Override
321            public void onSingleTapUp(int slotIndex) {
322                AlbumSetPage.this.onSingleTapUp(slotIndex);
323            }
324            @Override
325            public void onLongTap(int slotIndex) {
326                AlbumSetPage.this.onLongTap(slotIndex);
327            }
328        });
329
330        mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
331        mActionModeHandler.setActionModeListener(new ActionModeListener() {
332            public boolean onActionItemClicked(MenuItem item) {
333                return onItemSelected(item);
334            }
335        });
336        mRootPane.addComponent(mAlbumSetView);
337
338        mStaticBackground.setImage(R.drawable.background,
339                R.drawable.background_portrait);
340    }
341
342    @Override
343    protected boolean onCreateActionBar(Menu menu) {
344        Activity activity = (Activity) mActivity;
345        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
346        MenuInflater inflater = activity.getMenuInflater();
347
348        final boolean inAlbum = mActivity.getStateManager().hasStateClass(
349                AlbumPage.class);
350
351        if (mGetContent) {
352            inflater.inflate(R.menu.pickup, menu);
353            int typeBits = mData.getInt(
354                    Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE);
355            int id = R.string.select_image;
356            if ((typeBits & DataManager.INCLUDE_VIDEO) != 0) {
357                id = (typeBits & DataManager.INCLUDE_IMAGE) == 0
358                        ? R.string.select_video
359                        : R.string.select_item;
360            }
361            actionBar.setTitle(id);
362        } else  if (mGetAlbum) {
363            inflater.inflate(R.menu.pickup, menu);
364            actionBar.setTitle(R.string.select_album);
365        } else {
366            mShowClusterTabs = !inAlbum;
367            inflater.inflate(R.menu.albumset, menu);
368            if (mTitle != null) {
369                actionBar.setTitle(mTitle);
370            } else {
371                actionBar.setTitle(activity.getApplicationInfo().labelRes);
372            }
373            MenuItem selectItem = menu.findItem(R.id.action_select);
374
375            if (selectItem != null) {
376                boolean selectAlbums = !inAlbum &&
377                        actionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM;
378                if (selectAlbums) {
379                    selectItem.setTitle(R.string.select_album);
380                } else {
381                    selectItem.setTitle(R.string.select_group);
382                }
383            }
384
385            MenuItem switchCamera = menu.findItem(R.id.action_camera);
386            if (switchCamera != null) {
387                switchCamera.setVisible(GalleryUtils.isCameraAvailable(activity));
388            }
389
390            actionBar.setSubtitle(mSubtitle);
391        }
392        return true;
393    }
394
395    @Override
396    protected boolean onItemSelected(MenuItem item) {
397        Activity activity = (Activity) mActivity;
398        switch (item.getItemId()) {
399            case R.id.action_cancel:
400                activity.setResult(Activity.RESULT_CANCELED);
401                activity.finish();
402                return true;
403            case R.id.action_select:
404                mSelectionManager.setAutoLeaveSelectionMode(false);
405                mSelectionManager.enterSelectionMode();
406                return true;
407            case R.id.action_details:
408                if (mAlbumSetDataAdapter.size() != 0) {
409                    if (mShowDetails) {
410                        hideDetails();
411                    } else {
412                        showDetails();
413                    }
414                } else {
415                    Toast.makeText(activity,
416                            activity.getText(R.string.no_albums_alert),
417                            Toast.LENGTH_SHORT).show();
418                }
419                return true;
420            case R.id.action_camera: {
421                Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
422                        .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
423                        | Intent.FLAG_ACTIVITY_NEW_TASK);
424                activity.startActivity(intent);
425                return true;
426            }
427            case R.id.action_manage_offline: {
428                Bundle data = new Bundle();
429                String mediaPath = mActivity.getDataManager().getTopSetPath(
430                    DataManager.INCLUDE_ALL);
431                data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
432                mActivity.getStateManager().startState(ManageCachePage.class, data);
433                return true;
434            }
435            case R.id.action_sync_picasa_albums: {
436                PicasaSource.requestSync(activity);
437                return true;
438            }
439            case R.id.action_settings: {
440                activity.startActivity(new Intent(activity, GallerySettings.class));
441                return true;
442            }
443            default:
444                return false;
445        }
446    }
447
448    @Override
449    protected void onStateResult(int requestCode, int resultCode, Intent data) {
450        switch (requestCode) {
451            case REQUEST_DO_ANIMATION: {
452                startTransition();
453            }
454        }
455    }
456
457    private void startTransition() {
458        final PositionRepository repository =
459                PositionRepository.getInstance(mActivity);
460        mAlbumSetView.startTransition(new PositionProvider() {
461            private Position mTempPosition = new Position();
462            public Position getPosition(long identity, Position target) {
463                Position p = repository.get(identity);
464                if (p == null) {
465                    p = mTempPosition;
466                    p.set(target.x, target.y, 128, target.theta, 1);
467                }
468                return p;
469            }
470        });
471    }
472
473    private String getSelectedString() {
474        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
475        int count = mSelectionManager.getSelectedCount();
476        int action = actionBar.getClusterTypeAction();
477        int string = action == FilterUtils.CLUSTER_BY_ALBUM
478                ? R.plurals.number_of_albums_selected
479                : R.plurals.number_of_groups_selected;
480        String format = mActivity.getResources().getQuantityString(string, count);
481        return String.format(format, count);
482    }
483
484    public void onSelectionModeChange(int mode) {
485
486        switch (mode) {
487            case SelectionManager.ENTER_SELECTION_MODE: {
488                mActivity.getGalleryActionBar().hideClusterTabs();
489                mActionMode = mActionModeHandler.startActionMode();
490                break;
491            }
492            case SelectionManager.LEAVE_SELECTION_MODE: {
493                mActionMode.finish();
494                mActivity.getGalleryActionBar().showClusterTabs(this);
495                mRootPane.invalidate();
496                break;
497            }
498            case SelectionManager.SELECT_ALL_MODE: {
499                mActionModeHandler.setTitle(getSelectedString());
500                mRootPane.invalidate();
501                break;
502            }
503        }
504    }
505
506    public void onSelectionChange(Path path, boolean selected) {
507        Utils.assertTrue(mActionMode != null);
508        mActionModeHandler.setTitle(getSelectedString());
509        mActionModeHandler.updateSupportedOperation(path, selected);
510    }
511
512    private void hideDetails() {
513        mShowDetails = false;
514        mDetailsHelper.hide();
515        mAlbumSetView.setSelectionDrawer(mGridDrawer);
516        mAlbumSetView.invalidate();
517    }
518
519    private void showDetails() {
520        mShowDetails = true;
521        if (mDetailsHelper == null) {
522            mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext());
523            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
524            mDetailsHelper.setCloseListener(new CloseListener() {
525                public void onClose() {
526                    hideDetails();
527                }
528            });
529        }
530        mAlbumSetView.setSelectionDrawer(mHighlightDrawer);
531        mDetailsHelper.show();
532    }
533
534    private class MyLoadingListener implements LoadingListener {
535        public void onLoadingStarted() {
536            GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
537        }
538
539        public void onLoadingFinished() {
540            if (!mIsActive) return;
541            GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
542            if (mAlbumSetDataAdapter.size() == 0) {
543                Toast.makeText((Context) mActivity,
544                        R.string.empty_album, Toast.LENGTH_LONG).show();
545                if (mActivity.getStateManager().getStateCount() > 1) {
546                    mActivity.getStateManager().finishState(AlbumSetPage.this);
547                }
548            }
549        }
550    }
551
552    private class MyDetailsSource implements DetailsHelper.DetailsSource {
553        private int mIndex;
554        public int size() {
555            return mAlbumSetDataAdapter.size();
556        }
557
558        public int getIndex() {
559            return mIndex;
560        }
561
562        // If requested index is out of active window, suggest a valid index.
563        // If there is no valid index available, return -1.
564        public int findIndex(int indexHint) {
565            if (mAlbumSetDataAdapter.isActive(indexHint)) {
566                mIndex = indexHint;
567            } else {
568                mIndex = mAlbumSetDataAdapter.getActiveStart();
569                if (!mAlbumSetDataAdapter.isActive(mIndex)) {
570                    return -1;
571                }
572            }
573            return mIndex;
574        }
575
576        public MediaDetails getDetails() {
577            MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex);
578            if (item != null) {
579                mHighlightDrawer.setHighlightItem(item.getPath());
580                return item.getDetails();
581            } else {
582                return null;
583            }
584        }
585    }
586}
587