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.ViewItemListAdapter
|
||||
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 java.util.UUID
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
|
|
|
@ -5,8 +5,8 @@ import android.view.ViewGroup
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dev.jdtech.jellyfin.database.Server
|
||||
import dev.jdtech.jellyfin.databinding.ServerItemBinding
|
||||
import dev.jdtech.jellyfin.models.Server
|
||||
|
||||
class ServerGridAdapter(
|
||||
private val onClickListener: OnClickListener,
|
||||
|
|
|
@ -2,8 +2,13 @@ package dev.jdtech.jellyfin.database
|
|||
|
||||
import androidx.room.Database
|
||||
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 val serverDatabaseDao: ServerDatabaseDao
|
||||
}
|
||||
|
|
|
@ -5,12 +5,25 @@ import androidx.room.Dao
|
|||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
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
|
||||
interface ServerDatabaseDao {
|
||||
@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
|
||||
fun update(server: Server)
|
||||
|
@ -18,6 +31,21 @@ interface ServerDatabaseDao {
|
|||
@Query("select * from servers where id = :id")
|
||||
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")
|
||||
fun clear()
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import dagger.hilt.components.SingletonComponent
|
||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||
import java.util.UUID
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
|
@ -26,11 +25,14 @@ object ApiModule {
|
|||
|
||||
val serverId = sharedPreferences.getString("selectedServer", 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 {
|
||||
api.baseUrl = server.address
|
||||
api.accessToken = server.accessToken
|
||||
userId = UUID.fromString(server.userId)
|
||||
api.baseUrl = serverAddress.address
|
||||
api.accessToken = user.accessToken
|
||||
userId = user.id
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.os.Bundle
|
|||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dev.jdtech.jellyfin.R
|
||||
import dev.jdtech.jellyfin.database.Server
|
||||
import dev.jdtech.jellyfin.models.Server
|
||||
import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel
|
||||
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.PrimaryKey
|
||||
import java.util.UUID
|
||||
|
||||
@Entity(tableName = "servers")
|
||||
data class Server(
|
||||
@PrimaryKey
|
||||
val id: String,
|
||||
val name: String,
|
||||
val address: String,
|
||||
val userId: String,
|
||||
val userName: String,
|
||||
val accessToken: String,
|
||||
val currentServerAddressId: UUID?,
|
||||
val currentUserId: UUID?,
|
||||
)
|
|
@ -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
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import java.util.UUID
|
||||
|
||||
data class User(
|
||||
val id: UUID,
|
||||
val name: String
|
||||
@Entity(
|
||||
tableName = "users",
|
||||
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.R
|
||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||
import dev.jdtech.jellyfin.database.Server
|
||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||
import dev.jdtech.jellyfin.models.DiscoveredServer
|
||||
import dev.jdtech.jellyfin.models.Server
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
|
@ -8,9 +8,11 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import dev.jdtech.jellyfin.BaseApplication
|
||||
import dev.jdtech.jellyfin.R
|
||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||
import dev.jdtech.jellyfin.database.Server
|
||||
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 java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import kotlin.Exception
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -60,7 +62,8 @@ constructor(
|
|||
_usersState.emit(UsersState.Loading)
|
||||
try {
|
||||
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))
|
||||
} catch (e: Exception) {
|
||||
_usersState.emit(UsersState.Users(emptyList()))
|
||||
|
@ -88,16 +91,29 @@ constructor(
|
|||
|
||||
val serverInfo by jellyfinApi.systemApi.getPublicSystemInfo()
|
||||
|
||||
val server = Server(
|
||||
serverInfo.id!!,
|
||||
serverInfo.serverName!!,
|
||||
jellyfinApi.api.baseUrl!!,
|
||||
authenticationResult.user?.id.toString(),
|
||||
authenticationResult.user?.name!!,
|
||||
authenticationResult.accessToken!!
|
||||
val user = User(
|
||||
id = authenticationResult.user!!.id,
|
||||
name = authenticationResult.user!!.name!!,
|
||||
serverId = serverInfo.id!!,
|
||||
accessToken = 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()
|
||||
spEdit.putString("selectedServer", server.id)
|
||||
|
@ -125,9 +141,21 @@ constructor(
|
|||
*
|
||||
* @param server The server
|
||||
*/
|
||||
private suspend fun insert(server: Server) {
|
||||
private suspend fun insertServer(server: Server) {
|
||||
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 dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||
import dev.jdtech.jellyfin.database.Server
|
||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||
import java.util.UUID
|
||||
import dev.jdtech.jellyfin.models.Server
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
@ -40,16 +39,20 @@ constructor(
|
|||
|
||||
fun connectToServer(server: Server) {
|
||||
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()
|
||||
spEdit.putString("selectedServer", server.id)
|
||||
spEdit.apply()
|
||||
|
||||
jellyfinApi.apply {
|
||||
api.baseUrl = server.address
|
||||
api.accessToken = server.accessToken
|
||||
userId = UUID.fromString(server.userId)
|
||||
}
|
||||
|
||||
_navigateToMain.emit(true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<variable
|
||||
name="server"
|
||||
type="dev.jdtech.jellyfin.database.Server" />
|
||||
type="dev.jdtech.jellyfin.models.Server" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
|
Loading…
Reference in a new issue