From fe3f34933138f396d1c3d799093c995d5336e7d6 Mon Sep 17 00:00:00 2001 From: dimangty Date: Sun, 14 Jul 2024 22:30:54 +0700 Subject: [PATCH 1/3] Add screens --- app/build.gradle | 21 ++- app/src/main/AndroidManifest.xml | 9 +- .../java/ru/otus/basicarchitecture/App.kt | 10 ++ .../basicarchitecture/Core/Model/Address.kt | 7 + .../basicarchitecture/Core/Model/BaseModel.kt | 3 + .../basicarchitecture/Core/Model/Interests.kt | 5 + .../basicarchitecture/Core/Model/Person.kt | 22 +++ .../Core/Model/ViewModelFactory.kt | 13 ++ .../DI/Fragment1/Fragment1SubComponent.kt | 17 +++ .../DI/Fragment1/Fragment1ViewModelModule.kt | 18 +++ .../DI/Fragment1/PersonModule.kt | 17 +++ .../DI/Fragment2/AddressModule.kt | 14 ++ .../DI/Fragment2/Fragment2SubComponent.kt | 17 +++ .../DI/Fragment2/Fragment2ViewModelModule.kt | 17 +++ .../DI/Fragment3/Fragment3SubComponent.kt | 19 +++ .../DI/Fragment3/Fragment3ViewModelModule.kt | 18 +++ .../DI/Fragment4/Fragment4SubComponent.kt | 19 +++ .../DI/Fragment4/Fragment4ViewModelModule.kt | 18 +++ .../basicarchitecture/DI/FragmentScope.kt | 7 + .../DI/MainComponent/MainComponent.kt | 23 +++ .../DI/MainComponent/RepositoryModule.kt | 14 ++ .../DI/MainComponent/WizardCacheModule.kt | 23 +++ .../Domain/Data/AddressUseCase.kt | 13 ++ .../Domain/Data/InterestsUseCase.kt | 17 +++ .../Domain/Data/PersonUseCase.kt | 22 +++ .../basicarchitecture/Domain/Repository.kt | 14 ++ .../ru/otus/basicarchitecture/MainActivity.kt | 11 ++ .../Ui/Fragment1/Fragment1.kt | 137 ++++++++++++++++++ .../Ui/Fragment1/Fragment1ViewModel.kt | 75 ++++++++++ .../Ui/Fragment2/Fragment2.kt | 88 +++++++++++ .../Ui/Fragment2/Fragment2ViewModel.kt | 49 +++++++ .../Ui/Fragment3/Fragment3.kt | 82 +++++++++++ .../Ui/Fragment3/Fragment3ViewModel.kt | 61 ++++++++ .../Ui/Fragment3/InterestsAdapter.kt | 53 +++++++ .../Ui/Fragment3/InterestsDiffUtil.kt | 25 ++++ .../Ui/Fragment3/InterestsViewHolder.kt | 10 ++ .../Ui/Fragment3/ModelInterestsForView.kt | 20 +++ .../Ui/Fragment4/Fragment4.kt | 66 +++++++++ .../Ui/Fragment4/Fragment4Adapter.kt | 31 ++++ .../Ui/Fragment4/Fragment4Model.kt | 9 ++ .../Ui/Fragment4/Fragment4ViewModel.kt | 30 ++++ .../basicarchitecture/data/RepositoryImpl.kt | 111 ++++++++++++++ .../basicarchitecture/data/WizardCache.kt | 21 +++ .../res/drawable/background_state_button.xml | 4 + app/src/main/res/drawable/button_selector.xml | 7 + app/src/main/res/drawable/calendar.xml | 5 + .../res/drawable/form_text_view_for_rv.xml | 10 ++ app/src/main/res/layout/activity_main.xml | 5 + app/src/main/res/layout/fragment1.xml | 92 ++++++++++++ app/src/main/res/layout/fragment2.xml | 92 ++++++++++++ app/src/main/res/layout/fragment3.xml | 36 +++++ app/src/main/res/layout/fragment4.xml | 130 +++++++++++++++++ .../main/res/layout/interests_disabled.xml | 25 ++++ app/src/main/res/layout/interests_enabled.xml | 26 ++++ app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 8 + 56 files changed, 1710 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/ru/otus/basicarchitecture/App.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Core/Model/Address.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Core/Model/BaseModel.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Core/Model/Interests.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Core/Model/Person.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Core/Model/ViewModelFactory.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/Fragment1SubComponent.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/Fragment1ViewModelModule.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/PersonModule.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/AddressModule.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/Fragment2SubComponent.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/Fragment2ViewModelModule.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment3/Fragment3SubComponent.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment3/Fragment3ViewModelModule.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment4/Fragment4SubComponent.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/Fragment4/Fragment4ViewModelModule.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/FragmentScope.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/MainComponent.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/RepositoryModule.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/WizardCacheModule.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Domain/Data/AddressUseCase.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Domain/Data/InterestsUseCase.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Domain/Data/PersonUseCase.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Domain/Repository.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment1/Fragment1.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment1/Fragment1ViewModel.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment2/Fragment2.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment2/Fragment2ViewModel.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/Fragment3.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/Fragment3ViewModel.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsAdapter.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsDiffUtil.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsViewHolder.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/ModelInterestsForView.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4Adapter.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4Model.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4ViewModel.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/data/RepositoryImpl.kt create mode 100644 app/src/main/java/ru/otus/basicarchitecture/data/WizardCache.kt create mode 100644 app/src/main/res/drawable/background_state_button.xml create mode 100644 app/src/main/res/drawable/button_selector.xml create mode 100644 app/src/main/res/drawable/calendar.xml create mode 100644 app/src/main/res/drawable/form_text_view_for_rv.xml create mode 100644 app/src/main/res/layout/fragment1.xml create mode 100644 app/src/main/res/layout/fragment2.xml create mode 100644 app/src/main/res/layout/fragment3.xml create mode 100644 app/src/main/res/layout/fragment4.xml create mode 100644 app/src/main/res/layout/interests_disabled.xml create mode 100644 app/src/main/res/layout/interests_enabled.xml diff --git a/app/build.gradle b/app/build.gradle index 9c99d98..81bc092 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,16 +1,17 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id("org.jetbrains.kotlin.kapt") } android { namespace 'ru.otus.basicarchitecture' - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "ru.otus.basicarchitecture" - minSdk 24 - targetSdk 33 + minSdk 26 + targetSdk 34 versionCode 1 versionName "1.0" @@ -24,15 +25,23 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + buildFeatures { + viewBinding true } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' + } + kapt { + correctErrorTypes true } } dependencies { + implementation("com.google.dagger:dagger:2.48.1") + kapt("com.google.dagger:dagger-compiler:2.48.1") 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..0d9a3dd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> + 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..b7f3b2b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/App.kt @@ -0,0 +1,10 @@ +package ru.otus.basicarchitecture + +import android.app.Application +import ru.otus.basicarchitecture.DI.MainComponent.DaggerMainComponent + +class App : Application() { + val component by lazy { + DaggerMainComponent.builder().build() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Address.kt b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Address.kt new file mode 100644 index 0000000..92fe4b4 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Address.kt @@ -0,0 +1,7 @@ +package ru.otus.basicarchitecture.Core.Model + +data class Address( + val country: String, + val city: String, + val address: String +) : BaseModel \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Core/Model/BaseModel.kt b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/BaseModel.kt new file mode 100644 index 0000000..c6590e7 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/BaseModel.kt @@ -0,0 +1,3 @@ +package ru.otus.basicarchitecture.Core.Model + +sealed interface BaseModel \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Interests.kt b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Interests.kt new file mode 100644 index 0000000..c0b7a78 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Interests.kt @@ -0,0 +1,5 @@ +package ru.otus.basicarchitecture.Core.Model + +data class Interests ( + var interests: String +) : BaseModel \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Person.kt b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Person.kt new file mode 100644 index 0000000..d7ebfc1 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/Person.kt @@ -0,0 +1,22 @@ +package ru.otus.basicarchitecture.Core.Model + +import ru.otus.basicarchitecture.DI.FragmentScope + +@FragmentScope +data class Person( + var firstName: String, + var surName: String, + var dateOfBirth: String +) : BaseModel { + companion object { + const val defaultValueProperty = "" + + fun defaultPerson(): Person { + return Person( + defaultValueProperty, + defaultValueProperty, + defaultValueProperty + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Core/Model/ViewModelFactory.kt b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/ViewModelFactory.kt new file mode 100644 index 0000000..a47bf1c --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Core/Model/ViewModelFactory.kt @@ -0,0 +1,13 @@ +package ru.otus.basicarchitecture.Core.Model + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject + +class ViewModelFactory @Inject constructor( + private val viewModels: @JvmSuppressWildcards Map +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return viewModels[modelClass.simpleName] as T + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/Fragment1SubComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/Fragment1SubComponent.kt new file mode 100644 index 0000000..29dc82f --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/Fragment1SubComponent.kt @@ -0,0 +1,17 @@ +package ru.otus.basicarchitecture.DI.Fragment1 + +import dagger.Subcomponent +import ru.otus.basicarchitecture.DI.FragmentComponents.FirstScreen.PersonModule +import ru.otus.basicarchitecture.DI.FragmentScope +import ru.otus.basicarchitecture.Ui.Fragment1.Fragment1 + +@FragmentScope +@Subcomponent(modules = [PersonModule::class, Fragment1ViewModelModule::class]) +interface Fragment1SubComponent { + @Subcomponent.Builder + interface Builder { + fun build() : Fragment1SubComponent + } + + fun inject(fragment1: Fragment1) +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/Fragment1ViewModelModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/Fragment1ViewModelModule.kt new file mode 100644 index 0000000..2d4e8f4 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/Fragment1ViewModelModule.kt @@ -0,0 +1,18 @@ +package ru.otus.basicarchitecture.DI.Fragment1 + +import androidx.lifecycle.ViewModel +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey +import ru.otus.basicarchitecture.Ui.Fragment1.Fragment1ViewModel + +@Module +interface Fragment1ViewModelModule { + + @IntoMap + @StringKey("Fragment1ViewModel") + @Binds + fun bindFirstScreenViewModel(impl: Fragment1ViewModel): ViewModel + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/PersonModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/PersonModule.kt new file mode 100644 index 0000000..cb27c5b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment1/PersonModule.kt @@ -0,0 +1,17 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.FirstScreen + +import dagger.Module +import dagger.Provides +import ru.otus.basicarchitecture.Core.Model.Person +import ru.otus.basicarchitecture.DI.FragmentScope + + +@Module +class PersonModule { + @FragmentScope + @Provides + fun providePerson(): Person { + return Person.defaultPerson() + } +} + diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/AddressModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/AddressModule.kt new file mode 100644 index 0000000..e45a59b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/AddressModule.kt @@ -0,0 +1,14 @@ +package ru.otus.basicarchitecture.DI.Fragment2 +import dagger.Module +import dagger.Provides +import ru.otus.basicarchitecture.Core.Model.Address +import ru.otus.basicarchitecture.DI.FragmentScope + +@Module +class AddressModule { + @FragmentScope + @Provides + fun providesAddress(): Address { + return Address("","","") + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/Fragment2SubComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/Fragment2SubComponent.kt new file mode 100644 index 0000000..5021d4b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/Fragment2SubComponent.kt @@ -0,0 +1,17 @@ +package ru.otus.basicarchitecture.DI.Fragment2 + +import dagger.Subcomponent +import ru.otus.basicarchitecture.DI.FragmentComponents.FirstScreen.PersonModule +import ru.otus.basicarchitecture.DI.FragmentScope +import ru.otus.basicarchitecture.Ui.Fragment1.Fragment2 + +@FragmentScope +@Subcomponent(modules = [PersonModule::class, Fragment2ViewModelModule::class]) +interface Fragment2SubComponent { + @Subcomponent.Builder + interface Builder { + fun build() : Fragment2SubComponent + } + + fun inject(fragment2: Fragment2) +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/Fragment2ViewModelModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/Fragment2ViewModelModule.kt new file mode 100644 index 0000000..0f1f02c --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment2/Fragment2ViewModelModule.kt @@ -0,0 +1,17 @@ +package ru.otus.basicarchitecture.DI.Fragment2 +import androidx.lifecycle.ViewModel +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey +import ru.otus.basicarchitecture.Ui.Fragment1.Fragment2ViewModel + +@Module +interface Fragment2ViewModelModule { + + @IntoMap + @StringKey("Fragment2ViewModel") + @Binds + fun bindFirstScreenViewModel(impl: Fragment2ViewModel): ViewModel + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment3/Fragment3SubComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment3/Fragment3SubComponent.kt new file mode 100644 index 0000000..77c0dd9 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment3/Fragment3SubComponent.kt @@ -0,0 +1,19 @@ +package ru.otus.basicarchitecture.DI.Fragment3 + +import dagger.Subcomponent +import ru.otus.basicarchitecture.DI.FragmentScope +import ru.otus.basicarchitecture.Ui.Fragment3.Fragment3 + +@FragmentScope +@Subcomponent(modules = [Fragment3ViewModelModule::class]) +interface Fragment3SubComponent { + + @Subcomponent.Builder + interface Builder { + fun build() : Fragment3SubComponent + } + + fun inject(fragmentThird: Fragment3) + + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment3/Fragment3ViewModelModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment3/Fragment3ViewModelModule.kt new file mode 100644 index 0000000..0e704b9 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment3/Fragment3ViewModelModule.kt @@ -0,0 +1,18 @@ +package ru.otus.basicarchitecture.DI.Fragment3 + +import androidx.lifecycle.ViewModel +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey +import ru.otus.basicarchitecture.Ui.Fragment3.Fragment3ViewModel + +@Module +interface Fragment3ViewModelModule { + + @IntoMap + @StringKey("Fragment3ViewModel") + @Binds + fun bindThirdScreenViewModel(impl: Fragment3ViewModel): ViewModel + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment4/Fragment4SubComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment4/Fragment4SubComponent.kt new file mode 100644 index 0000000..e86eb8a --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment4/Fragment4SubComponent.kt @@ -0,0 +1,19 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.Fragment4 + +import dagger.Subcomponent +import ru.otus.basicarchitecture.DI.FragmentScope +import ru.otus.basicarchitecture.Ui.Fragment4.Fragment4 + +@FragmentScope +@Subcomponent(modules = [Fragment4ViewModelModule::class]) +interface Fragment4SubComponent { + + + @Subcomponent.Builder + interface Builder { + fun build() : Fragment4SubComponent + } + + fun inject(fragment: Fragment4) + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment4/Fragment4ViewModelModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment4/Fragment4ViewModelModule.kt new file mode 100644 index 0000000..3a65868 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/Fragment4/Fragment4ViewModelModule.kt @@ -0,0 +1,18 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.Fragment4 + +import androidx.lifecycle.ViewModel +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey +import ru.otus.basicarchitecture.Ui.Fragment4.Fragment4ViewModel + + +@Module +interface Fragment4ViewModelModule { + + @IntoMap + @StringKey("Fragment4ViewModel") + @Binds + fun bindFourthScreenViewModel(impl: Fragment4ViewModel): ViewModel +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentScope.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentScope.kt new file mode 100644 index 0000000..8141879 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentScope.kt @@ -0,0 +1,7 @@ +package ru.otus.basicarchitecture.DI + +import javax.inject.Scope + +@Scope +@Retention(value = AnnotationRetention.RUNTIME) +annotation class FragmentScope() \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/MainComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/MainComponent.kt new file mode 100644 index 0000000..d6f9107 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/MainComponent.kt @@ -0,0 +1,23 @@ +package ru.otus.basicarchitecture.DI.MainComponent + +import dagger.Component +import ru.otus.basicarchitecture.DI.Fragment1.Fragment1SubComponent +import ru.otus.basicarchitecture.DI.Fragment2.Fragment2SubComponent +import ru.otus.basicarchitecture.DI.Fragment3.Fragment3SubComponent +import ru.otus.basicarchitecture.DI.FragmentComponents.Fragment4.Fragment4SubComponent +import ru.otus.basicarchitecture.Domain.Repository +import ru.otus.basicarchitecture.data.WizardCache +import javax.inject.Singleton + +@Singleton +@Component(modules = [WizardCacheModule::class, RepositoryModule::class]) +interface MainComponent { + fun fragment1SubComponent() : Fragment1SubComponent.Builder + fun fragment2SubComponent() : Fragment2SubComponent.Builder + fun fragment3SubComponent() : Fragment3SubComponent.Builder + fun fragment4SubComponent() : Fragment4SubComponent.Builder + + fun wizardCache(): WizardCache + + fun repository(): Repository +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/RepositoryModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/RepositoryModule.kt new file mode 100644 index 0000000..0c5e0df --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/RepositoryModule.kt @@ -0,0 +1,14 @@ +package ru.otus.basicarchitecture.DI.MainComponent + +import dagger.Binds +import dagger.Module +import ru.otus.basicarchitecture.data.RepositoryImpl +import ru.otus.basicarchitecture.Domain.Repository +import javax.inject.Singleton + +@Module +interface RepositoryModule { + @Singleton + @Binds + fun getRepository(impl: RepositoryImpl) : Repository +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/WizardCacheModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/WizardCacheModule.kt new file mode 100644 index 0000000..93d74ee --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/WizardCacheModule.kt @@ -0,0 +1,23 @@ +package ru.otus.basicarchitecture.DI.MainComponent + +import dagger.Module +import dagger.Provides +import ru.otus.basicarchitecture.data.WizardCache +import javax.inject.Singleton + +@Module +class WizardCacheModule { + @Singleton + @Provides + fun provideWizardCache() : WizardCache{ + return WizardCache( + "", + "", + "", + "", + "", + "", + "" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/AddressUseCase.kt b/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/AddressUseCase.kt new file mode 100644 index 0000000..ca68e78 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/AddressUseCase.kt @@ -0,0 +1,13 @@ +package ru.otus.basicarchitecture.Domain.Data + +import ru.otus.basicarchitecture.Core.Model.Address +import ru.otus.basicarchitecture.Domain.Repository +import javax.inject.Inject + +class AddressUseCase @Inject constructor( + private val repository: Repository +) { + fun setAddress(address: Address){ + repository.setAddress(address) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/InterestsUseCase.kt b/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/InterestsUseCase.kt new file mode 100644 index 0000000..b9a20e6 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/InterestsUseCase.kt @@ -0,0 +1,17 @@ +package ru.otus.basicarchitecture.Domain.Data + +import ru.otus.basicarchitecture.Core.Model.Interests +import ru.otus.basicarchitecture.Domain.Repository +import javax.inject.Inject + +class InterestsUseCase @Inject constructor( + private val repository: Repository +) { + fun setInterests(interests: Interests){ + repository.setInterests(interests) + } + + fun getList(): List{ + return repository.getListInterests() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/PersonUseCase.kt b/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/PersonUseCase.kt new file mode 100644 index 0000000..6173731 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Domain/Data/PersonUseCase.kt @@ -0,0 +1,22 @@ +package ru.otus.basicarchitecture.Domain.Data + +import ru.otus.basicarchitecture.Core.Model.BaseModel +import ru.otus.basicarchitecture.Core.Model.Person +import ru.otus.basicarchitecture.Domain.Repository +import javax.inject.Inject + +class PersonUseCase @Inject constructor( + private val repository: Repository +) { + fun setPerson(person: Person) { + repository.setPerson(person) + } + + fun getList(): List{ + return repository.getListInterests() + } + + fun getPerson(): Map { + return repository.getInfoPersonUseCase() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Domain/Repository.kt b/app/src/main/java/ru/otus/basicarchitecture/Domain/Repository.kt new file mode 100644 index 0000000..0c3d604 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Domain/Repository.kt @@ -0,0 +1,14 @@ +package ru.otus.basicarchitecture.Domain + +import ru.otus.basicarchitecture.Core.Model.Address +import ru.otus.basicarchitecture.Core.Model.BaseModel +import ru.otus.basicarchitecture.Core.Model.Interests +import ru.otus.basicarchitecture.Core.Model.Person + +interface Repository { + fun setPerson(person: Person) + fun getInfoPersonUseCase() : Map + fun setAddress(address: Address) + fun setInterests(interests: Interests) + fun getListInterests(): List +} \ 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..22a92d5 100644 --- a/app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt +++ b/app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt @@ -2,10 +2,21 @@ package ru.otus.basicarchitecture import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import ru.otus.basicarchitecture.Ui.Fragment1.Fragment1 +import ru.otus.basicarchitecture.Ui.Fragment3.Fragment3 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + + if(savedInstanceState == null ){ + val fragment = Fragment1() + supportFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, fragment) + .addToBackStack(null) + .commit() + } } } \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment1/Fragment1.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment1/Fragment1.kt new file mode 100644 index 0000000..05ed3b2 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment1/Fragment1.kt @@ -0,0 +1,137 @@ +package ru.otus.basicarchitecture.Ui.Fragment1 + +import android.app.DatePickerDialog +import android.content.Context +import android.os.Bundle +import android.text.InputType +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import ru.otus.basicarchitecture.App +import ru.otus.basicarchitecture.Core.Model.ViewModelFactory +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.databinding.Fragment1Binding +import java.util.Calendar +import java.util.Locale +import javax.inject.Inject + +class Fragment1: Fragment() { + private lateinit var binding: Fragment1Binding + @Inject + lateinit var viewModelFactory: ViewModelFactory + private val MESSAGE_TOAST_INCORRECT_AGE ="Возраст пользователя не может быть меньше 18" + + private val viewModel by lazy { + ViewModelProvider(this, viewModelFactory)[Fragment1ViewModel::class.java] + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + binding = Fragment1Binding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + (requireActivity().application as App).component + .fragment1SubComponent() + .build() + .inject(this) + } + + override fun onResume() { + super.onResume() + setupView() + } + + private fun setupView() { + binding.birthInputLayout.apply { + startIconDrawable = ContextCompat.getDrawable(context, R.drawable.calendar) + startIconContentDescription = context.getString(R.string.DateOfBirthHint) + } + binding.birthEditText.apply { + inputType = InputType.TYPE_NULL + // isEnabled = false + isCursorVisible = false + keyListener = null + + onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus -> + if (hasFocus) { + showDatePicker(view.context) + }else{ + viewModel.validateEmptyValue() + } + this.clearFocus() + } + + } + setFocusListener() + + binding.nextButton.let { button -> + viewModel.enabledButtonLiveData.observe(viewLifecycleOwner) { + button.isEnabled = it + } + } + + binding.nextButton.setOnClickListener { + viewModel.setData( + binding.firstNameEditText.text.toString(), + binding.surNameEditText.text.toString(), + binding.birthEditText.text.toString() + ) { openSecondFragment() } + } + } + + + private fun setFocusListener(){ + val lossFocus = View.OnFocusChangeListener{_,hasFocus -> + if(!hasFocus){ + viewModel.validateEmptyValue() + } + } + binding.firstNameEditText.onFocusChangeListener = lossFocus + binding.surNameEditText.onFocusChangeListener = lossFocus + + } + + private fun openSecondFragment() { + val fragment = Fragment2() + parentFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, fragment) + .addToBackStack(null) + .commit() + } + + // возможно надо перенести внутрь ViewModel + private fun showDatePicker(context: Context) { + val currentDate = Calendar.getInstance() + val datePicker = DatePickerDialog( + context, + { _, year, month, day -> + val selectedData = + String.format(Locale.getDefault(), "%04d-%02d-%02d", year, month + 1, day) + viewModel.validData(day, month, year, + { showToast(MESSAGE_TOAST_INCORRECT_AGE, context) } + ) + binding.birthEditText.setText(selectedData) + }, + currentDate.get(Calendar.YEAR), + currentDate.get(Calendar.MONTH), + currentDate.get(Calendar.DAY_OF_MONTH) + ) + datePicker.show() + } + + + + + private fun showToast(message: String, context: Context) { + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment1/Fragment1ViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment1/Fragment1ViewModel.kt new file mode 100644 index 0000000..3e0a29e --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment1/Fragment1ViewModel.kt @@ -0,0 +1,75 @@ +package ru.otus.basicarchitecture.Ui.Fragment1 + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import ru.otus.basicarchitecture.Core.Model.Person +import ru.otus.basicarchitecture.Domain.Data.PersonUseCase +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit +import javax.inject.Inject + +class Fragment1ViewModel @Inject constructor( + val personUseCase: PersonUseCase +) : ViewModel() { + private val enabledButtonMutableLiveData = MutableLiveData() + val enabledButtonLiveData = enabledButtonMutableLiveData + + private var name = UNKNOWN_VALUE + private var surName = UNKNOWN_VALUE + private var birthDate = UNKNOWN_VALUE + + + init { + enabledButtonMutableLiveData.postValue(false) + } + + fun setData(name: String?, surName: String?, birthDate: String?, openFragment: () -> Unit) { + this.name = name ?: UNKNOWN_VALUE + this.surName = surName ?: UNKNOWN_VALUE + this.birthDate = birthDate ?: UNKNOWN_VALUE + if(validateEmptyValue()){ + val person = Person(this.name, this.surName, this.birthDate) + personUseCase.setPerson(person) + openFragment.invoke() + } + } + + fun validateEmptyValue(): Boolean { + return if (name == UNKNOWN_VALUE || surName == UNKNOWN_VALUE || birthDate == UNKNOWN_VALUE) { + enabledButtonMutableLiveData.postValue(false) + false + } else { + enabledButtonMutableLiveData.postValue(true) + true + } + } + + fun validData(day: Int, month: Int, year: Int, showToast: () -> Unit) { + val correctAge = 18 + val currentDate = LocalDate.now() + val monthText = if (month < 9) "0${month + 1}" else "${month + 1}" + val dayText = if (day<10) "0${day+1}" else "$day" + val selectedDateValue = LocalDate.parse("$year-$monthText-$dayText", DateTimeFormatter.ISO_DATE) + val diff = ChronoUnit.YEARS.between(selectedDateValue, currentDate) + if (diff < correctAge) { + setupFalseButton(showToast) + } else { + enabledButtonLiveData.postValue(true) + } + } + + private fun setupFalseButton(showToast: () -> Unit) { + enabledButtonMutableLiveData.postValue(false) + showToast.invoke() + } + + fun enabledButton(){ + enabledButtonMutableLiveData.postValue(true) + } + + + companion object { + private val UNKNOWN_VALUE = "" + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment2/Fragment2.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment2/Fragment2.kt new file mode 100644 index 0000000..fbce190 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment2/Fragment2.kt @@ -0,0 +1,88 @@ +package ru.otus.basicarchitecture.Ui.Fragment1 + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import ru.otus.basicarchitecture.App +import ru.otus.basicarchitecture.Core.Model.ViewModelFactory +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.Ui.Fragment3.Fragment3 +import ru.otus.basicarchitecture.databinding.Fragment2Binding +import javax.inject.Inject + +class Fragment2: Fragment() { + private lateinit var binding: Fragment2Binding + @Inject + lateinit var viewModelFactory: ViewModelFactory + + private val viewModel by lazy { + ViewModelProvider(this, viewModelFactory)[Fragment2ViewModel::class.java] + } + + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + binding = Fragment2Binding.inflate(inflater, container, false) + return binding.root + } + + + override fun onResume() { + super.onResume() + setupView() + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + (requireActivity().application as App).component + .fragment2SubComponent() + .build() + .inject(this) + setupView() + } + + private fun setupView() { + binding.nextButton.let { button -> + setListeners() + viewModel.enabledButtonLiveData.observe(viewLifecycleOwner) { + button.isEnabled = it + } + } + + + setFocusListener() + } + + private fun setListeners() { + binding.nextButton.setOnClickListener { + viewModel.setData( + binding.countryEditText.text.toString(), + binding.cityEditText.text.toString(), + binding.addressEditText.text.toString() + ) { openFragment() } + } + } + + + private fun openFragment() { + val fragment = Fragment3() + parentFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, fragment) + .addToBackStack(null) + .commit() + } + + private fun setFocusListener() { + val lossFocus = View.OnFocusChangeListener { _, hasFocus -> + if (hasFocus) viewModel.buttonEnabled() + } + binding.countryEditText.onFocusChangeListener = lossFocus + binding.cityEditText.onFocusChangeListener = lossFocus + binding.addressEditText.onFocusChangeListener = lossFocus + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment2/Fragment2ViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment2/Fragment2ViewModel.kt new file mode 100644 index 0000000..1feb4a5 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment2/Fragment2ViewModel.kt @@ -0,0 +1,49 @@ +package ru.otus.basicarchitecture.Ui.Fragment1 + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import ru.otus.basicarchitecture.Core.Model.Address +import ru.otus.basicarchitecture.Domain.Data.AddressUseCase +import javax.inject.Inject + +class Fragment2ViewModel @Inject constructor ( + private val addressUseCase: AddressUseCase +) : ViewModel() { + + + private val enabledButtonMutableLiveData = MutableLiveData() + val enabledButtonLiveData = enabledButtonMutableLiveData + private val UNKNOWN_VALUE = "" + private var country: String = UNKNOWN_VALUE + private var city: String = UNKNOWN_VALUE + private var address: String = UNKNOWN_VALUE + + init { + enabledButtonMutableLiveData.postValue(true) + } + + fun setData(country: String?, city: String?, address: String?, openFragment: () -> Unit){ + this.country = country ?: UNKNOWN_VALUE + this.city = city ?: UNKNOWN_VALUE + this.address = address ?: UNKNOWN_VALUE + if(validateEmptyValue(this.country, this.city , this.address)) { + val addres = Address(this.country, this.city, this.address) + addressUseCase.setAddress(addres) + openFragment.invoke() + } + } + + fun validateEmptyValue(country: String, city: String, address: String): Boolean { + return if (country.isEmpty() || city.isEmpty() || address.isEmpty()) { + enabledButtonMutableLiveData.postValue(false) + false + } else { + enabledButtonMutableLiveData.postValue(true) + true + } + } + + fun buttonEnabled(){ + enabledButtonMutableLiveData.postValue(true) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/Fragment3.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/Fragment3.kt new file mode 100644 index 0000000..463ac13 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/Fragment3.kt @@ -0,0 +1,82 @@ +package ru.otus.basicarchitecture.Ui.Fragment3 + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import ru.otus.basicarchitecture.App +import ru.otus.basicarchitecture.Core.Model.ViewModelFactory +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.Ui.Fragment4.Fragment4 +import ru.otus.basicarchitecture.databinding.Fragment3Binding +import javax.inject.Inject + +class Fragment3: Fragment() { + private lateinit var binding: Fragment3Binding + @Inject + lateinit var viewModelFactory: ViewModelFactory + + private lateinit var adapter: InterestsAdapter + + private val viewModel by lazy { + ViewModelProvider(this, viewModelFactory)[Fragment3ViewModel::class.java] + } + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + binding = Fragment3Binding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + (requireActivity().application as App).component + .fragment3SubComponent() + .build() + .inject(this) + } + + override fun onResume() { + super.onResume() + view?.let { setupRV(it) } + setupView() + + + } + + fun setupView() { + binding.nextButton.setOnClickListener { + viewModel.setData { + openFourthFragment() + } + } + + viewModel.listInterestsLD.observe(viewLifecycleOwner) { + adapter.interestsList = it + } + } + + private fun openFourthFragment() { + val fragment = Fragment4() + parentFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, fragment) + .addToBackStack(null) + .commit() + } + + private fun setupRV(view: View) { + adapter = InterestsAdapter() + val layoutManager = GridLayoutManager(view.context,3) + binding.recyclerView.adapter = adapter + binding.recyclerView.layoutManager = layoutManager + + adapter.onClickListener = { + viewModel.changeEnabledState(it) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/Fragment3ViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/Fragment3ViewModel.kt new file mode 100644 index 0000000..8efa961 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/Fragment3ViewModel.kt @@ -0,0 +1,61 @@ +package ru.otus.basicarchitecture.Ui.Fragment3 + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import ru.otus.basicarchitecture.Core.Model.Interests +import ru.otus.basicarchitecture.Domain.Data.InterestsUseCase +import ru.otus.basicarchitecture.presentation.Fragment3.ModelInterestsForView +import javax.inject.Inject + +class Fragment3ViewModel @Inject constructor( + private val interestsUseCase: InterestsUseCase +) : ViewModel() { + + private val saveInterests = mutableListOf() + + private val mutableListAllInterests = sortedSetOf( + { o1, o2 -> o1.id.compareTo(o2.id) } + ) + private val _listInterestsLD = MutableLiveData>() + + val listInterestsLD: LiveData> = _listInterestsLD + + init { + val aa = interestsUseCase.getList() + for (a in aa.indices) { + val model = ModelInterestsForView(a, aa[a], false) + mutableListAllInterests.add(model) + } + _listInterestsLD.postValue(mutableListAllInterests.toList()) + } + + fun setData(openFragment: () -> Unit){ + + val list = mutableListOf() + saveInterests.forEach { + list.add(it.interests) + } + interestsUseCase.setInterests( + Interests(list.joinToString()) + ) + openFragment.invoke() + } + + fun changeEnabledState(model: ModelInterestsForView) { + val newModel = model.copy(enabled = !model.enabled) + mutableListAllInterests.remove(model) + mutableListAllInterests.add(newModel) + if(newModel.enabled){ + saveInterests.add(newModel) + }else{ + saveInterests.remove(newModel) + } + updateList() + } + + private fun updateList() { + _listInterestsLD.postValue(mutableListAllInterests.toList()) + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsAdapter.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsAdapter.kt new file mode 100644 index 0000000..1d69679 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsAdapter.kt @@ -0,0 +1,53 @@ +package ru.otus.basicarchitecture.Ui.Fragment3 + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.presentation.Fragment3.InterestsDiffUtil +import ru.otus.basicarchitecture.presentation.Fragment3.InterestsViewHolder +import ru.otus.basicarchitecture.presentation.Fragment3.ModelInterestsForView + +class InterestsAdapter: RecyclerView.Adapter() { + + var onClickListener: ((ModelInterestsForView) -> Unit)? = null + + var interestsList = listOf() + set(value){ + field = value + notifyDataSetChanged() + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InterestsViewHolder { + + val layout = when (viewType) { + DISABLED_VIEW -> R.layout.interests_disabled + ENABLED_VIEW -> R.layout.interests_enabled + else -> throw RuntimeException("Unknown view type: $viewType") + } + val view = LayoutInflater.from(parent.context).inflate(layout, parent, false) + return InterestsViewHolder(view) + } + + override fun onBindViewHolder(holder: InterestsViewHolder, position: Int) { + val item = interestsList[position] + holder.view.setOnClickListener { + onClickListener?.invoke(item) + } + holder.textView.text = item.interests + } + override fun getItemViewType(position: Int): Int { + val item = interestsList[position] + return if (item.enabled) ENABLED_VIEW else DISABLED_VIEW + } + + override fun getItemCount(): Int { + return interestsList.size + } + + companion object { + val ENABLED_VIEW = 100 + val DISABLED_VIEW = 101 + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsDiffUtil.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsDiffUtil.kt new file mode 100644 index 0000000..a56f0e6 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsDiffUtil.kt @@ -0,0 +1,25 @@ +package ru.otus.basicarchitecture.presentation.Fragment3 + +import androidx.recyclerview.widget.DiffUtil + +class InterestsDiffUtil( + private val oldList: List, + private val newList: List, +) : DiffUtil.Callback() { + override fun getOldListSize(): Int { + return oldList.size + } + + override fun getNewListSize(): Int { + return newList.size + } + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return oldList[oldItemPosition].id == newList[newItemPosition].id + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return oldList[oldItemPosition] == newList[newItemPosition] + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsViewHolder.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsViewHolder.kt new file mode 100644 index 0000000..f29c1a1 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/InterestsViewHolder.kt @@ -0,0 +1,10 @@ +package ru.otus.basicarchitecture.presentation.Fragment3 + +import android.view.View +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import ru.otus.basicarchitecture.R + +class InterestsViewHolder(val view: View) : RecyclerView.ViewHolder(view) { + val textView = view.findViewById(R.id.textInterests) +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/ModelInterestsForView.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/ModelInterestsForView.kt new file mode 100644 index 0000000..26b18b8 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment3/ModelInterestsForView.kt @@ -0,0 +1,20 @@ +package ru.otus.basicarchitecture.presentation.Fragment3 + +import ru.otus.basicarchitecture.Core.Model.Interests + +data class ModelInterestsForView( + var id: Int, + val interests: String, + var enabled: Boolean +){ + + companion object { + fun toDomain(modelView: ModelInterestsForView): Interests { + return Interests(modelView.interests) + } + + fun toModel(id: Int, interests: Interests): ModelInterestsForView { + return ModelInterestsForView(id, interests.interests, false) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4.kt new file mode 100644 index 0000000..594b196 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4.kt @@ -0,0 +1,66 @@ +package ru.otus.basicarchitecture.Ui.Fragment4 + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.StaggeredGridLayoutManager +import ru.otus.basicarchitecture.App +import ru.otus.basicarchitecture.Core.Model.ViewModelFactory +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.databinding.Fragment3Binding +import ru.otus.basicarchitecture.databinding.Fragment4Binding + +import javax.inject.Inject + +class Fragment4: Fragment() { + private lateinit var binding: Fragment4Binding + + @Inject + lateinit var viewModelFactory: ViewModelFactory + + + private val viewModel by lazy{ + ViewModelProvider(this, viewModelFactory)[Fragment4ViewModel::class.java] + } + + private var adapter = Fragment4Adapter() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + binding = Fragment4Binding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + (requireActivity().application as App).component + .fragment4SubComponent() + .build() + .inject(this) + } + + override fun onResume() { + super.onResume() + setupView() + } + + private fun setupView(){ + binding.Name.text = viewModel.modelFourthScreen.name + binding.Surname.text = viewModel.modelFourthScreen.surName + binding.DateOfBirth.text = viewModel.modelFourthScreen.birthDate + binding.Address.text = viewModel.modelFourthScreen.fullAddress + adapter.listInterestsInfo = viewModel.modelFourthScreen.interests + + val spanCount = 2 + val orientation = StaggeredGridLayoutManager.VERTICAL + val layoutManager = StaggeredGridLayoutManager(spanCount, orientation) + + binding.recyclerView4.layoutManager = layoutManager + binding.recyclerView4.adapter = adapter + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4Adapter.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4Adapter.kt new file mode 100644 index 0000000..18b5f81 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4Adapter.kt @@ -0,0 +1,31 @@ +package ru.otus.basicarchitecture.Ui.Fragment4 + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.presentation.Fragment3.InterestsViewHolder + + +class Fragment4Adapter : RecyclerView.Adapter() { + + var listInterestsInfo: List = listOf() + set(value) { + field = value + notifyDataSetChanged() + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InterestsViewHolder { + val layout = R.layout.interests_disabled + val view = LayoutInflater.from(parent.context).inflate(layout, parent,false) + return InterestsViewHolder(view) + } + + override fun getItemCount(): Int { + return listInterestsInfo.size + } + + override fun onBindViewHolder(holder: InterestsViewHolder, position: Int) { + val item = listInterestsInfo[position] + holder.textView.text = item + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4Model.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4Model.kt new file mode 100644 index 0000000..dadcf6b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4Model.kt @@ -0,0 +1,9 @@ +package ru.otus.basicarchitecture.Ui.Fragment4 + +data class Fragment4Model( + val name: String, + val surName: String, + val birthDate: String, + val fullAddress: String, + val interests: List +) \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4ViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4ViewModel.kt new file mode 100644 index 0000000..b637083 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/Ui/Fragment4/Fragment4ViewModel.kt @@ -0,0 +1,30 @@ +package ru.otus.basicarchitecture.Ui.Fragment4 + +import androidx.lifecycle.ViewModel +import ru.otus.basicarchitecture.Core.Model.Address +import ru.otus.basicarchitecture.Core.Model.Interests +import ru.otus.basicarchitecture.Core.Model.Person +import ru.otus.basicarchitecture.Domain.Data.PersonUseCase +import ru.otus.basicarchitecture.Ui.Fragment4.Fragment4Model +import javax.inject.Inject + +class Fragment4ViewModel @Inject constructor( + info: PersonUseCase +) : ViewModel() { + + val modelFourthScreen: Fragment4Model + + init { + val information = info.getPerson() + val person = information["person"] as Person + val address = information["address"] as Address + val interest = information["interests"] as Interests + modelFourthScreen = Fragment4Model( + person.firstName, + person.surName, + person.dateOfBirth, + "${address.country}, ${address.city}, ${address.city}", + interest.interests.split(",") + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/data/RepositoryImpl.kt b/app/src/main/java/ru/otus/basicarchitecture/data/RepositoryImpl.kt new file mode 100644 index 0000000..7f99c62 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/data/RepositoryImpl.kt @@ -0,0 +1,111 @@ +package ru.otus.basicarchitecture.data + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import ru.otus.basicarchitecture.Core.Model.Address +import ru.otus.basicarchitecture.Core.Model.BaseModel +import ru.otus.basicarchitecture.Core.Model.Interests +import ru.otus.basicarchitecture.Core.Model.Person +import ru.otus.basicarchitecture.Domain.Repository +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RepositoryImpl @Inject constructor( + private val wizardCache: WizardCache +) : Repository { + + + private val listInterests = listOf( + "Путешествия на велосипеде", + "Готовка экзотических блюд", + "Фотография природы", + "Чтение фантастической литературы", + "Участие в квестах и головоломках", + "Скалолазание", + "Изучение иностранных языков", + "Фильмы ужасов", + "Коллекционирование винтажных книг", + "Графический дизайн", + "Велоспорт", + "Рисование мандал и зентанглов", + "Плавание с аквалангом", + "Изучение астрономии", + "Фотография уличного искусства", + "Участие в косплей-событиях", + "Йога и медитация", + "Игры настольные", + "Пение в караоке", + "Реставрация старинной мебели", + "Занятия танцами", + "Развитие навыков программирования", + "Садоводство и уход за растениями", + "Фотография архитектурных сооружений", + "Шитье и создание одежды", + "Философия и обсуждение философских вопросов", + "Развитие навыков игры на музыкальных инструментах", + "Прослушивание подкастов о науке", + "Рукоделие и создание поделок", + "Экстримальные виды спорта: парашютный спорт, сноубординг, скейтбординг" + ) + + + + + private val mutableLiveDataWizardCache = MutableLiveData() + + val liveDataWizardCache: LiveData = mutableLiveDataWizardCache + + + + override fun setPerson(person: Person) { + wizardCache.firstName = person.firstName + wizardCache.surName = person.surName + wizardCache.dateOfBirth = person.firstName + updateWizardCache() + } + + override fun setAddress(address: Address) { + wizardCache.city = address.city + wizardCache.country = address.country + wizardCache.address = address.address + updateWizardCache() + } + + private fun updateWizardCache() { + mutableLiveDataWizardCache.postValue(wizardCache) + } + + override fun getListInterests(): List = listInterests + + override fun setInterests(interests: Interests) { + wizardCache.interests = interests.interests + updateWizardCache() + } + + override fun getInfoPersonUseCase(): Map { + val person = Person( + wizardCache.firstName, + wizardCache.surName, + wizardCache.dateOfBirth + ) + + val address = Address( + wizardCache.country, + wizardCache.city, + wizardCache.address + ) + + val interests = Interests( + wizardCache.interests + ) + + + return mapOf( + "person" to person, + "address" to address, + "interests" to interests + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/data/WizardCache.kt b/app/src/main/java/ru/otus/basicarchitecture/data/WizardCache.kt new file mode 100644 index 0000000..08dd655 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/data/WizardCache.kt @@ -0,0 +1,21 @@ +package ru.otus.basicarchitecture.data + + +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +data class WizardCache( + //person + var firstName: String, + var surName: String, + var dateOfBirth: String, + + //address + var country: String, + var city: String, + var address: String, + + //interests + var interests: String +) \ No newline at end of file diff --git a/app/src/main/res/drawable/background_state_button.xml b/app/src/main/res/drawable/background_state_button.xml new file mode 100644 index 0000000..4ababae --- /dev/null +++ b/app/src/main/res/drawable/background_state_button.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_selector.xml b/app/src/main/res/drawable/button_selector.xml new file mode 100644 index 0000000..132c26d --- /dev/null +++ b/app/src/main/res/drawable/button_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/calendar.xml b/app/src/main/res/drawable/calendar.xml new file mode 100644 index 0000000..0083b28 --- /dev/null +++ b/app/src/main/res/drawable/calendar.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/form_text_view_for_rv.xml b/app/src/main/res/drawable/form_text_view_for_rv.xml new file mode 100644 index 0000000..c01d672 --- /dev/null +++ b/app/src/main/res/drawable/form_text_view_for_rv.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ 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..d242c41 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,4 +6,9 @@ android:layout_height="match_parent" tools:context=".MainActivity"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment1.xml b/app/src/main/res/layout/fragment1.xml new file mode 100644 index 0000000..e39657a --- /dev/null +++ b/app/src/main/res/layout/fragment1.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +