diff --git a/.kotlin/errors/errors-1739897793894.log b/.kotlin/errors/errors-1739897793894.log new file mode 100644 index 0000000..59473cc --- /dev/null +++ b/.kotlin/errors/errors-1739897793894.log @@ -0,0 +1,82 @@ +kotlin version: 2.0.21 +error message: Daemon compilation failed: null +java.lang.Exception + at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:69) + at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:65) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:240) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) + at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76) + at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62) + at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62) + at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44) + at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59) + at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53) + at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) + at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) + at java.base/java.util.concurrent.FutureTask.run(Unknown Source) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:195) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:128) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:170) + at org.gradle.internal.Factories$1.create(Factories.java:31) + at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:267) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:131) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:136) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:165) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:134) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) + at java.base/java.util.concurrent.FutureTask.run(Unknown Source) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.nio.file.DirectoryNotEmptyException: C:\Users\User\AppData\Local\Temp\kotlin-backups17220056760637860906 + at java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(Unknown Source) + at java.base/sun.nio.fs.AbstractFileSystemProvider.delete(Unknown Source) + at java.base/java.nio.file.Files.delete(Unknown Source) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction$cleanupStash$2$1$1.invoke(CompilationTransaction.kt:244) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction$cleanupStash$2$1$1.invoke(CompilationTransaction.kt:244) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.cleanupStash$lambda$11$lambda$10$lambda$9(CompilationTransaction.kt:244) + at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source) + at java.base/java.util.ArrayList.forEach(Unknown Source) + at java.base/java.util.stream.SortedOps$RefSortingSink.end(Unknown Source) + at java.base/java.util.stream.Sink$ChainedReference.end(Unknown Source) + at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source) + at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source) + at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source) + at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source) + at java.base/java.util.stream.ReferencePipeline.forEach(Unknown Source) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.cleanupStash(CompilationTransaction.kt:244) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.close(CompilationTransaction.kt:254) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally(IncrementalCompilerRunner.kt:747) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:120) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + ... 3 more + + diff --git a/app/src/main/java/ru/myitschool/work/ui/admin/AdminDestination.kt b/app/src/main/java/ru/myitschool/work/ui/admin/AdminDestination.kt new file mode 100644 index 0000000..703625f --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/admin/AdminDestination.kt @@ -0,0 +1,5 @@ +package ru.myitschool.work.ui.admin +import kotlinx.serialization.Serializable + +@Serializable +data object AdminDestination \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/admin/AdminFragment.kt b/app/src/main/java/ru/myitschool/work/ui/admin/AdminFragment.kt new file mode 100644 index 0000000..45dec00 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/admin/AdminFragment.kt @@ -0,0 +1,85 @@ +package ru.myitschool.work.ui.admin + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.squareup.picasso.Picasso +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import ru.myitschool.work.R +import ru.myitschool.work.databinding.FragmentAdminBinding +import ru.myitschool.work.ui.main.MainDestination +import ru.myitschool.work.utils.collectWhenStarted +import ru.myitschool.work.utils.TextChangedListener + +@AndroidEntryPoint +class AdminFragment : Fragment(R.layout.fragment_admin) { + private var _binding: FragmentAdminBinding? = null + private val binding: FragmentAdminBinding get() = _binding!! + private val picasso: Picasso by lazy { Picasso.get() } + + private val viewModel: AdminViewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + _binding = FragmentAdminBinding.bind(view) + + binding.loginadmin.addTextChangedListener(TextChangedListener { viewModel.onUsernameChanged(it) }) + subscribe() + binding.find.setOnClickListener { + login(binding.loginadmin.text.toString()) + } + } + + private fun login(username: String) { + viewModel.loadPersonInfo(username) { + viewModel.state.collectWhenStarted(this) { state -> + if (state.photo.isNotEmpty()) { + picasso.load(state.photo).into(binding.photoAdmin) + } + binding.fullnameAdmin.text = state.fullName + binding.positionAdmin.text = state.position + binding.lastEntryAdmin.text = state.lastVisit + setError(state.error) + } + } + } + + private fun subscribe() { +// viewModel.state.collectWhenStarted(this) { state -> +// binding.login.isEnabled = state.isLoginEnabled +// if (state.error != null) { +// binding.error.visibility = View.VISIBLE +// binding.error.text = state.error +// } else { +// binding.error.visibility = View.GONE +// } +// } +// viewModel.savedUsername.collectWhenStarted(this) { username -> +// viewModel.savedPassword.collectWhenStarted(this) { password -> +// if (!username.isNullOrBlank() && !password.isNullOrBlank()) { +// login(username, password) +// } +// } +// } + println("akjshdfg") + } + + + private fun setError(error: String?) { + val showError = error != null + val views = listOf(binding.fullnameAdmin, binding.positionAdmin, binding.lastEntryAdmin, binding.photoAdmin) + views.forEach { it.visibility = if (showError) View.GONE else View.VISIBLE} + binding.errorAdmin.visibility = if (showError) View.VISIBLE else View.GONE + binding.block.visibility = if (showError) View.GONE else View.VISIBLE + binding.errorAdmin.text = error ?: "" + } + + override fun onDestroyView() { + _binding = null + super.onDestroyView() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/admin/AdminState.kt b/app/src/main/java/ru/myitschool/work/ui/admin/AdminState.kt new file mode 100644 index 0000000..4c868c1 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/admin/AdminState.kt @@ -0,0 +1,13 @@ +package ru.myitschool.work.ui.admin + +data class AdminState( + val isLoginEnabled: Boolean = false, val error: String? = null +) + +data class AdminMainState( + val fullName: String = "", + val photo: String = "", + val position: String = "", + val lastVisit: String = "", + val error: String? = null +) diff --git a/app/src/main/java/ru/myitschool/work/ui/admin/AdminViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/admin/AdminViewModel.kt new file mode 100644 index 0000000..d6ae36d --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/admin/AdminViewModel.kt @@ -0,0 +1,86 @@ +package ru.myitschool.work.ui.admin + +import android.content.Context +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.google.gson.GsonBuilder +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import retrofit2.HttpException +import ru.myitschool.work.data.remote.LoginApi +import ru.myitschool.work.data.remote.ErrorDto +import ru.myitschool.work.di.AppModule +import ru.myitschool.work.ui.admin.AdminState +import java.text.SimpleDateFormat +import javax.inject.Inject + +@HiltViewModel +class AdminViewModel @Inject constructor( + @ApplicationContext private val context: Context, + private val api: LoginApi, + private val dataStoreManager: AppModule.DataStoreManager +) : ViewModel() { + private val _state = MutableStateFlow(AdminMainState()) + val state = _state.asStateFlow() + + private val _stateL = MutableStateFlow(AdminState()) + val stateL = _stateL.asStateFlow() + + private val dfo = SimpleDateFormat("yyyy-MM-dd HH:mm") + private val dfi= SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss") + + fun loadPersonInfo(username: String, onSuccess: () -> Unit) { + viewModelScope.launch { + try { + val info = api.info(username) + _state.update { + AdminMainState( + fullName = info.name, + photo = info.photo, + position = info.position, + lastVisit = dfo.format(dfi.parse(info.lastVisit)!!), + error = null + ) + } + onSuccess() + } catch (httpException: HttpException) { + try { + httpException.response()?.errorBody()?.string()?.let { errorString -> + val gson = GsonBuilder().create() + val errorDto = gson.fromJson(errorString, ErrorDto::class.java) + _state.update { + AdminMainState( + error = errorDto.error + ) + } + } + } catch (e: Exception) { + _state.update { AdminMainState(error = httpException.message()) } + } + } catch (e: Exception) { + _state.update { AdminMainState(error = "Unknown error: ${e.message}") } + } + } + } + + fun onUsernameChanged(username: String) = + _stateL.update { it.copy(isLoginEnabled = isUsernameValid(username), error = null) } + + companion object { + fun isUsernameValid(username: String): Boolean { + if (username.isEmpty() || username.length < 3 || username.first().isDigit()) { + return false + } + return username.all { it.isLetterOrDigit() && it.isAsciiPrintable() } + } + + private fun Char.isAsciiPrintable(): Boolean { + return this.code in 32..126 + } + } +} diff --git a/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt b/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt index 2dbeab1..a86f8a1 100644 --- a/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt @@ -10,6 +10,7 @@ import com.squareup.picasso.Picasso import dagger.hilt.android.AndroidEntryPoint import ru.myitschool.work.R import ru.myitschool.work.databinding.FragmentMainBinding +import ru.myitschool.work.ui.admin.AdminDestination import ru.myitschool.work.ui.login.LoginDestination import ru.myitschool.work.ui.qr.result.QrResultDestination import ru.myitschool.work.ui.qr.scan.QrScanDestination @@ -43,7 +44,7 @@ class MainFragment: Fragment(R.layout.fragment_main) { findNavController().navigate(QrScanDestination) } binding.admin.setOnClickListener{ - + findNavController().navigate(AdminDestination) } } subscribe() diff --git a/app/src/main/res/layout/fragment_admin.xml b/app/src/main/res/layout/fragment_admin.xml new file mode 100644 index 0000000..e419363 --- /dev/null +++ b/app/src/main/res/layout/fragment_admin.xml @@ -0,0 +1,141 @@ + + + + + +