Ajout de la connexion d’un utilisateur au serveur Palto et connexion anonyme hors ligne.y
This commit is contained in:
parent
69d1880295
commit
e81f70f045
17 changed files with 207 additions and 310 deletions
|
@ -1,9 +0,0 @@
|
|||
package com.example.palto
|
||||
|
||||
import android.nfc.Tag
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class PaltoViewModel: ViewModel() {
|
||||
val tagLiveData = MutableLiveData<Tag>()
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package com.example.palto.data.local
|
||||
|
||||
/**
|
||||
* Class that handles authentication w/ login credentials and retrieves user information.
|
||||
*/
|
||||
class LocalDataSource {
|
||||
/*
|
||||
fun login(username: String, password: String): Result<LoggedInUser> {
|
||||
try {
|
||||
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
// TODO: revoke authentication
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -1,10 +1,3 @@
|
|||
package com.example.palto.data.repository
|
||||
|
||||
import com.example.palto.data.network.PaltoApiService
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class AttendanceRepository() {
|
||||
// private val cards
|
||||
}
|
||||
class AttendanceRepository
|
|
@ -1,47 +0,0 @@
|
|||
package com.example.palto.data.repository
|
||||
|
||||
import com.example.palto.data.Result
|
||||
import com.example.palto.data.network.ServerDataSource
|
||||
import com.example.palto.model.LoggedInUser
|
||||
|
||||
/**
|
||||
* 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: ServerDataSource) {
|
||||
|
||||
var user: LoggedInUser? = null
|
||||
private set
|
||||
|
||||
val isLoggedIn: Boolean
|
||||
get() = user != null
|
||||
|
||||
init {
|
||||
user = null
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
user = null
|
||||
dataSource.logout()
|
||||
}
|
||||
|
||||
fun login(
|
||||
hostname: String,
|
||||
username: String,
|
||||
password: String
|
||||
): Result<LoggedInUser> {
|
||||
// handle login
|
||||
val result = dataSource.login(hostname, username, password)
|
||||
|
||||
if (result is Result.Success) {
|
||||
setLoggedInUser(result.data)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun setLoggedInUser(loggedInUser: LoggedInUser) {
|
||||
this.user = loggedInUser
|
||||
}
|
||||
}
|
|
@ -1,10 +1,3 @@
|
|||
package com.example.palto.data.repository
|
||||
|
||||
import com.example.palto.data.network.PaltoApiService
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class SessionRepository(val dataSource: PaltoApiService) {
|
||||
// private val cards
|
||||
}
|
||||
class SessionRepository
|
|
@ -0,0 +1,3 @@
|
|||
package com.example.palto.data.repository
|
||||
|
||||
class UserRepository
|
|
@ -1,14 +1,13 @@
|
|||
package com.example.palto.model
|
||||
|
||||
import java.io.Serializable
|
||||
package com.example.palto.domain
|
||||
|
||||
/**
|
||||
* Data class that captures user information for logged in users retrieved from LoginRepository
|
||||
*/
|
||||
data class LoggedInUser(
|
||||
val id: String,
|
||||
data class User(
|
||||
//Not in the domain layer
|
||||
//val id: String,
|
||||
val username: String,
|
||||
val first_name: String,
|
||||
val last_name: String,
|
||||
val email: String
|
||||
) : Serializable
|
||||
)
|
78
app/src/main/java/com/example/palto/ui/UserViewModel.kt
Normal file
78
app/src/main/java/com/example/palto/ui/UserViewModel.kt
Normal file
|
@ -0,0 +1,78 @@
|
|||
package com.example.palto.ui
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.palto.R
|
||||
import com.example.palto.data.repository.TokenRepository
|
||||
import com.example.palto.data.repository.UserRepository
|
||||
import com.example.palto.domain.User
|
||||
import com.example.palto.ui.login.LoginResult
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class UserViewModel(
|
||||
private val tokenRepository: TokenRepository,
|
||||
private val userRepository: UserRepository
|
||||
): ViewModel() {
|
||||
|
||||
private var _result = MutableLiveData<LoginResult>()
|
||||
val result = _result as LiveData<LoginResult>
|
||||
|
||||
// User is initially set to null to be disconnected.
|
||||
private var _user = MutableLiveData<User?>(null)
|
||||
val user = _user as LiveData<User?>
|
||||
|
||||
fun login(
|
||||
hostname: String,
|
||||
username: String,
|
||||
password: String) {
|
||||
|
||||
// Coroutine runs in background.
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
tokenRepository.authenticate(hostname, username, password)
|
||||
_user.value = User(
|
||||
username,
|
||||
"",
|
||||
"",
|
||||
""
|
||||
)
|
||||
_result.value = LoginResult(success = true)
|
||||
} catch (e: Exception) {
|
||||
Log.e("Palto", "Connection error: " + e.message)
|
||||
_result.value = LoginResult(
|
||||
success = false,
|
||||
error = R.string.login_failed,
|
||||
exception = e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loginAnonymous() {
|
||||
_user.value = User(
|
||||
"anonymous", "", "", ""
|
||||
)
|
||||
_result.value = LoginResult(success = true)
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
_user.value = null
|
||||
_result.value = LoginResult(success = false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return UserViewModel(
|
||||
tokenRepository = TokenRepository(),
|
||||
userRepository = UserRepository()
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.example.palto.ui.login
|
||||
|
||||
/* Est-ce que c’est util ?
|
||||
* Updater la vue dans le fragment
|
||||
*/
|
||||
|
||||
/**
|
||||
* User details post authentication that is exposed to the UI
|
||||
*/
|
||||
data class LoggedInUserView(
|
||||
val displayName: String
|
||||
//... other data fields that may be accessible to the UI
|
||||
)
|
|
@ -1,42 +1,68 @@
|
|||
package com.example.palto.ui.login
|
||||
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.navGraphViewModels
|
||||
import com.example.palto.databinding.FragmentLoginBinding
|
||||
|
||||
import com.example.palto.R
|
||||
import com.example.palto.ui.UserViewModel
|
||||
|
||||
class LoginFragment : Fragment() {
|
||||
|
||||
private val loginViewModel: LoginViewModel by
|
||||
navGraphViewModels(R.id.nav_graph) { LoginViewModelFactory() }
|
||||
// loginViewModel is used to update the login screen dynamically.
|
||||
private val loginViewModel: LoginViewModel by viewModels()
|
||||
|
||||
private var _binding: FragmentLoginBinding? = null
|
||||
// userViewModel is where the user is logged in, at the activity level.
|
||||
private val userViewModel: UserViewModel by activityViewModels() { UserViewModel.Factory }
|
||||
|
||||
// This property is only valid between onCreateView and onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
private lateinit var binding: FragmentLoginBinding
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = FragmentLoginBinding.inflate(inflater, container, false)
|
||||
|
||||
val navController = findNavController()
|
||||
|
||||
// Bind the login button.
|
||||
binding.login.setOnClickListener {
|
||||
binding.loading.visibility = View.VISIBLE
|
||||
userViewModel.login(
|
||||
binding.hostname.text.toString(),
|
||||
binding.username.text.toString(),
|
||||
binding.password.text.toString()
|
||||
)
|
||||
}
|
||||
|
||||
// Bind anonymous login clickable text.
|
||||
binding.loginAnonymous.setOnClickListener {
|
||||
userViewModel.loginAnonymous()
|
||||
}
|
||||
|
||||
// On result of logging.
|
||||
userViewModel.result.observe(viewLifecycleOwner) {
|
||||
binding.loading.visibility = View.GONE
|
||||
if (it.success) {
|
||||
navController.popBackStack()
|
||||
} else if (it.error != null) {
|
||||
binding.loginError.text = "Exception : " + it.exception.toString()
|
||||
Toast.makeText(activity, it.error, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
_binding = FragmentLoginBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
/*
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
@ -46,23 +72,21 @@ class LoginFragment : Fragment() {
|
|||
val loginButton = binding.login
|
||||
val loadingProgressBar = binding.loading
|
||||
|
||||
//
|
||||
loginViewModel.loginFormState.observe(viewLifecycleOwner,
|
||||
Observer { loginFormState ->
|
||||
if (loginFormState == null) {
|
||||
return@Observer
|
||||
}
|
||||
loginButton.isEnabled = loginFormState.isDataValid
|
||||
loginFormState.hostnameError?.let {
|
||||
hostnameEditText.error = getString(it)
|
||||
}
|
||||
loginFormState.usernameError?.let {
|
||||
usernameEditText.error = getString(it)
|
||||
}
|
||||
loginFormState.passwordError?.let {
|
||||
passwordEditText.error = getString(it)
|
||||
}
|
||||
})
|
||||
loginViewModel.loginFormState.observe(viewLifecycleOwner) {
|
||||
if (it == null) {
|
||||
return@Observer
|
||||
}
|
||||
loginButton.isEnabled = it.isDataValid
|
||||
it.hostnameError?.let {
|
||||
hostnameEditText.error = getString(it)
|
||||
}
|
||||
it.usernameError?.let {
|
||||
usernameEditText.error = getString(it)
|
||||
}
|
||||
it.passwordError?.let {
|
||||
passwordEditText.error = getString(it)
|
||||
}
|
||||
}
|
||||
|
||||
loginViewModel.loginResult.observe(viewLifecycleOwner,
|
||||
Observer { loginResult ->
|
||||
|
@ -103,17 +127,8 @@ class LoginFragment : Fragment() {
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
// Damien : Le setOnClickListener est là !
|
||||
loginButton.setOnClickListener {
|
||||
loadingProgressBar.visibility = View.VISIBLE
|
||||
loginViewModel.login(
|
||||
hostnameEditText.text.toString(),
|
||||
usernameEditText.text.toString(),
|
||||
passwordEditText.text.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
private fun updateUiWithUser(model: LoggedInUserView) {
|
||||
|
@ -122,15 +137,11 @@ class LoginFragment : Fragment() {
|
|||
val appContext = context?.applicationContext ?: return
|
||||
Toast.makeText(appContext, welcome, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
*/
|
||||
|
||||
private fun showLoginFailed(@StringRes errorString: Int) {
|
||||
val appContext = context?.applicationContext ?: return
|
||||
Toast.makeText(appContext, errorString, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
package com.example.palto.ui.login
|
||||
|
||||
/**
|
||||
* Authentication result : success (user details) or error message.
|
||||
* Authentication result : success is true if connected or error message with exception.
|
||||
*/
|
||||
data class LoginResult(
|
||||
val success: LoggedInUserView? = null,
|
||||
val error: Int? = null
|
||||
val success: Boolean,
|
||||
val error: Int? = null, // Id of the string resource to display to the user.
|
||||
val exception: Exception? = null
|
||||
)
|
|
@ -1,40 +1,14 @@
|
|||
package com.example.palto.ui.login
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import android.util.Patterns
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.example.palto.data.repository.LoginRepository
|
||||
|
||||
import com.example.palto.R
|
||||
import com.example.palto.data.network.ServerDataSource
|
||||
|
||||
class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() {
|
||||
|
||||
private val _loginForm = MutableLiveData<LoginFormState>()
|
||||
val loginFormState: LiveData<LoginFormState> = _loginForm
|
||||
|
||||
private val _loginResult = MutableLiveData<LoginResult>()
|
||||
val loginResult: LiveData<LoginResult> = _loginResult
|
||||
|
||||
fun login(
|
||||
hostname: String,
|
||||
username: String,
|
||||
password: String) {
|
||||
// can be launched in a separate asynchronous job
|
||||
val result = loginRepository.login(hostname, username, password)
|
||||
|
||||
/*
|
||||
if (result is Result.Success) {
|
||||
_loginResult.value =
|
||||
LoginResult(success = LoggedInUserView(
|
||||
displayName = result.data.displayName))
|
||||
} else {
|
||||
_loginResult.value = LoginResult(error = R.string.login_failed)
|
||||
}
|
||||
*/
|
||||
}
|
||||
/**
|
||||
* View Model to check dynamically the values of the form.
|
||||
*/
|
||||
class LoginViewModel() : ViewModel() {
|
||||
/*
|
||||
private val _loginFormState = MutableLiveData<LoginFormState>()
|
||||
val loginFormState: LiveData<LoginFormState> = _loginFormState
|
||||
|
||||
fun loginDataChanged(
|
||||
hostname: String,
|
||||
|
@ -50,35 +24,18 @@ class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel()
|
|||
_loginForm.value = LoginFormState(isDataValid = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isHostNameValid(hostname: String): Boolean {
|
||||
return hostname.isNotBlank()
|
||||
}
|
||||
|
||||
// A placeholder username validation check
|
||||
private fun isUserNameValid(username: String): Boolean {
|
||||
return if (username.contains("@")) {
|
||||
Patterns.EMAIL_ADDRESS.matcher(username).matches()
|
||||
} else {
|
||||
username.isNotBlank()
|
||||
}
|
||||
return username.isNotBlank()
|
||||
}
|
||||
|
||||
// A placeholder password validation check
|
||||
private fun isPasswordValid(password: String): Boolean {
|
||||
return password.length > 5
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
class LoginViewModelFactory : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(LoginViewModel::class.java)) {
|
||||
return LoginViewModel(
|
||||
loginRepository = LoginRepository(
|
||||
dataSource = ServerDataSource()
|
||||
)
|
||||
) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,8 +27,20 @@ class MenuFragment : Fragment() {
|
|||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
_binding = FragmentMenuListBinding.inflate(inflater, container, false)
|
||||
binding = FragmentMenuListBinding.inflate(inflater, container, false)
|
||||
|
||||
val navController = findNavController()
|
||||
|
||||
// Connect the user.
|
||||
userViewModel.user.observe(viewLifecycleOwner) {
|
||||
if (it != null) {
|
||||
// Get sessions of the user from remote.
|
||||
} else {
|
||||
navController.navigate(R.id.loginFragment)
|
||||
}
|
||||
}
|
||||
|
||||
// Display the list of sessions.
|
||||
val adapter = MenuAdapter()
|
||||
binding.menuList.adapter = adapter
|
||||
menuViewModel.sessions.observe(viewLifecycleOwner) {
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
package com.example.palto.ui.sessionList.placeholder
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
|
||||
/**
|
||||
* Helper class for providing sample content for user interfaces created by
|
||||
* Android template wizards.
|
||||
*
|
||||
* TODO: Replace all uses of this class before publishing your app.
|
||||
*/
|
||||
object PlaceholderContent {
|
||||
|
||||
/**
|
||||
* An array of sample (placeholder) items.
|
||||
*/
|
||||
val ITEMS: MutableList<PlaceholderItem> = ArrayList()
|
||||
|
||||
/**
|
||||
* A map of sample (placeholder) items, by ID.
|
||||
*/
|
||||
val ITEM_MAP: MutableMap<String, PlaceholderItem> = HashMap()
|
||||
|
||||
private val COUNT = 25
|
||||
|
||||
init {
|
||||
// Add some sample items.
|
||||
for (i in 1..COUNT) {
|
||||
addItem(createPlaceholderItem(i))
|
||||
}
|
||||
}
|
||||
|
||||
private fun addItem(item: PlaceholderItem) {
|
||||
ITEMS.add(item)
|
||||
ITEM_MAP.put(item.id, item)
|
||||
}
|
||||
|
||||
private fun createPlaceholderItem(position: Int): PlaceholderItem {
|
||||
return PlaceholderItem(position.toString(), "Item " + position, makeDetails(position))
|
||||
}
|
||||
|
||||
private fun makeDetails(position: Int): String {
|
||||
val builder = StringBuilder()
|
||||
builder.append("Details about Item: ").append(position)
|
||||
for (i in 0..position - 1) {
|
||||
builder.append("\nMore details information here.")
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder item representing a piece of content.
|
||||
*/
|
||||
data class PlaceholderItem(val id: String, val content: String, val details: String) {
|
||||
override fun toString(): String = content
|
||||
}
|
||||
}
|
|
@ -13,11 +13,8 @@
|
|||
|
||||
<EditText
|
||||
android:id="@+id/hostname"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="331dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:autofillHints="@string/prompt_hostname"
|
||||
android:hint="@string/prompt_hostname"
|
||||
android:inputType="text"
|
||||
|
@ -30,10 +27,8 @@
|
|||
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="331dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:autofillHints="@string/prompt_username"
|
||||
android:hint="@string/prompt_username"
|
||||
android:inputType="text"
|
||||
|
@ -46,29 +41,28 @@
|
|||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_width="331dp"
|
||||
android:layout_height="50dp"
|
||||
android:autofillHints="@string/prompt_password"
|
||||
android:hint="@string/prompt_password"
|
||||
android:imeActionLabel="@string/action_sign_in_short"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textPassword"
|
||||
android:selectAllOnFocus="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/help_message"
|
||||
app:layout_constraintBottom_toTopOf="@+id/login_error"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/username" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/help_message"
|
||||
android:id="@+id/login_error"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/help_message"
|
||||
android:text=""
|
||||
app:layout_constraintBottom_toTopOf="@+id/login"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/password" />
|
||||
|
||||
|
@ -77,33 +71,38 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginStart="48dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:layout_marginBottom="64dp"
|
||||
android:enabled="false"
|
||||
android:enabled="true"
|
||||
android:text="@string/action_sign_in"
|
||||
app:layout_constraintBottom_toTopOf="@+id/login_anonymous"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/login_error" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loading"
|
||||
android:layout_width="39dp"
|
||||
android:layout_height="47dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/help_message"
|
||||
app:layout_constraintVertical_bias="0.2" />
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.5" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loading"
|
||||
<TextView
|
||||
android:id="@+id/login_anonymous"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="64dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="64dp"
|
||||
android:visibility="gone"
|
||||
android:clickable="true"
|
||||
android:text="@string/login_anonymous_text"
|
||||
android:textColor="#F4511E"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/password"
|
||||
app:layout_constraintStart_toStartOf="@+id/password"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.3" />
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/login" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -5,6 +5,7 @@
|
|||
<string name="prompt_username">Nom d’utilisateur</string>
|
||||
<string name="prompt_password">Mot de passe</string>
|
||||
<string name="action_sign_in">Connexion</string>
|
||||
<string name="login_anonymous_text">Connexion anonyme</string>
|
||||
<string name="action_sign_in_short">Sign in</string>
|
||||
<string name="welcome">"Bienvenue !"</string>
|
||||
<string name="invalid_hostname">Serveur inaccessible</string>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.0" apply false
|
||||
id("com.android.application") version "8.2.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
|
||||
}
|
Loading…
Reference in a new issue