Pārlūkot izejas kodu

add animation for transmition fragment
add Person entities for app, db, network and mapping

MrOzOn 5 gadi atpakaļ
vecāks
revīzija
a19244ca84
37 mainītis faili ar 501 papildinājumiem un 21 dzēšanām
  1. 4 3
      app/build.gradle
  2. 4 0
      app/src/main/java/com/mrozon/healthdiary/navigation/LoginNavigatorImpl.kt
  3. 3 4
      app/src/main/java/com/mrozon/healthdiary/navigation/SplashNavigatorImpl.kt
  4. 1 1
      app/src/main/java/com/mrozon/healthdiary/presentation/main/MainActivity.kt
  5. 6 0
      app/src/main/res/anim/slide_in_left.xml
  6. 6 0
      app/src/main/res/anim/slide_in_right.xml
  7. 6 0
      app/src/main/res/anim/slide_out_left.xml
  8. 6 0
      app/src/main/res/anim/slide_out_right.xml
  9. 49 4
      app/src/main/res/navigation/nav_graph.xml
  10. 4 0
      app/src/main/res/values/anim.xml
  11. 17 0
      core_api/src/main/java/com/mrozon/core_api/db/BlobTransmogrifier.kt
  12. 4 0
      core_api/src/main/java/com/mrozon/core_api/db/HealthDiaryDao.kt
  13. 25 0
      core_api/src/main/java/com/mrozon/core_api/db/model/PersonDb.kt
  14. 14 0
      core_api/src/main/java/com/mrozon/core_api/entity/Person.kt
  15. 37 0
      core_api/src/main/java/com/mrozon/core_api/mapper/PersonToPersonDbMapper.kt
  16. 1 0
      core_api/src/main/java/com/mrozon/core_api/navigation/LoginNavigator.kt
  17. 1 0
      core_api/src/main/java/com/mrozon/core_api/navigation/SplashNavigator.kt
  18. 6 5
      core_api/src/main/java/com/mrozon/core_api/network/HealthDiaryService.kt
  19. 7 0
      core_api/src/main/java/com/mrozon/core_api/network/model/PersonRequest.kt
  20. 24 0
      core_api/src/main/java/com/mrozon/core_api/network/model/PersonResponse.kt
  21. 2 1
      core_impl/src/main/java/com/mrozon/core_impl/db/HealthDiaryDb.kt
  22. 6 0
      feature_auth/src/main/java/com/mrozon/feature_auth/presentation/LoginFragment.kt
  23. 5 0
      feature_auth/src/main/java/com/mrozon/feature_auth/presentation/LoginFragmentViewModel.kt
  24. 3 2
      feature_person/build.gradle
  25. 31 0
      feature_person/src/main/java/com/mrozon/feature_person/di/DaggerViewModelFactory.kt
  26. 32 0
      feature_person/src/main/java/com/mrozon/feature_person/di/ListPersonFragmentComponent.kt
  27. 20 0
      feature_person/src/main/java/com/mrozon/feature_person/di/ListPersonFragmentModule.kt
  28. 30 0
      feature_person/src/main/java/com/mrozon/feature_person/presentation/ListPersonFragment.kt
  29. 7 0
      feature_person/src/main/java/com/mrozon/feature_person/presentation/ListPersonFragmentViewModel.kt
  30. 47 0
      feature_person/src/main/res/layout/fragment_list_person.xml
  31. 61 0
      feature_person/src/main/res/layout/item_person.xml
  32. 5 0
      feature_person/src/main/res/values/dimens.xml
  33. 5 0
      feature_person/src/main/res/values/strings.xml
  34. 1 1
      feature_splash/src/main/java/com/mrozon/feature_splash/presentation/SplashFragment.kt
  35. 7 0
      scripts/deps_versions.gradle
  36. 9 0
      utils/src/main/java/com/mrozon/utils/extension/TypeExt.kt
  37. 5 0
      utils/src/main/res/drawable/ic_add_24.xml

+ 4 - 3
app/build.gradle

@@ -43,16 +43,16 @@ dependencies {
     api project(':core_api')
     implementation project(':core')
     implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
-    //DAGGER
     implementation dagger
     kapt daggerCompiler
-    //NAVIGATION
     implementation navigationFragment
     implementation navigationUi
     implementation navigationDynamicFeatures
-    //Timber
     implementation timber
     implementation constraintlayout
+    implementation cardview
+    implementation recyclerview
+    implementation material
 
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@@ -64,4 +64,5 @@ dependencies {
     implementation project(':utils')
     implementation project(':feature_splash')
     implementation project(':feature_auth')
+    implementation project(':feature_person')
 }

+ 4 - 0
app/src/main/java/com/mrozon/healthdiary/navigation/LoginNavigatorImpl.kt

@@ -13,5 +13,9 @@ class LoginNavigatorImpl @Inject constructor()
         navController.navigate(R.id.action_loginFragment_to_registrationFragment)
     }
 
+    override fun navigateToListPerson(navController: NavController) {
+        navController.navigate(R.id.action_loginFragment_to_listPersonFragment)
+    }
+
 
 }

+ 3 - 4
app/src/main/java/com/mrozon/healthdiary/navigation/SplashNavigatorImpl.kt

@@ -12,9 +12,8 @@ class SplashNavigatorImpl @Inject constructor()
         navController.navigate(R.id.action_splashFragment_to_loginFragment)
     }
 
-//    override fun navigateToGameVideo() {
-//        navController?.navigate(R.id.action_gameListFragment_to_gameVideoFragment)
-//            ?: throw IllegalStateException("NavController must be bound to ${this.javaClass.name} before processing navigation")
-//    }
+    override fun navigateToListPerson(navController: NavController) {
+        navController.navigate(R.id.action_splashFragment_to_listPersonFragment)
+    }
 
 }

+ 1 - 1
app/src/main/java/com/mrozon/healthdiary/presentation/main/MainActivity.kt

@@ -60,7 +60,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
         setSupportActionBar(binding.toolbar)
         drawerLayout = binding.drawerLayout
         navController = findNavController(R.id.nav_host_fragment)
-        appBarConfiguration = AppBarConfiguration(navController.graph, binding.drawerLayout)
+        appBarConfiguration = AppBarConfiguration(setOf(R.id.listPersonFragment), binding.drawerLayout)
         setupActionBarWithNavController(navController, appBarConfiguration)
     }
 

+ 6 - 0
app/src/main/res/anim/slide_in_left.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate android:fromXDelta="-100%" android:toXDelta="0%"
+        android:fromYDelta="0%" android:toYDelta="0%"
+        android:duration="@integer/slide"/>
+</set>

+ 6 - 0
app/src/main/res/anim/slide_in_right.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate android:fromXDelta="100%" android:toXDelta="0%"
+        android:fromYDelta="0%" android:toYDelta="0%"
+        android:duration="@integer/slide"/>
+</set>

+ 6 - 0
app/src/main/res/anim/slide_out_left.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate android:fromXDelta="0%" android:toXDelta="-100%"
+        android:fromYDelta="0%" android:toYDelta="0%"
+        android:duration="@integer/slide"/>
+</set>

+ 6 - 0
app/src/main/res/anim/slide_out_right.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate android:fromXDelta="0%" android:toXDelta="100%"
+        android:fromYDelta="0%" android:toYDelta="0%"
+        android:duration="@integer/slide"/>
+</set>

+ 49 - 4
app/src/main/res/navigation/nav_graph.xml

@@ -12,7 +12,22 @@
         tools:layout="@layout/fragment_splash" >
         <action
             android:id="@+id/action_splashFragment_to_loginFragment"
-            app:destination="@id/loginFragment" />
+            app:destination="@id/loginFragment"
+            app:enterAnim="@anim/slide_in_right"
+            app:exitAnim="@anim/slide_out_left"
+            app:launchSingleTop="true"
+            app:popEnterAnim="@anim/slide_in_left"
+            app:popExitAnim="@anim/slide_out_right" />
+        <action
+            android:id="@+id/action_splashFragment_to_listPersonFragment"
+            app:destination="@id/listPersonFragment"
+            app:enterAnim="@anim/slide_in_right"
+            app:exitAnim="@anim/slide_out_left"
+            app:launchSingleTop="true"
+            app:popEnterAnim="@anim/slide_in_left"
+            app:popExitAnim="@anim/slide_out_right"
+            app:popUpTo="@id/listPersonFragment"
+            app:popUpToInclusive="true" />
     </fragment>
 
     <fragment
@@ -22,7 +37,20 @@
         android:label="LoginFragment" >
         <action
             android:id="@+id/action_loginFragment_to_registrationFragment"
-            app:destination="@id/registrationFragment" />
+            app:destination="@id/registrationFragment"
+            app:enterAnim="@anim/slide_in_right"
+            app:exitAnim="@anim/slide_out_left"
+            app:popEnterAnim="@anim/slide_in_left"
+            app:popExitAnim="@anim/slide_out_right"/>
+        <action
+            android:id="@+id/action_loginFragment_to_listPersonFragment"
+            app:destination="@id/listPersonFragment"
+            app:enterAnim="@anim/slide_in_right"
+            app:exitAnim="@anim/slide_out_left"
+            app:popEnterAnim="@anim/slide_in_left"
+            app:popExitAnim="@anim/slide_out_right"
+            app:popUpTo="@id/listPersonFragment"
+            app:popUpToInclusive="true" />
     </fragment>
 
     <fragment
@@ -32,7 +60,24 @@
         android:label="RegistrationFragment" >
         <action
             android:id="@+id/action_registrationFragment_to_loginFragment"
-            app:destination="@id/loginFragment" />
-    </fragment><action android:id="@+id/action_global_loginFragment" app:destination="@id/loginFragment"/>
+            app:destination="@id/loginFragment"
+            app:enterAnim="@anim/slide_in_right"
+            app:exitAnim="@anim/slide_out_left"
+            app:popEnterAnim="@anim/slide_in_left"
+            app:popExitAnim="@anim/slide_out_right"/>
+    </fragment>
+
+    <action android:id="@+id/action_global_loginFragment"
+        app:destination="@id/loginFragment"
+        app:enterAnim="@anim/slide_in_right"
+        app:exitAnim="@anim/slide_out_left"
+        app:popEnterAnim="@anim/slide_in_left"
+        app:popExitAnim="@anim/slide_out_right"/>
+
+    <fragment
+        android:id="@+id/listPersonFragment"
+        android:name="com.mrozon.feature_person.presentation.ListPersonFragment"
+        android:label="ListPersonFragment"
+        tools:layout="@layout/fragment_list_person"/>
 
 </navigation>

+ 4 - 0
app/src/main/res/values/anim.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <integer name="slide">200</integer>
+</resources>

+ 17 - 0
core_api/src/main/java/com/mrozon/core_api/db/BlobTransmogrifier.kt

@@ -0,0 +1,17 @@
+package com.mrozon.core_api.db
+
+import androidx.room.TypeConverter
+import java.util.Date
+
+class BlobTransmogrifier {
+
+    @TypeConverter
+    fun fromDate(date: Date): Long {
+        return date.time
+    }
+
+    @TypeConverter
+    fun toDate(millisSinceEpoch: Long): Date {
+        return Date(millisSinceEpoch)
+    }
+}

+ 4 - 0
core_api/src/main/java/com/mrozon/core_api/db/HealthDiaryDao.kt

@@ -2,6 +2,7 @@ package com.mrozon.core_api.db
 
 import androidx.lifecycle.LiveData
 import androidx.room.*
+import com.mrozon.core_api.db.model.PersonDb
 import com.mrozon.core_api.db.model.UserDb
 
 @Dao
@@ -15,4 +16,7 @@ interface HealthDiaryDao {
 
     @Delete
     suspend fun deleteUser(userDb: UserDb)
+
+    @Query("SELECT * FROM person_table")
+    fun getPersons(): LiveData<PersonDb>
 }

+ 25 - 0
core_api/src/main/java/com/mrozon/core_api/db/model/PersonDb.kt

@@ -0,0 +1,25 @@
+package com.mrozon.core_api.db.model
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import androidx.room.TypeConverters
+import com.mrozon.core_api.db.BlobTransmogrifier
+import java.util.Date
+
+@Entity(tableName = "person_table")
+@TypeConverters(BlobTransmogrifier::class)
+data class PersonDb(
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "person_id")
+    var id: Long = 0L,
+
+    @ColumnInfo(name = "person_name")
+    val name: String = "",
+
+    @ColumnInfo(name = "person_gender")
+    var gender: Int = 0,
+
+    @ColumnInfo(name = "user_born")
+    var born: Date
+)

+ 14 - 0
core_api/src/main/java/com/mrozon/core_api/entity/Person.kt

@@ -0,0 +1,14 @@
+package com.mrozon.core_api.entity
+
+import java.util.Date
+
+data class Person (
+    var id: Long = 0L,
+    var name: String,
+    var gender: Gender = Gender.MALE,
+    var born: Date
+)
+
+enum class Gender(val code: Int) {
+    MALE(0),
+    FEMALE(1)}

+ 37 - 0
core_api/src/main/java/com/mrozon/core_api/mapper/PersonToPersonDbMapper.kt

@@ -0,0 +1,37 @@
+package com.mrozon.core_api.mapper
+
+import com.mrozon.core_api.db.model.PersonDb
+import com.mrozon.core_api.db.model.UserDb
+import com.mrozon.core_api.entity.Gender
+import com.mrozon.core_api.entity.Person
+import com.mrozon.core_api.entity.User
+import com.mrozon.utils.base.BaseMapper
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class PersonToPersonDbMapper @Inject constructor(): BaseMapper<Person, PersonDb>() {
+
+    override fun map(entity: Person?): PersonDb? {
+        entity?.let {
+            return PersonDb(id = it.id,
+                name = it.name,
+                gender = it.gender.code,
+                born = it.born
+            )
+        }
+        return null
+    }
+
+    override fun reverseMap(model: PersonDb?): Person? {
+        model?.let {
+            return Person(id = it.id,
+                name = it.name,
+                gender = Gender.values()[it.gender],
+                born = it.born
+            )
+        }
+        return null
+    }
+
+}

+ 1 - 0
core_api/src/main/java/com/mrozon/core_api/navigation/LoginNavigator.kt

@@ -4,4 +4,5 @@ import androidx.navigation.NavController
 
 interface LoginNavigator {
     fun navigateToRegisterUser(navController: NavController)
+    fun navigateToListPerson(navController: NavController)
 }

+ 1 - 0
core_api/src/main/java/com/mrozon/core_api/navigation/SplashNavigator.kt

@@ -5,4 +5,5 @@ import androidx.navigation.NavController
 
 interface SplashNavigator {
     fun navigateToAuth(navController: NavController)
+    fun navigateToListPerson(navController: NavController)
 }

+ 6 - 5
core_api/src/main/java/com/mrozon/core_api/network/HealthDiaryService.kt

@@ -1,15 +1,13 @@
 package com.mrozon.core_api.network
 
-import com.mrozon.core_api.network.model.LoginRequest
-import com.mrozon.core_api.network.model.LoginResponse
-import com.mrozon.core_api.network.model.RegisterRequest
-import com.mrozon.core_api.network.model.RegisterResponse
+import com.mrozon.core_api.network.model.*
 import retrofit2.Response
 import retrofit2.http.*
 
 interface HealthDiaryService {
     companion object {
-        const val ENDPOINT =  "http://10.0.2.2:8000/"//"https://hdb.mr-ozon-1982.tk/"
+        const val ENDPOINT = "http://10.0.2.2:8000/"
+//        const val ENDPOINT = "https://hdb.mr-ozon-1982.tk/"
     }
 
     @POST("login/")
@@ -18,6 +16,9 @@ interface HealthDiaryService {
     @POST("register/")
     suspend fun registerUser(@Body body: RegisterRequest): Response<RegisterResponse>
 
+    @GET("patients/")
+    suspend fun getPatients(): Response<List<PersonResponse>>
+
 //    @GET("lego/themes/")
 //    suspend fun getThemes(@Query("page") page: Int? = null,
 //                          @Query("page_size") pageSize: Int? = null,

+ 7 - 0
core_api/src/main/java/com/mrozon/core_api/network/model/PersonRequest.kt

@@ -0,0 +1,7 @@
+package com.mrozon.core_api.network.model
+
+data class PersonRequest(
+    val born: String,
+    val gender: Boolean,
+    val name: String
+)

+ 24 - 0
core_api/src/main/java/com/mrozon/core_api/network/model/PersonResponse.kt

@@ -0,0 +1,24 @@
+package com.mrozon.core_api.network.model
+
+import com.mrozon.core_api.entity.Gender
+import com.mrozon.core_api.entity.Person
+import com.mrozon.utils.extension.toSimpleDate
+
+data class PersonResponse(
+    val avatar: Any,
+    val born: String,
+    val created_date: String,
+    val gender: Boolean,
+    val id: Int,
+    val name: String,
+    val owners: List<Int>
+)
+
+fun PersonResponse.toPerson(): Person{
+    return Person(
+        id = id.toLong(),
+        name = name,
+        gender = if (gender) Gender.MALE else Gender.FEMALE,
+        born = born.toSimpleDate()
+    )
+}

+ 2 - 1
core_impl/src/main/java/com/mrozon/core_impl/db/HealthDiaryDb.kt

@@ -3,7 +3,8 @@ package com.mrozon.core_impl.db
 import androidx.room.Database
 import androidx.room.RoomDatabase
 import com.mrozon.core_api.db.HealthDiaryDatabaseContract
+import com.mrozon.core_api.db.model.PersonDb
 import com.mrozon.core_api.db.model.UserDb
 
-@Database(entities = [UserDb::class], version = 1)
+@Database(entities = [UserDb::class, PersonDb::class], version = 1)
 abstract class HealthDiaryDb : RoomDatabase(), HealthDiaryDatabaseContract

+ 6 - 0
feature_auth/src/main/java/com/mrozon/feature_auth/presentation/LoginFragment.kt

@@ -88,6 +88,12 @@ class LoginFragment : BaseFragment<FragmentLoginBinding>() {
             if(error!=null)
                 showError(error) {}
         })
+
+        viewModel.showPersons.observe(viewLifecycleOwner, Observer { showPersons ->
+            if(showPersons) {
+                navigator.navigateToListPerson(findNavController())
+            }
+        })
     }
 
 }

+ 5 - 0
feature_auth/src/main/java/com/mrozon/feature_auth/presentation/LoginFragmentViewModel.kt

@@ -29,6 +29,10 @@ class LoginFragmentViewModel @Inject constructor(
     val progress: LiveData<Boolean>
         get() = _progress
 
+    private val _showPersons = MutableLiveData<Boolean>(false)
+    val showPersons: LiveData<Boolean>
+        get() = _showPersons
+
     private val _error = MutableLiveData<String?>()
     val error: LiveData<String?>
         get() = _error
@@ -73,6 +77,7 @@ class LoginFragmentViewModel @Inject constructor(
                     healthDiaryDao.insertUser(result?.toUserDb()!!)
                     Timber.d(result.token)
                     withContext(Dispatchers.Main) {
+                        _showPersons.value = true
                         _progress.value = false
                     }
                 } else if (response.status == com.mrozon.utils.network.Result.Status.ERROR) {

+ 3 - 2
feature_person/build.gradle

@@ -40,16 +40,17 @@ apply from: "$project.rootDir/scripts/deps_versions.gradle"
 dependencies {
     api project(':core_api')
     implementation project(':core')
-    //DAGGER
     implementation dagger
     implementation 'androidx.legacy:legacy-support-v4:1.0.0'
     kapt daggerCompiler
     implementation project(':utils')
     implementation constraintlayout
-    //Timber
     implementation timber
     implementation navigationFragment
     implementation retrofit
+    implementation cardview
+    implementation recyclerview
+    implementation material
 
     implementation fileTree(dir: "libs", include: ["*.jar"])
     implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

+ 31 - 0
feature_person/src/main/java/com/mrozon/feature_person/di/DaggerViewModelFactory.kt

@@ -0,0 +1,31 @@
+package com.mrozon.feature_person.di
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import javax.inject.Inject
+import javax.inject.Provider
+
+class DaggerViewModelFactory @Inject constructor(
+    private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
+) : ViewModelProvider.Factory {
+    override fun <T : ViewModel> create(modelClass: Class<T>): T {
+        var creator: Provider<out ViewModel>? = creators[modelClass]
+        if (creator == null) {
+            for ((key, value) in creators) {
+                if (modelClass.isAssignableFrom(key)) {
+                    creator = value
+                    break
+                }
+            }
+        }
+        if (creator == null) {
+            throw IllegalArgumentException("Unknown model class: $modelClass")
+        }
+        try {
+            @Suppress("UNCHECKED_CAST")
+            return creator.get() as T
+        } catch (e: Exception) {
+            throw RuntimeException(e)
+        }
+    }
+}

+ 32 - 0
feature_person/src/main/java/com/mrozon/feature_person/di/ListPersonFragmentComponent.kt

@@ -0,0 +1,32 @@
+package com.mrozon.feature_person.di
+
+import com.mrozon.core_api.providers.AppWithFacade
+import com.mrozon.core_api.providers.ProvidersFacade
+import com.mrozon.core_api.viewmodel.ViewModelsFactoryProvider
+import com.mrozon.feature_person.presentation.ListPersonFragment
+import dagger.Component
+import javax.inject.Singleton
+
+@Singleton
+@Component(
+    modules = [ListPersonFragmentModule::class],
+    dependencies = [ProvidersFacade::class]
+)
+interface ListPersonFragmentComponent: ViewModelsFactoryProvider {
+
+    companion object {
+        fun create(providersFacade: ProvidersFacade): ListPersonFragmentComponent {
+            return DaggerListPersonFragmentComponent.builder()
+                .providersFacade(providersFacade)
+                .build()
+        }
+        fun injectFragment(fragment: ListPersonFragment): ListPersonFragmentComponent  {
+            val component = create((fragment.activity?.application
+                    as AppWithFacade).getFacade())
+            component.inject(fragment)
+            return component
+        }
+    }
+
+    fun inject(fragment: ListPersonFragment)
+}

+ 20 - 0
feature_person/src/main/java/com/mrozon/feature_person/di/ListPersonFragmentModule.kt

@@ -0,0 +1,20 @@
+package com.mrozon.feature_person.di
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.mrozon.core_api.viewmodel.ViewModelKey
+import com.mrozon.feature_person.presentation.ListPersonFragmentViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+
+@Module
+interface ListPersonFragmentModule {
+    @Binds
+    @IntoMap
+    @ViewModelKey(ListPersonFragmentViewModel::class)
+    fun bindViewModel(viewmodel: ListPersonFragmentViewModel): ViewModel
+
+    @Binds
+    fun viewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory
+}

+ 30 - 0
feature_person/src/main/java/com/mrozon/feature_person/presentation/ListPersonFragment.kt

@@ -0,0 +1,30 @@
+package com.mrozon.feature_person.presentation
+
+import android.content.Context
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.ViewModelProvider
+import com.mrozon.feature_person.R
+import com.mrozon.feature_person.databinding.FragmentListPersonBinding
+import com.mrozon.feature_person.di.ListPersonFragmentComponent
+import com.mrozon.utils.base.BaseFragment
+import javax.inject.Inject
+
+class ListPersonFragment : BaseFragment<FragmentListPersonBinding>() {
+
+    override fun getLayoutId(): Int = R.layout.fragment_list_person
+
+    @Inject
+    lateinit var viewModelFactory: ViewModelProvider.Factory
+
+    private val viewModel by viewModels<ListPersonFragmentViewModel> { viewModelFactory }
+
+    override fun onAttach(context: Context) {
+        super.onAttach(context)
+        ListPersonFragmentComponent.injectFragment(this)
+    }
+
+    override fun subscribeUi() {
+
+    }
+
+}

+ 7 - 0
feature_person/src/main/java/com/mrozon/feature_person/presentation/ListPersonFragmentViewModel.kt

@@ -0,0 +1,7 @@
+package com.mrozon.feature_person.presentation
+
+import com.mrozon.utils.base.BaseViewModel
+import javax.inject.Inject
+
+class ListPersonFragmentViewModel @Inject constructor(): BaseViewModel() {
+}

+ 47 - 0
feature_person/src/main/res/layout/fragment_list_person.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <com.google.android.material.floatingactionbutton.FloatingActionButton
+            android:id="@+id/floatingActionButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="32dp"
+            android:layout_marginBottom="32dp"
+            android:clickable="true"
+            android:focusable="true"
+            app:fabSize="normal"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:srcCompat="@drawable/ic_add_24"
+            app:useCompatPadding="true" />
+
+        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+            android:id="@+id/srlPerson"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/rvPerson"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                tools:listitem="@layout/item_person" />
+
+
+        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 61 - 0
feature_person/src/main/res/layout/item_person.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:tools="http://schemas.android.com/tools"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <data>
+
+    </data>
+
+    <androidx.cardview.widget.CardView
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:id="@+id/cvPerson"
+        android:layout_gravity="center"
+        android:layout_margin="@dimen/card_margin"
+        app:cardCornerRadius="5dp"
+        app:elevation="@dimen/cardview_elevation">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ImageView
+                android:id="@+id/ivGender"
+                android:layout_width="40dp"
+                android:layout_height="40dp"
+                android:layout_marginBottom="8dp"
+                android:layout_marginStart="8dp"
+                android:layout_marginTop="8dp"
+                android:contentDescription="@string/ivGender"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:srcCompat="@drawable/ic_logo_24" />
+
+            <TextView
+                android:id="@+id/person_name"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_centerVertical="true"
+                android:layout_marginLeft="16dp"
+                android:layout_marginRight="16dp"
+                android:layout_toEndOf="@id/ivGender"
+                android:paddingTop="16dp"
+                android:text="textview_name"
+                android:textAppearance="@style/TextAppearance.AppCompat.Large"
+                android:textColor="#000000"
+                android:textSize="20sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintHorizontal_bias="0.0"
+                app:layout_constraintStart_toEndOf="@+id/ivGender"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintVertical_chainStyle="packed"
+                tools:text="Полезный человек" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </androidx.cardview.widget.CardView>
+
+</layout>

+ 5 - 0
feature_person/src/main/res/values/dimens.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <dimen name="card_margin">3dp</dimen>
+    <dimen name="cardview_elevation">3dp</dimen>
+</resources>

+ 5 - 0
feature_person/src/main/res/values/strings.xml

@@ -0,0 +1,5 @@
+<resources>
+    <!-- TODO: Remove or change this placeholder text -->
+    <string name="hello_blank_fragment">Hello blank fragment</string>
+    <string name="ivGender">gender\'s person</string>
+</resources>

+ 1 - 1
feature_splash/src/main/java/com/mrozon/feature_splash/presentation/SplashFragment.kt

@@ -44,7 +44,7 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
             }
             else
             {
-                //show profiles
+                navigator.navigateToListPerson(findNavController())
             }
         })
     }

+ 7 - 0
scripts/deps_versions.gradle

@@ -15,6 +15,9 @@ ext {
     gsonVersion = '2.8.2'
     okhttpVersion = '3.10.0'
     constraintlayoutVersion = '1.1.3'
+    cardviewVersion = '1.0.0'
+    recyclerviewViersion = '1.2.0-alpha05'
+    materialVersion = '1.2.1'
 
 //    retrofit = "com.squareup.retrofit2:retrofit:$retrofitVersion"
 //    jsr330 = "javax.inject:javax.inject:$jsr330Version"
@@ -48,5 +51,9 @@ ext {
     converterGson = "com.squareup.retrofit2:converter-gson:$retrofitVersion"
     okhttp = "com.squareup.okhttp3:okhttp:$okhttpVersion"
     loggingInterceptor = "com.squareup.okhttp3:logging-interceptor:$okhttpVersion"
+    // UI
     constraintlayout = "androidx.constraintlayout:constraintlayout:$constraintlayoutVersion"
+    cardview =  "androidx.cardview:cardview:$cardviewVersion"
+    recyclerview = "androidx.recyclerview:recyclerview:$recyclerviewViersion"
+    material = "com.google.android.material:material:$materialVersion"
 }

+ 9 - 0
utils/src/main/java/com/mrozon/utils/extension/TypeExt.kt

@@ -0,0 +1,9 @@
+package com.mrozon.utils.extension
+
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+fun String.toSimpleDate(format: String = "YYYY-MM-DD"): Date {
+    return SimpleDateFormat(format, Locale.getDefault()).parse(this)!!
+}

+ 5 - 0
utils/src/main/res/drawable/ic_add_24.xml

@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FFFFFF"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>