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