mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-07-12 23:14:48 +02:00
Update:Notification player closed when closing in-app player and stop button on notification player #255, Add:Setting for disabling auto rewind
This commit is contained in:
parent
3c1120ea48
commit
e4c6093a82
10 changed files with 59 additions and 51 deletions
|
@ -27,6 +27,11 @@ data class DeviceSettings(
|
||||||
return DeviceSettings(false, 10, 10)
|
return DeviceSettings(false, 10, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@get:JsonIgnore
|
||||||
|
val jumpBackwardsTimeMs get() = jumpBackwardsTime * 1000L
|
||||||
|
@get:JsonIgnore
|
||||||
|
val jumpForwardTimeMs get() = jumpForwardTime * 1000L
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DeviceData(
|
data class DeviceData(
|
||||||
|
|
|
@ -21,9 +21,6 @@ class AbMediaDescriptionAdapter constructor(private val controller: MediaControl
|
||||||
var currentIconUri: Uri? = null
|
var currentIconUri: Uri? = null
|
||||||
var currentBitmap: Bitmap? = null
|
var currentBitmap: Bitmap? = null
|
||||||
|
|
||||||
private val glideOptions = RequestOptions()
|
|
||||||
.fallback(R.drawable.icon)
|
|
||||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
|
||||||
private val serviceJob = SupervisorJob()
|
private val serviceJob = SupervisorJob()
|
||||||
private val serviceScope = CoroutineScope(Dispatchers.Main + serviceJob)
|
private val serviceScope = CoroutineScope(Dispatchers.Main + serviceJob)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.audiobookshelf.app.player
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.audiobookshelf.app.data.PlayerState
|
import com.audiobookshelf.app.data.PlayerState
|
||||||
|
import com.audiobookshelf.app.device.DeviceManager
|
||||||
import com.google.android.exoplayer2.PlaybackException
|
import com.google.android.exoplayer2.PlaybackException
|
||||||
import com.google.android.exoplayer2.Player
|
import com.google.android.exoplayer2.Player
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEvents(player: Player, events: Player.Events) {
|
override fun onEvents(player: Player, events: Player.Events) {
|
||||||
Log.d(tag, "onEvents ${player.deviceInfo} | ${playerNotificationService.getMediaPlayer()} | ${events.size()}")
|
Log.d(tag, "onEvents ${playerNotificationService.getMediaPlayer()} | ${events.size()}")
|
||||||
|
|
||||||
if (events.contains(Player.EVENT_POSITION_DISCONTINUITY)) {
|
if (events.contains(Player.EVENT_POSITION_DISCONTINUITY)) {
|
||||||
Log.d(tag, "EVENT_POSITION_DISCONTINUITY")
|
Log.d(tag, "EVENT_POSITION_DISCONTINUITY")
|
||||||
|
@ -69,7 +70,7 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) :
|
||||||
|
|
||||||
if (player.isPlaying) {
|
if (player.isPlaying) {
|
||||||
Log.d(tag, "SeekBackTime: Player is playing")
|
Log.d(tag, "SeekBackTime: Player is playing")
|
||||||
if (lastPauseTime > 0) {
|
if (lastPauseTime > 0 && DeviceManager.deviceData.deviceSettings?.disableAutoRewind != true) {
|
||||||
if (onSeekBack) onSeekBack = false
|
if (onSeekBack) onSeekBack = false
|
||||||
else {
|
else {
|
||||||
Log.d(tag, "SeekBackTime: playing started now set seek back time $lastPauseTime")
|
Log.d(tag, "SeekBackTime: playing started now set seek back time $lastPauseTime")
|
||||||
|
|
|
@ -14,6 +14,7 @@ class PlayerNotificationListener(var playerNotificationService:PlayerNotificatio
|
||||||
|
|
||||||
// Start foreground service
|
// Start foreground service
|
||||||
Log.d(tag, "Notification Posted $notificationId - Start Foreground | $notification")
|
Log.d(tag, "Notification Posted $notificationId - Start Foreground | $notification")
|
||||||
|
PlayerNotificationService.isClosed = false
|
||||||
playerNotificationService.startForeground(notificationId, notification)
|
playerNotificationService.startForeground(notificationId, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +27,12 @@ class PlayerNotificationListener(var playerNotificationService:PlayerNotificatio
|
||||||
playerNotificationService.stopSelf()
|
playerNotificationService.stopSelf()
|
||||||
} else {
|
} else {
|
||||||
Log.d(tag, "onNotificationCancelled not dismissed by user")
|
Log.d(tag, "onNotificationCancelled not dismissed by user")
|
||||||
|
|
||||||
|
// When stop button is pressed on the notification I guess it isn't considered "dismissedByUser" so we need to close playback ourselves
|
||||||
|
if (!PlayerNotificationService.isClosed) {
|
||||||
|
Log.d(tag, "PNS is not closed - closing it now")
|
||||||
|
playerNotificationService.closePlayback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var isStarted = false
|
var isStarted = false
|
||||||
|
var isClosed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClientEventEmitter {
|
interface ClientEventEmitter {
|
||||||
|
@ -112,11 +113,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
fun getService(): PlayerNotificationService = this@PlayerNotificationService
|
fun getService(): PlayerNotificationService = this@PlayerNotificationService
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopService(context: Context) {
|
|
||||||
val stopIntent = Intent(context, PlayerNotificationService::class.java)
|
|
||||||
context.stopService(stopIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
isStarted = true
|
isStarted = true
|
||||||
Log.d(tag, "onStartCommand $startId")
|
Log.d(tag, "onStartCommand $startId")
|
||||||
|
@ -159,31 +155,11 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
Log.d(tag, "onCreate")
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
ctx = this
|
ctx = this
|
||||||
|
|
||||||
// Initialize player
|
|
||||||
val customLoadControl:LoadControl = DefaultLoadControl.Builder().setBufferDurationsMs(
|
|
||||||
1000 * 20, // 20s min buffer
|
|
||||||
1000 * 45, // 45s max buffer
|
|
||||||
1000 * 5, // 5s playback start
|
|
||||||
1000 * 20 // 20s playback rebuffer
|
|
||||||
).build()
|
|
||||||
|
|
||||||
mPlayer = ExoPlayer.Builder(this)
|
|
||||||
.setLoadControl(customLoadControl)
|
|
||||||
.setSeekBackIncrementMs(10000)
|
|
||||||
.setSeekForwardIncrementMs(10000)
|
|
||||||
.build()
|
|
||||||
mPlayer.setHandleAudioBecomingNoisy(true)
|
|
||||||
mPlayer.addListener(PlayerListener(this))
|
|
||||||
val audioAttributes:AudioAttributes = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).setContentType(C.CONTENT_TYPE_SPEECH).build()
|
|
||||||
mPlayer.setAudioAttributes(audioAttributes, true)
|
|
||||||
|
|
||||||
currentPlayer = mPlayer
|
|
||||||
|
|
||||||
// Initialize API
|
// Initialize API
|
||||||
apiHandler = ApiHandler(ctx)
|
apiHandler = ApiHandler(ctx)
|
||||||
|
|
||||||
|
@ -234,6 +210,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
playerNotificationManager.setUseNextAction(false)
|
playerNotificationManager.setUseNextAction(false)
|
||||||
playerNotificationManager.setUsePreviousAction(false)
|
playerNotificationManager.setUsePreviousAction(false)
|
||||||
playerNotificationManager.setUseChronometer(false)
|
playerNotificationManager.setUseChronometer(false)
|
||||||
|
playerNotificationManager.setUseStopAction(true)
|
||||||
playerNotificationManager.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
playerNotificationManager.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
playerNotificationManager.setPriority(NotificationCompat.PRIORITY_MAX)
|
playerNotificationManager.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
playerNotificationManager.setUseFastForwardActionInCompactView(true)
|
playerNotificationManager.setUseFastForwardActionInCompactView(true)
|
||||||
|
@ -276,17 +253,47 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
)
|
)
|
||||||
mediaSessionConnector.setQueueNavigator(queueNavigator)
|
mediaSessionConnector.setQueueNavigator(queueNavigator)
|
||||||
mediaSessionConnector.setPlaybackPreparer(MediaSessionPlaybackPreparer(this))
|
mediaSessionConnector.setPlaybackPreparer(MediaSessionPlaybackPreparer(this))
|
||||||
mediaSessionConnector.setPlayer(mPlayer)
|
|
||||||
|
mediaSession.setCallback(MediaSessionCallback(this))
|
||||||
|
|
||||||
|
initializeMPlayer()
|
||||||
|
currentPlayer = mPlayer
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeMPlayer() {
|
||||||
|
val customLoadControl:LoadControl = DefaultLoadControl.Builder().setBufferDurationsMs(
|
||||||
|
1000 * 20, // 20s min buffer
|
||||||
|
1000 * 45, // 45s max buffer
|
||||||
|
1000 * 5, // 5s playback start
|
||||||
|
1000 * 20 // 20s playback rebuffer
|
||||||
|
).build()
|
||||||
|
|
||||||
|
val seekBackTime = DeviceManager.deviceData.deviceSettings?.jumpBackwardsTimeMs ?: 10000
|
||||||
|
val seekForwardTime = DeviceManager.deviceData.deviceSettings?.jumpForwardTimeMs ?: 10000
|
||||||
|
Log.d(tag, "Seek Back Time $seekBackTime")
|
||||||
|
Log.d(tag, "Seek Forward Time $seekForwardTime")
|
||||||
|
|
||||||
|
mPlayer = ExoPlayer.Builder(this)
|
||||||
|
.setLoadControl(customLoadControl)
|
||||||
|
.setSeekBackIncrementMs(seekBackTime)
|
||||||
|
.setSeekForwardIncrementMs(seekForwardTime)
|
||||||
|
.build()
|
||||||
|
mPlayer.setHandleAudioBecomingNoisy(true)
|
||||||
|
mPlayer.addListener(PlayerListener(this))
|
||||||
|
val audioAttributes:AudioAttributes = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).setContentType(C.CONTENT_TYPE_SPEECH).build()
|
||||||
|
mPlayer.setAudioAttributes(audioAttributes, true)
|
||||||
|
|
||||||
//attach player to playerNotificationManager
|
//attach player to playerNotificationManager
|
||||||
playerNotificationManager.setPlayer(mPlayer)
|
playerNotificationManager.setPlayer(mPlayer)
|
||||||
mediaSession.setCallback(MediaSessionCallback(this))
|
|
||||||
|
mediaSessionConnector.setPlayer(mPlayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
User callable methods
|
User callable methods
|
||||||
*/
|
*/
|
||||||
fun preparePlayer(playbackSession: PlaybackSession, playWhenReady:Boolean, playbackRate:Float?) {
|
fun preparePlayer(playbackSession: PlaybackSession, playWhenReady:Boolean, playbackRate:Float?) {
|
||||||
|
isClosed = false
|
||||||
val playbackRateToUse = playbackRate ?: initialPlaybackRate ?: 1f
|
val playbackRateToUse = playbackRate ?: initialPlaybackRate ?: 1f
|
||||||
initialPlaybackRate = playbackRate
|
initialPlaybackRate = playbackRate
|
||||||
|
|
||||||
|
@ -633,11 +640,14 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun closePlayback() {
|
fun closePlayback() {
|
||||||
|
Log.d(tag, "closePlayback")
|
||||||
currentPlayer.clearMediaItems()
|
currentPlayer.clearMediaItems()
|
||||||
currentPlayer.stop()
|
currentPlayer.stop()
|
||||||
currentPlaybackSession = null
|
currentPlaybackSession = null
|
||||||
clientEventEmitter?.onPlaybackClosed()
|
clientEventEmitter?.onPlaybackClosed()
|
||||||
PlayerListener.lastPauseTime = 0
|
PlayerListener.lastPauseTime = 0
|
||||||
|
isClosed = true
|
||||||
|
stopForeground(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendClientMetadata(playerState: PlayerState) {
|
fun sendClientMetadata(playerState: PlayerState) {
|
||||||
|
|
|
@ -11,11 +11,5 @@
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="@android:color/white"
|
||||||
android:pathData="M18,13c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6s2.69,-6 6,-6v4l5,-5l-5,-5v4c-4.42,0 -8,3.58 -8,8c0,4.42 3.58,8 8,8s8,-3.58 8,-8H18z"/>
|
android:pathData="M18,13c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6s2.69,-6 6,-6v4l5,-5l-5,-5v4c-4.42,0 -8,3.58 -8,8c0,4.42 3.58,8 8,8s8,-3.58 8,-8H18z"/>
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M10.86,15.94l0,-4.27l-0.09,0l-1.77,0.63l0,0.69l1.01,-0.31l0,3.26z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M12.25,13.44v0.74c0,1.9 1.31,1.82 1.44,1.82c0.14,0 1.44,0.09 1.44,-1.82v-0.74c0,-1.9 -1.31,-1.82 -1.44,-1.82C13.55,11.62 12.25,11.53 12.25,13.44zM14.29,13.32v0.97c0,0.77 -0.21,1.03 -0.59,1.03c-0.38,0 -0.6,-0.26 -0.6,-1.03v-0.97c0,-0.75 0.22,-1.01 0.59,-1.01C14.07,12.3 14.29,12.57 14.29,13.32z"/>
|
|
||||||
</group>
|
</group>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -11,11 +11,5 @@
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="@android:color/white"
|
||||||
android:pathData="M11.99,5V1l-5,5l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6s-6,-2.69 -6,-6h-2c0,4.42 3.58,8 8,8s8,-3.58 8,-8S16.41,5 11.99,5z"/>
|
android:pathData="M11.99,5V1l-5,5l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6s-6,-2.69 -6,-6h-2c0,4.42 3.58,8 8,8s8,-3.58 8,-8S16.41,5 11.99,5z"/>
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M10.89,16h-0.85v-3.26l-1.01,0.31v-0.69l1.77,-0.63h0.09V16z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M15.17,14.24c0,0.32 -0.03,0.6 -0.1,0.82s-0.17,0.42 -0.29,0.57s-0.28,0.26 -0.45,0.33s-0.37,0.1 -0.59,0.1s-0.41,-0.03 -0.59,-0.1s-0.33,-0.18 -0.46,-0.33s-0.23,-0.34 -0.3,-0.57s-0.11,-0.5 -0.11,-0.82V13.5c0,-0.32 0.03,-0.6 0.1,-0.82s0.17,-0.42 0.29,-0.57s0.28,-0.26 0.45,-0.33s0.37,-0.1 0.59,-0.1s0.41,0.03 0.59,0.1c0.18,0.07 0.33,0.18 0.46,0.33s0.23,0.34 0.3,0.57s0.11,0.5 0.11,0.82V14.24zM14.32,13.38c0,-0.19 -0.01,-0.35 -0.04,-0.48s-0.07,-0.23 -0.12,-0.31s-0.11,-0.14 -0.19,-0.17s-0.16,-0.05 -0.25,-0.05s-0.18,0.02 -0.25,0.05s-0.14,0.09 -0.19,0.17s-0.09,0.18 -0.12,0.31s-0.04,0.29 -0.04,0.48v0.97c0,0.19 0.01,0.35 0.04,0.48s0.07,0.24 0.12,0.32s0.11,0.14 0.19,0.17s0.16,0.05 0.25,0.05s0.18,-0.02 0.25,-0.05s0.14,-0.09 0.19,-0.17s0.09,-0.19 0.11,-0.32s0.04,-0.29 0.04,-0.48V13.38z"/>
|
|
||||||
</group>
|
</group>
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -2,6 +2,4 @@
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M18,13c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6s2.69,-6 6,-6v4l5,-5l-5,-5v4c-4.42,0 -8,3.58 -8,8c0,4.42 3.58,8 8,8s8,-3.58 8,-8H18z"/>
|
<path android:fillColor="@android:color/white" android:pathData="M18,13c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6s2.69,-6 6,-6v4l5,-5l-5,-5v4c-4.42,0 -8,3.58 -8,8c0,4.42 3.58,8 8,8s8,-3.58 8,-8H18z"/>
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M10.86,15.94l0,-4.27l-0.09,0l-1.77,0.63l0,0.69l1.01,-0.31l0,3.26z"/>
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M12.25,13.44v0.74c0,1.9 1.31,1.82 1.44,1.82c0.14,0 1.44,0.09 1.44,-1.82v-0.74c0,-1.9 -1.31,-1.82 -1.44,-1.82C13.55,11.62 12.25,11.53 12.25,13.44zM14.29,13.32v0.97c0,0.77 -0.21,1.03 -0.59,1.03c-0.38,0 -0.6,-0.26 -0.6,-1.03v-0.97c0,-0.75 0.22,-1.01 0.59,-1.01C14.07,12.3 14.29,12.57 14.29,13.32z"/>
|
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -2,6 +2,4 @@
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M11.99,5V1l-5,5l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6s-6,-2.69 -6,-6h-2c0,4.42 3.58,8 8,8s8,-3.58 8,-8S16.41,5 11.99,5z"/>
|
<path android:fillColor="@android:color/white" android:pathData="M11.99,5V1l-5,5l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6s-6,-2.69 -6,-6h-2c0,4.42 3.58,8 8,8s8,-3.58 8,-8S16.41,5 11.99,5z"/>
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M10.89,16h-0.85v-3.26l-1.01,0.31v-0.69l1.77,-0.63h0.09V16z"/>
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M15.17,14.24c0,0.32 -0.03,0.6 -0.1,0.82s-0.17,0.42 -0.29,0.57s-0.28,0.26 -0.45,0.33s-0.37,0.1 -0.59,0.1s-0.41,-0.03 -0.59,-0.1s-0.33,-0.18 -0.46,-0.33s-0.23,-0.34 -0.3,-0.57s-0.11,-0.5 -0.11,-0.82V13.5c0,-0.32 0.03,-0.6 0.1,-0.82s0.17,-0.42 0.29,-0.57s0.28,-0.26 0.45,-0.33s0.37,-0.1 0.59,-0.1s0.41,0.03 0.59,0.1c0.18,0.07 0.33,0.18 0.46,0.33s0.23,0.34 0.3,0.57s0.11,0.5 0.11,0.82V14.24zM14.32,13.38c0,-0.19 -0.01,-0.35 -0.04,-0.48s-0.07,-0.23 -0.12,-0.31s-0.11,-0.14 -0.19,-0.17s-0.16,-0.05 -0.25,-0.05s-0.18,0.02 -0.25,0.05s-0.14,0.09 -0.19,0.17s-0.09,0.18 -0.12,0.31s-0.04,0.29 -0.04,0.48v0.97c0,0.19 0.01,0.35 0.04,0.48s0.07,0.24 0.12,0.32s0.11,0.14 0.19,0.17s0.16,0.05 0.25,0.05s0.18,-0.02 0.25,-0.05s0.14,-0.09 0.19,-0.17s0.09,-0.19 0.11,-0.32s0.04,-0.29 0.04,-0.48V13.38z"/>
|
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -76,10 +76,14 @@
|
||||||
import { Dialog } from '@capacitor/dialog'
|
import { Dialog } from '@capacitor/dialog'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {},
|
props: {
|
||||||
|
deviceData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
deviceData: null,
|
|
||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
showAuth: false,
|
showAuth: false,
|
||||||
processing: false,
|
processing: false,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue