/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera; import com.android.gallery.R; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.util.Log; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.android.camera.gallery.IImage; import com.android.camera.gallery.IImageList; import com.android.camera.gallery.VideoObject; import java.util.ArrayList; import java.util.HashSet; public class ImageGallery extends NoSearchActivity implements GridViewSpecial.Listener, GridViewSpecial.DrawAdapter { private static final String STATE_SCROLL_POSITION = "scroll_position"; private static final String STATE_SELECTED_INDEX = "first_index"; private static final String TAG = "ImageGallery"; private static final float INVALID_POSITION = -1f; private ImageManager.ImageListParam mParam; private IImageList mAllImages; private int mInclusion; boolean mSortAscending = false; private View mNoImagesView; public static final int CROP_MSG = 2; private Dialog mMediaScanningDialog; private MenuItem mSlideShowItem; private SharedPreferences mPrefs; private long mVideoSizeLimit = Long.MAX_VALUE; private View mFooterOrganizeView; private BroadcastReceiver mReceiver = null; private final Handler mHandler = new Handler(); private boolean mLayoutComplete; private boolean mPausing = true; private ImageLoader mLoader; private GridViewSpecial mGvs; private Uri mCropResultUri; // The index of the first picture in GridViewSpecial. private int mSelectedIndex = GridViewSpecial.INDEX_NONE; private float mScrollPosition = INVALID_POSITION; private boolean mConfigurationChanged = false; private HashSet<IImage> mMultiSelected = null; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); mPrefs = PreferenceManager.getDefaultSharedPreferences(this); // Must be called before setContentView(). requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.image_gallery); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_gallery_title); mNoImagesView = findViewById(R.id.no_images); mGvs = (GridViewSpecial) findViewById(R.id.grid); mGvs.setListener(this); mFooterOrganizeView = findViewById(R.id.footer_organize); // consume all click events on the footer view mFooterOrganizeView.setOnClickListener(Util.getNullOnClickListener()); initializeFooterButtons(); if (isPickIntent()) { mVideoSizeLimit = getIntent().getLongExtra( MediaStore.EXTRA_SIZE_LIMIT, Long.MAX_VALUE); } else { mVideoSizeLimit = Long.MAX_VALUE; mGvs.setOnCreateContextMenuListener( new CreateContextMenuListener()); } setupInclusion(); mLoader = new ImageLoader(getContentResolver(), mHandler); } private void initializeFooterButtons() { Button deleteButton = (Button) findViewById(R.id.button_delete); deleteButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { onDeleteMultipleClicked(); } }); Button shareButton = (Button) findViewById(R.id.button_share); shareButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { onShareMultipleClicked(); } }); Button closeButton = (Button) findViewById(R.id.button_close); closeButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { closeMultiSelectMode(); } }); } private MenuItem addSlideShowMenu(Menu menu) { return menu.add(Menu.NONE, Menu.NONE, MenuHelper.POSITION_SLIDESHOW, R.string.slide_show) .setOnMenuItemClickListener( new MenuItem.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { return onSlideShowClicked(); } }).setIcon(android.R.drawable.ic_menu_slideshow); } public boolean onSlideShowClicked() { if (!canHandleEvent()) { return false; } IImage img = getCurrentImage(); if (img == null) { img = mAllImages.getImageAt(0); if (img == null) { return true; } } Uri targetUri = img.fullSizeImageUri(); Uri thisUri = getIntent().getData(); if (thisUri != null) { String bucket = thisUri.getQueryParameter("bucketId"); if (bucket != null) { targetUri = targetUri.buildUpon() .appendQueryParameter("bucketId", bucket) .build(); } } Intent intent = new Intent(Intent.ACTION_VIEW, targetUri); intent.putExtra("slideshow", true); startActivity(intent); return true; } private final Runnable mDeletePhotoRunnable = new Runnable() { public void run() { if (!canHandleEvent()) return; IImage currentImage = getCurrentImage(); // The selection will be cleared when mGvs.stop() is called, so // we need to call getCurrentImage() before mGvs.stop(). mGvs.stop(); if (currentImage != null) { mAllImages.removeImage(currentImage); } mGvs.setImageList(mAllImages); mGvs.start(); mNoImagesView.setVisibility(mAllImages.isEmpty() ? View.VISIBLE : View.GONE); } }; private Uri getCurrentImageUri() { IImage image = getCurrentImage(); if (image != null) { return image.fullSizeImageUri(); } else { return null; } } private IImage getCurrentImage() { int currentSelection = mGvs.getCurrentSelection(); if (currentSelection < 0 || currentSelection >= mAllImages.getCount()) { return null; } else { return mAllImages.getImageAt(currentSelection); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mConfigurationChanged = true; } boolean canHandleEvent() { // Don't process event in pause state. return (!mPausing) && (mLayoutComplete); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (!canHandleEvent()) return false; switch (keyCode) { case KeyEvent.KEYCODE_DEL: IImage image = getCurrentImage(); if (image != null) { MenuHelper.deleteImage( this, mDeletePhotoRunnable, getCurrentImage()); } return true; } return super.onKeyDown(keyCode, event); } private boolean isPickIntent() { String action = getIntent().getAction(); return (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)); } private void launchCropperOrFinish(IImage img) { Bundle myExtras = getIntent().getExtras(); long size = MenuHelper.getImageFileSize(img); if (size < 0) { // Return if the image file is not available. return; } if (size > mVideoSizeLimit) { DialogInterface.OnClickListener buttonListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }; new AlertDialog.Builder(this) .setIcon(android.R.drawable.ic_dialog_info) .setTitle(R.string.file_info_title) .setMessage(R.string.video_exceed_mms_limit) .setNeutralButton(R.string.details_ok, buttonListener) .show(); return; } String cropValue = myExtras != null ? myExtras.getString("crop") : null; if (cropValue != null) { Bundle newExtras = new Bundle(); if (cropValue.equals("circle")) { newExtras.putString("circleCrop", "true"); } Intent cropIntent = new Intent(); cropIntent.setData(img.fullSizeImageUri()); cropIntent.setClass(this, CropImage.class); cropIntent.putExtras(newExtras); /* pass through any extras that were passed in */ cropIntent.putExtras(myExtras); startActivityForResult(cropIntent, CROP_MSG); } else { Intent result = new Intent(null, img.fullSizeImageUri()); if (myExtras != null && myExtras.getBoolean("return-data")) { // The size of a transaction should be below 100K. Bitmap bitmap = img.fullSizeBitmap( IImage.UNCONSTRAINED, 100 * 1024); if (bitmap != null) { result.putExtra("data", bitmap); } } setResult(RESULT_OK, result); finish(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case MenuHelper.RESULT_COMMON_MENU_CROP: { if (resultCode == RESULT_OK) { // The CropImage activity passes back the Uri of the cropped // image as the Action rather than the Data. // We store this URI so we can move the selection box to it // later. mCropResultUri = Uri.parse(data.getAction()); } break; } case CROP_MSG: { if (resultCode == RESULT_OK) { setResult(resultCode, data); finish(); } break; } } } @Override public void onPause() { super.onPause(); mPausing = true; mLoader.stop(); mGvs.stop(); if (mReceiver != null) { unregisterReceiver(mReceiver); mReceiver = null; } // Now that we've paused the threads that are using the cursor it is // safe to close it. mAllImages.close(); mAllImages = null; } private void rebake(boolean unmounted, boolean scanning) { mGvs.stop(); if (mAllImages != null) { mAllImages.close(); mAllImages = null; } if (mMediaScanningDialog != null) { mMediaScanningDialog.cancel(); mMediaScanningDialog = null; } if (scanning) { mMediaScanningDialog = ProgressDialog.show( this, null, getResources().getString(R.string.wait), true, true); } mParam = allImages(!unmounted && !scanning); mAllImages = ImageManager.makeImageList(getContentResolver(), mParam); mGvs.setImageList(mAllImages); mGvs.setDrawAdapter(this); mGvs.setLoader(mLoader); mGvs.start(); mNoImagesView.setVisibility(mAllImages.getCount() > 0 ? View.GONE : View.VISIBLE); } @Override protected void onSaveInstanceState(Bundle state) { super.onSaveInstanceState(state); state.putFloat(STATE_SCROLL_POSITION, mScrollPosition); state.putInt(STATE_SELECTED_INDEX, mSelectedIndex); } @Override protected void onRestoreInstanceState(Bundle state) { super.onRestoreInstanceState(state); mScrollPosition = state.getFloat( STATE_SCROLL_POSITION, INVALID_POSITION); mSelectedIndex = state.getInt(STATE_SELECTED_INDEX, 0); } @Override public void onResume() { super.onResume(); mGvs.setSizeChoice(Integer.parseInt( mPrefs.getString("pref_gallery_size_key", "1"))); mGvs.requestFocus(); String sortOrder = mPrefs.getString("pref_gallery_sort_key", null); if (sortOrder != null) { mSortAscending = sortOrder.equals("ascending"); } mPausing = false; // install an intent filter to receive SD card related events. IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED); intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); intentFilter.addAction(Intent.ACTION_MEDIA_EJECT); intentFilter.addDataScheme("file"); mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) { // SD card available // TODO put up a "please wait" message // TODO also listen for the media scanner finished message } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) { // SD card unavailable rebake(true, false); } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) { rebake(false, true); } else if (action.equals( Intent.ACTION_MEDIA_SCANNER_FINISHED)) { rebake(false, false); } else if (action.equals(Intent.ACTION_MEDIA_EJECT)) { rebake(true, false); } } }; registerReceiver(mReceiver, intentFilter); rebake(false, ImageManager.isMediaScannerScanning( getContentResolver())); } @Override public boolean onCreateOptionsMenu(Menu menu) { if (isPickIntent()) { String type = getIntent().resolveType(this); if (type != null) { if (isImageType(type)) { MenuHelper.addCapturePictureMenuItems(menu, this); } else if (isVideoType(type)) { MenuHelper.addCaptureVideoMenuItems(menu, this); } } } else { MenuHelper.addCaptureMenuItems(menu, this); if ((mInclusion & ImageManager.INCLUDE_IMAGES) != 0) { mSlideShowItem = addSlideShowMenu(menu); } MenuItem item = menu.add(Menu.NONE, Menu.NONE, MenuHelper.POSITION_GALLERY_SETTING, R.string.camerasettings); item.setOnMenuItemClickListener( new MenuItem.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { Intent preferences = new Intent(); preferences.setClass(ImageGallery.this, GallerySettings.class); startActivity(preferences); return true; } }); item.setAlphabeticShortcut('p'); item.setIcon(android.R.drawable.ic_menu_preferences); item = menu.add(Menu.NONE, Menu.NONE, MenuHelper.POSITION_MULTISELECT, R.string.multiselect); item.setOnMenuItemClickListener( new MenuItem.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { if (isInMultiSelectMode()) { closeMultiSelectMode(); } else { openMultiSelectMode(); } return true; } }); item.setIcon(R.drawable.ic_menu_multiselect_gallery); } return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { if (!canHandleEvent()) return false; if ((mInclusion & ImageManager.INCLUDE_IMAGES) != 0) { boolean videoSelected = isVideoSelected(); // TODO: Only enable slide show if there is at least one image in // the folder. if (mSlideShowItem != null) { mSlideShowItem.setEnabled(!videoSelected); } } return true; } private boolean isVideoSelected() { IImage image = getCurrentImage(); return (image != null) && ImageManager.isVideo(image); } private boolean isImageType(String type) { return type.equals("vnd.android.cursor.dir/image") || type.equals("image/*"); } private boolean isVideoType(String type) { return type.equals("vnd.android.cursor.dir/video") || type.equals("video/*"); } // According to the intent, setup what we include (image/video) in the // gallery and the title of the gallery. private void setupInclusion() { mInclusion = ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS; Intent intent = getIntent(); if (intent != null) { String type = intent.resolveType(this); TextView leftText = (TextView) findViewById(R.id.left_text); if (type != null) { if (isImageType(type)) { mInclusion = ImageManager.INCLUDE_IMAGES; if (isPickIntent()) { leftText.setText(R.string.pick_photos_gallery_title); } else { leftText.setText(R.string.photos_gallery_title); } } if (isVideoType(type)) { mInclusion = ImageManager.INCLUDE_VIDEOS; if (isPickIntent()) { leftText.setText(R.string.pick_videos_gallery_title); } else { leftText.setText(R.string.videos_gallery_title); } } } Bundle extras = intent.getExtras(); String title = (extras != null) ? extras.getString("windowTitle") : null; if (title != null && title.length() > 0) { leftText.setText(title); } if (extras != null) { mInclusion = (ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS) & extras.getInt("mediaTypes", mInclusion); } if (extras != null && extras.getBoolean("pick-drm")) { Log.d(TAG, "pick-drm is true"); mInclusion = ImageManager.INCLUDE_DRM_IMAGES; } } } // Returns the image list parameter which contains the subset of image/video // we want. private ImageManager.ImageListParam allImages(boolean storageAvailable) { if (!storageAvailable) { return ImageManager.getEmptyImageListParam(); } else { Uri uri = getIntent().getData(); return ImageManager.getImageListParam( ImageManager.DataLocation.EXTERNAL, mInclusion, mSortAscending ? ImageManager.SORT_ASCENDING : ImageManager.SORT_DESCENDING, (uri != null) ? uri.getQueryParameter("bucketId") : null); } } private void toggleMultiSelected(IImage image) { int original = mMultiSelected.size(); if (!mMultiSelected.add(image)) { mMultiSelected.remove(image); } mGvs.invalidate(); if (original == 0) showFooter(); if (mMultiSelected.size() == 0) hideFooter(); } public void onImageClicked(int index) { if (index < 0 || index >= mAllImages.getCount()) { return; } mSelectedIndex = index; mGvs.setSelectedIndex(index); IImage image = mAllImages.getImageAt(index); if (isInMultiSelectMode()) { toggleMultiSelected(image); return; } if (isPickIntent()) { launchCropperOrFinish(image); } else { Intent intent; if (image instanceof VideoObject) { intent = new Intent( Intent.ACTION_VIEW, image.fullSizeImageUri()); intent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { intent = new Intent(this, ViewImage.class); intent.putExtra(ViewImage.KEY_IMAGE_LIST, mParam); intent.setData(image.fullSizeImageUri()); } startActivity(intent); } } public void onImageTapped(int index) { // In the multiselect mode, once the finger finishes tapping, we hide // the selection box by setting the selected index to none. However, if // we use the dpad center key, we will keep the selected index in order // to show the the selection box. We do this because we have the // multiselect marker on the images to indicate which of them are // selected, so we don't need the selection box, but in the dpad case // we still need the selection box to show as a "cursor". if (isInMultiSelectMode()) { mGvs.setSelectedIndex(GridViewSpecial.INDEX_NONE); toggleMultiSelected(mAllImages.getImageAt(index)); } else { onImageClicked(index); } } private class CreateContextMenuListener implements View.OnCreateContextMenuListener { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { if (!canHandleEvent()) return; IImage image = getCurrentImage(); if (image == null) { return; } boolean isImage = ImageManager.isImage(image); if (isImage) { menu.add(R.string.view) .setOnMenuItemClickListener( new MenuItem.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { if (!canHandleEvent()) return false; onImageClicked(mGvs.getCurrentSelection()); return true; } }); } menu.setHeaderTitle(isImage ? R.string.context_menu_header : R.string.video_context_menu_header); if ((mInclusion & (ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS)) != 0) { MenuHelper.MenuItemsResult r = MenuHelper.addImageMenuItems( menu, MenuHelper.INCLUDE_ALL, ImageGallery.this, mHandler, mDeletePhotoRunnable, new MenuHelper.MenuInvoker() { public void run(MenuHelper.MenuCallback cb) { if (!canHandleEvent()) { return; } cb.run(getCurrentImageUri(), getCurrentImage()); mGvs.invalidateImage(mGvs.getCurrentSelection()); } }); if (r != null) { r.gettingReadyToOpen(menu, image); } if (isImage) { MenuHelper.enableShowOnMapMenuItem( menu, MenuHelper.hasLatLngData(image)); addSlideShowMenu(menu); } } } } public void onLayoutComplete(boolean changed) { mLayoutComplete = true; if (mCropResultUri != null) { IImage image = mAllImages.getImageForUri(mCropResultUri); mCropResultUri = null; if (image != null) { mSelectedIndex = mAllImages.getImageIndex(image); } } mGvs.setSelectedIndex(mSelectedIndex); if (mScrollPosition == INVALID_POSITION) { if (mSortAscending) { mGvs.scrollTo(0, mGvs.getHeight()); } else { mGvs.scrollToImage(0); } } else if (mConfigurationChanged) { mConfigurationChanged = false; mGvs.scrollTo(mScrollPosition); if (mGvs.getCurrentSelection() != GridViewSpecial.INDEX_NONE) { mGvs.scrollToVisible(mSelectedIndex); } } else { mGvs.scrollTo(mScrollPosition); } } public void onScroll(float scrollPosition) { mScrollPosition = scrollPosition; } private Drawable mVideoOverlay; private Drawable mVideoMmsErrorOverlay; private Drawable mMultiSelectTrue; private Drawable mMultiSelectFalse; // mSrcRect and mDstRect are only used in drawImage, but we put them as // instance variables to reduce the memory allocation overhead because // drawImage() is called a lot. private final Rect mSrcRect = new Rect(); private final Rect mDstRect = new Rect(); private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); public void drawImage(Canvas canvas, IImage image, Bitmap b, int xPos, int yPos, int w, int h) { if (b != null) { // if the image is close to the target size then crop, // otherwise scale both the bitmap and the view should be // square but I suppose that could change in the future. int bw = b.getWidth(); int bh = b.getHeight(); int deltaW = bw - w; int deltaH = bh - h; if (deltaW >= 0 && deltaW < 10 && deltaH >= 0 && deltaH < 10) { int halfDeltaW = deltaW / 2; int halfDeltaH = deltaH / 2; mSrcRect.set(0 + halfDeltaW, 0 + halfDeltaH, bw - halfDeltaW, bh - halfDeltaH); mDstRect.set(xPos, yPos, xPos + w, yPos + h); canvas.drawBitmap(b, mSrcRect, mDstRect, null); } else { mSrcRect.set(0, 0, bw, bh); mDstRect.set(xPos, yPos, xPos + w, yPos + h); canvas.drawBitmap(b, mSrcRect, mDstRect, mPaint); } } else { // If the thumbnail cannot be drawn, put up an error icon // instead Bitmap error = getErrorBitmap(image); int width = error.getWidth(); int height = error.getHeight(); mSrcRect.set(0, 0, width, height); int left = (w - width) / 2 + xPos; int top = (w - height) / 2 + yPos; mDstRect.set(left, top, left + width, top + height); canvas.drawBitmap(error, mSrcRect, mDstRect, null); } if (ImageManager.isVideo(image)) { Drawable overlay = null; long size = MenuHelper.getImageFileSize(image); if (size >= 0 && size <= mVideoSizeLimit) { if (mVideoOverlay == null) { mVideoOverlay = getResources().getDrawable( R.drawable.ic_gallery_video_overlay); } overlay = mVideoOverlay; } else { if (mVideoMmsErrorOverlay == null) { mVideoMmsErrorOverlay = getResources().getDrawable( R.drawable.ic_error_mms_video_overlay); } overlay = mVideoMmsErrorOverlay; Paint paint = new Paint(); paint.setARGB(0x80, 0x00, 0x00, 0x00); canvas.drawRect(xPos, yPos, xPos + w, yPos + h, paint); } int width = overlay.getIntrinsicWidth(); int height = overlay.getIntrinsicHeight(); int left = (w - width) / 2 + xPos; int top = (h - height) / 2 + yPos; mSrcRect.set(left, top, left + width, top + height); overlay.setBounds(mSrcRect); overlay.draw(canvas); } } public boolean needsDecoration() { return (mMultiSelected != null); } public void drawDecoration(Canvas canvas, IImage image, int xPos, int yPos, int w, int h) { if (mMultiSelected != null) { initializeMultiSelectDrawables(); Drawable checkBox = mMultiSelected.contains(image) ? mMultiSelectTrue : mMultiSelectFalse; int width = checkBox.getIntrinsicWidth(); int height = checkBox.getIntrinsicHeight(); int left = 5 + xPos; int top = h - height - 5 + yPos; mSrcRect.set(left, top, left + width, top + height); checkBox.setBounds(mSrcRect); checkBox.draw(canvas); } } private void initializeMultiSelectDrawables() { if (mMultiSelectTrue == null) { mMultiSelectTrue = getResources() .getDrawable(R.drawable.btn_check_buttonless_on); } if (mMultiSelectFalse == null) { mMultiSelectFalse = getResources() .getDrawable(R.drawable.btn_check_buttonless_off); } } private Bitmap mMissingImageThumbnailBitmap; private Bitmap mMissingVideoThumbnailBitmap; // Create this bitmap lazily, and only once for all the ImageBlocks to // use public Bitmap getErrorBitmap(IImage image) { if (ImageManager.isImage(image)) { if (mMissingImageThumbnailBitmap == null) { mMissingImageThumbnailBitmap = BitmapFactory.decodeResource( getResources(), R.drawable.ic_missing_thumbnail_picture); } return mMissingImageThumbnailBitmap; } else { if (mMissingVideoThumbnailBitmap == null) { mMissingVideoThumbnailBitmap = BitmapFactory.decodeResource( getResources(), R.drawable.ic_missing_thumbnail_video); } return mMissingVideoThumbnailBitmap; } } private Animation mFooterAppear; private Animation mFooterDisappear; private void showFooter() { mFooterOrganizeView.setVisibility(View.VISIBLE); if (mFooterAppear == null) { mFooterAppear = AnimationUtils.loadAnimation( this, R.anim.footer_appear); } mFooterOrganizeView.startAnimation(mFooterAppear); } private void hideFooter() { if (mFooterOrganizeView.getVisibility() != View.GONE) { mFooterOrganizeView.setVisibility(View.GONE); if (mFooterDisappear == null) { mFooterDisappear = AnimationUtils.loadAnimation( this, R.anim.footer_disappear); } mFooterOrganizeView.startAnimation(mFooterDisappear); } } private String getShareMultipleMimeType() { final int FLAG_IMAGE = 1, FLAG_VIDEO = 2; int flag = 0; for (IImage image : mMultiSelected) { flag |= ImageManager.isImage(image) ? FLAG_IMAGE : FLAG_VIDEO; } return flag == FLAG_IMAGE ? "image/*" : flag == FLAG_VIDEO ? "video/*" : "*/*"; } private void onShareMultipleClicked() { if (mMultiSelected == null) return; if (mMultiSelected.size() > 1) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND_MULTIPLE); String mimeType = getShareMultipleMimeType(); intent.setType(mimeType); ArrayList<Parcelable> list = new ArrayList<Parcelable>(); for (IImage image : mMultiSelected) { list.add(image.fullSizeImageUri()); } intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, list); try { startActivity(Intent.createChooser( intent, getText(R.string.send_media_files))); } catch (android.content.ActivityNotFoundException ex) { Toast.makeText(this, R.string.no_way_to_share, Toast.LENGTH_SHORT).show(); } } else if (mMultiSelected.size() == 1) { IImage image = mMultiSelected.iterator().next(); Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); String mimeType = image.getMimeType(); intent.setType(mimeType); intent.putExtra(Intent.EXTRA_STREAM, image.fullSizeImageUri()); boolean isImage = ImageManager.isImage(image); try { startActivity(Intent.createChooser(intent, getText( isImage ? R.string.sendImage : R.string.sendVideo))); } catch (android.content.ActivityNotFoundException ex) { Toast.makeText(this, isImage ? R.string.no_way_to_share_image : R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show(); } } } private void onDeleteMultipleClicked() { if (mMultiSelected == null) return; Runnable action = new Runnable() { public void run() { ArrayList<Uri> uriList = new ArrayList<Uri>(); for (IImage image : mMultiSelected) { uriList.add(image.fullSizeImageUri()); } closeMultiSelectMode(); Intent intent = new Intent(ImageGallery.this, DeleteImage.class); intent.putExtra("delete-uris", uriList); try { startActivity(intent); } catch (ActivityNotFoundException ex) { Log.e(TAG, "Delete images fail", ex); } } }; MenuHelper.deleteMultiple(this, action); } private boolean isInMultiSelectMode() { return mMultiSelected != null; } private void closeMultiSelectMode() { if (mMultiSelected == null) return; mMultiSelected = null; mGvs.invalidate(); hideFooter(); } private void openMultiSelectMode() { if (mMultiSelected != null) return; mMultiSelected = new HashSet<IImage>(); mGvs.invalidate(); } }