Servers database v2 (#177)
* New server db schema Adds support for multiple addresses and users per server * Fix crash when the only available server is deleted and app is restarted * Set serverId as foreign key in User and ServerAddress * Format using ktlint * Bump ServerDatabase version to 2
This commit is contained in:
parent
4ab0a96740
commit
d3b4fe6ea3
16 changed files with 202 additions and 40 deletions
|
@ -11,7 +11,7 @@ import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.ServerGridAdapter
|
import dev.jdtech.jellyfin.adapters.ServerGridAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.database.Server
|
import dev.jdtech.jellyfin.models.Server
|
||||||
import dev.jdtech.jellyfin.models.User
|
import dev.jdtech.jellyfin.models.User
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
|
@ -5,8 +5,8 @@ import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dev.jdtech.jellyfin.database.Server
|
|
||||||
import dev.jdtech.jellyfin.databinding.ServerItemBinding
|
import dev.jdtech.jellyfin.databinding.ServerItemBinding
|
||||||
|
import dev.jdtech.jellyfin.models.Server
|
||||||
|
|
||||||
class ServerGridAdapter(
|
class ServerGridAdapter(
|
||||||
private val onClickListener: OnClickListener,
|
private val onClickListener: OnClickListener,
|
||||||
|
|
|
@ -2,8 +2,13 @@ package dev.jdtech.jellyfin.database
|
||||||
|
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
|
import androidx.room.TypeConverters
|
||||||
|
import dev.jdtech.jellyfin.models.Server
|
||||||
|
import dev.jdtech.jellyfin.models.ServerAddress
|
||||||
|
import dev.jdtech.jellyfin.models.User
|
||||||
|
|
||||||
@Database(entities = [Server::class], version = 1, exportSchema = false)
|
@Database(entities = [Server::class, ServerAddress::class, User::class], version = 2, exportSchema = false)
|
||||||
|
@TypeConverters(Converters::class)
|
||||||
abstract class ServerDatabase : RoomDatabase() {
|
abstract class ServerDatabase : RoomDatabase() {
|
||||||
abstract val serverDatabaseDao: ServerDatabaseDao
|
abstract val serverDatabaseDao: ServerDatabaseDao
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,25 @@ import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.Transaction
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
|
import dev.jdtech.jellyfin.models.Server
|
||||||
|
import dev.jdtech.jellyfin.models.ServerAddress
|
||||||
|
import dev.jdtech.jellyfin.models.ServerWithAddresses
|
||||||
|
import dev.jdtech.jellyfin.models.ServerWithAddressesAndUsers
|
||||||
|
import dev.jdtech.jellyfin.models.User
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface ServerDatabaseDao {
|
interface ServerDatabaseDao {
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insert(server: Server)
|
fun insertServer(server: Server)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertServerAddress(address: ServerAddress)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertUser(user: User)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
fun update(server: Server)
|
fun update(server: Server)
|
||||||
|
@ -18,6 +31,21 @@ interface ServerDatabaseDao {
|
||||||
@Query("select * from servers where id = :id")
|
@Query("select * from servers where id = :id")
|
||||||
fun get(id: String): Server?
|
fun get(id: String): Server?
|
||||||
|
|
||||||
|
@Query("select * from users where id = :id")
|
||||||
|
fun getUser(id: UUID): User?
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("select * from servers where id = :id")
|
||||||
|
fun getServerWithAddresses(id: String): ServerWithAddresses
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("select * from servers where id = :id")
|
||||||
|
fun getServerWithUsers(id: String): ServerWithAddresses
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("select * from servers where id = :id")
|
||||||
|
fun getServerWithAddressesAndUsers(id: String): ServerWithAddressesAndUsers?
|
||||||
|
|
||||||
@Query("delete from servers")
|
@Query("delete from servers")
|
||||||
fun clear()
|
fun clear()
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||||
import java.util.UUID
|
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
|
@ -26,11 +25,14 @@ object ApiModule {
|
||||||
|
|
||||||
val serverId = sharedPreferences.getString("selectedServer", null)
|
val serverId = sharedPreferences.getString("selectedServer", null)
|
||||||
if (serverId != null) {
|
if (serverId != null) {
|
||||||
val server = serverDatabase.get(serverId) ?: return jellyfinApi
|
val serverWithAddressesAndUsers = serverDatabase.getServerWithAddressesAndUsers(serverId) ?: return jellyfinApi
|
||||||
|
val server = serverWithAddressesAndUsers.server
|
||||||
|
val serverAddress = serverWithAddressesAndUsers.addresses.firstOrNull { it.id == server.currentServerAddressId } ?: return jellyfinApi
|
||||||
|
val user = serverWithAddressesAndUsers.users.firstOrNull { it.id == server.currentUserId } ?: return jellyfinApi
|
||||||
jellyfinApi.apply {
|
jellyfinApi.apply {
|
||||||
api.baseUrl = server.address
|
api.baseUrl = serverAddress.address
|
||||||
api.accessToken = server.accessToken
|
api.accessToken = user.accessToken
|
||||||
userId = UUID.fromString(server.userId)
|
userId = user.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import android.os.Bundle
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.database.Server
|
import dev.jdtech.jellyfin.models.Server
|
||||||
import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel
|
import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
package dev.jdtech.jellyfin.database
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
@Entity(tableName = "servers")
|
@Entity(tableName = "servers")
|
||||||
data class Server(
|
data class Server(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
val id: String,
|
val id: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val address: String,
|
val currentServerAddressId: UUID?,
|
||||||
val userId: String,
|
val currentUserId: UUID?,
|
||||||
val userName: String,
|
|
||||||
val accessToken: String,
|
|
||||||
)
|
)
|
|
@ -0,0 +1,26 @@
|
||||||
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "serverAddresses",
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Server::class,
|
||||||
|
parentColumns = arrayOf("id"),
|
||||||
|
childColumns = arrayOf("serverId"),
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class ServerAddress(
|
||||||
|
@PrimaryKey
|
||||||
|
val id: UUID,
|
||||||
|
@ColumnInfo(index = true)
|
||||||
|
val serverId: String,
|
||||||
|
val address: String
|
||||||
|
)
|
|
@ -0,0 +1,19 @@
|
||||||
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class ServerWithAddresses(
|
||||||
|
@Embedded
|
||||||
|
val server: Server,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "id",
|
||||||
|
entityColumn = "serverId"
|
||||||
|
)
|
||||||
|
val addresses: List<ServerAddress>,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "currentUserId",
|
||||||
|
entityColumn = "id"
|
||||||
|
)
|
||||||
|
val user: User?
|
||||||
|
)
|
|
@ -0,0 +1,19 @@
|
||||||
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class ServerWithAddressesAndUsers(
|
||||||
|
@Embedded
|
||||||
|
val server: Server,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "id",
|
||||||
|
entityColumn = "serverId"
|
||||||
|
)
|
||||||
|
val addresses: List<ServerAddress>,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "id",
|
||||||
|
entityColumn = "serverId"
|
||||||
|
)
|
||||||
|
val users: List<User>
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class ServerWithUsers(
|
||||||
|
@Embedded
|
||||||
|
val server: Server,
|
||||||
|
@Relation(
|
||||||
|
parentColumn = "id",
|
||||||
|
entityColumn = "serverId"
|
||||||
|
)
|
||||||
|
val users: List<User>
|
||||||
|
)
|
|
@ -1,8 +1,27 @@
|
||||||
package dev.jdtech.jellyfin.models
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
data class User(
|
@Entity(
|
||||||
val id: UUID,
|
tableName = "users",
|
||||||
val name: String
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Server::class,
|
||||||
|
parentColumns = arrayOf("id"),
|
||||||
|
childColumns = arrayOf("serverId"),
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class User(
|
||||||
|
@PrimaryKey
|
||||||
|
val id: UUID,
|
||||||
|
val name: String,
|
||||||
|
@ColumnInfo(index = true)
|
||||||
|
val serverId: String,
|
||||||
|
val accessToken: String? = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,9 +8,9 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.BaseApplication
|
import dev.jdtech.jellyfin.BaseApplication
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.database.Server
|
|
||||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||||
import dev.jdtech.jellyfin.models.DiscoveredServer
|
import dev.jdtech.jellyfin.models.DiscoveredServer
|
||||||
|
import dev.jdtech.jellyfin.models.Server
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
|
@ -8,9 +8,11 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.BaseApplication
|
import dev.jdtech.jellyfin.BaseApplication
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.database.Server
|
|
||||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||||
|
import dev.jdtech.jellyfin.models.Server
|
||||||
|
import dev.jdtech.jellyfin.models.ServerAddress
|
||||||
import dev.jdtech.jellyfin.models.User
|
import dev.jdtech.jellyfin.models.User
|
||||||
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.Exception
|
import kotlin.Exception
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -60,7 +62,8 @@ constructor(
|
||||||
_usersState.emit(UsersState.Loading)
|
_usersState.emit(UsersState.Loading)
|
||||||
try {
|
try {
|
||||||
val publicUsers by jellyfinApi.userApi.getPublicUsers()
|
val publicUsers by jellyfinApi.userApi.getPublicUsers()
|
||||||
val users = publicUsers.map { User(it.id, it.name.orEmpty()) }
|
val users =
|
||||||
|
publicUsers.map { User(id = it.id, name = it.name.orEmpty(), serverId = it.serverId!!) }
|
||||||
_usersState.emit(UsersState.Users(users))
|
_usersState.emit(UsersState.Users(users))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_usersState.emit(UsersState.Users(emptyList()))
|
_usersState.emit(UsersState.Users(emptyList()))
|
||||||
|
@ -88,16 +91,29 @@ constructor(
|
||||||
|
|
||||||
val serverInfo by jellyfinApi.systemApi.getPublicSystemInfo()
|
val serverInfo by jellyfinApi.systemApi.getPublicSystemInfo()
|
||||||
|
|
||||||
val server = Server(
|
val user = User(
|
||||||
serverInfo.id!!,
|
id = authenticationResult.user!!.id,
|
||||||
serverInfo.serverName!!,
|
name = authenticationResult.user!!.name!!,
|
||||||
jellyfinApi.api.baseUrl!!,
|
serverId = serverInfo.id!!,
|
||||||
authenticationResult.user?.id.toString(),
|
accessToken = authenticationResult.accessToken!!
|
||||||
authenticationResult.user?.name!!,
|
|
||||||
authenticationResult.accessToken!!
|
|
||||||
)
|
)
|
||||||
|
|
||||||
insert(server)
|
val serverAddress = ServerAddress(
|
||||||
|
id = UUID.randomUUID(),
|
||||||
|
serverId = serverInfo.id!!,
|
||||||
|
address = jellyfinApi.api.baseUrl!!
|
||||||
|
)
|
||||||
|
|
||||||
|
val server = Server(
|
||||||
|
id = serverInfo.id!!,
|
||||||
|
name = serverInfo.serverName!!,
|
||||||
|
currentServerAddressId = serverAddress.id,
|
||||||
|
currentUserId = user.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
insertServer(server)
|
||||||
|
insertServerAddress(serverAddress)
|
||||||
|
insertUser(user)
|
||||||
|
|
||||||
val spEdit = sharedPreferences.edit()
|
val spEdit = sharedPreferences.edit()
|
||||||
spEdit.putString("selectedServer", server.id)
|
spEdit.putString("selectedServer", server.id)
|
||||||
|
@ -125,9 +141,21 @@ constructor(
|
||||||
*
|
*
|
||||||
* @param server The server
|
* @param server The server
|
||||||
*/
|
*/
|
||||||
private suspend fun insert(server: Server) {
|
private suspend fun insertServer(server: Server) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
database.insert(server)
|
database.insertServer(server)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun insertServerAddress(address: ServerAddress) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
database.insertServerAddress(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun insertUser(user: User) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
database.insertUser(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,8 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.database.Server
|
|
||||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||||
import java.util.UUID
|
import dev.jdtech.jellyfin.models.Server
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
@ -40,16 +39,20 @@ constructor(
|
||||||
|
|
||||||
fun connectToServer(server: Server) {
|
fun connectToServer(server: Server) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val serverWithAddressesAndUsers = database.getServerWithAddressesAndUsers(server.id)!!
|
||||||
|
val serverAddress = serverWithAddressesAndUsers.addresses.firstOrNull { it.id == server.currentServerAddressId } ?: return@launch
|
||||||
|
val user = serverWithAddressesAndUsers.users.firstOrNull { it.id == server.currentUserId } ?: return@launch
|
||||||
|
|
||||||
|
jellyfinApi.apply {
|
||||||
|
api.baseUrl = serverAddress.address
|
||||||
|
api.accessToken = user.accessToken
|
||||||
|
userId = user.id
|
||||||
|
}
|
||||||
|
|
||||||
val spEdit = sharedPreferences.edit()
|
val spEdit = sharedPreferences.edit()
|
||||||
spEdit.putString("selectedServer", server.id)
|
spEdit.putString("selectedServer", server.id)
|
||||||
spEdit.apply()
|
spEdit.apply()
|
||||||
|
|
||||||
jellyfinApi.apply {
|
|
||||||
api.baseUrl = server.address
|
|
||||||
api.accessToken = server.accessToken
|
|
||||||
userId = UUID.fromString(server.userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
_navigateToMain.emit(true)
|
_navigateToMain.emit(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="server"
|
name="server"
|
||||||
type="dev.jdtech.jellyfin.database.Server" />
|
type="dev.jdtech.jellyfin.models.Server" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|
Loading…
Reference in a new issue