Add template
This commit is contained in:
parent
a43a36be40
commit
4c051cd158
@ -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
@ -1 +1 @@
|
||||
Subproject commit d9590600045906edeb852eaa3f0b9bf7d1875813
|
||||
Subproject commit ec48d5f6b8c45e8058303282e9ec1c1d0ed02989
|
Loading…
x
Reference in New Issue
Block a user