Android Image Picker Tutorial — Pick image from Gallery/Camera

Zeba Rahman
fabcoding
Published in
5 min readAug 21, 2019

We will be using Dexter library for permissions; and uCrop for cropping. Add these dependencies to you app level build.gradle file.

implementation "com.karumi:dexter:5.0.0"
implementation 'com.github.yalantis:ucrop:2.2.2'

Add the necessary permissions in AndroidManifest.xml.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

In the same file, we need to add file provider within the application tag; and activities for cropper and picker

<application
..........>

<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.Design.NoActionBar" />

<!-- cache directory file provider paths -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

<activity android:name=".helpers.ImagePickerActivity">
</activity>

</application>

Create a file res/xml/file_paths.xml, with the following content.

<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-cache-path
name="cache"
path="camera" />
</paths>

Now let’s create the activity. Create the layout.

act_image_picker.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".helpers.ImagePickerActivity">

</androidx.constraintlayout.widget.ConstraintLayout>

Create the activity class ImagePickerActivity.java.

import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
import android.widget.Toast;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import com.kugoapp.kugoguide.R;
import com.yalantis.ucrop.UCrop;

import java.io.File;
import java.util.List;

import static androidx.core.content.FileProvider.getUriForFile;

public class ImagePickerActivity extends AppCompatActivity {
private static final String TAG = ImagePickerActivity.class.getSimpleName();
public static final String INTENT_IMAGE_PICKER_OPTION = "image_picker_option";
public static final String INTENT_ASPECT_RATIO_X = "aspect_ratio_x";
public static final String INTENT_ASPECT_RATIO_Y = "aspect_ratio_Y";
public static final String INTENT_LOCK_ASPECT_RATIO = "lock_aspect_ratio";
public static final String INTENT_IMAGE_COMPRESSION_QUALITY = "compression_quality";
public static final String INTENT_SET_BITMAP_MAX_WIDTH_HEIGHT = "set_bitmap_max_width_height";
public static final String INTENT_BITMAP_MAX_WIDTH = "max_width";
public static final String INTENT_BITMAP_MAX_HEIGHT = "max_height";


public static final int REQUEST_IMAGE_CAPTURE = 0;
public static final int REQUEST_GALLERY_IMAGE = 1;

private boolean lockAspectRatio = false, setBitmapMaxWidthHeight = false;
private int ASPECT_RATIO_X = 16, ASPECT_RATIO_Y = 9, bitmapMaxWidth = 1000, bitmapMaxHeight = 1000;
private int IMAGE_COMPRESSION = 80;
public static String fileName;

public interface PickerOptionListener {
void onTakeCameraSelected();

void onChooseGallerySelected();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_image_picker);

Intent intent = getIntent();
if (intent == null) {
Toast.makeText(getApplicationContext(), "Image picker option is missing", Toast.LENGTH_LONG).show();
return;
}

ASPECT_RATIO_X = intent.getIntExtra(INTENT_ASPECT_RATIO_X, ASPECT_RATIO_X);
ASPECT_RATIO_Y = intent.getIntExtra(INTENT_ASPECT_RATIO_Y, ASPECT_RATIO_Y);
IMAGE_COMPRESSION = intent.getIntExtra(INTENT_IMAGE_COMPRESSION_QUALITY, IMAGE_COMPRESSION);
lockAspectRatio = intent.getBooleanExtra(INTENT_LOCK_ASPECT_RATIO, false);
setBitmapMaxWidthHeight = intent.getBooleanExtra(INTENT_SET_BITMAP_MAX_WIDTH_HEIGHT, false);
bitmapMaxWidth = intent.getIntExtra(INTENT_BITMAP_MAX_WIDTH, bitmapMaxWidth);
bitmapMaxHeight = intent.getIntExtra(INTENT_BITMAP_MAX_HEIGHT, bitmapMaxHeight);

int requestCode = intent.getIntExtra(INTENT_IMAGE_PICKER_OPTION, -1);
if (requestCode == REQUEST_IMAGE_CAPTURE) {
takeCameraImage();
} else {
chooseImageFromGallery();
}
}

public static void showImagePickerOptions(Context context, PickerOptionListener listener) {
// setup the alert builder
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Set image");

// add a list
String[] animals = {"Take a picture", "Choose from gallery"};
builder.setItems(animals, (dialog, which) -> {
switch (which) {
case 0:
listener.onTakeCameraSelected();
break;
case 1:
listener.onChooseGallerySelected();
break;
}
});

// create and show the alert dialog
AlertDialog dialog = builder.create();
dialog.show();
}

private void takeCameraImage() {
Dexter.withActivity(this)
.withPermissions(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
fileName = System.currentTimeMillis() + ".jpg";
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, getCacheImagePath(fileName));
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}

@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
token.continuePermissionRequest();
}
}).check();
}

private void chooseImageFromGallery() {
Dexter.withActivity(this)
.withPermissions(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
Intent pickPhoto = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(pickPhoto, REQUEST_GALLERY_IMAGE);
}
}

@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
token.continuePermissionRequest();
}
}).check();

}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_IMAGE_CAPTURE:
if (resultCode == RESULT_OK) {
cropImage(getCacheImagePath(fileName));
} else {
setResultCancelled();
}
break;
case REQUEST_GALLERY_IMAGE:
if (resultCode == RESULT_OK) {
Uri imageUri = data.getData();
cropImage(imageUri);
} else {
setResultCancelled();
}
break;
case UCrop.REQUEST_CROP:
if (resultCode == RESULT_OK) {
handleUCropResult(data);
} else {
setResultCancelled();
}
break;
case UCrop.RESULT_ERROR:
final Throwable cropError = UCrop.getError(data);
Log.e(TAG, "Crop error: " + cropError);
setResultCancelled();
break;
default:
setResultCancelled();
}
}

private void cropImage(Uri sourceUri) {
Uri destinationUri = Uri.fromFile(new File(getCacheDir(), queryName(getContentResolver(), sourceUri)));
UCrop.Options options = new UCrop.Options();
options.setCompressionQuality(IMAGE_COMPRESSION);
options.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary));
options.setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimary));
options.setActiveWidgetColor(ContextCompat.getColor(this, R.color.colorPrimary));

if (lockAspectRatio)
options.withAspectRatio(ASPECT_RATIO_X, ASPECT_RATIO_Y);

if (setBitmapMaxWidthHeight)
options.withMaxResultSize(bitmapMaxWidth, bitmapMaxHeight);

UCrop.of(sourceUri, destinationUri)
.withOptions(options)
.start(this);
}

private void handleUCropResult(Intent data) {
if (data == null) {
setResultCancelled();
return;
}
final Uri resultUri = UCrop.getOutput(data);
setResultOk(resultUri);
}

private void setResultOk(Uri imagePath) {
Intent intent = new Intent();
intent.putExtra("path", imagePath);
setResult(Activity.RESULT_OK, intent);
finish();
}

private void setResultCancelled() {
Intent intent = new Intent();
setResult(Activity.RESULT_CANCELED, intent);
finish();
}

private Uri getCacheImagePath(String fileName) {
File path = new File(getExternalCacheDir(), "camera");
if (!path.exists()) path.mkdirs();
File image = new File(path, fileName);
return getUriForFile(ImagePickerActivity.this, getPackageName() + ".provider", image);
}

private static String queryName(ContentResolver resolver, Uri uri) {
Cursor returnCursor =
resolver.query(uri, null, null, null, null);
assert returnCursor != null;
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
String name = returnCursor.getString(nameIndex);
returnCursor.close();
return name;
}

/**
* Calling this will delete the images from cache directory
* useful to clear some memory
*/
public static void clearCache(Context context) {
File path = new File(context.getExternalCacheDir(), "camera");
if (path.exists() && path.isDirectory()) {
for (File child : path.listFiles()) {
child.delete();
}
}
}
}

Now, in your main activity, from where you intend to launch the picker, add the following:

public void pickImg() {
Dexter.withActivity(this)
.withPermissions(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
showImagePickerOptions();
}

if (report.isAnyPermissionPermanentlyDenied()) {
showSettingsDialog();
}
}

@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
token.continuePermissionRequest();
}
}).check();
}


public void showImagePickerOptions() {

ImagePickerActivity.showImagePickerOptions(this, new ImagePickerActivity.PickerOptionListener() {
@Override
public void onTakeCameraSelected() {
launchCameraIntent();
}

@Override
public void onChooseGallerySelected() {
launchGalleryIntent();

}
});

}


private void launchCameraIntent() {
Intent intent = new Intent(MainActivity.this, ImagePickerActivity.class);
intent.putExtra(ImagePickerActivity.INTENT_IMAGE_PICKER_OPTION, ImagePickerActivity.REQUEST_IMAGE_CAPTURE);

// setting aspect ratio
intent.putExtra(ImagePickerActivity.INTENT_LOCK_ASPECT_RATIO, true);
intent.putExtra(ImagePickerActivity.INTENT_ASPECT_RATIO_X, 1); // 16x9, 1x1, 3:4, 3:2
intent.putExtra(ImagePickerActivity.INTENT_ASPECT_RATIO_Y, 1);

// setting maximum bitmap width and height
intent.putExtra(ImagePickerActivity.INTENT_SET_BITMAP_MAX_WIDTH_HEIGHT, true);
intent.putExtra(ImagePickerActivity.INTENT_BITMAP_MAX_WIDTH, 1000);
intent.putExtra(ImagePickerActivity.INTENT_BITMAP_MAX_HEIGHT, 1000);

startActivityForResult(intent, REQUEST_IMAGE);
}

private void launchGalleryIntent() {
Intent intent = new Intent(MainActivity.this, ImagePickerActivity.class);
intent.putExtra(ImagePickerActivity.INTENT_IMAGE_PICKER_OPTION, ImagePickerActivity.REQUEST_GALLERY_IMAGE);

// setting aspect ratio
intent.putExtra(ImagePickerActivity.INTENT_LOCK_ASPECT_RATIO, true);
intent.putExtra(ImagePickerActivity.INTENT_ASPECT_RATIO_X, 1); // 16x9, 1x1, 3:4, 3:2
intent.putExtra(ImagePickerActivity.INTENT_ASPECT_RATIO_Y, 1);
startActivityForResult(intent, REQUEST_IMAGE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == REQUEST_IMAGE) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = data.getParcelableExtra("path");
try {
// You can update this bitmap to your server
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);

// loading profile image from local cache
//loadProfile(uri.toString());

} catch (IOException e) {
e.printStackTrace();
}
}
}
}

/**
* Showing Alert Dialog with Settings option
* Navigates user to app settings
* NOTE: Keep proper title and message depending on your app
*/
private void showSettingsDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Grant Permissions");
builder.setMessage("This app needs permission to use this feature. You can grant them in app settings.");
builder.setPositiveButton("GOTO SETTINGS", (dialog, which) -> {
dialog.cancel();
openSettings();
});
builder.setNegativeButton(getString(android.R.string.cancel), (dialog, which) -> dialog.cancel());
builder.show();

}

// navigating user to app settings
private void openSettings() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, 101);
}

You’re done. Call the function pickImg() from your button click event, or wherever you wish to do so.

Originally published at Fabcoding.

--

--