From dc426bf75ac5ffd81612a0696a7618ba2f4f3066 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 19 Feb 2025 17:39:09 +0300 Subject: [PATCH] version2 --- app/build.gradle.kts | 6 +- app/src/main/AndroidManifest.xml | 10 +- .../work/ui/login/LoginActivity.java | 115 +++++++++++++++ .../work/ui/login/LoginViewModel.java | 56 +++++++ .../myitschool/work/ui/main/MainActivity.java | 26 ++++ .../work/ui/qr/scan/QrScanDestination.kt | 30 ---- .../work/ui/qr/scan/QrScanFragment.kt | 139 ------------------ .../work/ui/qr/scan/QrScanViewModel.kt | 93 ------------ .../work/utils/TextChangedListener.java | 30 ++++ .../work/utils/TextChangedListener.kt | 12 -- app/src/main/res/drawable/account.png | Bin 0 -> 433 bytes app/src/main/res/drawable/background.png | Bin 0 -> 4202 bytes app/src/main/res/drawable/management.png | Bin 0 -> 535 bytes app/src/main/res/drawable/office.png | Bin 0 -> 387 bytes app/src/main/res/drawable/scanqr.png | Bin 0 -> 409 bytes app/src/main/res/layout/activity_login.xml | 96 ++++++++++++ app/src/main/res/layout/activity_main.xml | 36 +++++ app/src/main/res/layout/fragment_main.xml | 115 +++++++++++++++ app/src/main/res/menu/bottom_nav_menu.xml | 30 ++++ app/src/main/res/values/strings.xml | 4 + 20 files changed, 514 insertions(+), 284 deletions(-) create mode 100644 app/src/main/java/ru/myitschool/work/ui/login/LoginActivity.java create mode 100644 app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.java create mode 100644 app/src/main/java/ru/myitschool/work/ui/main/MainActivity.java delete mode 100644 app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanDestination.kt delete mode 100644 app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanFragment.kt delete mode 100644 app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanViewModel.kt create mode 100644 app/src/main/java/ru/myitschool/work/utils/TextChangedListener.java delete mode 100644 app/src/main/java/ru/myitschool/work/utils/TextChangedListener.kt create mode 100644 app/src/main/res/drawable/account.png create mode 100644 app/src/main/res/drawable/background.png create mode 100644 app/src/main/res/drawable/management.png create mode 100644 app/src/main/res/drawable/office.png create mode 100644 app/src/main/res/drawable/scanqr.png create mode 100644 app/src/main/res/layout/activity_login.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/fragment_main.xml create mode 100644 app/src/main/res/menu/bottom_nav_menu.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 55805e6..705a308 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,12 +10,12 @@ val packageName = "ru.myitschool.work" android { namespace = packageName - compileSdk = Version.Android.Sdk.compile + compileSdk = 35 defaultConfig { applicationId = packageName - minSdk = Version.Android.Sdk.min - targetSdk = Version.Android.Sdk.target + minSdk = 35 + targetSdk = 35 versionCode = 1 versionName = "1.0" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 44f6361..2ae5448 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> @@ -8,7 +8,6 @@ + tools:targetApi="31"> - + android:exported="true"> diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginActivity.java b/app/src/main/java/ru/myitschool/work/ui/login/LoginActivity.java new file mode 100644 index 0000000..a2ef4b8 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginActivity.java @@ -0,0 +1,115 @@ +package ru.myitschool.work.ui.login; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.text.Editable; +import android.view.View; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.lifecycle.ViewModelProvider; + +import com.example.myapplication.core.SettingConstants; +import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.textfield.TextInputEditText; + +import ru.myitschool.work.R; +import ru.myitschool.work.databinding.ActivityLoginBinding; +import ru.myitschool.work.ui.main.MainActivity; + +public class LoginActivity extends AppCompatActivity { + + private ActivityLoginBinding binding; + private LoginViewModel viewModel; + + + public LoginActivity() { + super(R.layout.activity_login); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_login); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + + binding = ActivityLoginBinding.bind(v); + viewModel = new ViewModelProvider(this).get(ActivityLoginBinding.class); + + binding.email.addTextChangedListener(new TextChangedListener<>(binding.email) { + @Override + public void onTextChanged(TextInputEditText target, Editable s) { + listenerEmailEditText(s); + } + }); + + binding.password.addTextChangedListener(new TextChangedListener<>(binding.password) { + @Override + public void onTextChanged(TextInputEditText target, Editable s) { + listenerPasswordEditText(s); + } + }); + + binding.btEnter.setOnClickListener(this.onClickListenerLoginButton); + + subscribe(); + }); + } + + private void subscribe() { + viewModel.errorLiveData.observe(getViewLifecycleOwner(), error -> { + binding.btEnter.setEnabled(true); + Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show(); + }); + viewModel.openLiveData.observe(getViewLifecycleOwner(), employee -> { + binding.btEnter.setEnabled(true); + + SharedPreferences settings = requireView().getContext().getSharedPreferences( + SettingConstants.PREFS_FILE, Context.MODE_PRIVATE + ); + settings.edit().putLong(SettingConstants.PREF_ID, employee.getId()).apply(); + settings.edit().putString(SettingConstants.PREF_ROLE, employee.getRole()).apply(); + + startActivity(new Intent(getApplicationContext(), MainActivity.class)); + }); + } + + private void listenerEmailEditText(Editable s) { + if (s.toString().isEmpty()) { + binding.email.setError("Обязательное поле"); + } else if (!isEmailValid(s.toString())) { + binding.emailLay.setError("Неверный формат"); + } else { + binding.emailLay.setErrorEnabled(false); + } + } + + private void listenerPasswordEditText(Editable s) { + if (s.toString().isEmpty()) { + binding.passwordLay.setError("Обязательное поле"); + } else { + binding.passwordLay.setErrorEnabled(false); + } + } + + private void onClickListenerLoginButton(View view) { + binding.btEnter.setEnabled(false); + + viewModel.changeEmail(String.valueOf(binding.email.getText())); + viewModel.changePassword(String.valueOf(binding.password.getText())); + viewModel.confirm(); + } + + private boolean isEmailValid(String email) { + return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches(); + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.java b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.java new file mode 100644 index 0000000..2b7a25d --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.java @@ -0,0 +1,56 @@ +package ru.myitschool.work.ui.login; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import ru.myitschool.work.api.data.SignRepositoryImpl; +import ru.myitschool.work.api.domain.entity.employee.EmpolyeeEntity; +import ru.myitschool.work.api.domain.useCases.sign.LoginEmployeeUseCase; + +public class LoginViewModel { + private final MutableLiveData mutableErrorLiveData = new MutableLiveData<>(); + public final LiveData errorLiveData = mutableErrorLiveData; + + private final MutableLiveData mutableOpenLiveData = new MutableLiveData<>(); + public final LiveData openLiveData = mutableOpenLiveData; + + private final LoginEmployeeUseCase loginEmployeeUseCase = new LoginEmployeeUseCase( + SignRepositoryImpl.getInstance() + ); + + @Nullable + private String email; + @Nullable + private String password; + + public void changeEmail(@NonNull String email) { + this.email = email; + } + + public void changePassword(@NonNull String password) { + this.password = password; + } + + public void confirm() { + final String currentEmail = email; + final String currentPassword = password; + + if (currentEmail == null || currentEmail.isEmpty()) { + mutableErrorLiveData.postValue("Пароль пустой!"); + return; + } + if (currentPassword == null || currentPassword.isEmpty()) { + mutableErrorLiveData.postValue("email пустой!"); + return; + } + loginEmployeeUseCase.execute(currentEmail, currentPassword, status -> { + if (status.getStatusCode() == 200 && status.getErrors() == null && status.getValue() != null) { + mutableOpenLiveData.postValue(status.getValue()); + } else if (status.getStatusCode() == 401) mutableErrorLiveData.postValue("Данные не верны. Попробуйте ещё разок :("); + else mutableErrorLiveData.postValue("Вы не подключены к интернету :("); + }); + } + +} diff --git a/app/src/main/java/ru/myitschool/work/ui/main/MainActivity.java b/app/src/main/java/ru/myitschool/work/ui/main/MainActivity.java new file mode 100644 index 0000000..339e6a7 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/main/MainActivity.java @@ -0,0 +1,26 @@ +package ru.myitschool.work.ui.main; + +import android.os.Bundle; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import ru.myitschool.work.R; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_main); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanDestination.kt b/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanDestination.kt deleted file mode 100644 index 7e34b28..0000000 --- a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanDestination.kt +++ /dev/null @@ -1,30 +0,0 @@ -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 - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanFragment.kt b/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanFragment.kt deleted file mode 100644 index a9ddaab..0000000 --- a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanFragment.kt +++ /dev/null @@ -1,139 +0,0 @@ -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 - } - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanViewModel.kt deleted file mode 100644 index 14565ab..0000000 --- a/app/src/main/java/ru/myitschool/work/ui/qr/scan/QrScanViewModel.kt +++ /dev/null @@ -1,93 +0,0 @@ -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() - val action = _action.asSharedFlow() - - private val _state = MutableStateFlow(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 - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/utils/TextChangedListener.java b/app/src/main/java/ru/myitschool/work/utils/TextChangedListener.java new file mode 100644 index 0000000..4e3be1e --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/utils/TextChangedListener.java @@ -0,0 +1,30 @@ +package ru.myitschool.work.utils; + +import android.text.Editable; +import android.text.TextWatcher; + +public abstract class TextChangedListener implements TextWatcher { + private final T target; + + public TextChangedListener(T target) { + this.target = target; + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + + } + + public abstract void onTextChanged(T target, Editable s); +} + diff --git a/app/src/main/java/ru/myitschool/work/utils/TextChangedListener.kt b/app/src/main/java/ru/myitschool/work/utils/TextChangedListener.kt deleted file mode 100644 index c81147d..0000000 --- a/app/src/main/java/ru/myitschool/work/utils/TextChangedListener.kt +++ /dev/null @@ -1,12 +0,0 @@ -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 -} \ No newline at end of file diff --git a/app/src/main/res/drawable/account.png b/app/src/main/res/drawable/account.png new file mode 100644 index 0000000000000000000000000000000000000000..0afe7f4d66b8e4da80af4575cd0a92e15080eee3 GIT binary patch literal 433 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 Z_6pjgXaiUexk3N{002ovPDHLkV1mk<=zstK literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/office.png b/app/src/main/res/drawable/office.png new file mode 100644 index 0000000000000000000000000000000000000000..82b5ef57a38366938e08e2157c639cc73e7c5c12 GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l5$8 za(7}_cTVOdki(Mh=T6dg`7~~RUP+xC)F#Drp}t&R~5q7rd{#dIOtM_=6s&KHSH2s(>_HWUaRVM>-UoK zLl@*q=!U z$v;bH`WDn&=kS#=+~V2P&iRN*-}TCKG#WgbP0l+XkKdn=nc literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/scanqr.png b/app/src/main/res/drawable/scanqr.png new file mode 100644 index 0000000000000000000000000000000000000000..88b90251089968efaa50da47b495c8a7e8ead8ad GIT binary patch literal 409 zcmV;K0cQS*P)rza0ez^d>a1&zKpk$Q1uEP!7uv~Ct<+^E$~Lqnywj`$?nSN zxeS9hOy@2520hSWVBnb8whm);HVqu;hDUITCcJA`U9u2j)r6Kwu{xYPQn2=6JHl#SSR7OK3BGe zI!#;b%(REqKIgM(;tDY(N}c2{Qtq>$la<6FMwN@RxNX`0=(S^i)s^y~|4VrUH|^0- z8eN=sdgqE=Upm@7-@Og2z9T8GudeY%1M6xZ&+eK5wpl_hQEhF600000NkvXXu0mjf DZJDcn literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..b9e5d30 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..c025673 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,36 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml new file mode 100644 index 0000000..e170100 --- /dev/null +++ b/app/src/main/res/layout/fragment_main.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + +