From cf80c6b493c5988f19cd58f643980cc68e19d9a8 Mon Sep 17 00:00:00 2001 From: DKaverznev Date: Wed, 19 Feb 2025 19:00:24 +0300 Subject: [PATCH] =?UTF-8?q?code:=20=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D1=81=D0=BF=D0=B8=D1=81=D0=BE=D0=BA,=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82=20?= =?UTF-8?q?=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?(=D0=B7=D0=B0=D0=B2=D1=82=D1=80=D0=B0=20=D0=BF=D0=BE=D1=87?= =?UTF-8?q?=D0=B8=D0=BD=D1=83=D1=8E)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .kotlin/errors/errors-1739973336195.log | 82 ++++++++++++++++ app/build.gradle.kts | 1 + .../myitschool/work/administrator_screen.java | 64 ------------ .../ru/myitschool/work/api/NetworkModule.kt | 7 ++ .../ru/myitschool/work/api/RetrofitClient.kt | 13 +++ .../work/api/list/ApiServiceList.kt | 6 +- .../ru/myitschool/work/api/list/ListInfo.kt | 7 ++ .../work/api/login/ApiServiceLogin.kt | 2 +- .../work/api/scan/ApiServiceScan.kt | 4 +- .../ru/myitschool/work/api/scan/CodeJson.kt | 8 +- .../java/ru/myitschool/work/core/Constants.kt | 2 +- .../ru/myitschool/work/ui/RootActivity.kt | 4 +- .../ru/myitschool/work/ui/list/ListAdapter.kt | 35 +++++++ .../myitschool/work/ui/list/ListRepository.kt | 13 +++ .../myitschool/work/ui/list/ListViewHolder.kt | 16 +++ .../myitschool/work/ui/main/MainFragment.kt | 97 ++++++++++++------- .../myitschool/work/ui/main/MainViewModel.kt | 27 +++++- .../work/ui/result/ResultFragment.kt | 33 ++++--- .../{qr/scan => scan/qr}/QrScanDestination.kt | 3 +- .../ui/{qr/scan => scan/qr}/QrScanFragment.kt | 38 +++++++- .../{qr/scan => scan/qr}/QrScanViewModel.kt | 2 +- .../ru/myitschool/work/utils/QrPreferences.kt | 27 ++++++ 22 files changed, 362 insertions(+), 129 deletions(-) create mode 100644 .kotlin/errors/errors-1739973336195.log delete mode 100644 app/src/main/java/ru/myitschool/work/administrator_screen.java create mode 100644 app/src/main/java/ru/myitschool/work/api/list/ListInfo.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/list/ListAdapter.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/list/ListRepository.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/list/ListViewHolder.kt rename app/src/main/java/ru/myitschool/work/ui/{qr/scan => scan/qr}/QrScanDestination.kt (91%) rename app/src/main/java/ru/myitschool/work/ui/{qr/scan => scan/qr}/QrScanFragment.kt (79%) rename app/src/main/java/ru/myitschool/work/ui/{qr/scan => scan/qr}/QrScanViewModel.kt (98%) create mode 100644 app/src/main/java/ru/myitschool/work/utils/QrPreferences.kt diff --git a/.kotlin/errors/errors-1739973336195.log b/.kotlin/errors/errors-1739973336195.log new file mode 100644 index 0000000..fee20b4 --- /dev/null +++ b/.kotlin/errors/errors-1739973336195.log @@ -0,0 +1,82 @@ +kotlin version: 2.0.21 +error message: Daemon compilation failed: null +java.lang.Exception + at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:69) + at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:65) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:240) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) + at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76) + at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62) + at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62) + at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44) + at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209) + at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66) + at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166) + at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59) + at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53) + at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41) + at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) + at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) + at java.base/java.util.concurrent.FutureTask.run(Unknown Source) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:195) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:128) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:170) + at org.gradle.internal.Factories$1.create(Factories.java:31) + at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:267) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:131) + at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:136) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:165) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:134) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) + at java.base/java.util.concurrent.FutureTask.run(Unknown Source) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) +Caused by: java.nio.file.DirectoryNotEmptyException: C:\Users\User\AppData\Local\Temp\kotlin-backups5267971900938964466 + at java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(Unknown Source) + at java.base/sun.nio.fs.AbstractFileSystemProvider.delete(Unknown Source) + at java.base/java.nio.file.Files.delete(Unknown Source) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction$cleanupStash$2$1$1.invoke(CompilationTransaction.kt:244) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction$cleanupStash$2$1$1.invoke(CompilationTransaction.kt:244) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.cleanupStash$lambda$11$lambda$10$lambda$9(CompilationTransaction.kt:244) + at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source) + at java.base/java.util.ArrayList.forEach(Unknown Source) + at java.base/java.util.stream.SortedOps$RefSortingSink.end(Unknown Source) + at java.base/java.util.stream.Sink$ChainedReference.end(Unknown Source) + at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source) + at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source) + at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source) + at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source) + at java.base/java.util.stream.ReferencePipeline.forEach(Unknown Source) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.cleanupStash(CompilationTransaction.kt:244) + at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.close(CompilationTransaction.kt:254) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally(IncrementalCompilerRunner.kt:747) + at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:120) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:675) + at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92) + at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1660) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) + at java.base/java.lang.reflect.Method.invoke(Unknown Source) + at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) + at java.base/java.security.AccessController.doPrivileged(Unknown Source) + at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) + ... 3 more + + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 51c1ecc..492e413 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(Dependencies.Retrofit.library) implementation(Dependencies.Retrofit.gsonConverter) + implementation("com.squareup.okhttp3:logging-interceptor:4.10.0") implementation("com.squareup.picasso:picasso:2.8") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") diff --git a/app/src/main/java/ru/myitschool/work/administrator_screen.java b/app/src/main/java/ru/myitschool/work/administrator_screen.java deleted file mode 100644 index 67346da..0000000 --- a/app/src/main/java/ru/myitschool/work/administrator_screen.java +++ /dev/null @@ -1,64 +0,0 @@ -package ru.myitschool.work; - -import android.os.Bundle; - -import androidx.fragment.app.Fragment; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -/** - * A simple {@link Fragment} subclass. - * Use the {@link administrator_screen#newInstance} factory method to - * create an instance of this fragment. - */ -public class administrator_screen extends Fragment { - - // TODO: Rename parameter arguments, choose names that match - // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER - private static final String ARG_PARAM1 = "param1"; - private static final String ARG_PARAM2 = "param2"; - - // TODO: Rename and change types of parameters - private String mParam1; - private String mParam2; - - public administrator_screen() { - // Required empty public constructor - } - - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment administrator_screen. - */ - // TODO: Rename and change types and number of parameters - public static administrator_screen newInstance(String param1, String param2) { - administrator_screen fragment = new administrator_screen(); - Bundle args = new Bundle(); - args.putString(ARG_PARAM1, param1); - args.putString(ARG_PARAM2, param2); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - mParam1 = getArguments().getString(ARG_PARAM1); - mParam2 = getArguments().getString(ARG_PARAM2); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_administrator_screen, container, false); - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/api/NetworkModule.kt b/app/src/main/java/ru/myitschool/work/api/NetworkModule.kt index 23aabc8..205c1c3 100644 --- a/app/src/main/java/ru/myitschool/work/api/NetworkModule.kt +++ b/app/src/main/java/ru/myitschool/work/api/NetworkModule.kt @@ -6,6 +6,7 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import dagger.hilt.android.qualifiers.ApplicationContext +import ru.myitschool.work.api.list.ApiServiceList import ru.myitschool.work.api.login.ApiServiceLogin import ru.myitschool.work.api.main.ApiServiceMain import ru.myitschool.work.api.scan.ApiServiceScan @@ -38,4 +39,10 @@ object NetworkModule { fun provideApiServiceScan(retrofitClient: RetrofitClient): ApiServiceScan { return retrofitClient.getApiServiceScanAuth() } + + @Provides + @Singleton + fun provideListApiService(retrofitClient: RetrofitClient): ApiServiceList { + return retrofitClient.getApiServiceList() + } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/api/RetrofitClient.kt b/app/src/main/java/ru/myitschool/work/api/RetrofitClient.kt index 1f1e59d..ce4b061 100644 --- a/app/src/main/java/ru/myitschool/work/api/RetrofitClient.kt +++ b/app/src/main/java/ru/myitschool/work/api/RetrofitClient.kt @@ -1,9 +1,12 @@ package ru.myitschool.work.api import android.content.Context +import com.google.android.datatransport.BuildConfig import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory +import ru.myitschool.work.api.list.ApiServiceList import ru.myitschool.work.api.login.ApiServiceLogin import ru.myitschool.work.api.main.ApiServiceMain import ru.myitschool.work.api.scan.ApiServiceScan @@ -16,12 +19,18 @@ class RetrofitClient(context: Context) { private val serverAddress = SERVER_ADDRESS private var authPreferences: AuthPreferences = AuthPreferences(context) + + private val loggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + private val httpClientWithAuth = OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .addInterceptor(BasicAuthInterceptor( authPreferences.getLogin() ?: "", authPreferences.getPassword() ?: "" )) + .addInterceptor(loggingInterceptor) .readTimeout(5, TimeUnit.SECONDS) .build() @@ -44,4 +53,8 @@ class RetrofitClient(context: Context) { fun getApiServiceScanAuth(): ApiServiceScan { return retrofitWithAuth.create(ApiServiceScan::class.java) } + + fun getApiServiceList(): ApiServiceList { + return retrofitWithAuth.create(ApiServiceList::class.java) + } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/api/list/ApiServiceList.kt b/app/src/main/java/ru/myitschool/work/api/list/ApiServiceList.kt index a0e52ac..c92f80e 100644 --- a/app/src/main/java/ru/myitschool/work/api/list/ApiServiceList.kt +++ b/app/src/main/java/ru/myitschool/work/api/list/ApiServiceList.kt @@ -1,10 +1,10 @@ package ru.myitschool.work.api.list -import retrofit2.Call +import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path interface ApiServiceList { - @GET("api/auth/{login}") - fun authenticate(@Path("login") login: String): Call + @GET("api/list/{login}") + suspend fun getList(@Path("login") login: String): Response> } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/api/list/ListInfo.kt b/app/src/main/java/ru/myitschool/work/api/list/ListInfo.kt new file mode 100644 index 0000000..a2dbb95 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/api/list/ListInfo.kt @@ -0,0 +1,7 @@ +package ru.myitschool.work.api.list + +data class ListInfo( + val value: String, + val type: String, + val time: String +) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/api/login/ApiServiceLogin.kt b/app/src/main/java/ru/myitschool/work/api/login/ApiServiceLogin.kt index a99eb33..51dd1d0 100644 --- a/app/src/main/java/ru/myitschool/work/api/login/ApiServiceLogin.kt +++ b/app/src/main/java/ru/myitschool/work/api/login/ApiServiceLogin.kt @@ -8,6 +8,6 @@ import retrofit2.http.Path import ru.myitschool.work.api.main.UserInfo interface ApiServiceLogin { - @GET("api/auth") + @GET("api/login") fun authenticate(): Call } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/api/scan/ApiServiceScan.kt b/app/src/main/java/ru/myitschool/work/api/scan/ApiServiceScan.kt index b3d48e3..e83ffd4 100644 --- a/app/src/main/java/ru/myitschool/work/api/scan/ApiServiceScan.kt +++ b/app/src/main/java/ru/myitschool/work/api/scan/ApiServiceScan.kt @@ -2,10 +2,10 @@ package ru.myitschool.work.api.scan import retrofit2.Call import retrofit2.http.Body -import retrofit2.http.PATCH +import retrofit2.http.POST import retrofit2.http.Path interface ApiServiceScan { - @PATCH("api/info/{login}") + @POST("api/add/{login}") fun open(@Path("login") login: String, @Body data: CodeJson): Call } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/api/scan/CodeJson.kt b/app/src/main/java/ru/myitschool/work/api/scan/CodeJson.kt index e4934ce..b7d121b 100644 --- a/app/src/main/java/ru/myitschool/work/api/scan/CodeJson.kt +++ b/app/src/main/java/ru/myitschool/work/api/scan/CodeJson.kt @@ -1,9 +1,11 @@ package ru.myitschool.work.api.scan +import com.google.gson.annotations.SerializedName + class CodeJson( - private var value: String? = null, - private var type: String? = null, - private var time: String? = null + @SerializedName("value") var value: String? = null, + @SerializedName("type") var type: String? = null, + @SerializedName("time") var time: String? = null ) object OpenType { diff --git a/app/src/main/java/ru/myitschool/work/core/Constants.kt b/app/src/main/java/ru/myitschool/work/core/Constants.kt index 9cabb89..75d62b2 100644 --- a/app/src/main/java/ru/myitschool/work/core/Constants.kt +++ b/app/src/main/java/ru/myitschool/work/core/Constants.kt @@ -1,5 +1,5 @@ package ru.myitschool.work.core object Constants { - const val SERVER_ADDRESS = "http://192.168.1.113:8080" + const val SERVER_ADDRESS = "http://10.6.66.93:8080" } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/RootActivity.kt b/app/src/main/java/ru/myitschool/work/ui/RootActivity.kt index b0de275..8f1b5fe 100644 --- a/app/src/main/java/ru/myitschool/work/ui/RootActivity.kt +++ b/app/src/main/java/ru/myitschool/work/ui/RootActivity.kt @@ -13,8 +13,8 @@ import ru.myitschool.work.ui.login.LoginDestination import ru.myitschool.work.ui.login.LoginFragment import ru.myitschool.work.ui.main.MainDestination import ru.myitschool.work.ui.main.MainFragment -import ru.myitschool.work.ui.qr.scan.QrScanDestination -import ru.myitschool.work.ui.qr.scan.QrScanFragment +import ru.myitschool.work.ui.scan.qr.QrScanDestination +import ru.myitschool.work.ui.scan.qr.QrScanFragment import ru.myitschool.work.ui.result.ResultDestination import ru.myitschool.work.ui.result.ResultFragment diff --git a/app/src/main/java/ru/myitschool/work/ui/list/ListAdapter.kt b/app/src/main/java/ru/myitschool/work/ui/list/ListAdapter.kt new file mode 100644 index 0000000..5134076 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/list/ListAdapter.kt @@ -0,0 +1,35 @@ +package ru.myitschool.work.ui.list + + + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import ru.myitschool.work.R +import ru.myitschool.work.api.list.ListInfo + +class ListAdapter : ListAdapter(DiffCallback()) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_recycler_view_entrancedata, parent, false) + return ListViewHolder(view) + } + + override fun onBindViewHolder(holder: ListViewHolder, position: Int) { + val item = getItem(position) + holder.bind(item) + } + + class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: ListInfo, newItem: ListInfo): Boolean { + return oldItem.time == newItem.time && oldItem.value == newItem.value + } + + override fun areContentsTheSame(oldItem: ListInfo, newItem: ListInfo): Boolean { + return oldItem == newItem + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/list/ListRepository.kt b/app/src/main/java/ru/myitschool/work/ui/list/ListRepository.kt new file mode 100644 index 0000000..b7b11fe --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/list/ListRepository.kt @@ -0,0 +1,13 @@ +package ru.myitschool.work.ui.list + +import ru.myitschool.work.api.list.ApiServiceList +import ru.myitschool.work.api.list.ListInfo +import javax.inject.Inject + +class ListRepository @Inject constructor( + private val apiService: ApiServiceList +) { + suspend fun getList(login: String): List { + return apiService.getList(login).body() ?: emptyList() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/list/ListViewHolder.kt b/app/src/main/java/ru/myitschool/work/ui/list/ListViewHolder.kt new file mode 100644 index 0000000..a04c678 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/list/ListViewHolder.kt @@ -0,0 +1,16 @@ +package ru.myitschool.work.ui.list + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import ru.myitschool.work.api.list.ListInfo +import ru.myitschool.work.databinding.ItemRecyclerViewEntrancedataBinding + +class ListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val binding = ItemRecyclerViewEntrancedataBinding.bind(itemView) + + fun bind(item: ListInfo) { + binding.text1.text = item.time + binding.text2.text = item.value + binding.text3.text = item.type + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt b/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt index db7a2ba..b4b1ede 100644 --- a/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt @@ -1,24 +1,23 @@ package ru.myitschool.work.ui.main -import android.graphics.BitmapFactory import android.os.Bundle import android.util.Log import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import com.squareup.picasso.Picasso import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import ru.myitschool.work.R import ru.myitschool.work.databinding.FragmentMainBinding +import ru.myitschool.work.ui.list.ListAdapter import ru.myitschool.work.ui.login.LoginDestination -import ru.myitschool.work.ui.qr.scan.QrScanDestination +import ru.myitschool.work.ui.scan.qr.QrScanDestination import ru.myitschool.work.utils.AuthPreferences import ru.myitschool.work.utils.collectWhenStarted -import java.net.URL +import ru.myitschool.work.utils.visibleOrGone import java.util.Locale @AndroidEntryPoint @@ -27,6 +26,7 @@ class MainFragment : Fragment(R.layout.fragment_main) { private val binding: FragmentMainBinding get() = _binding!! private lateinit var authPreferences: AuthPreferences private val viewModel: MainViewModel by viewModels() + private lateinit var listAdapter: ListAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -41,23 +41,41 @@ class MainFragment : Fragment(R.layout.fragment_main) { super.onViewCreated(view, savedInstanceState) _binding = FragmentMainBinding.bind(view) + setupRecyclerView() + + if (authPreferences.isLoggedIn()) { setupMainComponents() + observeUserState() + observeListState() + viewModel.getUserData(authPreferences.getLogin() ?: "") + + val login = authPreferences.getLogin().toString() + viewModel.loadList(login) } } private fun hideAll() { binding.apply { - username.visibility = View.GONE - position.visibility = View.GONE - lastEntry.visibility = View.GONE - scan.visibility = View.GONE - logout.visibility = View.GONE + username.visibleOrGone(false) + position.visibleOrGone(false) + lastEntry.visibleOrGone(false) + scan.visibleOrGone(false) + logout.visibleOrGone(false) - error.visibility = View.VISIBLE - refresh.visibility = View.VISIBLE + error.visibleOrGone(true) + refresh.visibleOrGone(true) + } + } + + private fun setupRecyclerView() { + listAdapter = ListAdapter() + binding.entranceData.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = listAdapter + addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) } } @@ -65,19 +83,19 @@ class MainFragment : Fragment(R.layout.fragment_main) { viewModel.state.collectWhenStarted(this) { state -> when (state) { is MainViewModel.MainState.Initial -> { - binding.error.visibility = View.GONE + binding.error.visibleOrGone(false) } is MainViewModel.MainState.Loading -> { - binding.error.visibility = View.GONE + binding.error.visibleOrGone(false) } is MainViewModel.MainState.Success -> { binding.apply { - username.visibility = View.VISIBLE - position.visibility = View.VISIBLE - lastEntry.visibility = View.VISIBLE - scan.visibility = View.VISIBLE - logout.visibility = View.VISIBLE - error.visibility = View.GONE + username.visibleOrGone(true) + position.visibleOrGone(true) + lastEntry.visibleOrGone(true) + scan.visibleOrGone(true) + logout.visibleOrGone(true) + error.visibleOrGone(false) username.text = state.userInfo.name position.text = state.userInfo.position @@ -103,26 +121,31 @@ class MainFragment : Fragment(R.layout.fragment_main) { } } - private fun loadImageFromUrl(url: String) { - CoroutineScope(Dispatchers.IO).launch { - val bitmap = try { - val input = URL(url).openStream() - BitmapFactory.decodeStream(input) - } catch (e: Exception) { - Log.e("MainFragment", "Error loading image", e) - null - } - - withContext(Dispatchers.Main) { - if (bitmap != null) { - binding.photo.setImageBitmap(bitmap) - } else { - binding.photo.setImageResource(R.drawable.ic_no_img) + private fun observeListState() { + viewModel.listState.collectWhenStarted(this) { state -> + when (state) { + is MainViewModel.ListState.Loading -> { + //TODO + } + is MainViewModel.ListState.Success -> { + listAdapter.submitList(state.data) + } + is MainViewModel.ListState.Error -> { + //TODO + binding.error.text = state.message } } } } + private fun loadImageFromUrl(url: String) { + Picasso + .get() + .load(url) + .error(R.drawable.icon_profile) + .into(binding.photo) + } + private fun setupMainComponents() { binding.logout.setOnClickListener { authPreferences.clearLoginState() diff --git a/app/src/main/java/ru/myitschool/work/ui/main/MainViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/main/MainViewModel.kt index 9333dd0..73c6a29 100644 --- a/app/src/main/java/ru/myitschool/work/ui/main/MainViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/main/MainViewModel.kt @@ -5,18 +5,22 @@ 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.flow.asStateFlow import kotlinx.coroutines.launch import retrofit2.Call import retrofit2.Callback import retrofit2.Response +import ru.myitschool.work.api.list.ListInfo import ru.myitschool.work.api.main.ApiServiceMain import ru.myitschool.work.api.main.UserInfo +import ru.myitschool.work.ui.list.ListRepository import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( - private val apiService: ApiServiceMain + private val apiService: ApiServiceMain, + private val listRepository: ListRepository ) : ViewModel() { private val _state = MutableStateFlow(MainState.Initial) @@ -52,6 +56,27 @@ class MainViewModel @Inject constructor( } } + private val _listState = MutableStateFlow(ListState.Loading) + val listState: StateFlow = _listState + + sealed class ListState { + data object Loading : ListState() + data class Success(val data: List) : ListState() + data class Error(val message: String) : ListState() + } + + fun loadList(login: String) { + viewModelScope.launch { + _listState.value = ListState.Loading + try { + val list = listRepository.getList(login) + _listState.value = ListState.Success(list) + } catch (e: Exception) { + _listState.value = ListState.Error(e.message ?: "Unknown error") + } + } + } + sealed class MainState { data object Initial : MainState() data object Loading : MainState() diff --git a/app/src/main/java/ru/myitschool/work/ui/result/ResultFragment.kt b/app/src/main/java/ru/myitschool/work/ui/result/ResultFragment.kt index a37d36e..df74102 100644 --- a/app/src/main/java/ru/myitschool/work/ui/result/ResultFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/result/ResultFragment.kt @@ -13,8 +13,8 @@ import ru.myitschool.work.api.scan.CodeJson import ru.myitschool.work.api.scan.OpenType import ru.myitschool.work.databinding.FragmentScanResultBinding import ru.myitschool.work.ui.main.MainDestination -import ru.myitschool.work.ui.qr.scan.QrScanDestination import ru.myitschool.work.utils.AuthPreferences +import ru.myitschool.work.utils.QrPreferences import ru.myitschool.work.utils.collectWhenStarted import ru.myitschool.work.utils.getCurrentTime import ru.myitschool.work.utils.visibleOrGone @@ -24,11 +24,16 @@ class ResultFragment : Fragment(R.layout.fragment_scan_result) { private var _binding: FragmentScanResultBinding? = null private val binding: FragmentScanResultBinding get() = _binding!! private val viewModel: ResultViewModel by viewModels() + private lateinit var authPreferences: AuthPreferences + private lateinit var qrPreferences: QrPreferences override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + authPreferences = AuthPreferences(requireContext()) + qrPreferences = QrPreferences(requireContext()) + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -45,18 +50,24 @@ class ResultFragment : Fragment(R.layout.fragment_scan_result) { successIcon.visibleOrGone(false) close.setOnClickListener { - navigateToMainScreen() + navigateToResultScreen() } } - val qrData = QrScanDestination.getDataIfExist(arguments ?: Bundle()) - authPreferences.getLogin()?.let { login -> - val currentTime = getCurrentTime() - viewModel.open(login, CodeJson( - value = qrData, - type = OpenType.QR_TYPE, - time = currentTime - )) + val qrData = qrPreferences.getQr() + + if (qrData != null) { + authPreferences.getLogin()?.let { login -> + val currentTime = getCurrentTime() + viewModel.open(login, CodeJson( + value = qrData.toString(), + type = OpenType.QR_TYPE, + time = currentTime + )) + } + } else { + Log.e("ResultFragment", "QR data is null") + Toast.makeText(context, getString(R.string.error), Toast.LENGTH_SHORT).show() } } @@ -94,7 +105,7 @@ class ResultFragment : Fragment(R.layout.fragment_scan_result) { } } - private fun navigateToMainScreen() { + private fun navigateToResultScreen() { try { findNavController().apply { popBackStack(MainDestination, false) diff --git a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanDestination.kt b/app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanDestination.kt similarity index 91% rename from app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanDestination.kt rename to app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanDestination.kt index d7b8e6d..7eb80a3 100644 --- a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanDestination.kt +++ b/app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanDestination.kt @@ -1,6 +1,7 @@ -package ru.myitschool.work.ui.qr.scan +package ru.myitschool.work.ui.scan.qr import android.os.Bundle +import android.util.Log import androidx.core.os.bundleOf import kotlinx.serialization.Serializable diff --git a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanFragment.kt b/app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanFragment.kt similarity index 79% rename from app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanFragment.kt rename to app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanFragment.kt index 38beb4d..46314dc 100644 --- a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanFragment.kt @@ -1,7 +1,9 @@ -package ru.myitschool.work.ui.qr.scan +package ru.myitschool.work.ui.scan.qr import android.os.Bundle +import android.util.Log import android.view.View +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.camera.core.ImageAnalysis import androidx.camera.mlkit.vision.MlKitAnalyzer @@ -20,6 +22,9 @@ import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.barcode.common.Barcode import ru.myitschool.work.R import ru.myitschool.work.databinding.FragmentQrScanBinding +import ru.myitschool.work.ui.main.MainDestination +import ru.myitschool.work.ui.result.ResultDestination +import ru.myitschool.work.utils.QrPreferences import ru.myitschool.work.utils.collectWhenStarted import ru.myitschool.work.utils.visibleOrGone @@ -34,6 +39,14 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) { ) { isGranted -> viewModel.onPermissionResult(isGranted) } private val viewModel: QrScanViewModel by viewModels() + private lateinit var qrPreferences: QrPreferences + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + qrPreferences = QrPreferences(requireContext()) + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -65,7 +78,7 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) { } is QrScanViewModel.Action.CloseWithResult -> { sendResult(QrScanDestination.packToBundle(action.result)) - goBack() + goResult() } } } @@ -113,10 +126,31 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) { } private fun goBack() { + val qrData = arguments?.let { QrScanDestination.getDataIfExist(it) } + qrPreferences.saveQr(qrData.toString()) + findNavControllerOrNull()?.popBackStack() ?: requireActivity().onBackPressedDispatcher.onBackPressed() } + private fun goResult() { + try { + val qrData = arguments?.let { QrScanDestination.getDataIfExist(it) } + qrPreferences.saveQr(qrData.toString()) + + Log.i("ResultFragment", qrData.toString()) + + findNavController().apply { + popBackStack(ResultDestination, false) + navigate(ResultDestination) + } + } catch (e: Exception) { + Log.e("ResultFragment", "Navigation error", e) + Toast.makeText(context, getText(R.string.errorGoText).toString(), Toast.LENGTH_SHORT).show() + } + } + + private fun sendResult(bundle: Bundle) { setFragmentResult( QrScanDestination.REQUEST_KEY, diff --git a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanViewModel.kt similarity index 98% rename from app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanViewModel.kt rename to app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanViewModel.kt index e4fd4da..424afea 100644 --- a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/scan/qr/QrScanViewModel.kt @@ -1,4 +1,4 @@ -package ru.myitschool.work.ui.qr.scan +package ru.myitschool.work.ui.scan.qr import android.Manifest import android.app.Application diff --git a/app/src/main/java/ru/myitschool/work/utils/QrPreferences.kt b/app/src/main/java/ru/myitschool/work/utils/QrPreferences.kt new file mode 100644 index 0000000..289a33e --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/utils/QrPreferences.kt @@ -0,0 +1,27 @@ +package ru.myitschool.work.utils + +import android.content.Context +import android.content.SharedPreferences + +class QrPreferences(context: Context) { + private val sharedPreferences: SharedPreferences = context.getSharedPreferences( + PREFS_NAME, + Context.MODE_PRIVATE + ) + + fun saveQr(login: String) { + sharedPreferences.edit().apply { + putString("qr", login) + apply() + } + } + + fun getQr(): String? { + return sharedPreferences.getString("qr", null) + } + + + companion object { + private const val PREFS_NAME = "ArPreferences" + } +} \ No newline at end of file