From a911de59b15affed39d508f8826a1e6bf0771fe4 Mon Sep 17 00:00:00 2001 From: biloute02 Date: Mon, 18 Dec 2023 22:46:26 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20de=20l=E2=80=99hostname=20et=20d?= =?UTF-8?q?=E2=80=99un=20r=C3=A9pository=20pour=20le=20Token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LocalDataSource.kt} | 9 ++-- .../example/palto/data/model/LoggedInUser.kt | 11 +++-- .../com/example/palto/data/model/Tokens.kt | 10 ++++ .../palto/data/network/ServerDataSource.kt | 47 +++++++++++++++++++ .../data/{ => repository}/LoginRepository.kt | 18 ++++--- .../palto/data/repository/TokensRepository.kt | 45 ++++++++++++++++++ .../example/palto/ui/login/LoginFormState.kt | 1 + .../example/palto/ui/login/LoginFragment.kt | 16 ++++--- .../example/palto/ui/login/LoginViewModel.kt | 20 ++++++-- .../palto/ui/login/LoginViewModelFactory.kt | 6 +-- .../palto/ui/sheet/SheetDetailFragment.kt | 32 +++++++++++++ .../palto/ui/sheet/SheetDetailViewModel.kt | 7 +++ app/src/main/res/layout/fragment_login.xml | 47 +++++++++++++++---- .../main/res/layout/fragment_sheet_detail.xml | 13 +++++ app/src/main/res/navigation/nav_graph.xml | 11 +++-- app/src/main/res/values/strings.xml | 16 ++++--- 16 files changed, 264 insertions(+), 45 deletions(-) rename app/src/main/java/com/example/palto/data/{LoginDataSource.kt => local/LocalDataSource.kt} (84%) create mode 100644 app/src/main/java/com/example/palto/data/model/Tokens.kt create mode 100644 app/src/main/java/com/example/palto/data/network/ServerDataSource.kt rename app/src/main/java/com/example/palto/data/{ => repository}/LoginRepository.kt (68%) create mode 100644 app/src/main/java/com/example/palto/data/repository/TokensRepository.kt create mode 100644 app/src/main/java/com/example/palto/ui/sheet/SheetDetailFragment.kt create mode 100644 app/src/main/java/com/example/palto/ui/sheet/SheetDetailViewModel.kt create mode 100644 app/src/main/res/layout/fragment_sheet_detail.xml diff --git a/app/src/main/java/com/example/palto/data/LoginDataSource.kt b/app/src/main/java/com/example/palto/data/local/LocalDataSource.kt similarity index 84% rename from app/src/main/java/com/example/palto/data/LoginDataSource.kt rename to app/src/main/java/com/example/palto/data/local/LocalDataSource.kt index 450e4c0..11d6613 100644 --- a/app/src/main/java/com/example/palto/data/LoginDataSource.kt +++ b/app/src/main/java/com/example/palto/data/local/LocalDataSource.kt @@ -1,18 +1,21 @@ -package com.example.palto.data +package com.example.palto.data.local +import com.example.palto.data.Result import com.example.palto.data.model.LoggedInUser import java.io.IOException /** * Class that handles authentication w/ login credentials and retrieves user information. */ -class LoginDataSource { +class LocalDataSource { fun login(username: String, password: String): Result { try { - // TODO: handle loggedInUser authentication + + val fakeUser = LoggedInUser(java.util.UUID.randomUUID().toString(), "Jane Doe") return Result.Success(fakeUser) + } catch (e: Throwable) { return Result.Error(IOException("Error logging in", e)) } diff --git a/app/src/main/java/com/example/palto/data/model/LoggedInUser.kt b/app/src/main/java/com/example/palto/data/model/LoggedInUser.kt index b9e4156..ff8cbee 100644 --- a/app/src/main/java/com/example/palto/data/model/LoggedInUser.kt +++ b/app/src/main/java/com/example/palto/data/model/LoggedInUser.kt @@ -1,9 +1,14 @@ package com.example.palto.data.model +import java.io.Serializable + /** * Data class that captures user information for logged in users retrieved from LoginRepository */ data class LoggedInUser( - val userId: String, - val displayName: String -) \ No newline at end of file + val id: String, + val username: String, + val first_name: String, + val last_name: String, + val email: String +) : Serializable diff --git a/app/src/main/java/com/example/palto/data/model/Tokens.kt b/app/src/main/java/com/example/palto/data/model/Tokens.kt new file mode 100644 index 0000000..6fffdeb --- /dev/null +++ b/app/src/main/java/com/example/palto/data/model/Tokens.kt @@ -0,0 +1,10 @@ +package com.example.palto.data.model +import java.io.Serializable + +/** + * Data class that captures tokens for logged in users retrieved from LoginRepository + */ +data class Tokens( + val refresh: String, + val access: String +) : Serializable \ No newline at end of file diff --git a/app/src/main/java/com/example/palto/data/network/ServerDataSource.kt b/app/src/main/java/com/example/palto/data/network/ServerDataSource.kt new file mode 100644 index 0000000..08c3e7a --- /dev/null +++ b/app/src/main/java/com/example/palto/data/network/ServerDataSource.kt @@ -0,0 +1,47 @@ +package com.example.palto.data.network + +import com.example.palto.data.Result +import com.example.palto.data.model.LoggedInUser +import com.example.palto.data.model.Tokens +import java.io.IOException + +/** + * Class that handles API calls. + */ +class ServerDataSource { + + private var hostname: String? = null + + fun requestToken(hostname: String, username: String, password: String): Result { + try { + val tokens = Tokens() + return Result.Success() + } catch () { + return Result.Error() + } + } + + fun refreshToken(): Result { + + } + + fun verifyToken(): Boolean { + + } + + fun login(hostname: String, username: String, password: String): Result { + try { + /* + val fakeUser = LoggedInUser(java.util.UUID.randomUUID().toString(), "Jane Doe") + return Result.Success(fakeUser) + */ + return + } catch (e: Throwable) { + return Result.Error(IOException("Error logging in", e)) + } + } + + fun logout() { + // TODO: revoke authentication + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/palto/data/LoginRepository.kt b/app/src/main/java/com/example/palto/data/repository/LoginRepository.kt similarity index 68% rename from app/src/main/java/com/example/palto/data/LoginRepository.kt rename to app/src/main/java/com/example/palto/data/repository/LoginRepository.kt index 2c819bf..73c1df2 100644 --- a/app/src/main/java/com/example/palto/data/LoginRepository.kt +++ b/app/src/main/java/com/example/palto/data/repository/LoginRepository.kt @@ -1,15 +1,17 @@ -package com.example.palto.data +package com.example.palto.data.repository +import com.example.palto.data.Result +import com.example.palto.data.network.ServerDataSource import com.example.palto.data.model.LoggedInUser +import com.example.palto.data.model.Tokens /** * Class that requests authentication and user information from the remote data source and * maintains an in-memory cache of login status and user credentials information. */ -class LoginRepository(val dataSource: LoginDataSource) { +class LoginRepository(val dataSource: ServerDataSource) { - // in-memory cache of the loggedInUser object var user: LoggedInUser? = null private set @@ -27,12 +29,16 @@ class LoginRepository(val dataSource: LoginDataSource) { dataSource.logout() } - fun login(username: String, password: String): Result { + fun login( + hostname: String, + username: String, + password: String + ): Result { // handle login - val result = dataSource.login(username, password) + val result = dataSource.login(hostname, username, password) if (result is Result.Success) { - setLoggedInUser(result.data) + setTokens(result.data) } return result diff --git a/app/src/main/java/com/example/palto/data/repository/TokensRepository.kt b/app/src/main/java/com/example/palto/data/repository/TokensRepository.kt new file mode 100644 index 0000000..0351fc0 --- /dev/null +++ b/app/src/main/java/com/example/palto/data/repository/TokensRepository.kt @@ -0,0 +1,45 @@ +package com.example.palto.data.repository + +import com.example.palto.data.Result +import com.example.palto.data.network.ServerDataSource +import com.example.palto.data.model.LoggedInUser +import com.example.palto.data.model.Tokens + +/** + * Class that requests authentication and user information from the remote data source and + * maintains an in-memory cache of login status and user credentials information. + */ + +class TokensRepository(val dataSource: ServerDataSource) { + + var tokens: Tokens? = null + private set + + /* + val isLoggedIn: Boolean + get() = user != null + */ + + init { + // If user credentials will be cached in local storage, it is recommended it be encrypted + // @see https://developer.android.com/training/articles/keystore + tokens = null + } + + /* + fun login(username: String, password: String): Result { + // handle login + val result = dataSource.login(username, password) + + if (result is Result.Success) { + setLoggedInUser(result.data) + } + + return result + } + */ + + private fun setTokens(tokens: Tokens) { + this.tokens = tokens + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/palto/ui/login/LoginFormState.kt b/app/src/main/java/com/example/palto/ui/login/LoginFormState.kt index ffcaeac..8edce28 100644 --- a/app/src/main/java/com/example/palto/ui/login/LoginFormState.kt +++ b/app/src/main/java/com/example/palto/ui/login/LoginFormState.kt @@ -4,6 +4,7 @@ package com.example.palto.ui.login * Data validation state of the login form. */ data class LoginFormState( + val hostnameError: Int? = null, val usernameError: Int? = null, val passwordError: Int? = null, val isDataValid: Boolean = false diff --git a/app/src/main/java/com/example/palto/ui/login/LoginFragment.kt b/app/src/main/java/com/example/palto/ui/login/LoginFragment.kt index 4f83f09..5725487 100644 --- a/app/src/main/java/com/example/palto/ui/login/LoginFragment.kt +++ b/app/src/main/java/com/example/palto/ui/login/LoginFragment.kt @@ -45,6 +45,7 @@ class LoginFragment : Fragment() { loginViewModel = ViewModelProvider(this, LoginViewModelFactory()) .get(LoginViewModel::class.java) + val hostnameEditText = binding.hostname val usernameEditText = binding.username val passwordEditText = binding.password val loginButton = binding.login @@ -57,6 +58,9 @@ class LoginFragment : Fragment() { return@Observer } loginButton.isEnabled = loginFormState.isDataValid + loginFormState.hostnameError?.let { + hostnameEditText.error = getString(it) + } loginFormState.usernameError?.let { usernameEditText.error = getString(it) } @@ -79,26 +83,25 @@ class LoginFragment : Fragment() { }) val afterTextChangedListener = object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // ignore - } + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { } - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - // ignore - } + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { } override fun afterTextChanged(s: Editable) { loginViewModel.loginDataChanged( + hostnameEditText.text.toString(), usernameEditText.text.toString(), passwordEditText.text.toString() ) } } + hostnameEditText.addTextChangedListener(afterTextChangedListener) usernameEditText.addTextChangedListener(afterTextChangedListener) passwordEditText.addTextChangedListener(afterTextChangedListener) passwordEditText.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) { loginViewModel.login( + hostnameEditText.text.toString(), usernameEditText.text.toString(), passwordEditText.text.toString() ) @@ -110,6 +113,7 @@ class LoginFragment : Fragment() { loginButton.setOnClickListener { loadingProgressBar.visibility = View.VISIBLE loginViewModel.login( + hostnameEditText.text.toString(), usernameEditText.text.toString(), passwordEditText.text.toString() ) diff --git a/app/src/main/java/com/example/palto/ui/login/LoginViewModel.kt b/app/src/main/java/com/example/palto/ui/login/LoginViewModel.kt index aef57fa..0fa642e 100644 --- a/app/src/main/java/com/example/palto/ui/login/LoginViewModel.kt +++ b/app/src/main/java/com/example/palto/ui/login/LoginViewModel.kt @@ -4,7 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import android.util.Patterns -import com.example.palto.data.LoginRepository +import com.example.palto.data.repository.LoginRepository import com.example.palto.data.Result import com.example.palto.R @@ -17,7 +17,10 @@ class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() private val _loginResult = MutableLiveData() val loginResult: LiveData = _loginResult - fun login(username: String, password: String) { + fun login( + hostname: String, + username: String, + password: String) { // can be launched in a separate asynchronous job val result = loginRepository.login(username, password) @@ -29,8 +32,13 @@ class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() } } - fun loginDataChanged(username: String, password: String) { - if (!isUserNameValid(username)) { + fun loginDataChanged( + hostname: String, + username: String, + password: String) { + if (!isHostNameValid(hostname)) { + _loginForm.value = LoginFormState(hostnameError = R.string.invalid_hostname) + } else if (!isUserNameValid(username)) { _loginForm.value = LoginFormState(usernameError = R.string.invalid_username) } else if (!isPasswordValid(password)) { _loginForm.value = LoginFormState(passwordError = R.string.invalid_password) @@ -39,6 +47,10 @@ class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() } } + private fun isHostNameValid(hostname: String): Boolean { + return hostname.isNotBlank() + } + // A placeholder username validation check private fun isUserNameValid(username: String): Boolean { return if (username.contains("@")) { diff --git a/app/src/main/java/com/example/palto/ui/login/LoginViewModelFactory.kt b/app/src/main/java/com/example/palto/ui/login/LoginViewModelFactory.kt index cc18366..f418203 100644 --- a/app/src/main/java/com/example/palto/ui/login/LoginViewModelFactory.kt +++ b/app/src/main/java/com/example/palto/ui/login/LoginViewModelFactory.kt @@ -2,8 +2,8 @@ package com.example.palto.ui.login import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import com.example.palto.data.LoginDataSource -import com.example.palto.data.LoginRepository +import com.example.palto.data.network.ServerDataSource +import com.example.palto.data.repository.LoginRepository /** * ViewModel provider factory to instantiate LoginViewModel. @@ -16,7 +16,7 @@ class LoginViewModelFactory : ViewModelProvider.Factory { if (modelClass.isAssignableFrom(LoginViewModel::class.java)) { return LoginViewModel( loginRepository = LoginRepository( - dataSource = LoginDataSource() + dataSource = ServerDataSource() ) ) as T } diff --git a/app/src/main/java/com/example/palto/ui/sheet/SheetDetailFragment.kt b/app/src/main/java/com/example/palto/ui/sheet/SheetDetailFragment.kt new file mode 100644 index 0000000..e8a98c2 --- /dev/null +++ b/app/src/main/java/com/example/palto/ui/sheet/SheetDetailFragment.kt @@ -0,0 +1,32 @@ +package com.example.palto.ui.sheet + +import androidx.lifecycle.ViewModelProvider +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.palto.R + +class SheetDetailFragment : Fragment() { + + companion object { + fun newInstance() = SheetDetailFragment() + } + + private lateinit var viewModel: SheetDetailViewModel + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_sheet_detail, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + viewModel = ViewModelProvider(this).get(SheetDetailViewModel::class.java) + // TODO: Use the ViewModel + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/palto/ui/sheet/SheetDetailViewModel.kt b/app/src/main/java/com/example/palto/ui/sheet/SheetDetailViewModel.kt new file mode 100644 index 0000000..d32a102 --- /dev/null +++ b/app/src/main/java/com/example/palto/ui/sheet/SheetDetailViewModel.kt @@ -0,0 +1,7 @@ +package com.example.palto.ui.sheet + +import androidx.lifecycle.ViewModel + +class SheetDetailViewModel : ViewModel() { + // TODO: Implement the ViewModel +} \ 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 5b9ac0b..8b8c0bb 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -11,27 +11,44 @@ android:paddingBottom="@dimen/fragment_vertical_margin" tools:context=".ui.login.LoginFragment"> + + + app:layout_constraintTop_toBottomOf="@+id/hostname" /> + +