-
Notifications
You must be signed in to change notification settings - Fork 6
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
Introducing Frame Saving process into a Service #278
Introducing Frame Saving process into a Service #278
Conversation
You can test the changes on this Pull Request by downloading the APK here. |
app/src/main/java/com/automattic/portkey/compose/frame/FrameSaveService.kt
Show resolved
Hide resolved
app/src/main/java/com/automattic/portkey/compose/frame/FrameSaveService.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/automattic/portkey/compose/frame/FrameSaveService.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/automattic/portkey/compose/frame/FrameSaveService.kt
Outdated
Show resolved
Hide resolved
Co-Authored-By: Joel Dean <[email protected]>
…SaveManager is itself a CoorutineScope that gets definitively cancelled when the Service is destroyed
Ok! Figured out a few more things now, describing them here:
This should be ready for another round @jd-alexander 🙇 |
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.
Thanks for these changes @mzorz I tested this and it worked!
I even exited the app and the service continued in the background and I was able to see the newly created stories in my gallery.
I stumbled on two crashes that you could resolve before we proceed.
Process: com.automattic.portkey, PID: 28239
java.lang.IllegalStateException: Fragment already added: EmojiPickerFragment{c8ed3ee (de450151-c4e1-4835-a2c7-c00ac471d1cb)}
at androidx.fragment.app.FragmentManagerImpl.addFragment(FragmentManagerImpl.java:1379)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:399)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.get(ArrayList.java:437)
at java.util.Collections$UnmodifiableList.get(Collections.java:1356)
at com.automattic.portkey.compose.story.StoryViewModel.getCurrentStoryFrameAt(StoryViewModel.kt:84)
at com.automattic.portkey.compose.ComposeLoopFrameActivity.addCurrentViewsToFrameAtIndex(ComposeLoopFrameActivity.kt:598)
at com.automattic.portkey.compose.ComposeLoopFrameActivity.access$addCurrentViewsToFrameAtIndex(ComposeLoopFrameActivity.kt:105)
at com.automattic.portkey.compose.ComposeLoopFrameActivity$addClickListeners$9.onClick(ComposeLoopFrameActivity.kt:542)
at android.view.View.performClick(View.java:7125)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27336)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Awesome! Glad it worked 🎉
Nice spotting those! I think both issues are unrelated to this specific PR, but I'm so glad you've run into those and reported here 🙇.
Let me know if there are any other observations that I'm happy to look into @jd-alexander ! |
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.
Let me know if there are any other observations that I'm happy to look into @jd-alexander !
Okay great! Yes, once there aren't related to this PR and their are issues to track them then we are all good here and ready to go!
LGTM 🚢
Thank you very much for your review 🙇 ! Going to merge into the feature branch and update the next PR's base branch 👍 |
Closes #273
Intro
This PR takes the former work done in order to implement a Story Frames Save architecture (see #257 and related PRs) and encapsulate the work into a Service.
This is needed given sometimes, saving a Story's frames may take time (especially if they have video-background in one or more of their Story frames), and we don't want to block the user on a screen, waiting for things to get processed.
Services
The Android documentation describes a few of the possibilities we have for performing background work. We're going to use a Foreground Service given that's the mechanism that best fits the description of our use case as explained here.
In our case, the user taps on
Save
button and makes it explicit that they want their Story and Story frames to get saved. Hence, it makes sense to encapsulate this work in a Foreground Service.In our specific case, we can roughly say there are 2 big parts to finishing a Story:
For now, this PR will start with part 1, and move the already existing code from being carelessly called from the Activity (in the click listener for the
Next
button) into a foreground Service, as this operation can potentially be lengthy depending on hardware and amount of frames to be saved.For the upload part we're going to delegate that to the existing WordPress Android
UploadService
, which will manage both the adding of images and uploading a custom typed Post to an existing site (to be covered in further PRs when integration of the functionality into WPAndroid is planned).How it works
There's a problem I found while doing this: a started Service is passed parameters with an Intent extras bundle, and our parameters here would be a list of images with their added Views, so unless we access the
ViewModel
'sRepository
from theService
(with the Repository actually persisting the data, which is not our case right now as we just keep these in memory) then we can't pass the frames with their added views if we don't first serialize this information (that is, positioning, rotation and scale information for each of the added views).Given our Service is actually triggered from an Activity and is quite related to it (it's a user initiated action), looks like this is a good candidate for a bound service, thus allowing us to bypass the serialization process which among other things would also incur into some time penalty (both for execution and development time). In this case, for the reasons outlined above we also need to making it both a bound Service and a foreground service (as we need the background saving process to exist even if the user navigates away from the Activity or even further, away from the app itself). This PR only tackled the bound Service part, and leaves the foreground Service for an upcoming PR so to keep things simpler and easier to review.
Once the processing is ready, it can either have a successful ending, or some of the frames may have not been saved successfully. The error collection strategy will be tackled on another PR, for now we're introducing EventBus here just as a means to signal the saving process has ended (either successfully or not, it doesn't matter for now in this PR), so the event listener can properly do the cleanup they want.
Next steps (to be tackled in subsequent PRs):
A word on future WPAndroid integration
Before tackling this PR, I studied how other services were implemented, particularly SiteCreationService as a model for a well written Service using the latest guidelines and good stuff Kotlin brings (such as corroutines) and the UploadService given our process output here will be fed into the UploadService afterwards. We followed the same pattern as used for other Services in WPAndroid, which will make it easier later when we integrate this functionality there:
AutoForeground
which is implemented there for theSiteCreationService
.To test: