-
-
Save Jeevuz/813316be0b9e1a2b4a61 to your computer and use it in GitHub Desktop.
[Android] Advanced utility for picking an image from Gallery/Camera with Android Intents (Crop included. uCrop library is used)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
dependencies { | |
... | |
// Crop image library | |
compile 'com.yalantis:ucrop:1.3.0' | |
} | |
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.utils; | |
import android.app.Activity; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.content.pm.ResolveInfo; | |
import android.content.res.AssetFileDescriptor; | |
import android.database.Cursor; | |
import android.graphics.Bitmap; | |
import android.graphics.BitmapFactory; | |
import android.graphics.Matrix; | |
import android.media.ExifInterface; | |
import android.net.Uri; | |
import android.os.Parcelable; | |
import android.provider.MediaStore; | |
import android.support.annotation.NonNull; | |
import android.support.annotation.Nullable; | |
import android.support.annotation.StringRes; | |
import android.support.v4.app.Fragment; | |
import android.support.v4.content.ContextCompat; | |
import android.util.Log; | |
import android.widget.Toast; | |
import com.yalantis.ucrop.UCrop; | |
import java.io.File; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.List; | |
import com.example.R; | |
/** | |
* Author: Mario Velasco Casquero | |
* Date: 08/09/2015 | |
* <p> | |
* Edited: Vasili Chyrvon. Added choosing from camera or gallery separately. | |
* Done some fixes. Changed resize logic. Changed to uCrop library for cropping. | |
* | |
* @see <a href="https://gist.github.com/Mariovc/cedb5f4372510f7c39f1">Mario Velasco Casquero's initial gist</a> | |
* @see <a href="https://github.com/Yalantis/uCrop">uCrop library</a> | |
*/ | |
public class ImagePicker { | |
public static final int REQUEST_PICK = 7352; | |
public static final int REQUEST_CROP = UCrop.REQUEST_CROP; | |
private static final String TAG = "ImagePicker"; | |
private static final String TEMP_IMAGE_NAME = "tempImage"; | |
private static final String CROPPED_IMAGE_NAME = "croppedImage"; | |
private static final boolean DEBUG_LOG = false; | |
private static boolean isCamera; | |
private static Uri selectedImage; | |
public enum ResizeType { | |
/** | |
* Resize to have image less than max size. | |
* Can cause smaller images. | |
*/ | |
MAX_SIZE, | |
/** | |
* Scaled to exact size. | |
*/ | |
FIXED_SIZE, | |
NO_RESIZE | |
} | |
public static void pickImageFromCamera(Fragment fragment, @StringRes int noSuitableAppMessage) { | |
Intent intent = getPickFromCameraIntent(fragment.getContext()); | |
startActivityForResultFrom(fragment, intent, noSuitableAppMessage); | |
} | |
public static void pickImageFromGallery(Fragment fragment, @StringRes int noSuitableAppMessage) { | |
Intent intent = getPickFromGalleryIntent(); | |
startActivityForResultFrom(fragment, intent, noSuitableAppMessage); | |
} | |
public static void pickImageUsingChooser(Fragment fragment, @StringRes int chooserTitle, @StringRes int noSuitableAppMessage) { | |
Intent intent = getPickWithChooserIntent(fragment.getContext(), chooserTitle); | |
startActivityForResultFrom(fragment, intent, noSuitableAppMessage); | |
} | |
private static void startActivityForResultFrom(Fragment fragment, Intent intent, @StringRes int noSuitableAppMessage) { | |
Context context = fragment.getContext(); | |
if (intent.resolveActivity(context.getPackageManager()) != null) { | |
fragment.startActivityForResult(intent, REQUEST_PICK); | |
} else { | |
Toast.makeText(context, noSuitableAppMessage, Toast.LENGTH_SHORT).show(); | |
} | |
} | |
@Nullable | |
private static Intent getPickWithChooserIntent(Context context, @StringRes int chooserTitle) { | |
Intent chooserIntent = null; | |
List<Intent> intentList = new ArrayList<>(); | |
Intent pickIntent = getPickFromGalleryIntent(); | |
Intent takePhotoIntent = getPickFromCameraIntent(context); | |
addIntentsToList(context, intentList, pickIntent); | |
addIntentsToList(context, intentList, takePhotoIntent); | |
if (intentList.size() > 0) { | |
chooserIntent = Intent.createChooser( | |
intentList.remove(intentList.size() - 1), | |
context.getString(chooserTitle)); | |
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentList.toArray(new Parcelable[intentList.size()])); | |
} | |
return chooserIntent; | |
} | |
@NonNull | |
private static Intent getPickFromCameraIntent(Context context) { | |
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); | |
takePhotoIntent.putExtra("return-data", true); | |
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getTempFile(context))); | |
return takePhotoIntent; | |
} | |
@NonNull | |
private static Intent getPickFromGalleryIntent() { | |
return new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); | |
} | |
private static void addIntentsToList(Context context, List<Intent> list, Intent intent) { | |
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentActivities(intent, 0); | |
for (ResolveInfo resolveInfo : resInfo) { | |
String packageName = resolveInfo.activityInfo.packageName; | |
Intent targetedIntent = new Intent(intent); | |
targetedIntent.setPackage(packageName); | |
list.add(targetedIntent); | |
if (DEBUG_LOG) { | |
Log.d(TAG, "Intent: " + intent.getAction() + " package: " + packageName); | |
} | |
} | |
} | |
public static boolean isImageCroppedSuccessfully(int requestCode, int resultCode) { | |
return requestCode == ImagePicker.REQUEST_CROP | |
&& resultCode == Activity.RESULT_OK; | |
} | |
public static boolean wasErrorWhileCropping(int requestCode, int resultCode, Intent data) { | |
boolean wasError = requestCode == ImagePicker.REQUEST_CROP | |
&& resultCode == UCrop.RESULT_ERROR; | |
if (wasError) { | |
Log.w(TAG, "Was error while cropping.", UCrop.getError(data)); | |
} | |
return wasError; | |
} | |
public static boolean isImagePickedSuccessfully(int requestCode, int resultCode) { | |
return requestCode == ImagePicker.REQUEST_PICK | |
&& resultCode == Activity.RESULT_OK; | |
} | |
/** Simpler version of {@link this#beginCrop(Fragment, int, Intent, int)} with no {@code maxCroppedSize} set. */ | |
public static void beginCrop(Fragment fragment, int resultCode, Intent imageReturnedIntent) { | |
beginCrop(fragment,resultCode, imageReturnedIntent, 0); | |
} | |
/** | |
* Start cropping library activity. | |
* @param maxCroppedSize to use library version of size constraining. | |
* If you want, you can use the resizing options | |
* of the {@link this#getCroppedImage(Context, int, Intent, ResizeType, int)} method instead. | |
*/ | |
public static void beginCrop(Fragment fragment, int resultCode, Intent imageReturnedIntent, int maxCroppedSize) { | |
Context context = fragment.getContext(); | |
Uri selectedImage = getImageUri(context, resultCode, imageReturnedIntent); | |
if (selectedImage != null) { | |
Uri destination = Uri.fromFile(new File(context.getCacheDir(), CROPPED_IMAGE_NAME)); | |
UCrop crop = UCrop.of(selectedImage, destination).withAspectRatio(1, 1).withOptions(createCropOptions(context)); | |
if (maxCroppedSize > 0) { | |
crop = crop.withMaxResultSize(maxCroppedSize, maxCroppedSize); | |
} | |
crop.start(context, fragment); | |
} | |
} | |
public static UCrop.Options createCropOptions(Context context) { | |
UCrop.Options options = new UCrop.Options(); | |
options.setOvalDimmedLayer(true); | |
options.setShowCropFrame(false); | |
options.setShowCropGrid(false); | |
options.setStatusBarColor(ContextCompat.getColor(context, R.color.primary_dark)); | |
options.setToolbarColor(ContextCompat.getColor(context, R.color.primary)); | |
options.setActiveWidgetColor(ContextCompat.getColor(context, R.color.accent)); | |
options.setToolbarTitle(context.getString(R.string.crop_activity_title)); | |
options.setToolbarTitleTextColor(ContextCompat.getColor(context, R.color.text_color_primary)); | |
return options; | |
} | |
private static Uri getImageUri(Context context, int resultCode, Intent imageReturnedIntent) { | |
if (DEBUG_LOG) { | |
Log.d(TAG, "getImageUri, resultCode: " + resultCode); | |
} | |
File imageFile = getTempFile(context); | |
if (resultCode == Activity.RESULT_OK) { | |
isCamera = (imageReturnedIntent == null || | |
imageReturnedIntent.getData() == null || | |
imageReturnedIntent.getData().equals(Uri.fromFile(imageFile))); | |
if (isCamera) { /** CAMERA **/ | |
selectedImage = Uri.fromFile(imageFile); | |
} else { /** ALBUM **/ | |
selectedImage = imageReturnedIntent.getData(); | |
} | |
if (DEBUG_LOG) { | |
Log.d(TAG, "selectedImage: " + selectedImage); | |
} | |
} | |
return selectedImage; | |
} | |
/** Simpler version of {@link this#getCroppedImage(Context, int, Intent, ResizeType, int)} with no resizing. */ | |
public static Bitmap getCroppedImage(Context context, int resultCode, Intent result) { | |
return getCroppedImage(context, resultCode, result, ResizeType.NO_RESIZE, 0); | |
} | |
@Nullable | |
public static Bitmap getCroppedImage(Context context, int resultCode, Intent result, | |
ResizeType resizeType, int size) { | |
Bitmap bm = null; | |
if (resultCode == Activity.RESULT_OK) { | |
Uri croppedImageUri = UCrop.getOutput(result); | |
if (croppedImageUri != null) { | |
bm = getImageResized(context, croppedImageUri, resizeType, size); | |
} | |
} | |
return bm; | |
} | |
@Nullable | |
public static File getCroppedImageFile(int resultCode, Intent result) { | |
File file = null; | |
if (resultCode == Activity.RESULT_OK) { | |
Uri croppedImageUri = UCrop.getOutput(result); | |
if (croppedImageUri != null) { | |
file = new File(croppedImageUri.getPath()); | |
} | |
} | |
return file; | |
} | |
@SuppressWarnings("ResultOfMethodCallIgnored") | |
private static File getTempFile(Context context) { | |
File imageFile = new File(context.getExternalCacheDir(), TEMP_IMAGE_NAME); | |
imageFile.getParentFile().mkdirs(); | |
return imageFile; | |
} | |
/** | |
* Resize to avoid using too much memory | |
**/ | |
private static Bitmap getImageResized(Context context, Uri selectedImageUri, | |
ResizeType resizeType, int targetSize) { | |
Bitmap bitmap = null; | |
// Get file descriptor | |
AssetFileDescriptor fileDescriptor = null; | |
try { | |
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(selectedImageUri, "r"); | |
} catch (FileNotFoundException e) { | |
Log.e(TAG, "Can't find file", e); | |
} | |
if (fileDescriptor != null) { | |
BitmapFactory.Options options = null; | |
// If max size - calculate options inSampleSize | |
if (ResizeType.MAX_SIZE == resizeType) { | |
// Get the dimensions of the bitmap | |
options = new BitmapFactory.Options(); | |
options.inJustDecodeBounds = true; | |
BitmapFactory.decodeFileDescriptor( | |
fileDescriptor.getFileDescriptor(), null, options); | |
int imageW = options.outWidth; | |
int imageH = options.outHeight; | |
// Determine how much to scale down the image (less than max size) | |
int scaleFactor = Math.round(Math.min((float) imageW / targetSize, (float) imageH / targetSize)); | |
// Decode the image file into a Bitmap scaled down | |
options.inJustDecodeBounds = false; | |
options.inSampleSize = scaleFactor; | |
} | |
bitmap = BitmapFactory.decodeFileDescriptor( | |
fileDescriptor.getFileDescriptor(), null, options); | |
// If fixed - scale the bitmap | |
if (ResizeType.FIXED_SIZE == resizeType) { | |
bitmap = Bitmap.createScaledBitmap( | |
bitmap, targetSize, targetSize, true); | |
} | |
//noinspection PointlessBooleanExpression | |
if (DEBUG_LOG && bitmap != null) { | |
Log.d(TAG, "Image size: Width=" + bitmap.getWidth() + ", Height=" + bitmap.getHeight()); | |
} | |
// Close descriptor | |
try { | |
fileDescriptor.close(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
return bitmap; | |
} | |
private static int getRotation(Context context, Uri imageUri, boolean isCamera) { | |
int rotation; | |
if (isCamera) { | |
rotation = getRotationFromCamera(context, imageUri); | |
} else { | |
rotation = getRotationFromGallery(context, imageUri); | |
} | |
if (DEBUG_LOG) { | |
Log.d(TAG, "Image rotation: " + rotation); | |
} | |
return rotation; | |
} | |
private static int getRotationFromCamera(Context context, Uri imageFile) { | |
int rotate = 0; | |
try { | |
context.getContentResolver().notifyChange(imageFile, null); | |
ExifInterface exif = new ExifInterface(imageFile.getPath()); | |
int orientation = exif.getAttributeInt( | |
ExifInterface.TAG_ORIENTATION, | |
ExifInterface.ORIENTATION_NORMAL); | |
switch (orientation) { | |
case ExifInterface.ORIENTATION_ROTATE_270: | |
rotate = 270; | |
break; | |
case ExifInterface.ORIENTATION_ROTATE_180: | |
rotate = 180; | |
break; | |
case ExifInterface.ORIENTATION_ROTATE_90: | |
rotate = 90; | |
break; | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
return rotate; | |
} | |
public static int getRotationFromGallery(Context context, Uri imageUri) { | |
String[] columns = {MediaStore.Images.Media.ORIENTATION}; | |
Cursor cursor = context.getContentResolver().query(imageUri, columns, null, null, null); | |
if (cursor == null) return 0; | |
cursor.moveToFirst(); | |
int orientationColumnIndex = cursor.getColumnIndex(columns[0]); | |
int rotation = cursor.getInt(orientationColumnIndex); | |
cursor.close(); | |
return rotation; | |
} | |
private static Bitmap rotate(Bitmap bm, int rotation) { | |
if (rotation != 0) { | |
Matrix matrix = new Matrix(); | |
matrix.postRotate(rotation); | |
return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); | |
} | |
return bm; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SomeFragment extends Fragment implements implements PickPhotoDialogFragment.OnSourceChosenListener { | |
... | |
@Override | |
public void onTakePhotoClick() { | |
ImagePicker.pickImageFromCamera(this, R.string.no_suitable_app); | |
} | |
@Override | |
public void onChooseFromGalleryClick() { | |
ImagePicker.pickImageFromGallery(this, R.string.no_suitable_app); | |
} | |
// Or just use ImagePicker.pickImageUsingChooser(this, R.string.no_suitable_app) | |
@Override | |
public void onActivityResult(int requestCode, int resultCode, Intent data) { | |
if (ImagePicker.isImagePickedSuccessfully(requestCode, resultCode)) { | |
ImagePicker.beginCrop(this, resultCode, data, MAX_IMAGE_SIZE); | |
} else if (ImagePicker.isImageCroppedSuccessfully(requestCode, resultCode)) { | |
Bitmap bitmap = ImagePicker.getCroppedImage(getContext(), resultCode, data); | |
// TODO: Use the bitmap | |
} else if (ImagePicker.wasErrorWhileCropping(requestCode, resultCode, data)) { | |
// TODO: Handle error. Show snackbar or something | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
</manifest> | |
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> | |
... | |
<application> | |
... | |
<!-- Cropping activity from library --> | |
<activity | |
android:name="com.yalantis.ucrop.UCropActivity" | |
android:screenOrientation="portrait"/> | |
</application> | |
</manifest> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment