Почти сделал
This commit is contained in:
parent
21fbedfdf0
commit
2d44a17c2c
@ -49,7 +49,7 @@ dependencies {
|
||||
|
||||
implementation("com.squareup.picasso:picasso:2.8")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
|
||||
implementation("androidx.datastore:datastore-preferences:1.1.1")
|
||||
implementation("androidx.datastore:datastore-preferences:1.1.2")
|
||||
implementation("com.google.mlkit:barcode-scanning:17.3.0")
|
||||
|
||||
val cameraX = "1.3.4"
|
||||
|
@ -4,6 +4,9 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.myitschool.work.api.login.ApiServiceLogin
|
||||
import ru.myitschool.work.api.main.ApiServiceMain
|
||||
import ru.myitschool.work.api.scan.ApiServiceScan
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@ -27,4 +30,10 @@ object NetworkModule {
|
||||
fun provideApiServiceMain(retrofitClient: RetrofitClient): ApiServiceMain {
|
||||
return retrofitClient.getApiServiceMain()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideApiServiceScan(retrofitClient: RetrofitClient): ApiServiceScan {
|
||||
return retrofitClient.getApiServiceScan()
|
||||
}
|
||||
}
|
@ -1,11 +1,17 @@
|
||||
package ru.myitschool.work.api
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import ru.myitschool.work.api.login.ApiServiceLogin
|
||||
import ru.myitschool.work.api.main.ApiServiceMain
|
||||
import ru.myitschool.work.api.scan.ApiServiceScan
|
||||
import ru.myitschool.work.core.Constants.SERVER_ADDRESS
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class RetrofitClient {
|
||||
|
||||
private val serverAddress = SERVER_ADDRESS
|
||||
@ -30,4 +36,8 @@ class RetrofitClient {
|
||||
fun getApiServiceMain(): ApiServiceMain {
|
||||
return retrofit.create(ApiServiceMain::class.java)
|
||||
}
|
||||
|
||||
fun getApiServiceScan(): ApiServiceScan {
|
||||
return retrofit.create(ApiServiceScan::class.java)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ru.myitschool.work.api
|
||||
package ru.myitschool.work.api.login
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.GET
|
@ -1,4 +1,4 @@
|
||||
package ru.myitschool.work.api
|
||||
package ru.myitschool.work.api.main
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.GET
|
@ -1,4 +1,4 @@
|
||||
package ru.myitschool.work.api
|
||||
package ru.myitschool.work.api.main
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
@ -0,0 +1,11 @@
|
||||
package ru.myitschool.work.api.scan
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.PATCH
|
||||
import retrofit2.http.Path
|
||||
|
||||
interface ApiServiceScan {
|
||||
@PATCH("api/{login}/info")
|
||||
fun open(@Path("login") login: String, @Body data: CodeJson): Call<Void>
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package ru.myitschool.work.api.scan
|
||||
|
||||
class CodeJson(
|
||||
private var value: String? = null
|
||||
)
|
@ -1,5 +1,5 @@
|
||||
package ru.myitschool.work.core
|
||||
// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
|
||||
|
||||
object Constants {
|
||||
const val SERVER_ADDRESS = "http://localhost:8090"
|
||||
}
|
@ -15,8 +15,9 @@ import ru.myitschool.work.ui.main.MainDestination
|
||||
import ru.myitschool.work.ui.main.MainFragment
|
||||
import ru.myitschool.work.ui.qr.scan.QrScanDestination
|
||||
import ru.myitschool.work.ui.qr.scan.QrScanFragment
|
||||
import ru.myitschool.work.ui.result.ResultDestination
|
||||
import ru.myitschool.work.ui.result.ResultFragment
|
||||
|
||||
// НЕ ИЗМЕНЯЙТЕ НАЗВАНИЕ КЛАССА!
|
||||
@AndroidEntryPoint
|
||||
class RootActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -34,6 +35,7 @@ class RootActivity : AppCompatActivity() {
|
||||
fragment<LoginFragment, LoginDestination>()
|
||||
fragment<QrScanFragment, QrScanDestination>()
|
||||
fragment<MainFragment, MainDestination>()
|
||||
fragment<ResultFragment, ResultDestination>()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import ru.myitschool.work.api.ApiServiceLogin
|
||||
import ru.myitschool.work.api.login.ApiServiceLogin
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@ -22,7 +22,7 @@ class LoginViewModel @Inject constructor(
|
||||
|
||||
fun authenticate(login: String) {
|
||||
viewModelScope.launch {
|
||||
_state.value = LoginState.Loading // Устанавливаем состояние загрузки
|
||||
_state.value = LoginState.Loading
|
||||
|
||||
apiService.authenticate(login).enqueue(object : retrofit2.Callback<Void> {
|
||||
override fun onResponse(call: Call<Void>, response: Response<Void>) {
|
||||
|
@ -82,7 +82,6 @@ class MainFragment : Fragment(R.layout.main_fragment) {
|
||||
username.text = state.userInfo.name
|
||||
position.text = state.userInfo.position
|
||||
|
||||
// Загрузка изображения из URL
|
||||
loadImageFromUrl(state.userInfo.photoUrl)
|
||||
|
||||
val inputFormat = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault())
|
||||
|
@ -10,8 +10,8 @@ import kotlinx.coroutines.launch
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import ru.myitschool.work.api.ApiServiceMain
|
||||
import ru.myitschool.work.api.UserInfo
|
||||
import ru.myitschool.work.api.main.ApiServiceMain
|
||||
import ru.myitschool.work.api.main.UserInfo
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
@ -4,7 +4,6 @@ import android.os.Bundle
|
||||
import androidx.core.os.bundleOf
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
// НЕ ИЗМЕНЯЙТЕ ЭТОТ ФАЙЛ. В ТЕСТАХ ОН БУДЕМ ВОЗВРАЩЁН В ИСХОДНОЕ СОСТОЯНИЕ
|
||||
@Serializable
|
||||
data object QrScanDestination {
|
||||
const val REQUEST_KEY = "qr_result"
|
||||
|
@ -23,7 +23,6 @@ import ru.myitschool.work.databinding.FragmentQrScanBinding
|
||||
import ru.myitschool.work.utils.collectWhenStarted
|
||||
import ru.myitschool.work.utils.visibleOrGone
|
||||
|
||||
// НЕ ИЗМЕНЯЙТЕ ЭТОТ ФАЙЛ. В ТЕСТАХ ОН БУДЕМ ВОЗВРАЩЁН В ИСХОДНОЕ СОСТОЯНИЕ
|
||||
class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
|
||||
private var _binding: FragmentQrScanBinding? = null
|
||||
private val binding: FragmentQrScanBinding get() = _binding!!
|
||||
|
@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.myitschool.work.utils.MutablePublishFlow
|
||||
|
||||
// НЕ ИЗМЕНЯЙТЕ ЭТОТ ФАЙЛ. В ТЕСТАХ ОН БУДЕМ ВОЗВРАЩЁН В ИСХОДНОЕ СОСТОЯНИЕ
|
||||
class QrScanViewModel(
|
||||
application: Application
|
||||
) : AndroidViewModel(application) {
|
||||
|
@ -0,0 +1,63 @@
|
||||
package ru.myitschool.work.ui.result
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import ru.myitschool.work.api.scan.ApiServiceScan
|
||||
import ru.myitschool.work.api.scan.CodeJson
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ResultViewModel @Inject constructor(
|
||||
private val apiService: ApiServiceScan
|
||||
) : ViewModel() {
|
||||
|
||||
private val _state = MutableStateFlow<ResultState>(ResultState.Initial)
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
fun open(login: String, data: CodeJson) {
|
||||
viewModelScope.launch {
|
||||
_state.value = ResultState.Loading
|
||||
|
||||
apiService.open(login, data).enqueue(object : retrofit2.Callback<Void> {
|
||||
override fun onResponse(call: Call<Void>, response: Response<Void>) {
|
||||
Log.d("Open", "Response code: ${response.code()}")
|
||||
|
||||
when (response.code()) {
|
||||
200 -> {
|
||||
_state.value = ResultState.Success
|
||||
}
|
||||
500 -> {
|
||||
_state.value = ResultState.InvalidCredentials
|
||||
}
|
||||
401 -> {
|
||||
_state.value = ResultState.Error
|
||||
}
|
||||
else -> {
|
||||
_state.value = ResultState.InvalidCredentials
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Void>, t: Throwable) {
|
||||
_state.value = ResultState.Error
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sealed class ResultState {
|
||||
data object Initial : ResultState()
|
||||
data object InvalidCredentials : ResultState()
|
||||
data object Loading : ResultState()
|
||||
data object Success : ResultState()
|
||||
data object Error : ResultState()
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package ru.myitschool.work.ui.result
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data object ResultDestination
|
@ -0,0 +1,99 @@
|
||||
package ru.myitschool.work.ui.result
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import ru.myitschool.work.R
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.transition.Visibility
|
||||
import ru.myitschool.work.databinding.FragmentScanResultBinding
|
||||
import ru.myitschool.work.ui.login.LoginViewModel
|
||||
import ru.myitschool.work.ui.main.MainDestination
|
||||
import ru.myitschool.work.utils.AuthPreferences
|
||||
import ru.myitschool.work.utils.collectWhenStarted
|
||||
import ru.myitschool.work.utils.visibleOrGone
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ResultFragment : Fragment(R.layout.fragment_scan_result) {
|
||||
private var _binding: FragmentScanResultBinding? = null
|
||||
private val binding: FragmentScanResultBinding get() = _binding!!
|
||||
private val viewModel: ResultViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
_binding = FragmentScanResultBinding.bind(view)
|
||||
|
||||
setupLoginComponents()
|
||||
}
|
||||
|
||||
private fun setupLoginComponents() {
|
||||
binding.apply {
|
||||
result.visibleOrGone(false)
|
||||
successIcon.visibleOrGone(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeLoginState() {
|
||||
viewModel.state.collectWhenStarted(this) { state ->
|
||||
when (state) {
|
||||
is ResultViewModel.ResultState.Loading -> {
|
||||
binding.result.visibleOrGone(false)
|
||||
binding.successIcon.visibleOrGone(false)
|
||||
binding.error.visibleOrGone(false)
|
||||
|
||||
binding.loading.visibleOrGone(true)
|
||||
}
|
||||
is ResultViewModel.ResultState.Success -> {
|
||||
binding.result.visibleOrGone(true)
|
||||
binding.successIcon.visibleOrGone(true)
|
||||
|
||||
binding.error.visibleOrGone(false)
|
||||
binding.loading.visibleOrGone(false)
|
||||
}
|
||||
is ResultViewModel.ResultState.InvalidCredentials -> {
|
||||
binding.result.visibleOrGone(false)
|
||||
binding.successIcon.visibleOrGone(false)
|
||||
binding.loading.visibleOrGone(false)
|
||||
|
||||
binding.error.visibleOrGone(true)
|
||||
}
|
||||
is ResultViewModel.ResultState.Error -> {
|
||||
binding.loading.visibleOrGone(false)
|
||||
binding.result.apply {
|
||||
visibleOrGone(true)
|
||||
text = RiootoString(R.string.userNotFing)
|
||||
}
|
||||
Log.d("Authentication", "Ошибка сканирования")
|
||||
}
|
||||
|
||||
LoginViewModel.LoginState.Initial -> binding.loading.visibleOrGone(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToMainScreen() {
|
||||
try {
|
||||
findNavController().apply {
|
||||
popBackStack(MainDestination, false)
|
||||
navigate(MainDestination)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("ResultFragment", "Navigation error", e)
|
||||
Toast.makeText(context, "Ошибка перехода", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
_binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package ru.myitschool.work.ui.result
|
||||
|
||||
object TextStatus {
|
||||
const val error = "Вход был отменён/Operation was cancelled"
|
||||
const val success = "Успешно/Success"
|
||||
}
|
@ -12,7 +12,7 @@ class AuthPreferences(context: Context) {
|
||||
fun saveLoginState(isLoggedIn: Boolean) {
|
||||
sharedPreferences.edit().apply {
|
||||
putBoolean(KEY_IS_LOGGED_IN, isLoggedIn)
|
||||
apply() // Асинхронное сохранение
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,14 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_top"
|
||||
@ -13,12 +21,26 @@
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.25" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/serverError"
|
||||
android:textSize="14sp"
|
||||
android:gravity="center"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline_bottom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent= "0.7" />
|
||||
app:layout_constraintGuide_percent="0.7" />
|
||||
|
||||
<View
|
||||
android:id="@+id/close"
|
||||
@ -49,10 +71,11 @@
|
||||
android:textColor="@color/black"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/text_lastenter"
|
||||
app:layout_constraintBottom_toTopOf="@+id/guideline_bottom"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<!--
|
||||
<TextView
|
||||
android:id="@+id/text_lastenter"
|
||||
android:layout_width="wrap_content"
|
||||
@ -61,6 +84,6 @@
|
||||
app:layout_constraintBottom_toTopOf="@+id/guideline_bottom"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
-->
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -19,4 +19,6 @@
|
||||
<string name="closeText">Закрыть/Close</string>
|
||||
<string name="errorLoginText">Ошибка входа</string>
|
||||
<string name="text_last_enter">Последний вход 12.12.1212</string>
|
||||
<string name="serverError">Ошибка сервера</string>
|
||||
<string name="userNotFing">Пользователь не найден</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user