diff --git a/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt b/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt index 055d72ed..f7e1a307 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt @@ -6,6 +6,7 @@ import dev.jdtech.jellyfin.BuildConfig import org.jellyfin.sdk.Jellyfin import org.jellyfin.sdk.android import org.jellyfin.sdk.api.operations.SystemApi +import org.jellyfin.sdk.api.operations.UserApi import org.jellyfin.sdk.model.ClientInfo class JellyfinApi(context: Context, baseUrl: String) { @@ -16,6 +17,7 @@ class JellyfinApi(context: Context, baseUrl: String) { } val api = jellyfin.createApi(baseUrl = baseUrl) val systemApi = SystemApi(api) + val userApi = UserApi(api) init { Log.i("JellyfinApi", "Constructor called!") diff --git a/app/src/main/java/dev/jdtech/jellyfin/fragments/AddServerFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/fragments/AddServerFragment.kt index 84e196c4..3ea9037a 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/fragments/AddServerFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/fragments/AddServerFragment.kt @@ -26,7 +26,7 @@ class AddServerFragment : Fragment() { binding.lifecycleOwner = this binding.viewModel = viewModel - binding.buttonConnect.setOnClickListener { v: View -> + binding.buttonConnect.setOnClickListener { val serverAddress = binding.editTextServerAddress.text.toString() if (serverAddress.isNotBlank()) { viewModel.checkServer(serverAddress) diff --git a/app/src/main/java/dev/jdtech/jellyfin/fragments/LoginFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/fragments/LoginFragment.kt index 4e70abdd..0c95e310 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/fragments/LoginFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/fragments/LoginFragment.kt @@ -5,13 +5,36 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import dev.jdtech.jellyfin.R +import androidx.lifecycle.ViewModelProvider +import dev.jdtech.jellyfin.databinding.FragmentLoginBinding +import dev.jdtech.jellyfin.viewmodels.LoginViewModel +import dev.jdtech.jellyfin.viewmodels.LoginViewModelFactory class LoginFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_login, container, false) + ): View { + val application = requireNotNull(this.activity).application + val binding = FragmentLoginBinding.inflate(inflater) + val viewModelFactory = LoginViewModelFactory(application) + val viewModel = ViewModelProvider(this, viewModelFactory).get(LoginViewModel::class.java) + binding.lifecycleOwner = this + binding.viewModel = viewModel + + binding.buttonLogin.setOnClickListener { + val username = binding.editTextUsername.text.toString() + val password = binding.editTextPassword.text.toString() + + binding.progressCircular.visibility = View.VISIBLE + viewModel.login(username, password) + } + + viewModel.error.observe(viewLifecycleOwner, { + binding.progressCircular.visibility = View.GONE + binding.editTextUsername.error = it + }) + + return binding.root } } \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/fragments/ServerSelectFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/fragments/ServerSelectFragment.kt index ccfd8bec..098a4a1a 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/fragments/ServerSelectFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/fragments/ServerSelectFragment.kt @@ -34,7 +34,7 @@ class ServerSelectFragment : Fragment() { binding.lifecycleOwner = this binding.viewModel = viewModel binding.serversRecyclerView.adapter = ServerGridAdapter(ServerGridAdapter.OnClickListener { server -> - Toast.makeText(application, "You selected server ${server.name}", Toast.LENGTH_SHORT).show() + Toast.makeText(application, "You selected server $server", Toast.LENGTH_SHORT).show() }, ServerGridAdapter.OnLongClickListener { server -> DeleteServerDialogFragment(viewModel, server).show(parentFragmentManager, "deleteServer") true 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 e2f3cd00..eb4d5b28 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt @@ -28,7 +28,7 @@ class AddServerViewModel(val application: Application) : ViewModel() { _error.value = null _navigateToLogin.value = true } catch (e: Exception) { - Log.e("JellyfinApi", "${e.message}") + Log.e("AddServerViewModel", "${e.message}") _error.value = e.message _navigateToLogin.value = false } diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt new file mode 100644 index 00000000..f5638279 --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt @@ -0,0 +1,51 @@ +package dev.jdtech.jellyfin.viewmodels + +import android.app.Application +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dev.jdtech.jellyfin.api.JellyfinApi +import dev.jdtech.jellyfin.database.Server +import dev.jdtech.jellyfin.database.ServerDatabase +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.jellyfin.sdk.api.client.exception.ApiClientException +import org.jellyfin.sdk.model.api.AuthenticateUserByName +import java.lang.Exception + +class LoginViewModel(application: Application) : ViewModel() { + private val jellyfinApi = JellyfinApi.getInstance(application, "") + private val database = ServerDatabase.getInstance(application).serverDatabaseDao + + private val _error = MutableLiveData() + val error: LiveData + get() = _error + + fun login(username: String, password: String) { + viewModelScope.launch { + try { + val authenticationResult by jellyfinApi.userApi.authenticateUserByName( + data = AuthenticateUserByName( + username = username, + pw = password) + ) + _error.value = null + 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!!) + insert(server) + } catch (e: Exception) { + Log.e("LoginViewModel", "${e.message}") + _error.value = e.message + } + } + } + + private suspend fun insert(server: Server) { + withContext(Dispatchers.IO) { + database.insert(server) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModelFactory.kt b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModelFactory.kt new file mode 100644 index 00000000..c8beead0 --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModelFactory.kt @@ -0,0 +1,18 @@ +package dev.jdtech.jellyfin.viewmodels + +import android.app.Application +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import java.lang.IllegalArgumentException + +class LoginViewModelFactory( + private val application: Application +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(LoginViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return LoginViewModel(application) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index 6cfdc35a..a43d4700 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -1,75 +1,102 @@ - + xmlns:tools="http://schemas.android.com/tools"> - + - + + + + android:layout_height="match_parent" + tools:context=".fragments.LoginFragment"> - + - + android:layout_marginStart="24dp" + android:layout_marginEnd="24dp" + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/image_banner" + app:layout_constraintVertical_bias="0.36"> - + -