mirror of
https://github.com/sudoxnym/audiobookshelf-atv.git
synced 2026-04-14 19:46:30 +00:00
Add:Android device setting to adjust shake sensitivity for sleep timer #260
This commit is contained in:
parent
f06afbd08c
commit
28e50dd461
9 changed files with 115 additions and 70 deletions
|
|
@ -2,6 +2,7 @@ package com.audiobookshelf.app.data
|
|||
|
||||
import android.content.Context
|
||||
import android.support.v4.media.MediaDescriptionCompat
|
||||
import android.util.Log
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||
|
|
@ -15,6 +16,10 @@ enum class HapticFeedbackSetting {
|
|||
OFF, LIGHT, MEDIUM, HEAVY
|
||||
}
|
||||
|
||||
enum class ShakeSensitivitySetting {
|
||||
VERY_LOW, LOW, MEDIUM, HIGH, VERY_HIGH
|
||||
}
|
||||
|
||||
data class ServerConnectionConfig(
|
||||
var id:String,
|
||||
var index:Int,
|
||||
|
|
@ -26,50 +31,6 @@ data class ServerConnectionConfig(
|
|||
var customHeaders:Map<String, String>?
|
||||
)
|
||||
|
||||
data class DeviceSettings(
|
||||
var disableAutoRewind:Boolean,
|
||||
var enableAltView:Boolean,
|
||||
var jumpBackwardsTime:Int,
|
||||
var jumpForwardTime:Int,
|
||||
var disableShakeToResetSleepTimer:Boolean,
|
||||
var lockOrientation:LockOrientationSetting,
|
||||
var hapticFeedback: HapticFeedbackSetting
|
||||
) {
|
||||
companion object {
|
||||
// Static method to get default device settings
|
||||
fun default():DeviceSettings {
|
||||
return DeviceSettings(
|
||||
disableAutoRewind = false,
|
||||
enableAltView = false,
|
||||
jumpBackwardsTime = 10,
|
||||
jumpForwardTime = 10,
|
||||
disableShakeToResetSleepTimer = false,
|
||||
lockOrientation = LockOrientationSetting.NONE,
|
||||
hapticFeedback = HapticFeedbackSetting.LIGHT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@get:JsonIgnore
|
||||
val jumpBackwardsTimeMs get() = (jumpBackwardsTime ?: default().jumpBackwardsTime) * 1000L
|
||||
@get:JsonIgnore
|
||||
val jumpForwardTimeMs get() = (jumpForwardTime ?: default().jumpForwardTime) * 1000L
|
||||
}
|
||||
|
||||
data class DeviceData(
|
||||
var serverConnectionConfigs:MutableList<ServerConnectionConfig>,
|
||||
var lastServerConnectionConfigId:String?,
|
||||
var currentLocalPlaybackSession:PlaybackSession?, // Stored to open up where left off for local media
|
||||
var deviceSettings:DeviceSettings?
|
||||
) {
|
||||
@JsonIgnore
|
||||
fun getLastServerConnectionConfig():ServerConnectionConfig? {
|
||||
return lastServerConnectionConfigId?.let { lsccid ->
|
||||
return serverConnectionConfigs.find { it.id == lsccid }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class LocalFile(
|
||||
var id:String,
|
||||
|
|
@ -127,3 +88,64 @@ data class PlayItemRequestPayload(
|
|||
var forceTranscode:Boolean,
|
||||
var deviceInfo:DeviceInfo
|
||||
)
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class DeviceSettings(
|
||||
var disableAutoRewind:Boolean,
|
||||
var enableAltView:Boolean,
|
||||
var jumpBackwardsTime:Int,
|
||||
var jumpForwardTime:Int,
|
||||
var disableShakeToResetSleepTimer:Boolean,
|
||||
var shakeSensitivity: ShakeSensitivitySetting,
|
||||
var lockOrientation: LockOrientationSetting,
|
||||
var hapticFeedback: HapticFeedbackSetting
|
||||
) {
|
||||
companion object {
|
||||
// Static method to get default device settings
|
||||
fun default():DeviceSettings {
|
||||
return DeviceSettings(
|
||||
disableAutoRewind = false,
|
||||
enableAltView = false,
|
||||
jumpBackwardsTime = 10,
|
||||
jumpForwardTime = 10,
|
||||
disableShakeToResetSleepTimer = false,
|
||||
shakeSensitivity = ShakeSensitivitySetting.MEDIUM,
|
||||
lockOrientation = LockOrientationSetting.NONE,
|
||||
hapticFeedback = HapticFeedbackSetting.LIGHT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@get:JsonIgnore
|
||||
val jumpBackwardsTimeMs get() = jumpBackwardsTime * 1000L
|
||||
@get:JsonIgnore
|
||||
val jumpForwardTimeMs get() = jumpForwardTime * 1000L
|
||||
|
||||
@JsonIgnore
|
||||
fun getShakeThresholdGravity() : Float { // Used in ShakeDetector
|
||||
return if (shakeSensitivity == ShakeSensitivitySetting.VERY_HIGH) 1.2f
|
||||
else if (shakeSensitivity == ShakeSensitivitySetting.HIGH) 1.4f
|
||||
else if (shakeSensitivity == ShakeSensitivitySetting.MEDIUM) 1.6f
|
||||
else if (shakeSensitivity == ShakeSensitivitySetting.LOW) 2f
|
||||
else if (shakeSensitivity == ShakeSensitivitySetting.VERY_LOW) 2.7f
|
||||
else {
|
||||
Log.e("DeviceSetting", "Invalid ShakeSensitivitySetting $shakeSensitivity")
|
||||
1.6f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class DeviceData(
|
||||
var serverConnectionConfigs:MutableList<ServerConnectionConfig>,
|
||||
var lastServerConnectionConfigId:String?,
|
||||
var currentLocalPlaybackSession: PlaybackSession?, // Stored to open up where left off for local media
|
||||
var deviceSettings: DeviceSettings?
|
||||
) {
|
||||
@JsonIgnore
|
||||
fun getLastServerConnectionConfig(): ServerConnectionConfig? {
|
||||
return lastServerConnectionConfigId?.let { lsccid ->
|
||||
return serverConnectionConfigs.find { it.id == lsccid }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ object DeviceManager {
|
|||
const val tag = "DeviceManager"
|
||||
|
||||
val dbManager: DbManager = DbManager()
|
||||
var deviceData:DeviceData = dbManager.getDeviceData()
|
||||
var deviceData: DeviceData = dbManager.getDeviceData()
|
||||
var serverConnectionConfig: ServerConnectionConfig? = null
|
||||
|
||||
val serverConnectionConfigId get() = serverConnectionConfig?.id ?: ""
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.arthenica.ffmpegkit.FFprobeKit
|
|||
import com.arthenica.ffmpegkit.Level
|
||||
import com.audiobookshelf.app.data.*
|
||||
import com.audiobookshelf.app.models.DownloadItem
|
||||
import com.audiobookshelf.app.plugins.AbsDownloader
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
|
|
|
|||
|
|
@ -12,16 +12,6 @@ import kotlin.math.roundToInt
|
|||
|
||||
const val SLEEP_EXTENSION_TIME = 900000L // 15m
|
||||
|
||||
class ShakeSensitivity {
|
||||
companion object {
|
||||
val veryHigh = 1f
|
||||
val high = 1.3f
|
||||
val medium = 1.6f
|
||||
val low = 2f
|
||||
val veryLow = 2.7f
|
||||
}
|
||||
}
|
||||
|
||||
class SleepTimerManager constructor(val playerNotificationService: PlayerNotificationService) {
|
||||
private val tag = "SleepTimerManager"
|
||||
|
||||
|
|
@ -194,8 +184,8 @@ class SleepTimerManager constructor(val playerNotificationService: PlayerNotific
|
|||
return
|
||||
}
|
||||
|
||||
// Does not apply to chapter sleep timers
|
||||
if (sleepTimerEndTime == 0L) {
|
||||
// Does not apply to chapter sleep timers and timer must be running for at least 3 seconds
|
||||
if (sleepTimerEndTime == 0L && sleepTimerElapsed > 3000L) {
|
||||
vibrate()
|
||||
setSleepTimer(sleepTimerExtensionTime, false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import com.audiobookshelf.app.managers.DbManager
|
|||
import com.audiobookshelf.app.managers.SleepTimerManager
|
||||
import com.audiobookshelf.app.media.MediaManager
|
||||
import com.audiobookshelf.app.server.ApiHandler
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.google.android.exoplayer2.*
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
||||
|
|
@ -849,8 +848,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||
return DeviceInfo(Build.MANUFACTURER, Build.MODEL, Build.BRAND, Build.VERSION.SDK_INT, BuildConfig.VERSION_NAME)
|
||||
}
|
||||
|
||||
@get:JsonIgnore
|
||||
val deviceSettings get() = DeviceManager.deviceData.deviceSettings ?: DeviceSettings.default()
|
||||
private val deviceSettings get() = DeviceManager.deviceData.deviceSettings ?: DeviceSettings.default()
|
||||
|
||||
fun getPlayItemRequestPayload(forceTranscode:Boolean):PlayItemRequestPayload {
|
||||
return PlayItemRequestPayload(getMediaPlayer(), !forceTranscode, forceTranscode, getDeviceInfo())
|
||||
|
|
@ -1046,7 +1044,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||
// Shake sensor used for sleep timer
|
||||
fun registerSensor() {
|
||||
if (isShakeSensorRegistered) {
|
||||
Log.w(tag, "Shake sensor already registered")
|
||||
Log.i(tag, "Shake sensor already registered")
|
||||
return
|
||||
}
|
||||
shakeSensorUnregisterTask?.cancel()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.hardware.Sensor
|
|||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import com.audiobookshelf.app.device.DeviceManager
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class ShakeDetector : SensorEventListener {
|
||||
|
|
@ -34,7 +35,8 @@ class ShakeDetector : SensorEventListener {
|
|||
val gZ = z / SensorManager.GRAVITY_EARTH
|
||||
// gForce will be close to 1 when there is no movement.
|
||||
val gForce: Float = sqrt(gX * gX + gY * gY + gZ * gZ)
|
||||
if (gForce > SHAKE_THRESHOLD_GRAVITY) {
|
||||
val shakeThreshold = DeviceManager.deviceData.deviceSettings?.getShakeThresholdGravity() ?: 1.6f
|
||||
if (gForce > shakeThreshold) {
|
||||
val now = System.currentTimeMillis()
|
||||
// ignore shake events too close to each other (500ms)
|
||||
if (mShakeTimestamp + SHAKE_SLOP_TIME_MS > now) {
|
||||
|
|
@ -59,7 +61,7 @@ class ShakeDetector : SensorEventListener {
|
|||
* from the Google Play Store and run it to see how
|
||||
* many G's it takes to register a shake
|
||||
*/
|
||||
var SHAKE_THRESHOLD_GRAVITY = 1.5f // orig 2.7f
|
||||
// var SHAKE_THRESHOLD_GRAVITY = 1.5f // orig 2.7f
|
||||
private const val SHAKE_SLOP_TIME_MS = 500
|
||||
private const val SHAKE_COUNT_RESET_TIME_MS = 3000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import kotlinx.coroutines.launch
|
|||
@CapacitorPlugin(name = "AbsDatabase")
|
||||
class AbsDatabase : Plugin() {
|
||||
val tag = "AbsDatabase"
|
||||
var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||
private var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||
|
||||
lateinit var mainActivity: MainActivity
|
||||
lateinit var apiHandler: ApiHandler
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export default {
|
|||
return {
|
||||
shelves: [],
|
||||
loading: false,
|
||||
lastServerFetch: 0,
|
||||
localLibraryItems: []
|
||||
}
|
||||
},
|
||||
|
|
@ -47,9 +48,11 @@ export default {
|
|||
console.log(`Network changed to ${newVal} - fetch categories`)
|
||||
|
||||
if (newVal) {
|
||||
setTimeout(() => {
|
||||
this.fetchCategories()
|
||||
}, 4000)
|
||||
if (!this.lastServerFetch || Date.now() - this.lastServerFetch < 4000) {
|
||||
setTimeout(() => {
|
||||
this.fetchCategories()
|
||||
}, 4000)
|
||||
}
|
||||
} else {
|
||||
this.fetchCategories()
|
||||
}
|
||||
|
|
@ -131,6 +134,7 @@ export default {
|
|||
const localCategories = await this.getLocalMediaItemCategories()
|
||||
|
||||
if (this.user && this.currentLibraryId && this.networkConnected) {
|
||||
this.lastServerFetch = Date.now()
|
||||
const categories = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/personalized?minified=1`).catch((error) => {
|
||||
console.error('Failed to fetch categories', error)
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="w-full h-full p-8">
|
||||
<div class="w-full h-full px-8 pt-8 pb-48 overflow-y-auto">
|
||||
<!-- Display settings -->
|
||||
<p class="uppercase text-xs font-semibold text-gray-300 mb-2">User Interface Settings</p>
|
||||
<div class="flex items-center py-3" @click="toggleEnableAltView">
|
||||
|
|
@ -49,6 +49,10 @@
|
|||
<p class="pl-4">Disable shake to reset</p>
|
||||
<span class="material-icons-outlined ml-2" @click.stop="showInfo('disableShakeToResetSleepTimer')">info</span>
|
||||
</div>
|
||||
<div v-if="!isiOS && !settings.disableShakeToResetSleepTimer" class="py-3 flex items-center">
|
||||
<p class="pr-4">Shake Sensitivity</p>
|
||||
<ui-dropdown v-model="settings.shakeSensitivity" :items="shakeSensitivityItems" style="max-width: 125px" @input="sensitivityUpdated" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -65,6 +69,7 @@ export default {
|
|||
jumpForwardTime: 10,
|
||||
jumpBackwardsTime: 10,
|
||||
disableShakeToResetSleepTimer: false,
|
||||
shakeSensitivity: 'MEDIUM',
|
||||
lockOrientation: 0,
|
||||
hapticFeedback: 'LIGHT'
|
||||
},
|
||||
|
|
@ -92,6 +97,28 @@ export default {
|
|||
text: 'Heavy',
|
||||
value: 'HEAVY'
|
||||
}
|
||||
],
|
||||
shakeSensitivityItems: [
|
||||
{
|
||||
text: 'Very Low',
|
||||
value: 'VERY_LOW'
|
||||
},
|
||||
{
|
||||
text: 'Low',
|
||||
value: 'LOW'
|
||||
},
|
||||
{
|
||||
text: 'Medium',
|
||||
value: 'MEDIUM'
|
||||
},
|
||||
{
|
||||
text: 'High',
|
||||
value: 'HIGH'
|
||||
},
|
||||
{
|
||||
text: 'Very High',
|
||||
value: 'VERY_HIGH'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
@ -121,6 +148,9 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
sensitivityUpdated(val) {
|
||||
this.saveSettings()
|
||||
},
|
||||
hapticFeedbackUpdated(val) {
|
||||
this.$store.commit('globals/setHapticFeedback', val)
|
||||
this.saveSettings()
|
||||
|
|
@ -176,7 +206,6 @@ export default {
|
|||
async saveSettings() {
|
||||
await this.$hapticsImpact()
|
||||
const updatedDeviceData = await this.$db.updateDeviceSettings({ ...this.settings })
|
||||
console.log('Saved device data', updatedDeviceData)
|
||||
if (updatedDeviceData) {
|
||||
this.$store.commit('setDeviceData', updatedDeviceData)
|
||||
this.init()
|
||||
|
|
@ -192,6 +221,7 @@ export default {
|
|||
this.settings.jumpForwardTime = deviceSettings.jumpForwardTime || 10
|
||||
this.settings.jumpBackwardsTime = deviceSettings.jumpBackwardsTime || 10
|
||||
this.settings.disableShakeToResetSleepTimer = !!deviceSettings.disableShakeToResetSleepTimer
|
||||
this.settings.shakeSensitivity = deviceSettings.shakeSensitivity || 'MEDIUM'
|
||||
this.settings.lockOrientation = deviceSettings.lockOrientation || 'NONE'
|
||||
this.lockCurrentOrientation = this.settings.lockOrientation !== 'NONE'
|
||||
this.settings.hapticFeedback = deviceSettings.hapticFeedback || 'LIGHT'
|
||||
|
|
|
|||
Loading…
Reference in a new issue