Last active
April 27, 2022 14:29
-
-
Save Mariovc/cedb5f4372510f7c39f1 to your computer and use it in GitHub Desktop.
[Android] Advanced utility for picking an image from Gallery/Camera with Android Intents (Crop included)
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 { | |
... | |
compile 'com.soundcloud.android:android-crop:1.0.1@aar' | |
} | |
... |
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
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.widget.Toast; | |
import com.soundcloud.android.crop.Crop; | |
import java.io.File; | |
import java.io.FileNotFoundException; | |
import java.util.ArrayList; | |
import java.util.List; | |
/** | |
* Author: Mario Velasco Casquero | |
* Date: 08/09/2015 | |
*/ | |
public class ImagePicker { | |
public static final int REQUEST_PICK = Crop.REQUEST_PICK; | |
public static final int REQUEST_CROP = Crop.REQUEST_CROP; | |
public static final int DEFAULT_MIN_WIDTH_QUALITY = 400; // min pixels | |
private static final String TAG = "ImagePicker"; | |
private static final String TEMP_IMAGE_NAME = "tempImage"; | |
private static final String CROPPED_IMAGE_NAME = "croppedImage"; | |
private static boolean isCamera; | |
private static Uri selectedImage; | |
public enum ResizeType { | |
MIN_QUALITY, FIXED_SIZE | |
} | |
public static void pickImage(Activity activity) { | |
Intent intent = getPickImageIntent(activity); | |
activity.startActivityForResult(intent, REQUEST_PICK); | |
} | |
private static Intent getPickImageIntent(Context context) { | |
Intent chooserIntent = null; | |
List<Intent> intentList = new ArrayList<>(); | |
Intent pickIntent = new Intent(Intent.ACTION_PICK, | |
MediaStore.Images.Media.EXTERNAL_CONTENT_URI); | |
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); | |
takePhotoIntent.putExtra("return-data", true); | |
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getTempFile(context))); | |
intentList = addIntentsToList(context, intentList, pickIntent); | |
intentList = addIntentsToList(context, intentList, takePhotoIntent); | |
if (intentList.size() > 0) { | |
chooserIntent = Intent.createChooser(intentList.remove(intentList.size() - 1), | |
context.getString(R.string.pick_image_intent_text)); | |
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentList.toArray(new Parcelable[]{})); | |
} | |
return chooserIntent; | |
} | |
private static List<Intent> 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); | |
Log.d(TAG, "Intent: " + intent.getAction() + " package: " + packageName); | |
} | |
return list; | |
} | |
public static void beginCrop(Activity activity, int resultCode, Intent imageReturnedIntent) { | |
Uri selectedImage = getImageUri(activity, resultCode, imageReturnedIntent); | |
if (selectedImage != null) { | |
Uri destination = Uri.fromFile(new File(activity.getExternalCacheDir(), CROPPED_IMAGE_NAME)); | |
Crop.of(selectedImage, destination).asSquare().start(activity); | |
} | |
} | |
private static Uri getImageUri(Activity activity, int resultCode, Intent imageReturnedIntent) { | |
Log.d(TAG, "getImageUri, resultCode: " + resultCode); | |
File imageFile = getTempFile(activity); | |
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(); | |
} | |
Log.d(TAG, "selectedImage: " + selectedImage); | |
} | |
return selectedImage; | |
} | |
public static Bitmap getImageCropped(Activity activity, int resultCode, Intent result, | |
ResizeType resizeType, int size) { | |
Bitmap bm = null; | |
if (resultCode == Activity.RESULT_OK) { | |
Uri croppedImageUri = Crop.getOutput(result); | |
bm = getImageResized(activity, croppedImageUri, resizeType, size); | |
int rotation = getRotation(activity, selectedImage, isCamera); | |
bm = rotate(bm, rotation); | |
} else if (resultCode == Crop.RESULT_ERROR) { | |
Toast.makeText(activity, Crop.getError(result).getMessage(), Toast.LENGTH_SHORT).show(); | |
} | |
return bm; | |
} | |
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 loading big images (e.g.: 2560*1920) | |
**/ | |
private static Bitmap getImageResized(Context context, Uri selectedImage, | |
ResizeType resizeType, int size) { | |
Bitmap bm; | |
int[] sampleSizes = new int[]{5, 3, 2, 1}; | |
int i = 0; | |
do { | |
bm = decodeBitmap(context, selectedImage, sampleSizes[i]); | |
Log.d(TAG, "Resizer: sample " + sampleSizes[i] + ", new bitmap width=" + | |
bm.getWidth() + " height=" + bm.getHeight()); | |
i++; | |
} while (bm.getWidth() < size && i < sampleSizes.length); | |
if (resizeType == ResizeType.FIXED_SIZE) { | |
bm = Bitmap.createScaledBitmap(bm, size, size, true); | |
} | |
Log.d(TAG, "Resizer: finalSize, width=" + bm.getWidth() + " height=" + bm.getHeight()); | |
return bm; | |
} | |
private static Bitmap decodeBitmap(Context context, Uri theUri, int sampleSize) { | |
Bitmap actuallyUsableBitmap = null; | |
BitmapFactory.Options options = new BitmapFactory.Options(); | |
options.inSampleSize = sampleSize; | |
AssetFileDescriptor fileDescriptor = null; | |
try { | |
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r"); | |
if (fileDescriptor != null) { | |
actuallyUsableBitmap = BitmapFactory.decodeFileDescriptor( | |
fileDescriptor.getFileDescriptor(), null, options); | |
} | |
} catch (FileNotFoundException e) { | |
e.printStackTrace(); | |
} | |
return actuallyUsableBitmap; | |
} | |
private static int getRotation(Context context, Uri imageUri, boolean isCamera) { | |
int rotation; | |
if (isCamera) { | |
rotation = getRotationFromCamera(context, imageUri); | |
} else { | |
rotation = getRotationFromGallery(context, imageUri); | |
} | |
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]); | |
return cursor.getInt(orientationColumnIndex); | |
} | |
private static Bitmap rotate(Bitmap bm, int rotation) { | |
if (rotation != 0) { | |
Matrix matrix = new Matrix(); | |
matrix.postRotate(rotation); | |
Bitmap bmOut = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); | |
return bmOut; | |
} | |
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 MainActivity { | |
public void pickImage() { | |
ImagePicker.pickImage(this); | |
} | |
@Override | |
protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |
if (resultCode == RESULT_OK && requestCode == ImagePicker.REQUEST_PICK) { | |
ImagePicker.beginCrop(this, resultCode, data); | |
} else if (requestCode == ImagePicker.REQUEST_CROP) { | |
Bitmap bitmap = ImagePicker.getImageCropped(this, resultCode, data, | |
ImagePicker.ResizeType.FIXED_SIZE, AVATAR_SIZE); | |
if (imagePickerListener != null) { | |
imagePickerListener.onImagePicked(bitmap); | |
} | |
Log.d(this, "bitmap picked: " + bitmap); | |
} else { | |
super.onActivityResult(requestCode, resultCode, data); | |
} | |
} | |
} |
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> | |
... | |
<activity android:name="com.soundcloud.android.crop.CropImageActivity" /> | |
</application> | |
</manifest> |
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
<style name="AppTheme" parent="Base.AppTheme"> | |
<item name="cropImageStyle">@style/Widget.CropImageView</item> | |
</style> | |
<style name="Widget.CropImageView" parent="android:Widget"> | |
<item name="showThirds">false</item> | |
<item name="showCircle">true</item> | |
<item name="showHandles">always</item> | |
<item name="highlightColor">?attr/colorAccent</item> | |
</style> |
Hi! I used your class in fragment but after crop image, onActivityResult not called, I cannot set my ImageView with image
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == ImagePicker.REQUEST_PICK) {
ImagePicker.beginCrop((LoginActivity)context, resultCode, data);
}
else if (requestCode == ImagePicker.REQUEST_CROP) {
Bitmap bitmap = ImagePicker.getImageCropped((LoginActivity)context, resultCode, data,
ImagePicker.ResizeType.FIXED_SIZE, 100);
this.profileImageView.setImageBitmap(bitmap);
Log.d("IMAGE PICKER", "bitmap picked: " + bitmap);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
Where I'm wrong?
Thank you
Hi LadyDeveloper, for you do this , you need the beginCrop receive Context and YourFragment , as follows:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == ImagePicker.REQUEST_PICK) {
ImagePicker.beginCrop(YourFragment, context, resultCode, data);
}
else if (requestCode == ImagePicker.REQUEST_CROP) {
Bitmap bitmap = ImagePicker.getImageCropped((LoginActivity)context, resultCode, data,
ImagePicker.ResizeType.FIXED_SIZE, 100);
this.profileImageView.setImageBitmap(bitmap);
Log.d("IMAGE PICKER", "bitmap picked: " + bitmap);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
In beginCrop method, you have to change the start method of Crop.of , and pass parameters of context and his Fragment.
public static void beginCrop(Fragment fragment, Context context, int resultCode, Intent imageReturnedIntent) {
Uri selectedImage = getImageUri(context, resultCode, imageReturnedIntent);
if (selectedImage != null) {
Uri destination = Uri.fromFile(new File(context.getExternalCacheDir(), CROPPED_IMAGE_NAME));
Crop.of(selectedImage, destination).asSquare().start(context, fragment);
}
}
Hope Helpful.
Thank you for the gist! Very helpful.
Hey bro can you please tell me the type of imagePickerListener in if check of onActivityForResults in activity? and where we declare this variable?
Hi, nice work.However, got "TransactionTooLargeException" in oreo when, any idea how to fix it?, this issue shows up when I click a button to show the image picker dialog
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great, very helpful! Can I suggest adding a public method that supports calling your ImagePicker from a Fragment: