Merge remote-tracking branch 'origin/master'

This commit is contained in:
Serafim_Pankin 2025-02-20 15:14:42 +03:00
commit 0ffd6b0ee6
14 changed files with 228 additions and 195 deletions

View File

@ -28,6 +28,7 @@ class MainActivity : AppCompatActivity() {
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment
?: throw IllegalStateException("NavHostFragment is null")
@ -38,6 +39,7 @@ class MainActivity : AppCompatActivity() {
)
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)
NavigationUI.setupWithNavController(navView, navController)
supportActionBar!!.setDisplayHomeAsUpEnabled(false)
navController.addOnDestinationChangedListener { _, destination, _ ->
Log.d("Navigate", "Navigate to " + destination.label)
@ -45,8 +47,8 @@ class MainActivity : AppCompatActivity() {
val userDTO = getCachedUser()
if (userDTO != null) {
if (!userDTO.roles.any { it.name == "ROLE_ADMIN" }) navView.menu.findItem(R.id.nav_admin)
.setVisible(false) else navView.menu.findItem(R.id.nav_admin).setVisible(true)
val adminButton = navView.menu.findItem(R.id.nav_admin)
if (!userDTO.roles.any { it.name == "ROLE_ADMIN" }) adminButton.setVisible(false) else adminButton.setVisible(true)
}
}
}

View File

@ -1,12 +1,12 @@
package com.displaynone.acss.components.auth.models.user
import android.content.Context
import android.util.Log
import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager
import com.displaynone.acss.components.auth.internal_utils.UserManager
import com.displaynone.acss.components.auth.models.AuthTokenPair
import com.displaynone.acss.components.auth.models.user.repository.UserRepository
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
class UserServiceST(
@ -34,69 +34,64 @@ class UserServiceST(
return instance ?: throw RuntimeException("null instance")
}
}
suspend fun login(login: String, password:String): Result<Unit>{
suspend fun login(login: String, password: String): Result<Unit> {
return runCatching {
userRepository.login(login = login, password).getOrThrow().let { data ->
tokenManager.saveTokens(data)
userManager.saveDto(data.user)
tokenManager.saveTokens(data.tokens)
}
}
}
fun getTokenPair(): AuthTokenPair? {
return tokenManager.authTokenPair
}
fun hasTokens(): Boolean {
return tokenManager.hasTokens()
}
// suspend fun getMyLastVisits(pageNum: Int,
// pageSize: Int): Result<List<VisitDto>> {
// if (!tokenManager.hasTokens()) {
// throw RuntimeException("access token is null")
// }
//
// return userRepository.getMyLastVisits(
// pageNum = pageNum,
// pageSize = pageSize,
// token = tokenManager.authTokenPair!!.accessToken
// ).map { pagingDto -> pagingDto.content }
// }
// suspend fun getLastVisitsByLogin(pageNum: Int,
// pageSize: Int,
// login: String): Result<List<VisitDto>> {
// if (!tokenManager.hasTokens()) {
// throw RuntimeException("access token is null")
// }
// return userRepository.getLastVisitsByLogin(pageNum, pageSize, tokenManager.authTokenPair!!.accessToken, login).map { pagingDto -> pagingDto.content }
// }
fun logout(){
fun logout() {
tokenManager.clear()
}
suspend fun changeRights(login: String, isACSBlocked: Boolean): Result<Unit> {
return userRepository.changeRightsByLogin(login, tokenManager.authTokenPair!!.accessToken, isACSBlocked)
return userRepository.changeRightsByLogin(
login,
tokenManager.authTokenPair!!.accessToken,
isACSBlocked
)
}
suspend fun getInfo(): Result<UserDTO>{
suspend fun getInfo(): Result<UserDTO> {
if (!tokenManager.hasTokens()) {
throw RuntimeException("access token is null")
}
val result = userRepository.getInfo(tokenManager.authTokenPair!!.accessToken)
result.map { dto ->
saveUserDTO(dto)
}
result.map { dto -> saveUserDTO(dto) }
return result
}
suspend fun getInfoByLogin(login: String): Result<UserDTO>{
suspend fun getInfoByLogin(login: String): Result<UserDTO> {
if (!tokenManager.hasTokens()) {
throw RuntimeException("access token is null")
}
return userRepository.getInfoByLogin(tokenManager.authTokenPair!!.accessToken, login)
}
suspend fun openDoor(code: String): Result<Int> {
return userRepository.openDoor(tokenManager.authTokenPair!!.accessToken, code = code)
}
fun getUserDTO(): UserDTO? {
return userManager.dto
}
fun saveUserDTO(userDTO: UserDTO){
return userManager.saveDto(userDTO)
fun saveUserDTO(userDTO: UserDTO) {
Log.d("USER", "UPDATE USER")
return userManager.saveDto(userDTO)
}
}

View File

@ -1,22 +1,18 @@
package com.displaynone.acss.components.auth.models.user.repository
import android.util.Log
import com.bumptech.glide.load.HttpException
import com.displaynone.acss.components.auth.models.AuthTokenPair
import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network
import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto
import com.displaynone.acss.components.auth.models.user.repository.dto.LoginDTO
import com.displaynone.acss.components.auth.models.user.repository.dto.LoginResponseDTO
import com.displaynone.acss.components.auth.models.user.repository.dto.UpdateUserDTO
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.components.auth.models.user.repository.dto.UserLoginDto
import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.client.request.patch
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
@ -24,40 +20,27 @@ import io.ktor.http.contentType
import io.ktor.http.encodeURLPath
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
class UserRepository(
) {
suspend fun isUserExist(login: String): Result<Boolean> = withContext(Dispatchers.IO) {
runCatching {
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/$encodedLogin/auth/")
result.status != HttpStatusCode.OK
}
}
suspend fun login(login: String, password: String): Result<AuthTokenPair> = withContext(Dispatchers.IO) {
runCatching {
val result = Network.client.post("$serverUrl/api/auth/login") {
headers {
append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
class UserRepository {
suspend fun login(login: String, password: String): Result<LoginResponseDTO> =
withContext(Dispatchers.IO) {
runCatching {
val result = Network.client.post("$serverUrl/api/auth/login") {
headers {
append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
}
contentType(ContentType.Application.Json)
setBody(LoginDTO(login, password))
}
contentType(ContentType.Application.Json)
setBody(UserLoginDto(login, password))
if (result.status != HttpStatusCode.OK) error(result.status)
result.body()
}
if (result.status != HttpStatusCode.OK) {
error(result.status)
}
// val gson = Gson()
// val tokenPair = gson.fromJson(result.bodyAsText(), AuthTokenPair::class.java)
Log.d("UserRepository", result.bodyAsText())
// result.body()
val tokenPair = Json.decodeFromString<AuthTokenPair>(result.bodyAsText())
tokenPair
}
}
suspend fun openDoor(token: String, code: String): Result<Int> = withContext(Dispatchers.IO){
runCatching{
suspend fun openDoor(token: String, code: String): Result<Int> = withContext(Dispatchers.IO) {
runCatching {
val result = Network.client.post("$serverUrl/api/acs/open") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
@ -65,58 +48,61 @@ class UserRepository(
contentType(ContentType.Application.Json)
setBody("""{ "code": $code }""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
if (result.status != HttpStatusCode.OK) error("Status ${result.status}: ${result.body<String>()}")
result.status.value
}
}
suspend fun getInfo(token: String): Result<UserDTO> = withContext(Dispatchers.IO){
suspend fun getInfo(token: String): Result<UserDTO> = withContext(Dispatchers.IO) {
runCatching {
val result = Network.client.get("$serverUrl/api/users/me") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
if (result.status != HttpStatusCode.OK) error("Status ${result.status}: ${result.body<String>()}")
result.body()
}
}
suspend fun getInfoByLogin(token: String, login: String): Result<UserDTO> = withContext(Dispatchers.IO){
runCatching {
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/users/login/$encodedLogin") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
suspend fun getInfoByLogin(token: String, login: String): Result<UserDTO> =
withContext(Dispatchers.IO) {
runCatching {
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/users/login/$encodedLogin") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
setBody("""{ "code": "$encodedLogin" }""")
}
setBody("""{ "code": "$encodedLogin" }""")
if (result.status != HttpStatusCode.OK) error("Status ${result.status}: ${result.body<String>()}")
result.body()
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.body()
}
}
suspend fun changeRightsByLogin(login: String, token: String, isACSBlocked: Boolean): Result<Unit> = withContext(Dispatchers.IO) {
suspend fun changeRightsByLogin(
login: String,
token: String,
isACSBlocked: Boolean
): Result<Unit> = withContext(Dispatchers.IO) {
runCatching {
val encodedLogin = login.encodeURLPath()
val result = Network.client.patch("$serverUrl/api/users/login/$encodedLogin") {
headers{
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
contentType(ContentType.Application.Json)
setBody(UpdateUserDTO(
password = null,
name = null,
photo = null,
position = null,
isACSBlocked = isACSBlocked
))
setBody(
UpdateUserDTO(
password = null,
name = null,
photo = null,
position = null,
isACSBlocked = isACSBlocked
)
)
}
if (result.status != HttpStatusCode.OK) {
Log.w("UserRepository", "Status: ${result.status}, Body: ${result.body<String>()}")

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class UserLoginDto(
data class LoginDTO(
@SerialName("login")
val login: String,
@SerialName("password")

View File

@ -0,0 +1,13 @@
package com.displaynone.acss.components.auth.models.user.repository.dto
import com.displaynone.acss.components.auth.models.AuthTokenPair
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class LoginResponseDTO(
@SerialName("tokens")
val tokens: AuthTokenPair,
@SerialName("user")
val user: UserDTO,
)

View File

@ -9,18 +9,17 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.displaynone.acss.R
import com.displaynone.acss.databinding.FragmentAuthBinding
import com.displaynone.acss.ui.auth.AuthViewModel.Action
import com.displaynone.acss.util.collectWithLifecycle
import com.displaynone.acss.util.navigateTo
import com.displaynone.acss.ui.auth.AuthViewModel.Action
class AuthFragment: Fragment(R.layout.fragment_auth) {
class AuthFragment : Fragment(R.layout.fragment_auth) {
private var _binding: FragmentAuthBinding? = null
private val binding: FragmentAuthBinding get() = _binding!!
private val viewModel: AuthViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentAuthBinding.bind(view)
@ -40,22 +39,25 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
binding.errorTitle.visibility = View.VISIBLE
}
}
binding.next.setOnClickListener{
binding.next.setOnClickListener {
onLoginButtonClicked(view)
}
}
@SuppressLint("StringFormatMatches")
private fun getErrorDescription(errorCode: String): String {
return when (errorCode) {
401.toString()+' ' -> getString(R.string.error_401)
404.toString()+' ' -> getString(R.string.error_404)
500.toString()+' ' -> getString(R.string.error_500)
401.toString() + ' ' -> getString(R.string.error_401)
404.toString() + ' ' -> getString(R.string.error_404)
500.toString() + ' ' -> getString(R.string.error_500)
else -> getString(R.string.error_unknown, errorCode)
}
}
private fun blockLoginButton() {
binding.next.isEnabled = false
}
private fun getPasswordValidError(password: String): String {
if (password.length < 8) {
return "LenError"
@ -76,6 +78,7 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
}
}
}
private fun getLoginValidError(username: String): String {
val alf = "^[a-zA-Z0-9_]+$".toRegex()
if (username.isEmpty()) {
@ -97,53 +100,61 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
// password.length >= 8
// }
// private fun subscribe() {
// private fun subscribe() {
// viewModel.state.collectWhenStarted(this) { state ->
// binding.login.setOnClickListener(this::onLoginButtonClicked)
// }
// }
private fun validateLoginAndSetError(username: String) {
val errorType = getLoginValidError(username)
private fun validateLoginAndSetError(username: String) {
val errorType = getLoginValidError(username)
when (errorType) {
"EmptyError" -> {
binding.login.error = getString(R.string.error_login_empty)
}
"LenError" -> {
binding.login.error = getString(R.string.error_login_too_short)
}
"StartsWithDigitError" -> {
binding.login.error = getString(R.string.error_login_starts_with_digit)
}
"InvalidCharactersError" -> {
binding.login.error = getString(R.string.error_login_invalid_characters)
}
"NoErrors" -> {
binding.login.error = null
when (errorType) {
"EmptyError" -> {
binding.login.error = getString(R.string.error_login_empty)
}
"LenError" -> {
binding.login.error = getString(R.string.error_login_too_short)
}
"StartsWithDigitError" -> {
binding.login.error = getString(R.string.error_login_starts_with_digit)
}
"InvalidCharactersError" -> {
binding.login.error = getString(R.string.error_login_invalid_characters)
}
"NoErrors" -> {
binding.login.error = null
}
}
}
}
private fun onLoginButtonClicked(view: View) {
val login = binding.login.text.toString()
val password = binding.password.text.toString()
if (getPasswordValidError(password) != "NoErrors") {
val color = R.color.secondary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color)
binding.next.backgroundTintList =
ContextCompat.getColorStateList(requireContext(), color)
validatePasswordAndSetError(password)
}else{
} else {
val color = R.color.primary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color)
binding.next.backgroundTintList =
ContextCompat.getColorStateList(requireContext(), color)
}
// if (login.isEmpty()) return
if (getLoginValidError(login) != "NoErrors") {
val color = R.color.secondary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color)
binding.next.backgroundTintList =
ContextCompat.getColorStateList(requireContext(), color)
validateLoginAndSetError(login)
}else{
} else {
val color = R.color.primary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color)
binding.next.backgroundTintList =
ContextCompat.getColorStateList(requireContext(), color)
}

View File

@ -4,7 +4,6 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
@ -12,9 +11,8 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlin.reflect.typeOf
class AuthViewModel(): ViewModel() {
class AuthViewModel : ViewModel() {
private val _action = Channel<Action>(
capacity = Channel.BUFFERED,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
@ -23,7 +21,7 @@ class AuthViewModel(): ViewModel() {
private val _errorState = MutableStateFlow<String?>(null)
val errorState: StateFlow<String?> = _errorState.asStateFlow()
fun login(login: String, password:String){
fun login(login: String, password: String) {
viewModelScope.launch {
try {
UserServiceST.getInstance().login(login, password).fold(
@ -40,6 +38,7 @@ class AuthViewModel(): ViewModel() {
}
}
}
private fun openProfile() {
viewModelScope.launch {
_action.send(Action.GotoProfile)

View File

@ -12,7 +12,6 @@ import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
import com.displaynone.acss.R
import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.databinding.FragmentInitBinding
import com.displaynone.acss.util.collectWithLifecycle
import com.displaynone.acss.util.navigateTo
@ -29,24 +28,26 @@ class InitFragment : Fragment(R.layout.fragment_init) {
_binding = FragmentInitBinding.bind(view)
if (!checkInternetConnection()) {
hideSpinner()
handleError(R.string.noInternet)
return
}
pingServer()
}
private fun pingServer() {
viewModel.pingServer()
viewModel.state.collectWithLifecycle(this) { state ->
if (state is InitFragmentViewModel.State.Show){
if(!state.item){
if (state is InitFragmentViewModel.State.Show) {
if (!state.item) {
hideSpinner()
handleError(R.string.serverIsUnabailable)
} else{
} else {
val navController: NavController = findNavController()
if (!isUserAuthenticated()) {
hideSpinner()
navigateTo(navController, R.id.action_nav_init_to_nav_auth)
} else {
updateUser(navController)
@ -58,15 +59,14 @@ class InitFragment : Fragment(R.layout.fragment_init) {
private fun updateUser(navController: NavController) {
viewModel.updateUserInfo()
viewModel.state.collectWithLifecycle(this){ state ->
viewModel.state.collectWithLifecycle(this) { state ->
hideSpinner()
if (state is InitFragmentViewModel.State.Update) {
if (state.item == null) {
navigateTo(navController, R.id.action_nav_init_to_nav_auth)
} else{
navigateTo(navController, R.id.action_nav_init_to_nav_profile)
}
navigateTo(navController, R.id.action_nav_init_to_nav_profile)
}
if (state is InitFragmentViewModel.State.Error){
if (state is InitFragmentViewModel.State.Error) {
navigateTo(navController, R.id.action_nav_init_to_nav_auth)
}
}
@ -95,8 +95,22 @@ class InitFragment : Fragment(R.layout.fragment_init) {
return false
}
private fun getCachedUser(): UserDTO? {
return UserServiceST.getInstance().getUserDTO();
private fun hideSpinner() {
binding.spinner.visibility = View.GONE
}
private fun unhideSpinner() {
binding.spinner.visibility = View.VISIBLE
}
private fun hideError() {
binding.errorImage.visibility = View.GONE
binding.error.visibility = View.GONE
}
private fun unhideError() {
binding.errorImage.visibility = View.VISIBLE
binding.error.visibility = View.VISIBLE
}
private fun isUserAuthenticated(): Boolean {
@ -104,11 +118,9 @@ class InitFragment : Fragment(R.layout.fragment_init) {
}
private fun handleError(string: Int) {
unhideError()
binding.error.text = requireContext().getString(string)
}
private fun handleError(errorMessage: String) {
binding.error.text = errorMessage
}
override fun onDestroyView() {
super.onDestroyView()

View File

@ -6,11 +6,10 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.acs.models.visit.VisitListPagingSource
import com.displaynone.acss.components.acs.models.visit.VisitServiceST
import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import io.ktor.util.reflect.instanceOf
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
@ -18,21 +17,24 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
class ProfileViewModel(): ViewModel() {
class ProfileViewModel() : ViewModel() {
private val _action = Channel<Action>(
capacity = Channel.BUFFERED,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
private var login: String = ""
fun setLogin(login1: String){
fun setLogin(login1: String) {
login = login1
}
val action = _action.receiveAsFlow()
val _state = MutableStateFlow<State>(State.Loading)
val state = _state.asStateFlow()
val visitListState = Pager(
config = PagingConfig(pageSize = 20,
config = PagingConfig(
pageSize = 20,
enablePlaceholders = false,
maxSize = 100
)
@ -45,19 +47,19 @@ class ProfileViewModel(): ViewModel() {
config = PagingConfig(pageSize = 20, enablePlaceholders = false, maxSize = 100)
) {
VisitListPagingSource { pageNum, pageSize ->
VisitServiceST.getInstance().getLastVisitsByLogin(pageNum,pageSize, login)
VisitServiceST.getInstance().getLastVisitsByLogin(pageNum, pageSize, login)
}
}.flow.cachedIn(viewModelScope)
fun logout(){
fun logout() {
UserServiceST.getInstance().logout()
}
fun getInfo(){
fun getInfo() {
viewModelScope.launch {
UserServiceST.getInstance().getInfo().fold(
onSuccess = { data ->
_state.emit(State.Show(data))
UserServiceST.getInstance().saveUserDTO(data)
Log.d("Pvm", data.login)
},
onFailure = { error ->
@ -67,7 +69,8 @@ class ProfileViewModel(): ViewModel() {
)
}
}
fun changeRights(login: String, isACSBlocked: Boolean){
fun changeRights(login: String, isACSBlocked: Boolean) {
viewModelScope.launch {
UserServiceST.getInstance().changeRights(login, isACSBlocked).fold(
onSuccess = {
@ -80,12 +83,14 @@ class ProfileViewModel(): ViewModel() {
)
}
}
fun openAuth(){
fun openAuth() {
viewModelScope.launch {
_action.send(Action.GoToAuth)
}
}
fun openScan(){
fun openScan() {
viewModelScope.launch {
_action.send(Action.GoToScan)
}
@ -96,12 +101,14 @@ class ProfileViewModel(): ViewModel() {
data class Show(
val item: UserDTO
) : State
data class Change(
val item: Boolean
) : State
}
sealed interface Action {
data object GoToAuth: Action
data object GoToScan: Action
data object GoToAuth : Action
data object GoToScan : Action
}
}

View File

@ -1,35 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView
android:visibility="gone"
android:id="@+id/error_image"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center"
android:layout_margin="10dp"
android:layout_height="250dp"
android:src="@drawable/lost_connection"/>
android:src="@drawable/dinosaur"
android:textAlignment="center"/>
<TextView
android:visibility="gone"
android:id="@+id/error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="24sp"
android:layout_margin="15dp"
android:letterSpacing="0.05"
android:background="?android:attr/colorBackground"
android:gravity="center"
android:letterSpacing="0.05"
android:textColor="?attr/colorOnBackground"
tools:text="@string/noInternet"
tools:ignore="MissingConstraints" />
<ImageView
android:layout_width="250dp"
android:layout_gravity="center"
android:layout_margin="10dp"
android:layout_height="250dp"
android:src="@drawable/dinosaur"/>
android:textSize="24sp"
tools:ignore="MissingConstraints"
tools:text="@string/noInternet" />
<ProgressBar
android:id="@+id/spinner"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,7 +6,7 @@
<fragment
android:id="@+id/nav_init"
android:name="com.displaynone.acss.ui.init.InitFragment"
android:label="InitFragment">
android:label="@string/app_name">
<action
android:id="@+id/action_nav_init_to_nav_profile"
app:destination="@id/nav_profile" />

View File

@ -1,5 +1,5 @@
<resources>
<string name="app_name">AS</string>
<string name="app_name">ACSS</string>
<string name="login">Enter Login</string>
<string name="password">Enter password</string>
<string name="hello">Welcome!</string>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">AS</string>
<string name="app_name">ACSS</string>
<string name="login">Введите логин</string>
<string name="password">Введите пароль</string>
<string name="hello">Здравствуйте!</string>

View File

@ -1,5 +1,5 @@
<resources>
<string name="app_name">AS</string>
<string name="app_name">ACSS</string>
<string name="login">Enter Login</string>
<string name="password">Enter password</string>
<string name="hello">Welcome!</string>