AlbumSetPage.java revision 4105e22d72d7a44e67cba4a58ad88018ed664d10
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.ui.SynchronizedHandler;
44import com.android.gallery3d.util.GalleryUtils;
45
46import android.app.Activity;
47import android.content.Context;
48import android.content.Intent;
49import android.graphics.Rect;
50import android.os.Bundle;
51import android.os.Message;
52import android.provider.MediaStore;
53import android.view.ActionMode;
54import android.view.Menu;
55import android.view.MenuInflater;
56import android.view.MenuItem;
57import android.view.View.MeasureSpec;
58import android.widget.Toast;
59
60public class AlbumSetPage extends ActivityState implements
61        SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner,
62        EyePosition.EyePositionListener {
63    @SuppressWarnings("unused")
64    private static final String TAG = "AlbumSetPage";
65
66    public static final String KEY_MEDIA_PATH = "media-path";
67    public static final String KEY_SET_TITLE = "set-title";
68    public static final String KEY_SET_SUBTITLE = "set-subtitle";
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 mShowClusterTabs;
81
82    protected SelectionManager mSelectionManager;
83    private AlbumSetDataAdapter mAlbumSetDataAdapter;
84    private GridDrawer mGridDrawer;
85    private HighlightDrawer mHighlightDrawer;
86
87    private boolean mGetContent;
88    private boolean mGetAlbum;
89    private ActionMode mActionMode;
90    private ActionModeHandler mActionModeHandler;
91    private DetailsHelper mDetailsHelper;
92    private MyDetailsSource mDetailsSource;
93    private boolean mShowDetails;
94    private EyePosition mEyePosition;
95
96    // The eyes' position of the user, the origin is at the center of the
97    // device and the unit is in pixels.
98    private float mX;
99    private float mY;
100    private float mZ;
101
102    private SynchronizedHandler mHandler;
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    public void onLongTap(int slotIndex) {
216        if (mGetContent || mGetAlbum) return;
217        if (mShowDetails) {
218            onSingleTapUp(slotIndex);
219        } else {
220            MediaSet set = mAlbumSetDataAdapter.getMediaSet(slotIndex);
221            if (set == null) return;
222            mSelectionManager.setAutoLeaveSelectionMode(true);
223            mSelectionManager.toggle(set.getPath());
224            mDetailsSource.findIndex(slotIndex);
225            mAlbumSetView.invalidate();
226        }
227    }
228
229    public void doCluster(int clusterType) {
230        String basePath = mMediaSet.getPath().toString();
231        String newPath = FilterUtils.switchClusterPath(basePath, clusterType);
232        Bundle data = new Bundle(getData());
233        data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
234        mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
235        mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
236    }
237
238    public void doFilter(int filterType) {
239        String basePath = mMediaSet.getPath().toString();
240        String newPath = FilterUtils.switchFilterPath(basePath, filterType);
241        Bundle data = new Bundle(getData());
242        data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
243        mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
244        mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
245    }
246
247    public void onOperationComplete() {
248        mAlbumSetView.invalidate();
249        // TODO: enable animation
250    }
251
252    @Override
253    public void onCreate(Bundle data, Bundle restoreState) {
254        mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
255            @Override
256            public void handleMessage(Message message) {
257                Utils.assertTrue(message.what == MSG_GOTO_MANAGE_CACHE_PAGE);
258                Bundle data = new Bundle();
259                String mediaPath = mActivity.getDataManager().getTopSetPath(
260                    DataManager.INCLUDE_ALL);
261                data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
262                mActivity.getStateManager().startState(ManageCachePage.class, data);
263            }
264        };
265
266        initializeViews();
267        initializeData(data);
268        mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
269        mGetAlbum = data.getBoolean(Gallery.KEY_GET_ALBUM, false);
270        mTitle = data.getString(AlbumSetPage.KEY_SET_TITLE);
271        mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
272        mEyePosition = new EyePosition(mActivity.getAndroidContext(), this);
273        mDetailsSource = new MyDetailsSource();
274
275        startTransition();
276    }
277
278    @Override
279    public void onPause() {
280        super.onPause();
281        mIsActive = false;
282        mActionModeHandler.pause();
283        mAlbumSetDataAdapter.pause();
284        mAlbumSetView.pause();
285        mEyePosition.pause();
286        DetailsHelper.pause();
287        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
288        if (actionBar != null) actionBar.hideClusterTabs();
289    }
290
291    @Override
292    public void onResume() {
293        super.onResume();
294        mIsActive = true;
295        setContentPane(mRootPane);
296        mAlbumSetDataAdapter.resume();
297        mAlbumSetView.resume();
298        mEyePosition.resume();
299        mActionModeHandler.resume();
300        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
301        if (mShowClusterTabs && actionBar != null) actionBar.showClusterTabs(this);
302    }
303
304    private void initializeData(Bundle data) {
305        String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);
306        mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
307        mSelectionManager.setSourceMediaSet(mMediaSet);
308        mAlbumSetDataAdapter = new AlbumSetDataAdapter(
309                mActivity, mMediaSet, DATA_CACHE_SIZE);
310        mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
311        mAlbumSetView.setModel(mAlbumSetDataAdapter);
312    }
313
314    private void initializeViews() {
315        mSelectionManager = new SelectionManager(mActivity, true);
316        mSelectionManager.setSelectionListener(this);
317        mStaticBackground = new StaticBackground(mActivity.getAndroidContext());
318        mRootPane.addComponent(mStaticBackground);
319
320        mGridDrawer = new GridDrawer((Context) mActivity, mSelectionManager);
321        Config.AlbumSetPage config = Config.AlbumSetPage.get((Context) mActivity);
322        mAlbumSetView = new AlbumSetView(mActivity, mGridDrawer,
323                config.slotWidth, config.slotHeight,
324                config.displayItemSize, config.labelFontSize,
325                config.labelOffsetY, config.labelMargin);
326        mAlbumSetView.setListener(new SlotView.SimpleListener() {
327            @Override
328            public void onSingleTapUp(int slotIndex) {
329                AlbumSetPage.this.onSingleTapUp(slotIndex);
330            }
331            @Override
332            public void onLongTap(int slotIndex) {
333                AlbumSetPage.this.onLongTap(slotIndex);
334            }
335        });
336
337        mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
338        mActionModeHandler.setActionModeListener(new ActionModeListener() {
339            public boolean onActionItemClicked(MenuItem item) {
340                return onItemSelected(item);
341            }
342        });
343        mRootPane.addComponent(mAlbumSetView);
344
345        mStaticBackground.setImage(R.drawable.background,
346                R.drawable.background_portrait);
347    }
348
349    @Override
350    protected boolean onCreateActionBar(Menu menu) {
351        Activity activity = (Activity) mActivity;
352        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
353        MenuInflater inflater = activity.getMenuInflater();
354
355        final boolean inAlbum = mActivity.getStateManager().hasStateClass(
356                AlbumPage.class);
357
358        if (mGetContent) {
359            inflater.inflate(R.menu.pickup, menu);
360            int typeBits = mData.getInt(
361                    Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE);
362            int id = R.string.select_image;
363            if ((typeBits & DataManager.INCLUDE_VIDEO) != 0) {
364                id = (typeBits & DataManager.INCLUDE_IMAGE) == 0
365                        ? R.string.select_video
366                        : R.string.select_item;
367            }
368            actionBar.setTitle(id);
369        } else  if (mGetAlbum) {
370            inflater.inflate(R.menu.pickup, menu);
371            actionBar.setTitle(R.string.select_album);
372        } else {
373            mShowClusterTabs = !inAlbum;
374            inflater.inflate(R.menu.albumset, menu);
375            if (mTitle != null) {
376                actionBar.setTitle(mTitle);
377            } else {
378                actionBar.setTitle(activity.getApplicationInfo().labelRes);
379            }
380            MenuItem selectItem = menu.findItem(R.id.action_select);
381
382            if (selectItem != null) {
383                boolean selectAlbums = !inAlbum &&
384                        actionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM;
385                if (selectAlbums) {
386                    selectItem.setTitle(R.string.select_album);
387                } else {
388                    selectItem.setTitle(R.string.select_group);
389                }
390            }
391
392            MenuItem switchCamera = menu.findItem(R.id.action_camera);
393            if (switchCamera != null) {
394                switchCamera.setVisible(GalleryUtils.isCameraAvailable(activity));
395            }
396
397            actionBar.setSubtitle(mSubtitle);
398        }
399        return true;
400    }
401
402    @Override
403    protected boolean onItemSelected(MenuItem item) {
404        Activity activity = (Activity) mActivity;
405        switch (item.getItemId()) {
406            case R.id.action_cancel:
407                activity.setResult(Activity.RESULT_CANCELED);
408                activity.finish();
409                return true;
410            case R.id.action_select:
411                mSelectionManager.setAutoLeaveSelectionMode(false);
412                mSelectionManager.enterSelectionMode();
413                return true;
414            case R.id.action_details:
415                if (mAlbumSetDataAdapter.size() != 0) {
416                    if (mShowDetails) {
417                        hideDetails();
418                    } else {
419                        showDetails();
420                    }
421                } else {
422                    Toast.makeText(activity,
423                            activity.getText(R.string.no_albums_alert),
424                            Toast.LENGTH_SHORT).show();
425                }
426                return true;
427            case R.id.action_camera: {
428                Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
429                        .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
430                        | Intent.FLAG_ACTIVITY_NEW_TASK);
431                activity.startActivity(intent);
432                return true;
433            }
434            case R.id.action_manage_offline: {
435                mHandler.sendEmptyMessage(MSG_GOTO_MANAGE_CACHE_PAGE);
436                return true;
437            }
438            case R.id.action_sync_picasa_albums: {
439                PicasaSource.requestSync(activity);
440                return true;
441            }
442            case R.id.action_settings: {
443                activity.startActivity(new Intent(activity, GallerySettings.class));
444                return true;
445            }
446            default:
447                return false;
448        }
449    }
450
451    @Override
452    protected void onStateResult(int requestCode, int resultCode, Intent data) {
453        switch (requestCode) {
454            case REQUEST_DO_ANIMATION: {
455                startTransition();
456            }
457        }
458    }
459
460    private void startTransition() {
461        final PositionRepository repository =
462                PositionRepository.getInstance(mActivity);
463        mAlbumSetView.startTransition(new PositionProvider() {
464            private Position mTempPosition = new Position();
465            public Position getPosition(long identity, Position target) {
466                Position p = repository.get(identity);
467                if (p == null) {
468                    p = mTempPosition;
469                    p.set(target.x, target.y, 128, target.theta, 1);
470                }
471                return p;
472            }
473        });
474    }
475
476    private String getSelectedString() {
477        GalleryActionBar actionBar = mActivity.getGalleryActionBar();
478        int count = mSelectionManager.getSelectedCount();
479        int action = actionBar.getClusterTypeAction();
480        int string = action == FilterUtils.CLUSTER_BY_ALBUM
481                ? R.plurals.number_of_albums_selected
482                : R.plurals.number_of_groups_selected;
483        String format = mActivity.getResources().getQuantityString(string, count);
484        return String.format(format, count);
485    }
486
487    public void onSelectionModeChange(int mode) {
488
489        switch (mode) {
490            case SelectionManager.ENTER_SELECTION_MODE: {
491                mActivity.getGalleryActionBar().hideClusterTabs();
492                mActionMode = mActionModeHandler.startActionMode();
493                break;
494            }
495            case SelectionManager.LEAVE_SELECTION_MODE: {
496                mActionMode.finish();
497                mActivity.getGalleryActionBar().showClusterTabs(this);
498                mRootPane.invalidate();
499                break;
500            }
501            case SelectionManager.SELECT_ALL_MODE: {
502                mActionModeHandler.setTitle(getSelectedString());
503                mRootPane.invalidate();
504                break;
505            }
506        }
507    }
508
509    public void onSelectionChange(Path path, boolean selected) {
510        Utils.assertTrue(mActionMode != null);
511        mActionModeHandler.setTitle(getSelectedString());
512        mActionModeHandler.updateSupportedOperation(path, selected);
513    }
514
515    private void hideDetails() {
516        mShowDetails = false;
517        mAlbumSetView.setSelectionDrawer(mGridDrawer);
518        mDetailsHelper.hide();
519    }
520
521    private void showDetails() {
522        mShowDetails = true;
523        if (mDetailsHelper == null) {
524            mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext());
525            mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
526            mDetailsHelper.setCloseListener(new CloseListener() {
527                public void onClose() {
528                    hideDetails();
529                }
530            });
531        }
532        mAlbumSetView.setSelectionDrawer(mHighlightDrawer);
533        mDetailsHelper.show();
534    }
535
536    private class MyLoadingListener implements LoadingListener {
537        public void onLoadingStarted() {
538            GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
539        }
540
541        public void onLoadingFinished() {
542            if (!mIsActive) return;
543            GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
544            if (mAlbumSetDataAdapter.size() == 0) {
545                Toast.makeText((Context) mActivity,
546                        R.string.empty_album, Toast.LENGTH_LONG).show();
547                if (mActivity.getStateManager().getStateCount() > 1) {
548                    mActivity.getStateManager().finishState(AlbumSetPage.this);
549                }
550            }
551        }
552    }
553
554    private class MyDetailsSource implements DetailsHelper.DetailsSource {
555        private int mIndex;
556        public int size() {
557            return mAlbumSetDataAdapter.size();
558        }
559
560        public int getIndex() {
561            return mIndex;
562        }
563
564        // If requested index is out of active window, suggest a valid index.
565        // If there is no valid index available, return -1.
566        public int findIndex(int indexHint) {
567            if (mAlbumSetDataAdapter.isActive(indexHint)) {
568                mIndex = indexHint;
569            } else {
570                mIndex = mAlbumSetDataAdapter.getActiveStart();
571                if (!mAlbumSetDataAdapter.isActive(mIndex)) {
572                    return -1;
573                }
574            }
575            return mIndex;
576        }
577
578        public MediaDetails getDetails() {
579            MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex);
580            if (item != null) {
581                mHighlightDrawer.setHighlightItem(item.getPath());
582                return item.getDetails();
583            } else {
584                return null;
585            }
586        }
587    }
588}
589