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.
Leave A Comment