RnD for caching cart and also adding items to cart locally.

Integrated product view api for viewing product.

Added removing cart item functionality from product view fragment.

Added callback to listen removal changes when gone to cart from product view fragment.

Debugged crash with configuration changes with multiple fragments in an activity with no public constructor.
And issue with no saved state when configuration is changed.

Understood and integrated navigation-safe-args in ShopActivity.
This commit is contained in:
2024-07-31 20:52:15 +05:30
parent 4d393b0fcf
commit 73e6a9f3fc
27 changed files with 489 additions and 203 deletions

View File

@@ -2,6 +2,7 @@ plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
id("kotlin-parcelize")
alias(libs.plugins.navigationSafeArgs)
}
android {

View File

@@ -180,7 +180,7 @@ class MyListFragment : Fragment() {
karaokeTxt.text = getString(R.string.karaoke)
gamesTxt.text = getString(R.string.games)
noData.text = getString(R.string.no_favorites_added)
noDataTxt.text = getString(R.string.no_favorites_added)
if (webSeriesEAdapter.currentList.isNotEmpty()){
webSeriesEAdapter.notifyItemRangeChanged(0, webSeriesEAdapter.currentList.size)

View File

@@ -12,6 +12,7 @@ import com.woka.shop.models.createorder.CreateOrderRequestData
import com.woka.shop.models.createorder.CreateOrderResponse
import com.woka.shop.models.edd.EDDResponse
import com.woka.shop.models.productlisting.ProductListingResponse
import com.woka.shop.models.productlisting.ShopProduct
import com.woka.shop.models.subcategorylisting.SubCategoryResponse
import com.woka.shop.models.superlisting.SuperCategoryResponse
import okhttp3.FormBody
@@ -35,6 +36,9 @@ interface ShopApiService {
@POST("v2/shop_product_listing")
suspend fun productListing(@Body formBody: FormBody): Response<ApiResponse<ProductListingResponse>>
@POST("shop_product_view")
suspend fun productView(@Body formBody: FormBody): Response<ApiResponse<ArrayList<ShopProduct>>>
// cart
@GET("cart_listing")
suspend fun cartListing(): Response<ApiResponse<CartResponse>>

View File

@@ -75,6 +75,16 @@ object ShopRepository {
}
}
suspend fun getProductData(id: Int): ApiResult<ArrayList<ShopProduct>> {
return handleApiCall {
apiService.productView(
FormBody.Builder()
.add("product_id", "$id")
.build()
)
}
}
// cart listing with loose caching
private var cartResponse: CartResponse? = null

View File

@@ -32,7 +32,7 @@ class CategoryAdapter:
inner class CategoryViewHolder(val binding: CategoryViweHolderBinding) :
ViewHolder(binding.root)
var onCategoryClickListener: ((BaseCategory) -> Unit)? = null
var onCategoryClickListener: ((Int) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
return CategoryViewHolder(
@@ -52,7 +52,9 @@ class CategoryAdapter:
title.text = category.title
holder.binding.root.setOnClickListener {
onCategoryClickListener?.invoke(category)
category.id?.let {
onCategoryClickListener?.invoke(it)
}
}
}
}

View File

@@ -7,12 +7,18 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
import com.woka.databinding.ProductImageViewHolderBinding
class ProductImagesAdapter(
private val imageList: ArrayList<String>
) : RecyclerView.Adapter<ProductImagesAdapter.ImageViewHolder>(){
class ProductImagesAdapter: RecyclerView.Adapter<ProductImagesAdapter.ImageViewHolder>(){
inner class ImageViewHolder(val binding: ProductImageViewHolderBinding): ViewHolder(binding.root)
private val imageList: ArrayList<String> = ArrayList()
fun submitList(imageList: ArrayList<String>){
this.imageList.clear()
this.imageList.addAll(imageList)
notifyItemRangeChanged(0, imageList.size)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
return ImageViewHolder(
ProductImageViewHolderBinding.inflate(

View File

@@ -24,7 +24,7 @@ class ShopProductAdapter: ListAdapter<ShopProduct, ShopProductAdapter.ProductVie
inner class ProductViewHolder(val binding: ProductViewHolderBinding): ViewHolder(binding.root)
var onProductClicked: ((ShopProduct) -> Unit)? = null
var onProductClicked: ((Int) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
return ProductViewHolder(
@@ -52,7 +52,9 @@ class ShopProductAdapter: ListAdapter<ShopProduct, ShopProductAdapter.ProductVie
price.text = "${product.product_price}"
root.setOnClickListener {
onProductClicked?.invoke(product)
product.id?.let {
onProductClicked?.invoke(it)
}
}
}
}

View File

@@ -0,0 +1,10 @@
package com.woka.shop.models.productlisting
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class ProductCategory(
val category_name: String?,
val id: Int?
):Parcelable

View File

@@ -19,7 +19,8 @@ data class ShopProduct(
val sku_id: String?,
val stock_status: String?,
val sub_category_master_id: Int?,
val tax_value: String?
val tax_value: String?,
val category: ProductCategory?
): Parcelable{
constructor(cartItem: CartItem, added_to_cart: Boolean?): this(
@@ -36,7 +37,8 @@ data class ShopProduct(
cartItem.sku_id,
cartItem.stock_status,
cartItem.sub_category_master_id,
cartItem.tax_value
cartItem.tax_value,
null
)
override fun equals(other: Any?): Boolean {

View File

@@ -31,9 +31,6 @@ class CartViewModel: ViewModel() {
var selectedAddressId: Int? = null
val eddMap = HashMap<Int, String>()
// cart
val removedProducts = ArrayList<ShopProduct>()
// data callbacks
private val repository = ShopRepository

View File

@@ -1,6 +1,5 @@
package com.woka.shop.viewmodels
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -12,9 +11,7 @@ import com.woka.shop.models.productlisting.ShopProduct
import com.woka.shop.models.subcategorylisting.SubCategoryData
import com.woka.shop.models.superlisting.SuperCategory
import com.woka.utils.PagingData
import com.woka.utils.TAG
import kotlinx.coroutines.launch
import kotlin.collections.HashSet
class ShopViewModel: ViewModel() {
@@ -122,11 +119,11 @@ class ShopViewModel: ViewModel() {
if (productDataMap.containsKey(key) && productDataMap[key]?.isNotEmpty() == true) {
_productListingLiveData.postValue(ApiResult.Success(productDataMap[key]))
} else {
loadMoreEpisodes(superCategoryId, categoryId, subCategoryId)
loadMoreProducts(superCategoryId, categoryId, subCategoryId)
}
}
fun loadMoreEpisodes(superCategoryId: String, categoryId: String, subCategoryId: String?) {
fun loadMoreProducts(superCategoryId: String, categoryId: String, subCategoryId: String?) {
viewModelScope.launch {
_productListingLiveData.postValue(ApiResult.Loading())
@@ -176,30 +173,23 @@ class ShopViewModel: ViewModel() {
}
}
fun removeProductsFromCart(products: List<ShopProduct>){
val toBeReplaced = mutableSetOf<ShopProduct>()
for (productSet in productDataMap.values){
toBeReplaced.clear()
for (product in products){
if (productSet.contains(product)){
toBeReplaced.add(product)
}
}
productSet.removeAll(toBeReplaced)
productSet.addAll(toBeReplaced)
}
_productListingLiveData.postValue(ApiResult.Success(productDataMap[""]))
suspend fun loadProductData(id: Int): ApiResult<ArrayList<ShopProduct>> {
return repository.getProductData(id)
}
fun clearProductListingLiveData() {
_productListingLiveData.postValue(ApiResult.Loading())
}
// product view
var currentProductRemovedListener: (() -> Unit)? = null
var currentProductId: Int = -1
fun clearCurrentProductCallbacks(){
currentProductRemovedListener = null
currentProductId = -1
}
// cart
val cartCountLivedata: LiveData<Int>
get() {
@@ -211,4 +201,8 @@ class ShopViewModel: ViewModel() {
suspend fun addToCart(shopProduct: ShopProduct): ApiResult<Any> {
return repository.addToCart(shopProduct)
}
suspend fun removeFromCart(id: Int): ApiResult<Double> {
return repository.removeCartItem(id)
}
}

View File

@@ -12,7 +12,7 @@ import com.woka.utils.WokaBaseActivity
class CartActivity : WokaBaseActivity() {
companion object{
const val EXTRA_REMOVED_CART_ITEMS = "extra_removed_cart_items"
const val EXTRA_CURRENT_PRODUCT = "extra_current_product"
}
private lateinit var binding: ActivityCartBinding

View File

@@ -3,14 +3,17 @@ package com.woka.shop.views
import android.content.Intent
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.ViewModelProvider
import com.woka.R
import com.woka.databinding.ActivityShopBinding
import com.woka.shop.viewmodels.ShopViewModel
import com.woka.shop.views.fragments.shop.ShopFragment1
import com.woka.shop.views.CartActivity.Companion.EXTRA_CURRENT_PRODUCT
import com.woka.utils.WokaBaseActivity
import com.woka.utils.lightStatusBar
import com.woka.utils.setVisibility
class ShopActivity : WokaBaseActivity() {
@@ -18,6 +21,8 @@ class ShopActivity : WokaBaseActivity() {
private lateinit var binding: ActivityShopBinding
private lateinit var viewModel: ShopViewModel
private lateinit var cartLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
@@ -32,40 +37,48 @@ class ShopActivity : WokaBaseActivity() {
viewModel = ViewModelProvider(this)[ShopViewModel::class.java]
window.navigationBarColor = getColor(R.color.orders_bg)
supportFragmentManager.beginTransaction()
.add(R.id.fcv_shop, ShopFragment1.newInstance())
.commit()
window.lightStatusBar(false)
initViews()
clickEvents()
setObservers()
registerLaunchers()
}
private fun initViews(){
private fun initViews() {
binding.apply {
title.text = getString(R.string.shop)
}
}
private fun clickEvents(){
private fun clickEvents() {
binding.apply {
backBtn.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
cart.setOnClickListener {
startActivity(
Intent(this@ShopActivity, CartActivity::class.java)
)
cartLauncher.launch(Intent(this@ShopActivity, CartActivity::class.java).apply {
putExtra(EXTRA_CURRENT_PRODUCT, viewModel.currentProductId)
})
}
}
}
private fun registerLaunchers() {
cartLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
if (activityResult.resultCode == RESULT_OK) {
viewModel.currentProductRemovedListener?.invoke()
}
}
}
private fun setObservers() {
viewModel.cartCountLivedata.observe(this){
viewModel.cartCountLivedata.observe(this) {
binding.cartCount.text = "$it"
binding.cartCountView.setVisibility(it > 0)
}

View File

@@ -2,6 +2,7 @@ package com.woka.shop.views.fragments.cart
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -14,10 +15,10 @@ import com.woka.R
import com.woka.databinding.FragmentCartBinding
import com.woka.networking.ApiResult
import com.woka.shop.adapters.CartAdapter
import com.woka.shop.models.productlisting.ShopProduct
import com.woka.shop.viewmodels.CartViewModel
import com.woka.shop.views.CartActivity
import com.woka.shop.views.CartActivity.Companion.EXTRA_CURRENT_PRODUCT
import com.woka.utils.ProgressView
import com.woka.utils.TAG
import com.woka.utils.hide
import com.woka.utils.show
import com.woka.utils.toast
@@ -32,6 +33,8 @@ class CartFragment: Fragment() {
private lateinit var progressDialog: ProgressView
private var currentProductId: Int? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@@ -42,6 +45,9 @@ class CartFragment: Fragment() {
adapter = CartAdapter()
progressDialog = ProgressView(requireContext())
currentProductId = requireActivity().intent.getIntExtra(EXTRA_CURRENT_PRODUCT, -1)
Log.d(TAG, "onCreateView: $currentProductId")
return binding.root
}
@@ -101,10 +107,9 @@ class CartFragment: Fragment() {
}
}
viewModel.removedProducts.add(ShopProduct(cartItem, false))
activity?.setResult(AppCompatActivity.RESULT_OK, Intent().apply {
putParcelableArrayListExtra(CartActivity.EXTRA_REMOVED_CART_ITEMS, viewModel.removedProducts)
})
if (cartItem.id == currentProductId){
activity?.setResult(AppCompatActivity.RESULT_OK, Intent())
}
}
}
}

View File

@@ -1,14 +1,10 @@
package com.woka.shop.views.fragments.shop
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.os.Bundle
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@@ -19,19 +15,24 @@ import com.woka.networking.ApiResult
import com.woka.shop.adapters.ProductImagesAdapter
import com.woka.shop.models.productlisting.ShopProduct
import com.woka.shop.viewmodels.ShopViewModel
import com.woka.shop.views.CartActivity
import com.woka.utils.ProgressView
import com.woka.utils.hide
import com.woka.utils.show
import com.woka.utils.toast
import kotlinx.coroutines.launch
class ProductFragment private constructor(
private val shopProduct: ShopProduct,
private val category: String
): Fragment() {
private const val ARG_PRODUCT_ID = "arg_product_id"
class ProductFragment : Fragment() {
companion object {
@JvmStatic
fun newInstance(shopProduct: ShopProduct, categoryName: String) = ProductFragment(shopProduct, categoryName)
fun newInstance(productId: Int) =
ProductFragment().apply {
arguments = Bundle().apply {
putInt(ARG_PRODUCT_ID, productId)
}
}
}
private lateinit var binding: FragmentProductBinding
@@ -39,7 +40,14 @@ class ProductFragment private constructor(
private lateinit var imageAdapter: ProductImagesAdapter
private lateinit var progressView: ProgressView
private lateinit var cartLauncher: ActivityResultLauncher<Intent>
private var mShopProduct: ShopProduct? = null
private var productId: Int = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
productId = arguments?.getInt(ARG_PRODUCT_ID, -1)?:-1
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -49,11 +57,7 @@ class ProductFragment private constructor(
viewModel = ViewModelProvider(requireActivity())[ShopViewModel::class.java]
progressView = ProgressView(requireContext(), getString(R.string.please_wait))
val imageList = ArrayList<String>()
shopProduct.shop_image?.filterNotNull()?.let {
imageList.addAll(it)
}
imageAdapter = ProductImagesAdapter(imageList)
imageAdapter = ProductImagesAdapter()
return binding.root
}
@@ -61,90 +65,152 @@ class ProductFragment private constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.currentProductId = productId
initViews()
clickEvents()
registerLaunchers()
loadProductData()
setObservers()
}
override fun onDestroyView() {
super.onDestroyView()
viewModel.clearCurrentProductCallbacks()
}
private fun initViews() {
binding.apply {
vpImages.adapter = imageAdapter
TabLayoutMediator(tabLayout, vpImages) { _, _ -> }.attach()
}
}
TabLayoutMediator(tabLayout, vpImages){_, _ -> }.attach()
private fun clickEvents() {
binding.apply {
retryBtn.setOnClickListener {
loadProductData()
}
}
}
categoryName.text = category
private fun loadProductData() {
lifecycleScope.launch {
binding.apply {
shimmer.show()
noData.hide()
productView.hide()
when (val response = viewModel.loadProductData(productId)) {
is ApiResult.Error -> {
shimmer.hide()
noData.show()
productView.hide()
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
response.data?.first()?.let {
shimmer.hide()
noData.hide()
productView.show()
displayProductData(it)
}
}
}
}
}
}
private fun displayProductData(shopProduct: ShopProduct) {
mShopProduct = shopProduct
binding.apply {
val imageList = ArrayList<String>()
shopProduct.shop_image?.filterNotNull()?.let {
imageList.addAll(it)
}
imageAdapter.submitList(imageList)
categoryName.text = shopProduct.category?.category_name
skuId.text = shopProduct.sku_id
price.text = shopProduct.product_price
addToCart.text = if (shopProduct.added_to_cart == true){
getString(R.string.view_cart)
}else{
addToCart.text = if (shopProduct.added_to_cart == true) {
getString(R.string.remove_from_cart)
} else {
getString(R.string.add_to_cart)
}
if (shopProduct.sub_category_master_id == 12){
if (shopProduct.sub_category_master_id == 12) {
title.text = shopProduct.shop_master_detail?.product_name_english
description.text = Html.fromHtml(
shopProduct.shop_master_detail?.description_english,
Html.FROM_HTML_MODE_LEGACY
)
}else{
} else {
title.text = shopProduct.shop_master_detail?.product_name_hindi
description.text = Html.fromHtml(
shopProduct.shop_master_detail?.description_hindi,
Html.FROM_HTML_MODE_LEGACY
)
}
}
}
private fun clickEvents(){
binding.apply {
addToCart.setOnClickListener {
if (shopProduct.added_to_cart == false){
lifecycleScope.launch {
progressView.show()
when (val response = viewModel.addToCart(shopProduct)){
lifecycleScope.launch {
progressView.show()
if (shopProduct.added_to_cart == false) {
when (val response = viewModel.addToCart(shopProduct)) {
is ApiResult.Error -> {
progressView.hide()
toast(response.errorMessage)
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
progressView.hide()
toast(response.message)
shopProduct.added_to_cart = true
addToCart.text = getString(R.string.view_cart)
addToCart.text = getString(R.string.remove_from_cart)
}
}
} else {
when (val response = viewModel.removeFromCart(productId)) {
is ApiResult.Error -> {
progressView.hide()
toast(response.errorMessage)
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
progressView.hide()
toast(response.message)
shopProduct.added_to_cart = false
addToCart.text = getString(R.string.add_to_cart)
}
}
}
}else{
activity?.let {
cartLauncher.launch(Intent(it, CartActivity::class.java))
}
}
}
}
}
private fun registerLaunchers(){
cartLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ activityResult ->
if (activityResult.resultCode == RESULT_OK){
@Suppress("DEPRECATION")
activityResult.data?.getParcelableArrayListExtra<ShopProduct>(CartActivity.EXTRA_REMOVED_CART_ITEMS)?.let {
viewModel.removeProductsFromCart(it)
if (it.toSet().contains(shopProduct)){
binding.addToCart.text = if (shopProduct.added_to_cart == true){
getString(R.string.view_cart)
}else{
getString(R.string.add_to_cart)
}
}
private fun setObservers() {
viewModel.currentProductRemovedListener = {
binding.apply {
mShopProduct?.added_to_cart = mShopProduct?.added_to_cart == false
addToCart.text = if (mShopProduct?.added_to_cart == true) {
getString(R.string.remove_from_cart)
} else {
getString(R.string.add_to_cart)
}
}
}

View File

@@ -6,7 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.woka.R
import androidx.navigation.fragment.findNavController
import com.woka.databinding.FragmentShop1Binding
import com.woka.networking.ApiResult
import com.woka.shop.adapters.CategoryAdapter
@@ -15,7 +15,7 @@ import com.woka.shop.viewmodels.ShopViewModel
import com.woka.utils.hide
import com.woka.utils.show
class ShopFragment1 private constructor(): Fragment() {
class ShopFragment1 : Fragment() {
private lateinit var binding: FragmentShop1Binding
private lateinit var viewModel: ShopViewModel
@@ -41,7 +41,8 @@ class ShopFragment1 private constructor(): Fragment() {
setObservers()
if (!viewModel.superCategoryLiveData.isInitialized ||
viewModel.superCategoryLiveData.value !is ApiResult.Success) {
viewModel.superCategoryLiveData.value !is ApiResult.Success
) {
viewModel.loadSuperCategories()
}
@@ -60,10 +61,7 @@ class ShopFragment1 private constructor(): Fragment() {
}
adapter.onCategoryClickListener = {
parentFragmentManager.beginTransaction()
.replace(R.id.fcv_shop, ShopFragment2.newInstance("${it.id}"))
.addToBackStack(null)
.commitAllowingStateLoss()
findNavController().navigate(ShopFragment1Directions.actionShopFragment1ToShopFragment2(it))
}
}
}

View File

@@ -6,7 +6,8 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.woka.R
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.woka.databinding.FragmentShop2Binding
import com.woka.networking.ApiResult
import com.woka.shop.adapters.CategoryAdapter
@@ -15,12 +16,18 @@ import com.woka.shop.viewmodels.ShopViewModel
import com.woka.utils.hide
import com.woka.utils.show
class ShopFragment2 private constructor(private val superCategoryId: String): Fragment() {
class ShopFragment2 : Fragment() {
private lateinit var binding: FragmentShop2Binding
private lateinit var viewModel: ShopViewModel
private lateinit var adapter: CategoryAdapter
private val args: ShopFragment2Args by navArgs()
private val superCategoryId: String by lazy {
"${args.superCategoryId}"
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@@ -61,10 +68,12 @@ class ShopFragment2 private constructor(private val superCategoryId: String): Fr
}
adapter.onCategoryClickListener = {
parentFragmentManager.beginTransaction()
.replace(R.id.fcv_shop, ShopFragment3.newInstance(superCategoryId, "${it.id}", "${it.title}"))
.addToBackStack(null)
.commitAllowingStateLoss()
findNavController().navigate(
ShopFragment2Directions.actionShopFragment2ToShopFragment3(
superCategoryId,
"$it"
)
)
}
}
}
@@ -106,9 +115,4 @@ class ShopFragment2 private constructor(private val superCategoryId: String): Fr
}
}
}
companion object {
@JvmStatic
fun newInstance(superCategoryId: String) = ShopFragment2(superCategoryId)
}
}

View File

@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.navArgs
import com.google.android.material.tabs.TabLayout
import com.woka.R
import com.woka.databinding.FragmentShop3Binding
@@ -16,22 +17,17 @@ import com.woka.utils.hide
import com.woka.utils.setVisibility
import com.woka.utils.show
class ShopFragment3 private constructor(
private val superCategoryId: String,
private val categoryId: String,
private val categoryName: String
) : Fragment(), TabLayout.OnTabSelectedListener {
companion object {
@JvmStatic
fun newInstance(superCategoryId: String, categoryId: String, categoryName: String) =
ShopFragment3(superCategoryId, categoryId, categoryName)
}
class ShopFragment3 : Fragment(), TabLayout.OnTabSelectedListener {
private lateinit var binding: FragmentShop3Binding
private lateinit var viewModel: ShopViewModel
private lateinit var productAdapter: ShopProductAdapter
private val args: ShopFragment3Args by navArgs()
private val superCategoryId: String by lazy { args.superCategoryId }
private val categoryId: String by lazy { args.categoryId }
private var loadMoreProducts = false
override fun onCreateView(
@@ -80,14 +76,11 @@ class ShopFragment3 private constructor(
prodLoadMoreBtn.setOnClickListener {
loadMoreProducts = true
val subCategoryId = categoryTabs.getTabAt(categoryTabs.selectedTabPosition)?.tag?.toString()
viewModel.loadMoreEpisodes(superCategoryId, categoryId, subCategoryId)
viewModel.loadMoreProducts(superCategoryId, categoryId, subCategoryId)
}
productAdapter.onProductClicked = {
parentFragmentManager.beginTransaction()
.add(R.id.fcv_shop, ProductFragment.newInstance(it, categoryName))
.addToBackStack(null)
.commit()
}
}
}

View File

@@ -102,15 +102,11 @@
</RelativeLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fcv_shop"
android:id="@+id/fc_shop"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar"
/>
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph_shop" />
</LinearLayout>

View File

@@ -263,23 +263,41 @@
</androidx.core.widget.NestedScrollView>
<TextView
<LinearLayout
android:id="@+id/no_data"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_favorites_added"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_14ssp"
android:orientation="vertical"
android:gravity="center_horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
/>
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:contentDescription="@string/image"
android:src="@drawable/img_support"
/>
<TextView
android:id="@+id/no_data_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_favorites_added"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_14ssp"
android:layout_marginTop="10dp"
/>
</LinearLayout>
<ProgressBar
android:id="@+id/progress_bar"

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/orders_bg"
tools:context=".shop.views.fragments.shop.ProductFragment">
@@ -12,40 +12,162 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.7"
/>
app:layout_constraintGuide_percent="0.7" />
<ImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:contentDescription="@string/image"
android:src="@drawable/img_my_orders_ng"
android:scaleType="fitXY"
android:src="@drawable/img_my_orders_ng"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/g1"
/>
app:layout_constraintTop_toBottomOf="@id/g1" />
<androidx.core.widget.NestedScrollView
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
app:shimmer_auto_start="true"
app:shimmer_duration="1500"
app:shimmer_highlight_alpha="0.5">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="15dp"
android:paddingVertical="10dp"
android:layout_margin="15dp"
android:orientation="vertical">
<View
android:layout_width="300dp"
android:layout_height="20dp"
android:background="@color/black_50" />
<View
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_marginTop="15dp"
android:background="@color/black_50" />
<View
android:layout_width="250dp"
android:layout_height="20dp"
android:layout_marginTop="15dp"
android:background="@color/black_50" />
<View
android:layout_width="200dp"
android:layout_height="20dp"
android:layout_marginTop="15dp"
android:background="@color/black_50" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
>
<View
android:layout_width="100dp"
android:layout_height="25dp"
android:layout_marginTop="15dp"
android:layout_centerVertical="true"
android:background="@drawable/round_bg_5"
android:backgroundTint="@color/black_50" />
<View
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginTop="15dp"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:backgroundTint="@color/black_50"
android:background="@drawable/round_25" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="15dp"
android:background="@color/black_50" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<LinearLayout
android:id="@+id/no_data"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:contentDescription="@string/image"
android:src="@drawable/img_support"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/something_went_wrong"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_14ssp"
android:layout_marginTop="10dp"
/>
<TextView
android:id="@+id/retry_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/retry_underline"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:padding="5dp"
/>
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/product_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="15dp"
android:paddingVertical="10dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Some title of the product"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_14ssp"
tools:text="Some title of the product"
/>
@@ -53,25 +175,23 @@
android:id="@+id/vp_images"
android:layout_width="match_parent"
android:layout_height="@dimen/_120sdp"
android:layout_marginTop="15dp"
/>
android:layout_marginTop="15dp" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginHorizontal="15dp"
android:background="@android:color/transparent"
app:tabBackground="@drawable/product_indicator_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabPaddingEnd="10dp"
app:tabPaddingStart="10dp"
app:tabRippleColor="@android:color/transparent"
android:background="@android:color/transparent"
android:layout_marginHorizontal="15dp"
/>
app:tabRippleColor="@android:color/transparent" />
<LinearLayout
android:layout_width="match_parent"
@@ -82,8 +202,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/category_name"
android:fontFamily="@font/exo_2_bold"
android:text="@string/category_name"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
@@ -94,12 +214,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="This is a random text for preview"
android:layout_marginStart="5dp"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:layout_marginStart="5dp"
tools:text="This is a random text for preview"
/>
@@ -115,8 +235,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sku_id"
android:fontFamily="@font/exo_2_bold"
android:text="@string/sku_id"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
@@ -127,12 +247,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="This is a random text for preview"
android:layout_marginStart="5dp"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:layout_marginStart="5dp"
tools:text="This is a random text for preview"
/>
@@ -148,17 +268,17 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:text="$ 200"
android:fontFamily="@font/exo_2_bold"
android:maxLines="1"
android:textColor="@color/color_primary"
android:textSize="@dimen/_16ssp"
android:maxLines="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/addToCart"
app:layout_constraintTop_toTopOf="@id/addToCart"
app:layout_constraintBottom_toBottomOf="@id/addToCart"
app:layout_constraintEnd_toStartOf="@id/addToCart"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/addToCart"
tools:text="$ 200"
/>
<Button
@@ -166,24 +286,24 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_to_cart"
android:fontFamily="@font/exo_2_extrabold"
android:textColor="@color/white"
android:textSize="@dimen/_10ssp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@drawable/round_bg_25"
android:backgroundTint="@color/color_primary"
android:drawableStart="@drawable/ic_cart_filled"
android:drawablePadding="15dp"
android:paddingEnd="20dp"
android:paddingStart="15dp"
android:fontFamily="@font/exo_2_extrabold"
android:paddingVertical="5dp"
android:paddingStart="15dp"
android:paddingEnd="20dp"
android:text="@string/add_to_cart"
android:textColor="@color/white"
android:textSize="@dimen/_10ssp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
@@ -194,11 +314,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:layout_marginTop="15dp"
android:textSize="@dimen/_12ssp"
/>

View File

@@ -5,7 +5,7 @@
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/orders_bg"
tools:context=".shop.views.fragments.shop.ShopFragment2">
tools:context=".shop.views.fragments.shop.ShopFragment1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_super_category"

View File

@@ -5,7 +5,7 @@
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/orders_bg"
tools:context=".shop.views.fragments.shop.ShopFragment2">
tools:context=".shop.views.fragments.shop.ShopFragment3">
<RelativeLayout
android:layout_width="match_parent"

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph_shop"
app:startDestination="@id/shopFragment1">
<fragment
android:id="@+id/shopFragment1"
android:name="com.woka.shop.views.fragments.shop.ShopFragment1"
android:label="fragment_shop1"
tools:layout="@layout/fragment_shop1" >
<action
android:id="@+id/action_shopFragment1_to_shopFragment2"
app:destination="@id/shopFragment2" />
</fragment>
<fragment
android:id="@+id/shopFragment2"
android:name="com.woka.shop.views.fragments.shop.ShopFragment2"
android:label="fragment_shop2"
tools:layout="@layout/fragment_shop2" >
<action
android:id="@+id/action_shopFragment2_to_shopFragment3"
app:destination="@id/shopFragment3" />
<argument
android:name="superCategoryId"
app:argType="integer"
android:defaultValue="-1" />
</fragment>
<fragment
android:id="@+id/shopFragment3"
android:name="com.woka.shop.views.fragments.shop.ShopFragment3"
android:label="fragment_shop3"
tools:layout="@layout/fragment_shop3" >
<argument
android:name="superCategoryId"
app:argType="string" />
<argument
android:name="categoryId"
app:argType="string" />
</fragment>
</navigation>

View File

@@ -281,5 +281,6 @@
<string name="category_name">Category Name :</string>
<string name="sku_id">SKU Id :</string>
<string name="add_to_cart">ADD TO CART</string>
<string name="remove_from_cart">REMOVE</string>
<string name="view_cart">view cart</string>
</resources>

View File

@@ -1,5 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.jetbrainsKotlinAndroid) apply false
alias(libs.plugins.navigationSafeArgs) apply false
}

View File

@@ -11,6 +11,7 @@ activity = "1.8.0"
constraintlayout = "2.1.4"
navigationFragmentKtx = "2.7.7"
navigationUiKtx = "2.7.7"
navigationSafeArgs = "2.7.7"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -27,4 +28,4 @@ androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
navigationSafeArgs = {id = "androidx.navigation.safeargs.kotlin", version.ref = "navigationSafeArgs"}