From da26c21d904466964f63785e8488bfd767756eaa Mon Sep 17 00:00:00 2001 From: Dmitry Ryaboshapko Date: Sun, 1 Mar 2026 16:12:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=20fr?= =?UTF-8?q?agment=20=D0=BD=D0=B0=20compose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 26 +++ .../java/ru/otus/marketsample/MainActivity.kt | 184 ++++++++++++++++-- .../java/ru/otus/marketsample/MainFragment.kt | 49 ----- .../details/feature/DetailsFragment.kt | 4 +- .../feature/navigation/DetailNavHost.kt | 38 ++++ .../ru/otus/marketsample/di/AppComponent.kt | 12 +- .../ru/otus/marketsample/di/ViewModelKey.kt | 10 + .../otus/marketsample/di/ViewModelModule.kt | 26 +++ .../otus/marketsample/di/ViewModelsFactory.kt | 15 ++ .../marketsample/navigation/MainNavHost.kt | 27 +++ .../products/feature/ProductListFragment.kt | 117 ----------- .../products/feature/ProductListViewModel.kt | 7 +- .../feature/ProductListViewModelFactory.kt | 32 --- .../products/feature/ProductStateFactory.kt | 4 +- .../products/feature/ProductsScreen.kt | 127 ++++++++++++ .../products/feature/adapter/ProductHolder.kt | 32 --- .../feature/adapter/ProductsAdapter.kt | 44 ----- .../feature/di/ProductListComponent.kt | 3 - .../feature/navigation/ProductsNavHost.kt | 28 +++ .../ru/otus/marketsample/promo/PromoScreen.kt | 77 ++++++++ .../promo/feature/PromoListFragment.kt | 106 ---------- .../promo/feature/PromoListViewModel.kt | 5 +- .../feature/PromoListViewModelFactory.kt | 32 --- .../promo/feature/PromoStateFactory.kt | 1 - .../promo/feature/adapter/PromoAdapter.kt | 40 ---- .../promo/feature/adapter/PromoHolder.kt | 17 -- .../promo/feature/di/PromoComponent.kt | 3 - .../promo/navigation/PromoNavHost.kt | 19 ++ app/src/main/res/layout/activity_main.xml | 16 -- app/src/main/res/layout/fragment_main.xml | 32 --- .../main/res/layout/fragment_product_list.xml | 34 ---- .../main/res/layout/fragment_promo_list.xml | 36 ---- app/src/main/res/layout/item_product.xml | 85 -------- app/src/main/res/layout/item_promo.xml | 48 ----- build.gradle | 1 + common/ui/build.gradle | 14 ++ .../main/kotlin/ru/otus/components/Badge.kt | 72 +++++++ .../ru/otus/components/MarketSnackbar.kt | 7 + .../main/kotlin/ru/otus/components/Price.kt | 47 +++++ .../kotlin/ru/otus/components/ProductCard.kt | 114 +++++++++++ .../kotlin/ru/otus/components/PromoCard.kt | 82 ++++++++ .../main/kotlin/ru/otus/theme/MarketTheme.kt | 26 +++ common/ui/src/main/res/drawable/cats.jpg | Bin 0 -> 60380 bytes gradle/libs.versions.toml | 28 ++- 44 files changed, 970 insertions(+), 757 deletions(-) delete mode 100644 app/src/main/java/ru/otus/marketsample/MainFragment.kt create mode 100644 app/src/main/java/ru/otus/marketsample/details/feature/navigation/DetailNavHost.kt create mode 100644 app/src/main/java/ru/otus/marketsample/di/ViewModelKey.kt create mode 100644 app/src/main/java/ru/otus/marketsample/di/ViewModelModule.kt create mode 100644 app/src/main/java/ru/otus/marketsample/di/ViewModelsFactory.kt create mode 100644 app/src/main/java/ru/otus/marketsample/navigation/MainNavHost.kt delete mode 100644 app/src/main/java/ru/otus/marketsample/products/feature/ProductListFragment.kt delete mode 100644 app/src/main/java/ru/otus/marketsample/products/feature/ProductListViewModelFactory.kt create mode 100644 app/src/main/java/ru/otus/marketsample/products/feature/ProductsScreen.kt delete mode 100644 app/src/main/java/ru/otus/marketsample/products/feature/adapter/ProductHolder.kt delete mode 100644 app/src/main/java/ru/otus/marketsample/products/feature/adapter/ProductsAdapter.kt create mode 100644 app/src/main/java/ru/otus/marketsample/products/feature/navigation/ProductsNavHost.kt create mode 100644 app/src/main/java/ru/otus/marketsample/promo/PromoScreen.kt delete mode 100644 app/src/main/java/ru/otus/marketsample/promo/feature/PromoListFragment.kt delete mode 100644 app/src/main/java/ru/otus/marketsample/promo/feature/PromoListViewModelFactory.kt delete mode 100644 app/src/main/java/ru/otus/marketsample/promo/feature/adapter/PromoAdapter.kt delete mode 100644 app/src/main/java/ru/otus/marketsample/promo/feature/adapter/PromoHolder.kt create mode 100644 app/src/main/java/ru/otus/marketsample/promo/navigation/PromoNavHost.kt delete mode 100644 app/src/main/res/layout/activity_main.xml delete mode 100644 app/src/main/res/layout/fragment_main.xml delete mode 100644 app/src/main/res/layout/fragment_product_list.xml delete mode 100644 app/src/main/res/layout/fragment_promo_list.xml delete mode 100644 app/src/main/res/layout/item_product.xml delete mode 100644 app/src/main/res/layout/item_promo.xml create mode 100644 common/ui/src/main/kotlin/ru/otus/components/Badge.kt create mode 100644 common/ui/src/main/kotlin/ru/otus/components/MarketSnackbar.kt create mode 100644 common/ui/src/main/kotlin/ru/otus/components/Price.kt create mode 100644 common/ui/src/main/kotlin/ru/otus/components/ProductCard.kt create mode 100644 common/ui/src/main/kotlin/ru/otus/components/PromoCard.kt create mode 100644 common/ui/src/main/kotlin/ru/otus/theme/MarketTheme.kt create mode 100644 common/ui/src/main/res/drawable/cats.jpg diff --git a/app/build.gradle b/app/build.gradle index 1cdd96f..fd9f05b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,6 +3,7 @@ plugins { alias(libs.plugins.kotlinAndroid) alias(libs.plugins.kotlinxSerialization) alias(libs.plugins.kapt) + alias(libs.plugins.compose.compiler) } android { @@ -34,10 +35,33 @@ android { } buildFeatures { viewBinding true + compose true } } +composeCompiler { + reportsDestination = layout.buildDirectory.dir("compose_compiler") + metricsDestination = layout.buildDirectory.dir("compose_compiler") +} + dependencies { + + implementation platform(libs.androidx.compose.bom) + + implementation libs.androidx.compose.material3 + implementation libs.androidx.compose.foundation + implementation libs.androidx.compose.ui + implementation libs.androidx.compose.ui.viewbinding + + implementation libs.androidx.compose.ui.tooling.preview + implementation libs.androidx.fragment.compose + debugImplementation libs.androidx.compose.ui.tooling + + implementation libs.androidx.activity.compose + implementation libs.androidx.lifecycle.viewmodel.compose + + implementation libs.androidx.navigation.compose + implementation project(":common:di") implementation project(":common:formatters") implementation project(":common:ui") @@ -56,6 +80,8 @@ dependencies { implementation libs.navigation.fragment.ktx implementation libs.navigation.ui.ktx implementation libs.coil + implementation libs.coil.compose + implementation libs.coil.network implementation libs.gson implementation libs.bundles.network implementation libs.kotlin.serialization diff --git a/app/src/main/java/ru/otus/marketsample/MainActivity.kt b/app/src/main/java/ru/otus/marketsample/MainActivity.kt index 7e34aaf..18e2b76 100644 --- a/app/src/main/java/ru/otus/marketsample/MainActivity.kt +++ b/app/src/main/java/ru/otus/marketsample/MainActivity.kt @@ -1,26 +1,188 @@ package ru.otus.marketsample import android.os.Bundle +import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import ru.otus.marketsample.databinding.ActivityMainBinding +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.BottomAppBar +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.NavigationBarItemDefaults +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Snackbar +import androidx.compose.material3.Text +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.os.bundleOf +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.rememberNavController +import androidx.navigation.findNavController +import androidx.navigation.navigation +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import ru.otus.marketsample.details.feature.navigation.FragmentHost +import ru.otus.marketsample.details.feature.navigation.detailNavHost +import ru.otus.marketsample.di.DaggerAppComponent +import ru.otus.marketsample.di.ViewModelsFactory +import ru.otus.marketsample.navigation.MainDestination +import ru.otus.marketsample.navigation.Product +import ru.otus.marketsample.navigation.Products +import ru.otus.marketsample.navigation.Promo +import ru.otus.marketsample.products.feature.navigation.productGraph +import ru.otus.marketsample.promo.navigation.promoGraph +import javax.inject.Inject +import ru.otus.common.ui.R as RUi + +private const val LENGTH_SHORT = 3000L class MainActivity : AppCompatActivity() { - private lateinit var binding: ActivityMainBinding + @Inject + lateinit var viewModelsFactory: ViewModelsFactory override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() + DaggerAppComponent.factory() + .create(this) + .inject(this) super.onCreate(savedInstanceState) - binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding.root) - - ViewCompat.setOnApplyWindowInsetsListener(binding.container) { view, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - view.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets + + val mainScreens = listOf( + Products(), + Promo() + ) + + setContent { + var isShowSnackbar by remember { + mutableStateOf(false) + } + MaterialTheme { + var selected by remember { + mutableStateOf(Products()) + } + val navController = rememberNavController() + MaterialTheme { + Scaffold( + containerColor = colorResource(RUi.color.white), + snackbarHost = { + AnimatedVisibility( + isShowSnackbar, + enter = slideInVertically { -it }, + exit = slideOutVertically { it }, + ) { + Snackbar() { + Text( + modifier = Modifier.padding(16.dp), + text = "Error wile loading data" + ) + } + LaunchedEffect(isShowSnackbar) { + launch { + delay(LENGTH_SHORT) + isShowSnackbar = false + } + } + } + }, + bottomBar = { + BottomAppBar( + containerColor = colorResource(RUi.color.white), + modifier = Modifier + ) { + mainScreens.forEach { destination -> + NavigationBarItem( + colors = NavigationBarItemDefaults.colors( + indicatorColor = Color.Transparent, + selectedTextColor = colorResource(RUi.color.purple_500), + unselectedTextColor = colorResource(RUi.color.black), + selectedIconColor = colorResource(RUi.color.purple_500), + unselectedIconColor = colorResource(RUi.color.black), + ), + selected = selected == destination, + onClick = { + selected = destination + navController.navigate(destination) { + launchSingleTop = true + restoreState = true + popUpTo(Products()) { + saveState = true + inclusive = true + } + } + }, + label = { + Text( + text = getString(destination.labelRes), + fontSize = 12.sp, + fontWeight = if (selected == destination) { + FontWeight.Bold + } else { + FontWeight.Normal + } + ) + }, + icon = { + Icon( + modifier = Modifier + .size(24.dp), + painter = painterResource(destination.iconRes), + contentDescription = null, + ) + } + ) + } + } + } + ) { paddingsValue -> + NavHost( + modifier = Modifier + .padding(paddingsValue), + navController = navController, + startDestination = Product, + enterTransition = { + slideInHorizontally() + fadeIn() + }, + exitTransition = { + slideOutHorizontally() + fadeOut() + } + ) { + promoGraph(navController, viewModelsFactory) + navigation(startDestination = Products()) { + productGraph( + navController = navController, + viewModelFactory = viewModelsFactory, + openDetail = { + navController.navigate(FragmentHost(it)) + }, + showSnackbarCallback = { isShowSnackbar = it } + ) + detailNavHost(navController) + } + } + } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/MainFragment.kt b/app/src/main/java/ru/otus/marketsample/MainFragment.kt deleted file mode 100644 index d03161e..0000000 --- a/app/src/main/java/ru/otus/marketsample/MainFragment.kt +++ /dev/null @@ -1,49 +0,0 @@ -package ru.otus.marketsample - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.fragment.app.Fragment -import androidx.navigation.Navigation.findNavController -import com.google.android.material.bottomnavigation.BottomNavigationView -import ru.otus.marketsample.databinding.FragmentMainBinding - -class MainFragment : Fragment() { - - private var _binding: FragmentMainBinding? = null - private val binding get() = _binding!! - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - _binding = FragmentMainBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val navView: BottomNavigationView = binding.navView - - navView.setOnItemSelectedListener { - findNavController(binding.navHostFragmentMain).navigate(it.itemId) - true - } - - ViewCompat.setOnApplyWindowInsetsListener(binding.container) { view, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - view.setPadding(systemBars.left, 0, systemBars.right, 0) - insets - } - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/details/feature/DetailsFragment.kt b/app/src/main/java/ru/otus/marketsample/details/feature/DetailsFragment.kt index e23c57e..bc5144d 100644 --- a/app/src/main/java/ru/otus/marketsample/details/feature/DetailsFragment.kt +++ b/app/src/main/java/ru/otus/marketsample/details/feature/DetailsFragment.kt @@ -6,12 +6,14 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.activity.addCallback import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import coil.load +import androidx.navigation.fragment.findNavController +import coil3.load import kotlinx.coroutines.launch import ru.otus.common.di.findDependencies import ru.otus.marketsample.details.feature.di.DaggerDetailsComponent diff --git a/app/src/main/java/ru/otus/marketsample/details/feature/navigation/DetailNavHost.kt b/app/src/main/java/ru/otus/marketsample/details/feature/navigation/DetailNavHost.kt new file mode 100644 index 0000000..43ded63 --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/details/feature/navigation/DetailNavHost.kt @@ -0,0 +1,38 @@ +package ru.otus.marketsample.details.feature.navigation + +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Modifier +import androidx.core.os.bundleOf +import androidx.fragment.compose.AndroidFragment +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import kotlinx.serialization.Serializable +import ru.otus.marketsample.details.feature.DetailsFragment + +fun NavGraphBuilder.detailNavHost(navHostController: NavHostController) { + composable( + enterTransition = { scaleIn() + fadeIn() }, + exitTransition = { scaleOut() + fadeOut() } + ) {backstack -> + val id = backstack.arguments?.getString("productId") + Box( + modifier = Modifier + .fillMaxSize() + ) { + AndroidFragment( + modifier = Modifier + .fillMaxSize(), + arguments = bundleOf("productId" to id) + ) + } + } +} + +@Serializable +data class FragmentHost(val productId: String) \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/di/AppComponent.kt b/app/src/main/java/ru/otus/marketsample/di/AppComponent.kt index 2b95681..a84dda4 100644 --- a/app/src/main/java/ru/otus/marketsample/di/AppComponent.kt +++ b/app/src/main/java/ru/otus/marketsample/di/AppComponent.kt @@ -7,6 +7,7 @@ import ru.otus.marketsample.details.feature.di.DetailsComponentDependencies import ru.otus.marketsample.products.feature.di.ProductListComponentDependencies import ru.otus.marketsample.promo.feature.di.PromoComponentDependencies import ru.otus.common.di.Dependencies +import ru.otus.marketsample.MainActivity import javax.inject.Singleton @Singleton @@ -14,16 +15,15 @@ import javax.inject.Singleton modules = [ NetworkModule::class, DataModule::class, + ViewModelModule::class, ] ) -interface AppComponent: - Dependencies, - DetailsComponentDependencies, - PromoComponentDependencies, - ProductListComponentDependencies -{ +interface AppComponent : Dependencies, + DetailsComponentDependencies { @Component.Factory interface Factory { fun create(@BindsInstance applicationContext: Context): AppComponent } + + fun inject(activity: MainActivity) } \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/di/ViewModelKey.kt b/app/src/main/java/ru/otus/marketsample/di/ViewModelKey.kt new file mode 100644 index 0000000..5e5f1ff --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/di/ViewModelKey.kt @@ -0,0 +1,10 @@ +package ru.otus.marketsample.di + +import androidx.lifecycle.ViewModel +import dagger.MapKey +import kotlin.reflect.KClass + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@MapKey +annotation class ViewModelKey(val value: KClass) \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/di/ViewModelModule.kt b/app/src/main/java/ru/otus/marketsample/di/ViewModelModule.kt new file mode 100644 index 0000000..11273db --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/di/ViewModelModule.kt @@ -0,0 +1,26 @@ +package ru.otus.marketsample.di + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import ru.otus.marketsample.products.feature.ProductListViewModel +import ru.otus.marketsample.promo.feature.PromoListViewModel + +@Module +interface ViewModelModule { + + @Binds + fun bindViewModelFactory(impl: ViewModelsFactory): ViewModelProvider.Factory + + @Binds + @IntoMap + @ViewModelKey(ProductListViewModel::class) + fun bindProductListViewModel(impl: ProductListViewModel): ViewModel + + @Binds + @IntoMap + @ViewModelKey(PromoListViewModel::class) + fun bindPromoListViewModel(impl: PromoListViewModel): ViewModel +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/di/ViewModelsFactory.kt b/app/src/main/java/ru/otus/marketsample/di/ViewModelsFactory.kt new file mode 100644 index 0000000..395de5c --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/di/ViewModelsFactory.kt @@ -0,0 +1,15 @@ +package ru.otus.marketsample.di + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject +import javax.inject.Provider + +@Suppress("UNCHECKED_CAST") +class ViewModelsFactory @Inject constructor(val vmMap: Map, @JvmSuppressWildcards Provider>) : + ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + return vmMap.getValue(modelClass).get() as T + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/navigation/MainNavHost.kt b/app/src/main/java/ru/otus/marketsample/navigation/MainNavHost.kt new file mode 100644 index 0000000..699be87 --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/navigation/MainNavHost.kt @@ -0,0 +1,27 @@ +package ru.otus.marketsample.navigation + +import kotlinx.serialization.Serializable +import ru.otus.marketsample.R +import ru.otus.common.ui.R as RUi + +interface MainDestination { + val labelRes: Int + val iconRes: Int +} + +@Serializable +data class +Products( + override val labelRes: Int = R.string.title_products, + override val iconRes: Int = RUi.drawable.ic_list, +) : MainDestination + +@Serializable +data class Promo( + override val labelRes: Int = R.string.title_promo, + override val iconRes: Int = RUi.drawable.ic_discount, +) : MainDestination + + +@Serializable +object Product diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/ProductListFragment.kt b/app/src/main/java/ru/otus/marketsample/products/feature/ProductListFragment.kt deleted file mode 100644 index 88f7ec0..0000000 --- a/app/src/main/java/ru/otus/marketsample/products/feature/ProductListFragment.kt +++ /dev/null @@ -1,117 +0,0 @@ -package ru.otus.marketsample.products.feature - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.core.os.bundleOf -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import androidx.navigation.findNavController -import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.coroutines.launch -import ru.otus.marketsample.MarketSampleApp -import ru.otus.marketsample.R -import ru.otus.marketsample.databinding.FragmentProductListBinding -import ru.otus.marketsample.products.feature.adapter.ProductsAdapter -import ru.otus.marketsample.products.feature.di.DaggerProductListComponent -import javax.inject.Inject - -class ProductListFragment : Fragment() { - - private var _binding: FragmentProductListBinding? = null - private val binding get() = _binding!! - - @Inject - lateinit var factory: ProductListViewModelFactory - - private val viewModel: ProductListViewModel by viewModels { factory } - - override fun onAttach(context: Context) { - super.onAttach(context) - - val appComponent = (activity?.applicationContext as MarketSampleApp).appComponent - - DaggerProductListComponent.factory() - .create(appComponent) - .inject(this) - } - - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - _binding = FragmentProductListBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.recyclerView.adapter = ProductsAdapter( - onItemClicked = { productId -> - requireActivity().findNavController(R.id.nav_host_activity_main) - .navigate( - resId = R.id.action_main_to_details, - args = bundleOf("productId" to productId), - ) - } - ) - binding.recyclerView.layoutManager = LinearLayoutManager(context) - - binding.swipeRefreshLayout.setOnRefreshListener { - viewModel.refresh() - } - - subscribeUI() - } - - private fun subscribeUI() { - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - viewModel.state.collect { state -> - when { - state.isLoading -> showLoading() - state.hasError -> { - Toast.makeText( - requireContext(), - "Error wile loading data", - Toast.LENGTH_SHORT - ).show() - - viewModel.errorHasShown() - } - - else -> showProductList(productListState = state.productListState) - } - } - } - } - } - } - - private fun showProductList(productListState: List) { - binding.progress.visibility = View.GONE - binding.recyclerView.visibility = View.VISIBLE - (binding.recyclerView.adapter as ProductsAdapter).submitList(productListState) - binding.swipeRefreshLayout.isRefreshing = false - } - - private fun showLoading() { - binding.progress.visibility = View.VISIBLE - binding.recyclerView.visibility = View.GONE - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/ProductListViewModel.kt b/app/src/main/java/ru/otus/marketsample/products/feature/ProductListViewModel.kt index ce33e63..736c352 100644 --- a/app/src/main/java/ru/otus/marketsample/products/feature/ProductListViewModel.kt +++ b/app/src/main/java/ru/otus/marketsample/products/feature/ProductListViewModel.kt @@ -6,16 +6,17 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.update -import ru.otus.marketsample.products.domain.ConsumeProductsUseCase import ru.otus.marketsample.R +import ru.otus.marketsample.products.domain.ConsumeProductsUseCase +import javax.inject.Inject + -class ProductListViewModel( +class ProductListViewModel @Inject constructor( private val consumeProductsUseCase: ConsumeProductsUseCase, private val productStateFactory: ProductStateFactory, ) : ViewModel() { diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/ProductListViewModelFactory.kt b/app/src/main/java/ru/otus/marketsample/products/feature/ProductListViewModelFactory.kt deleted file mode 100644 index 55eaeb7..0000000 --- a/app/src/main/java/ru/otus/marketsample/products/feature/ProductListViewModelFactory.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ru.otus.marketsample.products.feature - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.CreationExtras -import ru.otus.common.di.FeatureScope -import ru.otus.marketsample.products.domain.ConsumeProductsUseCase -import javax.inject.Inject - -@FeatureScope -class ProductListViewModelFactory @Inject constructor( - private val consumeProductsUseCase: ConsumeProductsUseCase, - private val productStateFactory: ProductStateFactory, -) : - ViewModelProvider.Factory { - - override fun create( - modelClass: Class, - extras: CreationExtras, - ): T { - when { - modelClass.isAssignableFrom(ProductListViewModel::class.java) -> { - @Suppress("UNCHECKED_CAST") - return ProductListViewModel( - consumeProductsUseCase = consumeProductsUseCase, - productStateFactory = productStateFactory, - ) as T - } - } - throw IllegalArgumentException("Unknown ViewModel class") - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/ProductStateFactory.kt b/app/src/main/java/ru/otus/marketsample/products/feature/ProductStateFactory.kt index 51f0490..4c10dff 100644 --- a/app/src/main/java/ru/otus/marketsample/products/feature/ProductStateFactory.kt +++ b/app/src/main/java/ru/otus/marketsample/products/feature/ProductStateFactory.kt @@ -1,12 +1,10 @@ package ru.otus.marketsample.products.feature -import ru.otus.common.di.FeatureScope -import ru.otus.marketsample.products.domain.Product import ru.otus.common.formatters.DiscountFormatter import ru.otus.common.formatters.PriceFormatter +import ru.otus.marketsample.products.domain.Product import javax.inject.Inject -@FeatureScope class ProductStateFactory @Inject constructor( private val discountFormatter: DiscountFormatter, private val priceFormatter: PriceFormatter, diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/ProductsScreen.kt b/app/src/main/java/ru/otus/marketsample/products/feature/ProductsScreen.kt new file mode 100644 index 0000000..50a9edf --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/products/feature/ProductsScreen.kt @@ -0,0 +1,127 @@ +package ru.otus.marketsample.products.feature + +//import coil.request.ImageRequest +import android.util.Log +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import coil3.compose.AsyncImage +import ru.otus.common.ui.R +import ru.otus.components.Badge +import ru.otus.components.Price +import ru.otus.components.ProductCard +import ru.otus.marketsample.di.ViewModelsFactory + +@Composable +fun ProductsScreen( + viewModelFactory: ViewModelsFactory, + viewModel: ProductListViewModel = viewModel(factory = viewModelFactory), + showSnackbarCallback: (Boolean) -> Unit, + openDetail: (id: String) -> Unit, +) { + val state by viewModel.state.collectAsState() + + Surface( + color = colorResource(R.color.white), + ) { + when { + state.isLoading -> { + Box( + modifier = Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + + state.hasError -> { + showSnackbarCallback.invoke(state.hasError) + + viewModel.errorHasShown() + } + + else -> Content(state, openDetail) + } + } +} + +@Composable +private fun Content(state: ProductsScreenState, openDetail: (id: String) -> Unit) { + LazyColumn( + modifier = Modifier + .padding(horizontal = 16.dp), + ) { + items( + count = state.productListState.size, + key = { index -> state.productListState[index].id }, + contentType = { index -> state.productListState[index] } + ) { index -> + val item = state.productListState[index] + ProductCard( + modifier = Modifier + .padding(vertical = 24.dp) + .clickable { + openDetail.invoke(item.id) + }, + image = { + AsyncImage( + model = item.image, + contentDescription = null, + modifier = Modifier + .fillMaxSize(), + contentScale = ContentScale.Crop, + ) + }, + badge = { + if (item.hasDiscount) { + Badge( + value = item.discount, + modifier = Modifier + .align(Alignment.TopEnd), + ) + } + }, + title = { + Text( + text = item.name, + modifier = Modifier + .fillMaxWidth(), + overflow = TextOverflow.Ellipsis, + maxLines = 2, + fontSize = 18.sp, + color = colorResource(R.color.black), + fontWeight = FontWeight.Medium + ) + }, + price = { + Price( + value = item.price, + modifier = Modifier + .align(Alignment.End) + ) + } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/adapter/ProductHolder.kt b/app/src/main/java/ru/otus/marketsample/products/feature/adapter/ProductHolder.kt deleted file mode 100644 index f216b25..0000000 --- a/app/src/main/java/ru/otus/marketsample/products/feature/adapter/ProductHolder.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ru.otus.marketsample.products.feature.adapter - -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.recyclerview.widget.RecyclerView -import coil.load -import ru.otus.marketsample.R -import ru.otus.marketsample.databinding.ItemProductBinding -import ru.otus.marketsample.products.feature.ProductState - -class ProductHolder( - private val binding: ItemProductBinding, - private val onItemClicked: (String) -> Unit, -) : RecyclerView.ViewHolder(binding.root) { - - fun bind(productState: ProductState) { - binding.image.load(productState.image) - binding.name.text = productState.name - binding.price.text = - binding.root.resources.getString(R.string.price_with_arg, productState.price) - if (productState.hasDiscount) { - binding.promo.visibility = VISIBLE - binding.promo.text = productState.discount - } else { - binding.promo.visibility = GONE - } - - binding.root.setOnClickListener { - onItemClicked(productState.id) - } - } -} diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/adapter/ProductsAdapter.kt b/app/src/main/java/ru/otus/marketsample/products/feature/adapter/ProductsAdapter.kt deleted file mode 100644 index 18354a2..0000000 --- a/app/src/main/java/ru/otus/marketsample/products/feature/adapter/ProductsAdapter.kt +++ /dev/null @@ -1,44 +0,0 @@ -package ru.otus.marketsample.products.feature.adapter - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import ru.otus.common.di.FeatureScope -import ru.otus.marketsample.databinding.ItemProductBinding -import ru.otus.marketsample.products.feature.ProductState -import javax.inject.Inject - -@FeatureScope -class ProductsAdapter @Inject constructor( - private val onItemClicked: (String) -> Unit, -) : - ListAdapter(DiffCallback()) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductHolder { - return ProductHolder( - binding = ItemProductBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ), - onItemClicked = onItemClicked, - ) - } - - override fun onBindViewHolder(holder: ProductHolder, position: Int) { - val entity = getItem(position) - entity?.let { - holder.bind(entity) - } - } -} - -private class DiffCallback : DiffUtil.ItemCallback() { - - override fun areItemsTheSame(oldItem: ProductState, newItem: ProductState): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame(oldItem: ProductState, newItem: ProductState): Boolean { - return oldItem == newItem - } -} diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/di/ProductListComponent.kt b/app/src/main/java/ru/otus/marketsample/products/feature/di/ProductListComponent.kt index 2b4c9fd..5ca8784 100644 --- a/app/src/main/java/ru/otus/marketsample/products/feature/di/ProductListComponent.kt +++ b/app/src/main/java/ru/otus/marketsample/products/feature/di/ProductListComponent.kt @@ -4,7 +4,6 @@ import dagger.Component import ru.otus.common.data.products.ProductRepository import ru.otus.common.data.promo.PromoRepository import ru.otus.common.di.FeatureScope -import ru.otus.marketsample.products.feature.ProductListFragment @FeatureScope @Component(dependencies = [ProductListComponentDependencies::class]) @@ -16,8 +15,6 @@ interface ProductListComponent { dependencies: ProductListComponentDependencies, ): ProductListComponent } - - fun inject(productListFragment: ProductListFragment) } interface ProductListComponentDependencies { diff --git a/app/src/main/java/ru/otus/marketsample/products/feature/navigation/ProductsNavHost.kt b/app/src/main/java/ru/otus/marketsample/products/feature/navigation/ProductsNavHost.kt new file mode 100644 index 0000000..deb9c10 --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/products/feature/navigation/ProductsNavHost.kt @@ -0,0 +1,28 @@ +package ru.otus.marketsample.products.feature.navigation + +import androidx.core.os.bundleOf +import androidx.lifecycle.ViewModelProvider +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import kotlinx.serialization.Serializable +import ru.otus.marketsample.R +import ru.otus.marketsample.di.ViewModelsFactory +import ru.otus.marketsample.navigation.Products +import ru.otus.marketsample.products.feature.ProductsScreen + +fun NavGraphBuilder.productGraph( + navController: NavController, + viewModelFactory: ViewModelsFactory, + showSnackbarCallback: (Boolean) -> Unit, + openDetail: (id: String) -> Unit, +) { + composable { + + ProductsScreen( + viewModelFactory = viewModelFactory, + showSnackbarCallback = showSnackbarCallback, + openDetail = openDetail + ) + } +} diff --git a/app/src/main/java/ru/otus/marketsample/promo/PromoScreen.kt b/app/src/main/java/ru/otus/marketsample/promo/PromoScreen.kt new file mode 100644 index 0000000..cca900a --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/promo/PromoScreen.kt @@ -0,0 +1,77 @@ +package ru.otus.marketsample.promo + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import coil3.compose.AsyncImage +import ru.otus.components.PromoCard +import ru.otus.marketsample.di.ViewModelsFactory +import ru.otus.marketsample.promo.feature.PromoListViewModel +import ru.otus.common.ui.R as RUi + +@Composable +fun PromoScreen( + viewModelsFactory: ViewModelsFactory, + viewModel: PromoListViewModel = viewModel(factory = viewModelsFactory) +) { + val state by viewModel.state.collectAsState() + + Surface( + modifier = Modifier + .fillMaxSize(), + color = colorResource(RUi.color.white) + ) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 10.dp), + verticalArrangement = Arrangement.spacedBy(20.dp), + ) { + items( + count = state.promoListState.size, + key = { state.promoListState[it].id }, + contentType = { state.promoListState[it] }, + ) { index -> + val promo = state.promoListState[index] + PromoCard( + image = { + AsyncImage( + model = promo.image, + contentDescription = null, + modifier = Modifier + .fillMaxSize(), + contentScale = ContentScale.Crop, + ) + }, + title = { + Text( + text = promo.name, + fontSize = 24.sp, + color = colorResource(RUi.color.white), + ) + }, + description = { + Text( + text = promo.description, + style = MaterialTheme.typography.bodyMedium, + color = colorResource(RUi.color.white) + ) + } + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListFragment.kt b/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListFragment.kt deleted file mode 100644 index 2e4f533..0000000 --- a/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListFragment.kt +++ /dev/null @@ -1,106 +0,0 @@ -package ru.otus.marketsample.promo.feature - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.LinearLayoutManager -import kotlinx.coroutines.launch -import ru.otus.common.di.findDependencies -import ru.otus.marketsample.databinding.FragmentPromoListBinding -import ru.otus.marketsample.promo.feature.adapter.PromoAdapter -import ru.otus.marketsample.promo.feature.di.DaggerPromoComponent -import javax.inject.Inject - -class PromoListFragment : Fragment() { - - private var _binding: FragmentPromoListBinding? = null - private val binding get() = _binding!! - - @Inject - lateinit var adapter: PromoAdapter - - @Inject - lateinit var factory: PromoListViewModelFactory - - private val viewModel: PromoListViewModel by viewModels { factory } - - override fun onAttach(context: Context) { - super.onAttach(context) - - DaggerPromoComponent.factory() - .create(dependencies = findDependencies()) - .inject(this) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - _binding = FragmentPromoListBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.recyclerView.adapter = adapter - binding.recyclerView.layoutManager = LinearLayoutManager(context) - - binding.swipeRefreshLayout.setOnRefreshListener { - viewModel.refresh() - } - - subscribeUI() - } - - private fun subscribeUI() { - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - viewModel.state.collect { state -> - when { - state.isLoading -> showLoading() - state.hasError -> { - Toast.makeText( - requireContext(), - "Error wile loading data", - Toast.LENGTH_SHORT - ).show() - - viewModel.errorHasShown() - } - - else -> showPromoList(promoListState = state.promoListState) - } - } - } - } - } - } - - private fun showPromoList(promoListState: List) { - binding.progress.visibility = View.GONE - binding.recyclerView.visibility = View.VISIBLE - adapter.submitList(promoListState) - binding.swipeRefreshLayout.isRefreshing = false - } - - private fun showLoading() { - binding.progress.visibility = View.VISIBLE - binding.recyclerView.visibility = View.GONE - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} diff --git a/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListViewModel.kt b/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListViewModel.kt index 6343012..2b0321d 100644 --- a/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListViewModel.kt +++ b/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListViewModel.kt @@ -11,10 +11,11 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.update -import ru.otus.marketsample.promo.domain.ConsumePromosUseCase import ru.otus.marketsample.R +import ru.otus.marketsample.promo.domain.ConsumePromosUseCase +import javax.inject.Inject -class PromoListViewModel( +class PromoListViewModel @Inject constructor( private val promoStateFactory: PromoStateFactory, private val consumePromosUseCase: ConsumePromosUseCase, ) : ViewModel() { diff --git a/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListViewModelFactory.kt b/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListViewModelFactory.kt deleted file mode 100644 index f397c06..0000000 --- a/app/src/main/java/ru/otus/marketsample/promo/feature/PromoListViewModelFactory.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ru.otus.marketsample.promo.feature - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.CreationExtras -import ru.otus.common.di.FeatureScope -import ru.otus.marketsample.promo.domain.ConsumePromosUseCase -import javax.inject.Inject - -@FeatureScope -class PromoListViewModelFactory @Inject constructor( - private val promoStateFactory: PromoStateFactory, - private val consumePromosUseCase: ConsumePromosUseCase, -) : - ViewModelProvider.Factory { - - override fun create( - modelClass: Class, - extras: CreationExtras, - ): T { - when { - modelClass.isAssignableFrom(PromoListViewModel::class.java) -> { - @Suppress("UNCHECKED_CAST") - return PromoListViewModel( - promoStateFactory = promoStateFactory, - consumePromosUseCase = consumePromosUseCase, - ) as T - } - } - throw IllegalArgumentException("Unknown ViewModel class") - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/otus/marketsample/promo/feature/PromoStateFactory.kt b/app/src/main/java/ru/otus/marketsample/promo/feature/PromoStateFactory.kt index 6d350a6..8be7ea7 100644 --- a/app/src/main/java/ru/otus/marketsample/promo/feature/PromoStateFactory.kt +++ b/app/src/main/java/ru/otus/marketsample/promo/feature/PromoStateFactory.kt @@ -4,7 +4,6 @@ import ru.otus.common.di.FeatureScope import ru.otus.marketsample.promo.domain.Promo import javax.inject.Inject -@FeatureScope class PromoStateFactory @Inject constructor() { fun map(promo: Promo): PromoState { return PromoState( diff --git a/app/src/main/java/ru/otus/marketsample/promo/feature/adapter/PromoAdapter.kt b/app/src/main/java/ru/otus/marketsample/promo/feature/adapter/PromoAdapter.kt deleted file mode 100644 index 0f6b562..0000000 --- a/app/src/main/java/ru/otus/marketsample/promo/feature/adapter/PromoAdapter.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ru.otus.marketsample.promo.feature.adapter - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import ru.otus.common.di.FeatureScope -import ru.otus.marketsample.databinding.ItemPromoBinding -import ru.otus.marketsample.promo.feature.PromoState -import javax.inject.Inject - -@FeatureScope -class PromoAdapter @Inject constructor() : ListAdapter(DiffCallback()) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PromoHolder { - return PromoHolder( - ItemPromoBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - ) - } - - override fun onBindViewHolder(holder: PromoHolder, position: Int) { - val entity = getItem(position) - entity?.let { - holder.bind(entity) - } - } -} - -private class DiffCallback : DiffUtil.ItemCallback() { - - override fun areItemsTheSame(oldItem: PromoState, newItem: PromoState): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame(oldItem: PromoState, newItem: PromoState): Boolean { - return oldItem == newItem - } -} diff --git a/app/src/main/java/ru/otus/marketsample/promo/feature/adapter/PromoHolder.kt b/app/src/main/java/ru/otus/marketsample/promo/feature/adapter/PromoHolder.kt deleted file mode 100644 index 5d08f5d..0000000 --- a/app/src/main/java/ru/otus/marketsample/promo/feature/adapter/PromoHolder.kt +++ /dev/null @@ -1,17 +0,0 @@ -package ru.otus.marketsample.promo.feature.adapter - -import androidx.recyclerview.widget.RecyclerView -import coil.load -import ru.otus.marketsample.databinding.ItemPromoBinding -import ru.otus.marketsample.promo.feature.PromoState - -class PromoHolder( - private val binding: ItemPromoBinding, -) : RecyclerView.ViewHolder(binding.root) { - - fun bind(promoState: PromoState) { - binding.image.load(promoState.image) - binding.name.text = promoState.name - binding.description.text = promoState.description - } -} diff --git a/app/src/main/java/ru/otus/marketsample/promo/feature/di/PromoComponent.kt b/app/src/main/java/ru/otus/marketsample/promo/feature/di/PromoComponent.kt index b1ad582..aebbe50 100644 --- a/app/src/main/java/ru/otus/marketsample/promo/feature/di/PromoComponent.kt +++ b/app/src/main/java/ru/otus/marketsample/promo/feature/di/PromoComponent.kt @@ -3,7 +3,6 @@ package ru.otus.marketsample.promo.feature.di import dagger.Component import ru.otus.common.data.promo.PromoRepository import ru.otus.common.di.FeatureScope -import ru.otus.marketsample.promo.feature.PromoListFragment @FeatureScope @Component(dependencies = [PromoComponentDependencies::class]) @@ -13,8 +12,6 @@ interface PromoComponent { interface Factory { fun create(dependencies: PromoComponentDependencies): PromoComponent } - - fun inject(productFragment: PromoListFragment) } interface PromoComponentDependencies { diff --git a/app/src/main/java/ru/otus/marketsample/promo/navigation/PromoNavHost.kt b/app/src/main/java/ru/otus/marketsample/promo/navigation/PromoNavHost.kt new file mode 100644 index 0000000..9a85f57 --- /dev/null +++ b/app/src/main/java/ru/otus/marketsample/promo/navigation/PromoNavHost.kt @@ -0,0 +1,19 @@ +package ru.otus.marketsample.promo.navigation + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import ru.otus.marketsample.di.ViewModelsFactory +import ru.otus.marketsample.navigation.Promo +import ru.otus.marketsample.promo.PromoScreen + +fun NavGraphBuilder.promoGraph( + navController: NavController, + viewModelFactory: ViewModelsFactory +) { + composable { + PromoScreen( + viewModelsFactory = viewModelFactory + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 9945e95..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml deleted file mode 100644 index 8252830..0000000 --- a/app/src/main/res/layout/fragment_main.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_product_list.xml b/app/src/main/res/layout/fragment_product_list.xml deleted file mode 100644 index 7dc8ee3..0000000 --- a/app/src/main/res/layout/fragment_product_list.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_promo_list.xml b/app/src/main/res/layout/fragment_promo_list.xml deleted file mode 100644 index da5a492..0000000 --- a/app/src/main/res/layout/fragment_promo_list.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_product.xml b/app/src/main/res/layout/item_product.xml deleted file mode 100644 index 29944da..0000000 --- a/app/src/main/res/layout/item_product.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_promo.xml b/app/src/main/res/layout/item_promo.xml deleted file mode 100644 index f12f53e..0000000 --- a/app/src/main/res/layout/item_promo.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/build.gradle b/build.gradle index cee1c80..f4e72af 100644 --- a/build.gradle +++ b/build.gradle @@ -5,4 +5,5 @@ plugins { alias(libs.plugins.kotlinxSerialization) apply false alias(libs.plugins.kapt) apply false alias(libs.plugins.androidLibrary) apply false + alias(libs.plugins.compose.compiler) apply false } \ No newline at end of file diff --git a/common/ui/build.gradle b/common/ui/build.gradle index c7261af..5c470b4 100644 --- a/common/ui/build.gradle +++ b/common/ui/build.gradle @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.androidLibrary) alias(libs.plugins.kotlinAndroid) + alias(libs.plugins.compose.compiler) } android { @@ -18,10 +19,23 @@ android { kotlinOptions { jvmTarget = '17' } + + buildFeatures { + compose true + } } dependencies { implementation libs.core.ktx implementation libs.appcompat implementation libs.material + + implementation platform(libs.androidx.compose.bom) + + implementation libs.androidx.compose.material3 + implementation libs.androidx.compose.foundation + implementation libs.androidx.compose.ui + + implementation libs.androidx.compose.ui.tooling.preview + debugImplementation libs.androidx.compose.ui.tooling } \ No newline at end of file diff --git a/common/ui/src/main/kotlin/ru/otus/components/Badge.kt b/common/ui/src/main/kotlin/ru/otus/components/Badge.kt new file mode 100644 index 0000000..f9a5d9d --- /dev/null +++ b/common/ui/src/main/kotlin/ru/otus/components/Badge.kt @@ -0,0 +1,72 @@ +package ru.otus.components + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import ru.otus.common.ui.R + +@Composable +fun Badge( + value: String, + modifier: Modifier = Modifier, +) { + val color1 = colorResource(R.color.purple_500) + val color2 = colorResource(R.color.purple_700) + Text( + text = value, + modifier = modifier + .padding(8.dp) + .graphicsLayer { + shape = RoundedCornerShape( + topStart = 40.dp, + topEnd = 4.dp, + bottomStart = 40.dp, + bottomEnd = 20.dp, + ) + clip = true + } + .border( + 2.dp, + color = colorResource(R.color.white), + shape = RoundedCornerShape( + topStart = 40.dp, + topEnd = 4.dp, + bottomStart = 40.dp, + bottomEnd = 20.dp, + ) + ) + .drawBehind { + drawRect( + brush = Brush.linearGradient( + colors = listOf(color1, color2) + ) + ) + } + .padding(horizontal = 10.dp, vertical = 4.dp), + color = colorResource(R.color.white), + fontSize = 14.sp, + fontWeight = FontWeight.Bold + ) +} + +@Preview +@Composable +private fun Badge_Preview() { + MaterialTheme { + Badge( + value = "20%" + ) + } +} \ No newline at end of file diff --git a/common/ui/src/main/kotlin/ru/otus/components/MarketSnackbar.kt b/common/ui/src/main/kotlin/ru/otus/components/MarketSnackbar.kt new file mode 100644 index 0000000..2800089 --- /dev/null +++ b/common/ui/src/main/kotlin/ru/otus/components/MarketSnackbar.kt @@ -0,0 +1,7 @@ +package ru.otus.components + +import androidx.compose.runtime.Composable + +@Composable +fun MarketSnackbar() { +} \ No newline at end of file diff --git a/common/ui/src/main/kotlin/ru/otus/components/Price.kt b/common/ui/src/main/kotlin/ru/otus/components/Price.kt new file mode 100644 index 0000000..d713647 --- /dev/null +++ b/common/ui/src/main/kotlin/ru/otus/components/Price.kt @@ -0,0 +1,47 @@ +package ru.otus.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import ru.otus.common.ui.R + +@Composable +fun Price( + value: String, + modifier: Modifier = Modifier, +) { + Text( + modifier = modifier + .graphicsLayer { + shape = RoundedCornerShape(8.dp) + clip = true + } + .background(Color(0xFFFFF3E0.toInt())) + .padding(horizontal = 12.dp, vertical = 8.dp), + text = value, + color = colorResource(R.color.purple_500), + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + ) +} + +@Preview(showBackground = true) +@Composable +private fun Price_Preview() { + MaterialTheme { + Price( + value = "2000 руб" + ) + } +} \ No newline at end of file diff --git a/common/ui/src/main/kotlin/ru/otus/components/ProductCard.kt b/common/ui/src/main/kotlin/ru/otus/components/ProductCard.kt new file mode 100644 index 0000000..94167ab --- /dev/null +++ b/common/ui/src/main/kotlin/ru/otus/components/ProductCard.kt @@ -0,0 +1,114 @@ +package ru.otus.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import ru.otus.common.ui.R + +@Composable +fun ProductCard( + modifier: Modifier = Modifier, + image: @Composable BoxScope.() -> Unit = {}, + badge: @Composable BoxScope.() -> Unit = {}, + title: @Composable ColumnScope.() -> Unit = {}, + price: @Composable ColumnScope.() -> Unit = {}, +) { + Column( + modifier = modifier + .height(130.dp) + .background(color = colorResource(R.color.white)), + ) { + Row { + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .graphicsLayer { + shape = RoundedCornerShape(12.dp) + clip = true + } + ) { + image.invoke(this) + badge.invoke(this) + } + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 12.dp) + .weight(1f), + verticalArrangement = Arrangement.SpaceBetween + ) { + title.invoke(this) + price.invoke(this) + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun ProductCard_Preview() { + MaterialTheme { + ProductCard( + image = { + Image( + painter = painterResource(R.drawable.cats), + contentDescription = null, + modifier = Modifier + .fillMaxSize(), + contentScale = ContentScale.Crop + ) + }, + badge = { + Badge( + value = "20%", + modifier = Modifier + .align(Alignment.TopEnd) + ) + }, + title = { + Text( + text = "Персиковый кранч", + modifier = Modifier + .fillMaxWidth(), + overflow = TextOverflow.Ellipsis, + maxLines = 2, + fontSize = 18.sp, + color = colorResource(R.color.black), + fontWeight = FontWeight.Medium + ) + }, + price = { + Price( + "2000 руб", + modifier = Modifier + .align(Alignment.End) + ) + } + ) + } +} \ No newline at end of file diff --git a/common/ui/src/main/kotlin/ru/otus/components/PromoCard.kt b/common/ui/src/main/kotlin/ru/otus/components/PromoCard.kt new file mode 100644 index 0000000..9093b93 --- /dev/null +++ b/common/ui/src/main/kotlin/ru/otus/components/PromoCard.kt @@ -0,0 +1,82 @@ +package ru.otus.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import ru.otus.common.ui.R + +@Composable +fun PromoCard( + modifier: Modifier = Modifier, + image: @Composable () -> Unit, + title: @Composable () -> Unit, + description: @Composable () -> Unit, +) { + val color2 = Color(0x00000000) + val color1 = Color(0x00AA0000) + Box( + modifier = modifier + .fillMaxWidth() + .height(250.dp) + ) { + image.invoke() + Column( + modifier = Modifier + .fillMaxWidth() + .drawBehind { + drawRect( + brush = Brush.linearGradient( + colors = listOf(color1, color2) + ) + ) + } + .padding(10.dp) + .align(Alignment.BottomCenter), + verticalArrangement = Arrangement.Bottom + ) { + title.invoke() + description.invoke() + } + } +} + +@Preview +@Composable +fun PromoCard_Preview() { + MaterialTheme { + PromoCard( + image = { + Image( + painter = painterResource(R.drawable.cats), + contentDescription = null, + contentScale = ContentScale.Crop + ) + }, + title = { + Text("Какой-то текст заголовка") + }, + description = { + Text("Какой-то текст описания") + } + ) + } +} \ No newline at end of file diff --git a/common/ui/src/main/kotlin/ru/otus/theme/MarketTheme.kt b/common/ui/src/main/kotlin/ru/otus/theme/MarketTheme.kt new file mode 100644 index 0000000..798ddf3 --- /dev/null +++ b/common/ui/src/main/kotlin/ru/otus/theme/MarketTheme.kt @@ -0,0 +1,26 @@ +package ru.otus.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable + + +@Composable +fun MarketTheme( + isDarkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit, +) { + + val colorScheme = if (isDarkTheme) { + darkColorScheme() + } else { + lightColorScheme() + } + + MaterialTheme( + colorScheme = colorScheme, + content = content + ) +} \ No newline at end of file diff --git a/common/ui/src/main/res/drawable/cats.jpg b/common/ui/src/main/res/drawable/cats.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2d2c3ba153eda150d307633b5ebe7c20c21ce3df GIT binary patch literal 60380 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