diff --git a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt index 9d463547..c5715ebe 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt @@ -12,6 +12,7 @@ import java.util.* import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.runBlocking import org.json.JSONException +import org.json.JSONObject import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -61,6 +62,33 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) { return 1f } + fun setSavedPlaybackRate(newRate: Float) { + val sharedPrefs = ctx.getSharedPreferences("CapacitorStorage", Activity.MODE_PRIVATE) + val sharedPrefEditor = sharedPrefs.edit() + if (sharedPrefs != null) { + val userSettingsPref = sharedPrefs.getString("userSettings", null) + if (userSettingsPref != null) { + try { + val userSettings = JSObject(userSettingsPref) + userSettings.put("playbackRate", newRate.toDouble()) + sharedPrefEditor.putString("userSettings", userSettings.toString()) + sharedPrefEditor.commit() + userSettingsPlaybackRate = newRate + Log.d(tag, "Saved userSettings JSON from Android Auto") + } catch(je:JSONException) { + Log.e(tag, "Failed to save userSettings JSON ${je.localizedMessage}") + } + } else { + // Not sure if this is the best place for this, but if a user has not changed any user settings in the app + // the object will not exist yet, could be moved to a centralized place or created on first app load + var userSettings = JSONObject() + userSettings.put("playbackRate", newRate.toDouble()) + sharedPrefEditor.putString("userSettings", userSettings.toString()) + Log.d(tag, "Created and saved userSettings JSON from Android Auto") + } + } + } + fun checkResetServerItems() { // When opening android auto need to check if still connected to server // and reset any server data already set diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt index 0dd7b2cb..bdddcae8 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt @@ -88,6 +88,24 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi playerNotificationService.seekPlayer(pos) } + private fun onChangeSpeed() { + // cycle to next speed, only contains preset android app options, as each increment needs it's own icon + var mediaManager = playerNotificationService.mediaManager + var currentSpeed = mediaManager.getSavedPlaybackRate() + var newSpeed = when (currentSpeed) { + 0.5f -> 1.0f + 1.0f -> 1.2f + 1.2f -> 1.5f + 1.5f -> 2.0f + 2.0f -> 3.0f + 3.0f -> 0.5f + // anything set above 3 (can happen in the android app) will be reset to 1 + else -> 1.0f + } + mediaManager.setSavedPlaybackRate(newSpeed) + playerNotificationService.setPlaybackSpeed(newSpeed) + } + override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) { Log.d(tag, "ON PLAY FROM MEDIA ID $mediaId") val libraryItemWrapper: LibraryItemWrapper? @@ -250,6 +268,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi CUSTOM_ACTION_JUMP_BACKWARD -> onRewind() CUSTOM_ACTION_SKIP_FORWARD -> onSkipToNext() CUSTOM_ACTION_SKIP_BACKWARD -> onSkipToPrevious() + CUSTOM_ACTION_CHANGE_SPEED -> onChangeSpeed() } } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerConstants.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerConstants.kt index 0938b912..35d787a7 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerConstants.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerConstants.kt @@ -4,6 +4,7 @@ const val CUSTOM_ACTION_JUMP_FORWARD = "com.audiobookshelf.customAction.jump_for const val CUSTOM_ACTION_JUMP_BACKWARD = "com.audiobookshelf.customAction.jump_backward"; const val CUSTOM_ACTION_SKIP_FORWARD = "com.audiobookshelf.customAction.skip_forward"; const val CUSTOM_ACTION_SKIP_BACKWARD = "com.audiobookshelf.customAction.skip_backward"; +const val CUSTOM_ACTION_CHANGE_SPEED = "com.audiobookshelf.customAction.change_speed"; const val PLAYMETHOD_DIRECTPLAY = 0 const val PLAYMETHOD_DIRECTSTREAM = 1 diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt index 15ced0bd..36fe2dd3 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt @@ -383,6 +383,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { val customActionProviders = mutableListOf( JumpBackwardCustomActionProvider(), JumpForwardCustomActionProvider(), + ChangePlaybackSpeedCustomActionProvider() // Will be pushed to far left ) val metadata = playbackSession.getMediaMetadataCompat(ctx) mediaSession.setMetadata(metadata) @@ -1175,5 +1176,37 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { ).build() } } + + inner class ChangePlaybackSpeedCustomActionProvider : CustomActionProvider { + override fun onCustomAction(player: Player, action: String, extras: Bundle?) { + /* + This does not appear to ever get called. Instead, MediaSessionCallback.onCustomAction() is + responsible to reacting to a custom action. + */ + } + + override fun getCustomAction(player: Player): PlaybackStateCompat.CustomAction? { + val playbackRate = mediaManager.getSavedPlaybackRate() + val drawable: Int = when (playbackRate) { + 0.5f -> R.drawable.ic_play_speed_0_5x + 1.0f -> R.drawable.ic_play_speed_1_0x + 1.2f -> R.drawable.ic_play_speed_1_2x + 1.5f -> R.drawable.ic_play_speed_1_5x + 2.0f -> R.drawable.ic_play_speed_2_0x + 3.0f -> R.drawable.ic_play_speed_3_0x + // anything set above 3 will be show the 3x to save from creating 100 icons + else -> R.drawable.ic_play_speed_3_0x + } + val customActionExtras = Bundle() + customActionExtras.putFloat("speed", playbackRate) + return PlaybackStateCompat.CustomAction.Builder( + CUSTOM_ACTION_CHANGE_SPEED, + getContext().getString(R.string.action_skip_backward), + drawable + ) + .setExtras(customActionExtras) + .build() + } + } } diff --git a/android/app/src/main/res/drawable-anydpi/ic_play_speed_0_5x.xml b/android/app/src/main/res/drawable-anydpi/ic_play_speed_0_5x.xml new file mode 100644 index 00000000..dc91ae5c --- /dev/null +++ b/android/app/src/main/res/drawable-anydpi/ic_play_speed_0_5x.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_0x.xml b/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_0x.xml new file mode 100644 index 00000000..e0c261c4 --- /dev/null +++ b/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_0x.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_2x.xml b/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_2x.xml new file mode 100644 index 00000000..6590aabc --- /dev/null +++ b/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_2x.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_5x.xml b/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_5x.xml new file mode 100644 index 00000000..1ed9793a --- /dev/null +++ b/android/app/src/main/res/drawable-anydpi/ic_play_speed_1_5x.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable-anydpi/ic_play_speed_2_0x.xml b/android/app/src/main/res/drawable-anydpi/ic_play_speed_2_0x.xml new file mode 100644 index 00000000..54ff98ae --- /dev/null +++ b/android/app/src/main/res/drawable-anydpi/ic_play_speed_2_0x.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable-anydpi/ic_play_speed_3_0x.xml b/android/app/src/main/res/drawable-anydpi/ic_play_speed_3_0x.xml new file mode 100644 index 00000000..6d834aab --- /dev/null +++ b/android/app/src/main/res/drawable-anydpi/ic_play_speed_3_0x.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable-hdpi/ic_play_speed_0_5x.png b/android/app/src/main/res/drawable-hdpi/ic_play_speed_0_5x.png new file mode 100644 index 00000000..ca5a5481 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_play_speed_0_5x.png differ diff --git a/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_0x.png b/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_0x.png new file mode 100644 index 00000000..cf490b4d Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_0x.png differ diff --git a/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_2x.png b/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_2x.png new file mode 100644 index 00000000..9c8aefb2 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_2x.png differ diff --git a/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_5x.png b/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_5x.png new file mode 100644 index 00000000..f5619d89 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_play_speed_1_5x.png differ diff --git a/android/app/src/main/res/drawable-hdpi/ic_play_speed_2_0x.png b/android/app/src/main/res/drawable-hdpi/ic_play_speed_2_0x.png new file mode 100644 index 00000000..2be3d42f Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_play_speed_2_0x.png differ diff --git a/android/app/src/main/res/drawable-hdpi/ic_play_speed_3_0x.png b/android/app/src/main/res/drawable-hdpi/ic_play_speed_3_0x.png new file mode 100644 index 00000000..4c1928de Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_play_speed_3_0x.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_play_speed_0_5x.png b/android/app/src/main/res/drawable-mdpi/ic_play_speed_0_5x.png new file mode 100644 index 00000000..536f9d93 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_play_speed_0_5x.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_0x.png b/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_0x.png new file mode 100644 index 00000000..d1aa14fe Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_0x.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_2x.png b/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_2x.png new file mode 100644 index 00000000..39a4c783 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_2x.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_5x.png b/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_5x.png new file mode 100644 index 00000000..6e387d0b Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_play_speed_1_5x.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_play_speed_2_0x.png b/android/app/src/main/res/drawable-mdpi/ic_play_speed_2_0x.png new file mode 100644 index 00000000..f1385c28 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_play_speed_2_0x.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_play_speed_3_0x.png b/android/app/src/main/res/drawable-mdpi/ic_play_speed_3_0x.png new file mode 100644 index 00000000..68d4f68d Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_play_speed_3_0x.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_play_speed_0_5x.png b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_0_5x.png new file mode 100644 index 00000000..f062a13b Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_0_5x.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_0x.png b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_0x.png new file mode 100644 index 00000000..025ad26a Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_0x.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_2x.png b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_2x.png new file mode 100644 index 00000000..b01cf919 Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_2x.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_5x.png b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_5x.png new file mode 100644 index 00000000..fab44afd Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_1_5x.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_play_speed_2_0x.png b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_2_0x.png new file mode 100644 index 00000000..2eac485d Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_2_0x.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_play_speed_3_0x.png b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_3_0x.png new file mode 100644 index 00000000..fb7b3d9f Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_play_speed_3_0x.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_0_5x.png b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_0_5x.png new file mode 100644 index 00000000..1aa6aa9d Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_0_5x.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_0x.png b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_0x.png new file mode 100644 index 00000000..f39d6661 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_0x.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_2x.png b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_2x.png new file mode 100644 index 00000000..78534fd7 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_2x.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_5x.png b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_5x.png new file mode 100644 index 00000000..99eef85d Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_1_5x.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_2_0x.png b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_2_0x.png new file mode 100644 index 00000000..30382a61 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_2_0x.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_3_0x.png b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_3_0x.png new file mode 100644 index 00000000..e35c0ccf Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_play_speed_3_0x.png differ