Skip to content

Commit

Permalink
Add feature for user to be able to pick image source (#233)
Browse files Browse the repository at this point in the history
* Add pick image source options

* remove includeDocument
  • Loading branch information
Zikstar authored Oct 6, 2021
1 parent 138dd30 commit a8e07dc
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 113 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.

## [unreleased 3.3.6] - 05/10/21
### Added
- Added `PickImageContractOptions` to enable users specify image source [#226](https://github.com/CanHub/Android-Image-Cropper/issues/226)

## [3.3.5] - 07/09/21
### Fixed
- Set output uri ignored [#207](https://github.com/CanHub/Android-Image-Cropper/issues/207)
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ class MainActivity {
}
)

//start picker to get image for cropping from only gallery and then use the image in
//cropping activity
cropImage.launch(
options {
setImagePickerContractOptions(
PickImageContractOptions(includeGallery = true, includeCamera = false)
)
}
)

// start cropping activity for pre-acquired image saved on the device and customize settings
cropImage.launch(
options(uri = imageUri) {
Expand Down
32 changes: 18 additions & 14 deletions cropper/src/main/java/com/canhub/cropper/CropImage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import com.canhub.cropper.common.CommonVersionCheck
import com.canhub.cropper.common.CommonVersionCheck.isAtLeastQ29
import com.canhub.cropper.utils.getFilePathFromUri
import java.io.File
import java.util.ArrayList

/**
* Helper to simplify crop image work like starting pick-image acitvity and handling camera/gallery
Expand All @@ -63,6 +62,11 @@ object CropImage {
*/
const val CROP_IMAGE_EXTRA_OPTIONS = "CROP_IMAGE_EXTRA_OPTIONS"

/**
* The key used to pass options for the picker of image to crop to [CropImageActivity].
*/
const val PICK_IMAGE_SOURCE_OPTIONS = "PICK_IMAGE_SOURCE_OPTIONS"

/**
* The key used to pass crop image bundle data to [CropImageActivity].
*/
Expand Down Expand Up @@ -165,8 +169,7 @@ object CropImage {
return getPickImageChooserIntent(
context = context,
title = context.getString(R.string.pick_image_intent_chooser_title),
includeDocuments = false,
includeCamera = true
options = PickImageContractOptions()
)
}

Expand All @@ -185,22 +188,24 @@ object CropImage {
fun getPickImageChooserIntent(
context: Context,
title: CharSequence?,
includeDocuments: Boolean, // todo, remove this. Should always be false for image to crop.
includeCamera: Boolean,
options: PickImageContractOptions
): Intent {
val includeCamera = options.includeCamera
val includeGallery = options.includeGallery
val allIntents: MutableList<Intent> = ArrayList()
val packageManager = context.packageManager
// collect all camera intents if Camera permission is available
if (!isExplicitCameraPermissionRequired(context) && includeCamera) {
allIntents.addAll(getCameraIntents(context, packageManager))
}
allIntents.addAll(
getGalleryIntents(
packageManager,
Intent.ACTION_GET_CONTENT,
includeDocuments
if (includeGallery) {
allIntents.addAll(
getGalleryIntents(
packageManager,
Intent.ACTION_GET_CONTENT
)
)
)
}
// Create a chooser from the main intent
val chooserIntent = Intent.createChooser(allIntents.removeAt(allIntents.size - 1), title)
// Add all other intents
Expand Down Expand Up @@ -266,12 +271,11 @@ object CropImage {
// todo this need be public?
fun getGalleryIntents(
packageManager: PackageManager,
action: String?,
includeDocuments: Boolean,
action: String?
): List<Intent> {
val intents: MutableList<Intent> = ArrayList()
val galleryIntent = Intent(action)
galleryIntent.type = if (includeDocuments) "*/*" else "image/*"
galleryIntent.type = "image/*"
galleryIntent.addCategory(Intent.CATEGORY_OPENABLE)
var listGallery = packageManager.queryIntentActivities(galleryIntent, 0)
if (isAtLeastQ29() && listGallery.size > 2) {
Expand Down
71 changes: 39 additions & 32 deletions cropper/src/main/java/com/canhub/cropper/CropImageActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ open class CropImageActivity :
/**
* the options that were set for the crop image
*/
lateinit var options: CropImageOptions
lateinit var cropImageOptions: CropImageOptions
lateinit var pickImageOptions: PickImageContractOptions

/** The crop image view library widget used in the activity */
private var cropImageView: CropImageView? = null
Expand All @@ -52,7 +53,12 @@ open class CropImageActivity :
setCropImageView(binding.cropImageView)
val bundle = intent.getBundleExtra(CropImage.CROP_IMAGE_EXTRA_BUNDLE)
cropImageUri = bundle?.getParcelable(CropImage.CROP_IMAGE_EXTRA_SOURCE)
options = bundle?.getParcelable(CropImage.CROP_IMAGE_EXTRA_OPTIONS) ?: CropImageOptions()
cropImageOptions =
bundle?.getParcelable(CropImage.CROP_IMAGE_EXTRA_OPTIONS) ?: CropImageOptions()
pickImageOptions =
bundle?.getParcelable(CropImage.PICK_IMAGE_SOURCE_OPTIONS) ?: PickImageContractOptions(
includeCamera = true
)

if (savedInstanceState == null) {
if (cropImageUri == null || cropImageUri == Uri.EMPTY) {
Expand All @@ -63,7 +69,7 @@ open class CropImageActivity :
CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE
)
} else {
pickImage.launch(true)
pickImage.launch(pickImageOptions)
}
} else if (
cropImageUri?.let {
Expand All @@ -84,9 +90,10 @@ open class CropImageActivity :

supportActionBar?.let {
title =
if (options.activityTitle.isNotEmpty()) options.activityTitle else resources.getString(
R.string.crop_image_activity_title
)
if (cropImageOptions.activityTitle.isNotEmpty())
cropImageOptions.activityTitle
else
resources.getString(R.string.crop_image_activity_title)
it.setDisplayHomeAsUpEnabled(true)
}
}
Expand All @@ -106,37 +113,37 @@ open class CropImageActivity :
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.crop_image_menu, menu)

if (!options.allowRotation) {
if (!cropImageOptions.allowRotation) {
menu.removeItem(R.id.ic_rotate_left_24)
menu.removeItem(R.id.ic_rotate_right_24)
} else if (options.allowCounterRotation) {
} else if (cropImageOptions.allowCounterRotation) {
menu.findItem(R.id.ic_rotate_left_24).isVisible = true
}

if (!options.allowFlipping) menu.removeItem(R.id.ic_flip_24)
if (!cropImageOptions.allowFlipping) menu.removeItem(R.id.ic_flip_24)

if (options.cropMenuCropButtonTitle != null) {
menu.findItem(R.id.crop_image_menu_crop).title = options.cropMenuCropButtonTitle
if (cropImageOptions.cropMenuCropButtonTitle != null) {
menu.findItem(R.id.crop_image_menu_crop).title = cropImageOptions.cropMenuCropButtonTitle
}
var cropIcon: Drawable? = null
try {
if (options.cropMenuCropButtonIcon != 0) {
cropIcon = ContextCompat.getDrawable(this, options.cropMenuCropButtonIcon)
if (cropImageOptions.cropMenuCropButtonIcon != 0) {
cropIcon = ContextCompat.getDrawable(this, cropImageOptions.cropMenuCropButtonIcon)
menu.findItem(R.id.crop_image_menu_crop).icon = cropIcon
}
} catch (e: Exception) {
Log.w("AIC", "Failed to read menu crop drawable", e)
}
if (options.activityMenuIconColor != 0) {
updateMenuItemIconColor(menu, R.id.ic_rotate_left_24, options.activityMenuIconColor)
updateMenuItemIconColor(menu, R.id.ic_rotate_right_24, options.activityMenuIconColor)
updateMenuItemIconColor(menu, R.id.ic_flip_24, options.activityMenuIconColor)
if (cropImageOptions.activityMenuIconColor != 0) {
updateMenuItemIconColor(menu, R.id.ic_rotate_left_24, cropImageOptions.activityMenuIconColor)
updateMenuItemIconColor(menu, R.id.ic_rotate_right_24, cropImageOptions.activityMenuIconColor)
updateMenuItemIconColor(menu, R.id.ic_flip_24, cropImageOptions.activityMenuIconColor)

if (cropIcon != null) {
updateMenuItemIconColor(
menu,
R.id.crop_image_menu_crop,
options.activityMenuIconColor
cropImageOptions.activityMenuIconColor
)
}
}
Expand All @@ -146,8 +153,8 @@ open class CropImageActivity :
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.crop_image_menu_crop -> cropImage()
R.id.ic_rotate_left_24 -> rotateImage(-options.rotationDegrees)
R.id.ic_rotate_right_24 -> rotateImage(options.rotationDegrees)
R.id.ic_rotate_left_24 -> rotateImage(-cropImageOptions.rotationDegrees)
R.id.ic_rotate_right_24 -> rotateImage(cropImageOptions.rotationDegrees)
R.id.ic_flip_24_horizontally -> cropImageView?.flipImageHorizontally()
R.id.ic_flip_24_vertically -> cropImageView?.flipImageVertically()
android.R.id.home -> setResultCancel()
Expand Down Expand Up @@ -205,17 +212,17 @@ open class CropImageActivity :
} else if (requestCode == CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE) {
// Irrespective of whether camera permission was given or not, we show the picker
// The picker will not add the camera intent if permission is not available
pickImage.launch(true)
pickImage.launch(pickImageOptions)
} else super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}

override fun onSetImageUriComplete(view: CropImageView, uri: Uri, error: Exception?) {
if (error == null) {
if (options.initialCropWindowRectangle != null)
cropImageView?.cropRect = options.initialCropWindowRectangle
if (cropImageOptions.initialCropWindowRectangle != null)
cropImageView?.cropRect = cropImageOptions.initialCropWindowRectangle

if (options.initialRotation > -1)
cropImageView?.rotatedDegrees = options.initialRotation
if (cropImageOptions.initialRotation > -1)
cropImageView?.rotatedDegrees = cropImageOptions.initialRotation
} else setResult(null, error, 1)
}

Expand All @@ -227,14 +234,14 @@ open class CropImageActivity :
* Execute crop image and save the result tou output uri.
*/
open fun cropImage() {
if (options.noOutputImage) setResult(null, null, 1)
if (cropImageOptions.noOutputImage) setResult(null, null, 1)
else cropImageView?.croppedImageAsync(
options.outputCompressFormat,
options.outputCompressQuality,
options.outputRequestWidth,
options.outputRequestHeight,
options.outputRequestSizeOptions,
options.customOutputUri,
cropImageOptions.outputCompressFormat,
cropImageOptions.outputCompressQuality,
cropImageOptions.outputRequestWidth,
cropImageOptions.outputRequestHeight,
cropImageOptions.outputRequestSizeOptions,
cropImageOptions.customOutputUri,
)
}

Expand Down
5 changes: 3 additions & 2 deletions cropper/src/main/java/com/canhub/cropper/CropImageContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ class CropImageContract :
ActivityResultContract<CropImageContractOptions, CropImageView.CropResult>() {

override fun createIntent(context: Context, input: CropImageContractOptions): Intent {
input.options.validate()
input.cropImageOptions.validate()
return Intent(context, CropImageActivity::class.java).apply {
val bundle = Bundle()
bundle.putParcelable(CropImage.CROP_IMAGE_EXTRA_SOURCE, input.uri)
bundle.putParcelable(CropImage.CROP_IMAGE_EXTRA_OPTIONS, input.options)
bundle.putParcelable(CropImage.CROP_IMAGE_EXTRA_OPTIONS, input.cropImageOptions)
bundle.putParcelable(CropImage.PICK_IMAGE_SOURCE_OPTIONS, input.pickImageOptions)
putExtra(CropImage.CROP_IMAGE_EXTRA_BUNDLE, bundle)
}
}
Expand Down
Loading

0 comments on commit a8e07dc

Please sign in to comment.