Add:Android device setting to adjust shake sensitivity for sleep timer #260

This commit is contained in:
advplyr 2023-01-28 17:31:52 -06:00
parent f06afbd08c
commit 28e50dd461
9 changed files with 115 additions and 70 deletions

View file

@ -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 }
}
}
}

View file

@ -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 ?: ""

View file

@ -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

View file

@ -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)
}

View file

@ -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()

View file

@ -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
}

View file

@ -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

View file

@ -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 []

View file

@ -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'