diff --git a/app/build.gradle b/app/build.gradle index 9c99d98..543c81b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,8 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'com.google.dagger.hilt.android' + id 'kotlin-kapt' } android { @@ -9,11 +11,13 @@ android { defaultConfig { applicationId "ru.otus.basicarchitecture" - minSdk 24 + minSdk 26 targetSdk 33 versionCode 1 versionName "1.0" + buildConfigField("String", "DADATA_API_KEY", "\"${project.DADATA_API_KEY}\"") + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -24,15 +28,33 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' + } + buildFeatures{ + buildConfig true + viewBinding true } } dependencies { + implementation 'com.google.dagger:hilt-android:2.52' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1' + implementation 'androidx.fragment:fragment-ktx:1.5.6' + kapt 'com.google.dagger:hilt-compiler:2.52' + + implementation 'com.google.android.flexbox:flexbox:3.0.0' + + implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3' + implementation "com.squareup.retrofit2:retrofit:2.9.0" + implementation "com.squareup.retrofit2:converter-gson:2.9.0" + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' implementation 'androidx.core:core-ktx:1.8.0' implementation 'androidx.appcompat:appcompat:1.6.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e81fea..0543a65 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,10 @@ + + tools:targetApi="31" + android:exported="true"> + android:exported="true" > + + + + + + \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/App.kt b/app/src/main/java/ru/otus/basicarchitecture/App.kt new file mode 100644 index 0000000..ee4c1a4 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/App.kt @@ -0,0 +1,8 @@ +package ru.otus.basicarchitecture + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class App :Application(){ +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt b/app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt index 623aba9..80b75a9 100644 --- a/app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt +++ b/app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt @@ -2,10 +2,22 @@ package ru.otus.basicarchitecture import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import dagger.hilt.android.AndroidEntryPoint +import dagger.hilt.android.HiltAndroidApp +import ru.otus.basicarchitecture.fragments.PersonFragment +import javax.inject.Inject +@AndroidEntryPoint class MainActivity : AppCompatActivity() { + @Inject lateinit var personFragment:PersonFragment override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + + + supportFragmentManager.beginTransaction() + .replace(R.id.fragment_container_view_tag, personFragment) + .commit() + } } \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/cache/WizardCache.kt b/app/src/main/java/ru/otus/basicarchitecture/cache/WizardCache.kt new file mode 100644 index 0000000..46322ca --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/cache/WizardCache.kt @@ -0,0 +1,18 @@ +package ru.otus.basicarchitecture.cache + +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class WizardCache @Inject constructor(){ + + var person:Person?=null + var address:Address?=null + var interests:Interests?=null + +} + +data class Person (val name: String = "test", val surname: String = "test", val dateOfBirth: String = "01.01.1970") +data class Address(val country: String = "", val city: String = "", val address: String = "") +data class Interests(val selectedInterest: List = emptyList()) \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/dadata/DadataApi.kt b/app/src/main/java/ru/otus/basicarchitecture/dadata/DadataApi.kt new file mode 100644 index 0000000..31bd214 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/dadata/DadataApi.kt @@ -0,0 +1,16 @@ +package ru.otus.basicarchitecture.dadata + +import retrofit2.http.Body +import retrofit2.http.Headers +import retrofit2.http.POST + +interface DadataApi { + @Headers("Content-Type: application/json", "Accept: application/json") + @POST("suggestions/api/4_1/rs/suggest/address") + suspend fun getAddressSuggestions(@Body request: AddressSuggestionRequest): DadataResponse +} + +data class AddressSuggestionRequest(val query: String) + +data class DadataResponse(val suggestions: List) +data class AddressSuggestion(val value: String) \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/dadata/DadataApiService.kt b/app/src/main/java/ru/otus/basicarchitecture/dadata/DadataApiService.kt new file mode 100644 index 0000000..836a7ed --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/dadata/DadataApiService.kt @@ -0,0 +1,42 @@ +package ru.otus.basicarchitecture.dadata + +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import ru.otus.basicarchitecture.BuildConfig +import java.util.concurrent.TimeUnit + +object DadataApiService { + private const val BASE_URL = "https://suggestions.dadata.ru/" + private const val API_KEY = BuildConfig.DADATA_API_KEY + + private val client: OkHttpClient by lazy { + val loggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + val headerInterceptor = Interceptor { chain -> + val request = chain.request().newBuilder() + .addHeader("Authorization", "Token $API_KEY") + .build() + chain.proceed(request) + } + + OkHttpClient.Builder() + .addInterceptor(loggingInterceptor) + .addInterceptor(headerInterceptor) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .build() + } + + val api: DadataApi by lazy { + Retrofit.Builder() + .baseUrl(BASE_URL) + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(DadataApi::class.java) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/fragments/AddressFragment.kt b/app/src/main/java/ru/otus/basicarchitecture/fragments/AddressFragment.kt new file mode 100644 index 0000000..e6ae508 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/fragments/AddressFragment.kt @@ -0,0 +1,80 @@ +package ru.otus.basicarchitecture.fragments + +import androidx.fragment.app.viewModels +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.AutoCompleteTextView +import android.widget.Button +import android.widget.Toast +import androidx.core.widget.addTextChangedListener +import dagger.hilt.android.AndroidEntryPoint +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.viewmodel.AddressViewModel + +@AndroidEntryPoint +class AddressFragment : Fragment() { + + companion object { + fun newInstance() = AddressFragment() + } + + private val viewModel: AddressViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.fragment_address, container, false) + } + + private lateinit var addressEditText: AutoCompleteTextView + private lateinit var nextBtn: Button + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + addressEditText = view.findViewById(R.id.address) + nextBtn = view.findViewById(R.id.button_to_interest) + + + viewModel.addressSuggestions.observe(viewLifecycleOwner) { suggestions -> + val adapter = ArrayAdapter( + requireContext(), + android.R.layout.simple_dropdown_item_1line, + suggestions + ) + addressEditText.setAdapter(adapter) + if (suggestions.isNotEmpty()) { + addressEditText.showDropDown() + } + } + + addressEditText.addTextChangedListener { + if(it != null && it.length > 5) { + viewModel.updateAddress(it.toString()) + viewModel.fetchAddressSuggestions(it.toString()) + } + } + + nextBtn.setOnClickListener { + if (nextBtn.isEnabled) { + viewModel.saveData() + parentFragmentManager.beginTransaction() + .replace(R.id.fragment_container_view_tag, InterestFragment.newInstance()) + .addToBackStack(null) + .commit() + } else { + Toast.makeText(context, "Валидация не пройдена", Toast.LENGTH_SHORT).show() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/fragments/InterestFragment.kt b/app/src/main/java/ru/otus/basicarchitecture/fragments/InterestFragment.kt new file mode 100644 index 0000000..f29753a --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/fragments/InterestFragment.kt @@ -0,0 +1,81 @@ +package ru.otus.basicarchitecture.fragments + +import androidx.fragment.app.viewModels +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import com.google.android.flexbox.FlexboxLayout +import dagger.hilt.android.AndroidEntryPoint +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.viewmodel.InterestViewModel +import javax.inject.Inject + +@AndroidEntryPoint +class InterestFragment @Inject constructor(): Fragment() { + + companion object { + fun newInstance() = InterestFragment() + } + + private val viewModel: InterestViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.fragment_interest, container, false) + } + + private lateinit var tagFlexboxLayout: FlexboxLayout + private lateinit var nextBtn: Button + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + tagFlexboxLayout = view.findViewById(R.id.tagFlexboxLayout) + nextBtn = view.findViewById(R.id.button_to_summary) + + viewModel.selectedInterests.observe(viewLifecycleOwner) { selectedInterests -> + updateTags(selectedInterests) + } + + nextBtn.setOnClickListener { + viewModel.saveSelectedInterests() + parentFragmentManager.beginTransaction() + .replace(R.id.fragment_container_view_tag, SummaryFragment.newInstance()) + .addToBackStack(null) + .commit() + } + + viewModel.interests.forEach { interest -> + val tagView = createTagView(interest) + tagView.setOnClickListener { + viewModel.toggleInterest(interest) + } + tagFlexboxLayout.addView(tagView) + } + } + + private fun createTagView(interest: String): TextView { + val tagView = LayoutInflater.from(context).inflate(R.layout.tag, tagFlexboxLayout, false) as TextView + tagView.text = interest + return tagView + } + + private fun updateTags(selectedInterests: List) { + for (i in 0 until tagFlexboxLayout.childCount) { + val tagView = tagFlexboxLayout.getChildAt(i) as TextView + val interest = tagView.text.toString() + tagView.isSelected = selectedInterests.contains(interest) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/fragments/PersonFragment.kt b/app/src/main/java/ru/otus/basicarchitecture/fragments/PersonFragment.kt new file mode 100644 index 0000000..50fab8e --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/fragments/PersonFragment.kt @@ -0,0 +1,108 @@ +package ru.otus.basicarchitecture.fragments + +import androidx.fragment.app.viewModels +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.EditText +import android.widget.Toast +import androidx.core.widget.addTextChangedListener +import androidx.lifecycle.Observer +import dagger.hilt.android.AndroidEntryPoint +import hilt_aggregated_deps._ru_otus_basicarchitecture_viewmodel_PersonViewModel_HiltModules_BindsModule +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.viewmodel.PersonViewModel +import java.time.LocalDate +import java.time.Period +import java.time.format.DateTimeFormatter +import javax.inject.Inject + +@AndroidEntryPoint +class PersonFragment @Inject constructor() : Fragment() { + + private lateinit var firstNameEditText: EditText + private lateinit var lastNameEditText: EditText + private lateinit var birthDateEditText: EditText + private lateinit var nextBtn: Button + + + private val viewModel: PersonViewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.fragment_person, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + firstNameEditText = view.findViewById(R.id.name) + lastNameEditText = view.findViewById(R.id.surname) + birthDateEditText = view.findViewById(R.id.birthdate) + nextBtn = view.findViewById(R.id.button_to_address) + + + viewModel.state.observe(viewLifecycleOwner) { + if (it == null || it.name.isBlank()) { + firstNameEditText.error = "Поле не должно быть пустым" + } else { + firstNameEditText.error = null + } + + if (it == null || it.surname.isBlank()) { + lastNameEditText.error = "Поле не должно быть пустым" + } else { + lastNameEditText.error = null + } + + try { + val dob = LocalDate.parse(it.dateOfBirth, DateTimeFormatter.ofPattern("dd.MM.yyyy")) + val years = Period.between(dob, LocalDate.now()).years + if (years < 18) { + birthDateEditText.error = "Вам не исполнилось 18" + } else { + birthDateEditText.error = null + } + } catch (e: Exception) { + birthDateEditText.error = "Неверный формат даты" + } + } + + + + viewModel.state.observe(viewLifecycleOwner){ + if(firstNameEditText.text.toString()!= it.name) { + firstNameEditText.setText(it.name) + } + if(lastNameEditText.text.toString()!= it.surname) { + lastNameEditText.setText(it.surname) + } + if(birthDateEditText.text.toString()!= it.dateOfBirth) { + birthDateEditText.setText(it.dateOfBirth) + } + } + + + firstNameEditText.addTextChangedListener { viewModel.updateName(it.toString()) } + lastNameEditText.addTextChangedListener { viewModel.updateSurname(it.toString()) } + birthDateEditText.addTextChangedListener { viewModel.updateDateOfBirth(it.toString()) } + + nextBtn.setOnClickListener { + + if (viewModel.validateForm()) { + viewModel.saveData() + parentFragmentManager.beginTransaction() + .replace(R.id.fragment_container_view_tag, AddressFragment.newInstance()) + .addToBackStack(null) + .commit() + } else { + Toast.makeText(context, "Валидация не пройдена", Toast.LENGTH_SHORT).show() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/fragments/SummaryFragment.kt b/app/src/main/java/ru/otus/basicarchitecture/fragments/SummaryFragment.kt new file mode 100644 index 0000000..65b62aa --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/fragments/SummaryFragment.kt @@ -0,0 +1,66 @@ +package ru.otus.basicarchitecture.fragments + +import androidx.fragment.app.viewModels +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import com.google.android.flexbox.FlexboxLayout +import dagger.hilt.android.AndroidEntryPoint +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.viewmodel.SummaryViewModel + +@AndroidEntryPoint +class SummaryFragment : Fragment() { + + companion object { + fun newInstance() = SummaryFragment() + } + + private val viewModel: SummaryViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + + private lateinit var firstNameTextView: TextView + private lateinit var lastNameTextView: TextView + private lateinit var birthDateTextView: TextView + private lateinit var addressTextView: TextView + private lateinit var tagFlexboxLayout: FlexboxLayout + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.fragment_summary, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + firstNameTextView = view.findViewById(R.id.tvFirstName) + lastNameTextView = view.findViewById(R.id.tvLastName) + birthDateTextView = view.findViewById(R.id.tvBirthDate) + addressTextView = view.findViewById(R.id.tvAddress) + tagFlexboxLayout = view.findViewById(R.id.tagFlexboxLayout) + + firstNameTextView.text = viewModel.firstName ?: "N/A" + lastNameTextView.text = viewModel.lastName ?: "N/A" + birthDateTextView.text = viewModel.birthDate ?: "N/A" + addressTextView.text = viewModel.fullAddress + + viewModel.interests?.forEach { interest -> + val tagView = createTagView(interest) + tagFlexboxLayout.addView(tagView) + } + } + + private fun createTagView(interest: String): TextView { + val tagView = LayoutInflater.from(context).inflate(R.layout.tag, tagFlexboxLayout, false) as TextView + tagView.text = interest + return tagView + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodel/AddressViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodel/AddressViewModel.kt new file mode 100644 index 0000000..3cd15c0 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodel/AddressViewModel.kt @@ -0,0 +1,73 @@ +package ru.otus.basicarchitecture.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import ru.otus.basicarchitecture.cache.Address +import ru.otus.basicarchitecture.cache.Person +import ru.otus.basicarchitecture.cache.WizardCache +import ru.otus.basicarchitecture.dadata.AddressSuggestionRequest +import ru.otus.basicarchitecture.dadata.DadataApiService +import javax.inject.Inject + +@HiltViewModel +class AddressViewModel @Inject constructor(private val wizardCache: WizardCache) : ViewModel() { + + private val _addressSuggestions = MutableLiveData>() + val addressSuggestions: LiveData> get() = _addressSuggestions + + private val _state =MutableLiveData
() + val state : LiveData
get() = _state + + + init { + state.observeForever { validateForm() } + } + + private fun validateForm():Boolean{ + return state.value?.address.isNullOrBlank() + + } + + fun fetchAddressSuggestions(query: String) { + if (query.isBlank()) { + _addressSuggestions.value = emptyList() + return + } + + viewModelScope.launch { + try { + val response = DadataApiService.api.getAddressSuggestions(AddressSuggestionRequest(query)) + _addressSuggestions.value = response.suggestions.map { it.value } + } catch (e: Exception) { + _addressSuggestions.value = emptyList() + } + } + } + + fun saveData() { + wizardCache.address = state.value + } + + + fun updateCity(city : String){ + val currentState = _state.value ?: Address() + _state.setValue(currentState.copy(city = city)) + saveData() + } + + fun updateCountry(country : String){ + val currentState = _state.value ?: Address() + _state.value=currentState.copy(country =country) + saveData() + } + + fun updateAddress(address : String){ + val currentState = _state.value ?: Address() + _state.setValue(currentState.copy(address = address)) + saveData() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodel/InterestViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodel/InterestViewModel.kt new file mode 100644 index 0000000..dcf728d --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodel/InterestViewModel.kt @@ -0,0 +1,41 @@ +package ru.otus.basicarchitecture.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import ru.otus.basicarchitecture.cache.Interests +import ru.otus.basicarchitecture.cache.WizardCache +import javax.inject.Inject + +@HiltViewModel +class InterestViewModel @Inject constructor( +private val wizardCache: WizardCache +) : ViewModel() { + private val _interests = listOf( + "Водка", "Колька","Завод", "Деньги", "Много денег", "ОЧЕНЬ МНОГО Денег" + ) + + private val _selectedInterests = MutableLiveData>() + val selectedInterests: LiveData> get() = _selectedInterests + + val interests: List get() = _interests + + init { + _selectedInterests.value = emptyList() + } + + fun toggleInterest(interest: String) { + _selectedInterests.value = _selectedInterests.value?.let { + if (it.contains(interest)) { + it - interest + } else { + it + interest + } + } + } + + fun saveSelectedInterests() { + wizardCache.interests = Interests(selectedInterests.value?: emptyList()) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodel/PersonViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodel/PersonViewModel.kt new file mode 100644 index 0000000..6446495 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodel/PersonViewModel.kt @@ -0,0 +1,62 @@ +package ru.otus.basicarchitecture.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import ru.otus.basicarchitecture.cache.Person +import ru.otus.basicarchitecture.cache.WizardCache +import java.time.LocalDate +import java.time.Period +import java.time.format.DateTimeFormatter +import javax.inject.Inject + +@HiltViewModel +class PersonViewModel @Inject constructor(private val wizardCache: WizardCache) : ViewModel() { + + private val _state = MutableLiveData() + val state: LiveData get() = _state + + init { + _state.value = wizardCache.person?:Person() + } + + fun validateForm():Boolean { + val isNameValid = !state.value?.name.isNullOrBlank() + val isSurnameValid = !state.value?.surname.isNullOrBlank() + val isDateOfBirthValid = state.value?.dateOfBirth.let { + try { + val dob = LocalDate.parse(it, DateTimeFormatter.ofPattern("dd.MM.yyyy")) + val years = Period.between(dob,LocalDate.now()).years + years >= 18 + } catch (e: Exception) { + false + } + } + + return isNameValid && isSurnameValid && isDateOfBirthValid + } + + fun saveData() { + wizardCache.person = _state.value + } + + fun updateName(name : String){ + val currentState = _state.value ?: Person() + _state.setValue(currentState.copy(name =name)) + saveData() + } + + fun updateSurname(surname : String){ + val currentState = _state.value ?: Person() + _state.value=currentState.copy(surname =surname) + saveData() + } + + fun updateDateOfBirth(dateOfBirth : String){ + val currentState = _state.value ?: Person() + _state.setValue(currentState.copy(dateOfBirth = dateOfBirth)) + saveData() + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodel/SummaryViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodel/SummaryViewModel.kt new file mode 100644 index 0000000..336d75b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodel/SummaryViewModel.kt @@ -0,0 +1,24 @@ +package ru.otus.basicarchitecture.viewmodel + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import ru.otus.basicarchitecture.cache.WizardCache +import javax.inject.Inject + +@HiltViewModel +class SummaryViewModel @Inject constructor( + private val wizardCache: WizardCache +) : ViewModel() { + + + val firstName: String? get() = wizardCache.person?.name + val lastName: String? get() = wizardCache.person?.surname + val birthDate: String? get() = wizardCache.person?.dateOfBirth + val country: String? get() = wizardCache.address?.country + val city: String? get() = wizardCache.address?.city + val address: String? get() = wizardCache.address?.address + val interests: List? get() = wizardCache.interests?.selectedInterest + + val fullAddress: String + get() = listOfNotNull(country, city, address).joinToString(" ") +} \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_tag.xml b/app/src/main/res/drawable/selector_tag.xml new file mode 100644 index 0000000..68a0c12 --- /dev/null +++ b/app/src/main/res/drawable/selector_tag.xml @@ -0,0 +1,5 @@ + + + + + \ 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 index 0b15a20..cf1c936 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,9 +1,14 @@ - - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_address.xml b/app/src/main/res/layout/fragment_address.xml new file mode 100644 index 0000000..aee8e49 --- /dev/null +++ b/app/src/main/res/layout/fragment_address.xml @@ -0,0 +1,44 @@ + + + + + + + + +