diff --git a/app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt b/app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt index 0a7c6e61..50d245d6 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt @@ -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 diff --git a/app/src/main/java/dev/jdtech/jellyfin/adapters/ServerGridAdapter.kt b/app/src/main/java/dev/jdtech/jellyfin/adapters/ServerGridAdapter.kt index 760be617..4065d086 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/adapters/ServerGridAdapter.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/adapters/ServerGridAdapter.kt @@ -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, diff --git a/app/src/main/java/dev/jdtech/jellyfin/database/ServerDatabase.kt b/app/src/main/java/dev/jdtech/jellyfin/database/ServerDatabase.kt index e10178a4..c1671903 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/database/ServerDatabase.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/database/ServerDatabase.kt @@ -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 } diff --git a/app/src/main/java/dev/jdtech/jellyfin/database/ServerDatabaseDao.kt b/app/src/main/java/dev/jdtech/jellyfin/database/ServerDatabaseDao.kt index b060ae3d..5acf8620 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/database/ServerDatabaseDao.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/database/ServerDatabaseDao.kt @@ -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() diff --git a/app/src/main/java/dev/jdtech/jellyfin/di/ApiModule.kt b/app/src/main/java/dev/jdtech/jellyfin/di/ApiModule.kt index bda9b2ca..f086ac6a 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/di/ApiModule.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/di/ApiModule.kt @@ -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 } } diff --git a/app/src/main/java/dev/jdtech/jellyfin/dialogs/DeleteServerDialogFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/dialogs/DeleteServerDialogFragment.kt index 683bbfdb..0b93daa2 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/dialogs/DeleteServerDialogFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/dialogs/DeleteServerDialogFragment.kt @@ -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 diff --git a/app/src/main/java/dev/jdtech/jellyfin/database/Server.kt b/app/src/main/java/dev/jdtech/jellyfin/models/Server.kt similarity index 54% rename from app/src/main/java/dev/jdtech/jellyfin/database/Server.kt rename to app/src/main/java/dev/jdtech/jellyfin/models/Server.kt index d5d33751..d7232bca 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/database/Server.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/models/Server.kt @@ -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?, ) diff --git a/app/src/main/java/dev/jdtech/jellyfin/models/ServerAddress.kt b/app/src/main/java/dev/jdtech/jellyfin/models/ServerAddress.kt new file mode 100644 index 00000000..2049ee0d --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/models/ServerAddress.kt @@ -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 +) diff --git a/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithAddresses.kt b/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithAddresses.kt new file mode 100644 index 00000000..1b74539e --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithAddresses.kt @@ -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, + @Relation( + parentColumn = "currentUserId", + entityColumn = "id" + ) + val user: User? +) diff --git a/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithAddressesAndUsers.kt b/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithAddressesAndUsers.kt new file mode 100644 index 00000000..90561764 --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithAddressesAndUsers.kt @@ -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, + @Relation( + parentColumn = "id", + entityColumn = "serverId" + ) + val users: List +) diff --git a/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithUsers.kt b/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithUsers.kt new file mode 100644 index 00000000..b2e2ce73 --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/models/ServerWithUsers.kt @@ -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 +) diff --git a/app/src/main/java/dev/jdtech/jellyfin/models/User.kt b/app/src/main/java/dev/jdtech/jellyfin/models/User.kt index e9c8d480..1ba96d24 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/models/User.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/models/User.kt @@ -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 ) diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt index f4c61043..723cdd84 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt @@ -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 diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt index 024a7634..fd71897c 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt @@ -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) } } } diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerSelectViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerSelectViewModel.kt index b29e9933..52817c01 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerSelectViewModel.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerSelectViewModel.kt @@ -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) } } diff --git a/app/src/main/res/layout/server_item.xml b/app/src/main/res/layout/server_item.xml index 9ad427b6..c7b0f078 100644 --- a/app/src/main/res/layout/server_item.xml +++ b/app/src/main/res/layout/server_item.xml @@ -7,7 +7,7 @@ + type="dev.jdtech.jellyfin.models.Server" />