Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ActivityResultContracts #145

Merged
merged 22 commits into from
Jul 1, 2021
Merged

Conversation

connyduck
Copy link
Contributor

@connyduck connyduck commented Jun 16, 2021

close #26

Description:

This adds ActivityResultContracts to make opening the file picker and the image cropper nice after onActivityResult has been deprecated

Check list for the Code Reviewer:

  • CHANGELOG
  • README
  • Wiki
  • Version Number

Open questions:

  • Should the old ActivityBuilder be marked @Deprecated?

@connyduck
Copy link
Contributor Author

This is a draft PR so we can discuss if this is the API we want. After we decided on the API we can add tests and cleanup the code/changelog/wiki etc.

Copy link
Member

@Canato Canato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some real quality work, thanks! Will help a lot the library an it users. 🎉

About the changes, before we have CropImage that would be responsible for all the calls from the user. Now we have CropImageContract and OpenChooser that make it split.

I would be keen to find a way the users could use the same contract for call the ImageCropper or just the ImagePicker.

Not sure how this would be possible, probably something like:

fun cropImageOptions(
    uri: Uri? = null,
    builder: CropImageContractOptions.() -> (Unit) = {}
): CropImageContractOptions {
    val options = CropImageContractOptions(uri, null, CropImageOptions())
    options.run(builder)
    return options
}

and

fun pickerImageOptions(
    includeCamera: Boolean = true,
): CropImageContractOptions {
    val options = CropImageContractOptions(null, includeCamera, CropImageOptions())
    options.run(builder)
    return options
}

where

data class CropImageContractOptions @JvmOverloads constructor(
     val uri: Uri?,
     val includeCamera: Boolean,
     val options: CropImageOptions,
}

So, if include != null we have picker we have
CropImage.getPickImageChooserIntent(...

Tell me what you think

README.md Outdated Show resolved Hide resolved
README.md Show resolved Hide resolved
@connyduck
Copy link
Contributor Author

OpenChooser is meant as a replacement for CropImage.getPickImageChooserIntent and that one only has the includeCamera options so I don't think we need a helper function like for the CropImageContractOptions.
If you want to have the camera option configurable when opening the CropImageActivity (currently it defaults to true), that should be a different PR.

@Canato
Copy link
Member

Canato commented Jun 18, 2021

OpenChooser is meant as a replacement for CropImage.getPickImageChooserIntent and that one only has the includeCamera options so I don't think we need a helper function like for the CropImageContractOptions.
If you want to have the camera option configurable when opening the CropImageActivity (currently it defaults to true), that should be a different PR.

Right now we have just on class with different methods CropImage.something I want to avoid different contracts for the same library, increase the complexity about what to use. My goal here is that we could keep it all in one contract.

@connyduck
Copy link
Contributor Author

Right now we have just on class with different methods CropImage.something I want to avoid different contracts for the same library, increase the complexity about what to use. My goal here is that we could keep it all in one contract.

I disagree, there are two very distinct usecases here

  • Show an image picker. Get back a uri.
  • Crop an image. Either provide the image or have the user choose one. Get back a result with information about the crop.

Putting both usecases into one contract makes that contract way more complex, it is easier to comprehend two small contracts.

If you want to keep things simple, we can discuss if the first usecase should even be part of this library. AndroidX provides the GetContent contract which does almost the same (minus the camera option).

@Canato
Copy link
Member

Canato commented Jun 21, 2021

Right now we have just on class with different methods CropImage.something I want to avoid different contracts for the same library, increase the complexity about what to use. My goal here is that we could keep it all in one contract.

I disagree, there are two very distinct usecases here

  • Show an image picker. Get back a uri.
  • Crop an image. Either provide the image or have the user choose one. Get back a result with information about the crop.

Putting both usecases into one contract makes that contract way more complex, it is easier to comprehend two small contracts.

If you want to keep things simple, we can discuss if the first usecase should even be part of this library. AndroidX provides the GetContent contract which does almost the same (minus the camera option).

I like this discussion, maybe we should extract the picker library, so the user could decide what to import in his project.

Anyway, this is no the PR for this.
We have CropImageContract let's use PickerImageContract for consistency. And we can keep both.

We will just need to update readme, docs and wiki later.

Copy link
Member

@Canato Canato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are getting closer =D

Copy link
Member

@Canato Canato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a great solution.

Now is about update the sample apps and documentation.
If you want access to wiki update let me know or you can point where I need to update something and I will do it.

Congratulations for the work, version 4.0 coming.

@connyduck connyduck marked this pull request as ready for review June 24, 2021 18:12
@connyduck connyduck requested a review from a team as a code owner June 24, 2021 18:12
Copy link
Member

@Canato Canato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 nice so far.

Just to keep track, what next steps are you planning to do?

Maybe we could add some checklist in the description, so we keep the track.

@connyduck connyduck force-pushed the activity_result_contract branch from 7167e73 to 4a71b55 Compare June 24, 2021 18:32
@connyduck
Copy link
Contributor Author

I think I am pretty much done.

I would appreciate it if you could update the Wiki yourself @Canato. Basically all occurences of the old methods have to be replaced with the new contracts. And I guess a migration guide would also be nice.

Copy link
Member

@Canato Canato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some tasks we need to finish before merge:

  • Need to update SCameraFragment and SCameraFragmentJava to the new Contract and remove the deprecated methods.

  • Change method onActivityResult in SCameraContract, SCameraContractJava

  • Need to update CropImage.kt documentation where it say Result will be received in onActivityResult...

Don't worry I will update the wiki!

@connyduck
Copy link
Contributor Author

Need to update CropImage.kt documentation where it say Result will be received in onActivityResult...

No, this is still true if you use those methods. I updated the other things.

@Canato
Copy link
Member

Canato commented Jun 26, 2021

Need to update CropImage.kt documentation where it say Result will be received in onActivityResult...

No, this is still true if you use those methods. I updated the other things.

You are not wrong, but would prefer the documentation and examples/samples always related to the latest changes. So it explain to the user the way the library should be used

@connyduck
Copy link
Contributor Author

You are not wrong, but would prefer the documentation and examples/samples always related to the latest changes. So it explain to the user the way the library should be used

The samples are up to date now, and the old methods are deprecated with an explanation what to use instead. That should be enough.

Copy link
Member

@Canato Canato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small points before we can merge.

The most important one is the test and return of UriFilePath since this give so much headache this year. More comments on the classes.

Please on the SCamera changes, apply to the Java code where make sense too please.

Comment on lines -99 to -105
val bitmap = context?.let { CropImage.getActivityResult(data)?.getBitmap(it) }
Log.v(
"File Path",
context
?.let { CropImage.getActivityResult(data)?.getUriFilePath(it) }
.toString()
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this two don't have direct value for the code, but we need to keep the logic for method testing and user example of how getting the bitmap and UriFilePath (big issues on this latest one)

So we would need:

val bitmap = result.bitmap
Log.v("File Path", context?.let { result.getUriFilePath(it) }.toString())


override fun onPickImageResult(resultUri: Uri?) {
if (resultUri != null) {
Log.v("File Path", resultUri.toString())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realise we are not returning UriFilePath just UriContent. Will add a comment into PickImageContract class

if (intent != null) {
context?.let {
context = null
return getPickImageResultUriContent(it, intent)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is returning only UriContent missing UriFilePath. We need to return both.

Sorry I should have spotted this before.

To give a little more context. With the Android 10/11 changes UriContent doesn't represent a file path anymore, using the content:// schema for it URI. What is right by definition.
But many users of this library and other ones, got used of using URI as file path for saving the crop image.
So we split UriContent and UriFilePath as user request. In previous versions of Android UriContent can be in the file schema, in recent ones in content schema.
So now we have UriFilePath that always represent a file schema.

Why not put all in this file schema? To save space/memory, since some users doesn't need it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what do we do? Have it return a custom class with two uris?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is better that we avoid the filePath generation, if the user will not use it.
But would be nice to make the method available for usage, so we generate only if the user need.

No idea the best way of doing this =/

Copy link
Contributor Author

@connyduck connyduck Jun 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make PickImageContract an open class so users can override the parseResult method and parse the result differently

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@@ -56,6 +53,15 @@ internal class SCameraFragment :
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean -> presenter.onPermissionResult(isGranted) }

private val pickImage =
registerForActivityResult(PickImageContract(), presenter::onPickImageResult)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, let's avoid to use the reflection ::. I agree is much better in an aesthetics perspective, I tend to avoid reflection in general + some times Android Studio go crazy not finding it usages making harder for debug.

registerForActivityResult(PickImageContract()) { presenter.onPickImageResult(it) }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤨

Copy link
Member

@Canato Canato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Day for celebration!!! 🎉 🎉

Amazing work @connyduck
👏 👏 👏 👏 👏

Will update the Wiki before release, hopefully will do this week

@Canato Canato merged commit 630ed8f into CanHub:main Jul 1, 2021
@Canato Canato mentioned this pull request Nov 24, 2021
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

onActivityResult is deprecated
2 participants