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

Tasker plugin #754

Open
OxygenCobalt opened this issue Apr 12, 2024 · 33 comments
Open

Tasker plugin #754

OxygenCobalt opened this issue Apr 12, 2024 · 33 comments
Assignees
Labels
blocked Currently blocked by another task enhancement New feature or request playback Related to music playback priority Prioritized by a sponsor, I will implement these first

Comments

@OxygenCobalt
Copy link
Owner

Self-explanatory, implement a tasker plugin so users don't have to forge intents to start Auxio independently. Docs.

@OxygenCobalt OxygenCobalt added enhancement New feature or request playback Related to music playback priority Prioritized by a sponsor, I will implement these first labels Apr 12, 2024
@OxygenCobalt OxygenCobalt self-assigned this Apr 14, 2024
@etyarews
Copy link

etyarews commented Apr 15, 2024

Well, might as well throw a wish list. And due to ambiguity, I need to explain things as if someone has never used Tasker.

Tasker has, broadly speaking, the concept of:

  1. Contexts: Auxio tells Tasker something is happening/has happened. There's two types: States (thing is happening) and Events (Thing happened)
  2. Actions: Tasker tells Auxio to do something. Most of the items in my wishlist can be repurposed as shortcuts. Playback ones are what I consider basic Tasker integration, and those should also have equivalent as shortcuts, I will put a bunch of ideas here with customization to ridiculous degrees, but just basic functionality is actually needed.

When you use a Plug-in Context/Action, usually what happens is that the app opens a fullscreen activity containing configs for that Plug-in Context/Action. This means that, for instance, you don't need an action for each playback option, a single playback action could allow the user to select what type of playback they want Auxio to execute.

Actions

  • PlayBack Control: This could be a single plug-in action, with a parameter to select the type of Playback the user desires. It would be cool if rather than selecting the type out of a list, there was a way to use a Tasker variable right there, so Auxio would do %Thing, and %Thing could be dynamically changed inside Tasker. This is the basic functionality I mentioned:

    • Play:
    • Pause
    • Stop: as in, it resets the current track progress to zero and stops playback.
    • Toggle: if there's music playing then it pauses, if the music is paused (or stopped) it plays it. It needs to be a different function from Play/Pause. I personally only use Play and Pause separately, but there are uses for toggling.
    • Shuffle library: Just shuffles the entire library and starts playback.
    • Skip/Next
  • Get Info: This would be an action for Auxio to retrieve info right now and send back to Tasker in the form of output variables. Some info can be retrieved through the notification and media info, but would be nice if Auxio itself sent the info about basic playback (current music, album, artist, metadata, etc...), the current... way (?) the music was played (is it an album/artist/playlist/genre or is it just random?), what is the folder method used for the library? What is the folder (s) allowed/denied? When was the last time the library was reloaded? Etc...

  • Playback Manipulation: More along the lines of Tasker sending a command so that Auxio plays a specific song/artist/genre/playlist. Or it sends a command to add song/artist/genre/playlist to the queue (after the current song or to the bottom of the list). Or it turns shuffling/repeat on

  • Load Library: Ok, this sounds weird but could be really cool in some circumstances. Based on what you've mentioned, I'm assuming that Auxio can stay between 5 and 10 seconds in the background (or more) without receiving a command before it crashesstops and the library needs to be reloaded on playback due to the service being killed by android or something. Why would I want this? Well, Auxio takes a while to load the library, if you want Tasker to do a list of actions related to audio playback, why not start the library loading, do a bunch of actions, and then send the playback command? Like, for instance, a user wants Tasker to control Auxio through voice commands, it could be a button on the home screen. Rather than having to listen to the user, and then starting playback which would require some seconds of loading, it could load the library right from the get-go, listen to the user, and then immediately start playback. This would be useful on really large libraries or if the user wants to reload the entire library from scratch for some goddamn reason.

Contexts

States: Something is happening on Auxio and you want Tasker to do something while the thing happens or stops happening. I can see two vaguely useful states:

  • Playback: I.e. Music is playing on auxio, could have filter, like to react once a specific music is playing, they type of playback (random/playlist/artist/album/etc...)
  • Library Loading: Tasker to react if Auxio is loading. Might be useful or something

Events: Basically something happened. Some states should have events variations because it really helps Task users, while other events can't be states.

  • Playback Changed: Rather than react while audio is playing, reacts when something happened, might be starting playback, pausing, stopping, etc..
  • Library Loading Changed: Basically reacts when the loading started and/or ended

@OxygenCobalt
Copy link
Owner Author

OxygenCobalt commented Apr 15, 2024

Okay, I'll need some time to process this.

I think all of these are fine minus Load Library. In Auxio all tasker commands would be queued and then executed once loading and state restore completes. I think instead you just execute your first command and then block on the "Library Loading" state and "Playback initialized" state until it's done, if that's possible.

Biggest issues I see is making sure the lifecycle is sensible and marshalling data around. I need to make sure it's usable by Auxio (i.e UID to use) while also being human-readable (include some extra metadata).

What strikes me is that this is very close to the surface provided by the Media3 API. Given my desire to make sure Auxio doesn't do too much, I wonder if this would be better as an external app that exposes Media or Media3 actions in any app as a generic tasker plugin @etyarews. But that's a lot harder to manage. I think I might start at bare minimum basic playback commands through #753 and a basic tasker plugin, then look into further extensions as they become useful.

@OxygenCobalt OxygenCobalt added maybe Uncertain if this will be done and removed priority Prioritized by a sponsor, I will implement these first labels Apr 15, 2024
@OxygenCobalt
Copy link
Owner Author

Re-marking as a maybe due to me needing more time to reflect. Don't worry, this isn't an outright rejection. I'm generally erring closer to this being a good idea since tasker is a "standard" automation tool, and it might provide solid infrastructure if Google decides to introduce their own automation tool a la iOS shortcuts.

@etyarews
Copy link

Yeah, this is more of a wishlist, a best case scenario for Tasker integration .

A more realistic expectation is just having an action to Play, which would satisfy most of the users. Actually, depending on the implementation, a single Static Shortcut would be more than enough.

Heck, if there's an ADB command to start playback that's more than enough. Pausing can be done through the notification.

I'm not actually sure if Tasker implementation of Media Controls is up to date. It actually works on pretty much everything once the player has started(I'm making Tasker open Auxio, wait a few seconds then start playback, which sucks because it doesn't work on a locked device), any issues it might have are swept under the rug as users don't expect most media apps to work from a cold boot after Android 11(?)

@OxygenCobalt
Copy link
Owner Author

I'm not actually sure if Tasker implementation of Media Controls is up to date. It actually works on pretty much everything once the player has started(I'm making Tasker open Auxio, wait a few seconds then start playback, which sucks because it doesn't work on a locked device), any issues it might have are swept under the rug as users don't expect most media apps to work from a cold boot after Android 11(?)

Tasker uses the very old MediaButtonIntent API that has existed since like Android Froyo I think. What Tasker should do is create a MediaController, bind to the app, and make more complicated requests. But given that Tasker has so much technical debt as is from it's UI and general behavior, I really don't want to make a patch for it.

@etyarews
Copy link

Eeeh, Tasker is very slowly getting a redesign to M3, and I'm the designer of that project. I've sent a message to the dev regarding the MediaController thing.

@OxygenCobalt OxygenCobalt added priority Prioritized by a sponsor, I will implement these first and removed maybe Uncertain if this will be done labels Apr 22, 2024
@OxygenCobalt
Copy link
Owner Author

I've decided that exposing a raw service API would be far easier to mess up than if I define some tasker actions, so I'll do this instead. I just have to work out lifecycle details though such that it's impossible for a tasker user to trigger a foreground crash.

@OxygenCobalt
Copy link
Owner Author

OxygenCobalt commented May 17, 2024

Okay, I have arrived at an MVP tasker setup:

Action: Prepare Playback
State: Available -> Whether Auxio's Service is 100% initialized

Implicit assumptions: You will call Prepare Playback, Block Until Available is True, and then immediately send some playback action that will trigger Auxio to foreground. If you don't, then expect Auxio to crash.

You can just use MediaButtons from then on, hopefully.

Since Tasker is for enthusiasts, I think it's fine to not have very many guardrails. I'll make it more robust if one day Google decides "hm... i wanna copy ios shortcuts...".

@OxygenCobalt
Copy link
Owner Author

OxygenCobalt commented May 17, 2024

Wait, are you aware of any Tasker actions that take a long time to execute @etyarews? Perhaps I can just collapse it into one action by just looping until it's available.

@etyarews
Copy link

Quite a number of actions can take a while to finish, specially if they involve the internet in some capacity.

A somewhat good rule of thumb is that it shouldn't take more than 30s to run an action. But honestly, the initial plan isn't that bad, but yeah, it it would be better to be a single action.

@OxygenCobalt
Copy link
Owner Author

OxygenCobalt commented May 18, 2024

Okay, here's a build with a Tasker action that might work (I really don't know since I can't buy Tasker) @etyarews:

Auxio_Tasker.zip

@etyarews
Copy link

Ok, it works but it is a pain to know when the service is ready to receive media buttons events.

@etyarews
Copy link

Alright, it appears I need to run the plug-in action two times before it allows me to control Auxio with Media Buttons. I tried putting an wait action between the plug-in action and the media button action, but it doesn't appear to have worked as well as I hoped.

The issue is that the plug-in action only makes Auxio ready to receive media buttons, but there's no way of knowing when Auxio is actually ready. It is missing an state or event.

Either you add an event or state when Auxio is ready, or you refactor the action to immediately start playing.

@OxygenCobalt
Copy link
Owner Author

Wait, I see. The action only starts and then blocks until the service initializes but not necessarily restores it's state. Let me quickly tweak that @etyarews.

@etyarews
Copy link

While I wait for an update, the workaround I've found was to use an action to start the service, add a 4 seconds wait, then start the service again and only then send a media button action.

I've also managed to make it work while the screen is turned off, which is basically my entire goal, one time I had an issue and couldn't start the service, but because I'm trying to do so while the phone is locked this is difficult to reproduce and narrow it down if it was a fluke or not.

@OxygenCobalt
Copy link
Owner Author

OxygenCobalt commented May 18, 2024

I've built out a more robust plugin, but I'm running into SEVERE implementation issues since the "If you go background once you can't go foreground again" issue I've talked about earlier actually exists and applies to ONLY services started by Tasker.

I don't know if I can actually implement this now. Each attempt to fix it has caused another problem and is increasingly wrecking my internal service lifecycle. That lifecycle is already buckling under the weight of the clumsy workarounds I've done to cram in music loading. I seriously risk degrading UX to appease tasker's extremely outdated interface, and that's honestly where I start drawing the line at features.

Is it not feasible for the tasker developer to implement things like MediaController @etyarews? This binds the service and allows me to go foreground whenever, and is basically what the rest of the Android OS uses. I can't tell if it will need a foreground activity itself however.

@OxygenCobalt
Copy link
Owner Author

I'm going to ice out this issue for now and disable the current tasker code until I can stabilize the service lifecycle with media3's demands.

For now, enjoy this maybe partially working build with slightly more usable half-built actions.
Auxio_Tasker2.zip

@OxygenCobalt OxygenCobalt added the blocked Currently blocked by another task label May 18, 2024
@etyarews
Copy link

etyarews commented May 18, 2024

I've asked the dev about the issue before, he confirmed that Tasker's Media Button Action actually uses MediaController if the media player has an active notification. He was, however, unsure about how to send a MediaController intent without the service existing in some form or another.

He also said he needed a sample of... something that I'm frankly unqualified to describe. Something about a sample of what triggers Auxio to play, or a thing to bind the service.

I'm unsure what you meant by "seriously risk degrading UX to appease tasker's extremely outdated interface", as none of what you described appears to be limited by Tasker's interface, and Tasker's plugins also work with Macrodroid and Automate both which have more modern interfaces than Tasker's default one.

@OxygenCobalt
Copy link
Owner Author

OxygenCobalt commented May 19, 2024

Something about a sample of what triggers Auxio to play, or a thing to bind the service.

Tell them this:

This is really messy. Auxio does have a binder available now in 3.5.0 that follows the legacy MediaBrowserService but also the modern media3 APIs too. Auxio never plays without user interaction (ideally), at minimum it will load music, restore state, and then idle. What triggers issues is that I briefly stop going foreground between load music and restore state, and the fact that I also stop going foreground when I begin to idle (I can change this but I don't know if it will have second-order impacts). These are extremely difficult to remedy on my end without risking wrecking more "standard" bound usage of the service. Either way, I need to make it such that the service needs to be bound or for the service to be launched by a foreground activity to still be be allowed to go foreground later in these cases.

My ideal hope is basically that I could do nothing and Tasker would:

  • Be able to identify my playback service (through the relevant media intents)
  • Start and bind to it (Binding most importantly, this makes my life so much easier)
  • Expose relevant state and actions

From a user's end, then maybe a workflow would be:

  • Start [Auxio] Media Browser Service
  • Wait until [Currently Playing Media Item of [Auxio]] != [Null] (i.e Finished loading and restored state, ensuring that your media intents would work)
  • Send command [Play] to Auxio

I'm unsure what you meant by "seriously risk degrading UX to appease tasker's extremely outdated interface", as none of what you described appears to be limited by Tasker's interface, and Tasker's plugins also work with Macrodroid and Automate both which have more modern interfaces than Tasker's default one.

My assumption is that Tasker was forging legacy media button intents and throwing them around, which have always been a nightmare to manage personally. Now that I know it is MediaController, I'm more okay with it. I just wished it went further with binding and starting services.

Send me any response if you get one @etyarews. Better, if you could tell me how to establish contact w/the Tasker developer, that would also be nice.

@etyarews
Copy link

@etyarews
Copy link

etyarews commented May 19, 2024

Btw, I tested Automate and MacroDroid and neither could kickstart Auxio without using the plug-in. So afaik there's no app that currently implements MediaController in the manner you wished Tasker would. Which makes it hard to explain and understand how much it would improve with binding and starting services.

@etyarews
Copy link

etyarews commented May 19, 2024

After making some more tests with the new update, I got say this is perfect. I am able to start Auxio from cold boot after a system reboot, even with the display locked.

I only use Restore State and immediately after, I send a Play command and music starts playing after a short while. I don't need to use any wait action because the new action is only finished inside Tasker once Auxio is done, so there's no need to try to guess when Auxio is ready.

Honestly, I don't see the need for the Start Auxio action anymore, as the Restore State already does everything, and better.

@OxygenCobalt
Copy link
Owner Author

Sounds good @etyarews. I designed Restore State with the intent of rolling it all together. It's just half-complete since there's some annoying foreground state logic that I had to do.

@etyarews
Copy link

Do you intend to implement all of this into the next Auxio release? I wish to make a post on Tasker's Subreddit as Auxio is right now the best music player for that community, and I think it might be better if I wait until an official released rather than having to point people out to a debug version

@OxygenCobalt
Copy link
Owner Author

OxygenCobalt commented May 20, 2024

Not until I can make sure my service lifecycle will behave sanely with Media3 @etyarews. I don't want to destabilize it too much at once.

@etyarews
Copy link

etyarews commented May 25, 2024

There is one small issue:

If for some reason Auxio doesn't receive a command, it crashes, which is expected and isn't the issue.

The issue is that after the crash, Auxio somehow nuked the playback state, which means that it can't be controlled with the Restore State anymore.

I wish after Restore State is used, Auxio created a Tasker variable about the playback state, if it is valid or not, so I can make Tasker react when it isn't "valid"

@OxygenCobalt
Copy link
Owner Author

I see. I'll focus on writing in a tasker variable once I take this off of the ice.

@etyarews
Copy link

Alright just to make it clear, once this happens if I open Auxio it throws an error that it couldn't load the library and I need to manually tap into reload it, which doesn't make much to me because there isn't really any reasonable option other than reload the library, so it feels kinda weird.

@etyarews
Copy link

android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service org.oxycblt.auxio.debug/org.oxycblt.auxio.AuxioService
at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
at android.os.Parcel.readParcelableInternal(Parcel.java:4816)
at android.os.Parcel.readParcelable(Parcel.java:4778)
at android.os.Parcel.createExceptionOrNull(Parcel.java:3006)
at android.os.Parcel.createException(Parcel.java:2995)
at android.os.Parcel.readException(Parcel.java:2978)
at android.os.Parcel.readException(Parcel.java:2920)
at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:6079)
at java.lang.reflect.Method.invoke(Native Method)
at leakcanary.ServiceWatcher$install$4$2.invoke(ServiceWatcher.kt:93)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy3.setServiceForeground(Unknown Source)
at android.app.Service.startForeground(Service.java:743)
at org.oxycblt.auxio.AuxioService$updateForeground$2.invoke(AuxioService.kt:102)
at org.oxycblt.auxio.AuxioService$updateForeground$2.invoke(AuxioService.kt:100)
at org.oxycblt.auxio.music.service.IndexerServiceFragment.createNotification(IndexerServiceFragment.kt:93)
at org.oxycblt.auxio.AuxioService.updateForeground(AuxioService.kt:100)
at org.oxycblt.auxio.music.service.IndexerServiceFragment.onIndexingStateChanged(IndexerServiceFragment.kt:115)
at org.oxycblt.auxio.music.MusicRepositoryImpl.emitIndexingProgress(MusicRepository.kt:584)
at org.oxycblt.auxio.music.MusicRepositoryImpl.access$emitIndexingProgress(MusicRepository.kt:224)
at org.oxycblt.auxio.music.MusicRepositoryImpl$emitIndexingProgress$1.invokeSuspend(Unknown Source:15)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)

@OxygenCobalt
Copy link
Owner Author

Really weird @etyarews. This was during normal operation, or when the library issues occur?

Also: The kind of problems you're reporting is why I've kind of iced this issue right now. There's a combinatoric (translation: big) amount of edge cases I have to handle and it's why I don't want to ship this when I'm already dealing with Media3's combinatoric edge cases.

@etyarews
Copy link

It is related to the library issue I've mentioned. Sometimes it just gets nuked.

I thought it was the Tasker Plugin action not being executed right, but I'm starting to suspect it happens due to some other stuff, and then the action just makes Auxio confused and then it nukes itself.

@etyarews
Copy link

etyarews commented May 26, 2024

Like, being honest, my issue isn't that it nukes itself, the issue is that after it nukes itself I need to open Auxio and manually tap the reload library button.

It would be a non-issue if Auxio could be reloaded in this state with a Shuffle All Songs action.

I do understand why this is all kinda janky as hell and why you maybe don't want to implement it officially, but I wish there was a more of a semi-official way to obtain the debug version with the plug-in action, that way I could suggest that version of Auxio in the Tasker/Automate/MacroDroid Community, they would be more understanding of the janky than the general public.

I'm not sure if you understand how important this feature is for the small niche of people who want to control a music player through those automation apps. The current solution is to have no proper control at all, like Auxio was, or to use an outdated music player, or build your own music player inside Tasker

@OxygenCobalt
Copy link
Owner Author

OxygenCobalt commented May 26, 2024

I see.

The most I can say is that you can go recommend a manual build from Auxio's tasker branch @etyarews. That's where all of the functionality is right now. Perhaps other enthusiasts could fiddle with it and try to fix issues. Otherwise it's on ice for now, as I said.

Also: I think I've finally figured out how to correctly set up the service lifecycle to correctly play along with Media3, with the additional bonus that the "downtime" problem that crashes the service prior should hopefully no longer be an issue. Hopefully the third revision of the tasker plugin should work without much fuss.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked Currently blocked by another task enhancement New feature or request playback Related to music playback priority Prioritized by a sponsor, I will implement these first
Projects
None yet
Development

No branches or pull requests

2 participants