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 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_interest.xml b/app/src/main/res/layout/fragment_interest.xml
new file mode 100644
index 0000000..be1de26
--- /dev/null
+++ b/app/src/main/res/layout/fragment_interest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_person.xml b/app/src/main/res/layout/fragment_person.xml
new file mode 100644
index 0000000..e393300
--- /dev/null
+++ b/app/src/main/res/layout/fragment_person.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_summary.xml b/app/src/main/res/layout/fragment_summary.xml
new file mode 100644
index 0000000..805564a
--- /dev/null
+++ b/app/src/main/res/layout/fragment_summary.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/tag.xml b/app/src/main/res/layout/tag.xml
new file mode 100644
index 0000000..35c146b
--- /dev/null
+++ b/app/src/main/res/layout/tag.xml
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f8c6127..41c7663 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,4 +7,6 @@
#FF018786
#FF000000
#FFFFFFFF
+ #0000FF
+ #888888
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index c84cccf..cda1b66 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id 'com.android.application' version '8.0.0' apply false
- id 'com.android.library' version '8.0.0' apply false
+ id 'com.android.application' version '8.5.2' apply false
+ id 'com.android.library' version '8.5.2' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
+ id 'com.google.dagger.hilt.android' version '2.52' apply false
}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index bfac0f0..9c774e2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Sun May 14 22:33:44 GST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists