diff --git a/app/build.gradle b/app/build.gradle index 9c99d98..7918184 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,11 +1,13 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'com.google.dagger.hilt.android' + id 'kotlin-kapt' } android { namespace 'ru.otus.basicarchitecture' - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "ru.otus.basicarchitecture" @@ -24,11 +26,11 @@ 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' } } @@ -38,7 +40,32 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.fragment:fragment-ktx:1.5.6' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + + implementation 'com.google.android.flexbox:flexbox:3.0.0' + + + // Hilt + implementation 'com.google.dagger:hilt-android:2.52' + kapt 'com.google.dagger:hilt-compiler:2.52' + + def lifecycle_version = "2.6.1" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" + implementation 'androidx.activity:activity-ktx:1.7.2' + + // Retrofit2 + 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' +} + +kapt { + correctErrorTypes true } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e81fea..19a3ac8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,10 @@ + + android:name=".ui.MainActivity" + android:exported="true"> + + + + + + \ 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 deleted file mode 100644 index 623aba9..0000000 --- a/app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ru.otus.basicarchitecture - -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle - -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/di/MyApplication.kt b/app/src/main/java/ru/otus/basicarchitecture/di/MyApplication.kt new file mode 100644 index 0000000..f829e8e --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/di/MyApplication.kt @@ -0,0 +1,23 @@ +package ru.otus.basicarchitecture.di + +import android.app.Application +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.HiltAndroidApp +import dagger.hilt.components.SingletonComponent +import ru.otus.basicarchitecture.repository.WizardCache +import javax.inject.Singleton + +@HiltAndroidApp +class MyApplication : Application() + +@InstallIn(SingletonComponent::class) +@Module +object CacheModule { + @Provides + @Singleton + fun provideWizardCache(): WizardCache { + return WizardCache() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/network/DadataApi.kt b/app/src/main/java/ru/otus/basicarchitecture/network/DadataApi.kt new file mode 100644 index 0000000..24cc7cb --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/network/DadataApi.kt @@ -0,0 +1,16 @@ +package ru.otus.basicarchitecture.network + +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) diff --git a/app/src/main/java/ru/otus/basicarchitecture/network/DadataApiService.kt b/app/src/main/java/ru/otus/basicarchitecture/network/DadataApiService.kt new file mode 100644 index 0000000..6ee23e4 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/network/DadataApiService.kt @@ -0,0 +1,41 @@ +package ru.otus.basicarchitecture.network + +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +object DadataApiService { + private const val BASE_URL = "https://suggestions.dadata.ru/" + private const val API_KEY = "8fa10df494b20be347e86fb921643f852a13cc07" + + 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) + } +} diff --git a/app/src/main/java/ru/otus/basicarchitecture/repository/WizardCache.kt b/app/src/main/java/ru/otus/basicarchitecture/repository/WizardCache.kt new file mode 100644 index 0000000..dbc988a --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/repository/WizardCache.kt @@ -0,0 +1,18 @@ +package ru.otus.basicarchitecture.repository + +import javax.inject.Inject + +class WizardCache @Inject constructor() { + var firstName: String? = null + var lastName: String? = null + var birthDate: String? = null + var country: String? = null + var city: String? = null + var address: String? = null + var interests: List = emptyList() + + fun setInterests(interests: Set) { + this.interests = interests.toList() + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/ui/MainActivity.kt b/app/src/main/java/ru/otus/basicarchitecture/ui/MainActivity.kt new file mode 100644 index 0000000..a49d853 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/ui/MainActivity.kt @@ -0,0 +1,24 @@ +package ru.otus.basicarchitecture.ui + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import androidx.fragment.app.Fragment +import dagger.hilt.android.AndroidEntryPoint +import ru.otus.basicarchitecture.ui.fragments.Fragment1 +import ru.otus.basicarchitecture.R +@AndroidEntryPoint +class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + loadFragment(Fragment1()) + } + + private fun loadFragment(fragment: Fragment) { + val transaction = supportFragmentManager.beginTransaction() + transaction.replace(R.id.fragment_container, fragment) + transaction.addToBackStack("null") + transaction.commit() + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment1.kt b/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment1.kt new file mode 100644 index 0000000..7692722 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment1.kt @@ -0,0 +1,107 @@ +package ru.otus.basicarchitecture.ui.fragments + +import android.os.Build +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 androidx.annotation.RequiresApi +import androidx.core.widget.addTextChangedListener +import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.viewmodels.Fragment1ViewModel +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit + +@AndroidEntryPoint +class Fragment1 : Fragment() { + private val viewModel: Fragment1ViewModel by viewModels() + + private lateinit var firstNameEditText: EditText + private lateinit var lastNameEditText: EditText + private lateinit var birthDateEditText: EditText + private lateinit var nextBtn: Button + + private lateinit var toast: View + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.fragment_fragment1, container, false) + } + + + @RequiresApi(Build.VERSION_CODES.O) + 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.fragment1Btn) + toast = view.findViewById(R.id.toast) + + viewModel.firstName.observe(viewLifecycleOwner) { + if (it == null || it.isBlank()) { + firstNameEditText.error = "Поле не должно быть пустым" + } else { + firstNameEditText.error = null + } + } + + viewModel.lastName.observe(viewLifecycleOwner) { + if (it == null || it.isBlank()) { + lastNameEditText.error = "Поле не должно быть пустым" + } else { + lastNameEditText.error = null + } + } + + viewModel.birthDate.observe(viewLifecycleOwner) { + try { + val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") + val dob = LocalDate.parse(it, formatter) + val years = ChronoUnit.YEARS.between(dob, LocalDate.now()) + if (years < 18) { + birthDateEditText.error = "Вам не исполнилось 18" + } else { + birthDateEditText.error = null + } + } catch (e: Exception) { + birthDateEditText.error = "Неверный формат даты" + } + } + + viewModel.isFormValid.observe(viewLifecycleOwner) { isValid -> + nextBtn.isEnabled = isValid + } + + firstNameEditText.addTextChangedListener { viewModel.firstName.value = it.toString() } + lastNameEditText.addTextChangedListener { viewModel.lastName.value = it.toString() } + birthDateEditText.addTextChangedListener { viewModel.birthDate.value = it.toString() } + + nextBtn.setOnClickListener { + viewModel.firstName.value = firstNameEditText.text.toString() + viewModel.lastName.value = lastNameEditText.text.toString() + viewModel.birthDate.value = birthDateEditText.text.toString() + + if (nextBtn.isEnabled) { + viewModel.saveData() + parentFragmentManager.beginTransaction() + .replace(R.id.fragment_container, Fragment2()) + .addToBackStack(null) + .commit() + } else { + Snackbar.make(toast, "Валидация не пройдена", Snackbar.LENGTH_SHORT).show() + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment2.kt b/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment2.kt new file mode 100644 index 0000000..878347b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment2.kt @@ -0,0 +1,78 @@ +package ru.otus.basicarchitecture.ui.fragments + +import android.os.Build +import android.os.Bundle +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 androidx.annotation.RequiresApi +import androidx.core.widget.addTextChangedListener +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.viewmodels.Fragment2ViewModel + +@AndroidEntryPoint +class Fragment2 : Fragment() { + private val viewModel: Fragment2ViewModel by viewModels() + + private lateinit var addressEditText: AutoCompleteTextView + private lateinit var nextBtn: Button + + private lateinit var toast: View + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.fragment_fragment2, container, false) + } + + @RequiresApi(Build.VERSION_CODES.O) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + addressEditText = view.findViewById(R.id.address) + nextBtn = view.findViewById(R.id.fragment2Btn) + toast = view.findViewById(R.id.toast) + + viewModel.isFormValid.observe(viewLifecycleOwner) { isValid -> + nextBtn.isEnabled = isValid + } + + 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 { + viewModel.address.value = it.toString() + viewModel.fetchAddressSuggestions(it.toString()) + } + + nextBtn.setOnClickListener { + viewModel.address.value = addressEditText.text.toString() + if (nextBtn.isEnabled) { + viewModel.saveData() + parentFragmentManager.beginTransaction() + .replace(R.id.fragment_container, Fragment3()) + .addToBackStack(null) + .commit() + } else { + Snackbar.make(toast, "Валидация не пройдена", Snackbar.LENGTH_SHORT).show() + } + } + } +} diff --git a/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment3.kt b/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment3.kt new file mode 100644 index 0000000..ab0dabb --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment3.kt @@ -0,0 +1,70 @@ +package ru.otus.basicarchitecture.ui.fragments + +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 androidx.fragment.app.viewModels +import com.google.android.flexbox.FlexboxLayout +import dagger.hilt.android.AndroidEntryPoint +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.viewmodels.Fragment3ViewModel + +@AndroidEntryPoint +class Fragment3 : Fragment() { + private val viewModel: Fragment3ViewModel by viewModels() + + private lateinit var tagFlexboxLayout: FlexboxLayout + private lateinit var nextBtn: Button + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.fragment_fragment3, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + tagFlexboxLayout = view.findViewById(R.id.tagFlexboxLayout) + nextBtn = view.findViewById(R.id.fragment3Btn) + + viewModel.selectedInterests.observe(viewLifecycleOwner) { selectedInterests -> + updateTags(selectedInterests) + } + + nextBtn.setOnClickListener { + viewModel.saveSelectedInterests() + parentFragmentManager.beginTransaction() + .replace(R.id.fragment_container, Fragment4()) + .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_item, tagFlexboxLayout, false) as TextView + tagView.text = interest + return tagView + } + + private fun updateTags(selectedInterests: Set) { + 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/ui/fragments/Fragment4.kt b/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment4.kt new file mode 100644 index 0000000..e84f59b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment4.kt @@ -0,0 +1,57 @@ +package ru.otus.basicarchitecture.ui.fragments + +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 androidx.fragment.app.viewModels +import com.google.android.flexbox.FlexboxLayout +import dagger.hilt.android.AndroidEntryPoint +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.viewmodels.Fragment4ViewModel + +@AndroidEntryPoint +class Fragment4 : Fragment() { + private val viewModel: Fragment4ViewModel by viewModels() + + 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_fragment4, 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_item, tagFlexboxLayout, false) as TextView + tagView.text = interest + return tagView + } +} diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment1ViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment1ViewModel.kt new file mode 100644 index 0000000..ad1b8aa --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment1ViewModel.kt @@ -0,0 +1,53 @@ +package ru.otus.basicarchitecture.viewmodels + +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import ru.otus.basicarchitecture.repository.WizardCache +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit +import javax.inject.Inject +@RequiresApi(Build.VERSION_CODES.O) +@HiltViewModel +class Fragment1ViewModel @Inject constructor(private val wizardCache: WizardCache) : ViewModel() { + + val firstName = MutableLiveData() + val lastName = MutableLiveData() + val birthDate = MutableLiveData() + + private val _isFormValid = MutableLiveData() + val isFormValid: LiveData get() = _isFormValid + + init { + firstName.observeForever { validateForm() } + lastName.observeForever { validateForm() } + birthDate.observeForever { validateForm() } + } + + private fun validateForm() { + val isNameValid = !firstName.value.isNullOrBlank() + val isSurnameValid = !lastName.value.isNullOrBlank() + val isDateOfBirthValid = birthDate.value?.let { + try { + val dob = LocalDate.parse(it, DateTimeFormatter.ofPattern("dd.MM.yyyy")) + val years = ChronoUnit.YEARS.between(dob, LocalDate.now()) + years >= 18 + } catch (e: Exception) { + false + } + } ?: false + + _isFormValid.value = isNameValid && isSurnameValid && isDateOfBirthValid + } + + fun saveData() { + wizardCache.firstName = firstName.value + wizardCache.lastName = lastName.value + wizardCache.birthDate = birthDate.value + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment2ViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment2ViewModel.kt new file mode 100644 index 0000000..7048e9f --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment2ViewModel.kt @@ -0,0 +1,55 @@ +package ru.otus.basicarchitecture.viewmodels + +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.network.AddressSuggestionRequest +import ru.otus.basicarchitecture.network.DadataApiService +import ru.otus.basicarchitecture.repository.WizardCache +import javax.inject.Inject + +@HiltViewModel +class Fragment2ViewModel @Inject constructor(private val wizardCache: WizardCache) : ViewModel() { + + val address = MutableLiveData() + private val _isFormValid = MutableLiveData() + val isFormValid: LiveData get() = _isFormValid + + private val _addressSuggestions = MutableLiveData>() + val addressSuggestions: LiveData> get() = _addressSuggestions + + init { + address.observeForever { validateForm() } + } + + private fun validateForm() { + val isAddressValid = !address.value.isNullOrBlank() + _isFormValid.value = isAddressValid + } + + 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() { + val addressParts = address.value?.split(",")?.map { it.trim() } + wizardCache.country = addressParts?.getOrNull(0) + wizardCache.city = addressParts?.getOrNull(1) + wizardCache.address = addressParts?.getOrNull(2) + } +} diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment3ViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment3ViewModel.kt new file mode 100644 index 0000000..a61f023 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment3ViewModel.kt @@ -0,0 +1,43 @@ +package ru.otus.basicarchitecture.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import ru.otus.basicarchitecture.repository.WizardCache +import javax.inject.Inject + +@HiltViewModel +class Fragment3ViewModel @Inject constructor( + private val wizardCache: WizardCache +) : ViewModel() { + + private val _interests = listOf( + "Sport", "Music", "Travel", "Reading", "Movies", "Games", "Cooking", "Art" + ) + + private val _selectedInterests = MutableLiveData>() + val selectedInterests: LiveData> get() = _selectedInterests + + val interests: List get() = _interests + + init { + _selectedInterests.value = emptySet() + } + + fun toggleInterest(interest: String) { + _selectedInterests.value = _selectedInterests.value?.let { + if (it.contains(interest)) { + it - interest + } else { + it + interest + } + } + } + + fun saveSelectedInterests() { + _selectedInterests.value?.let { + wizardCache.setInterests(it) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment4ViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment4ViewModel.kt new file mode 100644 index 0000000..73946a8 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/Fragment4ViewModel.kt @@ -0,0 +1,23 @@ +package ru.otus.basicarchitecture.viewmodels + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import ru.otus.basicarchitecture.repository.WizardCache +import javax.inject.Inject + +@HiltViewModel +class Fragment4ViewModel @Inject constructor( + private val wizardCache: WizardCache +) : ViewModel() { + + val firstName: String? get() = wizardCache.firstName + val lastName: String? get() = wizardCache.lastName + val birthDate: String? get() = wizardCache.birthDate + val country: String? get() = wizardCache.country + val city: String? get() = wizardCache.city + val address: String? get() = wizardCache.address + val interests: List get() = wizardCache.interests + + val fullAddress: String + get() = listOfNotNull(country, city, address).joinToString(" ") +} diff --git a/app/src/main/java/ru/otus/basicarchitecture/viewmodels/MainViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/MainViewModel.kt new file mode 100644 index 0000000..1b62a0c --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/viewmodels/MainViewModel.kt @@ -0,0 +1,6 @@ +package ru.otus.basicarchitecture.viewmodels + +import androidx.lifecycle.ViewModel +import javax.inject.Inject + +class MainViewModel @Inject constructor() : ViewModel() \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_tag_background.xml b/app/src/main/res/drawable/selector_tag_background.xml new file mode 100644 index 0000000..0a7bff2 --- /dev/null +++ b/app/src/main/res/drawable/selector_tag_background.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/tag_default_background.xml b/app/src/main/res/drawable/tag_default_background.xml new file mode 100644 index 0000000..2598f24 --- /dev/null +++ b/app/src/main/res/drawable/tag_default_background.xml @@ -0,0 +1,4 @@ + + + + \ 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..0510d5b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,6 +4,11 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity"> + tools:context=".ui.MainActivity"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_fragment1.xml b/app/src/main/res/layout/fragment_fragment1.xml new file mode 100644 index 0000000..ca0fb18 --- /dev/null +++ b/app/src/main/res/layout/fragment_fragment1.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + +