package eu.ttbox.androgister.ui.admin.product.photo; import java.io.File; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaScannerConnection; import android.net.Uri; import android.provider.ContactsContract.DisplayPhoto; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ListPopupWindow; import android.widget.PopupWindow.OnDismissListener; import android.widget.Toast; import eu.ttbox.androgister.R; public abstract class PhotoSelectionHandler implements OnClickListener { private static final String TAG = "PhotoSelectionHandler"; private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1001; private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 1002; protected final Context mContext; private final View mPhotoView; private final int mPhotoMode; private final int mPhotoPickSize; private final boolean mIsDirectoryContact; private ListPopupWindow mPopup; public PhotoSelectionHandler(Context context, View photoView, int photoMode, boolean isDirectoryContact) { mContext = context; mPhotoView = photoView; mPhotoMode = photoMode; mIsDirectoryContact = isDirectoryContact; mPhotoPickSize = getPhotoPickSize(); } public void destroy() { if (mPopup != null) { mPopup.dismiss(); } } public abstract PhotoActionListener getListener(); @Override public void onClick(View v) { final PhotoActionListener listener = getListener(); if (listener != null) { Log.d(TAG, "Create Photo Popup Menu"); mPopup = PhotoActionPopup.createPopupMenu(mContext, mPhotoView, listener, mPhotoMode); mPopup.setOnDismissListener(new OnDismissListener() { @Override public void onDismiss() { listener.onPhotoSelectionDismissed(); } }); Log.d(TAG, "Create Photo Popup Menu : Show"); mPopup.show(); } } /** * Attempts to handle the given activity result. Returns whether this * handler was able to process the result successfully. * * @param requestCode * The request code. * @param resultCode * The result code. * @param data * The intent that was returned. * @return Whether the handler was able to process the result. */ public boolean handlePhotoActivityResult(int requestCode, int resultCode, Intent data) { final PhotoActionListener listener = getListener(); Log.d(TAG, "handlePhotoActivityResult requestCode : " + requestCode); if (resultCode == Activity.RESULT_OK) { switch (requestCode) { // Photo was chosen (either new or existing from gallery), and // cropped. case REQUEST_CODE_PHOTO_PICKED_WITH_DATA: { final String path = ProductPhotoUtils.pathForCroppedPhoto(mContext, listener.getCurrentPhotoFile()); Bitmap bitmap = BitmapFactory.decodeFile(path); Log.d(TAG, "Bitmap decodeFile : " + path + " ==> Bitmap : " + bitmap); listener.onPhotoSelected(bitmap); return true; } // Photo was successfully taken, now crop it. case REQUEST_CODE_CAMERA_WITH_DATA: { doCropPhoto(listener.getCurrentPhotoFile()); return true; } } } return false; } /** Used by subclasses to delegate to their enclosing Activity or Fragment. */ protected abstract void startPhotoActivity(Intent intent, int requestCode, String photoFile); /** * Sends a newly acquired photo to Gallery for cropping */ private void doCropPhoto(String fileName) { Log.d(TAG, "doCropPhoto for : " + fileName); try { // Obtain the absolute paths for the newly-taken photo, and the // destination // for the soon-to-be-cropped photo. final String newPath = ProductPhotoUtils.pathForNewCameraPhoto(fileName); final String croppedPath = ProductPhotoUtils.pathForCroppedPhoto(mContext, fileName); Log.d(TAG, "doCropPhoto newPath : " + newPath); Log.d(TAG, "doCropPhoto croppedPath : " + croppedPath); // Add the image to the media store MediaScannerConnection.scanFile(mContext, new String[] { newPath }, new String[] { null }, null); // Launch gallery to crop the photo final Intent intent = getCropImageIntent(newPath, croppedPath); startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, fileName); } catch (Exception e) { Log.e(TAG, "Cannot crop image", e); Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); } } /** * Should initiate an activity to take a photo using the camera. * * @param photoFile * The file path that will be used to store the photo. This is * generally what should be returned by * {@link PhotoSelectionHandler.PhotoActionListener#getCurrentPhotoFile()} * . */ private void startTakePhotoActivity(String photoFile) { final Intent intent = getTakePhotoIntent(photoFile); startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoFile); } /** * Should initiate an activity pick a photo from the gallery. * * @param photoFile * The temporary file that the cropped image is written to before * being stored by the content-provider. * {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)} * . */ private void startPickFromGalleryActivity(String photoFile) { final Intent intent = getPhotoPickIntent(photoFile); startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, photoFile); } private int getPhotoPickSize() { return 256; } /** * Constructs an intent for picking a photo from Gallery, cropping it and * returning the bitmap. */ private Intent getPhotoPickIntent(String photoFile) { final String croppedPhotoPath = ProductPhotoUtils.pathForCroppedPhoto(mContext, photoFile); final Uri croppedPhotoUri = Uri.fromFile(new File(croppedPhotoPath)); final Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null); intent.setType("image/*"); ProductPhotoUtils.addGalleryIntentExtras(intent, croppedPhotoUri, mPhotoPickSize); return intent; } /** * Constructs an intent for image cropping. */ private Intent getCropImageIntent(String inputPhotoPath, String croppedPhotoPath) { final Uri inputPhotoUri = Uri.fromFile(new File(inputPhotoPath)); final Uri croppedPhotoUri = Uri.fromFile(new File(croppedPhotoPath)); Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(inputPhotoUri, "image/*"); ProductPhotoUtils.addGalleryIntentExtras(intent, croppedPhotoUri, mPhotoPickSize); return intent; } /** * Constructs an intent for capturing a photo and storing it in a temporary * file. */ private static Intent getTakePhotoIntent(String fileName) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null); final String newPhotoPath = ProductPhotoUtils.pathForNewCameraPhoto(fileName); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(newPhotoPath))); return intent; } public abstract class PhotoActionListener implements PhotoActionPopup.Listener { @Override public void onUseAsPrimaryChosen() { // No default implementation. } @Override public void onRemovePictureChosen() { // No default implementation. } @Override public void onTakePhotoChosen() { try { // Launch camera to take photo for selected contact startTakePhotoActivity(ProductPhotoUtils.generateTempPhotoFileName()); } catch (ActivityNotFoundException e) { Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); } } @Override public void onPickFromGalleryChosen() { try { // Launch picker to choose photo for selected contact startPickFromGalleryActivity(ProductPhotoUtils.generateTempPhotoFileName()); } catch (ActivityNotFoundException e) { Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show(); } } /** * Called when the user has completed selection of a photo. * * @param bitmap * The selected and cropped photo. */ public abstract void onPhotoSelected(Bitmap bitmap); /** * Gets the current photo file that is being interacted with. It is the * activity or fragment's responsibility to maintain this in saved * state, since this handler instance will not survive rotation. */ public abstract String getCurrentPhotoFile(); /** * Called when the photo selection dialog is dismissed. */ public abstract void onPhotoSelectionDismissed(); } }