-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
[android][expo-media-library] migrate to new modules API #20232
[android][expo-media-library] migrate to new modules API #20232
Conversation
Not sure why the spotless check is failing here. I have ran it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking very promising, but I'll have to run through your changes one more time ;)
CodedException("Media library is corrupted") | ||
|
||
class EmptyAlbumException : | ||
CodedException("Found album is empty.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodedException("Found album is empty.") | |
CodedException("Found album is empty") |
We stoped using dots in the error messages.
CodedException("Found album is empty.") | ||
|
||
class AlbumPathException : | ||
CodedException("Couldn't get album path.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodedException("Couldn't get album path.") | |
CodedException("Couldn't get album path") |
CodedException("Couldn't find album") | ||
|
||
class AssetQueryException : | ||
CodedException("Could not get asset. Query returns null.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodedException("Could not get asset. Query returns null.") | |
CodedException("Could not get asset. Query returns null") |
CodedException(message) | ||
|
||
class AssetException : | ||
CodedException("Could not add image to gallery.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodedException("Could not add image to gallery.") | |
CodedException("Could not add image to gallery") |
CodedException("Could not add image to gallery.") | ||
|
||
class ContentEntryException : | ||
CodedException("Could not create content entry.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CodedException("Could not create content entry.") | |
CodedException("Could not create content entry") |
CreateAsset(context, localUri, promise, false) | ||
.execute() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CreateAsset(context, localUri, promise, false) | |
.execute() | |
throwUnlessPermissionsGranted { | |
CreateAsset(context, localUri, promise, false) | |
.execute() | |
} |
Thanks @lukmccall I'll wait until you finish your review before making any changes |
|
||
videosObserver = | ||
MediaStoreContentObserver(handler, MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO) | ||
AsyncFunction("startObserving") { _: Promise -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AsyncFunction("startObserving") { _: Promise -> | |
OnStartObserving { |
You can use the dedicated component to implement startObserving
.
contentResolver.unregisterContentObserver(it) | ||
videosObserver = null | ||
|
||
AsyncFunction("stopObserving") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AsyncFunction("stopObserving") { | |
OnStopObserving { |
The same applies to stopObserving
state = PromiseState.REJECTED | ||
rejectCode = code | ||
rejectThrowable = e | ||
rejectThrowable = exception | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're sure that those functions aren't used by anything else and can be safely deleted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, you are right. This was a mistake. I'll revert. Probably need a mock for the new Promise type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, there is one, just need to change the imports
val albumsMap = assets | ||
// All files should have mime type, but if not, we can safely assume that | ||
// those without mime type shouldn't be move | ||
.groupBy { it.parentFile } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.groupBy { it.parentFile } | |
.filter { it.mimeType != null } | |
.groupBy { it.parentFile } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can mimeType ever be null? It's typed as String
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right. We don't have to check that. But in that case, we can remove the comment above those lines.
@@ -24,11 +19,11 @@ internal class AddAssetsToAlbum( | |||
private val albumId: String, | |||
copyToAlbum: Boolean, | |||
private val promise: Promise | |||
) : AsyncTask<Void?, Void?, Void?>() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One very important thing here. You can't just remove async tasks and not replace them with some other forms of async solutions like coroutines. Your current solution will block the native module queue because all the disk operations will be dispatched to the same place where all calls from js are processed. This can lead to a lot of lags in your application, especially on low-end devices.
Could you replace the AsyncTask
with coroutines? To do it, you have to create a coroutine scope in the module and then dispatch all the work there. Here you can find a small example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem, I was wondering about this too. I wasn't sure whether to use the Coroutine builder on the AsyncFunction or create a scope myself.
@lukmccall would adding the promise assertion functions from |
Yes, we want to migrate functionalities from the legacy package anyway ;) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job 🔥
Thanks for applying all my comments ;)
packages/expo-modules-test-core/android/src/main/java/org/unimodules/test/core/PromiseMock.kt
Show resolved
Hide resolved
packages/expo-modules-test-core/android/src/main/java/org/unimodules/test/core/TestUtils.kt
Show resolved
Hide resolved
val albumsMap = assets | ||
// All files should have mime type, but if not, we can safely assume that | ||
// those without mime type shouldn't be move | ||
.groupBy { it.parentFile } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right. We don't have to check that. But in that case, we can remove the comment above those lines.
Why
Migrate native module to use new modules API
How
This was a much larger migration than any I have attempted before so I will need feedback from the team. Maybe something of this size is not wanted from external contributors.
Followed the typical migration steps.
Note
getAssetsAsync
accepts an options object. One of the parameters is an array that can be a mixed type. A string or a tuple. I wasn't sure how to type this. The Either type is still marked as experimental so I didn't want to use it. Also, regarding this options type, thefirst
variable was used as anInt
sometimes and aDouble
at other times. I settled on aDouble
.ExponentMediaLibrary
toExpoMediaLibrary
promise.resolve(null)
I'm aware this is not usually necessary but without it, some functions would never resolveTest Plan
Checked by running the bare expo app and running the existing test suite.
After the first iteration, the checks in the bare app all passed but the native unit tests were broken because of the changes in the Pomise API. These were fixed, and now all 43 native tests pass. The JS tests all pass as before.