tasker: add start action

Add a tasker action to start AuxioService in a HIGHLY limited ammner.

Resolves #754.
This commit is contained in:
Alexander Capehart 2024-08-17 18:21:39 -06:00
parent 3fa5628a1e
commit ea9c5d3c88
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 114 additions and 15 deletions

View file

@ -151,6 +151,9 @@ dependencies {
// Speed dial // Speed dial
implementation "com.leinardi.android:speed-dial:3.3.0" implementation "com.leinardi.android:speed-dial:3.3.0"
// Tasker integration
implementation 'com.joaomgcd:taskerpluginlibrary:0.4.10'
// Testing // Testing
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
testImplementation "junit:junit:4.13.2" testImplementation "junit:junit:4.13.2"

View file

@ -135,5 +135,15 @@
android:resource="@xml/widget_info" /> android:resource="@xml/widget_info" />
</receiver> </receiver>
<!-- Tasker 'start service' integration -->
<activity
android:name=".ActivityConfigBasicAction"
android:exported="true"
android:icon="@mipmap/ic_launcher"
android:label="My Tasker Action">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
</intent-filter>
</activity>
</application> </application>
</manifest> </manifest>

View file

@ -55,13 +55,9 @@ class AuxioService : MediaLibraryService(), ForegroundListener {
} }
private fun onHandleForeground(intent: Intent?) { private fun onHandleForeground(intent: Intent?) {
val nativeStart = intent?.getBooleanExtra(INTENT_KEY_NATIVE_START, false) ?: false val startId = intent?.getIntExtra(INTENT_KEY_START_ID, -1) ?: -1
indexingFragment.start() indexingFragment.start()
if (!nativeStart) { mediaSessionFragment.start(startId)
// Some foreign code started us, no guarantees about foreground stability. Figure
// out what to do.
mediaSessionFragment.handleNonNativeStart()
}
} }
override fun onTaskRemoved(rootIntent: Intent?) { override fun onTaskRemoved(rootIntent: Intent?) {
@ -87,6 +83,7 @@ class AuxioService : MediaLibraryService(), ForegroundListener {
if (change == ForegroundListener.Change.MEDIA_SESSION) { if (change == ForegroundListener.Change.MEDIA_SESSION) {
mediaSessionFragment.createNotification { mediaSessionFragment.createNotification {
startForeground(it.notificationId, it.notification) startForeground(it.notificationId, it.notification)
isForeground = true
} }
} }
// Nothing changed, but don't show anything music related since we can always // Nothing changed, but don't show anything music related since we can always
@ -95,16 +92,21 @@ class AuxioService : MediaLibraryService(), ForegroundListener {
indexingFragment.createNotification { indexingFragment.createNotification {
if (it != null) { if (it != null) {
startForeground(it.code, it.build()) startForeground(it.code, it.build())
isForeground = true
} else { } else {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
isForeground = false
} }
} }
} }
} }
companion object { companion object {
var isForeground = false
private set
// This is only meant for Auxio to internally ensure that it's state management will work. // This is only meant for Auxio to internally ensure that it's state management will work.
const val INTENT_KEY_NATIVE_START = BuildConfig.APPLICATION_ID + ".service.NATIVE_START" const val INTENT_KEY_START_ID = BuildConfig.APPLICATION_ID + ".service.START_ID"
} }
} }

View file

@ -59,6 +59,10 @@ object IntegerTable {
const val INDEXER_NOTIFICATION_CODE = 0xA0A1 const val INDEXER_NOTIFICATION_CODE = 0xA0A1
/** MainActivity Intent request code */ /** MainActivity Intent request code */
const val REQUEST_CODE = 0xA0C0 const val REQUEST_CODE = 0xA0C0
/** Activity AuxioService Start ID */
const val START_ID_ACTIVITY = 0xA050
/** Tasker AuxioService Start ID */
const val START_ID_TASKER = 0xA051
/** RepeatMode.NONE */ /** RepeatMode.NONE */
const val REPEAT_MODE_NONE = 0xA100 const val REPEAT_MODE_NONE = 0xA100
/** RepeatMode.ALL */ /** RepeatMode.ALL */
@ -133,7 +137,4 @@ object IntegerTable {
const val PLAY_SONG_FROM_PLAYLIST = 0xA123 const val PLAY_SONG_FROM_PLAYLIST = 0xA123
/** PlaySong.ByItself */ /** PlaySong.ByItself */
const val PLAY_SONG_BY_ITSELF = 0xA124 const val PLAY_SONG_BY_ITSELF = 0xA124
const val PLAYER_COMMAND_INC_REPEAT_MODE = 0xA125
const val PLAYER_COMMAND_TOGGLE_SHUFFLE = 0xA126
const val PLAYER_COMMAND_EXIT = 0xA127
} }

View file

@ -71,11 +71,11 @@ class MainActivity : AppCompatActivity() {
startService( startService(
Intent(this, AuxioService::class.java) Intent(this, AuxioService::class.java)
.putExtra(AuxioService.INTENT_KEY_NATIVE_START, true)) .putExtra(AuxioService.INTENT_KEY_START_ID, IntegerTable.START_ID_ACTIVITY))
if (!startIntentAction(intent)) { if (!startIntentAction(intent)) {
// No intent action to do, just restore the previously saved state. // No intent action to do, just restore the previously saved state.
playbackModel.playDeferred(DeferredPlayback.RestoreState) playbackModel.playDeferred(DeferredPlayback.RestoreState(false))
} }
} }

View file

@ -109,11 +109,23 @@ constructor(
} }
} }
fun handleNonNativeStart() { fun start(startedBy: Int) {
// At minimum we want to ensure an active playback state. // At minimum we want to ensure an active playback state.
// TODO: Possibly also force to go foreground? // TODO: Possibly also force to go foreground?
logD("Handling non-native start.") logD("Handling non-native start.")
playbackManager.playDeferred(DeferredPlayback.RestoreState) val action =
when (startedBy) {
IntegerTable.START_ID_ACTIVITY -> null
IntegerTable.START_ID_TASKER ->
DeferredPlayback.RestoreState(
play = true, fallback = DeferredPlayback.ShuffleAll)
// External services using Auxio better know what they are doing.
else -> DeferredPlayback.RestoreState(play = false)
}
if (action != null) {
logD("Initing service fragment using action $action")
playbackManager.playDeferred(action)
}
} }
fun hasNotification(): Boolean = exoHolder.sessionOngoing fun hasNotification(): Boolean = exoHolder.sessionOngoing

View file

@ -276,7 +276,8 @@ data class QueueChange(val type: Type, val instructions: UpdateInstructions) {
/** Possible long-running background tasks handled by the background playback task. */ /** Possible long-running background tasks handled by the background playback task. */
sealed interface DeferredPlayback { sealed interface DeferredPlayback {
/** Restore the previously saved playback state. */ /** Restore the previously saved playback state. */
data class RestoreState(val play: Boolean, val fallback: DeferredPlayback? = null) : DeferredPlayback data class RestoreState(val play: Boolean, val fallback: DeferredPlayback? = null) :
DeferredPlayback
/** /**
* Start shuffled playback of the entire music library. Analogous to the "Shuffle All" shortcut. * Start shuffled playback of the entire music library. Analogous to the "Shuffle All" shortcut.

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2024 Auxio Project
* Start.kt is part of Auxio.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.tasker
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.content.ContextCompat
import com.joaomgcd.taskerpluginlibrary.action.TaskerPluginRunnerActionNoOutputOrInput
import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfig
import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigHelperNoOutputOrInput
import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigNoInput
import com.joaomgcd.taskerpluginlibrary.input.TaskerInput
import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResult
import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultSucess
import org.oxycblt.auxio.AuxioService
import org.oxycblt.auxio.IntegerTable
class StartActionHelper(config: TaskerPluginConfig<Unit>) :
TaskerPluginConfigHelperNoOutputOrInput<StartActionRunner>(config) {
override val runnerClass: Class<StartActionRunner>
get() = StartActionRunner::class.java
override fun addToStringBlurb(input: TaskerInput<Unit>, blurbBuilder: StringBuilder) {
blurbBuilder.append(
"Starts Auxio using the previously saved state. If no saved state is available, all songs will be shuffled. Playback will start immediately. Be careful controlling this service, if you close it and then try to use it again, you will probably crash the app.")
}
}
class ActivityConfigStartAction : Activity(), TaskerPluginConfigNoInput {
override val context
get() = applicationContext
private val taskerHelper by lazy { StartActionHelper(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
taskerHelper.finishForTasker()
}
}
class StartActionRunner : TaskerPluginRunnerActionNoOutputOrInput() {
override fun run(context: Context, input: TaskerInput<Unit>): TaskerPluginResult<Unit> {
ContextCompat.startForegroundService(
context,
Intent(context, AuxioService::class.java)
.putExtra(AuxioService.INTENT_KEY_START_ID, IntegerTable.START_ID_TASKER))
while (!AuxioService.isForeground) {
Thread.sleep(100)
}
return TaskerPluginResultSucess()
}
}