diff --git a/app/build.gradle b/app/build.gradle index 9c99d98..a86020c 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,19 +25,25 @@ 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' } + + } dependencies { - implementation 'androidx.core:core-ktx:1.8.0' + //dagger2 + implementation("com.google.dagger:dagger:2.48.1") + kapt("com.google.dagger:dagger-compiler:2.48.1") + + implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e81fea..15ecd82 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:name=".presentation.MainActivity" + 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/DI/FragmentComponents/FirstScreen/FirstScreenSubComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FirstScreen/FirstScreenSubComponent.kt new file mode 100644 index 0000000..621e9c5 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FirstScreen/FirstScreenSubComponent.kt @@ -0,0 +1,21 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.FirstScreen + +import dagger.Subcomponent +import ru.otus.basicarchitecture.DI.FragmentComponents.FragmentScope +import ru.otus.basicarchitecture.presentation.FirstScreen.FirstScreenFragment + + +/** + * Подкомпонент для первого фрагмента + */ +@FragmentScope +@Subcomponent(modules = [PersonModule::class, FirstViewModelModule::class]) +interface FirstScreenSubComponent { + + @Subcomponent.Builder + interface Builder { + fun build() : FirstScreenSubComponent + } + + fun inject(fragmentFirst: FirstScreenFragment) +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FirstScreen/FirstViewModelModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FirstScreen/FirstViewModelModule.kt new file mode 100644 index 0000000..1aae91c --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FirstScreen/FirstViewModelModule.kt @@ -0,0 +1,18 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.FirstScreen + +import androidx.lifecycle.ViewModel +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey +import ru.otus.basicarchitecture.presentation.FirstScreen.FirstScreenViewModel + +@Module +interface FirstViewModelModule { + + @IntoMap + @StringKey("FirstScreenViewModel") + @Binds + fun bindFirstScreenViewModel(impl: FirstScreenViewModel): ViewModel + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FirstScreen/PersonModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FirstScreen/PersonModule.kt new file mode 100644 index 0000000..b4116b9 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FirstScreen/PersonModule.kt @@ -0,0 +1,19 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.FirstScreen + +import dagger.Module +import dagger.Provides +import ru.otus.basicarchitecture.DI.FragmentComponents.FragmentScope +import ru.otus.basicarchitecture.domain.Model.Address +import ru.otus.basicarchitecture.domain.Model.Person + +@Module +class PersonModule { + @FragmentScope + @Provides + fun providePerson(): Person{ + return Person.defaultPerson() + } + + +} + diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FourthScreen/FourthScreenSubComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FourthScreen/FourthScreenSubComponent.kt new file mode 100644 index 0000000..672287b --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FourthScreen/FourthScreenSubComponent.kt @@ -0,0 +1,19 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.FourthScreen + +import dagger.Subcomponent +import ru.otus.basicarchitecture.DI.FragmentComponents.FragmentScope +import ru.otus.basicarchitecture.presentation.FourthScreen.FourthScreenFragment + +@FragmentScope +@Subcomponent(modules = [FourthViewModelModule::class]) +interface FourthScreenSubComponent { + + + @Subcomponent.Builder + interface Builder { + fun build() : FourthScreenSubComponent + } + + fun inject(fragment: FourthScreenFragment) + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FourthScreen/FourthViewModelModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FourthScreen/FourthViewModelModule.kt new file mode 100644 index 0000000..4b42fd2 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FourthScreen/FourthViewModelModule.kt @@ -0,0 +1,18 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.FourthScreen + +import androidx.lifecycle.ViewModel +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey +import ru.otus.basicarchitecture.presentation.FourthScreen.FourthScreenViewModel + + +@Module +interface FourthViewModelModule { + + @IntoMap + @StringKey("FourthScreenViewModel") + @Binds + fun bindFourthScreenViewModel(impl: FourthScreenViewModel): ViewModel +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FragmentScope.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FragmentScope.kt new file mode 100644 index 0000000..4b5c132 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/FragmentScope.kt @@ -0,0 +1,8 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents + +import javax.inject.Scope + +//Синглтон внутри сабкомпонентов для фрагментов +@Scope +@Retention(value = AnnotationRetention.RUNTIME) +annotation class FragmentScope() diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/SecondScreen/AddressModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/SecondScreen/AddressModule.kt new file mode 100644 index 0000000..94db3f4 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/SecondScreen/AddressModule.kt @@ -0,0 +1,17 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.SecondScreen + +import dagger.Module +import dagger.Provides +import ru.otus.basicarchitecture.DI.FragmentComponents.FragmentScope +import ru.otus.basicarchitecture.domain.Model.Address + +@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/FragmentComponents/SecondScreen/SecondScreenSubComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/SecondScreen/SecondScreenSubComponent.kt new file mode 100644 index 0000000..8f55adf --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/SecondScreen/SecondScreenSubComponent.kt @@ -0,0 +1,19 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.SecondScreen + +import dagger.Subcomponent +import ru.otus.basicarchitecture.DI.FragmentComponents.FragmentScope +import ru.otus.basicarchitecture.presentation.SecondScreen.SecondScreenFragment +import ru.otus.basicarchitecture.presentation.SecondScreen.SecondScreenViewModel + + +@FragmentScope +@Subcomponent(modules = [AddressModule::class, SecondViewModelModule::class]) +interface SecondScreenSubComponent { + + @Subcomponent.Builder + interface Builder { + fun build() : SecondScreenSubComponent + } + + fun inject(fragmentSecond: SecondScreenFragment) +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/SecondScreen/SecondViewModelModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/SecondScreen/SecondViewModelModule.kt new file mode 100644 index 0000000..e330969 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/SecondScreen/SecondViewModelModule.kt @@ -0,0 +1,17 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.SecondScreen + +import androidx.lifecycle.ViewModel +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey +import ru.otus.basicarchitecture.presentation.SecondScreen.SecondScreenViewModel +@Module +interface SecondViewModelModule { + + @IntoMap + @StringKey("SecondScreenViewModel") + @Binds + fun bindSecondScreenViewModel(impl: SecondScreenViewModel): ViewModel + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/ThirdScreen/ThirdScreenSubComponent.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/ThirdScreen/ThirdScreenSubComponent.kt new file mode 100644 index 0000000..0c8d53c --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/ThirdScreen/ThirdScreenSubComponent.kt @@ -0,0 +1,19 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.ThirdScreen + +import dagger.Subcomponent +import ru.otus.basicarchitecture.DI.FragmentComponents.FragmentScope +import ru.otus.basicarchitecture.presentation.ThirdScreen.ThirdScreenFragment + +@FragmentScope +@Subcomponent(modules = [ThirdViewModelModule::class]) +interface ThirdScreenSubComponent { + + @Subcomponent.Builder + interface Builder { + fun build() : ThirdScreenSubComponent + } + + fun inject(fragmentThird: ThirdScreenFragment) + + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/ThirdScreen/ThirdViewModelModule.kt b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/ThirdScreen/ThirdViewModelModule.kt new file mode 100644 index 0000000..9eb584f --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/FragmentComponents/ThirdScreen/ThirdViewModelModule.kt @@ -0,0 +1,18 @@ +package ru.otus.basicarchitecture.DI.FragmentComponents.ThirdScreen + +import androidx.lifecycle.ViewModel +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey +import ru.otus.basicarchitecture.presentation.ThirdScreen.ThirdScreenViewModel + +@Module +interface ThirdViewModelModule { + + @IntoMap + @StringKey("ThirdScreenViewModel") + @Binds + fun bindThirdScreenViewModel(impl: ThirdScreenViewModel): ViewModel + +} \ 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..bf275c2 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/DI/MainComponent/MainComponent.kt @@ -0,0 +1,26 @@ +package ru.otus.basicarchitecture.DI.MainComponent + +import dagger.Component +import ru.otus.basicarchitecture.DI.FragmentComponents.FirstScreen.FirstScreenSubComponent +import ru.otus.basicarchitecture.DI.FragmentComponents.FourthScreen.FourthScreenSubComponent +import ru.otus.basicarchitecture.DI.FragmentComponents.SecondScreen.SecondScreenSubComponent +import ru.otus.basicarchitecture.DI.FragmentComponents.ThirdScreen.ThirdScreenSubComponent +import ru.otus.basicarchitecture.data.WizardCache +import ru.otus.basicarchitecture.domain.Repository +import javax.inject.Singleton + +@Singleton +@Component(modules = [WizardCacheModule::class, RepositoryModule::class]) +interface MainComponent { + fun firstScreenSubComponent() : FirstScreenSubComponent.Builder + + fun secondScreenSubComponent() : SecondScreenSubComponent.Builder + + fun thirdScreenSubComponent() : ThirdScreenSubComponent.Builder + + fun fourthScreenSubComponent(): FourthScreenSubComponent.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..7208a0e --- /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/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/data/RepositoryImpl.kt b/app/src/main/java/ru/otus/basicarchitecture/data/RepositoryImpl.kt new file mode 100644 index 0000000..cae43a8 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/data/RepositoryImpl.kt @@ -0,0 +1,110 @@ +package ru.otus.basicarchitecture.data + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import ru.otus.basicarchitecture.domain.Model.Address +import ru.otus.basicarchitecture.domain.Model.DomainModel +import ru.otus.basicarchitecture.domain.Model.Interests +import ru.otus.basicarchitecture.domain.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 setInterests(interests: Interests) { + wizardCache.interests = interests.interests + updateWizardCache() + } + + override fun setAddress(address: Address) { + wizardCache.city = address.city + wizardCache.country = address.country + wizardCache.address = address.address + 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 + ) + } + + override fun getListInterests(): List = listInterests + + private fun updateWizardCache() { + mutableLiveDataWizardCache.postValue(wizardCache) + } +} \ 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..557a344 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/data/WizardCache.kt @@ -0,0 +1,23 @@ +package ru.otus.basicarchitecture.data + +import ru.otus.basicarchitecture.domain.Model.Address +import ru.otus.basicarchitecture.domain.Model.Interests +import ru.otus.basicarchitecture.domain.Model.Person +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/java/ru/otus/basicarchitecture/domain/Model/Address.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/Model/Address.kt new file mode 100644 index 0000000..37d2cbb --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/Model/Address.kt @@ -0,0 +1,7 @@ +package ru.otus.basicarchitecture.domain.Model + +data class Address( + val country: String, + val city: String, + val address: String +) : DomainModel \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/domain/Model/DomainModel.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/Model/DomainModel.kt new file mode 100644 index 0000000..6749984 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/Model/DomainModel.kt @@ -0,0 +1,3 @@ +package ru.otus.basicarchitecture.domain.Model + +sealed interface DomainModel \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/domain/Model/Interests.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/Model/Interests.kt new file mode 100644 index 0000000..771917f --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/Model/Interests.kt @@ -0,0 +1,5 @@ +package ru.otus.basicarchitecture.domain.Model + +data class Interests ( + var interests: String +) : DomainModel \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/domain/Model/Person.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/Model/Person.kt new file mode 100644 index 0000000..54dd295 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/Model/Person.kt @@ -0,0 +1,23 @@ +package ru.otus.basicarchitecture.domain.Model + +import ru.otus.basicarchitecture.DI.FragmentComponents.FragmentScope + + +@FragmentScope +data class Person( + var firstName: String, + var surName: String, + var dateOfBirth: String +) : DomainModel { + companion object { + const val defaultValueProperty = "" + + fun defaultPerson(): Person { + return Person( + defaultValueProperty, + defaultValueProperty, + defaultValueProperty + ) + } + } +} 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..060f4af --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/Repository.kt @@ -0,0 +1,21 @@ +package ru.otus.basicarchitecture.domain + +import ru.otus.basicarchitecture.domain.Model.Address +import ru.otus.basicarchitecture.domain.Model.DomainModel +import ru.otus.basicarchitecture.domain.Model.Interests +import ru.otus.basicarchitecture.domain.Model.Person + +interface Repository { + + fun setPerson(person: Person) + + fun setInterests(interests: Interests) + + + fun setAddress(address: Address) + + + fun getInfoPersonUseCase() : Map + + fun getListInterests(): List +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/domain/getData/GetInfoPersonUseCase.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/getData/GetInfoPersonUseCase.kt new file mode 100644 index 0000000..157c310 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/getData/GetInfoPersonUseCase.kt @@ -0,0 +1,11 @@ +package ru.otus.basicarchitecture.domain.getData + +import ru.otus.basicarchitecture.domain.Model.DomainModel +import ru.otus.basicarchitecture.domain.Repository +import javax.inject.Inject + +class GetInfoPersonUseCase @Inject constructor(val repository: Repository) { + fun getInfo() : Map{ + return repository.getInfoPersonUseCase() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/domain/getData/GetListInterests.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/getData/GetListInterests.kt new file mode 100644 index 0000000..4646f65 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/getData/GetListInterests.kt @@ -0,0 +1,10 @@ +package ru.otus.basicarchitecture.domain.getData + +import ru.otus.basicarchitecture.domain.Repository +import javax.inject.Inject + +class GetListInterests @Inject constructor(val repository: Repository) { + fun getList(): List{ + return repository.getListInterests() + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/domain/setData/SetAddressUseCase.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/setData/SetAddressUseCase.kt new file mode 100644 index 0000000..0b0413c --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/setData/SetAddressUseCase.kt @@ -0,0 +1,13 @@ +package ru.otus.basicarchitecture.domain.setData + +import ru.otus.basicarchitecture.domain.Model.Address +import ru.otus.basicarchitecture.domain.Repository +import javax.inject.Inject + +class SetAddressUseCase @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/setData/SetInterestsPersonUseCase.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/setData/SetInterestsPersonUseCase.kt new file mode 100644 index 0000000..220ccd6 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/setData/SetInterestsPersonUseCase.kt @@ -0,0 +1,13 @@ +package ru.otus.basicarchitecture.domain.setData + +import ru.otus.basicarchitecture.domain.Model.Interests +import ru.otus.basicarchitecture.domain.Repository +import javax.inject.Inject + +class SetInterestsPersonUseCase @Inject constructor( + private val repository: Repository +) { + fun setInterests(interests: Interests){ + repository.setInterests(interests) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/domain/setData/SetPersonUseCase.kt b/app/src/main/java/ru/otus/basicarchitecture/domain/setData/SetPersonUseCase.kt new file mode 100644 index 0000000..0025568 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/domain/setData/SetPersonUseCase.kt @@ -0,0 +1,13 @@ +package ru.otus.basicarchitecture.domain.setData + +import ru.otus.basicarchitecture.domain.Model.Person +import ru.otus.basicarchitecture.domain.Repository +import javax.inject.Inject + +class SetPersonUseCase @Inject constructor( + private val repository: Repository +) { + fun setPerson(person: Person) { + repository.setPerson(person) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/presentation/FirstScreen/FirstScreenFragment.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/FirstScreen/FirstScreenFragment.kt new file mode 100644 index 0000000..cdb6ca6 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/FirstScreen/FirstScreenFragment.kt @@ -0,0 +1,197 @@ +package ru.otus.basicarchitecture.presentation.FirstScreen + +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.Button +import android.widget.EditText +import android.widget.Toast +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.google.android.material.textfield.TextInputLayout +import ru.otus.basicarchitecture.App +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.presentation.SecondScreen.SecondScreenFragment +import ru.otus.basicarchitecture.presentation.ViewModelFactory +import java.util.Calendar +import java.util.Locale +import javax.inject.Inject + +class FirstScreenFragment : Fragment() { + + @Inject + lateinit var viewModelFactory: ViewModelFactory + + + private val viewModel by lazy { + ViewModelProvider(this, viewModelFactory)[FirstScreenViewModel::class.java] + } + + + private var nextButton: Button? = null + + private var nameEditText: EditText? = null + private var surNameEditText: EditText? = null + private var birthDateEditText: EditText? = null + + + private var nameInputLayout: TextInputLayout? = null + private var surNameInputLayout: TextInputLayout? = null + private var birthDateInputLayout: TextInputLayout? = null + + + /* override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + parseParam() + }*/ + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.first_and_second_screen, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + (requireActivity().application as App).component + .firstScreenSubComponent() + .build() + .inject(this) + + setupView(view) + + + } + + + private fun setupView(view: View) { + with(view) { + nextButton = findViewById(R.id.nextButton) + + nameInputLayout = findViewById(R.id.firstInputLayout) + nameEditText = findViewById(R.id.firstEditText) + + surNameInputLayout = findViewById(R.id.secondInputLayout) + surNameEditText = findViewById(R.id.secondEditText) + + birthDateInputLayout = findViewById(R.id.thirdInputLayout) + birthDateEditText = findViewById(R.id.thirdEditText) + + + } + + nextButton?.let { button -> + setListeners() + viewModel.enabledButtonLiveData.observe(viewLifecycleOwner) { + button.isEnabled = it + } + } + nameInputLayout?.apply { + hint = context.getString(R.string.hintName) + } + surNameInputLayout?.apply { + hint = context.getString(R.string.surNameHint) + } + + birthDateInputLayout?.apply { + hint = context.getString(R.string.DateOfBirthHint) + startIconDrawable = ContextCompat.getDrawable(context, R.drawable.calendar) + startIconContentDescription = context.getString(R.string.DateOfBirthHint) + } + birthDateEditText?.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() + + + } + + + private fun setListeners() { + nextButton?.setOnClickListener { + viewModel.setData( + nameEditText?.text.toString(), + surNameEditText?.text.toString(), + birthDateEditText?.text.toString() + ) { openSecondFragment() } + } + } + + private fun setFocusListener(){ + val lossFocus = View.OnFocusChangeListener{_,hasFocus -> + if(!hasFocus){ + viewModel.validateEmptyValue() + } + } + nameEditText?.onFocusChangeListener = lossFocus + surNameEditText?.onFocusChangeListener = lossFocus + + } + + private fun openSecondFragment() { + val fragment = SecondScreenFragment.instance() + 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) } + ) + birthDateEditText?.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() + } + + + companion object { + private const val MESSAGE_TOAST_INCORRECT_AGE ="Возраст пользователя не может быть меньше 18" + + + fun instance(): FirstScreenFragment { + return FirstScreenFragment() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/presentation/FirstScreen/FirstScreenViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/FirstScreen/FirstScreenViewModel.kt new file mode 100644 index 0000000..44836b7 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/FirstScreen/FirstScreenViewModel.kt @@ -0,0 +1,78 @@ +package ru.otus.basicarchitecture.presentation.FirstScreen + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import ru.otus.basicarchitecture.domain.Model.Person +import ru.otus.basicarchitecture.domain.setData.SetPersonUseCase +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit +import javax.inject.Inject + +class FirstScreenViewModel @Inject constructor( + val setPersonUseCase: SetPersonUseCase +) : 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) + setPersonUseCase.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/presentation/FourthScreen/AdapterFourthScreen.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/AdapterFourthScreen.kt new file mode 100644 index 0000000..9abd1c3 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/AdapterFourthScreen.kt @@ -0,0 +1,31 @@ +package ru.otus.basicarchitecture.presentation.FourthScreen + + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.presentation.ThirdScreen.InterestsViewHolder + +class AdapterFourthScreen : RecyclerView.Adapter() { + + var listInterestsInfo: List = listOf() + set(value) { + field = value + notifyDataSetChanged() + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InterestsViewHolder { + val layout = R.layout.interests_forth_screen + 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/presentation/FourthScreen/FourthScreenFragment.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/FourthScreenFragment.kt new file mode 100644 index 0000000..148cafc --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/FourthScreenFragment.kt @@ -0,0 +1,91 @@ +package ru.otus.basicarchitecture.presentation.FourthScreen + +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.R +import ru.otus.basicarchitecture.presentation.FirstScreen.FirstScreenViewModel +import ru.otus.basicarchitecture.presentation.ViewModelFactory +import javax.inject.Inject + +class FourthScreenFragment: Fragment() { + + @Inject + lateinit var viewModelFactory: ViewModelFactory + + + private val viewModel by lazy{ + ViewModelProvider(this, viewModelFactory)[FourthScreenViewModel::class.java] + } + + private var recyclerView: RecyclerView? = null + + private var name: TextView? = null + private var surname: TextView? = null + private var dateOfBirth: TextView? = null + private var address: TextView? = null + + private var adapter = AdapterFourthScreen() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fourth_screen,container,false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + (requireActivity().application as App).component + .fourthScreenSubComponent() + .build() + .inject(this) + + setupView(view) + + } + + private fun setupView(view: View){ + + with(view){ + + recyclerView = findViewById(R.id.recyclerView4) + name = findViewById(R.id.Name) + surname = findViewById(R.id.Surname) + dateOfBirth = findViewById(R.id.DateOfBirth) + address = findViewById(R.id.Address) + + } + + + name?.text = viewModel.modelFourthScreen.name + surname?.text = viewModel.modelFourthScreen.surName + dateOfBirth?.text = viewModel.modelFourthScreen.birthDate + address?.text = viewModel.modelFourthScreen.fullAddress + adapter.listInterestsInfo = viewModel.modelFourthScreen.interests + + val spanCount = 2 + val orientation = StaggeredGridLayoutManager.VERTICAL + val layoutManager = StaggeredGridLayoutManager(spanCount, orientation) + + recyclerView?.layoutManager = layoutManager + + recyclerView?.adapter = adapter + + } + + companion object{ + fun instance() : FourthScreenFragment{ + return FourthScreenFragment() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/FourthScreenViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/FourthScreenViewModel.kt new file mode 100644 index 0000000..90ab958 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/FourthScreenViewModel.kt @@ -0,0 +1,29 @@ +package ru.otus.basicarchitecture.presentation.FourthScreen + +import androidx.lifecycle.ViewModel +import ru.otus.basicarchitecture.domain.Model.Address +import ru.otus.basicarchitecture.domain.Model.Interests +import ru.otus.basicarchitecture.domain.Model.Person +import ru.otus.basicarchitecture.domain.getData.GetInfoPersonUseCase +import javax.inject.Inject + +class FourthScreenViewModel @Inject constructor( + info: GetInfoPersonUseCase +) : ViewModel() { + + val modelFourthScreen: ModelFourthScreen + + init { + val information = info.getInfo() + val person = information["person"] as Person + val address = information["address"] as Address + val interest = information["interests"] as Interests + modelFourthScreen = ModelFourthScreen( + 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/presentation/FourthScreen/ModelFourthScreen.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/ModelFourthScreen.kt new file mode 100644 index 0000000..0949881 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/FourthScreen/ModelFourthScreen.kt @@ -0,0 +1,9 @@ +package ru.otus.basicarchitecture.presentation.FourthScreen + +data class ModelFourthScreen( + 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/presentation/MainActivity.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/MainActivity.kt new file mode 100644 index 0000000..84bb07d --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/MainActivity.kt @@ -0,0 +1,26 @@ +package ru.otus.basicarchitecture.presentation + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.presentation.FirstScreen.FirstScreenFragment +import ru.otus.basicarchitecture.presentation.ThirdScreen.ThirdScreenFragment + +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + if(savedInstanceState == null ){ + val fragment = FirstScreenFragment.instance() + 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/presentation/SecondScreen/SecondScreenFragment.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/SecondScreen/SecondScreenFragment.kt new file mode 100644 index 0000000..0c3df98 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/SecondScreen/SecondScreenFragment.kt @@ -0,0 +1,126 @@ +package ru.otus.basicarchitecture.presentation.SecondScreen + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.EditText +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.google.android.material.textfield.TextInputLayout +import ru.otus.basicarchitecture.App +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.presentation.ThirdScreen.ThirdScreenFragment +import ru.otus.basicarchitecture.presentation.ViewModelFactory +import javax.inject.Inject + +class SecondScreenFragment : Fragment() { + @Inject + lateinit var viewModelFactory: ViewModelFactory + + private val viewModel by lazy { + ViewModelProvider(this, viewModelFactory)[SecondScreenViewModel::class.java] + } + + + private var countryEditText: EditText? = null + private var cityEditText: EditText? = null + private var addressEditText: EditText? = null + + + private var countryInputLayout: TextInputLayout? = null + private var cityInputLayout: TextInputLayout? = null + private var addressInputLayout: TextInputLayout? = null + + private var nextButton: Button? = null + + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.first_and_second_screen, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + (requireActivity().application as App).component + .secondScreenSubComponent() + .build() + .inject(this) + setupView(view) + } + + private fun setupView(view: View) { + with(view) { + nextButton = findViewById(R.id.nextButton) + + countryInputLayout = findViewById(R.id.firstInputLayout) + countryEditText = findViewById(R.id.firstEditText) + + cityInputLayout = findViewById(R.id.secondInputLayout) + cityEditText = findViewById(R.id.secondEditText) + + addressInputLayout = findViewById(R.id.thirdInputLayout) + addressEditText = findViewById(R.id.thirdEditText) + + + } + + nextButton?.let { button -> + setListeners() + viewModel.enabledButtonLiveData.observe(viewLifecycleOwner) { + button.isEnabled = it + } + } + countryInputLayout?.apply { + hint = context.getString(R.string.country) + } + cityInputLayout?.apply { + hint = context.getString(R.string.city) + } + addressEditText?.apply { + hint = context.getString(R.string.address) + } + + setFocusListener() + } + + private fun setListeners() { + nextButton?.setOnClickListener { + viewModel.setData( + countryEditText?.text.toString(), + cityEditText?.text.toString(), + addressEditText?.text.toString() + ) { openFragment() } + } + } + + + private fun openFragment() { + val fragment = ThirdScreenFragment.instance() + parentFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, fragment) + .addToBackStack(null) + .commit() + } + + private fun setFocusListener() { + val lossFocus = View.OnFocusChangeListener { _, hasFocus -> + if (hasFocus) viewModel.buttonEnabled() + } + countryEditText?.onFocusChangeListener = lossFocus + cityEditText?.onFocusChangeListener = lossFocus + addressEditText?.onFocusChangeListener = lossFocus + } + + companion object { + + fun instance(): SecondScreenFragment { + return SecondScreenFragment() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/presentation/SecondScreen/SecondScreenViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/SecondScreen/SecondScreenViewModel.kt new file mode 100644 index 0000000..0047955 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/SecondScreen/SecondScreenViewModel.kt @@ -0,0 +1,58 @@ +package ru.otus.basicarchitecture.presentation.SecondScreen + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import ru.otus.basicarchitecture.domain.Model.Address +import ru.otus.basicarchitecture.domain.setData.SetAddressUseCase +import javax.inject.Inject + +class SecondScreenViewModel @Inject constructor ( + private val setAddressUseCase: SetAddressUseCase +) : ViewModel() { + + + private val enabledButtonMutableLiveData = MutableLiveData() + val enabledButtonLiveData = enabledButtonMutableLiveData + + 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) + setAddressUseCase.setAddress(addres) + openFragment.invoke() + } + } + + //убрать костыль с параметрами + fun validateEmptyValue(country: String, city: String, address: String): Boolean { + return if (country == UNKNOWN_VALUE || city == UNKNOWN_VALUE || address == UNKNOWN_VALUE) { + enabledButtonMutableLiveData.postValue(false) + false + } else { + enabledButtonMutableLiveData.postValue(true) + true + } + } + + fun buttonEnabled(){ + 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/presentation/ThirdScreen/InterestsDiffUtil.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/InterestsDiffUtil.kt new file mode 100644 index 0000000..e36a4af --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/InterestsDiffUtil.kt @@ -0,0 +1,25 @@ +package ru.otus.basicarchitecture.presentation.ThirdScreen + +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/presentation/ThirdScreen/InterestsViewHolder.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/InterestsViewHolder.kt new file mode 100644 index 0000000..a0457ca --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/InterestsViewHolder.kt @@ -0,0 +1,10 @@ +package ru.otus.basicarchitecture.presentation.ThirdScreen + +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/presentation/ThirdScreen/ListInterestsAdapter.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ListInterestsAdapter.kt new file mode 100644 index 0000000..9876d2d --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ListInterestsAdapter.kt @@ -0,0 +1,56 @@ +package ru.otus.basicarchitecture.presentation.ThirdScreen + +import android.util.Log +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import ru.otus.basicarchitecture.R + + +class ListInterestsAdapter + : RecyclerView.Adapter() { + + var onClickListener: ((ModelInterestsForView) -> Unit)? = null + + var interestsList = listOf() + set(value){ + val callback = InterestsDiffUtil(interestsList, value) + val diffResult = DiffUtil.calculateDiff(callback) + diffResult.dispatchUpdatesTo(this) + field = value + } + 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/presentation/ThirdScreen/ModelInterestsForView.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ModelInterestsForView.kt new file mode 100644 index 0000000..81cf329 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ModelInterestsForView.kt @@ -0,0 +1,20 @@ +package ru.otus.basicarchitecture.presentation.ThirdScreen + +import ru.otus.basicarchitecture.domain.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/presentation/ThirdScreen/ThirdScreenFragment.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ThirdScreenFragment.kt new file mode 100644 index 0000000..b836586 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ThirdScreenFragment.kt @@ -0,0 +1,107 @@ +package ru.otus.basicarchitecture.presentation.ThirdScreen + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.LayoutManager +import androidx.recyclerview.widget.StaggeredGridLayoutManager +import ru.otus.basicarchitecture.App +import ru.otus.basicarchitecture.R +import ru.otus.basicarchitecture.presentation.FourthScreen.FourthScreenFragment +import ru.otus.basicarchitecture.presentation.SecondScreen.SecondScreenFragment +import ru.otus.basicarchitecture.presentation.ViewModelFactory +import javax.inject.Inject + +class ThirdScreenFragment : Fragment() { + + @Inject + lateinit var viewModelFactory: ViewModelFactory + + private lateinit var adapterCastom: ListInterestsAdapter + + private val viewModel by lazy { + ViewModelProvider(this, viewModelFactory)[ThirdScreenViewModel::class.java] + } + + private var recyclerView: RecyclerView? = null + + private var button: Button? = null + + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.third_screen, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + (requireActivity().application as App).component + .thirdScreenSubComponent() + .build() + .inject(this) + + setupRV(view) + + viewModel.listInterestsLD.observe(viewLifecycleOwner) { + adapterCastom.interestsList = it + } + setupClickListener() + + button = view.findViewById(R.id.nextButton) + + button?.setOnClickListener { + viewModel.setData { + openFourthFragment() + } + } + } + private fun openFourthFragment() { + val fragment = FourthScreenFragment.instance() + parentFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, fragment) + .addToBackStack(null) + .commit() + } + + private fun setupRV(view: View) { + adapterCastom = ListInterestsAdapter() + recyclerView = view.findViewById(R.id.recyclerView) + + recyclerView?.let { + with(it) { + val layoutManager = GridLayoutManager(view.context,3) + this.layoutManager = layoutManager + adapter = adapterCastom + recycledViewPool.setMaxRecycledViews(ListInterestsAdapter.ENABLED_VIEW, 15) + recycledViewPool.setMaxRecycledViews(ListInterestsAdapter.DISABLED_VIEW, 15) + } + } + } + + private fun setupClickListener() { + adapterCastom.onClickListener = { + viewModel.changeEnabledState(it) + } + } + + companion object { + fun instance(): ThirdScreenFragment { + return ThirdScreenFragment() + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ThirdScreenViewModel.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ThirdScreenViewModel.kt new file mode 100644 index 0000000..45cd83c --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/ThirdScreen/ThirdScreenViewModel.kt @@ -0,0 +1,68 @@ +package ru.otus.basicarchitecture.presentation.ThirdScreen + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import ru.otus.basicarchitecture.domain.Model.Interests +import ru.otus.basicarchitecture.domain.getData.GetListInterests +import ru.otus.basicarchitecture.domain.setData.SetInterestsPersonUseCase +import java.util.TreeSet +import javax.inject.Inject + +class ThirdScreenViewModel @Inject constructor( + private val setInterestsPersonUseCase: SetInterestsPersonUseCase, + getListInterests: GetListInterests +) : ViewModel() { + + private val saveInterests = mutableListOf() + + private val mutableListAllInterests = sortedSetOf( + { o1, o2 -> o1.id.compareTo(o2.id) } + ) + private val _listInterestsLD = MutableLiveData>() + + val listInterestsLD: LiveData> = _listInterestsLD + + //todo: разберем наверное чтение с визарда, что бы при переходе экрана туда - сюда , значения не пропадали + init { + val aa = getListInterests.getList() + for (a in aa.indices) { + val model = ModelInterestsForView(a, aa[a], false) + mutableListAllInterests.add(model) + } + _listInterestsLD.postValue(mutableListAllInterests.toList()) + } + +// private fun updateListSaveInterests(model: ModelInterestsForView){ +// if(model.enabled) saveInterests.add(model) else saveInterests.remove(model) +// } + + fun setData(openFragment: () -> Unit){ + + val list = mutableListOf() + saveInterests.forEach { + list.add(it.interests) + } + setInterestsPersonUseCase.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/presentation/ViewModelFactory.kt b/app/src/main/java/ru/otus/basicarchitecture/presentation/ViewModelFactory.kt new file mode 100644 index 0000000..4b4c7e6 --- /dev/null +++ b/app/src/main/java/ru/otus/basicarchitecture/presentation/ViewModelFactory.kt @@ -0,0 +1,13 @@ +package ru.otus.basicarchitecture.presentation + +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/res/drawable/background_state_button.xml b/app/src/main/res/drawable/background_state_button.xml new file mode 100644 index 0000000..132c26d --- /dev/null +++ b/app/src/main/res/drawable/background_state_button.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..9580bfa --- /dev/null +++ b/app/src/main/res/drawable/calendar.xml @@ -0,0 +1,5 @@ + + + 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..16c0c81 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,9 +1,15 @@ - + tools:context=".presentation.MainActivity"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/first_and_second_screen.xml b/app/src/main/res/layout/first_and_second_screen.xml new file mode 100644 index 0000000..6036b56 --- /dev/null +++ b/app/src/main/res/layout/first_and_second_screen.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +