From 4188fddc1c64f28f9bcb74262e17bbf127fda514 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Wed, 15 Dec 2021 15:21:41 +0300 Subject: [PATCH 01/19] feat(sample): add base modules --- buildSrc/src/main/kotlin/Deps.kt | 26 +- sample/{ => app}/.gitignore | 0 sample/{ => app}/build.gradle.kts | 17 +- sample/{ => app}/proguard-rules.pro | 0 sample/{ => app}/src/main/AndroidManifest.xml | 2 +- .../sample/ui/SampleApplication.kt | 2 +- .../sample/ui/sample/SampleActivity.kt | 11 +- .../drawable-v24/ic_launcher_foreground.xml | 0 .../res/drawable/ic_launcher_background.xml | 0 .../src/main/res/layout/sample_activity.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../{ => app}/src/main/res/values/strings.xml | 0 sample/app/src/main/res/values/themes.xml | 18 ++ sample/features/base/core/.gitignore | 1 + sample/features/base/core/build.gradle.kts | 19 ++ .../compositeadapter/core/AppLogger.kt | 40 +++ .../compositeadapter/core/di/CoreModule.kt | 28 ++ .../compositeadapter/core/entity/Scene.kt | 20 ++ .../core/extensions/ExceptionsExtensions.kt | 24 ++ .../core/extensions/SceneExtensions.kt | 97 ++++++ .../compositeadapter/core/utils/Loading.kt | 22 ++ sample/features/base/ui/.gitignore | 1 + sample/features/base/ui/build.gradle.kts | 46 +++ sample/features/base/ui/consumer-rules.pro | 0 sample/features/base/ui/proguard-rules.pro | 21 ++ .../base/ui/src/main/AndroidManifest.xml | 2 + .../ui/cell/CommonErrorCell.kt | 30 ++ .../ui/cell/CommonFullEmptyCell.kt | 30 ++ .../ui/cell/CommonFullErrorCell.kt | 30 ++ .../ui/cell/CommonFullLoaderCell.kt | 15 + .../ui/cell/CommonLoaderCell.kt | 35 +++ .../ui/cell/databinding/DataBindingCell.kt | 49 ++++ .../ui/cell/viewbinding/CellViewHolder.kt | 6 + .../ui/cell/viewbinding/ViewBindingCell.kt} | 7 +- .../compositeadapter/ui/di/UIModule.kt | 20 ++ .../ui/entity/CommonErrorUI.kt | 17 ++ .../ui/entity/CommonLoaderUI.kt | 8 + .../ui/extensions/CellExtensions.kt | 13 + .../ui/fragment/BaseFragment.kt | 34 +++ .../ui/fragment/SimpleRecyclerFragment.kt | 15 + .../ui/mapper/ErrorUIMapper.kt | 39 +++ .../ui/mapper/LoaderUIMapper.kt | 14 + .../compositeadapter/ui/mapper/SceneMapper.kt | 276 ++++++++++++++++++ .../ui/utils/CompositeAdapterUtils.kt | 32 ++ .../compositeadapter/ui/utils/FlowUtils.kt | 22 ++ .../ui/utils/RecyclerUtils.kt | 87 ++++++ .../ui/utils/SwipeRefreshUtils.kt | 29 ++ .../ui/utils/ViewModelUtils.kt | 34 +++ .../ui/vm/BaseDataViewModel.kt | 17 ++ .../ui/vm/BaseStateViewModel.kt | 102 +++++++ .../compositeadapter/ui/vm/BaseViewModel.kt | 26 ++ .../src/main/res/layout/common_error_cell.xml | 12 + .../res/layout/common_full_empty_cell.xml | 12 + .../res/layout/common_full_error_cell.xml | 12 + .../res/layout/common_full_loader_cell.xml | 13 + .../main/res/layout/common_loader_cell.xml | 13 + .../res/layout/common_recycler_fragment.xml | 11 + .../res/layout/fake_databinding_layout.xml | 16 + .../base/ui/src/main/res/values/colors.xml | 22 ++ .../base/ui/src/main/res/values/strings.xml | 5 + sample/features/messages/core/.gitignore | 1 + .../features/messages/core/build.gradle.kts | 17 ++ .../features/messages/core/consumer-rules.pro | 0 .../features/messages/core/proguard-rules.pro | 21 ++ .../messages/core}/entity/MessageEntity.kt | 2 +- sample/features/messages/data/.gitignore | 1 + .../features/messages/data/build.gradle.kts | 26 ++ .../features/messages/data/consumer-rules.pro | 0 .../features/messages/data/proguard-rules.pro | 21 ++ .../data/src/main/AndroidManifest.xml | 2 + sample/features/messages/ui/.gitignore | 1 + sample/features/messages/ui/build.gradle.kts | 32 ++ .../features/messages/ui/consumer-rules.pro | 0 .../features/messages/ui/proguard-rules.pro | 21 ++ .../messages/ui/src/main/AndroidManifest.xml | 2 + .../compositeadapter/messages/ui/SampleDI.kt | 17 ++ .../messages/ui}/SampleMapper.kt | 73 +++-- .../messages/ui}/SampleState.kt | 4 +- .../messages/ui}/SampleViewModel.kt | 12 +- .../SampleDataBindingMessageCell.kt | 11 +- .../ui}/cell/view/SampleViewMessageCell.kt | 8 +- .../SampleViewBindingMessageCell.kt | 15 +- .../layout/message_databinding_list_item.xml} | 2 +- .../layout/message_viewbinding_list_item.xml} | 0 .../messages/ui/src/main/res/values/ids.xml | 4 + .../compositeadapter/sample/ui/di/SampleDI.kt | 17 -- .../cell/common/SampleCellViewHolder.kt | 6 - .../ui/sample/cell/common/SampleErrorCell.kt | 23 -- .../cell/common/SampleFullScreenErrorCell.kt | 23 -- .../cell/common/SampleFullScreenLoaderCell.kt | 36 --- .../ui/sample/cell/common/SampleLoaderCell.kt | 36 --- .../cell/databinding/SampleDataBindingCell.kt | 32 -- .../res/drawable/common_ic_big_refresh.xml | 9 - .../main/res/drawable/common_ic_refresh.xml | 9 - .../res/layout/common_error_list_item.xml | 18 -- .../common_fullscreen_error_list_item.xml | 20 -- sample/src/main/res/values/colors.xml | 6 - sample/src/main/res/values/ids.xml | 6 - sample/src/main/res/values/styles.xml | 9 - settings.gradle.kts | 9 +- 111 files changed, 1725 insertions(+), 327 deletions(-) rename sample/{ => app}/.gitignore (100%) rename sample/{ => app}/build.gradle.kts (66%) rename sample/{ => app}/proguard-rules.pro (100%) rename sample/{ => app}/src/main/AndroidManifest.xml (94%) rename sample/{ => app}/src/main/java/com/originsdigital/compositeadapter/sample/ui/SampleApplication.kt (77%) rename sample/{ => app}/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SampleActivity.kt (89%) rename sample/{ => app}/src/main/res/drawable-v24/ic_launcher_foreground.xml (100%) rename sample/{ => app}/src/main/res/drawable/ic_launcher_background.xml (100%) rename sample/{ => app}/src/main/res/layout/sample_activity.xml (100%) rename sample/{ => app}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename sample/{ => app}/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename sample/{ => app}/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename sample/{ => app}/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename sample/{ => app}/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename sample/{ => app}/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename sample/{ => app}/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename sample/{ => app}/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename sample/{ => app}/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename sample/{ => app}/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename sample/{ => app}/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename sample/{ => app}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename sample/{ => app}/src/main/res/values/strings.xml (100%) create mode 100644 sample/app/src/main/res/values/themes.xml create mode 100644 sample/features/base/core/.gitignore create mode 100644 sample/features/base/core/build.gradle.kts create mode 100644 sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt create mode 100644 sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt create mode 100644 sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt create mode 100644 sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt create mode 100644 sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt create mode 100644 sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt create mode 100644 sample/features/base/ui/.gitignore create mode 100644 sample/features/base/ui/build.gradle.kts create mode 100644 sample/features/base/ui/consumer-rules.pro create mode 100644 sample/features/base/ui/proguard-rules.pro create mode 100644 sample/features/base/ui/src/main/AndroidManifest.xml create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/databinding/DataBindingCell.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/CellViewHolder.kt rename sample/{src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/viewbinding/SampleViewBindingCell.kt => features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt} (50%) create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt create mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt create mode 100644 sample/features/base/ui/src/main/res/layout/common_error_cell.xml create mode 100644 sample/features/base/ui/src/main/res/layout/common_full_empty_cell.xml create mode 100644 sample/features/base/ui/src/main/res/layout/common_full_error_cell.xml create mode 100644 sample/features/base/ui/src/main/res/layout/common_full_loader_cell.xml create mode 100644 sample/features/base/ui/src/main/res/layout/common_loader_cell.xml create mode 100644 sample/features/base/ui/src/main/res/layout/common_recycler_fragment.xml create mode 100644 sample/features/base/ui/src/main/res/layout/fake_databinding_layout.xml create mode 100644 sample/features/base/ui/src/main/res/values/colors.xml create mode 100644 sample/features/base/ui/src/main/res/values/strings.xml create mode 100644 sample/features/messages/core/.gitignore create mode 100644 sample/features/messages/core/build.gradle.kts create mode 100644 sample/features/messages/core/consumer-rules.pro create mode 100644 sample/features/messages/core/proguard-rules.pro rename sample/{src/main/java/com/originsdigital/compositeadapter/sample/domain => features/messages/core/src/main/java/com/originsdigital/compositeadapter/messages/core}/entity/MessageEntity.kt (71%) create mode 100644 sample/features/messages/data/.gitignore create mode 100644 sample/features/messages/data/build.gradle.kts create mode 100644 sample/features/messages/data/consumer-rules.pro create mode 100644 sample/features/messages/data/proguard-rules.pro create mode 100644 sample/features/messages/data/src/main/AndroidManifest.xml create mode 100644 sample/features/messages/ui/.gitignore create mode 100644 sample/features/messages/ui/build.gradle.kts create mode 100644 sample/features/messages/ui/consumer-rules.pro create mode 100644 sample/features/messages/ui/proguard-rules.pro create mode 100644 sample/features/messages/ui/src/main/AndroidManifest.xml create mode 100644 sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleDI.kt rename sample/{src/main/java/com/originsdigital/compositeadapter/sample/ui/sample => features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui}/SampleMapper.kt (64%) rename sample/{src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/vm => features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui}/SampleState.kt (82%) rename sample/{src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/vm => features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui}/SampleViewModel.kt (92%) rename sample/{src/main/java/com/originsdigital/compositeadapter/sample/ui/sample => features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui}/cell/databinding/SampleDataBindingMessageCell.kt (54%) rename sample/{src/main/java/com/originsdigital/compositeadapter/sample/ui/sample => features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui}/cell/view/SampleViewMessageCell.kt (88%) rename sample/{src/main/java/com/originsdigital/compositeadapter/sample/ui/sample => features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui}/cell/viewbinding/SampleViewBindingMessageCell.kt (55%) rename sample/{src/main/res/layout/sample_databinding_message_list_item.xml => features/messages/ui/src/main/res/layout/message_databinding_list_item.xml} (86%) rename sample/{src/main/res/layout/sample_viewbinding_message_list_item.xml => features/messages/ui/src/main/res/layout/message_viewbinding_list_item.xml} (100%) create mode 100644 sample/features/messages/ui/src/main/res/values/ids.xml delete mode 100644 sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/di/SampleDI.kt delete mode 100644 sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleCellViewHolder.kt delete mode 100644 sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleErrorCell.kt delete mode 100644 sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleFullScreenErrorCell.kt delete mode 100644 sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleFullScreenLoaderCell.kt delete mode 100644 sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleLoaderCell.kt delete mode 100644 sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/databinding/SampleDataBindingCell.kt delete mode 100644 sample/src/main/res/drawable/common_ic_big_refresh.xml delete mode 100644 sample/src/main/res/drawable/common_ic_refresh.xml delete mode 100644 sample/src/main/res/layout/common_error_list_item.xml delete mode 100644 sample/src/main/res/layout/common_fullscreen_error_list_item.xml delete mode 100644 sample/src/main/res/values/colors.xml delete mode 100644 sample/src/main/res/values/ids.xml delete mode 100644 sample/src/main/res/values/styles.xml diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index d6c15fc..0df1821 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -10,7 +10,7 @@ object Config { const val sampleMinSdk = 21 const val targetSdk = compileSdk - val javaVersion = JavaVersion.VERSION_1_8 + val javaVersion = JavaVersion.VERSION_11 const val packageNameDev = "io.github.netcosports.compositeadapter.sample" const val packageNameProd = "io.github.netcosports.compositeadapter.sample" @@ -40,13 +40,20 @@ object Config { const val appcompat = "androidx.appcompat:appcompat:1.4.0" const val swipeRefresh = "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" const val recycler = "androidx.recyclerview:recyclerview:1.2.1" - const val lifecycleVersion = "2.4.0" + const val core = "androidx.core:core-ktx:1.6.0" + private const val lifecycleVersion = "2.4.0" + const val lifecycle = "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" const val lifecycleViewModelKtx = "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" const val lifecycleLiveDataKtx = "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion" } + object Material { + const val material = "com.google.android.material:material:1.4.0" + } + object Coroutines { - const val version = "1.5.2" + private const val version = "1.5.2" + const val core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version" const val android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version" } @@ -56,12 +63,25 @@ object Config { } } + object Koin { + private const val version = "3.1.2" + const val core = "io.insert-koin:koin-core:$version" + const val android = "io.insert-koin:koin-android:$version" + } + object Kotlin { const val kotlinJdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${Build.kotlinVersion}" } object Libs { const val compositeAdapter = ":composite-adapter" + + const val baseCore = ":sample:features:base:core" + const val baseUI = ":sample:features:base:ui" + + const val messagesData = ":sample:features:messages:data" + const val messagesCore = ":sample:features:messages:core" + const val messagesUI = ":sample:features:messages:ui" } } } \ No newline at end of file diff --git a/sample/.gitignore b/sample/app/.gitignore similarity index 100% rename from sample/.gitignore rename to sample/app/.gitignore diff --git a/sample/build.gradle.kts b/sample/app/build.gradle.kts similarity index 66% rename from sample/build.gradle.kts rename to sample/app/build.gradle.kts index 693f378..e0df957 100644 --- a/sample/build.gradle.kts +++ b/sample/app/build.gradle.kts @@ -1,14 +1,13 @@ plugins { id(Config.Plugins.androidApp) kotlin(Config.Plugins.android) - kotlin(Config.Plugins.kapt) } android { compileSdk = Config.Build.compileSdk defaultConfig { - minSdk = Config.Build.minSdk + minSdk = Config.Build.sampleMinSdk targetSdk = Config.Build.targetSdk versionCode = getCustomVersionCode() @@ -53,15 +52,7 @@ android { } dependencies { - implementation(Config.Deps.Kotlin.kotlinJdk8) - implementation(Config.Deps.Coroutines.android) - - implementation(Config.Deps.AndroidX.appcompat) - implementation(Config.Deps.AndroidX.recycler) - implementation(Config.Deps.AndroidX.swipeRefresh) - implementation(Config.Deps.AndroidX.lifecycleViewModelKtx) - implementation(Config.Deps.AndroidX.lifecycleLiveDataKtx) - -// implementation(Config.Deps.CompositeAdapter.compositeAdapter) - implementation(project(Config.Deps.Libs.compositeAdapter)) + implementation(project(Config.Deps.Libs.baseUI)) + implementation(project(Config.Deps.Libs.messagesCore)) + implementation(project(Config.Deps.Libs.messagesUI)) } \ No newline at end of file diff --git a/sample/proguard-rules.pro b/sample/app/proguard-rules.pro similarity index 100% rename from sample/proguard-rules.pro rename to sample/app/proguard-rules.pro diff --git a/sample/src/main/AndroidManifest.xml b/sample/app/src/main/AndroidManifest.xml similarity index 94% rename from sample/src/main/AndroidManifest.xml rename to sample/app/src/main/AndroidManifest.xml index ad878eb..a9d1e5f 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/AppTheme"> + android:theme="@style/SampleTheme"> + viewModel.state.refreshingData.observe(this@SampleActivity) { isRefreshing -> swipeRefreshLayout.isRefreshing = isRefreshing - }) - viewModel.state.cellsData.observe(this@SampleActivity, Observer { items -> + } + viewModel.state.cellsData.observe(this@SampleActivity) { items -> adapter.submitList(items) - }) + } } } } \ No newline at end of file diff --git a/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from sample/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to sample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/sample/src/main/res/drawable/ic_launcher_background.xml b/sample/app/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from sample/src/main/res/drawable/ic_launcher_background.xml rename to sample/app/src/main/res/drawable/ic_launcher_background.xml diff --git a/sample/src/main/res/layout/sample_activity.xml b/sample/app/src/main/res/layout/sample_activity.xml similarity index 100% rename from sample/src/main/res/layout/sample_activity.xml rename to sample/app/src/main/res/layout/sample_activity.xml diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/sample/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/mipmap-hdpi/ic_launcher.png rename to sample/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png b/sample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from sample/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to sample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/sample/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/mipmap-mdpi/ic_launcher.png rename to sample/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png b/sample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from sample/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to sample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/mipmap-xhdpi/ic_launcher.png rename to sample/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/sample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to sample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to sample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/sample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to sample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/sample/src/main/res/values/strings.xml b/sample/app/src/main/res/values/strings.xml similarity index 100% rename from sample/src/main/res/values/strings.xml rename to sample/app/src/main/res/values/strings.xml diff --git a/sample/app/src/main/res/values/themes.xml b/sample/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..a0232f1 --- /dev/null +++ b/sample/app/src/main/res/values/themes.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/sample/features/base/core/.gitignore b/sample/features/base/core/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/sample/features/base/core/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/features/base/core/build.gradle.kts b/sample/features/base/core/build.gradle.kts new file mode 100644 index 0000000..ed15a46 --- /dev/null +++ b/sample/features/base/core/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + kotlin("jvm") + `java-library` +} + +java.sourceCompatibility = Config.Build.javaVersion +java.targetCompatibility = Config.Build.javaVersion + +project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java).configureEach { + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } +} + +dependencies { + api(Config.Deps.Kotlin.kotlinJdk8) + api(Config.Deps.Coroutines.core) + api(Config.Deps.Koin.core) +} \ No newline at end of file diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt new file mode 100644 index 0000000..29f8525 --- /dev/null +++ b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt @@ -0,0 +1,40 @@ +package com.originsdigital.compositeadapter.core + +import org.koin.core.context.GlobalContext + +interface AppLogger { + + fun logD(tag: String, message: String) + + fun logD(tag: String, message: String, error: Throwable) + + fun trackNonFatal(tag: String, message: String) +} + +fun log(tag: Any, message: String) { + log(getTag(tag), message) +} + +fun log(tag: String, message: String) { + GlobalContext.get().get().logD(tag, message) +} + +fun log(tag: Any, message: String, error: Throwable) { + log(getTag(tag), message) +} + +fun log(tag: String, message: String, error: Throwable) { + GlobalContext.get().get().logD(tag, message) +} + +fun trackNonFatal(tag: Any, message: String) { + trackNonFatal(getTag(tag), message) +} + +fun trackNonFatal(tag: String, message: String) { + GlobalContext.get().get().trackNonFatal(tag, message) +} + +private fun getTag(tag: Any): String { + return "${tag::class.java.simpleName}@${tag.hashCode()}" +} \ No newline at end of file diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt new file mode 100644 index 0000000..c77c815 --- /dev/null +++ b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt @@ -0,0 +1,28 @@ +package com.originsdigital.compositeadapter.core.di + +import kotlinx.coroutines.Dispatchers +import org.koin.core.context.GlobalContext +import org.koin.core.qualifier.StringQualifier +import org.koin.core.qualifier.named +import org.koin.dsl.module + +val coreModule + get() = module { + factory(BG_DISPATCHER_QUALIFIER) { Dispatchers.IO } + factory(UI_DISPATCHER_QUALIFIER) { Dispatchers.Main } + factory(DEFAULT_DISPATCHER_QUALIFIER) { Dispatchers.Default } + } + +val UI_DISPATCHER_QUALIFIER: StringQualifier get() = named("UI_DISPATCHER") +val BG_DISPATCHER_QUALIFIER: StringQualifier get() = named("BG_DISPATCHER") +val DEFAULT_DISPATCHER_QUALIFIER: StringQualifier get() = named("DEFAULT_DISPATCHER") + +val IS_DEBUG_QUALIFIER: StringQualifier get() = named("IS_DEBUG") +val IS_INTEGRATION_QUALIFIER: StringQualifier get() = named("IS_INTEGRATION") +val IS_PRODUCTION_BETA_QUALIFIER: StringQualifier get() = named("IS_PRODUCTION_BETA") +val IS_NOT_PRODUCTION_RELEASE_QUALIFIER: StringQualifier get() = named("IS_NOT_PRODUCTION_RELEASE") + +val IS_DEBUG: Boolean get() = GlobalContext.get().get(IS_DEBUG_QUALIFIER) +val IS_INTEGRATION: Boolean get() = GlobalContext.get().get(IS_INTEGRATION_QUALIFIER) +val IS_PRODUCTION_BETA: Boolean get() = GlobalContext.get().get(IS_PRODUCTION_BETA_QUALIFIER) +val IS_NOT_PRODUCTION_RELEASE: Boolean get() = GlobalContext.get().get(IS_NOT_PRODUCTION_RELEASE_QUALIFIER) \ No newline at end of file diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt new file mode 100644 index 0000000..9ba1b65 --- /dev/null +++ b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt @@ -0,0 +1,20 @@ +package com.originsdigital.compositeadapter.core.entity + +sealed class Scene { + + abstract val isRefreshing: Boolean + + data class Loading( + override val isRefreshing: Boolean = false + ) : Scene() + + data class Error( + val throwable: Throwable, + override val isRefreshing: Boolean + ) : Scene() + + data class Data( + val data: DATA, + override val isRefreshing: Boolean + ) : Scene() +} \ No newline at end of file diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt new file mode 100644 index 0000000..01eadb8 --- /dev/null +++ b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt @@ -0,0 +1,24 @@ +package com.originsdigital.compositeadapter.core.extensions + +import kotlinx.coroutines.TimeoutCancellationException +import java.io.InterruptedIOException +import java.net.SocketException +import java.net.UnknownHostException +import java.net.UnknownServiceException +import java.nio.channels.ClosedChannelException +import kotlin.coroutines.cancellation.CancellationException + +fun Throwable.rethrowIfNeeded() { + if (this !is TimeoutCancellationException && this is CancellationException) { + throw this + } +} + +val Throwable.isInternetConnectionException: Boolean + get() { + return this is UnknownHostException + || this is UnknownServiceException + || this is InterruptedIOException + || this is SocketException + || this is ClosedChannelException + } \ No newline at end of file diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt new file mode 100644 index 0000000..5116e66 --- /dev/null +++ b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt @@ -0,0 +1,97 @@ +package com.originsdigital.compositeadapter.core.extensions + +import com.originsdigital.compositeadapter.core.entity.Scene + +val Scene.dataOrNull: DATA? + get() { + return when (this) { + is Scene.Loading, is Scene.Error -> null + is Scene.Data -> this.data + } + } + +val Scene.isEmpty: Boolean get() = dataOrNull.isEmpty + +val Any?.isEmpty: Boolean + get() { + return when (this) { + null -> true + is Collection<*> -> isEmpty() + else -> false + } + } + +fun Scene.toLoading(force: Boolean): Scene { + return when (this) { + is Scene.Loading -> this + is Scene.Error -> { + if (force) { + Scene.Loading() + } else { + this.copy(isRefreshing = true) + } + } + is Scene.Data -> { + if (force) { + Scene.Loading() + } else { + this.copy(isRefreshing = true) + } + } + } +} + +fun Scene.toError( + error: Throwable, + force: Boolean +): Scene { + return when (this) { + is Scene.Loading, is Scene.Error -> Scene.Error(throwable = error, isRefreshing = false) + is Scene.Data -> if (force) { + Scene.Error(throwable = error, isRefreshing = false) + } else { + this.copy(isRefreshing = false) + } + } +} + +fun Scene.toData(data: DATA): Scene { + return when (this) { + is Scene.Loading, is Scene.Error -> Scene.Data(data = data, isRefreshing = false) + is Scene.Data -> this.copy(data = data, isRefreshing = false) + } +} + +fun Scene.Loading.mapData(): Scene { + return Scene.Loading() +} + +fun Scene.Error.mapData(): Scene { + return Scene.Error(throwable = throwable, isRefreshing = isRefreshing) +} + +fun Scene.Data.mapData( + mapper: (DATA) -> NEW_DATA +): Scene { + return Scene.Data(data = mapper(data), isRefreshing = isRefreshing) +} + +fun Scene.mapData( + mapper: (DATA) -> NEW_DATA +): Scene { + return when (this) { + is Scene.Loading -> this.mapData() + is Scene.Error -> this.mapData() + is Scene.Data -> this.mapData(mapper = mapper) + } +} + +fun Scene.mapDataScene( + mapper: (Scene.Data) -> Scene +): Scene { + return when (this) { + is Scene.Loading -> this.mapData() + is Scene.Error -> this.mapData() + is Scene.Data -> mapper(this) + } +} \ No newline at end of file diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt new file mode 100644 index 0000000..3809cc9 --- /dev/null +++ b/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt @@ -0,0 +1,22 @@ +package com.originsdigital.compositeadapter.core.utils + +import com.originsdigital.compositeadapter.core.log +import com.originsdigital.compositeadapter.core.extensions.rethrowIfNeeded + +suspend inline fun safeLoad( + tag: Any, + text: String, + crossinline block: (suspend () -> T), + crossinline error: (suspend (Exception) -> T) +): T { + return try { + log(tag, "$text started") + block().also { + log(tag, "$text result = $it") + } + } catch (e: Exception) { + e.rethrowIfNeeded() + log(tag, "$text error =", e) + error(e) + } +} \ No newline at end of file diff --git a/sample/features/base/ui/.gitignore b/sample/features/base/ui/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/sample/features/base/ui/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/features/base/ui/build.gradle.kts b/sample/features/base/ui/build.gradle.kts new file mode 100644 index 0000000..56f31c1 --- /dev/null +++ b/sample/features/base/ui/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) + kotlin(Config.Plugins.kapt) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.minSdk + targetSdk = Config.Build.targetSdk + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } + + buildFeatures { + dataBinding = true + viewBinding = true + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseCore)) + api(Config.Deps.Coroutines.android) + + api(Config.Deps.AndroidX.appcompat) + api(Config.Deps.AndroidX.recycler) + api(Config.Deps.AndroidX.core) + api(Config.Deps.AndroidX.swipeRefresh) + api(Config.Deps.AndroidX.lifecycle) + api(Config.Deps.AndroidX.lifecycleViewModelKtx) + api(Config.Deps.AndroidX.lifecycleLiveDataKtx) + api(Config.Deps.Material.material) + + api(Config.Deps.Koin.android) + +// api(Config.Deps.CompositeAdapter.compositeAdapter) + api(project(Config.Deps.Libs.compositeAdapter)) +} \ No newline at end of file diff --git a/sample/features/base/ui/consumer-rules.pro b/sample/features/base/ui/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/sample/features/base/ui/proguard-rules.pro b/sample/features/base/ui/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/sample/features/base/ui/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/sample/features/base/ui/src/main/AndroidManifest.xml b/sample/features/base/ui/src/main/AndroidManifest.xml new file mode 100644 index 0000000..37efe39 --- /dev/null +++ b/sample/features/base/ui/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt new file mode 100644 index 0000000..09a7a5c --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt @@ -0,0 +1,30 @@ +package com.originsdigital.compositeadapter.ui.cell + +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.ui.R +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.databinding.CommonErrorCellBinding +import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI +import com.originsdigital.compositeadapter.utils.getViewBinding + +data class CommonErrorCell( + override val data: CommonErrorUI, + override val decoration: ItemDecoration>?, + override val onClickListener: ((ClickItem) -> Unit)? +) : ViewBindingCell { + + override val uniqueId = data.id + override val layoutId = R.layout.common_error_cell + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder.getViewBinding(CommonErrorCellBinding::bind)).apply { + text.text = when (data.message) { + is CommonErrorUI.Message.Text -> data.message.text + is CommonErrorUI.Message.Resource -> text.context.getString(data.message.textId) + } + } + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt new file mode 100644 index 0000000..8acded9 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt @@ -0,0 +1,30 @@ +package com.originsdigital.compositeadapter.ui.cell + +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.ui.R +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.databinding.CommonFullEmptyCellBinding +import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI +import com.originsdigital.compositeadapter.utils.getViewBinding + +data class CommonFullEmptyCell( + override val data: CommonErrorUI, + override val decoration: ItemDecoration>?, + override val onClickListener: ((ClickItem) -> Unit)? +) : ViewBindingCell { + + override val uniqueId = data.id + override val layoutId = R.layout.common_full_empty_cell + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder.getViewBinding(CommonFullEmptyCellBinding::bind)).apply { + text.text = when (data.message) { + is CommonErrorUI.Message.Text -> data.message.text + is CommonErrorUI.Message.Resource -> text.context.getString(data.message.textId) + } + } + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt new file mode 100644 index 0000000..bbe63a6 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt @@ -0,0 +1,30 @@ +package com.originsdigital.compositeadapter.ui.cell + +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.ui.R +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.databinding.CommonFullErrorCellBinding +import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI +import com.originsdigital.compositeadapter.utils.getViewBinding + +data class CommonFullErrorCell( + override val data: CommonErrorUI, + override val decoration: ItemDecoration>?, + override val onClickListener: ((ClickItem) -> Unit)? +) : ViewBindingCell { + + override val uniqueId = data.id + override val layoutId = R.layout.common_full_error_cell + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder.getViewBinding(CommonFullErrorCellBinding::bind)).apply { + text.text = when (data.message) { + is CommonErrorUI.Message.Text -> data.message.text + is CommonErrorUI.Message.Resource -> text.context.getString(data.message.textId) + } + } + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt new file mode 100644 index 0000000..af6f11c --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt @@ -0,0 +1,15 @@ +package com.originsdigital.compositeadapter.ui.cell + +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.ui.R +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell + +data class CommonFullLoaderCell( + override val data: Any = Unit +) : ViewBindingCell { + + override val uniqueId = "CommonFullLoaderCell" + override val layoutId = R.layout.common_full_loader_cell + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) = Unit +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt new file mode 100644 index 0000000..4f0c14c --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt @@ -0,0 +1,35 @@ +package com.originsdigital.compositeadapter.ui.cell + +import android.view.ViewGroup +import androidx.core.view.updateLayoutParams +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.ui.R +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.databinding.CommonLoaderCellBinding +import com.originsdigital.compositeadapter.ui.entity.CommonLoaderUI +import com.originsdigital.compositeadapter.utils.context +import com.originsdigital.compositeadapter.utils.getViewBinding + +data class CommonLoaderCell( + override val data: CommonLoaderUI, + override val decoration: ItemDecoration>? +) : ViewBindingCell { + + override val uniqueId = data.id + override val layoutId = R.layout.common_loader_cell + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder.getViewBinding(CommonLoaderCellBinding::bind)).apply { + val newHeight = if (data.heightId == null) { + ViewGroup.LayoutParams.WRAP_CONTENT + } else { + holder.context.resources.getDimension(data.heightId).toInt() + } + if (root.layoutParams.height != newHeight) { + root.updateLayoutParams { height = newHeight } + } + } + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/databinding/DataBindingCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/databinding/DataBindingCell.kt new file mode 100644 index 0000000..265ec2e --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/databinding/DataBindingCell.kt @@ -0,0 +1,49 @@ +package com.originsdigital.compositeadapter.ui.cell.databinding + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.ui.BR + +interface DataBindingCell : Cell { + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return DataBindingViewHolder.create(inflater, layoutId, parent) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as DataBindingViewHolder).apply { + bindings.setVariable(BR.item, data) + bindings.executePendingBindings() + } + } + + class DataBindingViewHolder( + val bindings: ViewDataBinding + ) : RecyclerView.ViewHolder(bindings.root) { + + companion object { + fun create( + inflater: LayoutInflater, + layoutResId: Int, + parent: ViewGroup + ): DataBindingViewHolder { + return DataBindingViewHolder( + DataBindingUtil.inflate( + inflater, + layoutResId, + parent, + false + ) + ) + } + } + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/CellViewHolder.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/CellViewHolder.kt new file mode 100644 index 0000000..5322714 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/CellViewHolder.kt @@ -0,0 +1,6 @@ +package com.originsdigital.compositeadapter.ui.cell.viewbinding + +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +open class CellViewHolder(view: View) : RecyclerView.ViewHolder(view) \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/viewbinding/SampleViewBindingCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt similarity index 50% rename from sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/viewbinding/SampleViewBindingCell.kt rename to sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt index 74cdd19..a3b10f3 100644 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/viewbinding/SampleViewBindingCell.kt +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt @@ -1,14 +1,13 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.viewbinding +package com.originsdigital.compositeadapter.ui.cell.viewbinding import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.sample.ui.sample.cell.common.SampleCellViewHolder -interface SampleViewBindingCell : Cell { +interface ViewBindingCell : Cell { override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return SampleCellViewHolder(inflater.inflate(layoutId, parent, false)) + return CellViewHolder(inflater.inflate(layoutId, parent, false)) } } \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt new file mode 100644 index 0000000..655addc --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt @@ -0,0 +1,20 @@ +package com.originsdigital.compositeadapter.ui.di + +import com.originsdigital.compositeadapter.ui.mapper.ErrorUIMapper +import com.originsdigital.compositeadapter.ui.mapper.LoaderUIMapper +import com.originsdigital.compositeadapter.ui.mapper.SceneMapper +import org.koin.android.ext.koin.androidApplication +import org.koin.dsl.module + +val uiModule + get() = module { + factory { ErrorUIMapper() } + factory { LoaderUIMapper() } + factory { + SceneMapper( + app = androidApplication(), + loaderUIMapper = get(), + errorUIMapper = get() + ) + } + } \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt new file mode 100644 index 0000000..5914fae --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt @@ -0,0 +1,17 @@ +package com.originsdigital.compositeadapter.ui.entity + +import androidx.annotation.DimenRes +import androidx.annotation.StringRes + +data class CommonErrorUI( + val id: String, + val message: Message, + @DimenRes val heightId: Int? +) { + + sealed class Message { + + data class Text(val text: String?) : Message() + data class Resource(@StringRes val textId: Int) : Message() + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt new file mode 100644 index 0000000..5baf1b7 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt @@ -0,0 +1,8 @@ +package com.originsdigital.compositeadapter.ui.entity + +import androidx.annotation.DimenRes + +data class CommonLoaderUI( + val id: String, + @DimenRes val heightId: Int? +) \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt new file mode 100644 index 0000000..a9e7f0d --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt @@ -0,0 +1,13 @@ +package com.originsdigital.compositeadapter.ui.extensions + +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.ui.cell.* + +val Cell<*>.isStateCell: Boolean + get() { + return this is CommonLoaderCell + || this is CommonFullLoaderCell + || this is CommonErrorCell + || this is CommonFullErrorCell + || this is CommonFullEmptyCell + } \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt new file mode 100644 index 0000000..4b8cc72 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt @@ -0,0 +1,34 @@ +package com.originsdigital.compositeadapter.ui.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.viewbinding.ViewBinding +import org.koin.core.component.KoinComponent + +abstract class BaseFragment : Fragment(), KoinComponent { + + protected open var viewBinding: VIEW_BINDING? = null + protected fun requireViewBinding(): VIEW_BINDING = requireNotNull(viewBinding) + protected abstract fun createViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): VIEW_BINDING + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return createViewBinding(inflater, container).also { + viewBinding = it + }.root + } + + override fun onDestroyView() { + super.onDestroyView() + viewBinding = null + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt new file mode 100644 index 0000000..d14cfb8 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt @@ -0,0 +1,15 @@ +package com.originsdigital.compositeadapter.ui.fragment + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.originsdigital.compositeadapter.ui.databinding.CommonRecyclerFragmentBinding + +abstract class SimpleRecyclerFragment : BaseFragment() { + + final override fun createViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): CommonRecyclerFragmentBinding { + return CommonRecyclerFragmentBinding.inflate(inflater, container, false) + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt new file mode 100644 index 0000000..2974f4a --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt @@ -0,0 +1,39 @@ +package com.originsdigital.compositeadapter.ui.mapper + +import androidx.annotation.DimenRes +import com.originsdigital.compositeadapter.core.extensions.isInternetConnectionException +import com.originsdigital.compositeadapter.ui.R +import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI + +class ErrorUIMapper { + + fun mapError( + id: String, + text: String, + @DimenRes heightId: Int? = null + ): CommonErrorUI { + return CommonErrorUI( + id = id, + message = CommonErrorUI.Message.Text(text = text), + heightId = heightId + ) + } + + fun mapError( + id: String, + throwable: Throwable, + @DimenRes heightId: Int? = null + ): CommonErrorUI { + return CommonErrorUI( + id = id, + message = CommonErrorUI.Message.Resource( + textId = if (throwable.isInternetConnectionException) { + R.string.common_no_connection_error + } else { + R.string.common_default_error + } + ), + heightId = heightId + ) + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt new file mode 100644 index 0000000..baa07f2 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt @@ -0,0 +1,14 @@ +package com.originsdigital.compositeadapter.ui.mapper + +import androidx.annotation.DimenRes +import com.originsdigital.compositeadapter.ui.entity.CommonLoaderUI + +class LoaderUIMapper { + + fun mapLoader(id: String, @DimenRes heightId: Int?): CommonLoaderUI { + return CommonLoaderUI( + id = id, + heightId = heightId + ) + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt new file mode 100644 index 0000000..55770b1 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt @@ -0,0 +1,276 @@ +package com.originsdigital.compositeadapter.ui.mapper + +import android.app.Application +import android.util.TypedValue +import androidx.annotation.DimenRes +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.core.entity.Scene +import com.originsdigital.compositeadapter.core.log +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration +import com.originsdigital.compositeadapter.ui.cell.CommonErrorCell +import com.originsdigital.compositeadapter.ui.cell.CommonFullErrorCell +import com.originsdigital.compositeadapter.ui.cell.CommonFullLoaderCell +import com.originsdigital.compositeadapter.ui.cell.CommonLoaderCell +import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI + +class SceneMapper( + app: Application, + val loaderUIMapper: LoaderUIMapper, + val errorUIMapper: ErrorUIMapper +) { + + private val stateItemDecoration: ItemDecoration> + + init { + val defaultSpace = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 40f, app.resources.displayMetrics + ).toInt() + stateItemDecoration = SpaceItemDecoration( + top = defaultSpace, + bottom = defaultSpace + ) + } + + fun isAllLoaders(vararg scenes: Scene<*>): Boolean { + return scenes.isNotEmpty() && scenes.all { scene -> scene is Scene.Loading } + } + + fun isAllErrors(vararg scenes: Scene<*>): Boolean { + return scenes.isNotEmpty() && scenes.all { scene -> scene is Scene.Error } + } + + fun isRefreshing(vararg scenes: Scene<*>): Boolean { + return scenes.isNotEmpty() && scenes.any { scene -> scene.isRefreshing } + } + + fun getErrorScene( + mainScene: Scene, + vararg scenes: Scene<*> + ): Scene.Error? { + return if (mainScene is Scene.Error && isAllErrors(*scenes)) { + mainScene + } else { + null + } + } + + fun mapScene( + scene: Scene, + loadingDelegate: (scene: Scene.Loading) -> CONTENT, + errorDelegate: (scene: Scene.Error) -> CONTENT, + dataDelegate: (Scene.Data) -> CONTENT + ): CONTENT { + log(this, "mapSceneToCells=${Thread.currentThread()}") + return when (scene) { + is Scene.Loading -> loadingDelegate(scene) + is Scene.Error -> errorDelegate(scene) + is Scene.Data -> dataDelegate(scene) + } + } + + fun mapSceneToCell( + scene: Scene, + loadingDelegate: (scene: Scene.Loading) -> Cell<*> = { getFullLoaderCell() }, + errorDelegate: (scene: Scene.Error) -> Cell<*>, + dataDelegate: (Scene.Data) -> Cell<*> + ): Cell<*> { + return mapScene( + scene = scene, + loadingDelegate = loadingDelegate, + errorDelegate = errorDelegate, + dataDelegate = dataDelegate + ) + } + + fun mapSceneToCellOrNull( + scene: Scene, + loadingDelegate: (scene: Scene.Loading) -> Cell<*>? = { getFullLoaderCell() }, + errorDelegate: (scene: Scene.Error) -> Cell<*>?, + dataDelegate: (Scene.Data) -> Cell<*>? + ): Cell<*>? { + return mapScene( + scene = scene, + loadingDelegate = loadingDelegate, + errorDelegate = errorDelegate, + dataDelegate = dataDelegate + ) + } + + fun mapSceneToCells( + scene: Scene, + loadingDelegate: (scene: Scene.Loading) -> List> = { + listOf(getFullLoaderCell()) + }, + errorDelegate: (scene: Scene.Error) -> List>, + dataDelegate: (Scene.Data) -> List> + ): List> { + return mapScene( + scene = scene, + loadingDelegate = loadingDelegate, + errorDelegate = errorDelegate, + dataDelegate = dataDelegate + ) + } + + fun mapSceneToCells( + scene: Scene, + onRetryClicked: (ClickItem) -> Unit, + dataDelegate: (Scene.Data) -> List> + ): List> { + return mapScene( + scene = scene, + loadingDelegate = { listOf(getFullLoaderCell()) }, + errorDelegate = { errorScene -> + listOf( + getFullErrorCell( + error = errorUIMapper.mapError( + id = "FullErrorCell", + throwable = errorScene.throwable + ), + decoration = null, + onRetryClicked = onRetryClicked + ) + ) + }, + dataDelegate = dataDelegate + ) + } + + fun mapSmallSceneToCell( + scene: Scene, + uniqueId: String, + onRetryClicked: ((ClickItem) -> Unit), + dataDelegate: (Scene.Data) -> Cell<*> + ): Cell<*> { + return mapSceneToCell( + scene = scene, + loadingDelegate = { getSmallLoaderCell(uniqueLoaderId = "loader=$uniqueId") }, + errorDelegate = { errorScene -> + getSmallErrorCell( + error = errorUIMapper.mapError( + id = "error=$uniqueId", + throwable = errorScene.throwable + ), + decoration = stateItemDecoration, + onRetryClicked = onRetryClicked + ) + }, + dataDelegate = dataDelegate + ) + } + + fun mapSmallSceneToCellOrNull( + scene: Scene, + uniqueId: String, + onRetryClicked: ((ClickItem) -> Unit), + dataDelegate: (Scene.Data) -> Cell<*>? + ): Cell<*>? { + return mapSceneToCellOrNull( + scene = scene, + loadingDelegate = { getSmallLoaderCell(uniqueLoaderId = uniqueId) }, + errorDelegate = { errorScene -> + getSmallErrorCell( + error = errorUIMapper.mapError( + id = "error=$uniqueId", + throwable = errorScene.throwable, + heightId = null + ), + decoration = stateItemDecoration, + onRetryClicked = onRetryClicked + ) + }, + dataDelegate = dataDelegate + ) + } + + fun mapSmallSceneToCells( + scene: Scene, + uniqueId: String, + onRetryClicked: ((ClickItem) -> Unit), + dataDelegate: (Scene.Data) -> List> + ): List> { + return mapSceneToCells( + scene = scene, + loadingDelegate = { listOf(getSmallLoaderCell(uniqueLoaderId = uniqueId)) }, + errorDelegate = { errorScene -> + listOf( + getSmallErrorCell( + error = errorUIMapper.mapError( + id = "error=$uniqueId", + throwable = errorScene.throwable, + heightId = null + ), + decoration = stateItemDecoration, + onRetryClicked = onRetryClicked + ) + ) + }, + dataDelegate = dataDelegate + ) + } + + fun getFullLoaderCell(): Cell<*> { + return CommonFullLoaderCell() + } + + fun getSmallLoaderCell( + uniqueLoaderId: String, + decoration: ItemDecoration>? = stateItemDecoration, + @DimenRes height: Int? = null + ): Cell<*> { + return CommonLoaderCell( + data = loaderUIMapper.mapLoader( + id = uniqueLoaderId, + heightId = height + ), + decoration = decoration + ) + } + + fun getErrorCell( + isFull: Boolean, + error: CommonErrorUI, + decoration: ItemDecoration>? = stateItemDecoration, + onRetryClicked: (ClickItem) -> Unit + ): Cell<*> { + return if (isFull) { + getFullErrorCell( + error = error, + decoration = null, + onRetryClicked = onRetryClicked + ) + } else { + getSmallErrorCell( + error = error, + decoration = decoration, + onRetryClicked = onRetryClicked + ) + } + } + + fun getFullErrorCell( + error: CommonErrorUI, + decoration: ItemDecoration>? = null, + onRetryClicked: (ClickItem) -> Unit + ): Cell<*> { + return CommonFullErrorCell( + data = error, + decoration = decoration, + onClickListener = onRetryClicked + ) + } + + fun getSmallErrorCell( + error: CommonErrorUI, + decoration: ItemDecoration>? = stateItemDecoration, + onRetryClicked: (ClickItem) -> Unit + ): Cell<*> { + return CommonErrorCell( + data = error, + decoration = decoration, + onClickListener = onRetryClicked + ) + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt new file mode 100644 index 0000000..08d7486 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt @@ -0,0 +1,32 @@ +package com.originsdigital.compositeadapter.ui.utils + +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import com.originsdigital.compositeadapter.adapter.CompositeAdapter +import com.originsdigital.compositeadapter.cell.Cell + +fun submitAdapterData( + adapter: CompositeAdapter, + cells: List>, + commitCallback: Runnable? = null +) { + if (commitCallback == null) { + adapter.submitList(cells) + } else { + adapter.submitList(cells, commitCallback) + } +} + +fun submitAdapterData( + adapter: CompositeAdapter, + swipeRefreshLayout: SwipeRefreshLayout, + isRefreshing: Boolean, + cells: List>, + commitCallback: Runnable? = null +) { + swipeRefreshLayout.isRefreshing = isRefreshing + if (commitCallback == null) { + adapter.submitList(cells) + } else { + adapter.submitList(cells, commitCallback) + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt new file mode 100644 index 0000000..e3c0f49 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt @@ -0,0 +1,22 @@ +package com.originsdigital.compositeadapter.ui.utils + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch + +fun observeFlow( + flow: Flow, + lifecycleOwner: LifecycleOwner, + lifecycleState: Lifecycle.State = Lifecycle.State.RESUMED, + delegate: suspend (T) -> Unit +) { + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.lifecycle.repeatOnLifecycle(lifecycleState) { + flow.collect { item -> delegate(item) } + } + } +} diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt new file mode 100644 index 0000000..99cfe67 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt @@ -0,0 +1,87 @@ +package com.originsdigital.compositeadapter.ui.utils + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import com.originsdigital.compositeadapter.adapter.CompositeAdapter +import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration +import com.originsdigital.compositeadapter.ui.vm.BaseStateViewModel + +fun initRecyclerView( + recyclerView: RecyclerView, + adapter: CompositeAdapter = CompositeAdapter(), + layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(recyclerView.context) +) { + recyclerView.adapter = adapter + recyclerView.layoutManager = layoutManager + recyclerView.addItemDecoration(CompositeItemDecoration()) +} + +fun initRecyclerView( + recyclerView: RecyclerView, + adapter: CompositeAdapter = CompositeAdapter(), + layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(recyclerView.context), + swipeRefreshLayout: SwipeRefreshLayout, + onRefresh: () -> Unit +) { + initRecyclerView( + recyclerView = recyclerView, + adapter = adapter, + layoutManager = layoutManager + ) + initSwipeRefresh( + swipeRefreshLayout = swipeRefreshLayout, + onRefresh = onRefresh + ) +} + +fun > initRecyclerView( + recyclerView: RecyclerView, + adapter: CompositeAdapter = CompositeAdapter(), + layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(recyclerView.context), + swipeRefreshLayout: SwipeRefreshLayout, + viewModel: VIEW_MODEL, + lifecycleOwner: LifecycleOwner, + lifecycleState: Lifecycle.State = Lifecycle.State.RESUMED, + stateDelegate: (CompositeAdapter, state: STATE) -> Unit +) { + initRecyclerView( + recyclerView = recyclerView, + adapter = adapter, + layoutManager = layoutManager, + swipeRefreshLayout = swipeRefreshLayout, + onRefresh = viewModel::onRefresh + ) + + observeCellViewModel( + viewModel = viewModel, + lifecycleOwner = lifecycleOwner, + lifecycleState = lifecycleState, + stateDelegate = { state -> stateDelegate(adapter, state) } + ) +} + +fun > initRecyclerView( + recyclerView: RecyclerView, + adapter: CompositeAdapter = CompositeAdapter(), + layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(recyclerView.context), + viewModel: VIEW_MODEL, + lifecycleOwner: LifecycleOwner, + lifecycleState: Lifecycle.State = Lifecycle.State.RESUMED, + stateDelegate: (CompositeAdapter, state: STATE) -> Unit +) { + initRecyclerView( + recyclerView = recyclerView, + adapter = adapter, + layoutManager = layoutManager + ) + + observeCellViewModel( + viewModel = viewModel, + lifecycleOwner = lifecycleOwner, + lifecycleState = lifecycleState, + stateDelegate = { state -> stateDelegate(adapter, state) } + ) +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt new file mode 100644 index 0000000..f995211 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt @@ -0,0 +1,29 @@ +package com.originsdigital.compositeadapter.ui.utils + +import android.graphics.Color +import androidx.core.content.res.use +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import com.originsdigital.compositeadapter.ui.R + +fun initSwipeRefresh( + swipeRefreshLayout: SwipeRefreshLayout, + onRefresh: () -> Unit +) { + val schemeColor = swipeRefreshLayout.context.obtainStyledAttributes( + intArrayOf(R.attr.colorSecondary) + ).use { + it.getColor(0, Color.BLACK) + } + swipeRefreshLayout.setColorSchemeColors(schemeColor, schemeColor, schemeColor) + swipeRefreshLayout.setOnRefreshListener { onRefresh() } +} + +fun setSwipeEnabled( + swipeRefreshLayout: SwipeRefreshLayout, + isEnabled: Boolean +) { + if (!isEnabled) { + swipeRefreshLayout.isRefreshing = false + } + swipeRefreshLayout.isEnabled = isEnabled +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt new file mode 100644 index 0000000..d0e33f3 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt @@ -0,0 +1,34 @@ +package com.originsdigital.compositeadapter.ui.utils + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import com.originsdigital.compositeadapter.ui.vm.BaseStateViewModel +import com.originsdigital.compositeadapter.ui.vm.BaseViewModel + +fun observeLifecycle( + viewModel: BaseViewModel, + lifecycleOwner: LifecycleOwner +) { + lifecycleOwner.lifecycle.addObserver(viewModel) +} + +fun observeCellViewModel( + viewModel: VIEW_MODEL, + lifecycleOwner: LifecycleOwner, + lifecycleState: Lifecycle.State = Lifecycle.State.RESUMED, + withLifecycle: Boolean = true, + stateDelegate: (STATE) -> Unit +) where VIEW_MODEL : BaseStateViewModel { + if (withLifecycle) { + observeLifecycle( + viewModel = viewModel, + lifecycleOwner = lifecycleOwner + ) + } + observeFlow( + flow = viewModel.stateFlow, + lifecycleOwner = lifecycleOwner, + lifecycleState = lifecycleState, + delegate = stateDelegate + ) +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt new file mode 100644 index 0000000..8d676ff --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt @@ -0,0 +1,17 @@ +package com.originsdigital.compositeadapter.ui.vm + +import kotlinx.coroutines.Job + +abstract class BaseDataViewModel : BaseViewModel() { + + open fun onRefresh() = loadData(force = false) + + protected abstract fun getData(force: Boolean): Job? + + protected open fun loadData(force: Boolean) { + job?.cancel() + job = getData(force) + } + + protected fun firstLoad() = loadData(force = true) +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt new file mode 100644 index 0000000..c69ad47 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt @@ -0,0 +1,102 @@ +package com.originsdigital.compositeadapter.ui.vm + +import androidx.lifecycle.viewModelScope +import com.originsdigital.compositeadapter.core.entity.Scene +import com.originsdigital.compositeadapter.core.extensions.toData +import com.originsdigital.compositeadapter.core.extensions.toError +import com.originsdigital.compositeadapter.core.extensions.toLoading +import com.originsdigital.compositeadapter.core.log +import com.originsdigital.compositeadapter.core.utils.safeLoad +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +abstract class BaseStateViewModel : BaseDataViewModel() { + + abstract val stateFlow: Flow + + protected fun loadDataWithState( + text: String, + force: Boolean, + currentSceneDelegate: () -> Scene, + newSceneDelegate: (Scene) -> Unit, + block: (suspend CoroutineScope.() -> DATA) + ): Job { + return viewModelScope.launch { + withContext(bgDispatcher) { + safeLoad( + scope = this, + text = text, + force = force, + currentSceneDelegate = currentSceneDelegate, + newSceneDelegate = newSceneDelegate, + block = block + ) + } + } + } + + protected fun loadDataWithState( + text: String, + force: Boolean, + sceneFlow: MutableStateFlow>, + block: (suspend CoroutineScope.() -> DATA) + ): Job { + return loadDataWithState( + text = text, + force = force, + currentSceneDelegate = { sceneFlow.value }, + newSceneDelegate = { newScene -> sceneFlow.value = newScene }, + block = block + ) + } + + protected fun loadFlowWithState( + text: String, + force: Boolean, + currentSceneDelegate: () -> Scene, + newSceneDelegate: (Scene) -> Unit, + flow: Flow + ): Job { + val tag = this@BaseStateViewModel + return viewModelScope.launch { + flow + .catch { error -> + log(tag, "$text error = $error") + newSceneDelegate(currentSceneDelegate().toError(error = error, force = force)) + } + .onStart { + log(tag, "$text onStart") + newSceneDelegate(currentSceneDelegate().toLoading(force = force)) + } + .collect { data -> + log(tag, "$text collect = $data") + newSceneDelegate(currentSceneDelegate().toData(data = data)) + } + } + } + + private suspend fun safeLoad( + scope: CoroutineScope, + text: String, + force: Boolean, + currentSceneDelegate: () -> Scene, + newSceneDelegate: (Scene) -> Unit, + block: (suspend CoroutineScope.() -> DATA) + ) { + safeLoad>( + tag = this@BaseStateViewModel, + text = text, + block = { + newSceneDelegate(currentSceneDelegate().toLoading(force = force)) + currentSceneDelegate() + .toData(data = block(scope)) + .also { newScene -> newSceneDelegate(newScene) } + }, + error = { error -> + currentSceneDelegate() + .toError(error = error, force = force) + .also { newScene -> newSceneDelegate(newScene) } + } + ) + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt new file mode 100644 index 0000000..0c88c14 --- /dev/null +++ b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt @@ -0,0 +1,26 @@ +package com.originsdigital.compositeadapter.ui.vm + +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.ViewModel +import com.originsdigital.compositeadapter.core.di.BG_DISPATCHER_QUALIFIER +import com.originsdigital.compositeadapter.core.di.UI_DISPATCHER_QUALIFIER +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Job +import org.koin.core.context.GlobalContext + +abstract class BaseViewModel : ViewModel(), DefaultLifecycleObserver { + + protected val bgDispatcher: CoroutineDispatcher by lazy { + GlobalContext.get().get(BG_DISPATCHER_QUALIFIER) + } + protected val uiDispatcher: CoroutineDispatcher by lazy { + GlobalContext.get().get(UI_DISPATCHER_QUALIFIER) + } + + protected var job: Job? = null + + override fun onCleared() { + job?.cancel() + super.onCleared() + } +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/layout/common_error_cell.xml b/sample/features/base/ui/src/main/res/layout/common_error_cell.xml new file mode 100644 index 0000000..58e1936 --- /dev/null +++ b/sample/features/base/ui/src/main/res/layout/common_error_cell.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/layout/common_full_empty_cell.xml b/sample/features/base/ui/src/main/res/layout/common_full_empty_cell.xml new file mode 100644 index 0000000..ddb05e3 --- /dev/null +++ b/sample/features/base/ui/src/main/res/layout/common_full_empty_cell.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/layout/common_full_error_cell.xml b/sample/features/base/ui/src/main/res/layout/common_full_error_cell.xml new file mode 100644 index 0000000..ddb05e3 --- /dev/null +++ b/sample/features/base/ui/src/main/res/layout/common_full_error_cell.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/layout/common_full_loader_cell.xml b/sample/features/base/ui/src/main/res/layout/common_full_loader_cell.xml new file mode 100644 index 0000000..c9ec6aa --- /dev/null +++ b/sample/features/base/ui/src/main/res/layout/common_full_loader_cell.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/layout/common_loader_cell.xml b/sample/features/base/ui/src/main/res/layout/common_loader_cell.xml new file mode 100644 index 0000000..58e8173 --- /dev/null +++ b/sample/features/base/ui/src/main/res/layout/common_loader_cell.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/layout/common_recycler_fragment.xml b/sample/features/base/ui/src/main/res/layout/common_recycler_fragment.xml new file mode 100644 index 0000000..ac92298 --- /dev/null +++ b/sample/features/base/ui/src/main/res/layout/common_recycler_fragment.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/layout/fake_databinding_layout.xml b/sample/features/base/ui/src/main/res/layout/fake_databinding_layout.xml new file mode 100644 index 0000000..99225c5 --- /dev/null +++ b/sample/features/base/ui/src/main/res/layout/fake_databinding_layout.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/values/colors.xml b/sample/features/base/ui/src/main/res/values/colors.xml new file mode 100644 index 0000000..982b026 --- /dev/null +++ b/sample/features/base/ui/src/main/res/values/colors.xml @@ -0,0 +1,22 @@ + + + @color/dusty_orange + @color/dusty_orange + @color/compositeadapter_white + @color/dusty_orange + @color/dusty_orange + @color/compositeadapter_white + @color/pinkish_red + @color/compositeadapter_white + @color/compositeadapter_white + @color/compositeadapter_black + @color/compositeadapter_white + @color/compositeadapter_black + + #FFFFFF + #000000 + #00000000 + + #F47A31 + #EB2227 + diff --git a/sample/features/base/ui/src/main/res/values/strings.xml b/sample/features/base/ui/src/main/res/values/strings.xml new file mode 100644 index 0000000..445a77e --- /dev/null +++ b/sample/features/base/ui/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + + No connection + Error + \ No newline at end of file diff --git a/sample/features/messages/core/.gitignore b/sample/features/messages/core/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/sample/features/messages/core/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/features/messages/core/build.gradle.kts b/sample/features/messages/core/build.gradle.kts new file mode 100644 index 0000000..3765cf9 --- /dev/null +++ b/sample/features/messages/core/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + kotlin("jvm") + `java-library` +} + +java.sourceCompatibility = Config.Build.javaVersion +java.targetCompatibility = Config.Build.javaVersion + +project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java).configureEach { + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseCore)) +} \ No newline at end of file diff --git a/sample/features/messages/core/consumer-rules.pro b/sample/features/messages/core/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/sample/features/messages/core/proguard-rules.pro b/sample/features/messages/core/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/sample/features/messages/core/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/domain/entity/MessageEntity.kt b/sample/features/messages/core/src/main/java/com/originsdigital/compositeadapter/messages/core/entity/MessageEntity.kt similarity index 71% rename from sample/src/main/java/com/originsdigital/compositeadapter/sample/domain/entity/MessageEntity.kt rename to sample/features/messages/core/src/main/java/com/originsdigital/compositeadapter/messages/core/entity/MessageEntity.kt index ed0d840..b8358e5 100644 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/domain/entity/MessageEntity.kt +++ b/sample/features/messages/core/src/main/java/com/originsdigital/compositeadapter/messages/core/entity/MessageEntity.kt @@ -1,4 +1,4 @@ -package com.originsdigital.compositeadapter.sample.domain.entity +package com.originsdigital.compositeadapter.messages.core.entity data class MessageEntity( val id: String, diff --git a/sample/features/messages/data/.gitignore b/sample/features/messages/data/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/sample/features/messages/data/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/features/messages/data/build.gradle.kts b/sample/features/messages/data/build.gradle.kts new file mode 100644 index 0000000..3f1d9de --- /dev/null +++ b/sample/features/messages/data/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.minSdk + targetSdk = Config.Build.targetSdk + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseCore)) + implementation(project(Config.Deps.Libs.messagesCore)) +} \ No newline at end of file diff --git a/sample/features/messages/data/consumer-rules.pro b/sample/features/messages/data/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/sample/features/messages/data/proguard-rules.pro b/sample/features/messages/data/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/sample/features/messages/data/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/sample/features/messages/data/src/main/AndroidManifest.xml b/sample/features/messages/data/src/main/AndroidManifest.xml new file mode 100644 index 0000000..47113d1 --- /dev/null +++ b/sample/features/messages/data/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/sample/features/messages/ui/.gitignore b/sample/features/messages/ui/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/sample/features/messages/ui/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/features/messages/ui/build.gradle.kts b/sample/features/messages/ui/build.gradle.kts new file mode 100644 index 0000000..a5ebc58 --- /dev/null +++ b/sample/features/messages/ui/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) + kotlin(Config.Plugins.kapt) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.minSdk + targetSdk = Config.Build.targetSdk + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } + + buildFeatures { + dataBinding = true + viewBinding = true + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseUI)) + implementation(project(Config.Deps.Libs.messagesCore)) +} \ No newline at end of file diff --git a/sample/features/messages/ui/consumer-rules.pro b/sample/features/messages/ui/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/sample/features/messages/ui/proguard-rules.pro b/sample/features/messages/ui/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/sample/features/messages/ui/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/AndroidManifest.xml b/sample/features/messages/ui/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d3df6ce --- /dev/null +++ b/sample/features/messages/ui/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleDI.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleDI.kt new file mode 100644 index 0000000..37eb247 --- /dev/null +++ b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleDI.kt @@ -0,0 +1,17 @@ +package com.originsdigital.compositeadapter.messages.ui + +import android.app.Application + +class SampleDI { + + companion object { + + private lateinit var app: Application + + fun init(app: Application) { + Companion.app = app + } + + fun provideApp(): Application = app + } +} \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SampleMapper.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleMapper.kt similarity index 64% rename from sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SampleMapper.kt rename to sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleMapper.kt index bb32e81..bc3af98 100644 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SampleMapper.kt +++ b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleMapper.kt @@ -1,4 +1,4 @@ -package com.originsdigital.compositeadapter.sample.ui.sample +package com.originsdigital.compositeadapter.messages.ui import android.app.Application import android.graphics.Canvas @@ -11,14 +11,15 @@ import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration -import com.originsdigital.compositeadapter.sample.domain.entity.MessageEntity -import com.originsdigital.compositeadapter.sample.ui.sample.cell.common.SampleErrorCell -import com.originsdigital.compositeadapter.sample.ui.sample.cell.common.SampleFullScreenErrorCell -import com.originsdigital.compositeadapter.sample.ui.sample.cell.common.SampleFullScreenLoaderCell -import com.originsdigital.compositeadapter.sample.ui.sample.cell.common.SampleLoaderCell -import com.originsdigital.compositeadapter.sample.ui.sample.cell.databinding.SampleDataBindingMessageCell -import com.originsdigital.compositeadapter.sample.ui.sample.cell.view.SampleViewMessageCell -import com.originsdigital.compositeadapter.sample.ui.sample.cell.viewbinding.SampleViewBindingMessageCell +import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity +import com.originsdigital.compositeadapter.messages.ui.cell.databinding.SampleDataBindingMessageCell +import com.originsdigital.compositeadapter.messages.ui.cell.view.SampleViewMessageCell +import com.originsdigital.compositeadapter.messages.ui.cell.viewbinding.SampleViewBindingMessageCell +import com.originsdigital.compositeadapter.ui.cell.CommonFullErrorCell +import com.originsdigital.compositeadapter.ui.cell.CommonFullLoaderCell +import com.originsdigital.compositeadapter.ui.cell.CommonLoaderCell +import com.originsdigital.compositeadapter.ui.entity.CommonLoaderUI +import com.originsdigital.compositeadapter.ui.mapper.ErrorUIMapper import java.util.* class SampleMapper( @@ -35,8 +36,13 @@ class SampleMapper( private val lastItemDecoration: SpaceItemDecoration> init { - val space = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, app.resources.displayMetrics).toInt() - firstItemDecoration = SpaceItemDecoration(top = 8 * space, bottom = space, start = 8 * space, end = 8 * space) + val space = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 10f, + app.resources.displayMetrics + ).toInt() + firstItemDecoration = + SpaceItemDecoration(top = 8 * space, bottom = space, start = 8 * space, end = 8 * space) middleSmallItemDecoration = object : SpaceItemDecoration>( top = 2 * space, bottom = 2 * space, start = 4 * space, end = 4 * space ) { @@ -47,7 +53,13 @@ class SampleMapper( this.isAntiAlias = true } - override fun onDraw(canvas: Canvas, view: View, parent: RecyclerView, state: RecyclerView.State, item: Cell<*>) { + override fun onDraw( + canvas: Canvas, + view: View, + parent: RecyclerView, + state: RecyclerView.State, + item: Cell<*> + ) { super.onDraw(canvas, view, parent, state, item) parent.layoutManager?.getDecoratedBoundsWithMargins(view, itemBounds) canvas.drawRect( @@ -59,8 +71,10 @@ class SampleMapper( ) } } - middleBigItemDecoration = SpaceItemDecoration(top = space, bottom = space, start = space, end = space) - lastItemDecoration = SpaceItemDecoration(top = space, bottom = 8 * space, start = 8 * space, end = 8 * space) + middleBigItemDecoration = + SpaceItemDecoration(top = space, bottom = space, start = space, end = space) + lastItemDecoration = + SpaceItemDecoration(top = space, bottom = 8 * space, start = 8 * space, end = 8 * space) } fun mapToCells(messages: List, extraCell: Cell<*>? = null): List> { @@ -103,7 +117,10 @@ class SampleMapper( return result } - fun changeMessageType(messages: List, input: MessageEntity): List { + fun changeMessageType( + messages: List, + input: MessageEntity + ): List { return messages.map { message -> if (input.id == message.id) { message.copy(type = MessageEntity.Type.values().toList().minus(input.type).random()) @@ -129,17 +146,33 @@ class SampleMapper( fun getLoaderCell(fullscreen: Boolean): Cell<*> { return if (fullscreen) { - SampleFullScreenLoaderCell() + CommonFullLoaderCell() } else { - SampleLoaderCell() + CommonLoaderCell( + data = CommonLoaderUI("", heightId = null), + decoration = null + ) } } - fun getErrorCell(error: String, fullscreen: Boolean, onRetryClickListener: () -> Unit): Cell<*> { + fun getErrorCell( + error: String, + fullscreen: Boolean, + onRetryClickListener: () -> Unit + ): Cell<*> { + val mapper = ErrorUIMapper() return if (fullscreen) { - SampleFullScreenErrorCell(data = error, onClickListener = { onRetryClickListener() }) + CommonFullErrorCell( + data = mapper.mapError(id = "messages", text = error), + decoration = null, + onClickListener = { onRetryClickListener() } + ) } else { - SampleErrorCell(data = error, onClickListener = { onRetryClickListener() }) + CommonFullErrorCell( + data = mapper.mapError(id = "messages", text = error), + decoration = null, + onClickListener = { onRetryClickListener() } + ) } } } \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/vm/SampleState.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleState.kt similarity index 82% rename from sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/vm/SampleState.kt rename to sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleState.kt index 9e628cf..8108011 100644 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/vm/SampleState.kt +++ b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleState.kt @@ -1,8 +1,8 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.vm +package com.originsdigital.compositeadapter.messages.ui import androidx.lifecycle.MutableLiveData import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.sample.domain.entity.MessageEntity +import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity class SampleState { diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/vm/SampleViewModel.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleViewModel.kt similarity index 92% rename from sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/vm/SampleViewModel.kt rename to sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleViewModel.kt index f252f87..817731d 100644 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/vm/SampleViewModel.kt +++ b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleViewModel.kt @@ -1,13 +1,10 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.vm +package com.originsdigital.compositeadapter.messages.ui -import android.app.Application import android.widget.Toast import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.sample.domain.entity.MessageEntity -import com.originsdigital.compositeadapter.sample.ui.di.SampleDI -import com.originsdigital.compositeadapter.sample.ui.sample.SampleMapper +import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity import kotlinx.coroutines.* import java.util.* @@ -15,7 +12,8 @@ class SampleViewModel : ViewModel() { val state: SampleState = SampleState() - private val app: Application = SampleDI.provideApp() + private val app = SampleDI.provideApp() + private var toast: Toast? = null private val mapper = SampleMapper( app = app, @@ -135,8 +133,6 @@ class SampleViewModel : ViewModel() { override fun onCleared() { super.onCleared() - clickJob?.cancel() - loadJob?.cancel() toast?.cancel() toast = null } diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/databinding/SampleDataBindingMessageCell.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/databinding/SampleDataBindingMessageCell.kt similarity index 54% rename from sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/databinding/SampleDataBindingMessageCell.kt rename to sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/databinding/SampleDataBindingMessageCell.kt index ac07467..1a2b2e1 100644 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/databinding/SampleDataBindingMessageCell.kt +++ b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/databinding/SampleDataBindingMessageCell.kt @@ -1,17 +1,18 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.databinding +package com.originsdigital.compositeadapter.messages.ui.cell.databinding import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration -import com.originsdigital.compositeadapter.sample.R -import com.originsdigital.compositeadapter.sample.domain.entity.MessageEntity +import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity +import com.originsdigital.compositeadapter.messages.ui.R +import com.originsdigital.compositeadapter.ui.cell.databinding.DataBindingCell data class SampleDataBindingMessageCell( override val data: MessageEntity, override val decoration: ItemDecoration>?, override val onClickListener: ((ClickItem) -> Unit) -) : SampleDataBindingCell { +) : DataBindingCell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.sample_databinding_message_list_item + override val layoutId: Int = R.layout.message_databinding_list_item } \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/view/SampleViewMessageCell.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/view/SampleViewMessageCell.kt similarity index 88% rename from sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/view/SampleViewMessageCell.kt rename to sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/view/SampleViewMessageCell.kt index f634c05..bad29a3 100644 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/view/SampleViewMessageCell.kt +++ b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/view/SampleViewMessageCell.kt @@ -1,4 +1,4 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.view +package com.originsdigital.compositeadapter.messages.ui.cell.view import android.util.TypedValue import android.view.LayoutInflater @@ -10,8 +10,8 @@ import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration -import com.originsdigital.compositeadapter.sample.R -import com.originsdigital.compositeadapter.sample.domain.entity.MessageEntity +import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity +import com.originsdigital.compositeadapter.messages.ui.R data class SampleViewMessageCell( override val data: MessageEntity, @@ -20,7 +20,7 @@ data class SampleViewMessageCell( ) : Cell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.sample_view_message_list_item + override val layoutId: Int = R.layout.message_view_list_item override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20f, parent.resources.displayMetrics).toInt() diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/viewbinding/SampleViewBindingMessageCell.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/viewbinding/SampleViewBindingMessageCell.kt similarity index 55% rename from sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/viewbinding/SampleViewBindingMessageCell.kt rename to sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/viewbinding/SampleViewBindingMessageCell.kt index 5712d9e..4148e20 100644 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/viewbinding/SampleViewBindingMessageCell.kt +++ b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/viewbinding/SampleViewBindingMessageCell.kt @@ -1,25 +1,26 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.viewbinding +package com.originsdigital.compositeadapter.messages.ui.cell.viewbinding import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration -import com.originsdigital.compositeadapter.sample.R -import com.originsdigital.compositeadapter.sample.databinding.SampleViewbindingMessageListItemBinding -import com.originsdigital.compositeadapter.sample.domain.entity.MessageEntity +import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity +import com.originsdigital.compositeadapter.messages.ui.R +import com.originsdigital.compositeadapter.messages.ui.databinding.MessageViewbindingListItemBinding +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell import com.originsdigital.compositeadapter.utils.getViewBinding data class SampleViewBindingMessageCell( override val data: MessageEntity, override val decoration: ItemDecoration>?, override val onClickListener: ((ClickItem) -> Unit)? -) : SampleViewBindingCell { +) : ViewBindingCell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.sample_viewbinding_message_list_item + override val layoutId: Int = R.layout.message_viewbinding_list_item override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder.getViewBinding(SampleViewbindingMessageListItemBinding::bind)).apply { + (holder.getViewBinding(MessageViewbindingListItemBinding::bind)).apply { text.text = data.text } } diff --git a/sample/src/main/res/layout/sample_databinding_message_list_item.xml b/sample/features/messages/ui/src/main/res/layout/message_databinding_list_item.xml similarity index 86% rename from sample/src/main/res/layout/sample_databinding_message_list_item.xml rename to sample/features/messages/ui/src/main/res/layout/message_databinding_list_item.xml index 187faff..13ff21a 100644 --- a/sample/src/main/res/layout/sample_databinding_message_list_item.xml +++ b/sample/features/messages/ui/src/main/res/layout/message_databinding_list_item.xml @@ -6,7 +6,7 @@ + type="com.originsdigital.compositeadapter.messages.core.entity.MessageEntity" /> + + + \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/di/SampleDI.kt b/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/di/SampleDI.kt deleted file mode 100644 index 451c4c9..0000000 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/di/SampleDI.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.originsdigital.compositeadapter.sample.ui.di - -import com.originsdigital.compositeadapter.sample.ui.SampleApplication - -class SampleDI { - - companion object { - - private lateinit var app: SampleApplication - - fun init(app: SampleApplication) { - this.app = app - } - - fun provideApp(): SampleApplication = app - } -} \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleCellViewHolder.kt b/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleCellViewHolder.kt deleted file mode 100644 index 33a3143..0000000 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleCellViewHolder.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.common - -import android.view.View -import androidx.recyclerview.widget.RecyclerView - -open class SampleCellViewHolder(view: View) : RecyclerView.ViewHolder(view) \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleErrorCell.kt b/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleErrorCell.kt deleted file mode 100644 index 0def71a..0000000 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleErrorCell.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.common - -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.ClickItem -import com.originsdigital.compositeadapter.sample.R -import com.originsdigital.compositeadapter.sample.databinding.CommonErrorListItemBinding -import com.originsdigital.compositeadapter.sample.ui.sample.cell.viewbinding.SampleViewBindingCell -import com.originsdigital.compositeadapter.utils.getViewBinding - -data class SampleErrorCell( - override val uniqueId: String = "ErrorCell", - override val data: String, - override val onClickListener: ((ClickItem) -> Unit) -) : SampleViewBindingCell { - - override val layoutId: Int = R.layout.common_error_list_item - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder.getViewBinding(CommonErrorListItemBinding::bind)).apply { - message.text = data - } - } -} \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleFullScreenErrorCell.kt b/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleFullScreenErrorCell.kt deleted file mode 100644 index 158939d..0000000 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleFullScreenErrorCell.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.common - -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.ClickItem -import com.originsdigital.compositeadapter.sample.R -import com.originsdigital.compositeadapter.sample.databinding.CommonFullscreenErrorListItemBinding -import com.originsdigital.compositeadapter.sample.ui.sample.cell.viewbinding.SampleViewBindingCell -import com.originsdigital.compositeadapter.utils.getViewBinding - -data class SampleFullScreenErrorCell( - override val uniqueId: String = "FullScreenErrorCell", - override val data: String, - override val onClickListener: ((ClickItem) -> Unit) -) : SampleViewBindingCell { - - override val layoutId: Int = R.layout.common_fullscreen_error_list_item - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder.getViewBinding(CommonFullscreenErrorListItemBinding::bind)).apply { - message.text = data - } - } -} \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleFullScreenLoaderCell.kt b/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleFullScreenLoaderCell.kt deleted file mode 100644 index e449372..0000000 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleFullScreenLoaderCell.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.common - -import android.view.Gravity -import android.view.LayoutInflater -import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.ProgressBar -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.sample.R - -data class SampleFullScreenLoaderCell( - override val uniqueId: String = "FullScreenLoaderCell" -) : Cell { - - override val data: Unit = Unit - override val layoutId: Int = R.layout.common_fullscreen_loader_list_item - - override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return SampleCellViewHolder(FrameLayout(inflater.context).apply { - layoutParams = RecyclerView.LayoutParams( - RecyclerView.LayoutParams.MATCH_PARENT, - RecyclerView.LayoutParams.MATCH_PARENT - ) - addView(ProgressBar(inflater.context, null, android.R.attr.progressBarStyleLarge).apply { - layoutParams = FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - FrameLayout.LayoutParams.WRAP_CONTENT, - Gravity.CENTER - ) - }) - }) - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) = Unit -} \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleLoaderCell.kt b/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleLoaderCell.kt deleted file mode 100644 index beefcca..0000000 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/common/SampleLoaderCell.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.common - -import android.view.Gravity -import android.view.LayoutInflater -import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.ProgressBar -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.sample.R - -data class SampleLoaderCell( - override val uniqueId: String = "LoaderCell" -) : Cell { - - override val data: Unit = Unit - override val layoutId: Int = R.layout.common_loader_list_item - - override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return SampleCellViewHolder(FrameLayout(inflater.context).apply { - layoutParams = RecyclerView.LayoutParams( - RecyclerView.LayoutParams.MATCH_PARENT, - RecyclerView.LayoutParams.WRAP_CONTENT - ) - addView(ProgressBar(inflater.context, null, android.R.attr.progressBarStyleLarge).apply { - layoutParams = FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - FrameLayout.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_HORIZONTAL - ) - }) - }) - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) = Unit -} \ No newline at end of file diff --git a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/databinding/SampleDataBindingCell.kt b/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/databinding/SampleDataBindingCell.kt deleted file mode 100644 index 4414a50..0000000 --- a/sample/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/cell/databinding/SampleDataBindingCell.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.originsdigital.compositeadapter.sample.ui.sample.cell.databinding - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.sample.BR - -interface SampleDataBindingCell : Cell { - - override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return DataBindingViewHolder.create(inflater, layoutId, parent) - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder as DataBindingViewHolder).apply { - bindings.setVariable(BR.item, data) - bindings.executePendingBindings() - } - } - - class DataBindingViewHolder(val bindings: ViewDataBinding) : RecyclerView.ViewHolder(bindings.root) { - - companion object { - fun create(inflater: LayoutInflater, layoutResId: Int, parent: ViewGroup): DataBindingViewHolder { - return DataBindingViewHolder(DataBindingUtil.inflate(inflater, layoutResId, parent, false)) - } - } - } -} \ No newline at end of file diff --git a/sample/src/main/res/drawable/common_ic_big_refresh.xml b/sample/src/main/res/drawable/common_ic_big_refresh.xml deleted file mode 100644 index fe6defb..0000000 --- a/sample/src/main/res/drawable/common_ic_big_refresh.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/sample/src/main/res/drawable/common_ic_refresh.xml b/sample/src/main/res/drawable/common_ic_refresh.xml deleted file mode 100644 index f9eade0..0000000 --- a/sample/src/main/res/drawable/common_ic_refresh.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/sample/src/main/res/layout/common_error_list_item.xml b/sample/src/main/res/layout/common_error_list_item.xml deleted file mode 100644 index 250a16b..0000000 --- a/sample/src/main/res/layout/common_error_list_item.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sample/src/main/res/layout/common_fullscreen_error_list_item.xml b/sample/src/main/res/layout/common_fullscreen_error_list_item.xml deleted file mode 100644 index 5d2fcb6..0000000 --- a/sample/src/main/res/layout/common_fullscreen_error_list_item.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml deleted file mode 100644 index 4faecfa..0000000 --- a/sample/src/main/res/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - #6200EE - #3700B3 - #03DAC5 - \ No newline at end of file diff --git a/sample/src/main/res/values/ids.xml b/sample/src/main/res/values/ids.xml deleted file mode 100644 index 9592d31..0000000 --- a/sample/src/main/res/values/ids.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml deleted file mode 100644 index 7fb1e2d..0000000 --- a/sample/src/main/res/values/styles.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 82725d6..d6951e3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,4 +2,11 @@ rootProject.name = "CompositeAdapter_Android" include(":composite-adapter") -include(":sample") \ No newline at end of file +include(":sample:app") + +include(":sample:features:base:core") +include(":sample:features:base:ui") + +include(":sample:features:messages:data") +include(":sample:features:messages:core") +include(":sample:features:messages:ui") \ No newline at end of file From 7a81cfd506026928cd7bcd23c325bd4d4e141842 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Wed, 22 Dec 2021 18:11:42 +0300 Subject: [PATCH 02/19] feat(sample): add basic sample --- sample/app/src/main/res/values/strings.xml | 3 --- {sample/app => samples/basic}/.gitignore | 0 {sample/app => samples/basic}/build.gradle.kts | 0 {sample/app => samples/basic}/proguard-rules.pro | 0 .../basic}/src/main/AndroidManifest.xml | 6 +++--- .../sample/ui/SamplesApplication.kt | 2 +- .../sample/ui/sample/SamplesActivity.kt | 12 ++++++------ .../res/drawable-v24/ic_launcher_foreground.xml | 0 .../main/res/drawable/ic_launcher_background.xml | 0 .../basic/src/main/res/layout/samples_activity.xml | 0 .../src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../basic}/src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-hdpi/ic_launcher_round.png | Bin .../basic}/src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher_round.png | Bin samples/basic/src/main/res/values/strings.xml | 3 +++ .../basic}/src/main/res/values/themes.xml | 3 +-- settings.gradle.kts | 3 ++- 25 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 sample/app/src/main/res/values/strings.xml rename {sample/app => samples/basic}/.gitignore (100%) rename {sample/app => samples/basic}/build.gradle.kts (100%) rename {sample/app => samples/basic}/proguard-rules.pro (100%) rename {sample/app => samples/basic}/src/main/AndroidManifest.xml (82%) rename sample/app/src/main/java/com/originsdigital/compositeadapter/sample/ui/SampleApplication.kt => samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/ui/SamplesApplication.kt (84%) rename sample/app/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SampleActivity.kt => samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SamplesActivity.kt (80%) rename {sample/app => samples/basic}/src/main/res/drawable-v24/ic_launcher_foreground.xml (100%) rename {sample/app => samples/basic}/src/main/res/drawable/ic_launcher_background.xml (100%) rename sample/app/src/main/res/layout/sample_activity.xml => samples/basic/src/main/res/layout/samples_activity.xml (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {sample/app => samples/basic}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) create mode 100644 samples/basic/src/main/res/values/strings.xml rename {sample/app => samples/basic}/src/main/res/values/themes.xml (91%) diff --git a/sample/app/src/main/res/values/strings.xml b/sample/app/src/main/res/values/strings.xml deleted file mode 100644 index 84c5b88..0000000 --- a/sample/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - CompositeAdapter Sample - \ No newline at end of file diff --git a/sample/app/.gitignore b/samples/basic/.gitignore similarity index 100% rename from sample/app/.gitignore rename to samples/basic/.gitignore diff --git a/sample/app/build.gradle.kts b/samples/basic/build.gradle.kts similarity index 100% rename from sample/app/build.gradle.kts rename to samples/basic/build.gradle.kts diff --git a/sample/app/proguard-rules.pro b/samples/basic/proguard-rules.pro similarity index 100% rename from sample/app/proguard-rules.pro rename to samples/basic/proguard-rules.pro diff --git a/sample/app/src/main/AndroidManifest.xml b/samples/basic/src/main/AndroidManifest.xml similarity index 82% rename from sample/app/src/main/AndroidManifest.xml rename to samples/basic/src/main/AndroidManifest.xml index a9d1e5f..a5ea226 100644 --- a/sample/app/src/main/AndroidManifest.xml +++ b/samples/basic/src/main/AndroidManifest.xml @@ -2,16 +2,16 @@ package="com.originsdigital.compositeadapter.sample"> + android:theme="@style/SamplesTheme"> diff --git a/sample/app/src/main/java/com/originsdigital/compositeadapter/sample/ui/SampleApplication.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/ui/SamplesApplication.kt similarity index 84% rename from sample/app/src/main/java/com/originsdigital/compositeadapter/sample/ui/SampleApplication.kt rename to samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/ui/SamplesApplication.kt index 770ba9e..4b18943 100644 --- a/sample/app/src/main/java/com/originsdigital/compositeadapter/sample/ui/SampleApplication.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/ui/SamplesApplication.kt @@ -3,7 +3,7 @@ package com.originsdigital.compositeadapter.sample.ui import android.app.Application import com.originsdigital.compositeadapter.messages.ui.SampleDI -class SampleApplication : Application() { +class SamplesApplication : Application() { override fun onCreate() { super.onCreate() diff --git a/sample/app/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SampleActivity.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SamplesActivity.kt similarity index 80% rename from sample/app/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SampleActivity.kt rename to samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SamplesActivity.kt index 5a0515a..fadfa25 100644 --- a/sample/app/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SampleActivity.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/ui/sample/SamplesActivity.kt @@ -8,23 +8,23 @@ import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.adapter.CompositeAdapter import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration import com.originsdigital.compositeadapter.messages.ui.SampleViewModel -import com.originsdigital.compositeadapter.sample.databinding.SampleActivityBinding +import com.originsdigital.compositeadapter.sample.databinding.SamplesActivityBinding -class SampleActivity : AppCompatActivity() { +class SamplesActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val viewModel = ViewModelProvider(this)[SampleViewModel::class.java] - val binding = SampleActivityBinding.inflate(layoutInflater) + val binding = SamplesActivityBinding.inflate(layoutInflater) setContentView(binding.root) binding.apply { swipeRefreshLayout.setOnRefreshListener { viewModel.onRefresh() } val adapter = CompositeAdapter() - val layoutManager = LinearLayoutManager(this@SampleActivity) + val layoutManager = LinearLayoutManager(this@SamplesActivity) recyclerView.adapter = adapter recyclerView.layoutManager = layoutManager recyclerView.addItemDecoration(CompositeItemDecoration()) @@ -34,10 +34,10 @@ class SampleActivity : AppCompatActivity() { } }) - viewModel.state.refreshingData.observe(this@SampleActivity) { isRefreshing -> + viewModel.state.refreshingData.observe(this@SamplesActivity) { isRefreshing -> swipeRefreshLayout.isRefreshing = isRefreshing } - viewModel.state.cellsData.observe(this@SampleActivity) { items -> + viewModel.state.cellsData.observe(this@SamplesActivity) { items -> adapter.submitList(items) } } diff --git a/sample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/samples/basic/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from sample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to samples/basic/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/sample/app/src/main/res/drawable/ic_launcher_background.xml b/samples/basic/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from sample/app/src/main/res/drawable/ic_launcher_background.xml rename to samples/basic/src/main/res/drawable/ic_launcher_background.xml diff --git a/sample/app/src/main/res/layout/sample_activity.xml b/samples/basic/src/main/res/layout/samples_activity.xml similarity index 100% rename from sample/app/src/main/res/layout/sample_activity.xml rename to samples/basic/src/main/res/layout/samples_activity.xml diff --git a/sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/samples/basic/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to samples/basic/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/samples/basic/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to samples/basic/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/sample/app/src/main/res/mipmap-hdpi/ic_launcher.png b/samples/basic/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from sample/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to samples/basic/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/sample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/samples/basic/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from sample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to samples/basic/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/sample/app/src/main/res/mipmap-mdpi/ic_launcher.png b/samples/basic/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from sample/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to samples/basic/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/sample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/samples/basic/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from sample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to samples/basic/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/sample/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/samples/basic/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from sample/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to samples/basic/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/sample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/samples/basic/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from sample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to samples/basic/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/sample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/samples/basic/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from sample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to samples/basic/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/sample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/samples/basic/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from sample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to samples/basic/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/samples/basic/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to samples/basic/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/samples/basic/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to samples/basic/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/samples/basic/src/main/res/values/strings.xml b/samples/basic/src/main/res/values/strings.xml new file mode 100644 index 0000000..5564fc3 --- /dev/null +++ b/samples/basic/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + CompositeAdapter Samples + \ No newline at end of file diff --git a/sample/app/src/main/res/values/themes.xml b/samples/basic/src/main/res/values/themes.xml similarity index 91% rename from sample/app/src/main/res/values/themes.xml rename to samples/basic/src/main/res/values/themes.xml index a0232f1..67ee964 100644 --- a/sample/app/src/main/res/values/themes.xml +++ b/samples/basic/src/main/res/values/themes.xml @@ -1,6 +1,5 @@ - - \ No newline at end of file diff --git a/samples/different-bindings/.gitignore b/samples/different-bindings/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/different-bindings/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/different-bindings/build.gradle.kts b/samples/different-bindings/build.gradle.kts new file mode 100644 index 0000000..9f9020d --- /dev/null +++ b/samples/different-bindings/build.gradle.kts @@ -0,0 +1,48 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) + kotlin(Config.Plugins.kapt) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.sampleMinSdk + targetSdk = Config.Build.targetSdk + } + + buildTypes { + getByName("debug") { + } + getByName("release") { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt")) + } + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } + + buildFeatures { + viewBinding = true + dataBinding = true + } +} + +dependencies { + implementation(Config.Deps.Kotlin.kotlinJdk8) + + implementation(Config.Deps.AndroidX.appcompat) + implementation(Config.Deps.AndroidX.swipeRefresh) + implementation(Config.Deps.AndroidX.recycler) + +// implementation(Config.Deps.CompositeAdapter.compositeAdapter) + implementation(project(Config.Deps.Libs.compositeAdapter)) +} \ No newline at end of file diff --git a/samples/different-bindings/proguard-rules.pro b/samples/different-bindings/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/samples/different-bindings/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/different-bindings/src/main/AndroidManifest.xml b/samples/different-bindings/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5e2f3c2 --- /dev/null +++ b/samples/different-bindings/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt new file mode 100644 index 0000000..f03abf0 --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt @@ -0,0 +1,106 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import com.originsdigital.compositeadapter.adapter.CompositeAdapter +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.DifferentBindingsDataBinding1MessageCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.DifferentBindingsDataBinding2MessageCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.view.DifferentBindingsViewMessageCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.DifferentBindingsViewBinding1MessageCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.DifferentBindingsViewBinding2MessageCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI + +class DifferentBindingsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val compositeAdapter = CompositeAdapter().apply { + submitList(generateData()) + } + val swipeRefreshLayout = SwipeRefreshLayout(this).apply { + setOnRefreshListener { + compositeAdapter.submitList(generateData()) { + isRefreshing = false + } + } + } + val recyclerView = RecyclerView(this).apply { + adapter = compositeAdapter + layoutManager = LinearLayoutManager(context) + addItemDecoration(CompositeItemDecoration()) + } + swipeRefreshLayout.addView( + recyclerView, + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + setContentView(swipeRefreshLayout) + } + + //should be inside ViewModel + private fun generateData(): List> { + return DifferentBindingsUI.Type.values().map { type -> + when (type) { + DifferentBindingsUI.Type.VIEW -> { + listOf( + DifferentBindingsViewMessageCell( + data = DifferentBindingsUI( + name = "Cell with a programmatically generated View", + type = type + ) + ) + ) + } + DifferentBindingsUI.Type.VIEW_BINDING -> { + listOf( + DifferentBindingsViewBinding1MessageCell( + data = DifferentBindingsUI( + name = "Cell with custom ViewHolder", + type = type + ) + ), + DifferentBindingsViewBinding2MessageCell( + data = DifferentBindingsUI( + name = "Cell with default ViewHolder", + type = type + ) + ) + ) + } + DifferentBindingsUI.Type.DATA_BINDING -> { + listOf( + DifferentBindingsDataBinding1MessageCell( + data = DifferentBindingsUI( + name = "Cell with DataBindings inside xml", + type = type + ) + ), + DifferentBindingsDataBinding2MessageCell( + data = DifferentBindingsUI( + name = "Cell with DataBindings inside Cell", + type = type + ) + ) + ) + } + } + } + .flatten() + .shuffled() + } + + companion object { + fun getLaunchIntent(context: Context): Intent { + return Intent(context, DifferentBindingsActivity::class.java) + } + } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt new file mode 100644 index 0000000..b9bdedd --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt @@ -0,0 +1,24 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding + +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.differentbindings.R +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI + +// ViewBinding is better anyway +data class DifferentBindingsDataBinding1MessageCell( + override val data: DifferentBindingsUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : DataBindingCell { + + override val uniqueId: String = data.type.name + override val layoutId: Int = R.layout.different_bindings_data_binding_1_list_item + + // We do not need `onBindViewHolder` because everything is done inside the DataBindingCell +// override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { +// super.onBindViewHolder(holder, position) +// } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt new file mode 100644 index 0000000..cb740ec --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt @@ -0,0 +1,29 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding + +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.differentbindings.R +import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsDataBinding2ListItemBinding +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingViewHolder +import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI + +// ViewBinding is better anyway +data class DifferentBindingsDataBinding2MessageCell( + override val data: DifferentBindingsUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : DataBindingCell { + + override val uniqueId: String = data.type.name + override val layoutId: Int = R.layout.different_bindings_data_binding_2_list_item + + // But we can have a custom onBindViewHolder instead of custom BindingAdapter functions + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + ((holder as DataBindingViewHolder).bindings as DifferentBindingsDataBinding2ListItemBinding).apply { + text.text = data.name + } + } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingCell.kt new file mode 100644 index 0000000..6fe36cd --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingCell.kt @@ -0,0 +1,26 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.sample.differentbindings.BR + +// ViewBinding is better anyway +interface DataBindingCell : Cell { + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return DataBindingViewHolder.create(inflater, layoutId, parent) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as DataBindingViewHolder).apply { + bindings.setVariable(BR.item, data) + bindings.executePendingBindings() + } + } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingViewHolder.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingViewHolder.kt new file mode 100644 index 0000000..94a0de2 --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingViewHolder.kt @@ -0,0 +1,30 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.databinding.ViewDataBinding +import androidx.recyclerview.widget.RecyclerView + +// ViewBinding is better anyway +class DataBindingViewHolder( + val bindings: ViewDataBinding +) : RecyclerView.ViewHolder(bindings.root) { + + companion object { + fun create( + inflater: LayoutInflater, + layoutResId: Int, + parent: ViewGroup + ): DataBindingViewHolder { + return DataBindingViewHolder( + DataBindingUtil.inflate( + inflater, + layoutResId, + parent, + false + ) + ) + } + } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt new file mode 100644 index 0000000..a915e3e --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt @@ -0,0 +1,58 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.view + +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.appcompat.widget.AppCompatTextView +import androidx.core.widget.TextViewCompat +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.differentbindings.R +import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI + +data class DifferentBindingsViewMessageCell( + override val data: DifferentBindingsUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : Cell { + + override val uniqueId: String = data.type.name + override val layoutId: Int = R.layout.different_bindings_view_list_item + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + val padding = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 20f, + parent.resources.displayMetrics + ).toInt() + return CustomViewHolder( + AppCompatTextView(inflater.context).apply { + id = R.id.text + layoutParams = RecyclerView.LayoutParams( + RecyclerView.LayoutParams.MATCH_PARENT, + RecyclerView.LayoutParams.WRAP_CONTENT + ) + setPadding(padding, padding, padding, padding) + val outValue = TypedValue() + context.theme.resolveAttribute(R.attr.selectableItemBackground, outValue, true) + setBackgroundResource(outValue.resourceId) + TextViewCompat.setTextAppearance(this, R.style.TextAppearance_AppCompat_Medium) + } + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as CustomViewHolder).apply { + text.text = data.name + } + } + + private class CustomViewHolder(val text: TextView) : RecyclerView.ViewHolder(text) +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt new file mode 100644 index 0000000..0ef1d6b --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt @@ -0,0 +1,45 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.differentbindings.R +import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsViewBinding1ListItemBinding +import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI + +data class DifferentBindingsViewBinding1MessageCell( + override val data: DifferentBindingsUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : Cell { + + override val uniqueId: String = data.type.name + override val layoutId: Int = R.layout.different_bindings_view_binding_1_list_item + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return CustomViewHolder( + DifferentBindingsViewBinding1ListItemBinding.inflate( + inflater, + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as CustomViewHolder).binding.apply { + text.text = data.name + } + } + + private class CustomViewHolder( + val binding: DifferentBindingsViewBinding1ListItemBinding + ) : RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt new file mode 100644 index 0000000..1006306 --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt @@ -0,0 +1,31 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding + +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.differentbindings.R +import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsViewBinding2ListItemBinding +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base.ViewBindingCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI +import com.originsdigital.compositeadapter.utils.getViewBinding + +data class DifferentBindingsViewBinding2MessageCell( + override val data: DifferentBindingsUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : ViewBindingCell { + + override val uniqueId: String = data.type.name + override val layoutId: Int = R.layout.different_bindings_view_binding_2_list_item + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + // Unfortunately the ViewBinding doesn't have a cache for bindings inside the root view tags + // like the DataBinding. To remove repeated findViewById calls, + // we can call the `getViewBinding` to bind and save the binding or get the saved binding + // Or we can have a custom ViewHolder for each Cell, see SampleViewBinding1MessageCell + (holder.getViewBinding(DifferentBindingsViewBinding2ListItemBinding::bind)).apply { + text.text = data.name + } + } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingCell.kt new file mode 100644 index 0000000..a54f7f6 --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingCell.kt @@ -0,0 +1,17 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell + +interface ViewBindingCell : Cell { + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return ViewBindingViewHolder(inflater.inflate(layoutId, parent, false)) + } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingViewHolder.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingViewHolder.kt new file mode 100644 index 0000000..a6fe330 --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingViewHolder.kt @@ -0,0 +1,6 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base + +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +open class ViewBindingViewHolder(view: View) : RecyclerView.ViewHolder(view) \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/entity/DifferentBindingsUI.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/entity/DifferentBindingsUI.kt new file mode 100644 index 0000000..f75c782 --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/entity/DifferentBindingsUI.kt @@ -0,0 +1,10 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.entity + +data class DifferentBindingsUI( + val name: String, + val type: Type +) { + enum class Type { + VIEW, VIEW_BINDING, DATA_BINDING + } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_1_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_1_list_item.xml new file mode 100644 index 0000000..026c4dd --- /dev/null +++ b/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_1_list_item.xml @@ -0,0 +1,21 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_2_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_2_list_item.xml new file mode 100644 index 0000000..f5fddcc --- /dev/null +++ b/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_2_list_item.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_list_item.xml new file mode 100644 index 0000000..a383a7c --- /dev/null +++ b/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_list_item.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_list_item.xml new file mode 100644 index 0000000..a383a7c --- /dev/null +++ b/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_list_item.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/samples/different-bindings/src/main/res/values/ids.xml b/samples/different-bindings/src/main/res/values/ids.xml new file mode 100644 index 0000000..7462663 --- /dev/null +++ b/samples/different-bindings/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 7043d18..42f2cab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,8 +2,8 @@ rootProject.name = "CompositeAdapter_Android" include(":composite-adapter") -//include(":sample:app") include(":samples:basic") +include(":samples:different-bindings") include(":sample:features:base:core") include(":sample:features:base:ui") From 6e0fd777be7093e737e9d012076a8f3c945e2dd7 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Fri, 24 Dec 2021 12:14:06 +0300 Subject: [PATCH 05/19] feat(adapter): make the SpaceItemDecoration params `open` to be able to add the `data` modifier --- .../compositeadapter/decoration/SpaceItemDecoration.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composite-adapter/src/main/java/com/originsdigital/compositeadapter/decoration/SpaceItemDecoration.kt b/composite-adapter/src/main/java/com/originsdigital/compositeadapter/decoration/SpaceItemDecoration.kt index 84332cd..73501dc 100644 --- a/composite-adapter/src/main/java/com/originsdigital/compositeadapter/decoration/SpaceItemDecoration.kt +++ b/composite-adapter/src/main/java/com/originsdigital/compositeadapter/decoration/SpaceItemDecoration.kt @@ -9,10 +9,10 @@ import android.view.View import androidx.recyclerview.widget.RecyclerView open class SpaceItemDecoration( - val top: Int = 0, - val bottom: Int = 0, - val start: Int = 0, - val end: Int = 0 + open val top: Int = 0, + open val bottom: Int = 0, + open val start: Int = 0, + open val end: Int = 0 ) : ItemDecoration { override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State, item: ITEM) { From 9dc27eefc7719a907600c81e1c581ae80a8441e7 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Fri, 24 Dec 2021 12:28:24 +0300 Subject: [PATCH 06/19] feat(sample): add sample with decorations --- buildSrc/src/main/kotlin/Deps.kt | 1 + samples/basic/build.gradle.kts | 1 + samples/basic/src/main/AndroidManifest.xml | 4 + .../sample/basic/ui/SamplesActivity.kt | 14 +- .../sample/basic/ui/entity/SampleUI.kt | 2 +- samples/decorations/.gitignore | 1 + samples/decorations/build.gradle.kts | 46 +++++ samples/decorations/proguard-rules.pro | 21 +++ .../decorations/src/main/AndroidManifest.xml | 1 + .../decorations/ui/DecorationsActivity.kt | 170 ++++++++++++++++++ .../decorations/ui/cell/DecorationsCell.kt | 39 ++++ .../ui/decorations/SampleItemDecoration.kt | 111 ++++++++++++ .../decorations/ui/entity/DecorationsUI.kt | 6 + .../main/res/layout/decorations_list_item.xml | 10 ++ .../ui/DifferentBindingsActivity.kt | 7 +- settings.gradle.kts | 1 + 16 files changed, 430 insertions(+), 5 deletions(-) create mode 100644 samples/decorations/.gitignore create mode 100644 samples/decorations/build.gradle.kts create mode 100644 samples/decorations/proguard-rules.pro create mode 100644 samples/decorations/src/main/AndroidManifest.xml create mode 100644 samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/DecorationsActivity.kt create mode 100644 samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/cell/DecorationsCell.kt create mode 100644 samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/decorations/SampleItemDecoration.kt create mode 100644 samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/entity/DecorationsUI.kt create mode 100644 samples/decorations/src/main/res/layout/decorations_list_item.xml diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index 732bd7d..c18e052 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -76,6 +76,7 @@ object Config { object Libs { const val compositeAdapter = ":composite-adapter" + const val decorations = ":samples:decorations" const val differentBindings = ":samples:different-bindings" const val baseCore = ":sample:features:base:core" diff --git a/samples/basic/build.gradle.kts b/samples/basic/build.gradle.kts index c3a950b..5d41ad5 100644 --- a/samples/basic/build.gradle.kts +++ b/samples/basic/build.gradle.kts @@ -62,5 +62,6 @@ dependencies { // implementation(Config.Deps.CompositeAdapter.compositeAdapter) implementation(project(Config.Deps.Libs.compositeAdapter)) + implementation(project(Config.Deps.Libs.decorations)) implementation(project(Config.Deps.Libs.differentBindings)) } \ No newline at end of file diff --git a/samples/basic/src/main/AndroidManifest.xml b/samples/basic/src/main/AndroidManifest.xml index 4dc90dc..1a641f4 100644 --- a/samples/basic/src/main/AndroidManifest.xml +++ b/samples/basic/src/main/AndroidManifest.xml @@ -23,5 +23,9 @@ + + \ No newline at end of file diff --git a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt index c92f134..fa50c2d 100644 --- a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt @@ -1,6 +1,7 @@ package com.originsdigital.compositeadapter.sample.basic.ui import android.os.Bundle +import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager @@ -11,6 +12,7 @@ import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration import com.originsdigital.compositeadapter.sample.basic.ui.cell.SampleCell import com.originsdigital.compositeadapter.sample.basic.ui.entity.SampleUI +import com.originsdigital.compositeadapter.sample.decorations.ui.DecorationsActivity import com.originsdigital.compositeadapter.sample.differentbindings.ui.DifferentBindingsActivity class SamplesActivity : AppCompatActivity() { @@ -18,6 +20,10 @@ class SamplesActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + setContentView(generateView()) + } + + private fun generateView(): View { val compositeAdapter = CompositeAdapter().apply { submitList(generateData()) } @@ -30,24 +36,26 @@ class SamplesActivity : AppCompatActivity() { layoutManager = LinearLayoutManager(context) addItemDecoration(CompositeItemDecoration()) } - setContentView(recyclerView) + return recyclerView } //should be inside ViewModel private fun generateData(): List> { return SampleUI.Type.values().map { type -> val name = when (type) { - SampleUI.Type.DIFFERENT_BINDINGS -> "ViewBindings/DataBindings/Programmatically View" SampleUI.Type.DECORATIONS -> "Decorations" + SampleUI.Type.DIFFERENT_BINDINGS -> "ViewBindings/DataBindings/Programmatically View" SampleUI.Type.INNER_RECYCLER -> "Inner RecyclerView/ViewPager/Etc" } val onClickListener: (ClickItem) -> Unit = { item -> @Suppress("UNUSED_VARIABLE") val result = when (item.item.type) { + SampleUI.Type.DECORATIONS -> { + startActivity(DecorationsActivity.getLaunchIntent(this)) + } SampleUI.Type.DIFFERENT_BINDINGS -> { startActivity(DifferentBindingsActivity.getLaunchIntent(this)) } - SampleUI.Type.DECORATIONS -> Unit SampleUI.Type.INNER_RECYCLER -> Unit } } diff --git a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/entity/SampleUI.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/entity/SampleUI.kt index 184d9f6..d5878e0 100644 --- a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/entity/SampleUI.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/entity/SampleUI.kt @@ -5,6 +5,6 @@ data class SampleUI( val type: Type ) { enum class Type { - DIFFERENT_BINDINGS, DECORATIONS, INNER_RECYCLER + DECORATIONS, DIFFERENT_BINDINGS, INNER_RECYCLER } } \ No newline at end of file diff --git a/samples/decorations/.gitignore b/samples/decorations/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/decorations/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/decorations/build.gradle.kts b/samples/decorations/build.gradle.kts new file mode 100644 index 0000000..ee95cc0 --- /dev/null +++ b/samples/decorations/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.sampleMinSdk + targetSdk = Config.Build.targetSdk + } + + buildTypes { + getByName("debug") { + } + getByName("release") { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt")) + } + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } + + buildFeatures { + viewBinding = true + } +} + +dependencies { + implementation(Config.Deps.Kotlin.kotlinJdk8) + + implementation(Config.Deps.AndroidX.appcompat) + implementation(Config.Deps.AndroidX.swipeRefresh) + implementation(Config.Deps.AndroidX.recycler) + +// implementation(Config.Deps.CompositeAdapter.compositeAdapter) + implementation(project(Config.Deps.Libs.compositeAdapter)) +} \ No newline at end of file diff --git a/samples/decorations/proguard-rules.pro b/samples/decorations/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/samples/decorations/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/decorations/src/main/AndroidManifest.xml b/samples/decorations/src/main/AndroidManifest.xml new file mode 100644 index 0000000..30dccc7 --- /dev/null +++ b/samples/decorations/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/DecorationsActivity.kt b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/DecorationsActivity.kt new file mode 100644 index 0000000..3baa403 --- /dev/null +++ b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/DecorationsActivity.kt @@ -0,0 +1,170 @@ +package com.originsdigital.compositeadapter.sample.decorations.ui + +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.util.TypedValue +import android.view.View +import android.view.ViewGroup +import androidx.annotation.ColorInt +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import com.originsdigital.compositeadapter.adapter.CompositeAdapter +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.decorations.ui.cell.DecorationsCell +import com.originsdigital.compositeadapter.sample.decorations.ui.decorations.SampleItemDecoration +import com.originsdigital.compositeadapter.sample.decorations.ui.entity.DecorationsUI +import kotlin.random.Random + +class DecorationsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val dataHolder = DataHolder( + space = dpToPx(20f).toInt(), + radius = dpToPx(6f), + dividerHeight = dpToPx(1f).toInt(), + dividerColorInt = Color.GRAY, + backgroundColorInt = Color.DKGRAY + ) + setContentView(generateView(dataHolder)) + } + + private fun Context.dpToPx(dp: Float): Float { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics) + } + + private fun generateView(dataHolder: DataHolder): View { + val compositeAdapter = CompositeAdapter().apply { + submitList(dataHolder.generateData()) + } + val swipeRefreshLayout = SwipeRefreshLayout(this).apply { + setOnRefreshListener { + compositeAdapter.submitList(dataHolder.generateData()) { + isRefreshing = false + } + } + } + val recyclerView = RecyclerView(this).apply { + adapter = compositeAdapter + layoutManager = LinearLayoutManager(context) + addItemDecoration(CompositeItemDecoration()) + } + swipeRefreshLayout.addView( + recyclerView, + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + return swipeRefreshLayout + } + + + companion object { + fun getLaunchIntent(context: Context): Intent { + return Intent(context, DecorationsActivity::class.java) + } + } + + //should be inside ViewModel/UIMapper + private class DataHolder( + space: Int, + radius: Float, + dividerHeight: Int, + @ColorInt dividerColorInt: Int, + @ColorInt backgroundColorInt: Int + ) { + + private val singleItemDecoration: ItemDecoration> + private val topItemDecoration: ItemDecoration> + private val middleItemDecoration: ItemDecoration> + private val bottomItemDecoration: ItemDecoration> + + init { + singleItemDecoration = SampleItemDecoration( + type = SampleItemDecoration.Type.SINGLE, + radius = radius, + dividerHeight = dividerHeight, + dividerColorInt = dividerColorInt, + backgroundColorInt = backgroundColorInt, + top = space, + bottom = space, + start = space, + end = space + ) + topItemDecoration = SampleItemDecoration( + type = SampleItemDecoration.Type.TOP, + radius = radius, + dividerHeight = dividerHeight, + dividerColorInt = dividerColorInt, + backgroundColorInt = backgroundColorInt, + top = space, + start = space, + end = space, + bottom = dividerHeight + ) + middleItemDecoration = SampleItemDecoration( + type = SampleItemDecoration.Type.MIDDLE, + radius = radius, + dividerHeight = dividerHeight, + dividerColorInt = dividerColorInt, + backgroundColorInt = backgroundColorInt, + start = space, + end = space, + bottom = dividerHeight + ) + bottomItemDecoration = SampleItemDecoration( + type = SampleItemDecoration.Type.BOTTOM, + radius = radius, + dividerHeight = dividerHeight, + dividerColorInt = dividerColorInt, + backgroundColorInt = backgroundColorInt, + bottom = space, + start = space, + end = space + ) + } + + //should be inside ViewModel/UIMapper + fun generateData(): List> { + val ids = (0..(Random.nextInt(10))).filterIndexed { index, _ -> + index == 0 || Random.nextBoolean() + } + val size = ids.size + return ids.mapIndexed { index, id -> + val name: String + val decoration: ItemDecoration> + when { + size == 1 -> { + name = "Single Cell id=$id" + decoration = singleItemDecoration + } + index == 0 -> { + name = "Top Cell id=$id" + decoration = topItemDecoration + } + index == size - 1 -> { + name = "Bottom Cell id=$id" + decoration = bottomItemDecoration + } + else -> { + name = "Middle Cell id=$id" + decoration = middleItemDecoration + } + } + DecorationsCell( + data = DecorationsUI( + id = id.toString(), + name = name + ), + decoration = decoration + ) + } + } + } +} \ No newline at end of file diff --git a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/cell/DecorationsCell.kt b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/cell/DecorationsCell.kt new file mode 100644 index 0000000..bebf4da --- /dev/null +++ b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/cell/DecorationsCell.kt @@ -0,0 +1,39 @@ +package com.originsdigital.compositeadapter.sample.decorations.ui.cell + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.decorations.R +import com.originsdigital.compositeadapter.sample.decorations.databinding.DecorationsListItemBinding +import com.originsdigital.compositeadapter.sample.decorations.ui.entity.DecorationsUI + +data class DecorationsCell( + override val data: DecorationsUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : Cell { + + override val uniqueId: String = data.id + override val layoutId: Int = R.layout.decorations_list_item + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return SampleViewHolder(DecorationsListItemBinding.inflate(inflater, parent, false)) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as SampleViewHolder).binding.apply { + text.text = data.name + } + } + + private class SampleViewHolder( + val binding: DecorationsListItemBinding + ) : RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/decorations/SampleItemDecoration.kt b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/decorations/SampleItemDecoration.kt new file mode 100644 index 0000000..584736a --- /dev/null +++ b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/decorations/SampleItemDecoration.kt @@ -0,0 +1,111 @@ +package com.originsdigital.compositeadapter.sample.decorations.ui.decorations + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Rect +import android.view.View +import androidx.annotation.ColorInt +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration + +data class SampleItemDecoration( + private val type: Type, + private val radius: Float, + private val dividerHeight: Int, + @ColorInt val dividerColorInt: Int, + @ColorInt val backgroundColorInt: Int, + override val top: Int = 0, + override val bottom: Int = 0, + override val start: Int = 0, + override val end: Int = 0 +) : SpaceItemDecoration>() { + + private val dividerPaint = Paint().apply { + color = dividerColorInt + } + private val backgroundPaint = Paint().apply { + color = backgroundColorInt + } + private val itemBounds = Rect() + + override fun onDraw( + canvas: Canvas, + view: View, + parent: RecyclerView, + state: RecyclerView.State, + item: Cell<*> + ) { + super.onDraw(canvas, view, parent, state, item) + parent.layoutManager?.getDecoratedBoundsWithMargins(view, itemBounds) + val drawBottomDivider: Boolean + val roundedTop: Boolean + val roundedBottom: Boolean + when (type) { + Type.SINGLE -> { + drawBottomDivider = false + roundedTop = true + roundedBottom = true + } + Type.TOP -> { + drawBottomDivider = true + roundedTop = true + roundedBottom = false + } + Type.MIDDLE -> { + drawBottomDivider = true + roundedTop = false + roundedBottom = false + } + Type.BOTTOM -> { + drawBottomDivider = false + roundedTop = false + roundedBottom = true + } + } + canvas.drawRoundedRect( + paint = backgroundPaint, + left = itemBounds.left.toFloat() + start, + top = itemBounds.top.toFloat() + top, + right = itemBounds.right.toFloat() - end, + bottom = itemBounds.bottom.toFloat() - bottom, + radius = radius, + withTop = roundedTop, + withBottom = roundedBottom + ) + if (drawBottomDivider) { + itemBounds.left = itemBounds.left + start + itemBounds.right = itemBounds.right - end + itemBounds.top = itemBounds.bottom - dividerHeight + canvas.drawRect(itemBounds, dividerPaint) + } + } + + private fun Canvas.drawRoundedRect( + paint: Paint, + left: Float, + top: Float, + right: Float, + bottom: Float, + radius: Float, + withTop: Boolean, + withBottom: Boolean + ) { + save() + clipRect(left, top, right, bottom) + drawRoundRect( + left, + if (withTop) top else top - radius, + right, + if (withBottom) bottom else bottom + radius, + radius, + radius, + paint + ) + restore() + } + + enum class Type { + SINGLE, TOP, MIDDLE, BOTTOM + } +} \ No newline at end of file diff --git a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/entity/DecorationsUI.kt b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/entity/DecorationsUI.kt new file mode 100644 index 0000000..e57970d --- /dev/null +++ b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/entity/DecorationsUI.kt @@ -0,0 +1,6 @@ +package com.originsdigital.compositeadapter.sample.decorations.ui.entity + +data class DecorationsUI( + val id: String, + val name: String +) \ No newline at end of file diff --git a/samples/decorations/src/main/res/layout/decorations_list_item.xml b/samples/decorations/src/main/res/layout/decorations_list_item.xml new file mode 100644 index 0000000..a383a7c --- /dev/null +++ b/samples/decorations/src/main/res/layout/decorations_list_item.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt index f03abf0..e161ab5 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt @@ -3,6 +3,7 @@ package com.originsdigital.compositeadapter.sample.differentbindings.ui import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager @@ -23,6 +24,10 @@ class DifferentBindingsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + setContentView(generateView()) + } + + private fun generateView(): View { val compositeAdapter = CompositeAdapter().apply { submitList(generateData()) } @@ -43,7 +48,7 @@ class DifferentBindingsActivity : AppCompatActivity() { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) - setContentView(swipeRefreshLayout) + return swipeRefreshLayout } //should be inside ViewModel diff --git a/settings.gradle.kts b/settings.gradle.kts index 42f2cab..2d01b54 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ rootProject.name = "CompositeAdapter_Android" include(":composite-adapter") include(":samples:basic") +include(":samples:decorations") include(":samples:different-bindings") include(":sample:features:base:core") From 32718b8ddf6152aeb5b468c24b3e66f5106c5c9d Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Fri, 24 Dec 2021 13:58:03 +0300 Subject: [PATCH 07/19] feat(sample): add sample with inner RecyclerView/ViewPager1 or 2/other complex view --- buildSrc/src/main/kotlin/Deps.kt | 1 + samples/basic/build.gradle.kts | 1 + samples/basic/src/main/AndroidManifest.xml | 4 + .../sample/basic/ui/SamplesActivity.kt | 22 +-- samples/inner-recyclerview/.gitignore | 1 + samples/inner-recyclerview/build.gradle.kts | 46 ++++++ samples/inner-recyclerview/proguard-rules.pro | 21 +++ .../src/main/AndroidManifest.xml | 1 + .../ui/InnerRecyclerActivity.kt | 142 ++++++++++++++++++ .../ui/cell/InnerRecycler2Cell.kt | 63 ++++++++ .../ui/cell/InnerRecyclerCell.kt | 84 +++++++++++ .../ui/cell/InnerRecyclerItemCell.kt | 67 +++++++++ .../ui/entity/InnerRecyclerItemUI.kt | 6 + .../ui/entity/InnerRecyclerUI.kt | 8 + .../PercentWidthLinearLayoutManager.kt | 37 +++++ .../layout/inner_recycler_item_list_item.xml | 12 ++ .../res/layout/inner_recycler_list_item.xml | 5 + settings.gradle.kts | 1 + 18 files changed, 513 insertions(+), 9 deletions(-) create mode 100644 samples/inner-recyclerview/.gitignore create mode 100644 samples/inner-recyclerview/build.gradle.kts create mode 100644 samples/inner-recyclerview/proguard-rules.pro create mode 100644 samples/inner-recyclerview/src/main/AndroidManifest.xml create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerCell.kt create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerItemUI.kt create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/layoutmanager/PercentWidthLinearLayoutManager.kt create mode 100644 samples/inner-recyclerview/src/main/res/layout/inner_recycler_item_list_item.xml create mode 100644 samples/inner-recyclerview/src/main/res/layout/inner_recycler_list_item.xml diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index c18e052..a3c4c4b 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -78,6 +78,7 @@ object Config { const val decorations = ":samples:decorations" const val differentBindings = ":samples:different-bindings" + const val innerRecyclerview = ":samples:inner-recyclerview" const val baseCore = ":sample:features:base:core" const val baseUI = ":sample:features:base:ui" diff --git a/samples/basic/build.gradle.kts b/samples/basic/build.gradle.kts index 5d41ad5..3116a3c 100644 --- a/samples/basic/build.gradle.kts +++ b/samples/basic/build.gradle.kts @@ -64,4 +64,5 @@ dependencies { implementation(project(Config.Deps.Libs.decorations)) implementation(project(Config.Deps.Libs.differentBindings)) + implementation(project(Config.Deps.Libs.innerRecyclerview)) } \ No newline at end of file diff --git a/samples/basic/src/main/AndroidManifest.xml b/samples/basic/src/main/AndroidManifest.xml index 1a641f4..e96d0ba 100644 --- a/samples/basic/src/main/AndroidManifest.xml +++ b/samples/basic/src/main/AndroidManifest.xml @@ -27,5 +27,9 @@ + + \ No newline at end of file diff --git a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt index fa50c2d..ed4b0ea 100644 --- a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt @@ -14,6 +14,7 @@ import com.originsdigital.compositeadapter.sample.basic.ui.cell.SampleCell import com.originsdigital.compositeadapter.sample.basic.ui.entity.SampleUI import com.originsdigital.compositeadapter.sample.decorations.ui.DecorationsActivity import com.originsdigital.compositeadapter.sample.differentbindings.ui.DifferentBindingsActivity +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.InnerRecyclerActivity class SamplesActivity : AppCompatActivity() { @@ -48,16 +49,19 @@ class SamplesActivity : AppCompatActivity() { SampleUI.Type.INNER_RECYCLER -> "Inner RecyclerView/ViewPager/Etc" } val onClickListener: (ClickItem) -> Unit = { item -> - @Suppress("UNUSED_VARIABLE") - val result = when (item.item.type) { - SampleUI.Type.DECORATIONS -> { - startActivity(DecorationsActivity.getLaunchIntent(this)) + startActivity( + when (item.item.type) { + SampleUI.Type.DECORATIONS -> { + DecorationsActivity.getLaunchIntent(this) + } + SampleUI.Type.DIFFERENT_BINDINGS -> { + DifferentBindingsActivity.getLaunchIntent(this) + } + SampleUI.Type.INNER_RECYCLER -> { + InnerRecyclerActivity.getLaunchIntent(this) + } } - SampleUI.Type.DIFFERENT_BINDINGS -> { - startActivity(DifferentBindingsActivity.getLaunchIntent(this)) - } - SampleUI.Type.INNER_RECYCLER -> Unit - } + ) } SampleCell( data = SampleUI( diff --git a/samples/inner-recyclerview/.gitignore b/samples/inner-recyclerview/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/inner-recyclerview/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/inner-recyclerview/build.gradle.kts b/samples/inner-recyclerview/build.gradle.kts new file mode 100644 index 0000000..ee95cc0 --- /dev/null +++ b/samples/inner-recyclerview/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.sampleMinSdk + targetSdk = Config.Build.targetSdk + } + + buildTypes { + getByName("debug") { + } + getByName("release") { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt")) + } + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } + + buildFeatures { + viewBinding = true + } +} + +dependencies { + implementation(Config.Deps.Kotlin.kotlinJdk8) + + implementation(Config.Deps.AndroidX.appcompat) + implementation(Config.Deps.AndroidX.swipeRefresh) + implementation(Config.Deps.AndroidX.recycler) + +// implementation(Config.Deps.CompositeAdapter.compositeAdapter) + implementation(project(Config.Deps.Libs.compositeAdapter)) +} \ No newline at end of file diff --git a/samples/inner-recyclerview/proguard-rules.pro b/samples/inner-recyclerview/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/samples/inner-recyclerview/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/AndroidManifest.xml b/samples/inner-recyclerview/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1612cac --- /dev/null +++ b/samples/inner-recyclerview/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt new file mode 100644 index 0000000..2e3b4c9 --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt @@ -0,0 +1,142 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.TypedValue +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import com.originsdigital.compositeadapter.adapter.CompositeAdapter +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecycler2Cell +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecyclerCell +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecyclerItemCell +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerItemUI +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI +import kotlin.random.Random + +class InnerRecyclerActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val dataHolder = DataHolder(dpToPx(8f).toInt()) + setContentView(generateView(dataHolder)) + } + + private fun Context.dpToPx(dp: Float): Float { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics) + } + + private fun generateView(dataHolder: DataHolder): View { + val compositeAdapter = CompositeAdapter().apply { + submitList(dataHolder.generateData()) + } + val swipeRefreshLayout = SwipeRefreshLayout(this).apply { + setOnRefreshListener { + compositeAdapter.submitList(dataHolder.generateData()) { + isRefreshing = false + } + } + } + val recyclerView = RecyclerView(this).apply { + adapter = compositeAdapter + layoutManager = LinearLayoutManager(context) + addItemDecoration(CompositeItemDecoration()) + } + swipeRefreshLayout.addView( + recyclerView, + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + return swipeRefreshLayout + } + + + companion object { + fun getLaunchIntent(context: Context): Intent { + return Intent(context, InnerRecyclerActivity::class.java) + } + } + + //should be inside ViewModel/UIMapper + private class DataHolder(space: Int) { + + private val singleItemDecoration: ItemDecoration> + private val firstItemDecoration: ItemDecoration> + private val middleItemDecoration: ItemDecoration> + private val lastItemDecoration: ItemDecoration> + + init { + singleItemDecoration = SpaceItemDecoration( + top = space, + bottom = space, + start = space, + end = space + ) + firstItemDecoration = SpaceItemDecoration( + top = space, + bottom = space, + start = space, + end = space / 2 + ) + middleItemDecoration = SpaceItemDecoration( + top = space, + bottom = space, + start = space / 2, + end = space / 2 + ) + lastItemDecoration = SpaceItemDecoration( + top = space, + bottom = space, + start = space / 2, + end = space + ) + } + + //should be inside ViewModel/UIMapper + fun generateData(): List> { + return (0..20).map { recyclerId -> + val size = 10 + val cells = (0..size).mapIndexed { index, itemId -> + val decoration = when { + size == 0 -> singleItemDecoration + index == 0 -> firstItemDecoration + index == size -> lastItemDecoration + else -> middleItemDecoration + } + InnerRecyclerItemCell( + data = InnerRecyclerItemUI( + id = itemId.toString(), + name = "Value $itemId is ${Random.nextBoolean()}" + ), + decoration = decoration + ) + } + val recyclerUI = InnerRecyclerUI( + id = recyclerId.toString(), + cells = cells + ) + // Cells do the same, but + // InnerRecyclerCell - more code but more clearer and `correct` + // InnerRecycler2Cell - less code (can be less with `ViewBindingCell`) + if (Random.nextBoolean()) { + InnerRecyclerCell( + data = recyclerUI + ) + } else { + InnerRecycler2Cell( + data = recyclerUI + ) + } + } + } + } +} \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt new file mode 100644 index 0000000..2df386d --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt @@ -0,0 +1,63 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.adapter.CompositeAdapter +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.innerrecyclerview.R +import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecyclerListItemBinding +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.layoutmanager.PercentWidthLinearLayoutManager + +data class InnerRecycler2Cell( + override val data: InnerRecyclerUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : Cell { + + override val uniqueId: String = data.id + override val layoutId: Int = R.layout.inner_recycler_list_item + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return SampleViewHolder( + InnerRecyclerListItemBinding.inflate(inflater, parent, false).also { binding -> + // Don't forget that Cell can survive the configuration changes inside the ViewModel + // or in some other way. So you MUST NOT store any link to + // View/ViewHolder/Fragment/Context/etc in the Cell otherwise it will be leaked. + binding.recyclerView.adapter = CompositeAdapter() + binding.recyclerView.layoutManager = + PercentWidthLinearLayoutManager(binding.root.context) + binding.recyclerView.addItemDecoration(CompositeItemDecoration()) + } + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as SampleViewHolder).binding.apply { + (recyclerView.adapter as CompositeAdapter).submitList(data.cells) + } + } + + // We do not need to animate the InnerRecyclerCell, because it has its own CompositeAdapter, + // which will calculate the diffs of his cells and animate these changes within itself. + // The same for the ViewPager1/ViewPager2/Webview/VideoPlayer/other complex view. + // `onBindViewHolder` with `payload` will be called. But its empty so it will call + // `onBindViewHolder` without `payload` where we use submitList + override fun getChangePayload(newItem: Cell<*>): Any = RECYCLER_VIEW_PAYLOAD + + companion object { + private const val RECYCLER_VIEW_PAYLOAD = "RECYCLER_VIEW" + } + + private class SampleViewHolder( + val binding: InnerRecyclerListItemBinding + ) : RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerCell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerCell.kt new file mode 100644 index 0000000..ad5fb7d --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerCell.kt @@ -0,0 +1,84 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.adapter.CompositeAdapter +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.innerrecyclerview.R +import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecyclerListItemBinding +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.layoutmanager.PercentWidthLinearLayoutManager + +data class InnerRecyclerCell( + override val data: InnerRecyclerUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : Cell { + + override val uniqueId: String = data.id + override val layoutId: Int = R.layout.inner_recycler_list_item + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return SampleViewHolder(InnerRecyclerListItemBinding.inflate(inflater, parent, false)) + } + + // This is the correct way to optimize bindings with payloads + // But you can skip this overload, so `onBindViewHolder` with `payload` will call + // `onBindViewHolder` without `payload`, and result will be the same in this case + override fun onBindViewHolder( + holder: RecyclerView.ViewHolder, + position: Int, + payloads: List + ): Boolean { + return if (payloads.isNotEmpty() && payloads.all { payload -> RECYCLER_VIEW_PAYLOAD == payload }) { + submitData(holder) + true + } else { + super.onBindViewHolder(holder, position, payloads) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + submitData(holder) + } + + private fun submitData(holder: RecyclerView.ViewHolder) { + (holder as SampleViewHolder).submitList(data.cells) + } + + // We do not need to animate the InnerRecyclerCell, because it has its own CompositeAdapter, + // which will calculate the diffs of his cells and animate these changes within itself. + // The same for the ViewPager1/ViewPager2/Webview/VideoPlayer/other complex view. + override fun getChangePayload(newItem: Cell<*>): Any = RECYCLER_VIEW_PAYLOAD + + companion object { + private const val RECYCLER_VIEW_PAYLOAD = "RECYCLER_VIEW" + } + + private class SampleViewHolder( + binding: InnerRecyclerListItemBinding + ) : RecyclerView.ViewHolder(binding.root) { + + private val adapter = CompositeAdapter() + + init { + binding.apply { + recyclerView.adapter = adapter + recyclerView.layoutManager = PercentWidthLinearLayoutManager(binding.root.context) + recyclerView.addItemDecoration(CompositeItemDecoration()) + } + } + + fun submitList(cells: List>) { + adapter.submitList(cells) + } + } +} \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt new file mode 100644 index 0000000..66ce34b --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt @@ -0,0 +1,67 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell + +import android.content.Context +import android.graphics.Outline +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.ViewOutlineProvider +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.sample.innerrecyclerview.R +import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecyclerItemListItemBinding +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerItemUI + +data class InnerRecyclerItemCell( + override val data: InnerRecyclerItemUI, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : Cell { + + override val uniqueId: String = data.id + override val layoutId: Int = R.layout.inner_recycler_item_list_item + + override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return SampleViewHolder( + InnerRecyclerItemListItemBinding.inflate(inflater, parent, false).also { holder -> + holder.root.applyRoundCorners(holder.root.context.dpToPx(6f)) + } + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as SampleViewHolder).binding.apply { + text.text = data.name + } + } + + private class SampleViewHolder( + val binding: InnerRecyclerItemListItemBinding + ) : RecyclerView.ViewHolder(binding.root) + + private fun Context.dpToPx(dp: Float): Float { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics) + } + + private fun View.applyRoundCorners(radius: Float) { + outlineProvider = object : ViewOutlineProvider() { + override fun getOutline(view: View, outline: Outline) { + outline.setRoundRect( + 0, + 0, + view.width, + view.height, + radius + ) + } + } + clipToOutline = true + } +} \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerItemUI.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerItemUI.kt new file mode 100644 index 0000000..7474955 --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerItemUI.kt @@ -0,0 +1,6 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity + +data class InnerRecyclerItemUI( + val id: String, + val name: String +) \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt new file mode 100644 index 0000000..9cae78f --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt @@ -0,0 +1,8 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity + +import com.originsdigital.compositeadapter.cell.Cell + +data class InnerRecyclerUI( + val id: String, + val cells: List> +) \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/layoutmanager/PercentWidthLinearLayoutManager.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/layoutmanager/PercentWidthLinearLayoutManager.kt new file mode 100644 index 0000000..692a53c --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/layoutmanager/PercentWidthLinearLayoutManager.kt @@ -0,0 +1,37 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.layoutmanager + +import android.content.Context +import android.util.AttributeSet +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView + +class PercentWidthLinearLayoutManager( + context: Context +) : LinearLayoutManager(context, HORIZONTAL, false) { + + override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams { + return super.generateDefaultLayoutParams().scale() + } + + override fun generateLayoutParams(lp: ViewGroup.LayoutParams?): RecyclerView.LayoutParams { + return super.generateLayoutParams(lp).scale() + } + + override fun generateLayoutParams( + c: Context?, + attrs: AttributeSet? + ): RecyclerView.LayoutParams { + return super.generateLayoutParams(c, attrs).scale() + } + + private fun RecyclerView.LayoutParams.scale(): RecyclerView.LayoutParams { + return this.apply { + width = calculateChildWidth(getWidth() - paddingStart - paddingEnd) + } + } + + companion object { + fun calculateChildWidth(parentWidth: Int): Int = parentWidth / 3 + } +} \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/res/layout/inner_recycler_item_list_item.xml b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_item_list_item.xml new file mode 100644 index 0000000..fd6eead --- /dev/null +++ b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_item_list_item.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/res/layout/inner_recycler_list_item.xml b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_list_item.xml new file mode 100644 index 0000000..a6be088 --- /dev/null +++ b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_list_item.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 2d01b54..4586f95 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,6 +5,7 @@ include(":composite-adapter") include(":samples:basic") include(":samples:decorations") include(":samples:different-bindings") +include(":samples:inner-recyclerview") include(":sample:features:base:core") include(":sample:features:base:ui") From a22e6d5340a4dec50b1dd82285767ed2212c8c7f Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Fri, 24 Dec 2021 15:07:37 +0300 Subject: [PATCH 08/19] feat(sample): add sample how to save scroll state for inner RecyclerViews --- .../sample/basic/ui/SamplesActivity.kt | 2 +- .../ui/InnerRecyclerActivity.kt | 21 +++++--- ...rRecyclerCell.kt => InnerRecycler1Cell.kt} | 30 ++++++++--- .../ui/cell/InnerRecycler2Cell.kt | 26 ++++++--- .../ui/cell/InnerRecyclerItemCell.kt | 8 +-- .../ui/entity/InnerRecyclerUI.kt | 6 ++- .../ui/stateholder/ScrollStateHolder.kt | 53 +++++++++++++++++++ .../ui/stateholder/ScrollStatesHolder.kt | 22 ++++++++ ...tem.xml => inner_recycler_1_list_item.xml} | 0 .../res/layout/inner_recycler_2_list_item.xml | 5 ++ 10 files changed, 146 insertions(+), 27 deletions(-) rename samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/{InnerRecyclerCell.kt => InnerRecycler1Cell.kt} (73%) create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStateHolder.kt create mode 100644 samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStatesHolder.kt rename samples/inner-recyclerview/src/main/res/layout/{inner_recycler_list_item.xml => inner_recycler_1_list_item.xml} (100%) create mode 100644 samples/inner-recyclerview/src/main/res/layout/inner_recycler_2_list_item.xml diff --git a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt index ed4b0ea..87c001d 100644 --- a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt @@ -46,7 +46,7 @@ class SamplesActivity : AppCompatActivity() { val name = when (type) { SampleUI.Type.DECORATIONS -> "Decorations" SampleUI.Type.DIFFERENT_BINDINGS -> "ViewBindings/DataBindings/Programmatically View" - SampleUI.Type.INNER_RECYCLER -> "Inner RecyclerView/ViewPager/Etc" + SampleUI.Type.INNER_RECYCLER -> "Inner RecyclerView/ViewPager/Etc and how to save scroll state" } val onClickListener: (ClickItem) -> Unit = { item -> startActivity( diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt index 2e3b4c9..727718e 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt @@ -16,10 +16,11 @@ import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecycler2Cell -import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecyclerCell +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecycler1Cell import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecyclerItemCell import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerItemUI import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.stateholder.ScrollStatesHolder import kotlin.random.Random class InnerRecyclerActivity : AppCompatActivity() { @@ -69,6 +70,9 @@ class InnerRecyclerActivity : AppCompatActivity() { //should be inside ViewModel/UIMapper private class DataHolder(space: Int) { + private val scrollStatesHolder = ScrollStatesHolder() + private val recycledViewPool = RecyclerView.RecycledViewPool() + private val singleItemDecoration: ItemDecoration> private val firstItemDecoration: ItemDecoration> private val middleItemDecoration: ItemDecoration> @@ -103,6 +107,10 @@ class InnerRecyclerActivity : AppCompatActivity() { //should be inside ViewModel/UIMapper fun generateData(): List> { + // Cells do the same, but + // InnerRecycler1Cell - more code but more clearer and `correct` + // InnerRecycler2Cell - less code (can be less with `ViewBindingCell`) + val useClearerVersion = Random.nextBoolean() return (0..20).map { recyclerId -> val size = 10 val cells = (0..size).mapIndexed { index, itemId -> @@ -122,13 +130,12 @@ class InnerRecyclerActivity : AppCompatActivity() { } val recyclerUI = InnerRecyclerUI( id = recyclerId.toString(), - cells = cells + cells = cells, + recycledViewPool = recycledViewPool, + scrollStatesHolder = scrollStatesHolder ) - // Cells do the same, but - // InnerRecyclerCell - more code but more clearer and `correct` - // InnerRecycler2Cell - less code (can be less with `ViewBindingCell`) - if (Random.nextBoolean()) { - InnerRecyclerCell( + if (useClearerVersion) { + InnerRecycler1Cell( data = recyclerUI ) } else { diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerCell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt similarity index 73% rename from samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerCell.kt rename to samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt index ad5fb7d..a5f3805 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerCell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt @@ -9,25 +9,26 @@ import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.innerrecyclerview.R -import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecyclerListItemBinding +import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecycler1ListItemBinding import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.layoutmanager.PercentWidthLinearLayoutManager -data class InnerRecyclerCell( +// More code but more clearer and `correct` than `InnerRecycler2Cell` +data class InnerRecycler1Cell( override val data: InnerRecyclerUI, override val decoration: ItemDecoration>? = null, override val onClickListener: ((ClickItem) -> Unit)? = null ) : Cell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.inner_recycler_list_item + override val layoutId: Int = R.layout.inner_recycler_1_list_item override fun onCreateViewHolder( inflater: LayoutInflater, parent: ViewGroup, viewType: Int ): RecyclerView.ViewHolder { - return SampleViewHolder(InnerRecyclerListItemBinding.inflate(inflater, parent, false)) + return SampleViewHolder(InnerRecycler1ListItemBinding.inflate(inflater, parent, false)) } // This is the correct way to optimize bindings with payloads @@ -51,7 +52,12 @@ data class InnerRecyclerCell( } private fun submitData(holder: RecyclerView.ViewHolder) { - (holder as SampleViewHolder).submitList(data.cells) + (holder as SampleViewHolder).setData(data) + } + + override fun onViewRecycled(holder: RecyclerView.ViewHolder) { + super.onViewRecycled(holder) + (holder as SampleViewHolder).onViewRecycled(data) } // We do not need to animate the InnerRecyclerCell, because it has its own CompositeAdapter, @@ -64,7 +70,7 @@ data class InnerRecyclerCell( } private class SampleViewHolder( - binding: InnerRecyclerListItemBinding + private val binding: InnerRecycler1ListItemBinding ) : RecyclerView.ViewHolder(binding.root) { private val adapter = CompositeAdapter() @@ -77,8 +83,16 @@ data class InnerRecyclerCell( } } - fun submitList(cells: List>) { - adapter.submitList(cells) + fun setData(data: InnerRecyclerUI) { + if (binding.recyclerView.recycledViewPool != data.recycledViewPool) { + binding.recyclerView.setRecycledViewPool(data.recycledViewPool) + } + adapter.submitList(data.cells) + data.scrollStatesHolder.setupRecyclerView(data.id, binding.recyclerView) + } + + fun onViewRecycled(data: InnerRecyclerUI) { + data.scrollStatesHolder.onRecycled(data.id, binding.recyclerView) } } } \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt index 2df386d..9ffa5b5 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt @@ -9,10 +9,11 @@ import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.innerrecyclerview.R -import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecyclerListItemBinding +import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecycler2ListItemBinding import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.layoutmanager.PercentWidthLinearLayoutManager +// less code (can be less with `ViewBindingCell`) than `InnerRecycler1Cell` data class InnerRecycler2Cell( override val data: InnerRecyclerUI, override val decoration: ItemDecoration>? = null, @@ -20,7 +21,7 @@ data class InnerRecycler2Cell( ) : Cell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.inner_recycler_list_item + override val layoutId: Int = R.layout.inner_recycler_2_list_item override fun onCreateViewHolder( inflater: LayoutInflater, @@ -28,10 +29,11 @@ data class InnerRecycler2Cell( viewType: Int ): RecyclerView.ViewHolder { return SampleViewHolder( - InnerRecyclerListItemBinding.inflate(inflater, parent, false).also { binding -> + InnerRecycler2ListItemBinding.inflate(inflater, parent, false).also { binding -> // Don't forget that Cell can survive the configuration changes inside the ViewModel // or in some other way. So you MUST NOT store any link to - // View/ViewHolder/Fragment/Context/etc in the Cell otherwise it will be leaked. + // Adapter/View/ViewHolder/Fragment/Context/etc in the Cell + // otherwise it will be leaked. binding.recyclerView.adapter = CompositeAdapter() binding.recyclerView.layoutManager = PercentWidthLinearLayoutManager(binding.root.context) @@ -42,15 +44,27 @@ data class InnerRecycler2Cell( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { (holder as SampleViewHolder).binding.apply { + if (recyclerView.recycledViewPool != data.recycledViewPool) { + recyclerView.setRecycledViewPool(data.recycledViewPool) + } (recyclerView.adapter as CompositeAdapter).submitList(data.cells) + data.scrollStatesHolder.setupRecyclerView(uniqueId, holder.binding.recyclerView) } } + override fun onViewRecycled(holder: RecyclerView.ViewHolder) { + super.onViewRecycled(holder) + data.scrollStatesHolder.onRecycled( + uniqueId, + (holder as SampleViewHolder).binding.recyclerView + ) + } + // We do not need to animate the InnerRecyclerCell, because it has its own CompositeAdapter, // which will calculate the diffs of his cells and animate these changes within itself. // The same for the ViewPager1/ViewPager2/Webview/VideoPlayer/other complex view. // `onBindViewHolder` with `payload` will be called. But its empty so it will call - // `onBindViewHolder` without `payload` where we use submitList + // `onBindViewHolder` without `payload` where submitList is called override fun getChangePayload(newItem: Cell<*>): Any = RECYCLER_VIEW_PAYLOAD companion object { @@ -58,6 +72,6 @@ data class InnerRecycler2Cell( } private class SampleViewHolder( - val binding: InnerRecyclerListItemBinding + val binding: InnerRecycler2ListItemBinding ) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt index 66ce34b..e6b2c03 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt @@ -42,10 +42,6 @@ data class InnerRecyclerItemCell( } } - private class SampleViewHolder( - val binding: InnerRecyclerItemListItemBinding - ) : RecyclerView.ViewHolder(binding.root) - private fun Context.dpToPx(dp: Float): Float { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics) } @@ -64,4 +60,8 @@ data class InnerRecyclerItemCell( } clipToOutline = true } + + private class SampleViewHolder( + val binding: InnerRecyclerItemListItemBinding + ) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt index 9cae78f..559f5f1 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt @@ -1,8 +1,12 @@ package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity +import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.stateholder.ScrollStatesHolder data class InnerRecyclerUI( val id: String, - val cells: List> + val cells: List>, + val recycledViewPool: RecyclerView.RecycledViewPool, + val scrollStatesHolder: ScrollStatesHolder ) \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStateHolder.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStateHolder.kt new file mode 100644 index 0000000..edd19d1 --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStateHolder.kt @@ -0,0 +1,53 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.stateholder + +import android.os.Parcelable +import androidx.recyclerview.widget.RecyclerView + +class ScrollStateHolder { + + private var pendingScroll: Boolean = false + private var currentState: Parcelable? = null + + private val scrollListener = object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + saveScrollState(recyclerView) + } + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dx != 0) { + pendingScroll = true + } + } + } + + fun setupRecyclerView(recyclerView: RecyclerView) { + recyclerView.removeOnScrollListener(scrollListener) + restoreScrollState(recyclerView) + recyclerView.addOnScrollListener(scrollListener) + } + + fun onRecycled(recyclerView: RecyclerView) { + saveScrollState(recyclerView) + } + + private fun saveScrollState(recyclerView: RecyclerView) { + if (pendingScroll) { + pendingScroll = false + currentState = recyclerView.layoutManager!!.onSaveInstanceState() + } + } + + private fun restoreScrollState(recyclerView: RecyclerView) { + val layoutManager = recyclerView.layoutManager!! + val currentState = currentState + if (currentState == null) { + layoutManager.scrollToPosition(0) + } else { + layoutManager.onRestoreInstanceState(currentState) + } + } +} \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStatesHolder.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStatesHolder.kt new file mode 100644 index 0000000..b42c30e --- /dev/null +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStatesHolder.kt @@ -0,0 +1,22 @@ +package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.stateholder + +import androidx.recyclerview.widget.RecyclerView + +class ScrollStatesHolder { + + private val stateHolders = hashMapOf() + + fun setupRecyclerView(uniqueId: String, recyclerView: RecyclerView) { + getStateHolder(uniqueId).setupRecyclerView(recyclerView) + } + + fun onRecycled(uniqueId: String, recyclerView: RecyclerView) { + getStateHolder(uniqueId).onRecycled(recyclerView) + } + + private fun getStateHolder(uniqueId: String): ScrollStateHolder { + return stateHolders[uniqueId] ?: ScrollStateHolder().also { stateHolder -> + stateHolders[uniqueId] = stateHolder + } + } +} \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/res/layout/inner_recycler_list_item.xml b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_1_list_item.xml similarity index 100% rename from samples/inner-recyclerview/src/main/res/layout/inner_recycler_list_item.xml rename to samples/inner-recyclerview/src/main/res/layout/inner_recycler_1_list_item.xml diff --git a/samples/inner-recyclerview/src/main/res/layout/inner_recycler_2_list_item.xml b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_2_list_item.xml new file mode 100644 index 0000000..a6be088 --- /dev/null +++ b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_2_list_item.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file From 47e9f432eca7b6df5d13fdfe2d269441301d1adf Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Tue, 18 Jan 2022 12:07:32 +0300 Subject: [PATCH 09/19] feat(sample): remove holder/binding casts from the cells --- .../ui/DifferentBindingsActivity.kt | 4 +- .../ui/cell/base/BaseCell.kt | 75 +++++++++++++++++++ .../ui/cell/base/BaseViewHolder.kt | 6 ++ ...ifferentBindingsDataBinding1MessageCell.kt | 14 ++-- ...ifferentBindingsDataBinding2MessageCell.kt | 38 +++++++--- .../cell/databinding/base/DataBindingCell.kt | 19 ++--- .../databinding/base/DataBindingViewHolder.kt | 14 ++-- .../view/DifferentBindingsViewMessageCell.kt | 16 ++-- ...ifferentBindingsViewBinding1MessageCell.kt | 30 +++----- ...ifferentBindingsViewBinding2MessageCell.kt | 37 ++++++--- .../cell/viewbinding/base/ViewBindingCell.kt | 19 +++-- .../viewbinding/base/ViewBindingViewHolder.kt | 8 +- 12 files changed, 200 insertions(+), 80 deletions(-) create mode 100644 samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt create mode 100644 samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseViewHolder.kt diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt index e161ab5..97105d5 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/DifferentBindingsActivity.kt @@ -69,13 +69,13 @@ class DifferentBindingsActivity : AppCompatActivity() { listOf( DifferentBindingsViewBinding1MessageCell( data = DifferentBindingsUI( - name = "Cell with custom ViewHolder", + name = "Cell with default ViewHolder", type = type ) ), DifferentBindingsViewBinding2MessageCell( data = DifferentBindingsUI( - name = "Cell with default ViewHolder", + name = "Cell with custom ViewHolder", type = type ) ) diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt new file mode 100644 index 0000000..b2907e0 --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt @@ -0,0 +1,75 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem + +abstract class BaseCell : Cell { + + abstract fun createViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): VIEW_HOLDER + + fun onBindViewHolder( + holder: VIEW_HOLDER, + position: Int, + payloads: List + ): Boolean = false + + abstract fun onBindViewHolder(holder: VIEW_HOLDER, position: Int) + + fun onViewAttachedToWindow(holder: VIEW_HOLDER) = Unit + fun onViewDetachedFromWindow(holder: VIEW_HOLDER) = Unit + + fun onViewRecycled(holder: VIEW_HOLDER) = Unit + + fun onClicked(context: Context, holder: VIEW_HOLDER, position: Int) { + onClickListener?.invoke(ClickItem(context, holder, position, data)) + } + + final override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return createViewHolder(inflater, parent, viewType) + } + + final override fun onBindViewHolder( + holder: RecyclerView.ViewHolder, + position: Int, + payloads: List + ): Boolean { + return onBindViewHolder(castHolder(holder), position, payloads) + } + + final override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + onBindViewHolder(castHolder(holder), position) + } + + final override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) { + onViewAttachedToWindow(castHolder(holder)) + } + + final override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { + onViewDetachedFromWindow(castHolder(holder)) + } + + final override fun onViewRecycled(holder: RecyclerView.ViewHolder) { + onViewRecycled(castHolder(holder)) + } + + final override fun onClicked(context: Context, holder: RecyclerView.ViewHolder, position: Int) { + onClicked(context, castHolder(holder), position) + } + + protected fun castHolder(holder: RecyclerView.ViewHolder): VIEW_HOLDER { + @Suppress("UNCHECKED_CAST") + return holder as VIEW_HOLDER + } +} \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseViewHolder.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseViewHolder.kt new file mode 100644 index 0000000..9a5694f --- /dev/null +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseViewHolder.kt @@ -0,0 +1,6 @@ +package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base + +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +abstract class BaseViewHolder(root: View) : RecyclerView.ViewHolder(root) \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt index b9bdedd..f4027a7 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt @@ -4,7 +4,9 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R +import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsDataBinding1ListItemBinding import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI // ViewBinding is better anyway @@ -12,13 +14,15 @@ data class DifferentBindingsDataBinding1MessageCell( override val data: DifferentBindingsUI, override val decoration: ItemDecoration>? = null, override val onClickListener: ((ClickItem) -> Unit)? = null -) : DataBindingCell { +) : DataBindingCell() { override val uniqueId: String = data.type.name override val layoutId: Int = R.layout.different_bindings_data_binding_1_list_item - // We do not need `onBindViewHolder` because everything is done inside the DataBindingCell -// override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { -// super.onBindViewHolder(holder, position) -// } + override fun onBindViewHolder( + holder: DataBindingViewHolder, + position: Int + ) { + // Do nothing, because everything is done inside the DataBindingCell + } } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt index cb740ec..b63f2f3 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt @@ -1,13 +1,14 @@ package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding -import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.ViewGroup import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsDataBinding2ListItemBinding -import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingCell -import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingViewHolder +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI // ViewBinding is better anyway @@ -15,15 +16,34 @@ data class DifferentBindingsDataBinding2MessageCell( override val data: DifferentBindingsUI, override val decoration: ItemDecoration>? = null, override val onClickListener: ((ClickItem) -> Unit)? = null -) : DataBindingCell { +) : BaseCell() { override val uniqueId: String = data.type.name override val layoutId: Int = R.layout.different_bindings_data_binding_2_list_item - // But we can have a custom onBindViewHolder instead of custom BindingAdapter functions - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - ((holder as DataBindingViewHolder).bindings as DifferentBindingsDataBinding2ListItemBinding).apply { - text.text = data.name - } + override fun createViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): CustomViewHolder { + return CustomViewHolder( + DifferentBindingsDataBinding2ListItemBinding.inflate( + inflater, + parent, + false + ) + ) } + + override fun onBindViewHolder( + holder: CustomViewHolder, + position: Int + ) { + // But we can have a custom onBindViewHolder instead of custom BindingAdapter functions + holder.binding.text.text = data.name + } + + class CustomViewHolder( + val binding: DifferentBindingsDataBinding2ListItemBinding + ) : BaseViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingCell.kt index 6fe36cd..1feb35e 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingCell.kt @@ -2,25 +2,26 @@ package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.dat import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell +import androidx.databinding.ViewDataBinding import com.originsdigital.compositeadapter.sample.differentbindings.BR +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseCell // ViewBinding is better anyway -interface DataBindingCell : Cell { +abstract class DataBindingCell + : BaseCell>() { - override fun onCreateViewHolder( + final override fun createViewHolder( inflater: LayoutInflater, parent: ViewGroup, viewType: Int - ): RecyclerView.ViewHolder { + ): DataBindingViewHolder { return DataBindingViewHolder.create(inflater, layoutId, parent) } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder as DataBindingViewHolder).apply { - bindings.setVariable(BR.item, data) - bindings.executePendingBindings() + override fun onBindViewHolder(holder: DataBindingViewHolder, position: Int) { + (holder.binding).apply { + setVariable(BR.item, data) + executePendingBindings() } } } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingViewHolder.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingViewHolder.kt index 94a0de2..e8ebfbb 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingViewHolder.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/base/DataBindingViewHolder.kt @@ -4,26 +4,26 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding -import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseViewHolder // ViewBinding is better anyway -class DataBindingViewHolder( - val bindings: ViewDataBinding -) : RecyclerView.ViewHolder(bindings.root) { +class DataBindingViewHolder( + val binding: DATA_BINDING +) : BaseViewHolder(binding.root) { companion object { - fun create( + fun create( inflater: LayoutInflater, layoutResId: Int, parent: ViewGroup - ): DataBindingViewHolder { + ): DataBindingViewHolder { return DataBindingViewHolder( DataBindingUtil.inflate( inflater, layoutResId, parent, false - ) + ) as DATA_BINDING ) } } diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt index a915e3e..906bfeb 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt @@ -11,22 +11,24 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI data class DifferentBindingsViewMessageCell( override val data: DifferentBindingsUI, override val decoration: ItemDecoration>? = null, override val onClickListener: ((ClickItem) -> Unit)? = null -) : Cell { +) : BaseCell() { override val uniqueId: String = data.type.name override val layoutId: Int = R.layout.different_bindings_view_list_item - override fun onCreateViewHolder( + override fun createViewHolder( inflater: LayoutInflater, parent: ViewGroup, viewType: Int - ): RecyclerView.ViewHolder { + ): CustomViewHolder { val padding = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 20f, @@ -48,11 +50,9 @@ data class DifferentBindingsViewMessageCell( ) } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder as CustomViewHolder).apply { - text.text = data.name - } + override fun onBindViewHolder(holder: CustomViewHolder, position: Int) { + holder.text.text = data.name } - private class CustomViewHolder(val text: TextView) : RecyclerView.ViewHolder(text) + class CustomViewHolder(val text: TextView) : BaseViewHolder(text) } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt index 0ef1d6b..9342294 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt @@ -2,44 +2,36 @@ package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.vie import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsViewBinding1ListItemBinding +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base.ViewBindingCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base.ViewBindingViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI data class DifferentBindingsViewBinding1MessageCell( override val data: DifferentBindingsUI, override val decoration: ItemDecoration>? = null, override val onClickListener: ((ClickItem) -> Unit)? = null -) : Cell { +) : ViewBindingCell() { override val uniqueId: String = data.type.name override val layoutId: Int = R.layout.different_bindings_view_binding_1_list_item - override fun onCreateViewHolder( + override fun createViewBinding( inflater: LayoutInflater, parent: ViewGroup, viewType: Int - ): RecyclerView.ViewHolder { - return CustomViewHolder( - DifferentBindingsViewBinding1ListItemBinding.inflate( - inflater, - parent, - false - ) - ) + ): DifferentBindingsViewBinding1ListItemBinding { + return DifferentBindingsViewBinding1ListItemBinding.inflate(inflater, parent, false) } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder as CustomViewHolder).binding.apply { - text.text = data.name - } + override fun onBindViewHolder( + holder: ViewBindingViewHolder, + position: Int + ) { + holder.binding.text.text = data.name } - - private class CustomViewHolder( - val binding: DifferentBindingsViewBinding1ListItemBinding - ) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt index 1006306..66d18e7 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt @@ -1,31 +1,44 @@ package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding -import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.ViewGroup import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsViewBinding2ListItemBinding -import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base.ViewBindingCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseCell +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI -import com.originsdigital.compositeadapter.utils.getViewBinding data class DifferentBindingsViewBinding2MessageCell( override val data: DifferentBindingsUI, override val decoration: ItemDecoration>? = null, override val onClickListener: ((ClickItem) -> Unit)? = null -) : ViewBindingCell { +) : BaseCell() { override val uniqueId: String = data.type.name override val layoutId: Int = R.layout.different_bindings_view_binding_2_list_item - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - // Unfortunately the ViewBinding doesn't have a cache for bindings inside the root view tags - // like the DataBinding. To remove repeated findViewById calls, - // we can call the `getViewBinding` to bind and save the binding or get the saved binding - // Or we can have a custom ViewHolder for each Cell, see SampleViewBinding1MessageCell - (holder.getViewBinding(DifferentBindingsViewBinding2ListItemBinding::bind)).apply { - text.text = data.name - } + override fun createViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): CustomViewHolder { + return CustomViewHolder( + DifferentBindingsViewBinding2ListItemBinding.inflate( + inflater, + parent, + false + ) + ) } + + override fun onBindViewHolder(holder: CustomViewHolder, position: Int) { + holder.binding.text.text = data.name + } + + class CustomViewHolder( + val binding: DifferentBindingsViewBinding2ListItemBinding + ) : BaseViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingCell.kt index a54f7f6..f102e7d 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingCell.kt @@ -2,16 +2,23 @@ package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.vie import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell +import androidx.viewbinding.ViewBinding +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseCell -interface ViewBindingCell : Cell { +abstract class ViewBindingCell + : BaseCell>() { - override fun onCreateViewHolder( + abstract fun createViewBinding( inflater: LayoutInflater, parent: ViewGroup, viewType: Int - ): RecyclerView.ViewHolder { - return ViewBindingViewHolder(inflater.inflate(layoutId, parent, false)) + ): VIEW_BINDING + + final override fun createViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): ViewBindingViewHolder { + return ViewBindingViewHolder(createViewBinding(inflater, parent, viewType)) } } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingViewHolder.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingViewHolder.kt index a6fe330..fc787d8 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingViewHolder.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/base/ViewBindingViewHolder.kt @@ -1,6 +1,8 @@ package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base -import android.view.View -import androidx.recyclerview.widget.RecyclerView +import androidx.viewbinding.ViewBinding +import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseViewHolder -open class ViewBindingViewHolder(view: View) : RecyclerView.ViewHolder(view) \ No newline at end of file +class ViewBindingViewHolder( + val binding: VIEW_BINDING +) : BaseViewHolder(binding.root) \ No newline at end of file From 63fcb2128a65aca5aaca4c1403bf530b44c62f86 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Tue, 18 Jan 2022 13:45:21 +0300 Subject: [PATCH 10/19] feat(sample): rename list_item to cell --- .../sample/basic/ui/cell/SampleCell.kt | 8 ++++---- .../{sample_list_item.xml => sample_cell.xml} | 0 .../decorations/ui/cell/DecorationsCell.kt | 8 ++++---- .../src/main/res/layout/decorations_cell.xml} | 1 + ...ifferentBindingsDataBinding1MessageCell.kt | 20 +++++++++---------- ...ifferentBindingsDataBinding2MessageCell.kt | 8 ++++---- .../view/DifferentBindingsViewMessageCell.kt | 2 +- ...ifferentBindingsViewBinding1MessageCell.kt | 12 +++++------ ...ifferentBindingsViewBinding2MessageCell.kt | 8 ++++---- ...ifferent_bindings_data_binding_1_cell.xml} | 0 ...ifferent_bindings_data_binding_2_cell.xml} | 0 ...different_bindings_view_binding_1_cell.xml | 0 ...ifferent_bindings_view_binding_2_cell.xml} | 0 .../src/main/res/values/ids.xml | 2 +- .../ui/cell/InnerRecycler1Cell.kt | 8 ++++---- .../ui/cell/InnerRecycler2Cell.kt | 8 ++++---- .../ui/cell/InnerRecyclerItemCell.kt | 8 ++++---- ...ist_item.xml => inner_recycler_1_cell.xml} | 0 ...ist_item.xml => inner_recycler_2_cell.xml} | 0 ..._item.xml => inner_recycler_item_cell.xml} | 0 20 files changed, 47 insertions(+), 46 deletions(-) rename samples/basic/src/main/res/layout/{sample_list_item.xml => sample_cell.xml} (100%) rename samples/{different-bindings/src/main/res/layout/different_bindings_view_binding_1_list_item.xml => decorations/src/main/res/layout/decorations_cell.xml} (90%) rename samples/different-bindings/src/main/res/layout/{different_bindings_data_binding_1_list_item.xml => different_bindings_data_binding_1_cell.xml} (100%) rename samples/different-bindings/src/main/res/layout/{different_bindings_data_binding_2_list_item.xml => different_bindings_data_binding_2_cell.xml} (100%) rename sample/features/messages/ui/src/main/res/layout/message_viewbinding_list_item.xml => samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_cell.xml (100%) rename samples/{decorations/src/main/res/layout/decorations_list_item.xml => different-bindings/src/main/res/layout/different_bindings_view_binding_2_cell.xml} (100%) rename samples/inner-recyclerview/src/main/res/layout/{inner_recycler_1_list_item.xml => inner_recycler_1_cell.xml} (100%) rename samples/inner-recyclerview/src/main/res/layout/{inner_recycler_2_list_item.xml => inner_recycler_2_cell.xml} (100%) rename samples/inner-recyclerview/src/main/res/layout/{inner_recycler_item_list_item.xml => inner_recycler_item_cell.xml} (100%) diff --git a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/cell/SampleCell.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/cell/SampleCell.kt index 72c41e2..36858a3 100644 --- a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/cell/SampleCell.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/cell/SampleCell.kt @@ -7,7 +7,7 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.basic.R -import com.originsdigital.compositeadapter.sample.basic.databinding.SampleListItemBinding +import com.originsdigital.compositeadapter.sample.basic.databinding.SampleCellBinding import com.originsdigital.compositeadapter.sample.basic.ui.entity.SampleUI data class SampleCell( @@ -17,14 +17,14 @@ data class SampleCell( ) : Cell { override val uniqueId: String = data.type.name - override val layoutId: Int = R.layout.sample_list_item + override val layoutId: Int = R.layout.sample_cell override fun onCreateViewHolder( inflater: LayoutInflater, parent: ViewGroup, viewType: Int ): RecyclerView.ViewHolder { - return SampleViewHolder(SampleListItemBinding.inflate(inflater, parent, false)) + return SampleViewHolder(SampleCellBinding.inflate(inflater, parent, false)) } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { @@ -34,6 +34,6 @@ data class SampleCell( } private class SampleViewHolder( - val binding: SampleListItemBinding + val binding: SampleCellBinding ) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/basic/src/main/res/layout/sample_list_item.xml b/samples/basic/src/main/res/layout/sample_cell.xml similarity index 100% rename from samples/basic/src/main/res/layout/sample_list_item.xml rename to samples/basic/src/main/res/layout/sample_cell.xml diff --git a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/cell/DecorationsCell.kt b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/cell/DecorationsCell.kt index bebf4da..94f491f 100644 --- a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/cell/DecorationsCell.kt +++ b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/cell/DecorationsCell.kt @@ -7,7 +7,7 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.decorations.R -import com.originsdigital.compositeadapter.sample.decorations.databinding.DecorationsListItemBinding +import com.originsdigital.compositeadapter.sample.decorations.databinding.DecorationsCellBinding import com.originsdigital.compositeadapter.sample.decorations.ui.entity.DecorationsUI data class DecorationsCell( @@ -17,14 +17,14 @@ data class DecorationsCell( ) : Cell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.decorations_list_item + override val layoutId: Int = R.layout.decorations_cell override fun onCreateViewHolder( inflater: LayoutInflater, parent: ViewGroup, viewType: Int ): RecyclerView.ViewHolder { - return SampleViewHolder(DecorationsListItemBinding.inflate(inflater, parent, false)) + return SampleViewHolder(DecorationsCellBinding.inflate(inflater, parent, false)) } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { @@ -34,6 +34,6 @@ data class DecorationsCell( } private class SampleViewHolder( - val binding: DecorationsListItemBinding + val binding: DecorationsCellBinding ) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_list_item.xml b/samples/decorations/src/main/res/layout/decorations_cell.xml similarity index 90% rename from samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_list_item.xml rename to samples/decorations/src/main/res/layout/decorations_cell.xml index a383a7c..6cc3a92 100644 --- a/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_list_item.xml +++ b/samples/decorations/src/main/res/layout/decorations_cell.xml @@ -7,4 +7,5 @@ android:background="?selectableItemBackground" android:padding="20dp" android:textAppearance="@style/TextAppearance.AppCompat.Medium" + android:textColor="@android:color/white" tools:text="TEXT" /> \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt index f4027a7..d247b0d 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding1MessageCell.kt @@ -4,9 +4,8 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R -import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsDataBinding1ListItemBinding +import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsDataBinding1CellBinding import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingCell -import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.databinding.base.DataBindingViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI // ViewBinding is better anyway @@ -14,15 +13,16 @@ data class DifferentBindingsDataBinding1MessageCell( override val data: DifferentBindingsUI, override val decoration: ItemDecoration>? = null, override val onClickListener: ((ClickItem) -> Unit)? = null -) : DataBindingCell() { +) : DataBindingCell() { override val uniqueId: String = data.type.name - override val layoutId: Int = R.layout.different_bindings_data_binding_1_list_item + override val layoutId: Int = R.layout.different_bindings_data_binding_1_cell - override fun onBindViewHolder( - holder: DataBindingViewHolder, - position: Int - ) { - // Do nothing, because everything is done inside the DataBindingCell - } + // We do not need `onBindViewHolder` because everything is done inside the DataBindingCell +// override fun onBindViewHolder( +// holder: DataBindingViewHolder, +// position: Int +// ) { +// super.onBindViewHolder(holder, position) +// } } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt index b63f2f3..dea41c9 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/databinding/DifferentBindingsDataBinding2MessageCell.kt @@ -6,7 +6,7 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R -import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsDataBinding2ListItemBinding +import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsDataBinding2CellBinding import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseCell import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI @@ -19,7 +19,7 @@ data class DifferentBindingsDataBinding2MessageCell( ) : BaseCell() { override val uniqueId: String = data.type.name - override val layoutId: Int = R.layout.different_bindings_data_binding_2_list_item + override val layoutId: Int = R.layout.different_bindings_data_binding_2_cell override fun createViewHolder( inflater: LayoutInflater, @@ -27,7 +27,7 @@ data class DifferentBindingsDataBinding2MessageCell( viewType: Int ): CustomViewHolder { return CustomViewHolder( - DifferentBindingsDataBinding2ListItemBinding.inflate( + DifferentBindingsDataBinding2CellBinding.inflate( inflater, parent, false @@ -44,6 +44,6 @@ data class DifferentBindingsDataBinding2MessageCell( } class CustomViewHolder( - val binding: DifferentBindingsDataBinding2ListItemBinding + val binding: DifferentBindingsDataBinding2CellBinding ) : BaseViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt index 906bfeb..71707e1 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/view/DifferentBindingsViewMessageCell.kt @@ -22,7 +22,7 @@ data class DifferentBindingsViewMessageCell( ) : BaseCell() { override val uniqueId: String = data.type.name - override val layoutId: Int = R.layout.different_bindings_view_list_item + override val layoutId: Int = R.layout.different_bindings_view_cell override fun createViewHolder( inflater: LayoutInflater, diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt index 9342294..418a885 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding1MessageCell.kt @@ -6,7 +6,7 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R -import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsViewBinding1ListItemBinding +import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsViewBinding1CellBinding import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base.ViewBindingCell import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.viewbinding.base.ViewBindingViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI @@ -15,21 +15,21 @@ data class DifferentBindingsViewBinding1MessageCell( override val data: DifferentBindingsUI, override val decoration: ItemDecoration>? = null, override val onClickListener: ((ClickItem) -> Unit)? = null -) : ViewBindingCell() { +) : ViewBindingCell() { override val uniqueId: String = data.type.name - override val layoutId: Int = R.layout.different_bindings_view_binding_1_list_item + override val layoutId: Int = R.layout.different_bindings_view_binding_1_cell override fun createViewBinding( inflater: LayoutInflater, parent: ViewGroup, viewType: Int - ): DifferentBindingsViewBinding1ListItemBinding { - return DifferentBindingsViewBinding1ListItemBinding.inflate(inflater, parent, false) + ): DifferentBindingsViewBinding1CellBinding { + return DifferentBindingsViewBinding1CellBinding.inflate(inflater, parent, false) } override fun onBindViewHolder( - holder: ViewBindingViewHolder, + holder: ViewBindingViewHolder, position: Int ) { holder.binding.text.text = data.name diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt index 66d18e7..85a0ea2 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/viewbinding/DifferentBindingsViewBinding2MessageCell.kt @@ -6,7 +6,7 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.differentbindings.R -import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsViewBinding2ListItemBinding +import com.originsdigital.compositeadapter.sample.differentbindings.databinding.DifferentBindingsViewBinding2CellBinding import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseCell import com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base.BaseViewHolder import com.originsdigital.compositeadapter.sample.differentbindings.ui.entity.DifferentBindingsUI @@ -18,7 +18,7 @@ data class DifferentBindingsViewBinding2MessageCell( ) : BaseCell() { override val uniqueId: String = data.type.name - override val layoutId: Int = R.layout.different_bindings_view_binding_2_list_item + override val layoutId: Int = R.layout.different_bindings_view_binding_2_cell override fun createViewHolder( inflater: LayoutInflater, @@ -26,7 +26,7 @@ data class DifferentBindingsViewBinding2MessageCell( viewType: Int ): CustomViewHolder { return CustomViewHolder( - DifferentBindingsViewBinding2ListItemBinding.inflate( + DifferentBindingsViewBinding2CellBinding.inflate( inflater, parent, false @@ -39,6 +39,6 @@ data class DifferentBindingsViewBinding2MessageCell( } class CustomViewHolder( - val binding: DifferentBindingsViewBinding2ListItemBinding + val binding: DifferentBindingsViewBinding2CellBinding ) : BaseViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_1_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_1_cell.xml similarity index 100% rename from samples/different-bindings/src/main/res/layout/different_bindings_data_binding_1_list_item.xml rename to samples/different-bindings/src/main/res/layout/different_bindings_data_binding_1_cell.xml diff --git a/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_2_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_data_binding_2_cell.xml similarity index 100% rename from samples/different-bindings/src/main/res/layout/different_bindings_data_binding_2_list_item.xml rename to samples/different-bindings/src/main/res/layout/different_bindings_data_binding_2_cell.xml diff --git a/sample/features/messages/ui/src/main/res/layout/message_viewbinding_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_cell.xml similarity index 100% rename from sample/features/messages/ui/src/main/res/layout/message_viewbinding_list_item.xml rename to samples/different-bindings/src/main/res/layout/different_bindings_view_binding_1_cell.xml diff --git a/samples/decorations/src/main/res/layout/decorations_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_cell.xml similarity index 100% rename from samples/decorations/src/main/res/layout/decorations_list_item.xml rename to samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_cell.xml diff --git a/samples/different-bindings/src/main/res/values/ids.xml b/samples/different-bindings/src/main/res/values/ids.xml index 7462663..a3ad6fc 100644 --- a/samples/different-bindings/src/main/res/values/ids.xml +++ b/samples/different-bindings/src/main/res/values/ids.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt index a5f3805..4dc81a9 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt @@ -9,7 +9,7 @@ import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.innerrecyclerview.R -import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecycler1ListItemBinding +import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecycler1CellBinding import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.layoutmanager.PercentWidthLinearLayoutManager @@ -21,14 +21,14 @@ data class InnerRecycler1Cell( ) : Cell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.inner_recycler_1_list_item + override val layoutId: Int = R.layout.inner_recycler_1_cell override fun onCreateViewHolder( inflater: LayoutInflater, parent: ViewGroup, viewType: Int ): RecyclerView.ViewHolder { - return SampleViewHolder(InnerRecycler1ListItemBinding.inflate(inflater, parent, false)) + return SampleViewHolder(InnerRecycler1CellBinding.inflate(inflater, parent, false)) } // This is the correct way to optimize bindings with payloads @@ -70,7 +70,7 @@ data class InnerRecycler1Cell( } private class SampleViewHolder( - private val binding: InnerRecycler1ListItemBinding + private val binding: InnerRecycler1CellBinding ) : RecyclerView.ViewHolder(binding.root) { private val adapter = CompositeAdapter() diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt index 9ffa5b5..6a69986 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt @@ -9,7 +9,7 @@ import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.innerrecyclerview.R -import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecycler2ListItemBinding +import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecycler2CellBinding import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.layoutmanager.PercentWidthLinearLayoutManager @@ -21,7 +21,7 @@ data class InnerRecycler2Cell( ) : Cell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.inner_recycler_2_list_item + override val layoutId: Int = R.layout.inner_recycler_2_cell override fun onCreateViewHolder( inflater: LayoutInflater, @@ -29,7 +29,7 @@ data class InnerRecycler2Cell( viewType: Int ): RecyclerView.ViewHolder { return SampleViewHolder( - InnerRecycler2ListItemBinding.inflate(inflater, parent, false).also { binding -> + InnerRecycler2CellBinding.inflate(inflater, parent, false).also { binding -> // Don't forget that Cell can survive the configuration changes inside the ViewModel // or in some other way. So you MUST NOT store any link to // Adapter/View/ViewHolder/Fragment/Context/etc in the Cell @@ -72,6 +72,6 @@ data class InnerRecycler2Cell( } private class SampleViewHolder( - val binding: InnerRecycler2ListItemBinding + val binding: InnerRecycler2CellBinding ) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt index e6b2c03..e3ea1f3 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecyclerItemCell.kt @@ -12,7 +12,7 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.sample.innerrecyclerview.R -import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecyclerItemListItemBinding +import com.originsdigital.compositeadapter.sample.innerrecyclerview.databinding.InnerRecyclerItemCellBinding import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerItemUI data class InnerRecyclerItemCell( @@ -22,7 +22,7 @@ data class InnerRecyclerItemCell( ) : Cell { override val uniqueId: String = data.id - override val layoutId: Int = R.layout.inner_recycler_item_list_item + override val layoutId: Int = R.layout.inner_recycler_item_cell override fun onCreateViewHolder( inflater: LayoutInflater, @@ -30,7 +30,7 @@ data class InnerRecyclerItemCell( viewType: Int ): RecyclerView.ViewHolder { return SampleViewHolder( - InnerRecyclerItemListItemBinding.inflate(inflater, parent, false).also { holder -> + InnerRecyclerItemCellBinding.inflate(inflater, parent, false).also { holder -> holder.root.applyRoundCorners(holder.root.context.dpToPx(6f)) } ) @@ -62,6 +62,6 @@ data class InnerRecyclerItemCell( } private class SampleViewHolder( - val binding: InnerRecyclerItemListItemBinding + val binding: InnerRecyclerItemCellBinding ) : RecyclerView.ViewHolder(binding.root) } \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/res/layout/inner_recycler_1_list_item.xml b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_1_cell.xml similarity index 100% rename from samples/inner-recyclerview/src/main/res/layout/inner_recycler_1_list_item.xml rename to samples/inner-recyclerview/src/main/res/layout/inner_recycler_1_cell.xml diff --git a/samples/inner-recyclerview/src/main/res/layout/inner_recycler_2_list_item.xml b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_2_cell.xml similarity index 100% rename from samples/inner-recyclerview/src/main/res/layout/inner_recycler_2_list_item.xml rename to samples/inner-recyclerview/src/main/res/layout/inner_recycler_2_cell.xml diff --git a/samples/inner-recyclerview/src/main/res/layout/inner_recycler_item_list_item.xml b/samples/inner-recyclerview/src/main/res/layout/inner_recycler_item_cell.xml similarity index 100% rename from samples/inner-recyclerview/src/main/res/layout/inner_recycler_item_list_item.xml rename to samples/inner-recyclerview/src/main/res/layout/inner_recycler_item_cell.xml From 5eead1591470eea32665e84dd3a2a3100e2f1af7 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Tue, 18 Jan 2022 14:30:11 +0300 Subject: [PATCH 11/19] feat(sample): remove unused onRecycled method from ScrollStateHolder --- .../innerrecyclerview/ui/cell/InnerRecycler1Cell.kt | 9 --------- .../innerrecyclerview/ui/cell/InnerRecycler2Cell.kt | 8 -------- .../ui/stateholder/ScrollStateHolder.kt | 4 ---- .../ui/stateholder/ScrollStatesHolder.kt | 4 ---- 4 files changed, 25 deletions(-) diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt index 4dc81a9..eda1878 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt @@ -55,11 +55,6 @@ data class InnerRecycler1Cell( (holder as SampleViewHolder).setData(data) } - override fun onViewRecycled(holder: RecyclerView.ViewHolder) { - super.onViewRecycled(holder) - (holder as SampleViewHolder).onViewRecycled(data) - } - // We do not need to animate the InnerRecyclerCell, because it has its own CompositeAdapter, // which will calculate the diffs of his cells and animate these changes within itself. // The same for the ViewPager1/ViewPager2/Webview/VideoPlayer/other complex view. @@ -90,9 +85,5 @@ data class InnerRecycler1Cell( adapter.submitList(data.cells) data.scrollStatesHolder.setupRecyclerView(data.id, binding.recyclerView) } - - fun onViewRecycled(data: InnerRecyclerUI) { - data.scrollStatesHolder.onRecycled(data.id, binding.recyclerView) - } } } \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt index 6a69986..4384284 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt @@ -52,14 +52,6 @@ data class InnerRecycler2Cell( } } - override fun onViewRecycled(holder: RecyclerView.ViewHolder) { - super.onViewRecycled(holder) - data.scrollStatesHolder.onRecycled( - uniqueId, - (holder as SampleViewHolder).binding.recyclerView - ) - } - // We do not need to animate the InnerRecyclerCell, because it has its own CompositeAdapter, // which will calculate the diffs of his cells and animate these changes within itself. // The same for the ViewPager1/ViewPager2/Webview/VideoPlayer/other complex view. diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStateHolder.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStateHolder.kt index edd19d1..e124730 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStateHolder.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStateHolder.kt @@ -30,10 +30,6 @@ class ScrollStateHolder { recyclerView.addOnScrollListener(scrollListener) } - fun onRecycled(recyclerView: RecyclerView) { - saveScrollState(recyclerView) - } - private fun saveScrollState(recyclerView: RecyclerView) { if (pendingScroll) { pendingScroll = false diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStatesHolder.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStatesHolder.kt index b42c30e..7688d89 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStatesHolder.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/stateholder/ScrollStatesHolder.kt @@ -10,10 +10,6 @@ class ScrollStatesHolder { getStateHolder(uniqueId).setupRecyclerView(recyclerView) } - fun onRecycled(uniqueId: String, recyclerView: RecyclerView) { - getStateHolder(uniqueId).onRecycled(recyclerView) - } - private fun getStateHolder(uniqueId: String): ScrollStateHolder { return stateHolders[uniqueId] ?: ScrollStateHolder().also { stateHolder -> stateHolders[uniqueId] = stateHolder From 5a2e4c15ad69cca2f48dbc5937e4d846bd2275d6 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Wed, 19 Jan 2022 16:40:51 +0300 Subject: [PATCH 12/19] feat(sample): add sample how to map LoadState in List of Cells --- buildSrc/src/main/kotlin/Deps.kt | 16 +- .../ui/cell/CommonFullLoaderCell.kt | 15 -- .../ui/cell/databinding/DataBindingCell.kt | 49 ----- .../ui/cell/viewbinding/CellViewHolder.kt | 6 - .../ui/cell/viewbinding/ViewBindingCell.kt | 13 -- .../ui/vm/BaseDataViewModel.kt | 17 -- .../res/layout/fake_databinding_layout.xml | 16 -- .../messages/core/entity/MessageEntity.kt | 11 -- .../data/src/main/AndroidManifest.xml | 2 - .../messages/ui/src/main/AndroidManifest.xml | 2 - .../compositeadapter/messages/ui/SampleDI.kt | 17 -- .../messages/ui/SampleMapper.kt | 178 ------------------ .../messages/ui/SampleState.kt | 22 --- .../messages/ui/SampleViewModel.kt | 145 -------------- .../SampleDataBindingMessageCell.kt | 18 -- .../ui/cell/view/SampleViewMessageCell.kt | 50 ----- .../SampleViewBindingMessageCell.kt | 27 --- .../layout/message_databinding_list_item.xml | 21 --- samples/basic/build.gradle.kts | 1 + samples/basic/src/main/AndroidManifest.xml | 4 + .../sample/basic/ui/SamplesActivity.kt | 5 + .../sample/basic/ui/SamplesApplication.kt | 10 +- .../sample/basic/ui/entity/SampleUI.kt | 2 +- ...rent_bindings_view_binding_2_list_item.xml | 10 - .../ui/InnerRecyclerActivity.kt | 2 +- .../state-as-cells/app}/.gitignore | 0 samples/state-as-cells/app/build.gradle.kts | 36 ++++ .../state-as-cells/app}/consumer-rules.pro | 0 .../state-as-cells/app}/proguard-rules.pro | 0 .../app/src/main/AndroidManifest.xml | 2 + .../stateascells/app/AppLoggerImpl.kt | 31 +++ .../stateascells/app/StateAsCellsDI.kt | 63 +++++++ .../features/base/core}/.gitignore | 0 .../features/base/core/build.gradle.kts | 0 .../compositeadapter/core/AppLogger.kt | 0 .../compositeadapter/core/di/CoreModule.kt | 0 .../compositeadapter/core/entity/Scene.kt | 0 .../core/extensions/ExceptionsExtensions.kt | 8 +- .../core/extensions/SceneExtensions.kt | 0 .../compositeadapter/core/utils/Loading.kt | 2 +- .../features/base/ui}/.gitignore | 0 .../features/base/ui/build.gradle.kts | 6 +- .../features/base/ui}/consumer-rules.pro | 0 .../features/base/ui}/proguard-rules.pro | 0 .../base/ui/src/main/AndroidManifest.xml | 0 .../ui/cell/CommonErrorCell.kt | 22 ++- .../ui/cell/CommonFullEmptyCell.kt | 22 ++- .../ui/cell/CommonFullErrorCell.kt | 22 ++- .../ui/cell/CommonFullLoaderCell.kt | 29 +++ .../ui/cell/CommonLoaderCell.kt | 21 ++- .../compositeadapter/ui/cell/base/BaseCell.kt | 75 ++++++++ .../ui/cell/base/BaseViewHolder.kt | 6 + .../ui/cell/viewbinding/ViewBindingCell.kt | 24 +++ .../cell/viewbinding/ViewBindingViewHolder.kt | 8 + .../compositeadapter/ui/di/UIModule.kt | 0 .../ui/entity/CommonErrorUI.kt | 0 .../ui/entity/CommonLoaderUI.kt | 0 .../ui/extensions/CellExtensions.kt | 6 +- .../ui/fragment/BaseFragment.kt | 0 .../ui/fragment/SimpleRecyclerFragment.kt | 0 .../ui/mapper/ErrorUIMapper.kt | 27 ++- .../ui/mapper/LoaderUIMapper.kt | 0 .../compositeadapter/ui/mapper/SceneMapper.kt | 0 .../ui/utils/CompositeAdapterUtils.kt | 10 +- .../compositeadapter/ui/utils/FlowUtils.kt | 0 .../ui/utils/RecyclerUtils.kt | 0 .../ui/utils/SwipeRefreshUtils.kt | 0 .../ui/utils/ViewModelUtils.kt | 0 .../ui/vm/BaseDataViewModel.kt | 6 + .../ui/vm/BaseStateViewModel.kt | 6 +- .../compositeadapter/ui/vm/BaseViewModel.kt | 0 .../src/main/res/layout/common_error_cell.xml | 0 .../res/layout/common_full_empty_cell.xml | 0 .../res/layout/common_full_error_cell.xml | 0 .../res/layout/common_full_loader_cell.xml | 0 .../main/res/layout/common_loader_cell.xml | 0 .../res/layout/common_recycler_fragment.xml | 0 .../base/ui/src/main/res/values/colors.xml | 2 +- .../base/ui/src/main/res/values/strings.xml | 0 .../features/home/core}/.gitignore | 0 .../features/home/core/build.gradle.kts | 19 ++ .../features/home/core}/consumer-rules.pro | 0 .../features/home/core}/proguard-rules.pro | 0 .../home/core/repository/HomeRepository.kt | 11 ++ .../features/home/data}/.gitignore | 0 .../features/home}/data/build.gradle.kts | 6 +- .../features/home/data}/consumer-rules.pro | 0 .../features/home/data}/proguard-rules.pro | 0 .../home/data/src/main/AndroidManifest.xml | 2 + .../stories/data/di/HomeDataModule.kt | 12 ++ .../data/repository/HomeRepositoryImpl.kt | 51 +++++ .../features/home/ui/.gitignore | 1 + .../features/home/ui/build.gradle.kts | 34 ++++ .../features/home/ui/consumer-rules.pro | 0 .../features/home/ui/proguard-rules.pro | 21 +++ .../home/ui/src/main/AndroidManifest.xml | 2 + .../stateascells/home/ui/HomeActivity.kt | 44 +++++ .../stateascells/home/ui/HomeUIMapper.kt | 131 +++++++++++++ .../stateascells/home/ui/HomeViewModel.kt | 88 +++++++++ .../stateascells/home/ui/di/HomeUIModule.kt | 25 +++ .../ui/src/main/res/layout/home_activity.xml | 11 ++ .../features/news/core/.gitignore | 1 + .../features/news}/core/build.gradle.kts | 0 .../features/news/core/consumer-rules.pro | 0 .../features/news/core/proguard-rules.pro | 21 +++ .../news/core/entity/NewsEntity.kt | 6 + .../features/news/ui/.gitignore | 1 + .../features/news}/ui/build.gradle.kts | 6 +- .../features/news/ui/consumer-rules.pro | 0 .../features/news/ui/proguard-rules.pro | 21 +++ .../news/ui/src/main/AndroidManifest.xml | 2 + .../compositeadapter/news/ui/cell/NewsCell.kt | 37 ++++ .../news/ui/src/main/res/layout/news_cell.xml | 17 ++ .../features/stories/core/.gitignore | 1 + .../features/stories/core/build.gradle.kts | 17 ++ .../features/stories/core/consumer-rules.pro | 0 .../features/stories/core/proguard-rules.pro | 21 +++ .../stories/core/entity/StoryEntity.kt | 7 + .../features/stories/ui/.gitignore | 1 + .../features/stories/ui/build.gradle.kts | 30 +++ .../features/stories/ui/consumer-rules.pro | 0 .../features/stories/ui/proguard-rules.pro | 21 +++ .../stories/ui/src/main/AndroidManifest.xml | 2 + .../stories/ui/cell/StoriesCell.kt | 63 +++++++ .../stories/ui/cell/StoryCell.kt | 40 ++++ .../drawable/story_cell_image_background.xml | 5 + .../ui/src/main/res/layout/story_cell.xml | 25 +++ .../stories}/ui/src/main/res/values/ids.xml | 2 +- settings.gradle.kts | 19 +- 129 files changed, 1229 insertions(+), 688 deletions(-) delete mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt delete mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/databinding/DataBindingCell.kt delete mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/CellViewHolder.kt delete mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt delete mode 100644 sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt delete mode 100644 sample/features/base/ui/src/main/res/layout/fake_databinding_layout.xml delete mode 100644 sample/features/messages/core/src/main/java/com/originsdigital/compositeadapter/messages/core/entity/MessageEntity.kt delete mode 100644 sample/features/messages/data/src/main/AndroidManifest.xml delete mode 100644 sample/features/messages/ui/src/main/AndroidManifest.xml delete mode 100644 sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleDI.kt delete mode 100644 sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleMapper.kt delete mode 100644 sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleState.kt delete mode 100644 sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleViewModel.kt delete mode 100644 sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/databinding/SampleDataBindingMessageCell.kt delete mode 100644 sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/view/SampleViewMessageCell.kt delete mode 100644 sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/viewbinding/SampleViewBindingMessageCell.kt delete mode 100644 sample/features/messages/ui/src/main/res/layout/message_databinding_list_item.xml delete mode 100644 samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_list_item.xml rename {sample/features/base/core => samples/state-as-cells/app}/.gitignore (100%) create mode 100644 samples/state-as-cells/app/build.gradle.kts rename {sample/features/base/ui => samples/state-as-cells/app}/consumer-rules.pro (100%) rename {sample/features/base/ui => samples/state-as-cells/app}/proguard-rules.pro (100%) create mode 100644 samples/state-as-cells/app/src/main/AndroidManifest.xml create mode 100644 samples/state-as-cells/app/src/main/java/com/originsdigital/compositeadapter/stateascells/app/AppLoggerImpl.kt create mode 100644 samples/state-as-cells/app/src/main/java/com/originsdigital/compositeadapter/stateascells/app/StateAsCellsDI.kt rename {sample/features/base/ui => samples/state-as-cells/features/base/core}/.gitignore (100%) rename {sample => samples/state-as-cells}/features/base/core/build.gradle.kts (100%) rename {sample => samples/state-as-cells}/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt (100%) rename {sample => samples/state-as-cells}/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt (100%) rename {sample => samples/state-as-cells}/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt (100%) rename {sample => samples/state-as-cells}/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt (76%) rename {sample => samples/state-as-cells}/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt (100%) rename {sample => samples/state-as-cells}/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt (100%) rename {sample/features/messages/core => samples/state-as-cells/features/base/ui}/.gitignore (100%) rename {sample => samples/state-as-cells}/features/base/ui/build.gradle.kts (87%) rename {sample/features/messages/core => samples/state-as-cells/features/base/ui}/consumer-rules.pro (100%) rename {sample/features/messages/core => samples/state-as-cells/features/base/ui}/proguard-rules.pro (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/AndroidManifest.xml (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt (63%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt (63%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt (63%) create mode 100644 samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt (67%) create mode 100644 samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt create mode 100644 samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseViewHolder.kt create mode 100644 samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt create mode 100644 samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingViewHolder.kt rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt (52%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt (60%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt (84%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt (100%) create mode 100644 samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt (96%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/res/layout/common_error_cell.xml (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/res/layout/common_full_empty_cell.xml (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/res/layout/common_full_error_cell.xml (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/res/layout/common_full_loader_cell.xml (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/res/layout/common_loader_cell.xml (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/res/layout/common_recycler_fragment.xml (100%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/res/values/colors.xml (99%) rename {sample => samples/state-as-cells}/features/base/ui/src/main/res/values/strings.xml (100%) rename {sample/features/messages/data => samples/state-as-cells/features/home/core}/.gitignore (100%) create mode 100644 samples/state-as-cells/features/home/core/build.gradle.kts rename {sample/features/messages/data => samples/state-as-cells/features/home/core}/consumer-rules.pro (100%) rename {sample/features/messages/data => samples/state-as-cells/features/home/core}/proguard-rules.pro (100%) create mode 100644 samples/state-as-cells/features/home/core/src/main/java/com/originsdigital/compositeadapter/home/core/repository/HomeRepository.kt rename {sample/features/messages/ui => samples/state-as-cells/features/home/data}/.gitignore (100%) rename {sample/features/messages => samples/state-as-cells/features/home}/data/build.gradle.kts (70%) rename {sample/features/messages/ui => samples/state-as-cells/features/home/data}/consumer-rules.pro (100%) rename {sample/features/messages/ui => samples/state-as-cells/features/home/data}/proguard-rules.pro (100%) create mode 100644 samples/state-as-cells/features/home/data/src/main/AndroidManifest.xml create mode 100644 samples/state-as-cells/features/home/data/src/main/java/com/originsdigital/compositeadapter/stories/data/di/HomeDataModule.kt create mode 100644 samples/state-as-cells/features/home/data/src/main/java/com/originsdigital/compositeadapter/stories/data/repository/HomeRepositoryImpl.kt create mode 100644 samples/state-as-cells/features/home/ui/.gitignore create mode 100644 samples/state-as-cells/features/home/ui/build.gradle.kts create mode 100644 samples/state-as-cells/features/home/ui/consumer-rules.pro create mode 100644 samples/state-as-cells/features/home/ui/proguard-rules.pro create mode 100644 samples/state-as-cells/features/home/ui/src/main/AndroidManifest.xml create mode 100644 samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeActivity.kt create mode 100644 samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeUIMapper.kt create mode 100644 samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeViewModel.kt create mode 100644 samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/di/HomeUIModule.kt create mode 100644 samples/state-as-cells/features/home/ui/src/main/res/layout/home_activity.xml create mode 100644 samples/state-as-cells/features/news/core/.gitignore rename {sample/features/messages => samples/state-as-cells/features/news}/core/build.gradle.kts (100%) create mode 100644 samples/state-as-cells/features/news/core/consumer-rules.pro create mode 100644 samples/state-as-cells/features/news/core/proguard-rules.pro create mode 100644 samples/state-as-cells/features/news/core/src/main/java/com/originsdigital/compositeadapter/news/core/entity/NewsEntity.kt create mode 100644 samples/state-as-cells/features/news/ui/.gitignore rename {sample/features/messages => samples/state-as-cells/features/news}/ui/build.gradle.kts (78%) create mode 100644 samples/state-as-cells/features/news/ui/consumer-rules.pro create mode 100644 samples/state-as-cells/features/news/ui/proguard-rules.pro create mode 100644 samples/state-as-cells/features/news/ui/src/main/AndroidManifest.xml create mode 100644 samples/state-as-cells/features/news/ui/src/main/java/com/originsdigital/compositeadapter/news/ui/cell/NewsCell.kt create mode 100644 samples/state-as-cells/features/news/ui/src/main/res/layout/news_cell.xml create mode 100644 samples/state-as-cells/features/stories/core/.gitignore create mode 100644 samples/state-as-cells/features/stories/core/build.gradle.kts create mode 100644 samples/state-as-cells/features/stories/core/consumer-rules.pro create mode 100644 samples/state-as-cells/features/stories/core/proguard-rules.pro create mode 100644 samples/state-as-cells/features/stories/core/src/main/java/com/originsdigital/compositeadapter/stories/core/entity/StoryEntity.kt create mode 100644 samples/state-as-cells/features/stories/ui/.gitignore create mode 100644 samples/state-as-cells/features/stories/ui/build.gradle.kts create mode 100644 samples/state-as-cells/features/stories/ui/consumer-rules.pro create mode 100644 samples/state-as-cells/features/stories/ui/proguard-rules.pro create mode 100644 samples/state-as-cells/features/stories/ui/src/main/AndroidManifest.xml create mode 100644 samples/state-as-cells/features/stories/ui/src/main/java/com/originsdigital/compositeadapter/stories/ui/cell/StoriesCell.kt create mode 100644 samples/state-as-cells/features/stories/ui/src/main/java/com/originsdigital/compositeadapter/stories/ui/cell/StoryCell.kt create mode 100644 samples/state-as-cells/features/stories/ui/src/main/res/drawable/story_cell_image_background.xml create mode 100644 samples/state-as-cells/features/stories/ui/src/main/res/layout/story_cell.xml rename {sample/features/messages => samples/state-as-cells/features/stories}/ui/src/main/res/values/ids.xml (52%) diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index a3c4c4b..3692569 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -80,12 +80,16 @@ object Config { const val differentBindings = ":samples:different-bindings" const val innerRecyclerview = ":samples:inner-recyclerview" - const val baseCore = ":sample:features:base:core" - const val baseUI = ":sample:features:base:ui" - - const val messagesData = ":sample:features:messages:data" - const val messagesCore = ":sample:features:messages:core" - const val messagesUI = ":sample:features:messages:ui" + const val stateAsCells = ":samples:state-as-cells:app" + const val baseCore = ":samples:state-as-cells:features:base:core" + const val baseUI = ":samples:state-as-cells:features:base:ui" + const val homeData = ":samples:state-as-cells:features:home:data" + const val homeCore = ":samples:state-as-cells:features:home:core" + const val homeUI = ":samples:state-as-cells:features:home:ui" + const val storiesCore = ":samples:state-as-cells:features:stories:core" + const val storiesUI = ":samples:state-as-cells:features:stories:ui" + const val newsCore = ":samples:state-as-cells:features:news:core" + const val newsUI = ":samples:state-as-cells:features:news:ui" } } } \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt deleted file mode 100644 index af6f11c..0000000 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.originsdigital.compositeadapter.ui.cell - -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.ui.R -import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell - -data class CommonFullLoaderCell( - override val data: Any = Unit -) : ViewBindingCell { - - override val uniqueId = "CommonFullLoaderCell" - override val layoutId = R.layout.common_full_loader_cell - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) = Unit -} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/databinding/DataBindingCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/databinding/DataBindingCell.kt deleted file mode 100644 index 265ec2e..0000000 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/databinding/DataBindingCell.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.originsdigital.compositeadapter.ui.cell.databinding - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.ui.BR - -interface DataBindingCell : Cell { - - override fun onCreateViewHolder( - inflater: LayoutInflater, - parent: ViewGroup, - viewType: Int - ): RecyclerView.ViewHolder { - return DataBindingViewHolder.create(inflater, layoutId, parent) - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder as DataBindingViewHolder).apply { - bindings.setVariable(BR.item, data) - bindings.executePendingBindings() - } - } - - class DataBindingViewHolder( - val bindings: ViewDataBinding - ) : RecyclerView.ViewHolder(bindings.root) { - - companion object { - fun create( - inflater: LayoutInflater, - layoutResId: Int, - parent: ViewGroup - ): DataBindingViewHolder { - return DataBindingViewHolder( - DataBindingUtil.inflate( - inflater, - layoutResId, - parent, - false - ) - ) - } - } - } -} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/CellViewHolder.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/CellViewHolder.kt deleted file mode 100644 index 5322714..0000000 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/CellViewHolder.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.originsdigital.compositeadapter.ui.cell.viewbinding - -import android.view.View -import androidx.recyclerview.widget.RecyclerView - -open class CellViewHolder(view: View) : RecyclerView.ViewHolder(view) \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt deleted file mode 100644 index a3b10f3..0000000 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.originsdigital.compositeadapter.ui.cell.viewbinding - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell - -interface ViewBindingCell : Cell { - - override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return CellViewHolder(inflater.inflate(layoutId, parent, false)) - } -} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt b/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt deleted file mode 100644 index 8d676ff..0000000 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.originsdigital.compositeadapter.ui.vm - -import kotlinx.coroutines.Job - -abstract class BaseDataViewModel : BaseViewModel() { - - open fun onRefresh() = loadData(force = false) - - protected abstract fun getData(force: Boolean): Job? - - protected open fun loadData(force: Boolean) { - job?.cancel() - job = getData(force) - } - - protected fun firstLoad() = loadData(force = true) -} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/res/layout/fake_databinding_layout.xml b/sample/features/base/ui/src/main/res/layout/fake_databinding_layout.xml deleted file mode 100644 index 99225c5..0000000 --- a/sample/features/base/ui/src/main/res/layout/fake_databinding_layout.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/sample/features/messages/core/src/main/java/com/originsdigital/compositeadapter/messages/core/entity/MessageEntity.kt b/sample/features/messages/core/src/main/java/com/originsdigital/compositeadapter/messages/core/entity/MessageEntity.kt deleted file mode 100644 index b8358e5..0000000 --- a/sample/features/messages/core/src/main/java/com/originsdigital/compositeadapter/messages/core/entity/MessageEntity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.originsdigital.compositeadapter.messages.core.entity - -data class MessageEntity( - val id: String, - val type: Type, - val text: String? -) { - enum class Type { - VIEW, VIEW_BINDING, DATA_BINDING - } -} \ No newline at end of file diff --git a/sample/features/messages/data/src/main/AndroidManifest.xml b/sample/features/messages/data/src/main/AndroidManifest.xml deleted file mode 100644 index 47113d1..0000000 --- a/sample/features/messages/data/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/AndroidManifest.xml b/sample/features/messages/ui/src/main/AndroidManifest.xml deleted file mode 100644 index d3df6ce..0000000 --- a/sample/features/messages/ui/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleDI.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleDI.kt deleted file mode 100644 index 37eb247..0000000 --- a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleDI.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.originsdigital.compositeadapter.messages.ui - -import android.app.Application - -class SampleDI { - - companion object { - - private lateinit var app: Application - - fun init(app: Application) { - Companion.app = app - } - - fun provideApp(): Application = app - } -} \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleMapper.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleMapper.kt deleted file mode 100644 index bc3af98..0000000 --- a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleMapper.kt +++ /dev/null @@ -1,178 +0,0 @@ -package com.originsdigital.compositeadapter.messages.ui - -import android.app.Application -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.Rect -import android.util.TypedValue -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.cell.ClickItem -import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration -import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity -import com.originsdigital.compositeadapter.messages.ui.cell.databinding.SampleDataBindingMessageCell -import com.originsdigital.compositeadapter.messages.ui.cell.view.SampleViewMessageCell -import com.originsdigital.compositeadapter.messages.ui.cell.viewbinding.SampleViewBindingMessageCell -import com.originsdigital.compositeadapter.ui.cell.CommonFullErrorCell -import com.originsdigital.compositeadapter.ui.cell.CommonFullLoaderCell -import com.originsdigital.compositeadapter.ui.cell.CommonLoaderCell -import com.originsdigital.compositeadapter.ui.entity.CommonLoaderUI -import com.originsdigital.compositeadapter.ui.mapper.ErrorUIMapper -import java.util.* - -class SampleMapper( - app: Application, - private val itemsPerPage: Int, - private val viewMessageClickListener: (ClickItem) -> Unit, - private val viewBindingMessageClickListener: (ClickItem) -> Unit, - private val dataBindingMessageClickListener: (ClickItem) -> Unit, -) { - - private val firstItemDecoration: SpaceItemDecoration> - private val middleSmallItemDecoration: SpaceItemDecoration> - private val middleBigItemDecoration: SpaceItemDecoration> - private val lastItemDecoration: SpaceItemDecoration> - - init { - val space = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - 10f, - app.resources.displayMetrics - ).toInt() - firstItemDecoration = - SpaceItemDecoration(top = 8 * space, bottom = space, start = 8 * space, end = 8 * space) - middleSmallItemDecoration = object : SpaceItemDecoration>( - top = 2 * space, bottom = 2 * space, start = 4 * space, end = 4 * space - ) { - private val itemBounds = Rect() - private val dividerPaint: Paint = Paint().apply { - this.color = Color.GREEN - this.style = Paint.Style.FILL - this.isAntiAlias = true - } - - override fun onDraw( - canvas: Canvas, - view: View, - parent: RecyclerView, - state: RecyclerView.State, - item: Cell<*> - ) { - super.onDraw(canvas, view, parent, state, item) - parent.layoutManager?.getDecoratedBoundsWithMargins(view, itemBounds) - canvas.drawRect( - itemBounds.left.toFloat(), - itemBounds.top.toFloat(), - itemBounds.right.toFloat(), - itemBounds.bottom.toFloat(), - dividerPaint - ) - } - } - middleBigItemDecoration = - SpaceItemDecoration(top = space, bottom = space, start = space, end = space) - lastItemDecoration = - SpaceItemDecoration(top = space, bottom = 8 * space, start = 8 * space, end = 8 * space) - } - - fun mapToCells(messages: List, extraCell: Cell<*>? = null): List> { - val result = mutableListOf>() - - messages.mapIndexedTo(result) { i, message -> - val decoration = when { - i == 0 -> firstItemDecoration - i == messages.size - 1 -> lastItemDecoration - else -> { - if (i % ((itemsPerPage + messages.size) / itemsPerPage) == 0) { - middleSmallItemDecoration - } else { - middleBigItemDecoration - } - } - } - when (message.type) { - MessageEntity.Type.VIEW -> SampleViewMessageCell( - message.copy(text = "ViewMessageCell ${message.text}"), - decoration = decoration, - onClickListener = viewMessageClickListener - ) - MessageEntity.Type.VIEW_BINDING -> SampleViewBindingMessageCell( - message.copy(text = "ViewBindingMessageCell ${message.text}"), - decoration = decoration, - onClickListener = viewBindingMessageClickListener - ) - MessageEntity.Type.DATA_BINDING -> SampleDataBindingMessageCell( - message.copy(text = "DataBindingMessageCell ${message.text}"), - decoration = decoration, - onClickListener = dataBindingMessageClickListener - ) - } - } - if (extraCell != null) { - result.add(extraCell) - } - - return result - } - - fun changeMessageType( - messages: List, - input: MessageEntity - ): List { - return messages.map { message -> - if (input.id == message.id) { - message.copy(type = MessageEntity.Type.values().toList().minus(input.type).random()) - } else { - message - } - } - } - - fun getErrorMessage(random: Random): String { - val diff = 'z' - 'a' - val errorMessage = (0..5).map { - ('a' + random.nextInt(diff)).let { char -> - if (random.nextBoolean()) { - char - } else { - char.uppercase() - } - } - }.joinToString("") - return "Error $errorMessage" - } - - fun getLoaderCell(fullscreen: Boolean): Cell<*> { - return if (fullscreen) { - CommonFullLoaderCell() - } else { - CommonLoaderCell( - data = CommonLoaderUI("", heightId = null), - decoration = null - ) - } - } - - fun getErrorCell( - error: String, - fullscreen: Boolean, - onRetryClickListener: () -> Unit - ): Cell<*> { - val mapper = ErrorUIMapper() - return if (fullscreen) { - CommonFullErrorCell( - data = mapper.mapError(id = "messages", text = error), - decoration = null, - onClickListener = { onRetryClickListener() } - ) - } else { - CommonFullErrorCell( - data = mapper.mapError(id = "messages", text = error), - decoration = null, - onClickListener = { onRetryClickListener() } - ) - } - } -} \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleState.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleState.kt deleted file mode 100644 index 8108011..0000000 --- a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleState.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.originsdigital.compositeadapter.messages.ui - -import androidx.lifecycle.MutableLiveData -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity - -class SampleState { - - var messages: List = emptyList() - val cellsData: MutableLiveData>> = MutableLiveData(emptyList()) - val refreshingData: MutableLiveData = MutableLiveData(false) - - fun setData(messages: List, cells: List>) { - this.messages = messages - this.cellsData.value = cells - setRefreshing(false) - } - - fun setRefreshing(isRefreshing: Boolean) { - refreshingData.value = isRefreshing - } -} \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleViewModel.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleViewModel.kt deleted file mode 100644 index 817731d..0000000 --- a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/SampleViewModel.kt +++ /dev/null @@ -1,145 +0,0 @@ -package com.originsdigital.compositeadapter.messages.ui - -import android.widget.Toast -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity -import kotlinx.coroutines.* -import java.util.* - -class SampleViewModel : ViewModel() { - - val state: SampleState = SampleState() - - private val app = SampleDI.provideApp() - - private var toast: Toast? = null - private val mapper = SampleMapper( - app = app, - itemsPerPage = ITEMS_PER_PAGE, - viewMessageClickListener = { - toast?.cancel() - toast = Toast.makeText(app, "viewMessageClickListener", Toast.LENGTH_SHORT).also { it.show() } - }, - viewBindingMessageClickListener = { click -> - changeMessageType(click.item) - }, - dataBindingMessageClickListener = { click -> - changeMessageType(click.item) - } - ) - - private var nextPage: Int = FIRST_PAGE - private var isPaginationByScrollAllowed = true - - private var loadJob: Job? = null - private var clickJob: Job? = null - - init { - loadData(nextPage) - } - - fun onRefresh() { - loadData(FIRST_PAGE) - } - - fun onLastItemVisibleChanged(position: Int) { - val currentItemsSize = state.cellsData.value.orEmpty().size - isPaginationByScrollAllowed = position < currentItemsSize - 1 - if (isPaginationByScrollAllowed && position > currentItemsSize - ITEMS_PER_PAGE / 2) { - if (loadJob?.isActive != true) { - isPaginationByScrollAllowed = false - loadData(nextPage) - } - } - } - - private fun loadData(page: Int) { - loadJob?.cancel() - if (page >= LAST_PAGE) { - state.setRefreshing(false) - return - } - loadJob = viewModelScope.launch { - val isReload = page == FIRST_PAGE - val messagesOnLoading: List - val cellsOnLoading: List> - withContext(Dispatchers.IO) { - messagesOnLoading = if (isReload) { - emptyList() - } else { - state.messages - } - cellsOnLoading = mapper.mapToCells( - messages = messagesOnLoading, - extraCell = mapper.getLoaderCell(fullscreen = messagesOnLoading.isEmpty()) - ) - } - state.setData(messagesOnLoading, cellsOnLoading) - - val newMessages: List - val newCells: List> - withContext(Dispatchers.IO) { - val random = Random() - delay(3000) - val isSuccess = random.nextInt(100) < 75 - - if (isSuccess) { - nextPage = getNextPage(page) - isPaginationByScrollAllowed = true - val messages = (0..ITEMS_PER_PAGE).map { index -> - val id = page * ITEMS_PER_PAGE + index - val type = when (random.nextInt(3)) { - 0 -> MessageEntity.Type.VIEW - 1 -> MessageEntity.Type.VIEW_BINDING - else -> MessageEntity.Type.DATA_BINDING - } - MessageEntity(id = id.toString(), type = type, text = "Item $id") - } - newMessages = if (isReload) { - messages - } else { - state.messages + messages - } - newCells = mapper.mapToCells(newMessages) - } else { - newMessages = state.messages - newCells = mapper.mapToCells( - messages = newMessages, - extraCell = mapper.getErrorCell( - mapper.getErrorMessage(random), - fullscreen = newMessages.isEmpty(), - onRetryClickListener = { loadData(page) } - ) - ) - } - } - state.setData(newMessages, newCells) - } - } - - private fun changeMessageType(input: MessageEntity) { - clickJob?.cancel() - clickJob = viewModelScope.launch { - val newMessages = withContext(Dispatchers.IO) { - mapper.changeMessageType(state.messages, input) - } - state.setData(newMessages, mapper.mapToCells(newMessages)) - } - } - - private fun getNextPage(currentPage: Int): Int = currentPage + 1 - - override fun onCleared() { - super.onCleared() - toast?.cancel() - toast = null - } - - companion object { - private const val FIRST_PAGE = 0 - private const val LAST_PAGE = 3 - private const val ITEMS_PER_PAGE = 10 - } -} \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/databinding/SampleDataBindingMessageCell.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/databinding/SampleDataBindingMessageCell.kt deleted file mode 100644 index 1a2b2e1..0000000 --- a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/databinding/SampleDataBindingMessageCell.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.originsdigital.compositeadapter.messages.ui.cell.databinding - -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.cell.ClickItem -import com.originsdigital.compositeadapter.decoration.ItemDecoration -import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity -import com.originsdigital.compositeadapter.messages.ui.R -import com.originsdigital.compositeadapter.ui.cell.databinding.DataBindingCell - -data class SampleDataBindingMessageCell( - override val data: MessageEntity, - override val decoration: ItemDecoration>?, - override val onClickListener: ((ClickItem) -> Unit) -) : DataBindingCell { - - override val uniqueId: String = data.id - override val layoutId: Int = R.layout.message_databinding_list_item -} \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/view/SampleViewMessageCell.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/view/SampleViewMessageCell.kt deleted file mode 100644 index bad29a3..0000000 --- a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/view/SampleViewMessageCell.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.originsdigital.compositeadapter.messages.ui.cell.view - -import android.util.TypedValue -import android.view.LayoutInflater -import android.view.ViewGroup -import android.widget.TextView -import androidx.appcompat.widget.AppCompatTextView -import androidx.core.widget.TextViewCompat -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.cell.ClickItem -import com.originsdigital.compositeadapter.decoration.ItemDecoration -import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity -import com.originsdigital.compositeadapter.messages.ui.R - -data class SampleViewMessageCell( - override val data: MessageEntity, - override val decoration: ItemDecoration>?, - override val onClickListener: ((ClickItem) -> Unit) -) : Cell { - - override val uniqueId: String = data.id - override val layoutId: Int = R.layout.message_view_list_item - - override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20f, parent.resources.displayMetrics).toInt() - return ViewMessageViewHolder( - AppCompatTextView(inflater.context).apply { - id = R.id.text - layoutParams = RecyclerView.LayoutParams( - RecyclerView.LayoutParams.MATCH_PARENT, - RecyclerView.LayoutParams.WRAP_CONTENT - ) - setPadding(padding, padding, padding, padding) - val outValue = TypedValue() - context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true) - setBackgroundResource(outValue.resourceId) - TextViewCompat.setTextAppearance(this, R.style.TextAppearance_AppCompat_Medium) - } - ) - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder as ViewMessageViewHolder).apply { - text.text = data.text - } - } - - private class ViewMessageViewHolder(val text: TextView) : RecyclerView.ViewHolder(text) -} \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/viewbinding/SampleViewBindingMessageCell.kt b/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/viewbinding/SampleViewBindingMessageCell.kt deleted file mode 100644 index 4148e20..0000000 --- a/sample/features/messages/ui/src/main/java/com/originsdigital/compositeadapter/messages/ui/cell/viewbinding/SampleViewBindingMessageCell.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.originsdigital.compositeadapter.messages.ui.cell.viewbinding - -import androidx.recyclerview.widget.RecyclerView -import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.cell.ClickItem -import com.originsdigital.compositeadapter.decoration.ItemDecoration -import com.originsdigital.compositeadapter.messages.core.entity.MessageEntity -import com.originsdigital.compositeadapter.messages.ui.R -import com.originsdigital.compositeadapter.messages.ui.databinding.MessageViewbindingListItemBinding -import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell -import com.originsdigital.compositeadapter.utils.getViewBinding - -data class SampleViewBindingMessageCell( - override val data: MessageEntity, - override val decoration: ItemDecoration>?, - override val onClickListener: ((ClickItem) -> Unit)? -) : ViewBindingCell { - - override val uniqueId: String = data.id - override val layoutId: Int = R.layout.message_viewbinding_list_item - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder.getViewBinding(MessageViewbindingListItemBinding::bind)).apply { - text.text = data.text - } - } -} \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/res/layout/message_databinding_list_item.xml b/sample/features/messages/ui/src/main/res/layout/message_databinding_list_item.xml deleted file mode 100644 index 13ff21a..0000000 --- a/sample/features/messages/ui/src/main/res/layout/message_databinding_list_item.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/samples/basic/build.gradle.kts b/samples/basic/build.gradle.kts index 3116a3c..2a5adc1 100644 --- a/samples/basic/build.gradle.kts +++ b/samples/basic/build.gradle.kts @@ -65,4 +65,5 @@ dependencies { implementation(project(Config.Deps.Libs.decorations)) implementation(project(Config.Deps.Libs.differentBindings)) implementation(project(Config.Deps.Libs.innerRecyclerview)) + implementation(project(Config.Deps.Libs.stateAsCells)) } \ No newline at end of file diff --git a/samples/basic/src/main/AndroidManifest.xml b/samples/basic/src/main/AndroidManifest.xml index e96d0ba..6571584 100644 --- a/samples/basic/src/main/AndroidManifest.xml +++ b/samples/basic/src/main/AndroidManifest.xml @@ -31,5 +31,9 @@ + + \ No newline at end of file diff --git a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt index 87c001d..9477a05 100644 --- a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesActivity.kt @@ -15,6 +15,7 @@ import com.originsdigital.compositeadapter.sample.basic.ui.entity.SampleUI import com.originsdigital.compositeadapter.sample.decorations.ui.DecorationsActivity import com.originsdigital.compositeadapter.sample.differentbindings.ui.DifferentBindingsActivity import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.InnerRecyclerActivity +import com.originsdigital.compositeadapter.stateascells.home.ui.HomeActivity class SamplesActivity : AppCompatActivity() { @@ -47,6 +48,7 @@ class SamplesActivity : AppCompatActivity() { SampleUI.Type.DECORATIONS -> "Decorations" SampleUI.Type.DIFFERENT_BINDINGS -> "ViewBindings/DataBindings/Programmatically View" SampleUI.Type.INNER_RECYCLER -> "Inner RecyclerView/ViewPager/Etc and how to save scroll state" + SampleUI.Type.STATE_AS_CELLS -> "State as List of Cells" } val onClickListener: (ClickItem) -> Unit = { item -> startActivity( @@ -60,6 +62,9 @@ class SamplesActivity : AppCompatActivity() { SampleUI.Type.INNER_RECYCLER -> { InnerRecyclerActivity.getLaunchIntent(this) } + SampleUI.Type.STATE_AS_CELLS -> { + HomeActivity.getLaunchIntent(this) + } } ) } diff --git a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesApplication.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesApplication.kt index 1a1674c..a2b4661 100644 --- a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesApplication.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/SamplesApplication.kt @@ -1,5 +1,13 @@ package com.originsdigital.compositeadapter.sample.basic.ui import android.app.Application +import com.originsdigital.compositeadapter.stateascells.app.StateAsCellsDI -class SamplesApplication : Application() \ No newline at end of file +class SamplesApplication : Application() { + + override fun onCreate() { + super.onCreate() + + StateAsCellsDI.init(this) + } +} \ No newline at end of file diff --git a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/entity/SampleUI.kt b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/entity/SampleUI.kt index d5878e0..35f28d6 100644 --- a/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/entity/SampleUI.kt +++ b/samples/basic/src/main/java/com/originsdigital/compositeadapter/sample/basic/ui/entity/SampleUI.kt @@ -5,6 +5,6 @@ data class SampleUI( val type: Type ) { enum class Type { - DECORATIONS, DIFFERENT_BINDINGS, INNER_RECYCLER + DECORATIONS, DIFFERENT_BINDINGS, INNER_RECYCLER, STATE_AS_CELLS } } \ No newline at end of file diff --git a/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_list_item.xml b/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_list_item.xml deleted file mode 100644 index a383a7c..0000000 --- a/samples/different-bindings/src/main/res/layout/different_bindings_view_binding_2_list_item.xml +++ /dev/null @@ -1,10 +0,0 @@ - - \ No newline at end of file diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt index 727718e..a65c607 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt @@ -15,8 +15,8 @@ import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.decoration.CompositeItemDecoration import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration -import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecycler2Cell import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecycler1Cell +import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecycler2Cell import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.cell.InnerRecyclerItemCell import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerItemUI import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity.InnerRecyclerUI diff --git a/sample/features/base/core/.gitignore b/samples/state-as-cells/app/.gitignore similarity index 100% rename from sample/features/base/core/.gitignore rename to samples/state-as-cells/app/.gitignore diff --git a/samples/state-as-cells/app/build.gradle.kts b/samples/state-as-cells/app/build.gradle.kts new file mode 100644 index 0000000..3262a93 --- /dev/null +++ b/samples/state-as-cells/app/build.gradle.kts @@ -0,0 +1,36 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.sampleMinSdk + targetSdk = Config.Build.targetSdk + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } + + buildFeatures { + viewBinding = true + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseUI)) + implementation(project(Config.Deps.Libs.homeData)) + implementation(project(Config.Deps.Libs.homeCore)) + api(project(Config.Deps.Libs.homeUI)) + implementation(project(Config.Deps.Libs.storiesCore)) + implementation(project(Config.Deps.Libs.storiesUI)) + implementation(project(Config.Deps.Libs.newsCore)) + implementation(project(Config.Deps.Libs.newsUI)) +} \ No newline at end of file diff --git a/sample/features/base/ui/consumer-rules.pro b/samples/state-as-cells/app/consumer-rules.pro similarity index 100% rename from sample/features/base/ui/consumer-rules.pro rename to samples/state-as-cells/app/consumer-rules.pro diff --git a/sample/features/base/ui/proguard-rules.pro b/samples/state-as-cells/app/proguard-rules.pro similarity index 100% rename from sample/features/base/ui/proguard-rules.pro rename to samples/state-as-cells/app/proguard-rules.pro diff --git a/samples/state-as-cells/app/src/main/AndroidManifest.xml b/samples/state-as-cells/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cf0daec --- /dev/null +++ b/samples/state-as-cells/app/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/samples/state-as-cells/app/src/main/java/com/originsdigital/compositeadapter/stateascells/app/AppLoggerImpl.kt b/samples/state-as-cells/app/src/main/java/com/originsdigital/compositeadapter/stateascells/app/AppLoggerImpl.kt new file mode 100644 index 0000000..60d7701 --- /dev/null +++ b/samples/state-as-cells/app/src/main/java/com/originsdigital/compositeadapter/stateascells/app/AppLoggerImpl.kt @@ -0,0 +1,31 @@ +package com.originsdigital.compositeadapter.stateascells.app + +import android.util.Log +import com.originsdigital.compositeadapter.core.AppLogger + +class AppLoggerImpl(private val isEnabled: Boolean) : AppLogger { + + override fun logD(tag: String, message: String) { + if (isEnabled) { + Log.d(tag, message) + } + } + + override fun logD(tag: String, message: String, error: Throwable) { + if (isEnabled) { + Log.d(tag, "$message $error") + error.printStackTrace() + } + } + + override fun trackNonFatal(tag: String, message: String) { + try { + throw IllegalStateException("$tag $message") + } catch (stackTrace: IllegalStateException) { +// FirebaseCrashlytics.getInstance().recordException(stackTrace) + if (isEnabled) { + throw stackTrace + } + } + } +} \ No newline at end of file diff --git a/samples/state-as-cells/app/src/main/java/com/originsdigital/compositeadapter/stateascells/app/StateAsCellsDI.kt b/samples/state-as-cells/app/src/main/java/com/originsdigital/compositeadapter/stateascells/app/StateAsCellsDI.kt new file mode 100644 index 0000000..64a86b8 --- /dev/null +++ b/samples/state-as-cells/app/src/main/java/com/originsdigital/compositeadapter/stateascells/app/StateAsCellsDI.kt @@ -0,0 +1,63 @@ +package com.originsdigital.compositeadapter.stateascells.app + +import android.app.Application +import com.originsdigital.compositeadapter.BuildConfig +import com.originsdigital.compositeadapter.core.AppLogger +import com.originsdigital.compositeadapter.core.di.IS_DEBUG_QUALIFIER +import com.originsdigital.compositeadapter.core.di.IS_INTEGRATION_QUALIFIER +import com.originsdigital.compositeadapter.core.di.IS_NOT_PRODUCTION_RELEASE +import com.originsdigital.compositeadapter.core.di.IS_NOT_PRODUCTION_RELEASE_QUALIFIER +import com.originsdigital.compositeadapter.core.di.IS_PRODUCTION_BETA_QUALIFIER +import com.originsdigital.compositeadapter.core.di.coreModule +import com.originsdigital.compositeadapter.stateascells.home.ui.di.homeUIModule +import com.originsdigital.compositeadapter.stories.data.di.homeDataModule +import com.originsdigital.compositeadapter.ui.di.uiModule +import org.koin.android.ext.koin.androidContext +import org.koin.core.context.startKoin +import org.koin.core.module.Module +import org.koin.dsl.module + + +class StateAsCellsDI { + + companion object { + fun init(application: Application) { + startKoin { + androidContext(application) + modules(getModules()) + } + } + + private fun getModules(): List { + return listOf( + coreModule, + uiModule, + homeDataModule(), + homeUIModule(), + appModule() + ) + } + + private fun appModule(): Module { + return module { + single(IS_DEBUG_QUALIFIER) { + "debug".equals(BuildConfig.BUILD_TYPE, true) + } + single(IS_INTEGRATION_QUALIFIER) { +// "integration".equals(BuildConfig.FLAVOR, true) + true + } + single(IS_PRODUCTION_BETA_QUALIFIER) { + /*"production".equals(BuildConfig.FLAVOR, true) + && */"beta".equals(BuildConfig.BUILD_TYPE, true) + } + single(IS_NOT_PRODUCTION_RELEASE_QUALIFIER) { + BuildConfig.DEBUG +// || !"production".equals(BuildConfig.FLAVOR, true) + || !"release".equals(BuildConfig.BUILD_TYPE, true) + } + single { AppLoggerImpl(IS_NOT_PRODUCTION_RELEASE) } + } + } + } +} \ No newline at end of file diff --git a/sample/features/base/ui/.gitignore b/samples/state-as-cells/features/base/core/.gitignore similarity index 100% rename from sample/features/base/ui/.gitignore rename to samples/state-as-cells/features/base/core/.gitignore diff --git a/sample/features/base/core/build.gradle.kts b/samples/state-as-cells/features/base/core/build.gradle.kts similarity index 100% rename from sample/features/base/core/build.gradle.kts rename to samples/state-as-cells/features/base/core/build.gradle.kts diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt b/samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt similarity index 100% rename from sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt rename to samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/AppLogger.kt diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt b/samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt similarity index 100% rename from sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt rename to samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/di/CoreModule.kt diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt b/samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt similarity index 100% rename from sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt rename to samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/entity/Scene.kt diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt b/samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt similarity index 76% rename from sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt rename to samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt index 01eadb8..ffc97d1 100644 --- a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt +++ b/samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/ExceptionsExtensions.kt @@ -17,8 +17,8 @@ fun Throwable.rethrowIfNeeded() { val Throwable.isInternetConnectionException: Boolean get() { return this is UnknownHostException - || this is UnknownServiceException - || this is InterruptedIOException - || this is SocketException - || this is ClosedChannelException + || this is UnknownServiceException + || this is InterruptedIOException + || this is SocketException + || this is ClosedChannelException } \ No newline at end of file diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt b/samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt similarity index 100% rename from sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt rename to samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/extensions/SceneExtensions.kt diff --git a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt b/samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt similarity index 100% rename from sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt rename to samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt index 3809cc9..8cc2e0f 100644 --- a/sample/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt +++ b/samples/state-as-cells/features/base/core/src/main/java/com/originsdigital/compositeadapter/core/utils/Loading.kt @@ -1,7 +1,7 @@ package com.originsdigital.compositeadapter.core.utils -import com.originsdigital.compositeadapter.core.log import com.originsdigital.compositeadapter.core.extensions.rethrowIfNeeded +import com.originsdigital.compositeadapter.core.log suspend inline fun safeLoad( tag: Any, diff --git a/sample/features/messages/core/.gitignore b/samples/state-as-cells/features/base/ui/.gitignore similarity index 100% rename from sample/features/messages/core/.gitignore rename to samples/state-as-cells/features/base/ui/.gitignore diff --git a/sample/features/base/ui/build.gradle.kts b/samples/state-as-cells/features/base/ui/build.gradle.kts similarity index 87% rename from sample/features/base/ui/build.gradle.kts rename to samples/state-as-cells/features/base/ui/build.gradle.kts index 56f31c1..cf3b4ad 100644 --- a/sample/features/base/ui/build.gradle.kts +++ b/samples/state-as-cells/features/base/ui/build.gradle.kts @@ -1,14 +1,13 @@ plugins { id(Config.Plugins.androidLibrary) kotlin(Config.Plugins.android) - kotlin(Config.Plugins.kapt) } android { compileSdk = Config.Build.compileSdk defaultConfig { - minSdk = Config.Build.minSdk + minSdk = Config.Build.sampleMinSdk targetSdk = Config.Build.targetSdk } @@ -21,13 +20,12 @@ android { } buildFeatures { - dataBinding = true viewBinding = true } } dependencies { - implementation(project(Config.Deps.Libs.baseCore)) + api(project(Config.Deps.Libs.baseCore)) api(Config.Deps.Coroutines.android) api(Config.Deps.AndroidX.appcompat) diff --git a/sample/features/messages/core/consumer-rules.pro b/samples/state-as-cells/features/base/ui/consumer-rules.pro similarity index 100% rename from sample/features/messages/core/consumer-rules.pro rename to samples/state-as-cells/features/base/ui/consumer-rules.pro diff --git a/sample/features/messages/core/proguard-rules.pro b/samples/state-as-cells/features/base/ui/proguard-rules.pro similarity index 100% rename from sample/features/messages/core/proguard-rules.pro rename to samples/state-as-cells/features/base/ui/proguard-rules.pro diff --git a/sample/features/base/ui/src/main/AndroidManifest.xml b/samples/state-as-cells/features/base/ui/src/main/AndroidManifest.xml similarity index 100% rename from sample/features/base/ui/src/main/AndroidManifest.xml rename to samples/state-as-cells/features/base/ui/src/main/AndroidManifest.xml diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt similarity index 63% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt index 09a7a5c..f934e2e 100644 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonErrorCell.kt @@ -1,26 +1,38 @@ package com.originsdigital.compositeadapter.ui.cell -import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.ViewGroup import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.ui.R import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingViewHolder import com.originsdigital.compositeadapter.ui.databinding.CommonErrorCellBinding import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI -import com.originsdigital.compositeadapter.utils.getViewBinding data class CommonErrorCell( override val data: CommonErrorUI, override val decoration: ItemDecoration>?, override val onClickListener: ((ClickItem) -> Unit)? -) : ViewBindingCell { +) : ViewBindingCell() { override val uniqueId = data.id override val layoutId = R.layout.common_error_cell - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder.getViewBinding(CommonErrorCellBinding::bind)).apply { + override fun createViewBinding( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): CommonErrorCellBinding { + return CommonErrorCellBinding.inflate(inflater, parent, false) + } + + override fun onBindViewHolder( + holder: ViewBindingViewHolder, + position: Int + ) { + holder.binding.apply { text.text = when (data.message) { is CommonErrorUI.Message.Text -> data.message.text is CommonErrorUI.Message.Resource -> text.context.getString(data.message.textId) diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt similarity index 63% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt index 8acded9..5c2adae 100644 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullEmptyCell.kt @@ -1,26 +1,38 @@ package com.originsdigital.compositeadapter.ui.cell -import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.ViewGroup import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.ui.R import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingViewHolder import com.originsdigital.compositeadapter.ui.databinding.CommonFullEmptyCellBinding import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI -import com.originsdigital.compositeadapter.utils.getViewBinding data class CommonFullEmptyCell( override val data: CommonErrorUI, override val decoration: ItemDecoration>?, override val onClickListener: ((ClickItem) -> Unit)? -) : ViewBindingCell { +) : ViewBindingCell() { override val uniqueId = data.id override val layoutId = R.layout.common_full_empty_cell - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder.getViewBinding(CommonFullEmptyCellBinding::bind)).apply { + override fun createViewBinding( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): CommonFullEmptyCellBinding { + return CommonFullEmptyCellBinding.inflate(inflater, parent, false) + } + + override fun onBindViewHolder( + holder: ViewBindingViewHolder, + position: Int + ) { + holder.binding.apply { text.text = when (data.message) { is CommonErrorUI.Message.Text -> data.message.text is CommonErrorUI.Message.Resource -> text.context.getString(data.message.textId) diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt similarity index 63% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt index bbe63a6..6b5848b 100644 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullErrorCell.kt @@ -1,26 +1,38 @@ package com.originsdigital.compositeadapter.ui.cell -import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.ViewGroup import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.cell.ClickItem import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.ui.R import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingViewHolder import com.originsdigital.compositeadapter.ui.databinding.CommonFullErrorCellBinding import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI -import com.originsdigital.compositeadapter.utils.getViewBinding data class CommonFullErrorCell( override val data: CommonErrorUI, override val decoration: ItemDecoration>?, override val onClickListener: ((ClickItem) -> Unit)? -) : ViewBindingCell { +) : ViewBindingCell() { override val uniqueId = data.id override val layoutId = R.layout.common_full_error_cell - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder.getViewBinding(CommonFullErrorCellBinding::bind)).apply { + override fun createViewBinding( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): CommonFullErrorCellBinding { + return CommonFullErrorCellBinding.inflate(inflater, parent, false) + } + + override fun onBindViewHolder( + holder: ViewBindingViewHolder, + position: Int + ) { + holder.binding.apply { text.text = when (data.message) { is CommonErrorUI.Message.Text -> data.message.text is CommonErrorUI.Message.Resource -> text.context.getString(data.message.textId) diff --git a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt new file mode 100644 index 0000000..9d5f420 --- /dev/null +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonFullLoaderCell.kt @@ -0,0 +1,29 @@ +package com.originsdigital.compositeadapter.ui.cell + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.originsdigital.compositeadapter.ui.R +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingViewHolder +import com.originsdigital.compositeadapter.ui.databinding.CommonFullLoaderCellBinding + +data class CommonFullLoaderCell( + override val data: Any = Unit +) : ViewBindingCell() { + + override val uniqueId = "CommonFullLoaderCell" + override val layoutId = R.layout.common_full_loader_cell + + override fun createViewBinding( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): CommonFullLoaderCellBinding { + return CommonFullLoaderCellBinding.inflate(inflater, parent, false) + } + + override fun onBindViewHolder( + holder: ViewBindingViewHolder, + position: Int + ) = Unit +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt similarity index 67% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt index 4f0c14c..6b29be5 100644 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/CommonLoaderCell.kt @@ -1,27 +1,38 @@ package com.originsdigital.compositeadapter.ui.cell +import android.view.LayoutInflater import android.view.ViewGroup import androidx.core.view.updateLayoutParams -import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.decoration.ItemDecoration import com.originsdigital.compositeadapter.ui.R import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingViewHolder import com.originsdigital.compositeadapter.ui.databinding.CommonLoaderCellBinding import com.originsdigital.compositeadapter.ui.entity.CommonLoaderUI import com.originsdigital.compositeadapter.utils.context -import com.originsdigital.compositeadapter.utils.getViewBinding data class CommonLoaderCell( override val data: CommonLoaderUI, override val decoration: ItemDecoration>? -) : ViewBindingCell { +) : ViewBindingCell() { override val uniqueId = data.id override val layoutId = R.layout.common_loader_cell - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - (holder.getViewBinding(CommonLoaderCellBinding::bind)).apply { + override fun createViewBinding( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): CommonLoaderCellBinding { + return CommonLoaderCellBinding.inflate(inflater, parent, false) + } + + override fun onBindViewHolder( + holder: ViewBindingViewHolder, + position: Int + ) { + holder.binding.apply { val newHeight = if (data.heightId == null) { ViewGroup.LayoutParams.WRAP_CONTENT } else { diff --git a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt new file mode 100644 index 0000000..542b061 --- /dev/null +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt @@ -0,0 +1,75 @@ +package com.originsdigital.compositeadapter.ui.cell.base + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem + +abstract class BaseCell : Cell { + + abstract fun createViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): VIEW_HOLDER + + fun onBindViewHolder( + holder: VIEW_HOLDER, + position: Int, + payloads: List + ): Boolean = false + + abstract fun onBindViewHolder(holder: VIEW_HOLDER, position: Int) + + fun onViewAttachedToWindow(holder: VIEW_HOLDER) = Unit + fun onViewDetachedFromWindow(holder: VIEW_HOLDER) = Unit + + fun onViewRecycled(holder: VIEW_HOLDER) = Unit + + fun onClicked(context: Context, holder: VIEW_HOLDER, position: Int) { + onClickListener?.invoke(ClickItem(context, holder, position, data)) + } + + final override fun onCreateViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return createViewHolder(inflater, parent, viewType) + } + + final override fun onBindViewHolder( + holder: RecyclerView.ViewHolder, + position: Int, + payloads: List + ): Boolean { + return onBindViewHolder(castHolder(holder), position, payloads) + } + + final override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + onBindViewHolder(castHolder(holder), position) + } + + final override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) { + onViewAttachedToWindow(castHolder(holder)) + } + + final override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { + onViewDetachedFromWindow(castHolder(holder)) + } + + final override fun onViewRecycled(holder: RecyclerView.ViewHolder) { + onViewRecycled(castHolder(holder)) + } + + final override fun onClicked(context: Context, holder: RecyclerView.ViewHolder, position: Int) { + onClicked(context, castHolder(holder), position) + } + + protected fun castHolder(holder: RecyclerView.ViewHolder): VIEW_HOLDER { + @Suppress("UNCHECKED_CAST") + return holder as VIEW_HOLDER + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseViewHolder.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseViewHolder.kt new file mode 100644 index 0000000..33d144f --- /dev/null +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseViewHolder.kt @@ -0,0 +1,6 @@ +package com.originsdigital.compositeadapter.ui.cell.base + +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +abstract class BaseViewHolder(root: View) : RecyclerView.ViewHolder(root) \ No newline at end of file diff --git a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt new file mode 100644 index 0000000..1a171f4 --- /dev/null +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingCell.kt @@ -0,0 +1,24 @@ +package com.originsdigital.compositeadapter.ui.cell.viewbinding + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.viewbinding.ViewBinding +import com.originsdigital.compositeadapter.ui.cell.base.BaseCell + +abstract class ViewBindingCell + : BaseCell>() { + + abstract fun createViewBinding( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): VIEW_BINDING + + final override fun createViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): ViewBindingViewHolder { + return ViewBindingViewHolder(createViewBinding(inflater, parent, viewType)) + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingViewHolder.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingViewHolder.kt new file mode 100644 index 0000000..a1a9025 --- /dev/null +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/viewbinding/ViewBindingViewHolder.kt @@ -0,0 +1,8 @@ +package com.originsdigital.compositeadapter.ui.cell.viewbinding + +import androidx.viewbinding.ViewBinding +import com.originsdigital.compositeadapter.ui.cell.base.BaseViewHolder + +class ViewBindingViewHolder( + val binding: VIEW_BINDING +) : BaseViewHolder(binding.root) \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/di/UIModule.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonErrorUI.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/entity/CommonLoaderUI.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt similarity index 52% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt index a9e7f0d..44ec535 100644 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/extensions/CellExtensions.kt @@ -1,7 +1,11 @@ package com.originsdigital.compositeadapter.ui.extensions import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.ui.cell.* +import com.originsdigital.compositeadapter.ui.cell.CommonErrorCell +import com.originsdigital.compositeadapter.ui.cell.CommonFullEmptyCell +import com.originsdigital.compositeadapter.ui.cell.CommonFullErrorCell +import com.originsdigital.compositeadapter.ui.cell.CommonFullLoaderCell +import com.originsdigital.compositeadapter.ui.cell.CommonLoaderCell val Cell<*>.isStateCell: Boolean get() { diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/BaseFragment.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/fragment/SimpleRecyclerFragment.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt similarity index 60% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt index 2974f4a..08f8322 100644 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/ErrorUIMapper.kt @@ -1,6 +1,7 @@ package com.originsdigital.compositeadapter.ui.mapper import androidx.annotation.DimenRes +import androidx.annotation.StringRes import com.originsdigital.compositeadapter.core.extensions.isInternetConnectionException import com.originsdigital.compositeadapter.ui.R import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI @@ -21,18 +22,28 @@ class ErrorUIMapper { fun mapError( id: String, - throwable: Throwable, + @StringRes textId: Int, @DimenRes heightId: Int? = null ): CommonErrorUI { return CommonErrorUI( id = id, - message = CommonErrorUI.Message.Resource( - textId = if (throwable.isInternetConnectionException) { - R.string.common_no_connection_error - } else { - R.string.common_default_error - } - ), + message = CommonErrorUI.Message.Resource(textId = textId), + heightId = heightId + ) + } + + fun mapError( + id: String, + throwable: Throwable, + @DimenRes heightId: Int? = null + ): CommonErrorUI { + return mapError( + id = id, + textId = if (throwable.isInternetConnectionException) { + R.string.common_no_connection_error + } else { + R.string.common_default_error + }, heightId = heightId ) } diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/LoaderUIMapper.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/mapper/SceneMapper.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt similarity index 84% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt index 08d7486..6d11aa0 100644 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/CompositeAdapterUtils.kt @@ -24,9 +24,9 @@ fun submitAdapterData( commitCallback: Runnable? = null ) { swipeRefreshLayout.isRefreshing = isRefreshing - if (commitCallback == null) { - adapter.submitList(cells) - } else { - adapter.submitList(cells, commitCallback) - } + submitAdapterData( + adapter = adapter, + cells = cells, + commitCallback = commitCallback + ) } \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/FlowUtils.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/RecyclerUtils.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/SwipeRefreshUtils.kt diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/utils/ViewModelUtils.kt diff --git a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt new file mode 100644 index 0000000..f2b598f --- /dev/null +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseDataViewModel.kt @@ -0,0 +1,6 @@ +package com.originsdigital.compositeadapter.ui.vm + +abstract class BaseDataViewModel : BaseViewModel() { + + abstract fun onRefresh() +} \ No newline at end of file diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt similarity index 96% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt index c69ad47..723f3ab 100644 --- a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseStateViewModel.kt @@ -14,7 +14,7 @@ abstract class BaseStateViewModel : BaseDataViewModel() { abstract val stateFlow: Flow - protected fun loadDataWithState( + protected fun loadBlockWithState( text: String, force: Boolean, currentSceneDelegate: () -> Scene, @@ -35,13 +35,13 @@ abstract class BaseStateViewModel : BaseDataViewModel() { } } - protected fun loadDataWithState( + protected fun loadBlockWithState( text: String, force: Boolean, sceneFlow: MutableStateFlow>, block: (suspend CoroutineScope.() -> DATA) ): Job { - return loadDataWithState( + return loadBlockWithState( text = text, force = force, currentSceneDelegate = { sceneFlow.value }, diff --git a/sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt similarity index 100% rename from sample/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt rename to samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/vm/BaseViewModel.kt diff --git a/sample/features/base/ui/src/main/res/layout/common_error_cell.xml b/samples/state-as-cells/features/base/ui/src/main/res/layout/common_error_cell.xml similarity index 100% rename from sample/features/base/ui/src/main/res/layout/common_error_cell.xml rename to samples/state-as-cells/features/base/ui/src/main/res/layout/common_error_cell.xml diff --git a/sample/features/base/ui/src/main/res/layout/common_full_empty_cell.xml b/samples/state-as-cells/features/base/ui/src/main/res/layout/common_full_empty_cell.xml similarity index 100% rename from sample/features/base/ui/src/main/res/layout/common_full_empty_cell.xml rename to samples/state-as-cells/features/base/ui/src/main/res/layout/common_full_empty_cell.xml diff --git a/sample/features/base/ui/src/main/res/layout/common_full_error_cell.xml b/samples/state-as-cells/features/base/ui/src/main/res/layout/common_full_error_cell.xml similarity index 100% rename from sample/features/base/ui/src/main/res/layout/common_full_error_cell.xml rename to samples/state-as-cells/features/base/ui/src/main/res/layout/common_full_error_cell.xml diff --git a/sample/features/base/ui/src/main/res/layout/common_full_loader_cell.xml b/samples/state-as-cells/features/base/ui/src/main/res/layout/common_full_loader_cell.xml similarity index 100% rename from sample/features/base/ui/src/main/res/layout/common_full_loader_cell.xml rename to samples/state-as-cells/features/base/ui/src/main/res/layout/common_full_loader_cell.xml diff --git a/sample/features/base/ui/src/main/res/layout/common_loader_cell.xml b/samples/state-as-cells/features/base/ui/src/main/res/layout/common_loader_cell.xml similarity index 100% rename from sample/features/base/ui/src/main/res/layout/common_loader_cell.xml rename to samples/state-as-cells/features/base/ui/src/main/res/layout/common_loader_cell.xml diff --git a/sample/features/base/ui/src/main/res/layout/common_recycler_fragment.xml b/samples/state-as-cells/features/base/ui/src/main/res/layout/common_recycler_fragment.xml similarity index 100% rename from sample/features/base/ui/src/main/res/layout/common_recycler_fragment.xml rename to samples/state-as-cells/features/base/ui/src/main/res/layout/common_recycler_fragment.xml diff --git a/sample/features/base/ui/src/main/res/values/colors.xml b/samples/state-as-cells/features/base/ui/src/main/res/values/colors.xml similarity index 99% rename from sample/features/base/ui/src/main/res/values/colors.xml rename to samples/state-as-cells/features/base/ui/src/main/res/values/colors.xml index 982b026..257344d 100644 --- a/sample/features/base/ui/src/main/res/values/colors.xml +++ b/samples/state-as-cells/features/base/ui/src/main/res/values/colors.xml @@ -12,7 +12,7 @@ @color/compositeadapter_black @color/compositeadapter_white @color/compositeadapter_black - + #FFFFFF #000000 #00000000 diff --git a/sample/features/base/ui/src/main/res/values/strings.xml b/samples/state-as-cells/features/base/ui/src/main/res/values/strings.xml similarity index 100% rename from sample/features/base/ui/src/main/res/values/strings.xml rename to samples/state-as-cells/features/base/ui/src/main/res/values/strings.xml diff --git a/sample/features/messages/data/.gitignore b/samples/state-as-cells/features/home/core/.gitignore similarity index 100% rename from sample/features/messages/data/.gitignore rename to samples/state-as-cells/features/home/core/.gitignore diff --git a/samples/state-as-cells/features/home/core/build.gradle.kts b/samples/state-as-cells/features/home/core/build.gradle.kts new file mode 100644 index 0000000..27fe096 --- /dev/null +++ b/samples/state-as-cells/features/home/core/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + kotlin("jvm") + `java-library` +} + +java.sourceCompatibility = Config.Build.javaVersion +java.targetCompatibility = Config.Build.javaVersion + +project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java).configureEach { + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseCore)) + implementation(project(Config.Deps.Libs.storiesCore)) + implementation(project(Config.Deps.Libs.newsCore)) +} \ No newline at end of file diff --git a/sample/features/messages/data/consumer-rules.pro b/samples/state-as-cells/features/home/core/consumer-rules.pro similarity index 100% rename from sample/features/messages/data/consumer-rules.pro rename to samples/state-as-cells/features/home/core/consumer-rules.pro diff --git a/sample/features/messages/data/proguard-rules.pro b/samples/state-as-cells/features/home/core/proguard-rules.pro similarity index 100% rename from sample/features/messages/data/proguard-rules.pro rename to samples/state-as-cells/features/home/core/proguard-rules.pro diff --git a/samples/state-as-cells/features/home/core/src/main/java/com/originsdigital/compositeadapter/home/core/repository/HomeRepository.kt b/samples/state-as-cells/features/home/core/src/main/java/com/originsdigital/compositeadapter/home/core/repository/HomeRepository.kt new file mode 100644 index 0000000..f698c61 --- /dev/null +++ b/samples/state-as-cells/features/home/core/src/main/java/com/originsdigital/compositeadapter/home/core/repository/HomeRepository.kt @@ -0,0 +1,11 @@ +package com.originsdigital.compositeadapter.home.core.repository + +import com.originsdigital.compositeadapter.news.core.entity.NewsEntity +import com.originsdigital.compositeadapter.stories.core.entity.StoryEntity + +interface HomeRepository { + + suspend fun getStories(): List + + suspend fun getNews(): List +} \ No newline at end of file diff --git a/sample/features/messages/ui/.gitignore b/samples/state-as-cells/features/home/data/.gitignore similarity index 100% rename from sample/features/messages/ui/.gitignore rename to samples/state-as-cells/features/home/data/.gitignore diff --git a/sample/features/messages/data/build.gradle.kts b/samples/state-as-cells/features/home/data/build.gradle.kts similarity index 70% rename from sample/features/messages/data/build.gradle.kts rename to samples/state-as-cells/features/home/data/build.gradle.kts index 3f1d9de..4ac3e95 100644 --- a/sample/features/messages/data/build.gradle.kts +++ b/samples/state-as-cells/features/home/data/build.gradle.kts @@ -7,7 +7,7 @@ android { compileSdk = Config.Build.compileSdk defaultConfig { - minSdk = Config.Build.minSdk + minSdk = Config.Build.sampleMinSdk targetSdk = Config.Build.targetSdk } @@ -22,5 +22,7 @@ android { dependencies { implementation(project(Config.Deps.Libs.baseCore)) - implementation(project(Config.Deps.Libs.messagesCore)) + implementation(project(Config.Deps.Libs.homeCore)) + implementation(project(Config.Deps.Libs.storiesCore)) + implementation(project(Config.Deps.Libs.newsCore)) } \ No newline at end of file diff --git a/sample/features/messages/ui/consumer-rules.pro b/samples/state-as-cells/features/home/data/consumer-rules.pro similarity index 100% rename from sample/features/messages/ui/consumer-rules.pro rename to samples/state-as-cells/features/home/data/consumer-rules.pro diff --git a/sample/features/messages/ui/proguard-rules.pro b/samples/state-as-cells/features/home/data/proguard-rules.pro similarity index 100% rename from sample/features/messages/ui/proguard-rules.pro rename to samples/state-as-cells/features/home/data/proguard-rules.pro diff --git a/samples/state-as-cells/features/home/data/src/main/AndroidManifest.xml b/samples/state-as-cells/features/home/data/src/main/AndroidManifest.xml new file mode 100644 index 0000000..86f34b9 --- /dev/null +++ b/samples/state-as-cells/features/home/data/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/samples/state-as-cells/features/home/data/src/main/java/com/originsdigital/compositeadapter/stories/data/di/HomeDataModule.kt b/samples/state-as-cells/features/home/data/src/main/java/com/originsdigital/compositeadapter/stories/data/di/HomeDataModule.kt new file mode 100644 index 0000000..7869950 --- /dev/null +++ b/samples/state-as-cells/features/home/data/src/main/java/com/originsdigital/compositeadapter/stories/data/di/HomeDataModule.kt @@ -0,0 +1,12 @@ +package com.originsdigital.compositeadapter.stories.data.di + +import com.originsdigital.compositeadapter.home.core.repository.HomeRepository +import com.originsdigital.compositeadapter.stories.data.repository.HomeRepositoryImpl +import org.koin.core.module.Module +import org.koin.dsl.module + +fun homeDataModule(): Module { + return module { + factory { HomeRepositoryImpl() } + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/home/data/src/main/java/com/originsdigital/compositeadapter/stories/data/repository/HomeRepositoryImpl.kt b/samples/state-as-cells/features/home/data/src/main/java/com/originsdigital/compositeadapter/stories/data/repository/HomeRepositoryImpl.kt new file mode 100644 index 0000000..5f426b0 --- /dev/null +++ b/samples/state-as-cells/features/home/data/src/main/java/com/originsdigital/compositeadapter/stories/data/repository/HomeRepositoryImpl.kt @@ -0,0 +1,51 @@ +package com.originsdigital.compositeadapter.stories.data.repository + +import android.graphics.Color +import com.originsdigital.compositeadapter.home.core.repository.HomeRepository +import com.originsdigital.compositeadapter.news.core.entity.NewsEntity +import com.originsdigital.compositeadapter.stories.core.entity.StoryEntity +import kotlinx.coroutines.delay +import kotlin.random.Random + +class HomeRepositoryImpl : HomeRepository { + + override suspend fun getStories(): List { + loadData() + val colors = listOf( + Color.BLACK, + Color.BLUE, + Color.CYAN, + Color.DKGRAY, + Color.GRAY, + Color.GREEN, + Color.LTGRAY, + Color.MAGENTA, + Color.RED, + Color.YELLOW + ) + return (0..20).map { index -> + StoryEntity( + id = index.toString(), + colorInt = colors[index % colors.size], + name = "Story $index" + ) + } + .filter { Random.nextBoolean() } + } + + override suspend fun getNews(): List { + loadData() + return (0..50).map { index -> + NewsEntity(id = index.toString(), text = "News $index") + } + .filter { Random.nextBoolean() } + } + + private suspend fun loadData() { + val delay = 500 + Random.nextLong(500) + delay(delay) + if (delay > 800) { + throw NullPointerException() + } + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/.gitignore b/samples/state-as-cells/features/home/ui/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/state-as-cells/features/home/ui/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/build.gradle.kts b/samples/state-as-cells/features/home/ui/build.gradle.kts new file mode 100644 index 0000000..b9e2188 --- /dev/null +++ b/samples/state-as-cells/features/home/ui/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.sampleMinSdk + targetSdk = Config.Build.targetSdk + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } + + buildFeatures { + viewBinding = true + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseUI)) + implementation(project(Config.Deps.Libs.homeCore)) + implementation(project(Config.Deps.Libs.storiesCore)) + implementation(project(Config.Deps.Libs.storiesUI)) + implementation(project(Config.Deps.Libs.newsCore)) + implementation(project(Config.Deps.Libs.newsUI)) +} \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/consumer-rules.pro b/samples/state-as-cells/features/home/ui/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/samples/state-as-cells/features/home/ui/proguard-rules.pro b/samples/state-as-cells/features/home/ui/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/samples/state-as-cells/features/home/ui/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/src/main/AndroidManifest.xml b/samples/state-as-cells/features/home/ui/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ef37bb3 --- /dev/null +++ b/samples/state-as-cells/features/home/ui/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeActivity.kt b/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeActivity.kt new file mode 100644 index 0000000..b7a379c --- /dev/null +++ b/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeActivity.kt @@ -0,0 +1,44 @@ +package com.originsdigital.compositeadapter.stateascells.home.ui + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.originsdigital.compositeadapter.stateascells.home.ui.databinding.HomeActivityBinding +import com.originsdigital.compositeadapter.ui.utils.initRecyclerView +import com.originsdigital.compositeadapter.ui.utils.submitAdapterData +import org.koin.androidx.viewmodel.ext.android.getViewModel + +class HomeActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val binding = HomeActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + val viewModel: HomeViewModel = getViewModel() + + binding.apply { + initRecyclerView( + recyclerView = recyclerView, + swipeRefreshLayout = swipeRefreshLayout, + viewModel = viewModel, + lifecycleOwner = this@HomeActivity, + stateDelegate = { adapter, state -> + submitAdapterData( + adapter = adapter, + swipeRefreshLayout = swipeRefreshLayout, + isRefreshing = state.isRefreshing, + cells = state.cells + ) + } + ) + } + } + + companion object { + fun getLaunchIntent(context: Context): Intent { + return Intent(context, HomeActivity::class.java) + } + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeUIMapper.kt b/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeUIMapper.kt new file mode 100644 index 0000000..eda0b91 --- /dev/null +++ b/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeUIMapper.kt @@ -0,0 +1,131 @@ +package com.originsdigital.compositeadapter.stateascells.home.ui + +import android.app.Application +import android.content.Context +import android.util.TypedValue +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.core.entity.Scene +import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration +import com.originsdigital.compositeadapter.news.core.entity.NewsEntity +import com.originsdigital.compositeadapter.news.ui.cell.NewsCell +import com.originsdigital.compositeadapter.stories.core.entity.StoryEntity +import com.originsdigital.compositeadapter.stories.ui.cell.StoriesCell +import com.originsdigital.compositeadapter.stories.ui.cell.StoryCell +import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI +import com.originsdigital.compositeadapter.ui.mapper.SceneMapper + +class HomeUIMapper( + app: Application, + private val sceneMapper: SceneMapper +) { + + private val firstStoryItemDecoration: SpaceItemDecoration> + private val middleStoryBigItemDecoration: SpaceItemDecoration> + private val lastStoryItemDecoration: SpaceItemDecoration> + private val firstNewsItemDecoration: SpaceItemDecoration> + private val middleNewsBigItemDecoration: SpaceItemDecoration> + private val lastNewsItemDecoration: SpaceItemDecoration> + + init { + firstStoryItemDecoration = SpaceItemDecoration( + top = app.dpToPx(8f).toInt(), + bottom = app.dpToPx(8f).toInt(), + start = app.dpToPx(8f).toInt() + ) + middleStoryBigItemDecoration = firstStoryItemDecoration + lastStoryItemDecoration = SpaceItemDecoration( + top = firstStoryItemDecoration.top, + bottom = firstStoryItemDecoration.bottom, + start = firstStoryItemDecoration.start, + end = firstStoryItemDecoration.start + ) + firstNewsItemDecoration = SpaceItemDecoration( + top = app.dpToPx(8f).toInt(), + start = app.dpToPx(8f).toInt(), + end = app.dpToPx(8f).toInt() + ) + middleNewsBigItemDecoration = firstNewsItemDecoration + lastNewsItemDecoration = SpaceItemDecoration( + top = firstStoryItemDecoration.top, + bottom = firstStoryItemDecoration.top, + start = firstStoryItemDecoration.start, + end = firstStoryItemDecoration.end + ) + } + + private fun Context.dpToPx(dp: Float): Float { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics) + } + + fun mapState(): HomeViewModel.State { + return HomeViewModel.State( + isRefreshing = false, + cells = emptyList() + ) + } + + fun mapState( + storiesScene: Scene>, + newsScene: Scene>, + onStoriesRetryClicked: ((ClickItem) -> Unit), + onNewsRetryClicked: ((ClickItem) -> Unit), + onStoryClickListener: ((ClickItem) -> Unit), + onNewsClickListener: ((ClickItem) -> Unit) + ): HomeViewModel.State { + val storiesCell = sceneMapper.mapSmallSceneToCell( + scene = storiesScene, + uniqueId = "stories", + onRetryClicked = onStoriesRetryClicked, + dataDelegate = { dataScene -> mapStories(dataScene, onStoryClickListener) } + ) + val newsCells = sceneMapper.mapSmallSceneToCells( + scene = newsScene, + uniqueId = "news", + onRetryClicked = onNewsRetryClicked, + dataDelegate = { dataScene -> mapNews(dataScene, onNewsClickListener) } + ) + return HomeViewModel.State( + isRefreshing = sceneMapper.isRefreshing(storiesScene, newsScene), + cells = listOf(storiesCell) + newsCells + ) + } + + private fun mapStories( + storiesScene: Scene.Data>, + onStoryClickListener: ((ClickItem) -> Unit) + ): Cell<*> { + return StoriesCell( + data = storiesScene.data.mapIndexed { index, story -> + val decoration = when (index) { + 0 -> firstStoryItemDecoration + storiesScene.data.size - 1 -> lastStoryItemDecoration + else -> middleStoryBigItemDecoration + } + StoryCell( + data = story, + decoration = decoration, + onClickListener = onStoryClickListener + ) + } + ) + } + + private fun mapNews( + newsScene: Scene.Data>, + onNewsClickListener: ((ClickItem) -> Unit) + ): List> { + return newsScene.data.mapIndexed { index, story -> + val decoration = when (index) { + 0 -> firstNewsItemDecoration + newsScene.data.size - 1 -> lastNewsItemDecoration + else -> middleNewsBigItemDecoration + } + NewsCell( + data = story, + decoration = decoration, + onClickListener = onNewsClickListener + ) + } + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeViewModel.kt b/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeViewModel.kt new file mode 100644 index 0000000..449e4fd --- /dev/null +++ b/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/HomeViewModel.kt @@ -0,0 +1,88 @@ +package com.originsdigital.compositeadapter.stateascells.home.ui + +import androidx.lifecycle.viewModelScope +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.core.entity.Scene +import com.originsdigital.compositeadapter.core.extensions.isEmpty +import com.originsdigital.compositeadapter.home.core.repository.HomeRepository +import com.originsdigital.compositeadapter.news.core.entity.NewsEntity +import com.originsdigital.compositeadapter.stories.core.entity.StoryEntity +import com.originsdigital.compositeadapter.ui.entity.CommonErrorUI +import com.originsdigital.compositeadapter.ui.vm.BaseStateViewModel +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn + +class HomeViewModel( + private val homeRepository: HomeRepository, + private val homeUIMapper: HomeUIMapper +) : BaseStateViewModel() { + + private val onStoriesRetryClicked: ((ClickItem) -> Unit) = { onRefresh() } + private val onNewsRetryClicked: ((ClickItem) -> Unit) = { onRefresh() } + private val onStoryClickListener: ((ClickItem) -> Unit) = { click -> + } + private val onNewsClickListener: ((ClickItem) -> Unit) = { click -> + } + + private val storiesFlow = MutableStateFlow>>(Scene.Loading()) + private val newsFlow = MutableStateFlow>>(Scene.Loading()) + + override val stateFlow: StateFlow = combine( + storiesFlow, + newsFlow + ) { storiesScene, newsScene -> + homeUIMapper.mapState( + storiesScene = storiesScene, + newsScene = newsScene, + onStoriesRetryClicked = onStoriesRetryClicked, + onNewsRetryClicked = onNewsRetryClicked, + onStoryClickListener = onStoryClickListener, + onNewsClickListener = onNewsClickListener + ) + } + .flowOn(bgDispatcher) + .stateIn(viewModelScope, SharingStarted.Lazily, homeUIMapper.mapState()) + + private var storiesJob: Job? = null + private var newsJob: Job? = null + + init { + onRefresh() + } + + override fun onRefresh() { + loadStories() + loadNews() + } + + private fun loadStories(force: Boolean = storiesFlow.value.isEmpty) { + storiesJob?.cancel() + storiesJob = loadBlockWithState( + text = "loadStories", + force = force, + sceneFlow = storiesFlow, + block = { homeRepository.getStories() } + ) + } + + private fun loadNews(force: Boolean = newsFlow.value.isEmpty) { + newsJob?.cancel() + newsJob = loadBlockWithState( + text = "loadNews", + force = force, + sceneFlow = newsFlow, + block = { homeRepository.getNews() } + ) + } + + data class State( + val isRefreshing: Boolean, + val cells: List> + ) +} \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/di/HomeUIModule.kt b/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/di/HomeUIModule.kt new file mode 100644 index 0000000..8c01b52 --- /dev/null +++ b/samples/state-as-cells/features/home/ui/src/main/java/com/originsdigital/compositeadapter/stateascells/home/ui/di/HomeUIModule.kt @@ -0,0 +1,25 @@ +package com.originsdigital.compositeadapter.stateascells.home.ui.di + +import com.originsdigital.compositeadapter.stateascells.home.ui.HomeUIMapper +import com.originsdigital.compositeadapter.stateascells.home.ui.HomeViewModel +import org.koin.android.ext.koin.androidApplication +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.module.Module +import org.koin.dsl.module + +fun homeUIModule(): Module { + return module { + factory { + HomeUIMapper( + app = androidApplication(), + sceneMapper = get() + ) + } + viewModel { + HomeViewModel( + homeRepository = get(), + homeUIMapper = get() + ) + } + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/home/ui/src/main/res/layout/home_activity.xml b/samples/state-as-cells/features/home/ui/src/main/res/layout/home_activity.xml new file mode 100644 index 0000000..ac92298 --- /dev/null +++ b/samples/state-as-cells/features/home/ui/src/main/res/layout/home_activity.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/samples/state-as-cells/features/news/core/.gitignore b/samples/state-as-cells/features/news/core/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/state-as-cells/features/news/core/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/features/messages/core/build.gradle.kts b/samples/state-as-cells/features/news/core/build.gradle.kts similarity index 100% rename from sample/features/messages/core/build.gradle.kts rename to samples/state-as-cells/features/news/core/build.gradle.kts diff --git a/samples/state-as-cells/features/news/core/consumer-rules.pro b/samples/state-as-cells/features/news/core/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/samples/state-as-cells/features/news/core/proguard-rules.pro b/samples/state-as-cells/features/news/core/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/samples/state-as-cells/features/news/core/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/state-as-cells/features/news/core/src/main/java/com/originsdigital/compositeadapter/news/core/entity/NewsEntity.kt b/samples/state-as-cells/features/news/core/src/main/java/com/originsdigital/compositeadapter/news/core/entity/NewsEntity.kt new file mode 100644 index 0000000..6a9aba2 --- /dev/null +++ b/samples/state-as-cells/features/news/core/src/main/java/com/originsdigital/compositeadapter/news/core/entity/NewsEntity.kt @@ -0,0 +1,6 @@ +package com.originsdigital.compositeadapter.news.core.entity + +data class NewsEntity( + val id: String, + val text: String? +) \ No newline at end of file diff --git a/samples/state-as-cells/features/news/ui/.gitignore b/samples/state-as-cells/features/news/ui/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/state-as-cells/features/news/ui/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/features/messages/ui/build.gradle.kts b/samples/state-as-cells/features/news/ui/build.gradle.kts similarity index 78% rename from sample/features/messages/ui/build.gradle.kts rename to samples/state-as-cells/features/news/ui/build.gradle.kts index a5ebc58..c117c3c 100644 --- a/sample/features/messages/ui/build.gradle.kts +++ b/samples/state-as-cells/features/news/ui/build.gradle.kts @@ -1,14 +1,13 @@ plugins { id(Config.Plugins.androidLibrary) kotlin(Config.Plugins.android) - kotlin(Config.Plugins.kapt) } android { compileSdk = Config.Build.compileSdk defaultConfig { - minSdk = Config.Build.minSdk + minSdk = Config.Build.sampleMinSdk targetSdk = Config.Build.targetSdk } @@ -21,12 +20,11 @@ android { } buildFeatures { - dataBinding = true viewBinding = true } } dependencies { implementation(project(Config.Deps.Libs.baseUI)) - implementation(project(Config.Deps.Libs.messagesCore)) + implementation(project(Config.Deps.Libs.newsCore)) } \ No newline at end of file diff --git a/samples/state-as-cells/features/news/ui/consumer-rules.pro b/samples/state-as-cells/features/news/ui/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/samples/state-as-cells/features/news/ui/proguard-rules.pro b/samples/state-as-cells/features/news/ui/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/samples/state-as-cells/features/news/ui/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/state-as-cells/features/news/ui/src/main/AndroidManifest.xml b/samples/state-as-cells/features/news/ui/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a46ba7d --- /dev/null +++ b/samples/state-as-cells/features/news/ui/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/samples/state-as-cells/features/news/ui/src/main/java/com/originsdigital/compositeadapter/news/ui/cell/NewsCell.kt b/samples/state-as-cells/features/news/ui/src/main/java/com/originsdigital/compositeadapter/news/ui/cell/NewsCell.kt new file mode 100644 index 0000000..f25e646 --- /dev/null +++ b/samples/state-as-cells/features/news/ui/src/main/java/com/originsdigital/compositeadapter/news/ui/cell/NewsCell.kt @@ -0,0 +1,37 @@ +package com.originsdigital.compositeadapter.news.ui.cell + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.news.core.entity.NewsEntity +import com.originsdigital.compositeadapter.news.ui.R +import com.originsdigital.compositeadapter.news.ui.databinding.NewsCellBinding +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingViewHolder + +data class NewsCell( + override val data: NewsEntity, + override val decoration: ItemDecoration>?, + override val onClickListener: ((ClickItem) -> Unit)? +) : ViewBindingCell() { + + override val uniqueId: String = data.id + override val layoutId: Int = R.layout.news_cell + + override fun createViewBinding( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): NewsCellBinding { + return NewsCellBinding.inflate(inflater, parent, false) + } + + override fun onBindViewHolder( + holder: ViewBindingViewHolder, + position: Int + ) { + holder.binding.text.text = data.text + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/news/ui/src/main/res/layout/news_cell.xml b/samples/state-as-cells/features/news/ui/src/main/res/layout/news_cell.xml new file mode 100644 index 0000000..b007c37 --- /dev/null +++ b/samples/state-as-cells/features/news/ui/src/main/res/layout/news_cell.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/core/.gitignore b/samples/state-as-cells/features/stories/core/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/state-as-cells/features/stories/core/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/core/build.gradle.kts b/samples/state-as-cells/features/stories/core/build.gradle.kts new file mode 100644 index 0000000..3765cf9 --- /dev/null +++ b/samples/state-as-cells/features/stories/core/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + kotlin("jvm") + `java-library` +} + +java.sourceCompatibility = Config.Build.javaVersion +java.targetCompatibility = Config.Build.javaVersion + +project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java).configureEach { + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseCore)) +} \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/core/consumer-rules.pro b/samples/state-as-cells/features/stories/core/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/samples/state-as-cells/features/stories/core/proguard-rules.pro b/samples/state-as-cells/features/stories/core/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/samples/state-as-cells/features/stories/core/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/core/src/main/java/com/originsdigital/compositeadapter/stories/core/entity/StoryEntity.kt b/samples/state-as-cells/features/stories/core/src/main/java/com/originsdigital/compositeadapter/stories/core/entity/StoryEntity.kt new file mode 100644 index 0000000..be3ea29 --- /dev/null +++ b/samples/state-as-cells/features/stories/core/src/main/java/com/originsdigital/compositeadapter/stories/core/entity/StoryEntity.kt @@ -0,0 +1,7 @@ +package com.originsdigital.compositeadapter.stories.core.entity + +data class StoryEntity( + val id: String, + val colorInt: Int, + val name: String? +) \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/ui/.gitignore b/samples/state-as-cells/features/stories/ui/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/samples/state-as-cells/features/stories/ui/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/ui/build.gradle.kts b/samples/state-as-cells/features/stories/ui/build.gradle.kts new file mode 100644 index 0000000..e515fe1 --- /dev/null +++ b/samples/state-as-cells/features/stories/ui/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + id(Config.Plugins.androidLibrary) + kotlin(Config.Plugins.android) +} + +android { + compileSdk = Config.Build.compileSdk + + defaultConfig { + minSdk = Config.Build.sampleMinSdk + targetSdk = Config.Build.targetSdk + } + + compileOptions { + sourceCompatibility = Config.Build.javaVersion + targetCompatibility = Config.Build.javaVersion + } + kotlinOptions { + jvmTarget = Config.Build.javaVersion.toString() + } + + buildFeatures { + viewBinding = true + } +} + +dependencies { + implementation(project(Config.Deps.Libs.baseUI)) + implementation(project(Config.Deps.Libs.storiesCore)) +} \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/ui/consumer-rules.pro b/samples/state-as-cells/features/stories/ui/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/samples/state-as-cells/features/stories/ui/proguard-rules.pro b/samples/state-as-cells/features/stories/ui/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/samples/state-as-cells/features/stories/ui/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/ui/src/main/AndroidManifest.xml b/samples/state-as-cells/features/stories/ui/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c170164 --- /dev/null +++ b/samples/state-as-cells/features/stories/ui/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/ui/src/main/java/com/originsdigital/compositeadapter/stories/ui/cell/StoriesCell.kt b/samples/state-as-cells/features/stories/ui/src/main/java/com/originsdigital/compositeadapter/stories/ui/cell/StoriesCell.kt new file mode 100644 index 0000000..b8fccfb --- /dev/null +++ b/samples/state-as-cells/features/stories/ui/src/main/java/com/originsdigital/compositeadapter/stories/ui/cell/StoriesCell.kt @@ -0,0 +1,63 @@ +package com.originsdigital.compositeadapter.stories.ui.cell + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.originsdigital.compositeadapter.adapter.CompositeAdapter +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.stories.ui.R +import com.originsdigital.compositeadapter.ui.cell.base.BaseCell +import com.originsdigital.compositeadapter.ui.cell.base.BaseViewHolder +import com.originsdigital.compositeadapter.ui.utils.initRecyclerView + +data class StoriesCell( + override val data: List, + override val decoration: ItemDecoration>? = null +) : BaseCell, StoriesCell.CustomViewHolder>() { + + override val uniqueId: String = "StoriesCell" + override val layoutId: Int = R.layout.stories_cell + + override fun createViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): CustomViewHolder { + return CustomViewHolder( + recyclerView = RecyclerView(inflater.context).apply { + layoutParams = RecyclerView.LayoutParams( + RecyclerView.LayoutParams.MATCH_PARENT, + RecyclerView.LayoutParams.WRAP_CONTENT + ) + } + ) + } + + override fun onBindViewHolder( + holder: CustomViewHolder, + position: Int + ) { + holder.compositeAdapter.submitList(data) + } + + override fun getChangePayload(newItem: Cell<*>): Any = "RECYCLER_VIEW" + + class CustomViewHolder(recyclerView: RecyclerView) : BaseViewHolder(recyclerView) { + + val compositeAdapter = CompositeAdapter() + + init { + initRecyclerView( + recyclerView = recyclerView, + adapter = compositeAdapter, + layoutManager = LinearLayoutManager( + recyclerView.context, + LinearLayoutManager.HORIZONTAL, + false + ) + ) + } + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/ui/src/main/java/com/originsdigital/compositeadapter/stories/ui/cell/StoryCell.kt b/samples/state-as-cells/features/stories/ui/src/main/java/com/originsdigital/compositeadapter/stories/ui/cell/StoryCell.kt new file mode 100644 index 0000000..6a146f8 --- /dev/null +++ b/samples/state-as-cells/features/stories/ui/src/main/java/com/originsdigital/compositeadapter/stories/ui/cell/StoryCell.kt @@ -0,0 +1,40 @@ +package com.originsdigital.compositeadapter.stories.ui.cell + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.originsdigital.compositeadapter.cell.Cell +import com.originsdigital.compositeadapter.cell.ClickItem +import com.originsdigital.compositeadapter.decoration.ItemDecoration +import com.originsdigital.compositeadapter.stories.core.entity.StoryEntity +import com.originsdigital.compositeadapter.stories.ui.R +import com.originsdigital.compositeadapter.stories.ui.databinding.StoryCellBinding +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingCell +import com.originsdigital.compositeadapter.ui.cell.viewbinding.ViewBindingViewHolder + +data class StoryCell( + override val data: StoryEntity, + override val decoration: ItemDecoration>? = null, + override val onClickListener: ((ClickItem) -> Unit)? = null +) : ViewBindingCell() { + + override val uniqueId: String = data.id + override val layoutId: Int = R.layout.story_cell + + override fun createViewBinding( + inflater: LayoutInflater, + parent: ViewGroup, + viewType: Int + ): StoryCellBinding { + return StoryCellBinding.inflate(inflater, parent, false) + } + + override fun onBindViewHolder( + holder: ViewBindingViewHolder, + position: Int + ) { + holder.binding.apply { + image.setColorFilter(data.colorInt) + text.text = data.name + } + } +} \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/ui/src/main/res/drawable/story_cell_image_background.xml b/samples/state-as-cells/features/stories/ui/src/main/res/drawable/story_cell_image_background.xml new file mode 100644 index 0000000..b9699b6 --- /dev/null +++ b/samples/state-as-cells/features/stories/ui/src/main/res/drawable/story_cell_image_background.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/samples/state-as-cells/features/stories/ui/src/main/res/layout/story_cell.xml b/samples/state-as-cells/features/stories/ui/src/main/res/layout/story_cell.xml new file mode 100644 index 0000000..1c49cf2 --- /dev/null +++ b/samples/state-as-cells/features/stories/ui/src/main/res/layout/story_cell.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/sample/features/messages/ui/src/main/res/values/ids.xml b/samples/state-as-cells/features/stories/ui/src/main/res/values/ids.xml similarity index 52% rename from sample/features/messages/ui/src/main/res/values/ids.xml rename to samples/state-as-cells/features/stories/ui/src/main/res/values/ids.xml index 02ecd7f..aa8d1b7 100644 --- a/sample/features/messages/ui/src/main/res/values/ids.xml +++ b/samples/state-as-cells/features/stories/ui/src/main/res/values/ids.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 4586f95..1b56029 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,13 +3,20 @@ rootProject.name = "CompositeAdapter_Android" include(":composite-adapter") include(":samples:basic") + include(":samples:decorations") + include(":samples:different-bindings") -include(":samples:inner-recyclerview") -include(":sample:features:base:core") -include(":sample:features:base:ui") +include(":samples:inner-recyclerview") -include(":sample:features:messages:data") -include(":sample:features:messages:core") -include(":sample:features:messages:ui") \ No newline at end of file +include(":samples:state-as-cells:app") +include(":samples:state-as-cells:features:base:core") +include(":samples:state-as-cells:features:base:ui") +include(":samples:state-as-cells:features:home:data") +include(":samples:state-as-cells:features:home:core") +include(":samples:state-as-cells:features:home:ui") +include(":samples:state-as-cells:features:stories:core") +include(":samples:state-as-cells:features:stories:ui") +include(":samples:state-as-cells:features:news:core") +include(":samples:state-as-cells:features:news:ui") \ No newline at end of file From 7bc6a4afc62a1f45a08c773e1a0327d1b515677d Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Wed, 19 Jan 2022 17:33:22 +0300 Subject: [PATCH 13/19] feat(sample): remove unused code --- .../sample/differentbindings/ui/cell/base/BaseCell.kt | 10 ---------- .../compositeadapter/ui/cell/base/BaseCell.kt | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt index b2907e0..93d59b7 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt @@ -1,11 +1,9 @@ package com.originsdigital.compositeadapter.sample.differentbindings.ui.cell.base -import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.cell.ClickItem abstract class BaseCell : Cell { @@ -28,10 +26,6 @@ abstract class BaseCell : Cell { fun onViewRecycled(holder: VIEW_HOLDER) = Unit - fun onClicked(context: Context, holder: VIEW_HOLDER, position: Int) { - onClickListener?.invoke(ClickItem(context, holder, position, data)) - } - final override fun onCreateViewHolder( inflater: LayoutInflater, parent: ViewGroup, @@ -64,10 +58,6 @@ abstract class BaseCell : Cell { onViewRecycled(castHolder(holder)) } - final override fun onClicked(context: Context, holder: RecyclerView.ViewHolder, position: Int) { - onClicked(context, castHolder(holder), position) - } - protected fun castHolder(holder: RecyclerView.ViewHolder): VIEW_HOLDER { @Suppress("UNCHECKED_CAST") return holder as VIEW_HOLDER diff --git a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt index 542b061..65c023c 100644 --- a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt @@ -1,11 +1,9 @@ package com.originsdigital.compositeadapter.ui.cell.base -import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell -import com.originsdigital.compositeadapter.cell.ClickItem abstract class BaseCell : Cell { @@ -28,10 +26,6 @@ abstract class BaseCell : Cell { fun onViewRecycled(holder: VIEW_HOLDER) = Unit - fun onClicked(context: Context, holder: VIEW_HOLDER, position: Int) { - onClickListener?.invoke(ClickItem(context, holder, position, data)) - } - final override fun onCreateViewHolder( inflater: LayoutInflater, parent: ViewGroup, @@ -64,10 +58,6 @@ abstract class BaseCell : Cell { onViewRecycled(castHolder(holder)) } - final override fun onClicked(context: Context, holder: RecyclerView.ViewHolder, position: Int) { - onClicked(context, castHolder(holder), position) - } - protected fun castHolder(holder: RecyclerView.ViewHolder): VIEW_HOLDER { @Suppress("UNCHECKED_CAST") return holder as VIEW_HOLDER From b3643bc7dd0fe89a38fab2fef72f00e74e52c4f5 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Wed, 19 Jan 2022 17:42:19 +0300 Subject: [PATCH 14/19] feat(adapter): add Deprecated for ViewBinding caching methods --- .../compositeadapter/utils/CompositeAdapterExtensions.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/composite-adapter/src/main/java/com/originsdigital/compositeadapter/utils/CompositeAdapterExtensions.kt b/composite-adapter/src/main/java/com/originsdigital/compositeadapter/utils/CompositeAdapterExtensions.kt index a8005b7..1ca0793 100644 --- a/composite-adapter/src/main/java/com/originsdigital/compositeadapter/utils/CompositeAdapterExtensions.kt +++ b/composite-adapter/src/main/java/com/originsdigital/compositeadapter/utils/CompositeAdapterExtensions.kt @@ -26,12 +26,17 @@ fun View.setCompositeAdapterItem(item: ITEM) { setTag(R.id.composite_adapter_item_tag, item) } +@Deprecated( + level = DeprecationLevel.ERROR, + message = "Get 'ViewBinding' from the 'ViewHolder'" +) inline fun RecyclerView.ViewHolder.getViewBinding( crossinline delegate: (View) -> VIEW_BINDING ): VIEW_BINDING { val binding = itemView.getTag(R.id.composite_adapter_viewbinding_tag) return if (binding == null) { delegate(itemView).also { + @Suppress("DEPRECATION_ERROR") setViewBinding(it) } } else { @@ -39,6 +44,10 @@ inline fun RecyclerView.ViewHolder.getViewBinding( } } +@Deprecated( + level = DeprecationLevel.ERROR, + message = "Save 'ViewBinding' in the 'ViewHolder'" +) fun RecyclerView.ViewHolder.setViewBinding(binding: VIEW_BINDING) { itemView.setTag(R.id.composite_adapter_viewbinding_tag, binding) } \ No newline at end of file From c435609ee5be1ff708781a852407f2be86493bd4 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Wed, 19 Jan 2022 18:01:17 +0300 Subject: [PATCH 15/19] feat(sample): suppress unused_parameter warnings --- .../sample/differentbindings/ui/cell/base/BaseCell.kt | 1 + .../com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt index 93d59b7..eb03992 100644 --- a/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt +++ b/samples/different-bindings/src/main/java/com/originsdigital/compositeadapter/sample/differentbindings/ui/cell/base/BaseCell.kt @@ -5,6 +5,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell +@Suppress("UNUSED_PARAMETER") abstract class BaseCell : Cell { abstract fun createViewHolder( diff --git a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt index 65c023c..943fae1 100644 --- a/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt +++ b/samples/state-as-cells/features/base/ui/src/main/java/com/originsdigital/compositeadapter/ui/cell/base/BaseCell.kt @@ -5,6 +5,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell +@Suppress("UNUSED_PARAMETER") abstract class BaseCell : Cell { abstract fun createViewHolder( From 794d20500390f929e5e8833049d22f5dd0ef1c8f Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Wed, 19 Jan 2022 18:07:55 +0300 Subject: [PATCH 16/19] feat(adapter): add getCompositeAdapterItem overloads to get current Cell from the ViewHolder/Root view --- .../adapter/BaseCompositeAdapter.kt | 8 ++++---- .../utils/CompositeAdapterExtensions.kt | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/composite-adapter/src/main/java/com/originsdigital/compositeadapter/adapter/BaseCompositeAdapter.kt b/composite-adapter/src/main/java/com/originsdigital/compositeadapter/adapter/BaseCompositeAdapter.kt index b18b5a1..837cbaa 100644 --- a/composite-adapter/src/main/java/com/originsdigital/compositeadapter/adapter/BaseCompositeAdapter.kt +++ b/composite-adapter/src/main/java/com/originsdigital/compositeadapter/adapter/BaseCompositeAdapter.kt @@ -93,21 +93,21 @@ abstract class BaseCompositeAdapter>( } protected open fun storeCellInHolder(holder: RecyclerView.ViewHolder, position: Int) { - holder.itemView.setCompositeAdapterItem(getItem(position)) + holder.setCompositeAdapterItem(getItem(position)) } override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) { super.onViewAttachedToWindow(holder) - holder.itemView.getCompositeAdapterItem().onViewAttachedToWindow(holder) + holder.getCompositeAdapterItem().onViewAttachedToWindow(holder) } override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { super.onViewDetachedFromWindow(holder) - holder.itemView.getCompositeAdapterItem().onViewDetachedFromWindow(holder) + holder.getCompositeAdapterItem().onViewDetachedFromWindow(holder) } override fun onViewRecycled(holder: RecyclerView.ViewHolder) { super.onViewRecycled(holder) - holder.itemView.getCompositeAdapterItem().onViewRecycled(holder) + holder.getCompositeAdapterItem().onViewRecycled(holder) } } \ No newline at end of file diff --git a/composite-adapter/src/main/java/com/originsdigital/compositeadapter/utils/CompositeAdapterExtensions.kt b/composite-adapter/src/main/java/com/originsdigital/compositeadapter/utils/CompositeAdapterExtensions.kt index 1ca0793..014bf77 100644 --- a/composite-adapter/src/main/java/com/originsdigital/compositeadapter/utils/CompositeAdapterExtensions.kt +++ b/composite-adapter/src/main/java/com/originsdigital/compositeadapter/utils/CompositeAdapterExtensions.kt @@ -8,6 +8,7 @@ import android.content.Context import android.view.View import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.R +import com.originsdigital.compositeadapter.cell.Cell val RecyclerView.ViewHolder.context: Context get() = itemView.context @@ -17,6 +18,22 @@ var View.compositeAdapterViewHolder: RecyclerView.ViewHolder setTag(R.id.composite_adapter_view_holder_tag, value) } +fun > CELL.getCompositeAdapterItem(viewHolder: RecyclerView.ViewHolder): CELL { + return viewHolder.itemView.getCompositeAdapterItem() +} + +fun > CELL.getCompositeAdapterItem(root: View): CELL { + return root.getCompositeAdapterItem() +} + +fun RecyclerView.ViewHolder.getCompositeAdapterItem(): ITEM { + return itemView.getCompositeAdapterItem() +} + +fun RecyclerView.ViewHolder.setCompositeAdapterItem(item: ITEM) { + return itemView.setCompositeAdapterItem(item) +} + fun View.getCompositeAdapterItem(): ITEM { @Suppress("UNCHECKED_CAST") return getTag(R.id.composite_adapter_item_tag) as ITEM From a037d5eb13aab3c164994246c208faae91da5f1c Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Wed, 19 Jan 2022 19:29:21 +0300 Subject: [PATCH 17/19] feat(sample): update decorations --- .../decorations/ui/DecorationsActivity.kt | 8 +++++++- .../ui/decorations/SampleItemDecoration.kt | 18 ++++++++++++++---- .../src/main/res/layout/decorations_cell.xml | 1 - 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/DecorationsActivity.kt b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/DecorationsActivity.kt index 3baa403..ecef19c 100644 --- a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/DecorationsActivity.kt +++ b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/DecorationsActivity.kt @@ -28,7 +28,8 @@ class DecorationsActivity : AppCompatActivity() { val dataHolder = DataHolder( space = dpToPx(20f).toInt(), - radius = dpToPx(6f), + radius = dpToPx(8f), + strokeWidth = dpToPx(2f), dividerHeight = dpToPx(1f).toInt(), dividerColorInt = Color.GRAY, backgroundColorInt = Color.DKGRAY @@ -75,6 +76,7 @@ class DecorationsActivity : AppCompatActivity() { private class DataHolder( space: Int, radius: Float, + strokeWidth: Float, dividerHeight: Int, @ColorInt dividerColorInt: Int, @ColorInt backgroundColorInt: Int @@ -89,6 +91,7 @@ class DecorationsActivity : AppCompatActivity() { singleItemDecoration = SampleItemDecoration( type = SampleItemDecoration.Type.SINGLE, radius = radius, + strokeWidth = strokeWidth, dividerHeight = dividerHeight, dividerColorInt = dividerColorInt, backgroundColorInt = backgroundColorInt, @@ -100,6 +103,7 @@ class DecorationsActivity : AppCompatActivity() { topItemDecoration = SampleItemDecoration( type = SampleItemDecoration.Type.TOP, radius = radius, + strokeWidth = strokeWidth, dividerHeight = dividerHeight, dividerColorInt = dividerColorInt, backgroundColorInt = backgroundColorInt, @@ -111,6 +115,7 @@ class DecorationsActivity : AppCompatActivity() { middleItemDecoration = SampleItemDecoration( type = SampleItemDecoration.Type.MIDDLE, radius = radius, + strokeWidth = strokeWidth, dividerHeight = dividerHeight, dividerColorInt = dividerColorInt, backgroundColorInt = backgroundColorInt, @@ -121,6 +126,7 @@ class DecorationsActivity : AppCompatActivity() { bottomItemDecoration = SampleItemDecoration( type = SampleItemDecoration.Type.BOTTOM, radius = radius, + strokeWidth = strokeWidth, dividerHeight = dividerHeight, dividerColorInt = dividerColorInt, backgroundColorInt = backgroundColorInt, diff --git a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/decorations/SampleItemDecoration.kt b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/decorations/SampleItemDecoration.kt index 584736a..804ce09 100644 --- a/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/decorations/SampleItemDecoration.kt +++ b/samples/decorations/src/main/java/com/originsdigital/compositeadapter/sample/decorations/ui/decorations/SampleItemDecoration.kt @@ -8,10 +8,12 @@ import androidx.annotation.ColorInt import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.decoration.SpaceItemDecoration +import kotlin.math.roundToInt data class SampleItemDecoration( private val type: Type, private val radius: Float, + private val strokeWidth: Float, private val dividerHeight: Int, @ColorInt val dividerColorInt: Int, @ColorInt val backgroundColorInt: Int, @@ -25,6 +27,9 @@ data class SampleItemDecoration( color = dividerColorInt } private val backgroundPaint = Paint().apply { + style = Paint.Style.STROKE + isAntiAlias = true + strokeWidth = this@SampleItemDecoration.strokeWidth color = backgroundColorInt } private val itemBounds = Rect() @@ -38,6 +43,9 @@ data class SampleItemDecoration( ) { super.onDraw(canvas, view, parent, state, item) parent.layoutManager?.getDecoratedBoundsWithMargins(view, itemBounds) + val translationY = view.translationY.roundToInt() + itemBounds.bottom = itemBounds.bottom + translationY + itemBounds.top = itemBounds.top + translationY val drawBottomDivider: Boolean val roundedTop: Boolean val roundedBottom: Boolean @@ -65,6 +73,7 @@ data class SampleItemDecoration( } canvas.drawRoundedRect( paint = backgroundPaint, + strokeWidth = strokeWidth / 2, left = itemBounds.left.toFloat() + start, top = itemBounds.top.toFloat() + top, right = itemBounds.right.toFloat() - end, @@ -83,6 +92,7 @@ data class SampleItemDecoration( private fun Canvas.drawRoundedRect( paint: Paint, + strokeWidth: Float, left: Float, top: Float, right: Float, @@ -94,10 +104,10 @@ data class SampleItemDecoration( save() clipRect(left, top, right, bottom) drawRoundRect( - left, - if (withTop) top else top - radius, - right, - if (withBottom) bottom else bottom + radius, + left + strokeWidth, + if (withTop) top + strokeWidth else top - radius, + right - strokeWidth, + if (withBottom) bottom - strokeWidth else bottom + radius, radius, radius, paint diff --git a/samples/decorations/src/main/res/layout/decorations_cell.xml b/samples/decorations/src/main/res/layout/decorations_cell.xml index 6cc3a92..a383a7c 100644 --- a/samples/decorations/src/main/res/layout/decorations_cell.xml +++ b/samples/decorations/src/main/res/layout/decorations_cell.xml @@ -7,5 +7,4 @@ android:background="?selectableItemBackground" android:padding="20dp" android:textAppearance="@style/TextAppearance.AppCompat.Medium" - android:textColor="@android:color/white" tools:text="TEXT" /> \ No newline at end of file From 8efc2e84997e5bdd9ae052a0be1cad1c7ec2fc73 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Thu, 20 Jan 2022 10:26:46 +0300 Subject: [PATCH 18/19] feat(sample): update libs --- buildSrc/src/main/kotlin/Deps.kt | 12 ++++++------ .../innerrecyclerview/ui/InnerRecyclerActivity.kt | 2 -- .../innerrecyclerview/ui/cell/InnerRecycler1Cell.kt | 3 --- .../innerrecyclerview/ui/cell/InnerRecycler2Cell.kt | 3 --- .../innerrecyclerview/ui/entity/InnerRecyclerUI.kt | 2 -- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index 3692569..6573433 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -3,7 +3,7 @@ import org.gradle.api.JavaVersion object Config { object Build { - const val kotlinVersion = "1.6.0" + const val kotlinVersion = "1.6.10" const val compileSdk = 31 const val minSdk = 17 @@ -37,10 +37,10 @@ object Config { object Deps { object AndroidX { - const val appcompat = "androidx.appcompat:appcompat:1.4.0" + const val appcompat = "androidx.appcompat:appcompat:1.4.1" const val swipeRefresh = "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" const val recycler = "androidx.recyclerview:recyclerview:1.2.1" - const val core = "androidx.core:core-ktx:1.6.0" + const val core = "androidx.core:core-ktx:1.7.0" private const val lifecycleVersion = "2.4.0" const val lifecycle = "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" const val lifecycleViewModelKtx = "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion" @@ -48,11 +48,11 @@ object Config { } object Material { - const val material = "com.google.android.material:material:1.4.0" + const val material = "com.google.android.material:material:1.5.0" } object Coroutines { - private const val version = "1.5.2" + private const val version = "1.6.0" const val core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version" const val android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version" } @@ -64,7 +64,7 @@ object Config { } object Koin { - private const val version = "3.1.2" + private const val version = "3.1.5" const val core = "io.insert-koin:koin-core:$version" const val android = "io.insert-koin:koin-android:$version" } diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt index a65c607..572a37e 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/InnerRecyclerActivity.kt @@ -71,7 +71,6 @@ class InnerRecyclerActivity : AppCompatActivity() { private class DataHolder(space: Int) { private val scrollStatesHolder = ScrollStatesHolder() - private val recycledViewPool = RecyclerView.RecycledViewPool() private val singleItemDecoration: ItemDecoration> private val firstItemDecoration: ItemDecoration> @@ -131,7 +130,6 @@ class InnerRecyclerActivity : AppCompatActivity() { val recyclerUI = InnerRecyclerUI( id = recyclerId.toString(), cells = cells, - recycledViewPool = recycledViewPool, scrollStatesHolder = scrollStatesHolder ) if (useClearerVersion) { diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt index eda1878..6fb47e0 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler1Cell.kt @@ -79,9 +79,6 @@ data class InnerRecycler1Cell( } fun setData(data: InnerRecyclerUI) { - if (binding.recyclerView.recycledViewPool != data.recycledViewPool) { - binding.recyclerView.setRecycledViewPool(data.recycledViewPool) - } adapter.submitList(data.cells) data.scrollStatesHolder.setupRecyclerView(data.id, binding.recyclerView) } diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt index 4384284..297401f 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/cell/InnerRecycler2Cell.kt @@ -44,9 +44,6 @@ data class InnerRecycler2Cell( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { (holder as SampleViewHolder).binding.apply { - if (recyclerView.recycledViewPool != data.recycledViewPool) { - recyclerView.setRecycledViewPool(data.recycledViewPool) - } (recyclerView.adapter as CompositeAdapter).submitList(data.cells) data.scrollStatesHolder.setupRecyclerView(uniqueId, holder.binding.recyclerView) } diff --git a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt index 559f5f1..9e50ab3 100644 --- a/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt +++ b/samples/inner-recyclerview/src/main/java/com/originsdigital/compositeadapter/sample/innerrecyclerview/ui/entity/InnerRecyclerUI.kt @@ -1,12 +1,10 @@ package com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.entity -import androidx.recyclerview.widget.RecyclerView import com.originsdigital.compositeadapter.cell.Cell import com.originsdigital.compositeadapter.sample.innerrecyclerview.ui.stateholder.ScrollStatesHolder data class InnerRecyclerUI( val id: String, val cells: List>, - val recycledViewPool: RecyclerView.RecycledViewPool, val scrollStatesHolder: ScrollStatesHolder ) \ No newline at end of file From 3dd0930a7c5c1f74c047ef2580ca35cb40231440 Mon Sep 17 00:00:00 2001 From: Woffkaa Date: Fri, 21 Jan 2022 14:50:42 +0300 Subject: [PATCH 19/19] feat(build): v1.0.2 --- buildSrc/src/main/kotlin/Deps.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index 6573433..44bf598 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -15,7 +15,7 @@ object Config { const val packageNameDev = "io.github.netcosports.compositeadapter.sample" const val packageNameProd = "io.github.netcosports.compositeadapter.sample" - const val versionName = "1.0.1" + const val versionName = "1.0.2" const val versionOffset = 0 }