Compare commits

..

1 Commits

Author SHA1 Message Date
94245f656c day1_commit_1.3_strings_updated 2025-02-18 20:18:20 +03:00
41 changed files with 412 additions and 1433 deletions

View File

@ -1,82 +0,0 @@
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-backups14802231755905846813
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

View File

@ -1,82 +0,0 @@
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-backups11164380389062662786
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

View File

@ -1,82 +0,0 @@
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-backups8341820686666786180
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

View File

@ -1,82 +0,0 @@
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-backups1146231745401124119
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

122
README.md
View File

@ -1 +1,121 @@
Figma: https://www.figma.com/design/v9YlfUjxz6ChHS5mNWSQPN/TheDevs_Final?node-id=2-3&t=Hnk1mGCVo7joisAC-1
[![Android Studio version](https://img.shields.io/endpoint?url=https%3A%2F%2Fsicampus.ru%2Fgitea%2Fcore%2Fdocs%2Fraw%2Fbranch%2Fmain%2Fandroid-studio-label.json)](https://sicampus.ru/gitea/core/docs/src/branch/main/how-upload-project.md)
# НТО 2024. II отборочный этап. Командные задани — клиентская часть
## 📖 Предыстория
В компании S контроль доступа в офис осуществляется с помощью СКУД (системы контроля управления доступом). На данный момент у каждого сотрудника компании есть карта-пропуск с NFC меткой. А у каждой входной двери есть считыватель с обеих сторон. При поднесении карты к считывателю, дверь открывается, а информация о времени входа или выхода сотрудника фиксируется в базе данных.
Администрации компании S требуется мобильное приложение, как для рядовых сотрудников, так и для администрации с возможностью просмотра посещений и работой электронного пропуска как временной замены обычного (при помощи сканировании QR кода, который находится на считывателе). Поскольку в приложении есть возможность использовать телефон как пропуск - то к данному приложению повышенные требования к безопасности всех данных, находящихся внутри него.
## 📋 Системные требования
| **Параметр** | **Требование** |
|-----------------------------|---------------------------------------|
| **Минимальная версия Android** | 9.0 (API 28) |
| **Целевая версия Android** | 14 (API 34) |
| **Поддерживаемые устройства** | смартфоны, планшеты |
| **Ориентация экранов** | портретная |
| **Языки** | русский, английский |
| **Разрешения** | доступ к интернету, камера (при необходимости) |
## 📱 Техническое задание
Требуется разработать нативное мобильное приложение, которое будет содержать следующие экраны.
### 1. Экран авторизации
> Данный экран должен быть показан при первом входе в приложение, а также в ситуациях, когда пользователь не зарегистрировался в приложении.
#### Элементы, которые должны присутствовать на экране:
- Поле ввода (`id/username`), в котором пользователю необходимо ввести свой логин.
- Кнопка входа (`id/login`), по нажатию на которую пользователь авторизуется в системе.
- По умолчанию скрытое (`GONE`) текстовое поле с ошибкой (`id/error`).
#### Требования к компонентам:
1. В пустом поле ввода должна отображаться подсказка, что требуется ввести пользователю.
2. Если хотя бы одно из условий ниже соблюдено - кнопка должна быть неактивной:
- Поле ввода пустое.
- Количество символов менее 3х.
- Логин начинается с цифры.
- Логин содержит символы, отличные от латинского алфавита и цифр.
3. Поле ввода и кнопку должно быть видно при раскрытии клавиатуры.
4. - При нажатии на кнопку входа необходимо проверить, что данный пользователь существует с помощью запроса `api/<LOGIN>/auth` (подробное описание представлено в техническом задании серверной части).
5. В случае отсутствия логина или любой другой неполадки - необходимо вывести ошибку, пока пользователь не изменит текстовое поле или повторно не нажмёт на кнопку.
6. После нажатия на кнопку - логин должен быть сохранён и при следующем открытии приложения экран авторизации не должен быть показан.
7. После нажатия на кнопку - при нажатии стрелки назад - экран авторизации не должен быть показан повторно.
8. Экран авторизации показывается только в случае, если пользователь неавторизован.
### 2. Главный экран
> Данный экран содержит общую информацию о пользователе:
>- ФИО
>- Фото
>- Должность
>- Время последнего входа
#### Элементы, которые должны присутствовать на экране:
- Текстовое поле (`id/fullname`), в котором написано имя пользователя.
- Изображение (`id/photo`), на котором отображено фото пользователя.
- Текстовое поле (`id/position`), в котором написана должность пользователя.
- Текстовое поле (`id/lastEntry`), в котором написана дата и время последнего входа пользователя.
- Кнопка (`id/logout`) для выхода пользователя из аккаунта.
- Кнопка (`id/refresh`) для обновления данных.
- Кнопка (`id/scan`) для сканирования QR кода.
- По умолчанию скрытое текстовое поле с ошибкой (`id/error`).
#### Требования к компонентам:
- В случае любой ошибки необходимо скрыть все элементы, кроме текстового поля с ошибкой и кнопки обновления данных.
- Для получения данных необходимо использовать сетевой запрос `/api/<LOGIN>/info`.
- Формат даты и времени последнего входа пользователя: `yyyy-MM-dd HH:mm` (например: 2024-02-31 08:31). Время необходимо отображать с сервера, без поправок на часовой пояс или локальное смещение.
- При нажатии на кнопку выход все данные (если есть) пользователя должны быть очищены, а приложение должно открыть экран авторизации.
- При нажатии кнопки сканирования необходимо открыть экран сканирования QR кода.
- При нажатии на кнопку обновления данных - необходимо повторно вызывать сетевой запрос для получения актуальных данных.
### 3. Экран сканирования QR-кода
> Данный экран позволяет отсканировать код на турникете и войти с помощью смартфона. В данном случае данный экран будет уже написан и представлен dам в готовом виде в заготовке. Вам необходимо только подписаться на его результат с помощью **Result API** и обработать считанные данные из QR кода. **Данный экран нельзя модифицировать. Он поставляется как есть.**
### 4. Экран с результатом сканирования QR кода
> На данном экране необходимо вывести успешность или неуспешность входа с помощью метода QR кода.
#### Элементы, которые должны присутствовать на экране:
- Текстовое поле (`id/result`), где содержится текст об успешности или неуспешности входа.
- Кнопка (`id/close`) для закрытия данного экрана.
#### Требования к компонентам:
- В случае, если результат пришёл пустым или со статусом “Отмена” - необходимо вывести пользователю текст:
*"Вход был отменён/Operation was cancelled"*
- В случае, если данные пришли, то необходимо их отправить на сервер с запросом `api/<LOGIN>/open`, добавив данные из результата и получить ответ.
- Если сервер ответил успешно - то отображаем текст:
*"Успешно/Success"*
- Если сервер ответил любой ошибкой - то отображаем текст:
*"Что-то пошло не так/Something wrong"*
- Кнопка закрытия всегда открывает главный экран.
## 🛠 Решение
Необходимо загрузить свое решение в систему [по ссылке](https://innovationcampus.ru/lms/mod/quiz/view.php?id=2149).
Отметим, что работу необходимо осуществлять в представленных проектах-заготовках (шаблонах).
## ✅ Особенности оценивания
Оценивание происходит с помощью автоматической системы тестирования, которая в автоматическом режиме находит элементы и взаимодействует с ними (именно для этого у каждого элемента указан уникальный идентификатор, по которому будет производится поиск). Каждый тест происходит с чистой установки приложения.
В случае тестирования сервера на него поочередно отправляются команды, описанные в API и ожидаются определенные корректные ответы.
Сервер и приложение тестируются независимо.

View File

@ -1,30 +1,30 @@
plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-kapt") // Добавлено для KAPT
id("dagger.hilt.android.plugin") // Используйте этот синтаксис для Hilt
id("org.jetbrains.kotlin.plugin.serialization") version Version.Kotlin.language // Убедитесь, что версия актуальна
kotlinAndroid
androidApplication
jetbrainsKotlinSerialization version Version.Kotlin.language
kotlinAnnotationProcessor
id("com.google.dagger.hilt.android").version("2.51.1")
}
val packageName = "ru.myitschool.work"
android {
namespace = packageName
compileSdk = 35 // Обновлено до 35
compileSdk = Version.Android.Sdk.compile
defaultConfig {
applicationId = packageName
minSdk = Version.Android.Sdk.min
targetSdk = 35 // Обновлено до 35
targetSdk = Version.Android.Sdk.target
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildFeatures {
viewBinding = true
}
buildFeatures.viewBinding = true
compileOptions {
sourceCompatibility = Version.Kotlin.javaSource
@ -37,41 +37,30 @@ android {
}
dependencies {
// Retrofit and OkHttp
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
implementation ("com.squareup.okhttp3:okhttp:4.9.0")
implementation ("com.github.bumptech.glide:glide:4.15.1")
kapt ("com.github.bumptech.glide:compiler:4.15.1")
// Glide
implementation("com.github.bumptech.glide:glide:4.15.1")
kapt("com.github.bumptech.glide:compiler:4.15.1")
defaultLibrary()
// AndroidX Libraries
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.10.0")
implementation("androidx.activity:activity:1.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
implementation(Dependencies.AndroidX.activity)
implementation(Dependencies.AndroidX.fragment)
implementation(Dependencies.AndroidX.constraintLayout)
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
// Hilt dependencies
implementation("com.google.dagger:hilt-android:2.51.1")
kapt("com.google.dagger:hilt-android-compiler:2.51.1")
// Navigation
implementation(Dependencies.AndroidX.Navigation.fragment)
implementation(Dependencies.AndroidX.Navigation.navigationUi)
// DataStore
implementation("androidx.datastore:datastore-preferences:1.1.1")
implementation(Dependencies.Retrofit.library)
implementation(Dependencies.Retrofit.gsonConverter)
// ML Kit
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("com.google.mlkit:barcode-scanning:17.3.0")
// CameraX
val cameraX = "1.3.4"
implementation("androidx.camera:camera-core:$cameraX")
implementation("androidx.camera:camera-camera2:$cameraX")
@ -79,13 +68,11 @@ dependencies {
implementation("androidx.camera:camera-view:$cameraX")
implementation("androidx.camera:camera-mlkit-vision:1.4.0-rc04")
// Picasso
implementation("com.squareup.picasso:picasso:2.8")
// Kotlin Serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
val hilt = "2.51.1"
implementation("com.google.dagger:hilt-android:$hilt")
kapt("com.google.dagger:hilt-android-compiler:$hilt")
}
kapt {
correctErrorTypes = true
}
}

View File

@ -23,7 +23,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Default"
tools:targetApi="34">
tools:targetApi="31">
<activity
android:name=".ui.RootActivity"
android:exported="true">
@ -33,7 +33,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -4,9 +4,4 @@ import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class App : Application() {
override fun onCreate() {
super.onCreate()
SessionManager.init(this) // Инициализация SessionManager
}
}
class App : Application()

View File

@ -1,41 +1,5 @@
package ru.myitschool.work
import android.content.Context
import android.content.SharedPreferences
import java.util.Base64
object SessionManager {
private const val PREF_NAME = "user_session"
private const val KEY_USER_LOGIN = "user_login"
private const val KEY_USER_ROLE = "user_role"
private lateinit var preferences: SharedPreferences
fun init(context: Context) {
preferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
}
var userLogin: String?
get() = preferences.getString(KEY_USER_LOGIN, null)
set(value) {
preferences.edit().putString(KEY_USER_LOGIN, value).apply()
}
var userRole: String?
get() = preferences.getString(KEY_USER_ROLE, null)
set(value) {
preferences.edit().putString(KEY_USER_ROLE, value).apply()
}
fun getAuthHeader(): String {
val username = userLogin ?: return ""
val password = "password123" // Замените на ваш пароль
val credential = Base64.getEncoder().encodeToString("$username:$password".toByteArray())
return "Basic $credential"
}
fun clearSession() {
userLogin = null
userRole = null
}
var userLogin: String = ""
}

View File

@ -1,7 +0,0 @@
package ru.myitschool.work.api
data class AccessLog(
val scanTime: String,
val readerId: String,
val accessType: String // "карта" или "смартфон"
)

View File

@ -1,13 +1,13 @@
package ru.myitschool.work.api
import LoggingInterceptor
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import ru.myitschool.work.api.ApiService
import ru.myitschool.work.core.Constants
import javax.inject.Singleton
@Module
@ -16,27 +16,9 @@ object ApiModule {
@Provides
@Singleton
fun provideOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(authInterceptor) // Добавляем интерсептор аутентификации
.addInterceptor(LoggingInterceptor()) // Добавляем интерсептор логирования
.build()
}
@Provides
@Singleton
fun provideAuthInterceptor(): AuthInterceptor {
val username = "pivanov" // Замените на ваш логин
val password = "password123" // Замените на ваш пароль
return AuthInterceptor(username, password)
}
@Provides
@Singleton
fun provideRetrofit(client: OkHttpClient): Retrofit {
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("http://10.6.66.110:8080/") // Убедитесь, что URL корректен
.client(client)
.baseUrl(Constants.SERVER_ADDRESS)
.addConverterFactory(GsonConverterFactory.create())
.build()
}

View File

@ -3,57 +3,18 @@ package ru.myitschool.work.api
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.PATCH
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
interface ApiService {
// Метод для аутентификации
@GET("/api/auth")
suspend fun authenticate(
@Query("login") login: String,
@Query("password") password: String
): Response<Unit> // Измените Response<String> на Response<Unit>
@GET("api/{login}/auth")
suspend fun authenticate(@Path("login") login: String): Response<Unit>
@GET("/api/{login}/info")
suspend fun getUserInfo(
@Path("login") login: String,
@Header("Authorization") authHeader: String
): Response<EmployeeData>
@GET("api/{login}/info")
suspend fun getUserInfo(@Path("login") login: String): Response<Map<String, Any>> // Возвращаем Map вместо UserInfo
@GET("/api/employee/{login}") // Получение информации о сотруднике
suspend fun getEmployeeInfo(@Path("login") login: String): Response<EmployeeData>
@PATCH("/api/open") // Открыть дверь
suspend fun openDoor(
@Header("Authorization") authHeader: String,
@Body request: OpenDoorRequest
): Response<Unit> // Измените Response<String> на Response<Unit>
@POST("/api/employee/toggleAccess") // Метод для блокировки/разблокировки доступа
suspend fun toggleAccess(@Body request: ToggleAccessRequest): Response<Unit>
@GET("/api/workers") // Получить всех сотрудников
suspend fun getAllWorkers(): Response<List<EmployeeData>>
@PATCH("api/{login}/open")
suspend fun openDoor(@Path("login") login: String, @Body body: OpenDoorRequest): Response<Unit>
}
// Модель данных для информации о сотруднике
data class EmployeeData(
val name: String,
val position: String,
val lastVisit: String,
val avatarUrl: String? // Добавьте это поле
)
// Модель данных для запроса блокировки/разблокировки доступа
data class ToggleAccessRequest(
val login: String, // Логин сотрудника
val action: String // Действие: "block" или "unblock"
)
// Модель данных для запроса открытия двери
data class OpenDoorRequest(
val value: Long // Код для открытия двери
)
data class OpenDoorRequest(val value: String)

View File

@ -1,16 +0,0 @@
package ru.myitschool.work.api
import okhttp3.Interceptor
import okhttp3.Response
import java.util.Base64
class AuthInterceptor(private val username: String, private val password: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val credential = Base64.getEncoder().encodeToString("$username:$password".toByteArray())
val newRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Basic $credential")
.build()
return chain.proceed(newRequest)
}
}

View File

@ -1,15 +0,0 @@
import okhttp3.Interceptor
import okhttp3.Response
import android.util.Log
class LoggingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
Log.d("LoggingInterceptor", "Sending request to ${request.url} with headers ${request.headers}")
val response = chain.proceed(request)
Log.d("LoggingInterceptor", "Received response for ${response.request.url} with code ${response.code}")
return response
}
}

View File

@ -1,6 +1,5 @@
package ru.myitschool.work.core
// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
object Constants {
const val SERVER_ADDRESS = "http://10.6.66.110:8080"
//,
const val SERVER_ADDRESS = "const val SERVER_ADDRESS = \"http://localhost:8090\"\n"
}

View File

@ -1,34 +0,0 @@
package ru.myitschool.work.ui.main
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import ru.myitschool.work.R
import ru.myitschool.work.api.AccessLog
class AccessLogAdapter(private val accessLogs: List<AccessLog>) : RecyclerView.Adapter<AccessLogAdapter.AccessLogViewHolder>() {
class AccessLogViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val scanTime: TextView = itemView.findViewById(R.id.scan_time)
val readerId: TextView = itemView.findViewById(R.id.reader_id)
val accessType: TextView = itemView.findViewById(R.id.access_type)
}
//.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccessLogViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_access_log, parent, false)
return AccessLogViewHolder(view)
}
override fun onBindViewHolder(holder: AccessLogViewHolder, position: Int) {
val log = accessLogs[position]
holder.scanTime.text = log.scanTime
holder.readerId.text = log.readerId
holder.accessType.text = log.accessType
}
override fun getItemCount(): Int {
return accessLogs.size
}
}

View File

@ -1,107 +0,0 @@
package ru.myitschool.work.ui.main
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import ru.myitschool.work.R
import ru.myitschool.work.api.ApiService
import ru.myitschool.work.api.ToggleAccessRequest
import ru.myitschool.work.core.Constants
import ru.myitschool.work.databinding.FragmentAdminBinding
class AdminFragment : Fragment(R.layout.fragment_admin) {
private var _binding: FragmentAdminBinding? = null
private val binding get() = _binding!!
private val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(Constants.SERVER_ADDRESS)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
private var isAccessBlocked: Boolean = false // Переменная для отслеживания состояния доступа
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentAdminBinding.bind(view)
setupUI()
}
//.
private fun setupUI() {
binding.viewEmployeeInfo.setOnClickListener {
val login = binding.employeeLogin.text.toString()
if (login.isNotEmpty()) {
fetchEmployeeInfo(login)
} else {
Toast.makeText(requireContext(), "Введите логин сотрудника", Toast.LENGTH_SHORT).show()
}
}
binding.toggleAccess.setOnClickListener {
val login = binding.employeeLogin.text.toString()
if (login.isNotEmpty()) {
// Определяем действие на основе текущего состояния доступа
val action = if (isAccessBlocked) "unblock" else "block"
toggleEmployeeAccess(login, action)
} else {
Toast.makeText(requireContext(), "Введите логин сотрудника", Toast.LENGTH_SHORT).show()
}
}
}
private fun fetchEmployeeInfo(login: String) {
lifecycleScope.launch {
try {
val response = apiService.getEmployeeInfo(login)
if (response.isSuccessful) {
val employeeData = response.body()
employeeData?.let {
binding.employeeInfo.text = "Имя: ${it.name}, Должность: ${it.position}, Последний визит: ${it.lastVisit}"
binding.employeeInfo.visibility = View.VISIBLE
binding.toggleAccess.visibility = View.VISIBLE
// Здесь можно установить состояние доступа, если оно доступно
isAccessBlocked = false // Предположим, что доступ не заблокирован по умолчанию
}
} else {
Toast.makeText(requireContext(), "Ошибка получения данных", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show()
}
}
}
private fun toggleEmployeeAccess(login: String, action: String) {
val request = ToggleAccessRequest(login, action)
lifecycleScope.launch {
try {
val response = apiService.toggleAccess(request)
if (response.isSuccessful) {
isAccessBlocked = !isAccessBlocked // Переключаем состояние доступа
val message = if (action == "block") "Доступ заблокирован" else "Доступ разблокирован"
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(requireContext(), "Ошибка изменения доступа", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show()
}
}
}
override fun onDestroyView() {
_binding = null // Освобождаем binding, когда представление уничтожается
super.onDestroyView()
}
}

View File

@ -1,34 +1,34 @@
package ru.myitschool.work.ui.main
import android.graphics.Bitmap
import android.graphics.BitmapFactory
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.setFragmentResultListener
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.squareup.picasso.Picasso
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import ru.myitschool.work.R
import ru.myitschool.work.api.ApiService
import ru.myitschool.work.api.EmployeeData
import ru.myitschool.work.core.Constants
import ru.myitschool.work.databinding.FragmentMainBinding
import ru.myitschool.work.SessionManager
import kotlinx.coroutines.withContext
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import ru.myitschool.work.R
import ru.myitschool.work.SessionManager
import ru.myitschool.work.api.ApiService
import ru.myitschool.work.core.Constants
import ru.myitschool.work.databinding.FragmentMainBinding
import ru.myitschool.work.ui.qr.scan.QrScanDestination
import java.net.HttpURLConnection
import java.net.URL
class MainFragment : Fragment(R.layout.fragment_main) {
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!
private val binding get() = _binding
private lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
apiService = Retrofit.Builder()
private val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(Constants.SERVER_ADDRESS)
.addConverterFactory(GsonConverterFactory.create())
.build()
@ -37,10 +37,10 @@ class MainFragment : Fragment(R.layout.fragment_main) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentMainBinding.bind(view)
_binding = FragmentMainBinding.bind(view) // Подключаем binding
setupUI()
fetchUserInfo()
fetchUserData()
// Проверяем, есть ли результат QR
checkQrResult()
@ -52,93 +52,117 @@ class MainFragment : Fragment(R.layout.fragment_main) {
val qrData = QrScanDestination.getDataIfExist(bundle)
if (qrData != null) {
// Если данные QR есть, переходим на экран с результатом
val resultBundle = QrScanDestination.packToBundle(qrData)
findNavController().navigate(R.id.qrResultFragment, resultBundle)
} else {
Toast.makeText(requireContext(), "QR данные не найдены", Toast.LENGTH_SHORT).show()
findNavController().navigate(R.id.qrResultFragment)
}
}
}
private fun setupUI() {
binding.refresh.setOnClickListener {
fetchUserInfo()
}
binding.scanQrCode?.setOnClickListener {
// Переход к экрану сканирования QR-кода
findNavController().navigate(R.id.qrScanFragment) // Убедитесь, что у вас есть правильный ID для навигации
}
// Проверяем роль пользователя и показываем кнопку AdminPanel, если роль admin
if (SessionManager.userRole == "admin") {
binding.adminPanelButton?.visibility = View.VISIBLE
binding.adminPanelButton?.setOnClickListener {
findNavController().navigate(R.id.adminFragment) // Переход к экрану администратора
}
} else {
binding.adminPanelButton?.visibility = View.GONE // Скрываем кнопку, если не admin
// Проверяем, что binding не null, прежде чем устанавливать слушателей
binding?.apply {
refresh.setOnClickListener { fetchUserData() }
logout.setOnClickListener { logout() }
scan.setOnClickListener { navigateToQrScan() }
}
}
private fun fetchUserInfo() {
private fun fetchUserData() {
lifecycleScope.launch {
val login = SessionManager.userLogin ?: run {
binding.error.text = "Пользователь не авторизован"
binding.error.visibility = View.VISIBLE
return@launch
}
val authHeader = SessionManager.getAuthHeader() ?: run {
binding.error.text = "Ошибка авторизации"
binding.error.visibility = View.VISIBLE
return@launch
}
showError(null) // Скрыть ошибку, если она была
try {
val response = apiService.getUserInfo(login, authHeader)
// Логируем код ответа
Log.d("MainFragment", "Response code: ${response.code()}")
val response = apiService.getUserInfo(SessionManager.userLogin) // Получаем данные пользователя
if (response.isSuccessful) {
val employeeData = response.body()
employeeData?.let {
binding.fullname.text = it.name
binding.position.text = it.position
binding.lastEntry.text = it.lastVisit
response.body()?.let { data ->
// Извлекаем значения из Map
val fullName = data["name"] as? String ?: "Неизвестно"
val position = data["position"] as? String ?: "Неизвестно"
val lastVisit = data["lastVisit"] as? String ?: "Неизвестно"
val photoUrl = data["photo"] as? String ?: ""
// Логируем URL аватара
Log.d("MainFragment", "Avatar URL: ${it.avatarUrl}")
// Загрузка аватара с помощью Picasso
if (it.avatarUrl != null) {
Picasso.get()
.load(it.avatarUrl)
.placeholder(R.drawable.ic_refresh) // Замените на ваш ресурс-заполнитель
.error(R.drawable.ic_close) // Замените на ваш ресурс ошибки
.into(binding.photo)
binding.photo.visibility = View.VISIBLE
} else {
binding.photo.visibility = View.GONE // Скрыть, если URL нет
}
// Показываем кнопку "Сканировать QR-код" после успешного получения данных
binding.scanQrCode?.visibility = View.VISIBLE
// Обновляем UI
updateUI(fullName, position, lastVisit, photoUrl)
}
} else {
binding.error.text = "Ошибка получения данных: ${response.message()}"
binding.error.visibility = View.VISIBLE
showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились
}
} catch (e: Exception) {
Log.e("MainFragment", "Error fetching user info", e)
binding.error.text = "Ошибка сети: ${e.message}"
binding.error.visibility = View.VISIBLE
showError(e.localizedMessage) // Показываем ошибку при исключении
}
}
}
private fun updateUI(fullName: String, position1: String, lastVisit: String, photoUrl: String) {
// Проверяем, что binding не null, прежде чем обновлять UI
binding?.apply {
fullname.text = fullName
position.text = position1
lastEntry.text = lastVisit
if (photoUrl.isNotEmpty()) {
// Загружаем изображение
lifecycleScope.launch {
val bitmap = loadImageFromUrl(photoUrl)
bitmap?.let { photo.setImageBitmap(it) }
}
}
// Показываем элементы
fullname.visibility = View.VISIBLE
position.visibility = View.VISIBLE
lastEntry.visibility = View.VISIBLE
photo.visibility = View.VISIBLE
logout.visibility = View.VISIBLE
scan.visibility = View.VISIBLE
}
}
private suspend fun loadImageFromUrl(urlString: String): Bitmap? {
return withContext(Dispatchers.IO) {
try {
val url = URL(urlString)
val connection = url.openConnection() as HttpURLConnection
connection.doInput = true
connection.connect()
val inputStream = connection.inputStream
BitmapFactory.decodeStream(inputStream)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
private fun showError(message: String?) {
// Проверяем, что binding не null, прежде чем обновлять ошибку
binding?.apply {
if (message != null) {
error.text = message
error.visibility = View.VISIBLE
// Скрываем остальные элементы, когда возникает ошибка
fullname.visibility = View.GONE
position.visibility = View.GONE
lastEntry.visibility = View.GONE
photo.visibility = View.GONE
logout.visibility = View.GONE
scan.visibility = View.GONE
} else {
error.visibility = View.GONE
}
}
}
private fun logout() {
// Очистите данные пользователя
Toast.makeText(requireContext(), getString(R.string.logged_out), Toast.LENGTH_SHORT).show()
findNavController().navigate(R.id.loginFragment) // Переход на экран входа
}
private fun navigateToQrScan() {
findNavController().navigate(R.id.qrScanFragment) // Переход на экран сканирования QR
}
override fun onDestroyView() {
_binding = null
_binding = null // Освобождаем binding, когда представление уничтожается
super.onDestroyView()
}
}

View File

@ -1,40 +1,35 @@
package ru.myitschool.work.ui.main
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.StateFlow
import kotlinx.coroutines.launch
import ru.myitschool.work.SessionManager
import ru.myitschool.work.api.ApiService
import ru.myitschool.work.api.EmployeeData
import javax.inject.Inject
@HiltViewModel
class MainViewModel @Inject constructor(
private val apiService: ApiService
) : ViewModel() {
private val _userInfoState = MutableStateFlow<Map<String, Any>?>(null)
val userInfoState: StateFlow<Map<String, Any>?> = _userInfoState
private val _employeeData = MutableStateFlow<EmployeeData?>(null)
val employeeData: StateFlow<EmployeeData?> get() = _employeeData
init {
loadUserData()
}
private val _errorMessage = MutableStateFlow<String?>(null)
val errorMessage: StateFlow<String?> get() = _errorMessage
fun fetchUserInfo(login: String, authHeader: String) {
private fun loadUserData() {
viewModelScope.launch {
try {
val response = apiService.getUserInfo(login, authHeader)
val login = SessionManager.userLogin
if (login != null) {
val response = apiService.getUserInfo(login)
if (response.isSuccessful) {
_employeeData.value = response.body()
} else {
_errorMessage.value = "Ошибка получения данных: ${response.message()}"
_userInfoState.value = response.body()
}
} catch (e: Exception) {
Log.e("MainViewModel", "Error fetching user info", e)
_errorMessage.value = "Ошибка сети: ${e.message}"
}
}
}
}
}

View File

@ -22,7 +22,7 @@ class RootActivity : AppCompatActivity() {
navController.setGraph(R.navigation.nav_graph) // Устанавливаем граф навигации
}
// Настройка кнопки "Назад"
// Настраиваем кнопку "Назад"
onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
@ -44,4 +44,4 @@ class RootActivity : AppCompatActivity() {
}
return popBackResult || super.onSupportNavigateUp()
}
}
}

View File

@ -1,6 +1,5 @@
package ru.myitschool.work.ui.login
import ru.myitschool.work.api.ApiService
import android.os.Bundle
import android.text.Editable
import android.text.InputType
@ -8,19 +7,17 @@ import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import ru.myitschool.work.R
import ru.myitschool.work.SessionManager
import ru.myitschool.work.databinding.FragmentLoginBinding
import ru.myitschool.work.utils.collectWhenStarted
import ru.myitschool.work.utils.visibleOrGone
import ru.myitschool.work.utils.AuthPreferences
import kotlinx.coroutines.launch
import androidx.lifecycle.lifecycleScope
@AndroidEntryPoint
class LoginFragment : Fragment(R.layout.fragment_login) {
@ -40,7 +37,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // Убедитесь, что этот вызов находится здесь
super.onViewCreated(view, savedInstanceState)
_binding = FragmentLoginBinding.bind(view)
setupUI()
@ -55,11 +52,9 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
binding.login.setOnClickListener {
val username = binding.username.text.toString()
val password = binding.password.text.toString() // Получаем пароль
performLogin(username, password) // Передаем пароль в метод performLogin
performLogin(username) // Вызываем метод performLogin
}
// Изначально скрываем индикаторы
binding.loading.visibleOrGone(false)
binding.error.visibleOrGone(false)
}
@ -77,44 +72,29 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
})
}
private fun performLogin(username: String, password: String) {
private fun performLogin(username: String) {
lifecycleScope.launch {
binding.loading.visibleOrGone(true) // Показываем индикатор загрузки
viewModel.authenticate(username, password) // Передаем пароль в метод authenticate
viewModel.authenticate(username) // Вызываем метод authenticate из ViewModel
}
}
private fun subscribe() {
lifecycleScope.launch {
viewModel.state.collect { state ->
binding.loading.visibleOrGone(false)
viewModel.state.collectWhenStarted(this) { state ->
binding.loading.visibleOrGone(false)
if (state.maintenance) {
showMaintenanceDialog() // Показываем диалог о техработах
} else if (state.success) {
binding.error.visibility = View.GONE
authPreferences.saveLoginState(true)
authPreferences.saveLogin(binding.username.text.toString()) // Сохраняем логин
Toast.makeText(context, "Авторизация прошла успешно", Toast.LENGTH_SHORT).show()
navigateToMainScreen() // Перенаправление на следующий экран
} else if (state.error != null) {
binding.error.text = state.error
binding.error.visibility = View.VISIBLE
}
if (state.error != null) {
binding.error.text = state.error
binding.error.visibility = View.VISIBLE
} else if (state.success) {
binding.error.visibility = View.GONE
authPreferences.saveLoginState(true)
authPreferences.saveLogin(binding.username.text.toString()) // Сохраняем логин
Toast.makeText(context, "Авторизация прошла успешно", Toast.LENGTH_SHORT).show()
navigateToMainScreen()
}
}
}
private fun showMaintenanceDialog() {
AlertDialog.Builder(requireContext())
.setTitle("Технические работы")
.setMessage("Проводятся техработы, пожалуйста, подождите.")
.setPositiveButton("ОК ") { dialog, _ -> dialog.dismiss() }
.setCancelable(false)
.show()
}
private fun navigateToMainScreen() {
try {
findNavController().apply {
@ -122,7 +102,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
navigate(R.id.mainFragment)
}
} catch (e: Exception) {
Log.e("LoginFragment", "Nav_err", e)
Log.e("LF", "Nav_err", e)
Toast.makeText(context, "Ошибка перехода", Toast.LENGTH_SHORT).show()
}
}

View File

@ -1,70 +1,54 @@
package ru.myitschool.work.ui.login
import ru.myitschool.work.api.ApiService
import android.util.Log
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import ru.myitschool.work.SessionManager
import ru.myitschool.work.api.ApiService
import ru.myitschool.work.core.Constants
import javax.inject.Inject
@HiltViewModel
class LoginViewModel @Inject constructor(
private val apiService: ApiService
@ApplicationContext private val context: Context,
) : ViewModel() {
private val _state = MutableStateFlow(LoginState())
val state: StateFlow<LoginState> get() = _state
val state = _state.asStateFlow()
fun authenticate(username: String, password: String) {
Log.d("LoginViewModel", "Authenticating user: $username") // Логируем начало аутентификации
private val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(Constants.SERVER_ADDRESS)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
fun authenticate(username: String) {
if (isValidUsername(username)) {
viewModelScope.launch {
try {
Log.d("LoginViewModel", "Sending authentication request to server") // Логируем отправку запроса
val response = apiService.authenticate(username, password)
Log.d("LoginViewModel", "Response code: ${response.code()}") // Логируем код ответа
// Проверяем код ответа
when (response.code()) {
200 -> {
SessionManager.userLogin = username
_state.value = LoginState(success = true) // Успешная авторизация
Log.d("LoginViewModel", "Authentication successful") // Логируем успешную аутентификацию
}
400 -> {
_state.value = LoginState(error = "Ошибка авторизации: Неверные учетные данные.")
Log.d("LoginViewModel", "Authentication failed: Invalid credentials") // Логируем ошибку
}
else -> {
_state.value = LoginState(error = "Ошибка авторизации: ${response.message()}")
Log.d("LoginViewModel", "Authentication failed: ${response.message()}") // Логируем ошибку
}
}
} catch (e: Exception) {
e.printStackTrace()
_state.value = LoginState(error = "Ошибка сети. Проверьте подключение к интернету.")
Log.e("LoginViewModel", "Network error: ${e.message}") // Логируем ошибку сети
val response = apiService.authenticate(username)
if (response.isSuccessful) {
SessionManager.userLogin = username
_state.value = LoginState(success = true)
} else {
_state.value = LoginState(error = "Ошибка авторизации")
}
}
} else {
_state.value = LoginState(error = "Неправильный логин")
Log.d("LoginViewModel", "Invalid username: $username") // Логируем неправильный логин
}
}
private fun isValidUsername(username: String): Boolean {
return username.isNotEmpty() // Пример проверки логина
return username.length >= 3 && !username.first().isDigit() && username.all { it.isLetterOrDigit() }
}
}
// Состояние аутентификации
data class LoginState(
val success: Boolean = false, // Успешность аутентификации
val error: String? = null, // Сообщение об ошибке
val maintenance: Boolean = false // Состояние техобслуживания
)
data class LoginState(val success: Boolean = false, val error: String? = null)
}

View File

@ -5,10 +5,8 @@ import androidx.navigation.fragment.findNavController
import ru.myitschool.work.SessionManager
import ru.myitschool.work.api.OpenDoorRequest
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.util.Base64
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
@ -21,9 +19,7 @@ import ru.myitschool.work.core.Constants
import ru.myitschool.work.databinding.FragmentQrScanResultBinding
class QrResult : Fragment(R.layout.fragment_qr_scan_result) {
private var _binding: FragmentQrScanResultBinding? = null
private val binding get() = _binding!!
private lateinit var binding: FragmentQrScanResultBinding
private lateinit var apiService: ApiService
override fun onCreateView(
@ -31,7 +27,7 @@ class QrResult : Fragment(R.layout.fragment_qr_scan_result) {
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentQrScanResultBinding.inflate(inflater, container, false)
binding = FragmentQrScanResultBinding.inflate(inflater, container, false)
apiService = Retrofit.Builder()
.baseUrl(Constants.SERVER_ADDRESS)
.addConverterFactory(GsonConverterFactory.create())
@ -44,12 +40,8 @@ class QrResult : Fragment(R.layout.fragment_qr_scan_result) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Получаем данные из аргументов
val qrData = QrScanDestination.getDataIfExist(requireArguments())
Log.d("QrResult", "QR Data: $qrData") // Логируем полученные данные
if (qrData != null) {
binding.result.text = "Результат сканирования: $qrData" // Отображаем результат сканирования
sendRequestToServer(qrData)
} else {
binding.result.text = "Вход был отменён/Operation was cancelled"
@ -63,40 +55,15 @@ class QrResult : Fragment(R.layout.fragment_qr_scan_result) {
private fun sendRequestToServer(qrData: String) {
lifecycleScope.launch {
try {
val qrValue = qrData.toLong() // Преобразуем данные QR-кода в Long
val login = SessionManager.userLogin ?: "default_login" // Замените на ваш логин
val password = "your_password" // Замените на ваш пароль
val authHeader = "Basic " + Base64.encodeToString("$login:$password".toByteArray(), Base64.NO_WRAP)
// Логируем данные перед отправкой
Log.d("QrResult", "Sending request with QR value: $qrValue and authHeader: $authHeader")
// Создаем объект запроса
val request = OpenDoorRequest(qrValue)
// Вызываем метод openDoor с правильными параметрами
val response = apiService.openDoor(authHeader, request)
// Логируем код ответа и тело ответа
Log.d("QrResult", "Response code: ${response.code()}")
Log.d("QrResult", "Response body: ${response.body()}")
val response = apiService.openDoor(SessionManager.userLogin, OpenDoorRequest(qrData))
if (response.isSuccessful) {
binding.result.text = "Door Opened" // Сообщение о том, что дверь открыта
binding.result.text = "Успешно/Success"
} else {
binding.result.text = "Door Closed" // Сообщение о том, что дверь закрыта
binding.result.text = "Что-то пошло не так/Something wrong"
}
} catch (e: NumberFormatException) {
binding.result.text = "Некорректные данные QR-кода"
} catch (e: Exception) {
binding.result.text = "Что-то пошло не так/Something went wrong: ${e.message}"
Log.e("QrResult", "Error sending request to server", e)
binding.result.text = "Что-то пошло не так/Something wrong"
}
}
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}

View File

@ -1,24 +0,0 @@
package ru.myitschool.work.utils
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
// Функция для сбора данных из Flow, когда жизненный цикл находится в состоянии STARTED
fun <T> Flow<T>.collectWhenStarted(lifecycleOwner: LifecycleOwner, collector: (T) -> Unit) {
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
collect { value -> collector(value) }
}
}
}
// Функция для управления видимостью View
fun View?.visibleOrGone(isVisible: Boolean) {
this?.visibility = if (isVisible) View.VISIBLE else View.GONE
}

View File

@ -1,5 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#F0F0F0" />
<corners android:radius="16dp" />
</shape>

View File

@ -1,60 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_worker_username"
android:textSize="18sp"
android:layout_marginBottom="8dp" />
<EditText
android:id="@+id/employee_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/worker_username"
android:padding="12dp"
android:background="@drawable/ic_android_black_24dp"
android:inputType="text" />
<Button
android:id="@+id/view_employee_info"
android:layout_width="280dp"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="@string/watch_info_about_worker"
android:backgroundTint="@color/colorPrimary"
app:cornerRadius="16dp"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/employee_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textSize="16sp"
android:visibility="gone" />
<Button
android:id="@+id/toggle_access"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/block_or_unblock"
android:layout_marginTop="16dp"
android:visibility="gone" />
<Button
android:id="@+id/close"
android:layout_width="280dp"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="@string/back"
android:backgroundTint="@color/colorPrimary"
app:cornerRadius="16dp"
android:layout_marginTop="0dp" />
</LinearLayout>

View File

@ -1,56 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp"
android:background="@android:color/white">
<EditText
android:id="@+id/username"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:hint="@string/username_hint"
android:inputType="text"
android:padding="12dp"
android:background="@drawable/ic_android_black_24dp"
android:layout_marginBottom="16dp"/>
<EditText
android:id="@+id/password"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:hint="@string/password_hint"
android:inputType="textPassword"
android:padding="12dp"
android:background="@drawable/ic_android_black_24dp"
android:layout_marginBottom="16dp"/>
<Button
android:id="@+id/login"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="@string/login_button"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:padding="12dp"
app:cornerRadius="16dp"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:gravity="center_horizontal"
android:textColor="@android:color/holo_red_light"
android:layout_marginBottom="16dp"/>
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</LinearLayout>

View File

@ -1,124 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:gravity="bottom"
android:padding="16dp"
android:background="@android:color/white">
<LinearLayout
android:layout_width="250dp"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Поле для ФИО -->
<TextView
android:id="@+id/fullname"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="@string/fullname_label"
android:textSize="18sp"
android:layout_marginBottom="5dp"
android:visibility="gone" />
<!-- Фото пользователя. -->
<ImageView
android:id="@+id/photo"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:contentDescription="@string/photo_description"
android:layout_marginBottom="5dp"
android:visibility="gone" />
<!-- Поле для должности -->
<TextView
android:id="@+id/position"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/position_label"
android:layout_marginBottom="5dp"
android:visibility="gone" />
<!-- Поле для даты последнего входа -->
<TextView
android:id="@+id/lastEntry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2024-02-31 08:31"
android:layout_marginBottom="75dp"
android:visibility="gone" />
<!-- RecyclerView для списка проходов -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="gone" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Кнопка обновления -->
<Button
android:id="@+id/refresh"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/refresh"
app:cornerRadius="16dp"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:padding="12dp"/>
<!-- Поле ошибки -->
<TextView
android:id="@+id/error"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="@string/error_placeholder"
android:textColor="@android:color/holo_red_dark"
android:visibility="gone" />
<!-- Кнопки -->
<Button
android:id="@+id/scan"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/scan_qr_code"
app:cornerRadius="16dp"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:visibility="gone" />
<Button
android:id="@+id/logout"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/logout"
app:cornerRadius="16dp"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:visibility="gone" />
<Button
android:id="@+id/admin_panel"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/admin_panel"
app:cornerRadius="16dp"
android:backgroundTint="@color/colorPrimary"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>

View File

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/close_button"
android:src="@drawable/ic_close"
app:elevation="0dp"
android:backgroundTint="@color/colorPrimary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/qrScanResultLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">
<TextView
android:id="@+id/result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/result"
android:textSize="18sp"
android:gravity="center"
android:padding="16dp" />
<Button
android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/close_button"
app:cornerRadius="16dp"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:padding="12dp"
android:layout_marginTop="24dp" />
</LinearLayout>

View File

@ -1,61 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_worker_username"
android:textSize="18sp"
android:layout_marginBottom="8dp" />
<EditText
android:id="@+id/employee_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/worker_username"
android:padding="12dp"
android:background="@drawable/ic_android_black_24dp"
android:inputType="text" />
<Button
android:id="@+id/view_employee_info"
android:layout_width="280dp"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="@string/watch_info_about_worker"
android:backgroundTint="@color/colorPrimary"
app:cornerRadius="16dp"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/employee_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textSize="16sp"
android:visibility="gone" />
<Button
android:id="@+id/toggle_access"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/block_or_unblock"
android:layout_marginTop="16dp"
android:visibility="gone" />
<Button
android:id="@+id/close"
android:layout_width="280dp"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="@string/back"
android:backgroundTint="@color/colorPrimary"
app:cornerRadius="16dp"
android:layout_marginTop="0dp" />
</LinearLayout>

View File

@ -1,52 +1,31 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp"
android:background="@android:color/white">
android:orientation="vertical">
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username_hint"
android:inputType="text"
android:padding="12dp"
android:background="@drawable/ic_android_black_24dp"
android:layout_marginBottom="16dp"/>
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/password_hint"
android:inputType="textPassword"
android:padding="12dp"
android:background="@drawable/ic_android_black_24dp"
android:layout_marginBottom="16dp"/>
android:inputType="text"/>
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login_button"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:padding="12dp"
app:cornerRadius="16dp"
android:layout_marginBottom="16dp"/>
android:visibility="gone" />
<TextView
android:id="@+id/error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:gravity="center_horizontal"
android:textColor="@android:color/holo_red_light"
android:layout_marginBottom="16dp"/>
android:textColor="@android:color/holo_red_light" />
<!-- Добавьте ProgressBar для индикатора загрузки -->
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"

View File

@ -1,78 +1,72 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:padding="16dp">
<!-- Поле для ФИО -->
<TextView
android:id="@+id/fullname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp" />
android:text="@string/name_and_surname"
android:textSize="18sp"
android:visibility="gone" />
<!-- Фото пользователя -->
<ImageView
android:id="@+id/photo"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:contentDescription="@string/photo_description" />
android:contentDescription="@string/photo_description"
android:visibility="gone" />
<!-- Поле для должности -->
<TextView
android:id="@+id/position"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp" />
android:text="@string/position_label"
android:visibility="gone" />
<!-- Поле для даты последнего входа -->
<TextView
android:id="@+id/lastEntry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp" />
android:text="@string/date_and_time"
android:visibility="gone" />
<!-- Кнопки -->
<Button
android:id="@+id/logout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/logout"
android:visibility="gone" />
<Button
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:padding="12dp"
app:cornerRadius="16dp"
android:text="@string/refresh" />
<Button
android:id="@+id/scan"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/scan_qr_code"
android:visibility="gone" />
<!-- Поле ошибки -->
<TextView
android:id="@+id/error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/error_placeholder"
android:textColor="@android:color/holo_red_dark"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/scan_qr_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:padding="12dp"
app:cornerRadius="16dp"
android:text="Сканировать QR-код"
android:visibility="gone" />
<Button
android:id="@+id/adminPanelButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:padding="12dp"
app:cornerRadius="16dp"
android:text="Admin Panel"
android:visibility="gone" />
</LinearLayout>

View File

@ -30,7 +30,6 @@
android:contentDescription="@string/close_button"
android:src="@drawable/ic_close"
app:elevation="0dp"
android:backgroundTint="@color/colorPrimary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/qrScanResultLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -22,9 +21,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/close_button"
app:cornerRadius="16dp"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white"
android:padding="12dp"
android:layout_marginTop="24dp" />
</LinearLayout>

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/scan_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp" />
<TextView
android:id="@+id/reader_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp" />
<TextView
android:id="@+id/access_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp" />
</LinearLayout>

View File

@ -22,12 +22,6 @@
android:label="Main Fragment"
tools:layout="@layout/fragment_main" />
<fragment
android:id="@+id/adminFragment"
android:name="ru.myitschool.work.ui.main.AdminFragment"
android:label="Admin Fragment"
tools:layout="@layout/fragment_admin" />
<fragment
android:id="@+id/qrResultFragment"
android:name="ru.myitschool.work.ui.qr.result.QrResult"

View File

@ -5,16 +5,17 @@
<string name="navigate">Go over</string>
<string name="photo_description">User\'s photo</string>
<string name="name_and_surname">Name Surname</string>
<string name="logout">Exit</string>
<string name="refresh">Update</string>
<string name="scan_qr_code">Scan QR Code</string>
<string name="error_placeholder">An error has occurred</string>
<string name="error_loading_data">Data upload error</string>
<string name="logged_out">You have logged out of your account</string>
<string name="date_and_time">2024-02-31 08:31</string>
<!-- Строки для экрана авторизации -->
<string name="username_hint">Enter username</string>
<string name="password_hint">Enter the password</string>
<string name="username_hint">Enter your username</string>
<string name="login_button">Enter</string>
<string name="error_invalid_username">The login is invalid. Check the entered data.</string>
<string name="error_empty_username">The login field must not be empty.</string>
@ -32,13 +33,8 @@
<string name="qr_scan_success">Successfully</string>
<string name="qr_scan_failure">Something went wrong.</string>
<string name="qr_scan_cancelled">Operation was cancelled</string>
<string name="close_button">Close</string>
<string name="result">Result</string>
<!-- Админ панель -->
<string name="enter_worker_username">Enter the employee\'s username:</string>
<string name="worker_username">Employee\'s login</string>
<string name="watch_info_about_worker">View employee information</string>
<string name="block_or_unblock">Block/Unblock access</string>
<string name="back">Back</string>
</resources>

View File

@ -7,6 +7,4 @@
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="colorPrimary">#2196F3</color>
<color name="light_gray">#F0F0F0</color>
</resources>

View File

@ -1,45 +1,40 @@
<resources>
<string name="app_name" translatable="false">NTO Pass</string>
<string name="welcome_message">Добро пожаловать в приложение!</string>
<string name="navigate">Перейти</string>
<string name="app_name" translatable="false">NTO Pass</string>
<string name="welcome_message">Добро пожаловать в приложение!</string>
<string name="navigate">Перейти</string>
<string name="photo_description">Фото пользователя</string>
<string name="logout">Выйти</string>
<string name="refresh">Обновить</string>
<string name="scan_qr_code">Сканировать QR</string>
<string name="error_placeholder">Произошла ошибка</string>
<string name="error_loading_data">Ошибка загрузки данных</string>
<string name="logged_out">Вы вышли из аккаунта</string>
<string name="photo_description">Фото пользователя</string>
<string name="name_and_surname">Имя Фамилия</string>
<string name="logout">Выйти</string>
<string name="refresh">Обновить</string>
<string name="scan_qr_code">Сканировать QR</string>
<string name="error_placeholder">Произошла ошибка</string>
<string name="error_loading_data">Ошибка загрузки данных</string>
<string name="logged_out">Вы вышли из аккаунта</string>
<string name="date_and_time">2024-02-31 08:31</string>
<!-- Строки для экрана авторизации -->
<string name="username_hint">Введите логин</string>
<string name="password_hint">Введите пароль</string>
<string name="login_button">Войти</string>
<string name="error_invalid_username">Логин недействителен. Проверьте введенные данные.</string>
<string name="error_empty_username">Поле логина не должно быть пустым.</string>
<string name="error_username_too_short">Логин должен содержать не менее 3 символов.</string>
<string name="error_username_starts_with_digit">Логин не должен начинаться с цифры.</string>
<string name="error_username_invalid_characters">Логин может содержать только латинские буквы и цифры.</string>
<!-- Строки для экрана авторизации -->
<string name="username_hint">Введите логин</string>
<string name="login_button">Войти</string>
<string name="error_invalid_username">Логин недействителен. Проверьте введенные данные.</string>
<string name="error_empty_username">Поле логина не должно быть пустым.</string>
<string name="error_username_too_short">Логин должен содержать не менее 3 символов.</string>
<string name="error_username_starts_with_digit">Логин не должен начинаться с цифры.</string>
<string name="error_username_invalid_characters">Логин может содержать только латинские буквы и цифры.</string>
<!-- Строки для главного экрана -->
<string name="fullname_label">ФИО</string>
<string name="position_label">Должность</string>
<string name="last_entry_label">Время последнего входа</string>
<string name="error_no_user_data">Нет данных о пользователе.</string>
<string name="admin_panel" translatable="false">Admin Panel</string>
<!-- Строки для главного экрана -->
<string name="fullname_label">ФИО</string>
<string name="position_label">Должность</string>
<string name="last_entry_label">Время последнего входа</string>
<string name="error_no_user_data">Нет данных о пользователе.</string>
<!-- Строки для экрана сканирования QR-кода -->
<string name="qr_scan_success">Успешно</string>
<string name="qr_scan_failure">Что-то пошло не так</string>
<string name="qr_scan_cancelled">Вход был отменён / Operation was cancelled</string>
<string name="close_button">Закрыть</string>
<string name="result">Результат</string>
<!-- Строки для экрана сканирования QR-кода -->
<string name="qr_scan_success">Успешно</string>
<string name="qr_scan_failure">Что-то пошло не так</string>
<string name="qr_scan_cancelled">Вход был отменён/Operation was cancelled</string>
<string name="close_button">Закрыть</string>
<string name="result">Результат</string>
<!-- Админ панель -->
<string name="enter_worker_username">Введите логин сотрудника:</string>
<string name="worker_username">Логин сотрудника</string>
<string name="watch_info_about_worker">Просмотреть информацию о сотруднике</string>
<string name="block_or_unblock">Блокировать/Разблокировать доступ</string>
<string name="back">Назад</string>
</resources>

View File

@ -4,6 +4,5 @@ plugins {
kotlinJvm version Version.Kotlin.language apply false
kotlinAnnotationProcessor version Version.Kotlin.language apply false
id("com.google.dagger.hilt.android") version "2.51.1" apply false
id("org.jetbrains.kotlin.android") version "1.9.24" apply false
}