2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.app;
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.graphics.Rect;
23 import android.os.Bundle;
24 import android.os.Vibrator;
25 import android.provider.MediaStore;
26 import android.view.ActionMode;
27 import android.view.Menu;
28 import android.view.MenuInflater;
29 import android.view.MenuItem;
30 import android.widget.Toast;
32 import com.android.gallery3d.R;
33 import com.android.gallery3d.common.Utils;
34 import com.android.gallery3d.data.DataManager;
35 import com.android.gallery3d.data.MediaDetails;
36 import com.android.gallery3d.data.MediaObject;
37 import com.android.gallery3d.data.MediaSet;
38 import com.android.gallery3d.data.Path;
39 import com.android.gallery3d.picasasource.PicasaSource;
40 import com.android.gallery3d.settings.GallerySettings;
41 import com.android.gallery3d.ui.ActionModeHandler;
42 import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener;
43 import com.android.gallery3d.ui.AlbumSetView;
44 import com.android.gallery3d.ui.DetailsHelper;
45 import com.android.gallery3d.ui.DetailsHelper.CloseListener;
46 import com.android.gallery3d.util.GalleryUtils;
47 import com.android.gallery3d.ui.GLCanvas;
48 import com.android.gallery3d.ui.GLView;
49 import com.android.gallery3d.ui.GridDrawer;
50 import com.android.gallery3d.ui.HighlightDrawer;
51 import com.android.gallery3d.ui.PositionProvider;
52 import com.android.gallery3d.ui.PositionRepository;
53 import com.android.gallery3d.ui.PositionRepository.Position;
54 import com.android.gallery3d.ui.SelectionManager;
55 import com.android.gallery3d.ui.SlotView;
56 import com.android.gallery3d.ui.StaticBackground;
58 public class AlbumSetPage extends ActivityState implements
59 SelectionManager.SelectionListener, GalleryActionBar.ClusterRunner,
60 EyePosition.EyePositionListener {
61 @SuppressWarnings("unused")
62 private static final String TAG = "AlbumSetPage";
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";
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;
73 private boolean mIsActive = false;
74 private StaticBackground mStaticBackground;
75 private AlbumSetView mAlbumSetView;
77 private MediaSet mMediaSet;
78 private String mSubtitle;
79 private boolean mShowClusterMenu;
80 private int mSelectedAction;
81 private Vibrator mVibrator;
83 protected SelectionManager mSelectionManager;
84 private AlbumSetDataAdapter mAlbumSetDataAdapter;
85 private GridDrawer mGridDrawer;
86 private HighlightDrawer mHighlightDrawer;
88 private boolean mGetContent;
89 private boolean mGetAlbum;
90 private ActionMode mActionMode;
91 private ActionModeHandler mActionModeHandler;
92 private DetailsHelper mDetailsHelper;
93 private MyDetailsSource mDetailsSource;
94 private boolean mShowDetails;
95 private EyePosition mEyePosition;
97 // The eyes' position of the user, the origin is at the center of the
98 // device and the unit is in pixels.
103 private final GLView mRootPane = new GLView() {
104 private final float mMatrix[] = new float[16];
107 protected void onLayout(
108 boolean changed, int left, int top, int right, int bottom) {
109 mStaticBackground.layout(0, 0, right - left, bottom - top);
110 mEyePosition.resetPosition();
112 int slotViewTop = GalleryActionBar.getHeight((Activity) mActivity);
113 int slotViewBottom = bottom - top;
114 int slotViewRight = right - left;
117 mDetailsHelper.layout(left, slotViewTop, right, bottom);
119 mAlbumSetView.setSelectionDrawer(mGridDrawer);
122 mAlbumSetView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
123 PositionRepository.getInstance(mActivity).setOffset(
128 protected void render(GLCanvas canvas) {
129 canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
130 GalleryUtils.setViewPointMatrix(mMatrix,
131 getWidth() / 2 + mX, getHeight() / 2 + mY, mZ);
132 canvas.multiplyMatrix(mMatrix, 0);
133 super.render(canvas);
139 public void onEyePositionChanged(float x, float y, float z) {
140 mRootPane.lockRendering();
144 mRootPane.unlockRendering();
145 mRootPane.invalidate();
149 public void onBackPressed() {
152 } else if (mSelectionManager.inSelectionMode()) {
153 mSelectionManager.leaveSelectionMode();
155 mAlbumSetView.savePositions(
156 PositionRepository.getInstance(mActivity));
157 super.onBackPressed();
161 private void savePositions(int slotIndex, int center[]) {
162 Rect offset = new Rect();
163 mRootPane.getBoundsOf(mAlbumSetView, offset);
164 mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
165 Rect r = mAlbumSetView.getSlotRect(slotIndex);
166 int scrollX = mAlbumSetView.getScrollX();
167 int scrollY = mAlbumSetView.getScrollY();
168 center[0] = offset.left + (r.left + r.right) / 2 - scrollX;
169 center[1] = offset.top + (r.top + r.bottom) / 2 - scrollY;
172 public void onSingleTapUp(int slotIndex) {
173 MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
174 if (targetSet == null) return; // Content is dirty, we shall reload soon
177 Path path = targetSet.getPath();
178 mHighlightDrawer.setHighlightItem(path);
179 mDetailsHelper.reloadDetails(slotIndex);
180 } else if (!mSelectionManager.inSelectionMode()) {
181 Bundle data = new Bundle(getData());
182 String mediaPath = targetSet.getPath().toString();
183 int[] center = new int[2];
184 savePositions(slotIndex, center);
185 data.putIntArray(AlbumPage.KEY_SET_CENTER, center);
186 if (mGetAlbum && targetSet.isLeafAlbum()) {
187 Activity activity = (Activity) mActivity;
188 Intent result = new Intent()
189 .putExtra(AlbumPicker.KEY_ALBUM_PATH, targetSet.getPath().toString());
190 activity.setResult(Activity.RESULT_OK, result);
192 } else if (targetSet.getSubMediaSetCount() > 0) {
193 data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
194 mActivity.getStateManager().startStateForResult(
195 AlbumSetPage.class, REQUEST_DO_ANIMATION, data);
197 if (!mGetContent && (targetSet.getSupportedOperations()
198 & MediaObject.SUPPORT_IMPORT) != 0) {
199 data.putBoolean(AlbumPage.KEY_AUTO_SELECT_ALL, true);
201 data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath);
202 boolean inAlbum = mActivity.getStateManager().hasStateClass(AlbumPage.class);
203 // We only show cluster menu in the first AlbumPage in stack
204 data.putBoolean(AlbumPage.KEY_SHOW_CLUSTER_MENU, !inAlbum);
205 mActivity.getStateManager().startStateForResult(
206 AlbumPage.class, REQUEST_DO_ANIMATION, data);
209 mSelectionManager.toggle(targetSet.getPath());
210 mAlbumSetView.invalidate();
214 private void onDown(int index) {
215 MediaSet set = mAlbumSetDataAdapter.getMediaSet(index);
216 Path path = (set == null) ? null : set.getPath();
217 mSelectionManager.setPressedPath(path);
218 mAlbumSetView.invalidate();
221 private void onUp() {
222 mSelectionManager.setPressedPath(null);
223 mAlbumSetView.invalidate();
226 public void onLongTap(int slotIndex) {
227 if (mGetContent || mGetAlbum) return;
229 onSingleTapUp(slotIndex);
231 MediaSet set = mAlbumSetDataAdapter.getMediaSet(slotIndex);
232 if (set == null) return;
233 mSelectionManager.setAutoLeaveSelectionMode(true);
234 mSelectionManager.toggle(set.getPath());
235 mDetailsSource.findIndex(slotIndex);
236 mAlbumSetView.invalidate();
240 public void doCluster(int clusterType) {
241 String basePath = mMediaSet.getPath().toString();
242 String newPath = FilterUtils.switchClusterPath(basePath, clusterType);
243 Bundle data = new Bundle(getData());
244 data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
245 data.putInt(KEY_SELECTED_CLUSTER_TYPE, clusterType);
246 mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
247 mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
250 public void doFilter(int filterType) {
251 String basePath = mMediaSet.getPath().toString();
252 String newPath = FilterUtils.switchFilterPath(basePath, filterType);
253 Bundle data = new Bundle(getData());
254 data.putString(AlbumSetPage.KEY_MEDIA_PATH, newPath);
255 mAlbumSetView.savePositions(PositionRepository.getInstance(mActivity));
256 mActivity.getStateManager().switchState(this, AlbumSetPage.class, data);
259 public void onOperationComplete() {
260 mAlbumSetView.invalidate();
261 // TODO: enable animation
265 public void onCreate(Bundle data, Bundle restoreState) {
267 initializeData(data);
268 Context context = mActivity.getAndroidContext();
269 mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
270 mGetAlbum = data.getBoolean(Gallery.KEY_GET_ALBUM, false);
271 mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
272 mEyePosition = new EyePosition(context, this);
273 mDetailsSource = new MyDetailsSource();
274 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
275 GalleryActionBar actionBar = mActivity.getGalleryActionBar();
276 if (actionBar != null) {
277 mSelectedAction = data.getInt(
278 AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE, FilterUtils.CLUSTER_BY_ALBUM);
284 public void onPause() {
287 mActionModeHandler.pause();
288 mAlbumSetDataAdapter.pause();
289 mAlbumSetView.pause();
290 mEyePosition.pause();
291 DetailsHelper.pause();
292 GalleryActionBar actionBar = mActivity.getGalleryActionBar();
293 if (actionBar != null) actionBar.hideClusterMenu();
297 public void onResume() {
300 setContentPane(mRootPane);
301 mAlbumSetDataAdapter.resume();
302 mAlbumSetView.resume();
303 mEyePosition.resume();
304 mActionModeHandler.resume();
305 GalleryActionBar actionBar = mActivity.getGalleryActionBar();
306 if (mShowClusterMenu && actionBar != null) {
307 actionBar.showClusterMenu(mSelectedAction, this);
311 private void initializeData(Bundle data) {
312 String mediaPath = data.getString(AlbumSetPage.KEY_MEDIA_PATH);
313 mMediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
314 mSelectionManager.setSourceMediaSet(mMediaSet);
315 mAlbumSetDataAdapter = new AlbumSetDataAdapter(
316 mActivity, mMediaSet, DATA_CACHE_SIZE);
317 mAlbumSetDataAdapter.setLoadingListener(new MyLoadingListener());
318 mAlbumSetView.setModel(mAlbumSetDataAdapter);
321 private void initializeViews() {
322 mSelectionManager = new SelectionManager(mActivity, true);
323 mSelectionManager.setSelectionListener(this);
324 mStaticBackground = new StaticBackground(mActivity.getAndroidContext());
325 mRootPane.addComponent(mStaticBackground);
327 mGridDrawer = new GridDrawer((Context) mActivity, mSelectionManager);
328 Config.AlbumSetPage config = Config.AlbumSetPage.get((Context) mActivity);
329 mAlbumSetView = new AlbumSetView(mActivity, mGridDrawer,
330 config.slotViewSpec, config.labelSpec);
331 mAlbumSetView.setListener(new SlotView.SimpleListener() {
333 public void onDown(int index) {
334 AlbumSetPage.this.onDown(index);
339 AlbumSetPage.this.onUp();
343 public void onSingleTapUp(int slotIndex) {
344 AlbumSetPage.this.onSingleTapUp(slotIndex);
348 public void onLongTap(int slotIndex) {
349 AlbumSetPage.this.onLongTap(slotIndex);
353 mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
354 mActionModeHandler.setActionModeListener(new ActionModeListener() {
355 public boolean onActionItemClicked(MenuItem item) {
356 return onItemSelected(item);
359 mRootPane.addComponent(mAlbumSetView);
361 mStaticBackground.setImage(R.drawable.background,
362 R.drawable.background_portrait);
366 protected boolean onCreateActionBar(Menu menu) {
367 Activity activity = (Activity) mActivity;
368 GalleryActionBar actionBar = mActivity.getGalleryActionBar();
369 MenuInflater inflater = activity.getMenuInflater();
371 final boolean inAlbum = mActivity.getStateManager().hasStateClass(
375 inflater.inflate(R.menu.pickup, menu);
376 int typeBits = mData.getInt(
377 Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE);
378 int id = R.string.select_image;
379 if ((typeBits & DataManager.INCLUDE_VIDEO) != 0) {
380 id = (typeBits & DataManager.INCLUDE_IMAGE) == 0
381 ? R.string.select_video
382 : R.string.select_item;
384 actionBar.setTitle(id);
385 } else if (mGetAlbum) {
386 inflater.inflate(R.menu.pickup, menu);
387 actionBar.setTitle(R.string.select_album);
389 mShowClusterMenu = !inAlbum;
390 inflater.inflate(R.menu.albumset, menu);
391 actionBar.setTitle(null);
392 MenuItem selectItem = menu.findItem(R.id.action_select);
394 if (selectItem != null) {
395 boolean selectAlbums = !inAlbum &&
396 actionBar.getClusterTypeAction() == FilterUtils.CLUSTER_BY_ALBUM;
398 selectItem.setTitle(R.string.select_album);
400 selectItem.setTitle(R.string.select_group);
404 MenuItem switchCamera = menu.findItem(R.id.action_camera);
405 if (switchCamera != null) {
406 switchCamera.setVisible(GalleryUtils.isCameraAvailable(activity));
409 actionBar.setSubtitle(mSubtitle);
415 protected boolean onItemSelected(MenuItem item) {
416 Activity activity = (Activity) mActivity;
417 switch (item.getItemId()) {
418 case R.id.action_cancel:
419 activity.setResult(Activity.RESULT_CANCELED);
422 case R.id.action_select:
423 mSelectionManager.setAutoLeaveSelectionMode(false);
424 mSelectionManager.enterSelectionMode();
426 case R.id.action_details:
427 if (mAlbumSetDataAdapter.size() != 0) {
434 Toast.makeText(activity,
435 activity.getText(R.string.no_albums_alert),
436 Toast.LENGTH_SHORT).show();
439 case R.id.action_camera: {
440 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
441 .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
442 | Intent.FLAG_ACTIVITY_NEW_TASK);
443 activity.startActivity(intent);
446 case R.id.action_manage_offline: {
447 Bundle data = new Bundle();
448 String mediaPath = mActivity.getDataManager().getTopSetPath(
449 DataManager.INCLUDE_ALL);
450 data.putString(AlbumSetPage.KEY_MEDIA_PATH, mediaPath);
451 mActivity.getStateManager().startState(ManageCachePage.class, data);
454 case R.id.action_sync_picasa_albums: {
455 PicasaSource.requestSync(activity);
458 case R.id.action_settings: {
459 activity.startActivity(new Intent(activity, GallerySettings.class));
468 protected void onStateResult(int requestCode, int resultCode, Intent data) {
469 switch (requestCode) {
470 case REQUEST_DO_ANIMATION: {
476 private void startTransition() {
477 final PositionRepository repository =
478 PositionRepository.getInstance(mActivity);
479 mAlbumSetView.startTransition(new PositionProvider() {
480 private final Position mTempPosition = new Position();
481 public Position getPosition(long identity, Position target) {
482 Position p = repository.get(identity);
485 p.set(target.x, target.y, 128, target.theta, 1);
492 private String getSelectedString() {
493 GalleryActionBar actionBar = mActivity.getGalleryActionBar();
494 int count = mSelectionManager.getSelectedCount();
495 int action = actionBar.getClusterTypeAction();
496 int string = action == FilterUtils.CLUSTER_BY_ALBUM
497 ? R.plurals.number_of_albums_selected
498 : R.plurals.number_of_groups_selected;
499 String format = mActivity.getResources().getQuantityString(string, count);
500 return String.format(format, count);
503 public void onSelectionModeChange(int mode) {
506 case SelectionManager.ENTER_SELECTION_MODE: {
507 mActivity.getGalleryActionBar().hideClusterMenu();
508 mActionMode = mActionModeHandler.startActionMode();
509 mVibrator.vibrate(100);
512 case SelectionManager.LEAVE_SELECTION_MODE: {
513 mActionMode.finish();
514 mActivity.getGalleryActionBar().showClusterMenu(mSelectedAction, this);
515 mRootPane.invalidate();
518 case SelectionManager.SELECT_ALL_MODE: {
519 mActionModeHandler.setTitle(getSelectedString());
520 mRootPane.invalidate();
526 public void onSelectionChange(Path path, boolean selected) {
527 Utils.assertTrue(mActionMode != null);
528 mActionModeHandler.setTitle(getSelectedString());
529 mActionModeHandler.updateSupportedOperation(path, selected);
532 private void hideDetails() {
533 mShowDetails = false;
534 mDetailsHelper.hide();
535 mAlbumSetView.setSelectionDrawer(mGridDrawer);
536 mAlbumSetView.invalidate();
539 private void showDetails() {
541 if (mDetailsHelper == null) {
542 mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext(),
544 mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
545 mDetailsHelper.setCloseListener(new CloseListener() {
546 public void onClose() {
551 mAlbumSetView.setSelectionDrawer(mHighlightDrawer);
552 mDetailsHelper.show();
555 private class MyLoadingListener implements LoadingListener {
556 public void onLoadingStarted() {
557 GalleryUtils.setSpinnerVisibility((Activity) mActivity, true);
560 public void onLoadingFinished() {
561 if (!mIsActive) return;
562 GalleryUtils.setSpinnerVisibility((Activity) mActivity, false);
563 if (mAlbumSetDataAdapter.size() == 0) {
564 Toast.makeText((Context) mActivity,
565 R.string.empty_album, Toast.LENGTH_LONG).show();
566 if (mActivity.getStateManager().getStateCount() > 1) {
567 mActivity.getStateManager().finishState(AlbumSetPage.this);
573 private class MyDetailsSource implements DetailsHelper.DetailsSource {
576 return mAlbumSetDataAdapter.size();
579 public int getIndex() {
583 // If requested index is out of active window, suggest a valid index.
584 // If there is no valid index available, return -1.
585 public int findIndex(int indexHint) {
586 if (mAlbumSetDataAdapter.isActive(indexHint)) {
589 mIndex = mAlbumSetDataAdapter.getActiveStart();
590 if (!mAlbumSetDataAdapter.isActive(mIndex)) {
597 public MediaDetails getDetails() {
598 MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex);
600 mHighlightDrawer.setHighlightItem(item.getPath());
601 return item.getDetails();