Merge remote-tracking branch 'origin/main'

This commit is contained in:
veronicagtea 2025-02-20 10:18:09 +03:00
commit 36004a2ba4
22 changed files with 362 additions and 129 deletions

View File

@ -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

View File

@ -46,6 +46,7 @@ dependencies {
implementation(Dependencies.Retrofit.library) implementation(Dependencies.Retrofit.library)
implementation(Dependencies.Retrofit.gsonConverter) implementation(Dependencies.Retrofit.gsonConverter)
implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
implementation("com.squareup.picasso:picasso:2.8") implementation("com.squareup.picasso:picasso:2.8")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")

View File

@ -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);
}
}

View File

@ -6,6 +6,7 @@ import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import dagger.hilt.android.qualifiers.ApplicationContext 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.login.ApiServiceLogin
import ru.myitschool.work.api.main.ApiServiceMain import ru.myitschool.work.api.main.ApiServiceMain
import ru.myitschool.work.api.scan.ApiServiceScan import ru.myitschool.work.api.scan.ApiServiceScan
@ -38,4 +39,10 @@ object NetworkModule {
fun provideApiServiceScan(retrofitClient: RetrofitClient): ApiServiceScan { fun provideApiServiceScan(retrofitClient: RetrofitClient): ApiServiceScan {
return retrofitClient.getApiServiceScanAuth() return retrofitClient.getApiServiceScanAuth()
} }
@Provides
@Singleton
fun provideListApiService(retrofitClient: RetrofitClient): ApiServiceList {
return retrofitClient.getApiServiceList()
}
} }

View File

@ -1,9 +1,12 @@
package ru.myitschool.work.api package ru.myitschool.work.api
import android.content.Context import android.content.Context
import com.google.android.datatransport.BuildConfig
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import ru.myitschool.work.api.list.ApiServiceList
import ru.myitschool.work.api.login.ApiServiceLogin import ru.myitschool.work.api.login.ApiServiceLogin
import ru.myitschool.work.api.main.ApiServiceMain import ru.myitschool.work.api.main.ApiServiceMain
import ru.myitschool.work.api.scan.ApiServiceScan import ru.myitschool.work.api.scan.ApiServiceScan
@ -16,12 +19,18 @@ class RetrofitClient(context: Context) {
private val serverAddress = SERVER_ADDRESS private val serverAddress = SERVER_ADDRESS
private var authPreferences: AuthPreferences = AuthPreferences(context) private var authPreferences: AuthPreferences = AuthPreferences(context)
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
private val httpClientWithAuth = OkHttpClient.Builder() private val httpClientWithAuth = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS) .connectTimeout(5, TimeUnit.SECONDS)
.addInterceptor(BasicAuthInterceptor( .addInterceptor(BasicAuthInterceptor(
authPreferences.getLogin() ?: "", authPreferences.getLogin() ?: "",
authPreferences.getPassword() ?: "" authPreferences.getPassword() ?: ""
)) ))
.addInterceptor(loggingInterceptor)
.readTimeout(5, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.SECONDS)
.build() .build()
@ -44,4 +53,8 @@ class RetrofitClient(context: Context) {
fun getApiServiceScanAuth(): ApiServiceScan { fun getApiServiceScanAuth(): ApiServiceScan {
return retrofitWithAuth.create(ApiServiceScan::class.java) return retrofitWithAuth.create(ApiServiceScan::class.java)
} }
fun getApiServiceList(): ApiServiceList {
return retrofitWithAuth.create(ApiServiceList::class.java)
}
} }

View File

@ -1,10 +1,10 @@
package ru.myitschool.work.api.list package ru.myitschool.work.api.list
import retrofit2.Call import retrofit2.Response
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Path import retrofit2.http.Path
interface ApiServiceList { interface ApiServiceList {
@GET("api/auth/{login}") @GET("api/list/{login}")
fun authenticate(@Path("login") login: String): Call<Void> suspend fun getList(@Path("login") login: String): Response<List<ListInfo>>
} }

View File

@ -0,0 +1,7 @@
package ru.myitschool.work.api.list
data class ListInfo(
val value: String,
val type: String,
val time: String
)

View File

@ -8,6 +8,6 @@ import retrofit2.http.Path
import ru.myitschool.work.api.main.UserInfo import ru.myitschool.work.api.main.UserInfo
interface ApiServiceLogin { interface ApiServiceLogin {
@GET("api/auth") @GET("api/login")
fun authenticate(): Call<Void> fun authenticate(): Call<Void>
} }

View File

@ -2,10 +2,10 @@ package ru.myitschool.work.api.scan
import retrofit2.Call import retrofit2.Call
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.PATCH import retrofit2.http.POST
import retrofit2.http.Path import retrofit2.http.Path
interface ApiServiceScan { interface ApiServiceScan {
@PATCH("api/info/{login}") @POST("api/add/{login}")
fun open(@Path("login") login: String, @Body data: CodeJson): Call<Void> fun open(@Path("login") login: String, @Body data: CodeJson): Call<Void>
} }

View File

@ -1,9 +1,11 @@
package ru.myitschool.work.api.scan package ru.myitschool.work.api.scan
import com.google.gson.annotations.SerializedName
class CodeJson( class CodeJson(
private var value: String? = null, @SerializedName("value") var value: String? = null,
private var type: String? = null, @SerializedName("type") var type: String? = null,
private var time: String? = null @SerializedName("time") var time: String? = null
) )
object OpenType { object OpenType {

View File

@ -1,5 +1,5 @@
package ru.myitschool.work.core package ru.myitschool.work.core
object Constants { object Constants {
const val SERVER_ADDRESS = "http://192.168.1.113:8080" const val SERVER_ADDRESS = "http://10.6.66.93:8080"
} }

View File

@ -13,8 +13,8 @@ import ru.myitschool.work.ui.login.LoginDestination
import ru.myitschool.work.ui.login.LoginFragment import ru.myitschool.work.ui.login.LoginFragment
import ru.myitschool.work.ui.main.MainDestination import ru.myitschool.work.ui.main.MainDestination
import ru.myitschool.work.ui.main.MainFragment import ru.myitschool.work.ui.main.MainFragment
import ru.myitschool.work.ui.qr.scan.QrScanDestination import ru.myitschool.work.ui.scan.qr.QrScanDestination
import ru.myitschool.work.ui.qr.scan.QrScanFragment import ru.myitschool.work.ui.scan.qr.QrScanFragment
import ru.myitschool.work.ui.result.ResultDestination import ru.myitschool.work.ui.result.ResultDestination
import ru.myitschool.work.ui.result.ResultFragment import ru.myitschool.work.ui.result.ResultFragment

View File

@ -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<ListInfo, ListViewHolder>(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<ListInfo>() {
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
}
}
}

View File

@ -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<ListInfo> {
return apiService.getList(login).body() ?: emptyList()
}
}

View File

@ -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
}
}

View File

@ -1,24 +1,23 @@
package ru.myitschool.work.ui.main package ru.myitschool.work.ui.main
import android.graphics.BitmapFactory
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController 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 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.R
import ru.myitschool.work.databinding.FragmentMainBinding 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.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.AuthPreferences
import ru.myitschool.work.utils.collectWhenStarted import ru.myitschool.work.utils.collectWhenStarted
import java.net.URL import ru.myitschool.work.utils.visibleOrGone
import java.util.Locale import java.util.Locale
@AndroidEntryPoint @AndroidEntryPoint
@ -27,6 +26,7 @@ class MainFragment : Fragment(R.layout.fragment_main) {
private val binding: FragmentMainBinding get() = _binding!! private val binding: FragmentMainBinding get() = _binding!!
private lateinit var authPreferences: AuthPreferences private lateinit var authPreferences: AuthPreferences
private val viewModel: MainViewModel by viewModels() private val viewModel: MainViewModel by viewModels()
private lateinit var listAdapter: ListAdapter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -41,23 +41,41 @@ class MainFragment : Fragment(R.layout.fragment_main) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentMainBinding.bind(view) _binding = FragmentMainBinding.bind(view)
setupRecyclerView()
if (authPreferences.isLoggedIn()) { if (authPreferences.isLoggedIn()) {
setupMainComponents() setupMainComponents()
observeUserState() observeUserState()
observeListState()
viewModel.getUserData(authPreferences.getLogin() ?: "") viewModel.getUserData(authPreferences.getLogin() ?: "")
val login = authPreferences.getLogin().toString()
viewModel.loadList(login)
} }
} }
private fun hideAll() { private fun hideAll() {
binding.apply { binding.apply {
username.visibility = View.GONE username.visibleOrGone(false)
position.visibility = View.GONE position.visibleOrGone(false)
lastEntry.visibility = View.GONE lastEntry.visibleOrGone(false)
scan.visibility = View.GONE scan.visibleOrGone(false)
logout.visibility = View.GONE logout.visibleOrGone(false)
error.visibility = View.VISIBLE error.visibleOrGone(true)
refresh.visibility = View.VISIBLE 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 -> viewModel.state.collectWhenStarted(this) { state ->
when (state) { when (state) {
is MainViewModel.MainState.Initial -> { is MainViewModel.MainState.Initial -> {
binding.error.visibility = View.GONE binding.error.visibleOrGone(false)
} }
is MainViewModel.MainState.Loading -> { is MainViewModel.MainState.Loading -> {
binding.error.visibility = View.GONE binding.error.visibleOrGone(false)
} }
is MainViewModel.MainState.Success -> { is MainViewModel.MainState.Success -> {
binding.apply { binding.apply {
username.visibility = View.VISIBLE username.visibleOrGone(true)
position.visibility = View.VISIBLE position.visibleOrGone(true)
lastEntry.visibility = View.VISIBLE lastEntry.visibleOrGone(true)
scan.visibility = View.VISIBLE scan.visibleOrGone(true)
logout.visibility = View.VISIBLE logout.visibleOrGone(true)
error.visibility = View.GONE error.visibleOrGone(false)
username.text = state.userInfo.name username.text = state.userInfo.name
position.text = state.userInfo.position position.text = state.userInfo.position
@ -103,26 +121,31 @@ class MainFragment : Fragment(R.layout.fragment_main) {
} }
} }
private fun loadImageFromUrl(url: String) { private fun observeListState() {
CoroutineScope(Dispatchers.IO).launch { viewModel.listState.collectWhenStarted(this) { state ->
val bitmap = try { when (state) {
val input = URL(url).openStream() is MainViewModel.ListState.Loading -> {
BitmapFactory.decodeStream(input) //TODO
} catch (e: Exception) { }
Log.e("MainFragment", "Error loading image", e) is MainViewModel.ListState.Success -> {
null listAdapter.submitList(state.data)
} }
is MainViewModel.ListState.Error -> {
withContext(Dispatchers.Main) { //TODO
if (bitmap != null) { binding.error.text = state.message
binding.photo.setImageBitmap(bitmap)
} else {
binding.photo.setImageResource(R.drawable.ic_no_img)
} }
} }
} }
} }
private fun loadImageFromUrl(url: String) {
Picasso
.get()
.load(url)
.error(R.drawable.icon_profile)
.into(binding.photo)
}
private fun setupMainComponents() { private fun setupMainComponents() {
binding.logout.setOnClickListener { binding.logout.setOnClickListener {
authPreferences.clearLoginState() authPreferences.clearLoginState()

View File

@ -5,18 +5,22 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import ru.myitschool.work.api.list.ListInfo
import ru.myitschool.work.api.main.ApiServiceMain import ru.myitschool.work.api.main.ApiServiceMain
import ru.myitschool.work.api.main.UserInfo import ru.myitschool.work.api.main.UserInfo
import ru.myitschool.work.ui.list.ListRepository
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class MainViewModel @Inject constructor( class MainViewModel @Inject constructor(
private val apiService: ApiServiceMain private val apiService: ApiServiceMain,
private val listRepository: ListRepository
) : ViewModel() { ) : ViewModel() {
private val _state = MutableStateFlow<MainState>(MainState.Initial) private val _state = MutableStateFlow<MainState>(MainState.Initial)
@ -52,6 +56,27 @@ class MainViewModel @Inject constructor(
} }
} }
private val _listState = MutableStateFlow<ListState>(ListState.Loading)
val listState: StateFlow<ListState> = _listState
sealed class ListState {
data object Loading : ListState()
data class Success(val data: List<ListInfo>) : 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 { sealed class MainState {
data object Initial : MainState() data object Initial : MainState()
data object Loading : MainState() data object Loading : MainState()

View File

@ -13,8 +13,8 @@ import ru.myitschool.work.api.scan.CodeJson
import ru.myitschool.work.api.scan.OpenType import ru.myitschool.work.api.scan.OpenType
import ru.myitschool.work.databinding.FragmentScanResultBinding import ru.myitschool.work.databinding.FragmentScanResultBinding
import ru.myitschool.work.ui.main.MainDestination 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.AuthPreferences
import ru.myitschool.work.utils.QrPreferences
import ru.myitschool.work.utils.collectWhenStarted import ru.myitschool.work.utils.collectWhenStarted
import ru.myitschool.work.utils.getCurrentTime import ru.myitschool.work.utils.getCurrentTime
import ru.myitschool.work.utils.visibleOrGone import ru.myitschool.work.utils.visibleOrGone
@ -24,11 +24,16 @@ class ResultFragment : Fragment(R.layout.fragment_scan_result) {
private var _binding: FragmentScanResultBinding? = null private var _binding: FragmentScanResultBinding? = null
private val binding: FragmentScanResultBinding get() = _binding!! private val binding: FragmentScanResultBinding get() = _binding!!
private val viewModel: ResultViewModel by viewModels() private val viewModel: ResultViewModel by viewModels()
private lateinit var authPreferences: AuthPreferences private lateinit var authPreferences: AuthPreferences
private lateinit var qrPreferences: QrPreferences
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
authPreferences = AuthPreferences(requireContext()) authPreferences = AuthPreferences(requireContext())
qrPreferences = QrPreferences(requireContext())
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -45,18 +50,24 @@ class ResultFragment : Fragment(R.layout.fragment_scan_result) {
successIcon.visibleOrGone(false) successIcon.visibleOrGone(false)
close.setOnClickListener { close.setOnClickListener {
navigateToMainScreen() navigateToResultScreen()
} }
} }
val qrData = QrScanDestination.getDataIfExist(arguments ?: Bundle()) val qrData = qrPreferences.getQr()
authPreferences.getLogin()?.let { login ->
val currentTime = getCurrentTime() if (qrData != null) {
viewModel.open(login, CodeJson( authPreferences.getLogin()?.let { login ->
value = qrData, val currentTime = getCurrentTime()
type = OpenType.QR_TYPE, viewModel.open(login, CodeJson(
time = currentTime 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 { try {
findNavController().apply { findNavController().apply {
popBackStack(MainDestination, false) popBackStack(MainDestination, false)

View File

@ -1,6 +1,7 @@
package ru.myitschool.work.ui.qr.scan package ru.myitschool.work.ui.scan.qr
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@ -1,7 +1,9 @@
package ru.myitschool.work.ui.qr.scan package ru.myitschool.work.ui.scan.qr
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageAnalysis
import androidx.camera.mlkit.vision.MlKitAnalyzer 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 com.google.mlkit.vision.barcode.common.Barcode
import ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentQrScanBinding 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.collectWhenStarted
import ru.myitschool.work.utils.visibleOrGone import ru.myitschool.work.utils.visibleOrGone
@ -34,6 +39,14 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
) { isGranted -> viewModel.onPermissionResult(isGranted) } ) { isGranted -> viewModel.onPermissionResult(isGranted) }
private val viewModel: QrScanViewModel by viewModels() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -65,7 +78,7 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
} }
is QrScanViewModel.Action.CloseWithResult -> { is QrScanViewModel.Action.CloseWithResult -> {
sendResult(QrScanDestination.packToBundle(action.result)) sendResult(QrScanDestination.packToBundle(action.result))
goBack() goResult()
} }
} }
} }
@ -113,10 +126,31 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
} }
private fun goBack() { private fun goBack() {
val qrData = arguments?.let { QrScanDestination.getDataIfExist(it) }
qrPreferences.saveQr(qrData.toString())
findNavControllerOrNull()?.popBackStack() findNavControllerOrNull()?.popBackStack()
?: requireActivity().onBackPressedDispatcher.onBackPressed() ?: 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) { private fun sendResult(bundle: Bundle) {
setFragmentResult( setFragmentResult(
QrScanDestination.REQUEST_KEY, QrScanDestination.REQUEST_KEY,

View File

@ -1,4 +1,4 @@
package ru.myitschool.work.ui.qr.scan package ru.myitschool.work.ui.scan.qr
import android.Manifest import android.Manifest
import android.app.Application import android.app.Application

View File

@ -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"
}
}