Add template
This commit is contained in:
		| @@ -1,6 +1,9 @@ | ||||
| plugins { | ||||
|     kotlinAndroid | ||||
|     androidApplication | ||||
|     jetbrainsKotlinSerialization version Version.Kotlin.language | ||||
|     kotlinAnnotationProcessor | ||||
|     id("com.google.dagger.hilt.android").version("2.51.1") | ||||
| } | ||||
|  | ||||
| val packageName = "ru.myitschool.work" | ||||
| @@ -34,4 +37,33 @@ android { | ||||
| dependencies { | ||||
|     defaultLibrary() | ||||
|  | ||||
|     implementation(Dependencies.AndroidX.activity) | ||||
|     implementation(Dependencies.AndroidX.fragment) | ||||
|     implementation(Dependencies.AndroidX.constraintLayout) | ||||
|  | ||||
|     implementation(Dependencies.AndroidX.Navigation.fragment) | ||||
|     implementation(Dependencies.AndroidX.Navigation.navigationUi) | ||||
|  | ||||
|     implementation(Dependencies.Retrofit.library) | ||||
|     implementation(Dependencies.Retrofit.gsonConverter) | ||||
|  | ||||
|     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") | ||||
|  | ||||
|     val cameraX = "1.3.4" | ||||
|     implementation("androidx.camera:camera-core:$cameraX") | ||||
|     implementation("androidx.camera:camera-camera2:$cameraX") | ||||
|     implementation("androidx.camera:camera-lifecycle:$cameraX") | ||||
|     implementation("androidx.camera:camera-view:$cameraX") | ||||
|     implementation("androidx.camera:camera-mlkit-vision:1.4.0-rc04") | ||||
|  | ||||
|     val hilt = "2.51.1" | ||||
|     implementation("com.google.dagger:hilt-android:$hilt") | ||||
|     kapt("com.google.dagger:hilt-android-compiler:$hilt") | ||||
| } | ||||
|  | ||||
| kapt { | ||||
|     correctErrorTypes = true | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,12 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|  | ||||
|     <uses-feature android:name="android.hardware.camera.any" /> | ||||
|     <uses-permission android:name="android.permission.CAMERA" /> | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|  | ||||
|     <application | ||||
|         android:name=".App" | ||||
|         android:allowBackup="true" | ||||
|         android:dataExtractionRules="@xml/data_extraction_rules" | ||||
|         android:fullBackupContent="@xml/backup_rules" | ||||
| @@ -11,6 +16,16 @@ | ||||
|         android:roundIcon="@mipmap/ic_launcher_round" | ||||
|         android:supportsRtl="true" | ||||
|         android:theme="@style/Theme.Default" | ||||
|         tools:targetApi="31" /> | ||||
|         tools:targetApi="31"> | ||||
|         <activity | ||||
|             android:name=".ui.RootActivity" | ||||
|             android:exported="true"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
|  | ||||
|                 <category android:name="android.intent.category.LAUNCHER" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
							
								
								
									
										7
									
								
								app/src/main/java/ru/myitschool/work/App.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/src/main/java/ru/myitschool/work/App.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| package ru.myitschool.work | ||||
|  | ||||
| import android.app.Application | ||||
| import dagger.hilt.android.HiltAndroidApp | ||||
|  | ||||
| @HiltAndroidApp | ||||
| class App : Application() | ||||
							
								
								
									
										5
									
								
								app/src/main/java/ru/myitschool/work/core/Constants.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/java/ru/myitschool/work/core/Constants.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| package ru.myitschool.work.core | ||||
| // НЕ ИЗМЕНЯЙТЕ ЭТОТ ФАЙЛ. В ТЕСТАХ ОН БУДЕМ ВОЗВРАЩЁН В ИСХОДНОЕ СОСТОЯНИЕ | ||||
| object Constants { | ||||
|     const val SERVER_ADDRESS = "http://localhost:8090" | ||||
| } | ||||
							
								
								
									
										56
									
								
								app/src/main/java/ru/myitschool/work/ui/RootActivity.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								app/src/main/java/ru/myitschool/work/ui/RootActivity.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| package ru.myitschool.work.ui | ||||
|  | ||||
| import android.os.Bundle | ||||
| import androidx.activity.OnBackPressedCallback | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.navigation.createGraph | ||||
| import androidx.navigation.findNavController | ||||
| import androidx.navigation.fragment.NavHostFragment | ||||
| import androidx.navigation.fragment.fragment | ||||
| import dagger.hilt.android.AndroidEntryPoint | ||||
| import ru.myitschool.work.R | ||||
| import ru.myitschool.work.ui.login.LoginDestination | ||||
| import ru.myitschool.work.ui.login.LoginFragment | ||||
| import ru.myitschool.work.ui.qr.scan.QrScanDestination | ||||
| import ru.myitschool.work.ui.qr.scan.QrScanFragment | ||||
|  | ||||
| // НЕ ИЗМЕНЯЙТЕ НАЗВАНИЕ КЛАССА! | ||||
| @AndroidEntryPoint | ||||
| class RootActivity : AppCompatActivity() { | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         setContentView(R.layout.activity_root) | ||||
|  | ||||
|         val navHostFragment = supportFragmentManager | ||||
|             .findFragmentById(R.id.nav_host_fragment) as NavHostFragment? | ||||
|  | ||||
|         if (navHostFragment != null) { | ||||
|             val navController = navHostFragment.navController | ||||
|             navController.graph = navController.createGraph( | ||||
|                 startDestination = LoginDestination | ||||
|             ) { | ||||
|                 fragment<LoginFragment, LoginDestination>() | ||||
|                 fragment<QrScanFragment, QrScanDestination>() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         onBackPressedDispatcher.addCallback( | ||||
|             this, | ||||
|             object : OnBackPressedCallback(true) { | ||||
|                 override fun handleOnBackPressed() { | ||||
|                     onSupportNavigateUp() | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     override fun onSupportNavigateUp(): Boolean { | ||||
|         val navController = findNavController(R.id.nav_host_fragment) | ||||
|         val popBackResult = if (navController.previousBackStackEntry != null) { | ||||
|             navController.popBackStack() | ||||
|         } else { | ||||
|             false | ||||
|         } | ||||
|         return popBackResult || super.onSupportNavigateUp() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,6 @@ | ||||
| package ru.myitschool.work.ui.login | ||||
|  | ||||
| import kotlinx.serialization.Serializable | ||||
|  | ||||
| @Serializable | ||||
| data object LoginDestination | ||||
| @@ -0,0 +1,36 @@ | ||||
| package ru.myitschool.work.ui.login | ||||
|  | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.viewModels | ||||
| import dagger.hilt.android.AndroidEntryPoint | ||||
| import ru.myitschool.work.R | ||||
| import ru.myitschool.work.databinding.FragmentLoginBinding | ||||
| import ru.myitschool.work.utils.collectWhenStarted | ||||
| import ru.myitschool.work.utils.visibleOrGone | ||||
|  | ||||
| @AndroidEntryPoint | ||||
| class LoginFragment : Fragment(R.layout.fragment_login) { | ||||
|     private var _binding: FragmentLoginBinding? = null | ||||
|     private val binding: FragmentLoginBinding get() = _binding!! | ||||
|  | ||||
|     private val viewModel: LoginViewModel by viewModels() | ||||
|  | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         _binding = FragmentLoginBinding.bind(view) | ||||
|         subscribe() | ||||
|     } | ||||
|  | ||||
|     private fun subscribe() { | ||||
|         viewModel.state.collectWhenStarted(this) { state -> | ||||
|             binding.loading.visibleOrGone(state) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onDestroyView() { | ||||
|         _binding = null | ||||
|         super.onDestroyView() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| package ru.myitschool.work.ui.login | ||||
|  | ||||
| import android.content.Context | ||||
| import androidx.lifecycle.ViewModel | ||||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||||
| import dagger.hilt.android.qualifiers.ApplicationContext | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.asStateFlow | ||||
| import javax.inject.Inject | ||||
|  | ||||
| @HiltViewModel | ||||
| class LoginViewModel @Inject constructor( | ||||
|     @ApplicationContext private val context: Context, | ||||
| ) : ViewModel() { | ||||
|     private val _state = MutableStateFlow(true) | ||||
|     val state = _state.asStateFlow() | ||||
| } | ||||
| @@ -0,0 +1,30 @@ | ||||
| package ru.myitschool.work.ui.qr.scan | ||||
|  | ||||
| import android.os.Bundle | ||||
| import androidx.core.os.bundleOf | ||||
| import kotlinx.serialization.Serializable | ||||
|  | ||||
| // НЕ ИЗМЕНЯЙТЕ ЭТОТ ФАЙЛ. В ТЕСТАХ ОН БУДЕМ ВОЗВРАЩЁН В ИСХОДНОЕ СОСТОЯНИЕ | ||||
| @Serializable | ||||
| data object QrScanDestination { | ||||
|     const val REQUEST_KEY = "qr_result" | ||||
|     private const val KEY_QR_DATA = "key_qr" | ||||
|  | ||||
|     fun newInstance(): QrScanFragment { | ||||
|         return QrScanFragment() | ||||
|     } | ||||
|  | ||||
|     fun getDataIfExist(bundle: Bundle): String? { | ||||
|         return if (bundle.containsKey(KEY_QR_DATA)) { | ||||
|             bundle.getString(KEY_QR_DATA) | ||||
|         } else { | ||||
|             null | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal fun packToBundle(data: String): Bundle { | ||||
|         return bundleOf( | ||||
|             KEY_QR_DATA to data | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,139 @@ | ||||
| package ru.myitschool.work.ui.qr.scan | ||||
|  | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| import androidx.activity.result.contract.ActivityResultContracts | ||||
| import androidx.camera.core.ImageAnalysis | ||||
| import androidx.camera.mlkit.vision.MlKitAnalyzer | ||||
| import androidx.camera.view.LifecycleCameraController | ||||
| import androidx.camera.view.PreviewView | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.core.os.bundleOf | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.setFragmentResult | ||||
| import androidx.fragment.app.viewModels | ||||
| import androidx.navigation.NavController | ||||
| import androidx.navigation.fragment.findNavController | ||||
| import com.google.mlkit.vision.barcode.BarcodeScanner | ||||
| import com.google.mlkit.vision.barcode.BarcodeScannerOptions | ||||
| 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.utils.collectWhenStarted | ||||
| import ru.myitschool.work.utils.visibleOrGone | ||||
|  | ||||
| // НЕ ИЗМЕНЯЙТЕ ЭТОТ ФАЙЛ. В ТЕСТАХ ОН БУДЕМ ВОЗВРАЩЁН В ИСХОДНОЕ СОСТОЯНИЕ | ||||
| class QrScanFragment : Fragment(R.layout.fragment_qr_scan) { | ||||
|     private var _binding: FragmentQrScanBinding? = null | ||||
|     private val binding: FragmentQrScanBinding get() = _binding!! | ||||
|  | ||||
|     private var barcodeScanner: BarcodeScanner? = null | ||||
|     private var isCameraInit: Boolean = false | ||||
|     private val permissionLauncher = registerForActivityResult( | ||||
|         ActivityResultContracts.RequestPermission() | ||||
|     ) { isGranted -> viewModel.onPermissionResult(isGranted) } | ||||
|  | ||||
|     private val viewModel: QrScanViewModel by viewModels() | ||||
|  | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         _binding = FragmentQrScanBinding.bind(view) | ||||
|         sendResult(bundleOf()) | ||||
|         subscribe() | ||||
|         initCallback() | ||||
|     } | ||||
|  | ||||
|     private fun initCallback() { | ||||
|         binding.close.setOnClickListener { viewModel.close() } | ||||
|     } | ||||
|  | ||||
|     private fun subscribe() { | ||||
|         viewModel.state.collectWhenStarted(this) { state -> | ||||
|             binding.loading.visibleOrGone(state is QrScanViewModel.State.Loading) | ||||
|             binding.viewFinder.visibleOrGone(state is QrScanViewModel.State.Scan) | ||||
|             if (!isCameraInit && state is QrScanViewModel.State.Scan) { | ||||
|                 startCamera() | ||||
|                 isCameraInit = true | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         viewModel.action.collectWhenStarted(this) { action -> | ||||
|             when (action) { | ||||
|                 is QrScanViewModel.Action.RequestPermission -> requestPermission(action.permission) | ||||
|                 is QrScanViewModel.Action.CloseWithCancel -> { | ||||
|                     goBack() | ||||
|                 } | ||||
|                 is QrScanViewModel.Action.CloseWithResult -> { | ||||
|                     sendResult(QrScanDestination.packToBundle(action.result)) | ||||
|                     goBack() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun requestPermission(permission: String) { | ||||
|         permissionLauncher.launch(permission) | ||||
|     } | ||||
|  | ||||
|     private fun startCamera() { | ||||
|         val context = requireContext() | ||||
|         val cameraController = LifecycleCameraController(context) | ||||
|         val previewView: PreviewView = binding.viewFinder | ||||
|         val executor = ContextCompat.getMainExecutor(context) | ||||
|  | ||||
|         val options = BarcodeScannerOptions.Builder() | ||||
|             .setBarcodeFormats(Barcode.FORMAT_QR_CODE) | ||||
|             .build() | ||||
|         val barcodeScanner = BarcodeScanning.getClient(options) | ||||
|         this.barcodeScanner = barcodeScanner | ||||
|  | ||||
|         cameraController.setImageAnalysisAnalyzer( | ||||
|             executor, | ||||
|             MlKitAnalyzer( | ||||
|                 listOf(barcodeScanner), | ||||
|                 ImageAnalysis.COORDINATE_SYSTEM_VIEW_REFERENCED, | ||||
|                 executor | ||||
|             ) { result -> | ||||
|                 result?.getValue(barcodeScanner)?.firstOrNull()?.let { value -> | ||||
|                     viewModel.findBarcode(value) | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|  | ||||
|         cameraController.bindToLifecycle(this) | ||||
|         previewView.controller = cameraController | ||||
|     } | ||||
|  | ||||
|     override fun onDestroyView() { | ||||
|         barcodeScanner?.close() | ||||
|         barcodeScanner = null | ||||
|         _binding = null | ||||
|         super.onDestroyView() | ||||
|     } | ||||
|  | ||||
|     private fun goBack() { | ||||
|         findNavControllerOrNull()?.popBackStack() | ||||
|             ?: requireActivity().onBackPressedDispatcher.onBackPressed() | ||||
|     } | ||||
|  | ||||
|     private fun sendResult(bundle: Bundle) { | ||||
|         setFragmentResult( | ||||
|             QrScanDestination.REQUEST_KEY, | ||||
|             bundle | ||||
|         ) | ||||
|         findNavControllerOrNull() | ||||
|             ?.previousBackStackEntry | ||||
|             ?.savedStateHandle | ||||
|             ?.set(QrScanDestination.REQUEST_KEY, bundle) | ||||
|     } | ||||
|  | ||||
|     private fun findNavControllerOrNull(): NavController? { | ||||
|         return try { | ||||
|             findNavController() | ||||
|         } catch (_: Throwable) { | ||||
|             null | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,93 @@ | ||||
| package ru.myitschool.work.ui.qr.scan | ||||
|  | ||||
| import android.Manifest | ||||
| import android.app.Application | ||||
| import android.content.pm.PackageManager | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.lifecycle.AndroidViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import com.google.mlkit.vision.barcode.common.Barcode | ||||
| import kotlinx.coroutines.delay | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.asSharedFlow | ||||
| import kotlinx.coroutines.flow.asStateFlow | ||||
| import kotlinx.coroutines.flow.update | ||||
| import kotlinx.coroutines.launch | ||||
| import ru.myitschool.work.utils.MutablePublishFlow | ||||
|  | ||||
| // НЕ ИЗМЕНЯЙТЕ ЭТОТ ФАЙЛ. В ТЕСТАХ ОН БУДЕМ ВОЗВРАЩЁН В ИСХОДНОЕ СОСТОЯНИЕ | ||||
| class QrScanViewModel( | ||||
|     application: Application | ||||
| ) : AndroidViewModel(application) { | ||||
|  | ||||
|     private val _action = MutablePublishFlow<Action>() | ||||
|     val action = _action.asSharedFlow() | ||||
|  | ||||
|     private val _state = MutableStateFlow<State>(initialState) | ||||
|     val state = _state.asStateFlow() | ||||
|  | ||||
|     init { | ||||
|         checkPermission() | ||||
|     } | ||||
|  | ||||
|     fun onPermissionResult(isGranted: Boolean) { | ||||
|         viewModelScope.launch { | ||||
|             if (isGranted) { | ||||
|                 _state.update { State.Scan } | ||||
|             } else { | ||||
|                 _action.emit(Action.CloseWithCancel) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun checkPermission() { | ||||
|         viewModelScope.launch { | ||||
|             val isPermissionGranted = ContextCompat.checkSelfPermission( | ||||
|                 getApplication(), | ||||
|                 CAMERA_PERMISSION | ||||
|             ) == PackageManager.PERMISSION_GRANTED | ||||
|             if (isPermissionGranted) { | ||||
|                 _state.update { State.Scan } | ||||
|             } else { | ||||
|                 delay(1000) | ||||
|                 _action.emit(Action.RequestPermission(CAMERA_PERMISSION)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun findBarcode(barcode: Barcode) { | ||||
|         viewModelScope.launch { | ||||
|             barcode.rawValue?.let { value -> | ||||
|                 _action.emit(Action.CloseWithResult(value)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun close() { | ||||
|         viewModelScope.launch { | ||||
|             _action.emit(Action.CloseWithCancel) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sealed interface State { | ||||
|         data object Loading : State | ||||
|  | ||||
|         data object Scan : State | ||||
|     } | ||||
|  | ||||
|     sealed interface Action { | ||||
|         data class RequestPermission( | ||||
|             val permission: String | ||||
|         ) : Action | ||||
|         data object CloseWithCancel : Action | ||||
|         data class CloseWithResult( | ||||
|             val result: String | ||||
|         ) : Action | ||||
|     } | ||||
|  | ||||
|     private companion object { | ||||
|         val initialState = State.Loading | ||||
|  | ||||
|         const val CAMERA_PERMISSION = Manifest.permission.CAMERA | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								app/src/main/java/ru/myitschool/work/utils/FlowExtensions.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/java/ru/myitschool/work/utils/FlowExtensions.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| package ru.myitschool.work.utils | ||||
|  | ||||
| import kotlinx.coroutines.channels.BufferOverflow | ||||
| import kotlinx.coroutines.flow.MutableSharedFlow | ||||
|  | ||||
| fun <T> MutablePublishFlow() = MutableSharedFlow<T>( | ||||
|     replay = 0, | ||||
|     extraBufferCapacity = 1, | ||||
|     BufferOverflow.DROP_OLDEST | ||||
| ) | ||||
| @@ -0,0 +1,18 @@ | ||||
| package ru.myitschool.work.utils | ||||
|  | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.lifecycle.flowWithLifecycle | ||||
| import androidx.lifecycle.lifecycleScope | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.launch | ||||
|  | ||||
| inline fun <T> Flow<T>.collectWhenStarted( | ||||
|     fragment: Fragment, | ||||
|     crossinline collector: (T) -> Unit | ||||
| ) { | ||||
|     fragment.viewLifecycleOwner.lifecycleScope.launch { | ||||
|         flowWithLifecycle(fragment.viewLifecycleOwner.lifecycle).collect { value -> | ||||
|             collector.invoke(value) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| package ru.myitschool.work.utils | ||||
|  | ||||
| import android.text.Editable | ||||
| import android.text.TextWatcher | ||||
|  | ||||
| open class TextChangedListener: TextWatcher { | ||||
|     override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit | ||||
|  | ||||
|     override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit | ||||
|  | ||||
|     override fun afterTextChanged(s: Editable?) = Unit | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| package ru.myitschool.work.utils | ||||
|  | ||||
| import android.view.View | ||||
|  | ||||
| fun View.visibleOrGone(isVisible: Boolean) { | ||||
|     this.visibility = if (isVisible) View.VISIBLE else View.GONE | ||||
| } | ||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/ic_close.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/ic_close.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/> | ||||
|      | ||||
| </vector> | ||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/ic_logout.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/ic_logout.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/> | ||||
|      | ||||
| </vector> | ||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/ic_no_img.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/ic_no_img.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M21.9,21.9l-8.49,-8.49l0,0L3.59,3.59l0,0L2.1,2.1L0.69,3.51L3,5.83V19c0,1.1 0.9,2 2,2h13.17l2.31,2.31L21.9,21.9zM5,18l3.5,-4.5l2.5,3.01L12.17,15l3,3H5zM21,18.17L5.83,3H19c1.1,0 2,0.9 2,2V18.17z"/> | ||||
|      | ||||
| </vector> | ||||
							
								
								
									
										25
									
								
								app/src/main/res/drawable/ic_qr_code.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/src/main/res/drawable/ic_qr_code.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M3,11h8V3H3V11zM5,5h4v4H5V5z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M3,21h8v-8H3V21zM5,15h4v4H5V15z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M13,3v8h8V3H13zM19,9h-4V5h4V9z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M19,19h2v2h-2z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M13,13h2v2h-2z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M15,15h2v2h-2z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M13,17h2v2h-2z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M15,19h2v2h-2z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M17,17h2v2h-2z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M17,13h2v2h-2z"/> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M19,15h2v2h-2z"/> | ||||
|      | ||||
| </vector> | ||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/ic_refresh.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/ic_refresh.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> | ||||
|        | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/> | ||||
|      | ||||
| </vector> | ||||
							
								
								
									
										13
									
								
								app/src/main/res/layout/activity_root.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/src/main/res/layout/activity_root.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <FrameLayout 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.fragment.app.FragmentContainerView | ||||
|         android:id="@+id/nav_host_fragment" | ||||
|         android:name="androidx.navigation.fragment.NavHostFragment" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         app:defaultNavHost="true" /> | ||||
| </FrameLayout> | ||||
							
								
								
									
										16
									
								
								app/src/main/res/layout/fragment_login.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/src/main/res/layout/fragment_login.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?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"> | ||||
|  | ||||
|     <ProgressBar | ||||
|         android:id="@+id/loading" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         app:layout_constraintEnd_toEndOf="parent" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent" | ||||
|         app:layout_constraintBottom_toBottomOf="parent" /> | ||||
|  | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
							
								
								
									
										35
									
								
								app/src/main/res/layout/fragment_qr_scan.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								app/src/main/res/layout/fragment_qr_scan.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| <?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" | ||||
|         app:layout_constraintStart_toStartOf="parent" | ||||
|         app:layout_constraintTop_toTopOf="parent" /> | ||||
| </androidx.constraintlayout.widget.ConstraintLayout> | ||||
							
								
								
									
										4
									
								
								app/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <item name="rootContainer" type="id"/> | ||||
| </resources> | ||||
| @@ -1,3 +1,3 @@ | ||||
| <resources> | ||||
|     <string name="app_name">Work</string> | ||||
|     <string name="app_name">NTO Pass</string> | ||||
| </resources> | ||||
							
								
								
									
										4
									
								
								app/src/main/res/values/strings_qr.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/src/main/res/values/strings_qr.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <string name="close_button">Close</string> | ||||
| </resources> | ||||
| @@ -2,4 +2,5 @@ | ||||
| plugins { | ||||
|     androidApplication version Version.agp apply false | ||||
|     kotlinJvm version Version.Kotlin.language apply false | ||||
|     id("com.google.dagger.hilt.android") version "2.51.1" apply false | ||||
| } | ||||
							
								
								
									
										2
									
								
								buildSrc
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								buildSrc
									
									
									
									
									
								
							 Submodule buildSrc updated: d959060004...ec48d5f6b8
									
								
							
		Reference in New Issue
	
	Block a user