forked from Automattic/pocket-casts-android
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added new "Control Playback" Tasker action and added way to directly …
…insert Tasker variables in inputs of both actions
- Loading branch information
Showing
20 changed files
with
431 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
...erplugin/src/main/java/au/com/shiftyjelly/pocketcasts/taskerplugin/base/ComposableBase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package au.com.shiftyjelly.pocketcasts.taskerplugin.base | ||
|
||
import androidx.annotation.StringRes | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.fillMaxHeight | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.lazy.LazyColumn | ||
import androidx.compose.material.Button | ||
import androidx.compose.material.DropdownMenu | ||
import androidx.compose.material.DropdownMenuItem | ||
import androidx.compose.material.Icon | ||
import androidx.compose.material.IconButton | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.OutlinedTextField | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.ExperimentalComposeUiApi | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController | ||
import androidx.compose.ui.res.painterResource | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import androidx.compose.ui.window.PopupProperties | ||
import au.com.shiftyjelly.pocketcasts.compose.AppTheme | ||
import au.com.shiftyjelly.pocketcasts.compose.theme | ||
import au.com.shiftyjelly.pocketcasts.localization.R | ||
import au.com.shiftyjelly.pocketcasts.ui.theme.Theme | ||
|
||
class TaskerInputFieldState<T>(val content: Content<T>) { | ||
data class Content<T> constructor( | ||
val value: String?, | ||
@StringRes val labelResId: Int, | ||
val onTextChange: (String) -> Unit, | ||
val taskerVariables: Array<String>, | ||
val possibleItems: List<T>? = null, | ||
val itemToString: (T?) -> String = { it?.toString() ?: "" }, | ||
val itemContent: @Composable (T) -> Unit = { Text(text = itemToString(it)) } | ||
) | ||
} | ||
|
||
@OptIn(ExperimentalComposeUiApi::class) | ||
@Composable | ||
fun <T> ComposableTaskerInputField(content: TaskerInputFieldState.Content<T>) { | ||
var isSearching by remember { mutableStateOf(false) } | ||
var isSelectingTaskerVariable by remember { mutableStateOf(false) } | ||
val keyboardController = LocalSoftwareKeyboardController.current | ||
Box { | ||
|
||
Row { | ||
val possibleItems = content.possibleItems | ||
|
||
val hasSuggestedItems = !possibleItems.isNullOrEmpty() | ||
val hasTaskerVariables = content.taskerVariables.isNotEmpty() | ||
OutlinedTextField( | ||
value = content.value ?: "", label = { Text(text = stringResource(id = content.labelResId)) }, onValueChange = { | ||
content.onTextChange(it) | ||
}, modifier = Modifier.weight(1f), | ||
trailingIcon = if (!hasSuggestedItems && !hasTaskerVariables) null else { | ||
{ | ||
Row { | ||
if (hasTaskerVariables) { | ||
IconButton(onClick = { isSelectingTaskerVariable = true }) { | ||
Icon( | ||
painter = painterResource(au.com.shiftyjelly.pocketcasts.images.R.drawable.ic_filters), | ||
contentDescription = stringResource(R.string.tasker_variables), | ||
tint = MaterialTheme.theme.colors.primaryIcon01, | ||
modifier = Modifier.padding(end = 16.dp, start = 16.dp) | ||
) | ||
} | ||
} | ||
if (hasSuggestedItems) { | ||
IconButton(onClick = { isSearching = true }) { | ||
Icon( | ||
painter = painterResource(au.com.shiftyjelly.pocketcasts.images.R.drawable.ic_search), | ||
contentDescription = stringResource(R.string.search), | ||
tint = MaterialTheme.theme.colors.primaryIcon01, | ||
modifier = Modifier.padding(end = 16.dp, start = 16.dp) | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
) | ||
if (possibleItems != null && !possibleItems.isEmpty()) { | ||
DropdownMenu( | ||
expanded = isSearching, | ||
onDismissRequest = { }, | ||
properties = PopupProperties(focusable = false) | ||
) { | ||
possibleItems.forEach { | ||
DropdownMenuItem(onClick = { | ||
isSearching = false | ||
keyboardController?.hide() | ||
content.onTextChange(content.itemToString(it)) | ||
}) { | ||
content.itemContent(it) | ||
} | ||
} | ||
} | ||
} | ||
if (hasTaskerVariables) { | ||
DropdownMenu( | ||
expanded = isSelectingTaskerVariable, | ||
onDismissRequest = { }, | ||
properties = PopupProperties(focusable = false) | ||
) { | ||
content.taskerVariables.forEach { | ||
DropdownMenuItem(onClick = { | ||
isSelectingTaskerVariable = false | ||
keyboardController?.hide() | ||
content.onTextChange(it) | ||
}) { | ||
Text(it) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Preview(showBackground = true) | ||
@Composable | ||
private fun ComposableTaskerInputFieldPreview() { | ||
AppTheme(Theme.ThemeType.CLASSIC_LIGHT) { | ||
ComposableTaskerInputField( | ||
TaskerInputFieldState.Content( | ||
"some value", R.string.archive, {}, | ||
arrayOf("%test"), | ||
listOf("Hi", "Hello") | ||
) | ||
) | ||
} | ||
} | ||
|
||
@Composable | ||
fun ComposableTaskerInputFieldList( | ||
fieldContents: List<TaskerInputFieldState.Content<*>>, | ||
onFinish: () -> Unit | ||
) { | ||
Box(modifier = Modifier.fillMaxHeight()) { | ||
LazyColumn { | ||
fieldContents.forEach { content -> | ||
item { | ||
ComposableTaskerInputField(content) | ||
} | ||
} | ||
} | ||
Button(onClick = onFinish, modifier = Modifier.align(Alignment.BottomEnd)) { | ||
Text(stringResource(R.string.ok)) | ||
} | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
...taskerplugin/src/main/java/au/com/shiftyjelly/pocketcasts/taskerplugin/base/Extensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package au.com.shiftyjelly.pocketcasts.taskerplugin.base | ||
|
||
val String?.nullIfEmpty get() = if (isNullOrEmpty()) null else this | ||
fun <T> tryOrNull(handleError: ((Throwable) -> T?)? = null, block: () -> T?): T? = try { | ||
block() | ||
} catch (t: Throwable) { | ||
handleError?.invoke(t) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...asts/taskerplugin/hilt/HiltEntryPoints.kt → ...taskerplugin/base/hilt/HiltEntryPoints.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
...u/com/shiftyjelly/pocketcasts/taskerplugin/controlplayback/ActionHelperControlPlayback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package au.com.shiftyjelly.pocketcasts.taskerplugin.controlplayback | ||
|
||
import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfig | ||
import com.joaomgcd.taskerpluginlibrary.config.TaskerPluginConfigHelperNoOutput | ||
import com.joaomgcd.taskerpluginlibrary.input.TaskerInput | ||
|
||
class ActionHelperControlPlayback(config: TaskerPluginConfig<InputControlPlayback>) : TaskerPluginConfigHelperNoOutput<InputControlPlayback, ActionRunnerControlPlayback>(config) { | ||
override val runnerClass: Class<ActionRunnerControlPlayback> get() = ActionRunnerControlPlayback::class.java | ||
override fun addToStringBlurb(input: TaskerInput<InputControlPlayback>, blurbBuilder: StringBuilder) { | ||
val inputControlPlayback = input.regular | ||
val commandEnum = inputControlPlayback.commandEnum | ||
blurbBuilder.append("${context.getString(au.com.shiftyjelly.pocketcasts.localization.R.string.playback_command)}: ${commandEnum?.getDescription(context)}") | ||
when (commandEnum) { | ||
InputControlPlayback.PlaybackCommand.SkipToChapter -> blurbBuilder.append("\n${context.getString(au.com.shiftyjelly.pocketcasts.localization.R.string.chapter_to_skip_to)}: ${inputControlPlayback.chapterToSkipTo}") | ||
else -> {} | ||
} | ||
} | ||
|
||
override val addDefaultStringBlurb: Boolean | ||
get() = false | ||
override val inputClass: Class<InputControlPlayback> | ||
get() = InputControlPlayback::class.java | ||
} |
36 changes: 36 additions & 0 deletions
36
...u/com/shiftyjelly/pocketcasts/taskerplugin/controlplayback/ActionRunnerControlPlayback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package au.com.shiftyjelly.pocketcasts.taskerplugin.controlplayback | ||
|
||
import android.content.Context | ||
import au.com.shiftyjelly.pocketcasts.localization.R | ||
import au.com.shiftyjelly.pocketcasts.taskerplugin.base.nullIfEmpty | ||
import au.com.shiftyjelly.pocketcasts.taskerplugin.base.hilt.playbackManager | ||
import com.joaomgcd.taskerpluginlibrary.action.TaskerPluginRunnerActionNoOutput | ||
import com.joaomgcd.taskerpluginlibrary.input.TaskerInput | ||
import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResult | ||
import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultError | ||
import com.joaomgcd.taskerpluginlibrary.runner.TaskerPluginResultSucess | ||
|
||
private const val ERROR_NO_COMMAND_PROVIDED = 1 | ||
private const val ERROR_INVALIUD_COMMAND_PROVIDED = 2 | ||
private const val ERROR_INVALIUD_CHAPTER_TO_SKIP_TO_PROVIDED = 3 | ||
|
||
class ActionRunnerControlPlayback : TaskerPluginRunnerActionNoOutput<InputControlPlayback>() { | ||
|
||
override fun run(context: Context, input: TaskerInput<InputControlPlayback>): TaskerPluginResult<Unit> { | ||
val command = input.regular.command.nullIfEmpty ?: return TaskerPluginResultError(ERROR_NO_COMMAND_PROVIDED, context.getString(au.com.shiftyjelly.pocketcasts.localization.R.string.must_provide_command_name)) | ||
|
||
val playbackManager = context.playbackManager | ||
val commandEnum = input.regular.commandEnum ?: return TaskerPluginResultError(ERROR_INVALIUD_COMMAND_PROVIDED, context.getString(au.com.shiftyjelly.pocketcasts.localization.R.string.command_x_not_valid, command)) | ||
|
||
when (commandEnum) { | ||
InputControlPlayback.PlaybackCommand.SkipToNextChapter -> playbackManager.skipToNextChapter() | ||
InputControlPlayback.PlaybackCommand.SkipToChapter -> { | ||
val chapterToSkipTo = input.regular.chapterToSkipTo | ||
playbackManager.skipToChapter(chapterToSkipTo?.toIntOrNull() ?: return TaskerPluginResultError(ERROR_INVALIUD_CHAPTER_TO_SKIP_TO_PROVIDED, context.getString(R.string.chapter_to_skip_to_not_valid, input.regular.chapterToSkipTo))) | ||
} | ||
InputControlPlayback.PlaybackCommand.SkipToPrevious -> playbackManager.skipToPreviousChapter() | ||
} | ||
|
||
return TaskerPluginResultSucess() | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
.../java/au/com/shiftyjelly/pocketcasts/taskerplugin/controlplayback/InputControlPlayback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package au.com.shiftyjelly.pocketcasts.taskerplugin.controlplayback | ||
|
||
import android.content.Context | ||
import androidx.annotation.StringRes | ||
import au.com.shiftyjelly.pocketcasts.localization.R | ||
import au.com.shiftyjelly.pocketcasts.taskerplugin.base.tryOrNull | ||
import com.joaomgcd.taskerpluginlibrary.input.TaskerInputField | ||
import com.joaomgcd.taskerpluginlibrary.input.TaskerInputRoot | ||
|
||
@TaskerInputRoot | ||
class InputControlPlayback @JvmOverloads constructor( | ||
@field:TaskerInputField("command") var command: String? = null, | ||
@field:TaskerInputField("skipToChapter") var chapterToSkipTo: String? = null | ||
) { | ||
|
||
val commandEnum get() = tryOrNull { command?.let { PlaybackCommand.valueOf(it) } } | ||
|
||
enum class PlaybackCommand(@StringRes val descriptionResId: Int) { | ||
SkipToNextChapter(R.string.skip_to_next_chapter), SkipToPrevious(R.string.skip_to_previous_chapter), SkipToChapter(R.string.skip_to_chapter); | ||
|
||
fun getDescription(context: Context) = context.getString(descriptionResId) | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...ftyjelly/pocketcasts/taskerplugin/controlplayback/config/ActivityConfigControlPlayback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package au.com.shiftyjelly.pocketcasts.taskerplugin.controlplayback.config | ||
|
||
import androidx.activity.viewModels | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.collectAsState | ||
import au.com.shiftyjelly.pocketcasts.taskerplugin.base.ActivityConfigBase | ||
import au.com.shiftyjelly.pocketcasts.taskerplugin.base.TaskerInputFieldState | ||
import dagger.hilt.android.AndroidEntryPoint | ||
|
||
@AndroidEntryPoint | ||
class ActivityConfigControlPlayback : ActivityConfigBase<ViewModelConfigControlPlayback>() { | ||
override val viewModel: ViewModelConfigControlPlayback by viewModels() | ||
|
||
@Composable | ||
override fun Content() { | ||
val commandContent = TaskerInputFieldState.Content( | ||
viewModel.commandState.collectAsState().value, | ||
au.com.shiftyjelly.pocketcasts.localization.R.string.playback_command, | ||
{ viewModel.command = it }, | ||
viewModel.taskerVariables, | ||
viewModel.availableCommands.toList() | ||
) { | ||
Text(viewModel.getDescription(it)) | ||
} | ||
val skipToChapterContent = if (viewModel.askForChapterToSkipToState.collectAsState().value) { | ||
TaskerInputFieldState.Content<String>( | ||
viewModel.chapterToSkipToState.collectAsState().value, | ||
au.com.shiftyjelly.pocketcasts.localization.R.string.chapter_to_skip_to, | ||
{ viewModel.chapterToSkipTo = it }, | ||
viewModel.taskerVariables | ||
) | ||
} else { | ||
null | ||
} | ||
ComposableConfigControlPlayback( | ||
commandContent, | ||
skipToChapterContent | ||
) { viewModel.finishForTasker() } | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...yjelly/pocketcasts/taskerplugin/controlplayback/config/ComposableConfigControlPlayback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package au.com.shiftyjelly.pocketcasts.taskerplugin.controlplayback.config | ||
|
||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import au.com.shiftyjelly.pocketcasts.compose.AppTheme | ||
import au.com.shiftyjelly.pocketcasts.taskerplugin.base.ComposableTaskerInputFieldList | ||
import au.com.shiftyjelly.pocketcasts.taskerplugin.base.TaskerInputFieldState | ||
import au.com.shiftyjelly.pocketcasts.taskerplugin.controlplayback.InputControlPlayback | ||
import au.com.shiftyjelly.pocketcasts.ui.theme.Theme | ||
|
||
@Composable | ||
fun ComposableConfigControlPlayback( | ||
inputCommandContent: TaskerInputFieldState.Content<InputControlPlayback.PlaybackCommand>, | ||
inputSkipToChapterContent: TaskerInputFieldState.Content<String>?, | ||
onFinish: () -> Unit | ||
) { | ||
val inputList = mutableListOf<TaskerInputFieldState.Content<*>>(inputCommandContent) | ||
inputSkipToChapterContent?.let { inputList.add(it) } | ||
ComposableTaskerInputFieldList(inputList, onFinish) | ||
} | ||
|
||
@Preview(showBackground = true) | ||
@Composable | ||
private fun ComposableConfigControlPlaybackPreview() { | ||
AppTheme(Theme.ThemeType.CLASSIC_LIGHT) { | ||
ComposableConfigControlPlayback( | ||
TaskerInputFieldState.Content( | ||
InputControlPlayback.PlaybackCommand.SkipToChapter.name, | ||
au.com.shiftyjelly.pocketcasts.localization.R.string.playback_command, | ||
{}, arrayOf("%test"), | ||
InputControlPlayback.PlaybackCommand.values().toList() | ||
), | ||
TaskerInputFieldState.Content( | ||
"1", | ||
au.com.shiftyjelly.pocketcasts.localization.R.string.chapter_to_skip_to, | ||
{}, arrayOf("%test") | ||
) | ||
) {} | ||
} | ||
} |
Oops, something went wrong.