From 88dedd3b5250a48a175eea077d5d998aa08e1124 Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Wed, 1 Apr 2026 13:51:19 -0600 Subject: [PATCH 1/2] Migrate CategoriesFragment from DataBinding/RecyclerView to Compose Co-Authored-By: Claude Sonnet 4.6 --- ui/article-renderer/build.gradle.kts | 7 +- .../ui/categories/CategoriesAdapter.kt | 50 --------- .../ui/categories/CategoriesFragment.kt | 100 +++++++++++++----- .../layout/article_categories_fragment.xml | 7 -- .../main/res/layout/list_item_category.xml | 42 -------- 5 files changed, 77 insertions(+), 129 deletions(-) delete mode 100644 ui/article-renderer/src/main/kotlin/org/cru/godtools/article/ui/categories/CategoriesAdapter.kt delete mode 100644 ui/article-renderer/src/main/res/layout/article_categories_fragment.xml delete mode 100644 ui/article-renderer/src/main/res/layout/list_item_category.xml diff --git a/ui/article-renderer/build.gradle.kts b/ui/article-renderer/build.gradle.kts index 1c5c7ba26d..d1cf2f30be 100644 --- a/ui/article-renderer/build.gradle.kts +++ b/ui/article-renderer/build.gradle.kts @@ -13,7 +13,6 @@ android { buildFeatures { dataBinding = true - viewBinding = true } } @@ -25,20 +24,16 @@ dependencies { implementation(libs.androidx.compose.material3) implementation(libs.androidx.fragment.ktx) implementation(libs.androidx.lifecycle.livedata.ktx) - implementation(libs.androidx.constraintlayout) - implementation(libs.androidx.recyclerview) implementation(libs.gtoSupport.androidx.fragment) implementation(libs.gtoSupport.androidx.lifecycle) - implementation(libs.gtoSupport.androidx.recyclerview) implementation(libs.gtoSupport.core) implementation(libs.gtoSupport.kotlin.coroutines) - implementation(libs.gtoSupport.picasso) implementation(libs.gtoSupport.sync) - implementation(libs.gtoSupport.util) implementation(libs.colormath.jetpack.compose) implementation(libs.dagger) + implementation(libs.godtoolsShared.renderer) implementation(libs.hilt) implementation(libs.splitties.fragmentargs) diff --git a/ui/article-renderer/src/main/kotlin/org/cru/godtools/article/ui/categories/CategoriesAdapter.kt b/ui/article-renderer/src/main/kotlin/org/cru/godtools/article/ui/categories/CategoriesAdapter.kt deleted file mode 100644 index 4cc1cc7903..0000000000 --- a/ui/article-renderer/src/main/kotlin/org/cru/godtools/article/ui/categories/CategoriesAdapter.kt +++ /dev/null @@ -1,50 +0,0 @@ -package org.cru.godtools.article.ui.categories - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.databinding.ObservableField -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.Observer -import androidx.recyclerview.widget.RecyclerView.NO_ID -import org.ccci.gto.android.common.androidx.recyclerview.adapter.SimpleDataBindingAdapter -import org.ccci.gto.android.common.util.Ids -import org.cru.godtools.shared.tool.parser.model.Category -import org.cru.godtools.tool.article.databinding.ListItemCategoryBinding - -internal class CategoriesAdapter(lifecycleOwner: LifecycleOwner? = null) : - SimpleDataBindingAdapter(lifecycleOwner), - Observer?> { - init { - setHasStableIds(true) - } - - val callbacks = ObservableField() - var categories: List? = null - set(value) { - field = value - notifyDataSetChanged() - } - - override fun getItemCount() = categories?.size ?: 0 - private fun getItem(position: Int) = categories?.get(position) - override fun getItemId(position: Int) = getItem(position)?.id?.let { Ids.generate(it) } ?: NO_ID - - // region Lifecycle - override fun onChanged(value: List?) { - categories = value - } - - override fun onCreateViewDataBinding(parent: ViewGroup, viewType: Int) = - ListItemCategoryBinding.inflate(LayoutInflater.from(parent.context), parent, false) - .also { it.callbacks = callbacks } - - override fun onBindViewDataBinding(binding: ListItemCategoryBinding, position: Int) { - binding.category = getItem(position) - } - - override fun onViewDataBindingRecycled(binding: ListItemCategoryBinding) { - super.onViewDataBindingRecycled(binding) - binding.category = null - } - // endregion Lifecycle -} diff --git a/ui/article-renderer/src/main/kotlin/org/cru/godtools/article/ui/categories/CategoriesFragment.kt b/ui/article-renderer/src/main/kotlin/org/cru/godtools/article/ui/categories/CategoriesFragment.kt index 2b181dcd57..2ac42f0644 100644 --- a/ui/article-renderer/src/main/kotlin/org/cru/godtools/article/ui/categories/CategoriesFragment.kt +++ b/ui/article-renderer/src/main/kotlin/org/cru/godtools/article/ui/categories/CategoriesFragment.kt @@ -3,56 +3,108 @@ package org.cru.godtools.article.ui.categories import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup -import androidx.lifecycle.map +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.compose.ui.unit.dp +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import dagger.hilt.android.AndroidEntryPoint import java.util.Locale import javax.inject.Inject +import javax.inject.Named +import okio.FileSystem import org.ccci.gto.android.common.androidx.fragment.app.findListener -import org.ccci.gto.android.common.androidx.recyclerview.decorator.MarginItemDecoration +import org.cru.godtools.base.tool.BaseToolRendererModule.Companion.TOOL_RESOURCE_FILE_SYSTEM import org.cru.godtools.base.tool.analytics.model.SCREEN_CATEGORIES import org.cru.godtools.base.tool.analytics.model.ToolAnalyticsScreenEvent -import org.cru.godtools.base.tool.fragment.BaseToolFragment +import org.cru.godtools.base.tool.viewmodel.LatestPublishedManifestDataModel +import org.cru.godtools.base.ui.theme.GodToolsTheme +import org.cru.godtools.shared.renderer.article.RenderArticleCategory +import org.cru.godtools.shared.renderer.util.ProvideRendererServices import org.cru.godtools.shared.tool.parser.model.Category -import org.cru.godtools.tool.article.R -import org.cru.godtools.tool.article.databinding.ArticleCategoriesFragmentBinding import org.greenrobot.eventbus.EventBus +import splitties.fragmentargs.arg @AndroidEntryPoint -class CategoriesFragment : - BaseToolFragment, - CategorySelectedListener { - constructor() : super(R.layout.article_categories_fragment) - constructor(code: String, locale: Locale) : super(R.layout.article_categories_fragment, code, locale) +class CategoriesFragment() : Fragment() { + constructor(code: String, locale: Locale) : this() { + tool = code + this.locale = locale + } + + private var tool by arg() + private var locale by arg() @Inject internal lateinit var eventBus: EventBus + @Inject + @Named(TOOL_RESOURCE_FILE_SYSTEM) + internal lateinit var resourceFileSystem: FileSystem + // region Lifecycle - override fun onCreateBinding(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = - ArticleCategoriesFragmentBinding.inflate(inflater, container, false).apply { setupCategoriesView() } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setupDataModel() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = + ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + GodToolsTheme { + ProvideRendererServices(resourceFileSystem) { + CategoriesContent() + } + } + } + } override fun onResume() { super.onResume() eventBus.post(ToolAnalyticsScreenEvent(SCREEN_CATEGORIES, tool, locale)) } - override fun onCategorySelected(category: Category?) { + private fun onCategorySelected(category: Category?) { findListener()?.onCategorySelected(category) } // endregion Lifecycle - // region Categories View - private fun ArticleCategoriesFragmentBinding.setupCategoriesView() { - categories.apply { - setHasFixedSize(true) - addItemDecoration( - MarginItemDecoration(bottomMargin = resources.getDimensionPixelSize(R.dimen.categories_list_gap)) - ) - adapter = CategoriesAdapter(viewLifecycleOwner).apply { - callbacks.set(this@CategoriesFragment) - toolDataModel.manifest.map { it?.categories }.observe(viewLifecycleOwner, this) + // region Data Model + private val toolDataModel: LatestPublishedManifestDataModel by viewModels() + + private fun setupDataModel() { + toolDataModel.toolCode.value = tool + toolDataModel.locale.value = locale + } + // endregion Data Model + + @Composable + private fun CategoriesContent(modifier: Modifier = Modifier) { + val manifest by toolDataModel.manifestFlow.collectAsState(null) + val categories by remember { derivedStateOf { manifest?.categories.orEmpty() } } + + LazyColumn( + verticalArrangement = Arrangement.spacedBy(1.dp), + modifier = modifier.fillMaxSize(), + ) { + items(categories, key = { it.id ?: it }) { category -> + RenderArticleCategory( + category, + modifier = Modifier.clickable { onCategorySelected(category) } + ) } } } - // endregion Categories View } diff --git a/ui/article-renderer/src/main/res/layout/article_categories_fragment.xml b/ui/article-renderer/src/main/res/layout/article_categories_fragment.xml deleted file mode 100644 index 61c22d467e..0000000000 --- a/ui/article-renderer/src/main/res/layout/article_categories_fragment.xml +++ /dev/null @@ -1,7 +0,0 @@ - - diff --git a/ui/article-renderer/src/main/res/layout/list_item_category.xml b/ui/article-renderer/src/main/res/layout/list_item_category.xml deleted file mode 100644 index 686fe29ee8..0000000000 --- a/ui/article-renderer/src/main/res/layout/list_item_category.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - From 975306506c5795d1df60ea6b3e5fb2378ccb24bd Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Wed, 1 Apr 2026 13:55:24 -0600 Subject: [PATCH 2/2] Remove unused article renderer drawables and styles Co-Authored-By: Claude Sonnet 4.6 --- .../drawable/list_item_decorator_article_divider.xml | 8 -------- .../src/main/res/values/styles_article.xml | 12 ------------ 2 files changed, 20 deletions(-) delete mode 100644 ui/article-renderer/src/main/res/drawable/list_item_decorator_article_divider.xml delete mode 100644 ui/article-renderer/src/main/res/values/styles_article.xml diff --git a/ui/article-renderer/src/main/res/drawable/list_item_decorator_article_divider.xml b/ui/article-renderer/src/main/res/drawable/list_item_decorator_article_divider.xml deleted file mode 100644 index ed1de6f36d..0000000000 --- a/ui/article-renderer/src/main/res/drawable/list_item_decorator_article_divider.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/ui/article-renderer/src/main/res/values/styles_article.xml b/ui/article-renderer/src/main/res/values/styles_article.xml deleted file mode 100644 index 3529112410..0000000000 --- a/ui/article-renderer/src/main/res/values/styles_article.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - 20dp - 30sp - 1dp - - - -