Created cart view, viewmodel

integrated cart list, cart item remove api and caching cart data.

Created Activity-fragment view for cart flow with navigation graph.

Created order summary fragment view.

Integrated coupon listing fragment and inflated data.
This commit is contained in:
2024-07-23 20:57:10 +05:30
parent 0def2ae3a5
commit 9742f34fff
24 changed files with 1195 additions and 4 deletions

View File

@@ -17,9 +17,15 @@
android:supportsRtl="true"
android:theme="@style/Theme.Woka"
tools:targetApi="31">
<activity
android:name=".shop.views.CartActivity"
android:exported="false"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan"/>
<activity
android:name=".shop.views.ShopActivity"
android:exported="false" />
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".webseries.views.WebSeriesActivity"
android:exported="false"

View File

@@ -1,12 +1,15 @@
package com.woka.shop
import com.woka.networking.ApiResponse
import com.woka.shop.models.cartlisting.CartResponse
import com.woka.shop.models.categorylisting.CategoryResponse
import com.woka.shop.models.couponlisting.CouponsResponse
import com.woka.shop.models.subcategorylisting.SubCategoryResponse
import com.woka.shop.models.superlisting.SuperCategoryResponse
import okhttp3.FormBody
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
interface ShopApiService {
@@ -19,4 +22,13 @@ interface ShopApiService {
@POST("sub_category_listing")
suspend fun subcategoryListing(@Body formBody: FormBody): Response<ApiResponse<SubCategoryResponse>>
@GET("cart_listing")
suspend fun cartListing(): Response<ApiResponse<CartResponse>>
@POST("remove_cart")
suspend fun removeCartItem(@Body formBody: FormBody): Response<ApiResponse<Any>>
@GET("coupon_listing")
suspend fun couponsListing(): Response<ApiResponse<CouponsResponse>>
}

View File

@@ -3,7 +3,9 @@ package com.woka.shop
import com.woka.networking.ApiResult
import com.woka.networking.RetrofitHelper
import com.woka.networking.RetrofitHelper.handleApiCall
import com.woka.shop.models.cartlisting.CartResponse
import com.woka.shop.models.categorylisting.CategoryResponse
import com.woka.shop.models.couponlisting.CouponsResponse
import com.woka.shop.models.subcategorylisting.SubCategoryResponse
import com.woka.shop.models.superlisting.SuperCategoryResponse
import okhttp3.FormBody
@@ -12,7 +14,7 @@ object ShopRepository {
private val apiService = RetrofitHelper.getRetrofit().create(ShopApiService::class.java)
suspend fun superCategoryListing(): ApiResult<SuperCategoryResponse> {
return handleApiCall{
return handleApiCall {
apiService.superCategoryListing(
FormBody.Builder()
.add("module_id", "5")
@@ -22,7 +24,7 @@ object ShopRepository {
}
suspend fun categoryListing(superCategoryId: String): ApiResult<CategoryResponse> {
return handleApiCall{
return handleApiCall {
apiService.categoryListing(
FormBody.Builder()
.add("module_id", "5")
@@ -33,7 +35,7 @@ object ShopRepository {
}
suspend fun subCategoryListing(categoryId: String): ApiResult<SubCategoryResponse> {
return handleApiCall{
return handleApiCall {
apiService.subcategoryListing(
FormBody.Builder()
.add("category_id", categoryId)
@@ -41,4 +43,67 @@ object ShopRepository {
)
}
}
// cart listing with loose caching
private var cartResponse: CartResponse? = null
suspend fun cartListing(): ApiResult<CartResponse> {
if (cartResponse != null) {
return ApiResult.Success(cartResponse)
}
val response = handleApiCall {
apiService.cartListing()
}
when (response) {
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
cartResponse = response.data
}
}
return response
}
suspend fun removeCartItem(id: Int): ApiResult<Double> {
val response = handleApiCall {
apiService.removeCartItem(
FormBody.Builder()
.add("shop_master_id", "$id")
.build()
)
}
when (response) {
is ApiResult.Error -> {
return ApiResult.Error(response.errorMessage, response.error)
}
is ApiResult.Loading -> {
return ApiResult.Loading()
}
is ApiResult.Success -> {
// removing from cache
cartResponse?.let { cartData ->
cartData.result?.let { cartItems ->
cartItems.find { it?.id == id }?.let {item ->
val remainingAmount = (cartData.total_amount?:0.0) - (item.product_final_price?:0.0)
cartData.total_amount = maxOf(0.0, remainingAmount)
}
cartItems.removeIf { it?.id == id }
}
}
return ApiResult.Success(cartResponse?.total_amount, response.message)
}
}
}
suspend fun couponsListing(): ApiResult<CouponsResponse> {
return handleApiCall {
apiService.couponsListing()
}
}
}

View File

@@ -0,0 +1,65 @@
package com.woka.shop.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.woka.databinding.CartItemViewHolderBinding
import com.woka.shop.models.cartlisting.CartItem
import com.woka.utils.show
import java.util.concurrent.Executors
class CartAdapter(private val showFullData: Boolean = true): ListAdapter<CartItem, CartAdapter.CartViewHolder>(ASYNC_DIFF_UTIL) {
companion object{
private val DIFF_UTIL = object :DiffUtil.ItemCallback<CartItem>(){
override fun areItemsTheSame(oldItem: CartItem, newItem: CartItem): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: CartItem, newItem: CartItem): Boolean = oldItem == newItem
}
private val ASYNC_DIFF_UTIL = AsyncDifferConfig.Builder(DIFF_UTIL)
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
.build()
}
inner class CartViewHolder(val binding: CartItemViewHolderBinding): ViewHolder(binding.root)
var onCartItemDeleteListener: ((Int, Int) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder {
return CartViewHolder(
CartItemViewHolderBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: CartViewHolder, position: Int) {
val cartItem = getItem(holder.absoluteAdapterPosition)
with(holder.binding){
if (cartItem.shop_image?.isNotEmpty() == true)
image.loadImage(cartItem.shop_image.first())
title.text = cartItem.product_name
price.text = "${cartItem.product_price}"
if (showFullData){
quantityTag.show()
quantity.text = "${cartItem.product_quantity}"
statusTag.show()
status.text = "${cartItem.stock_status}"
delete.show()
delete.setOnClickListener {
cartItem.id?.let { id -> onCartItemDeleteListener?.invoke(id, holder.absoluteAdapterPosition) }
}
}
}
}
}

View File

@@ -0,0 +1,52 @@
package com.woka.shop.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.woka.databinding.CouponViewHolderrBinding
import com.woka.shop.models.couponlisting.CouponData
import java.util.concurrent.Executors
class CouponAdapter : ListAdapter<CouponData, CouponAdapter.CouponViewHolder>(DIFF_CONFIG) {
companion object {
private val DIFF_UTIL = object : DiffUtil.ItemCallback<CouponData>() {
override fun areItemsTheSame(oldItem: CouponData, newItem: CouponData): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: CouponData, newItem: CouponData): Boolean =
oldItem == newItem
}
private val DIFF_CONFIG = AsyncDifferConfig.Builder(DIFF_UTIL)
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
.build()
}
inner class CouponViewHolder(val binding: CouponViewHolderrBinding) : ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CouponViewHolder {
return CouponViewHolder(
CouponViewHolderrBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: CouponViewHolder, position: Int) {
val coupon = getItem(holder.absoluteAdapterPosition)
holder.binding.apply {
val off = if (coupon.discount_type == 1) "${coupon.discount_value}%"
else "Rs. ${coupon.discount_value}"
val finalTxt = "$off OFF with coupon code ${coupon.coupon_code}"
textView.text = finalTxt
}
}
}

View File

@@ -0,0 +1,18 @@
package com.woka.shop.models.cartlisting
data class CartItem(
val category_master_id: Int?,
val id: Int?,
val product_name: String?,
val product_price: String?,
var product_final_price: Double?,
val product_quantity: Int?,
val remain_stock_quantity: Int?,
val shop_image: List<String?>?,
val shop_master_detail: ShopMasterDetail?,
val sku_id: String?,
val stock_status: String?,
val sub_category_master_id: Int?,
val tax_category: Any?,
val tax_value: String?
)

View File

@@ -0,0 +1,11 @@
package com.woka.shop.models.cartlisting
data class CartResponse(
val address_available: Boolean?,
val cart_quantity: Int?,
val cart_total_amount: String?,
val result: MutableList<CartItem?>?,
var total_amount: Double?,
val total_records: Int?,
val user_type: String?
)

View File

@@ -0,0 +1,10 @@
package com.woka.shop.models.cartlisting
data class ShopMasterDetail(
val description_english: String?,
val description_hindi: String?,
val id: Int?,
val product_id: Int?,
val product_name_english: String?,
val product_name_hindi: String?
)

View File

@@ -0,0 +1,6 @@
package com.woka.shop.models.couponlisting
data class Category(
val category_name: String?,
val id: Int?
)

View File

@@ -0,0 +1,13 @@
package com.woka.shop.models.couponlisting
data class CouponData(
val category: Category?,
val category_master_id: Int?,
val coupon_code: String?,
val discount_type: Int?,
val discount_value: String?,
val end_date: String?,
val id: Int?,
val start_from: String?,
val usage_limit: Int?
)

View File

@@ -0,0 +1,6 @@
package com.woka.shop.models.couponlisting
data class CouponsResponse(
val result: List<CouponData?>?,
val total_records: Int?
)

View File

@@ -0,0 +1,39 @@
package com.woka.shop.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.woka.networking.ApiResult
import com.woka.shop.ShopRepository
import com.woka.shop.models.cartlisting.CartResponse
import com.woka.shop.models.couponlisting.CouponsResponse
import kotlinx.coroutines.launch
class CartViewModel: ViewModel() {
// ui callbacks
var onToolBarTitleChange: ((String) -> Unit)? = null
// data callbacks
private val repository = ShopRepository
private val _cartListLiveData = MutableLiveData<ApiResult<CartResponse>>()
val cartListLiveData: LiveData<ApiResult<CartResponse>>
get() = _cartListLiveData
fun loadCartList(){
viewModelScope.launch {
_cartListLiveData.postValue(ApiResult.Loading())
_cartListLiveData.postValue(repository.cartListing())
}
}
suspend fun removeItem(id: Int): ApiResult<Double> {
return repository.removeCartItem(id)
}
suspend fun loadCoupons(): ApiResult<CouponsResponse>{
return repository.couponsListing()
}
}

View File

@@ -0,0 +1,48 @@
package com.woka.shop.views
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.ViewModelProvider
import com.woka.databinding.ActivityCartBinding
import com.woka.shop.viewmodels.CartViewModel
import com.woka.utils.WokaBaseActivity
class CartActivity : WokaBaseActivity() {
private lateinit var binding: ActivityCartBinding
private lateinit var viewModel: CartViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityCartBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
viewModel = ViewModelProvider(this)[CartViewModel::class.java]
clickEvents()
setObservers()
}
private fun clickEvents() {
binding.apply {
toolbar.backBtn.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
}
private fun setObservers(){
viewModel.onToolBarTitleChange = {
binding.toolbar.title.text = it
}
}
}

View File

@@ -1,5 +1,6 @@
package com.woka.shop.views
import android.content.Intent
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
@@ -46,6 +47,12 @@ class ShopActivity : WokaBaseActivity() {
backBtn.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
cart.setOnClickListener {
startActivity(
Intent(this@ShopActivity, CartActivity::class.java)
)
}
}
}
}

View File

@@ -0,0 +1,156 @@
package com.woka.shop.views.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.woka.R
import com.woka.databinding.FragmentCartBinding
import com.woka.networking.ApiResult
import com.woka.shop.adapters.CartAdapter
import com.woka.shop.viewmodels.CartViewModel
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 CartFragment: Fragment() {
private lateinit var binding: FragmentCartBinding
private lateinit var adapter: CartAdapter
private lateinit var viewModel: CartViewModel
private lateinit var progressDialog: ProgressView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentCartBinding.inflate(inflater, container, false)
viewModel = ViewModelProvider(requireActivity())[CartViewModel::class.java]
adapter = CartAdapter()
progressDialog = ProgressView(requireContext())
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
clickEvents()
setObservers()
if (!viewModel.cartListLiveData.isInitialized || viewModel.cartListLiveData.value !is ApiResult.Success){
viewModel.loadCartList()
}
}
private fun initViews() {
binding.apply {
viewModel.onToolBarTitleChange?.invoke(getString(R.string.shop))
rvCart.adapter = adapter
}
}
private fun clickEvents() {
binding.apply {
adapter.onCartItemDeleteListener = {id, position ->
lifecycleScope.launch {
progressDialog.show(getString(R.string.removing_item))
when (val response = viewModel.removeItem(id)){
is ApiResult.Error -> {
progressDialog.hide()
toast(response.errorMessage)
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
progressDialog.hide()
toast(response.message)
try {
adapter.notifyItemRemoved(position)
} finally {
response.data?.let {cartValue ->
if (cartValue > 0){
val finalAmount = "$cartValue"
totalAmount.text = finalAmount
}else{
rvCart.hide()
progressView.hide()
checkoutView.hide()
noDataView.show()
}
}
}
}
}
}
}
binding.checkout.setOnClickListener {
findNavController().navigate(R.id.action_cartFragment_to_orderSummaryFragment)
}
}
}
private fun setObservers(){
viewModel.cartListLiveData.observe(viewLifecycleOwner){
binding.apply {
when (it){
is ApiResult.Error -> {
rvCart.hide()
progressView.hide()
checkoutView.hide()
noDataView.show()
}
is ApiResult.Loading -> {
rvCart.hide()
progressView.show()
checkoutView.hide()
noDataView.hide()
}
is ApiResult.Success -> {
it.data?.let {cartData ->
cartData.result?.let {cartList ->
if (cartList.isNotEmpty()){
adapter.submitList(cartList){
progressView.hide()
noDataView.hide()
rvCart.show()
val finalAmount = "${cartData.total_amount}"
totalAmount.text = finalAmount
checkoutView.show()
}
}else{
// cart is empty
rvCart.hide()
progressView.hide()
checkoutView.hide()
noDataView.show()
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,102 @@
package com.woka.shop.views.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.woka.R
import com.woka.databinding.FragmentOrderSummaryBinding
import com.woka.networking.ApiResult
import com.woka.shop.adapters.CartAdapter
import com.woka.shop.adapters.CouponAdapter
import com.woka.shop.viewmodels.CartViewModel
import com.woka.utils.hide
import com.woka.utils.show
import kotlinx.coroutines.launch
class OrderSummaryFragment : Fragment() {
private lateinit var binding: FragmentOrderSummaryBinding
private lateinit var viewModel: CartViewModel
private lateinit var adapter: CartAdapter
private lateinit var couponAdapter: CouponAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentOrderSummaryBinding.inflate(inflater, container, false)
viewModel = ViewModelProvider(requireActivity())[CartViewModel::class.java]
adapter = CartAdapter(false)
couponAdapter = CouponAdapter()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
setObservers()
loadCoupons()
}
private fun initViews(){
binding.apply {
viewModel.onToolBarTitleChange?.invoke(getString(R.string.payment_options))
rvCart.adapter = adapter
rvCoupons.adapter = couponAdapter
}
}
private fun loadCoupons(){
lifecycleScope.launch {
when (val response = viewModel.loadCoupons()){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
response.data?.result?.filterNotNull()?.let {
couponAdapter.submitList(it)
}
}
}
}
}
private fun setObservers() {
viewModel.cartListLiveData.observe(viewLifecycleOwner){
binding.apply {
when (it){
is ApiResult.Error -> {
rvCart.hide()
}
is ApiResult.Loading -> {
rvCart.hide()
}
is ApiResult.Success -> {
it.data?.let {cartData ->
cartData.result?.let {cartList ->
if (cartList.isNotEmpty()){
mainLayout.show()
adapter.submitList(cartList){
rvCart.show()
}
}else{
// cart is empty
rvCart.hide()
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="2048"
android:viewportHeight="2048">
<path
android:fillColor="#FF000000"
android:pathData="m338,522h115l1320,1v15l-4,70 -3,49 -6,90 -6,104 -4,63 -5,69 -7,122 -4,64 -6,87 -6,105 -4,63 -5,70 -7,123 -4,63 -6,88 -5,76 -4,29 -5,20 -6,17 -8,16 -9,15 -10,14 -12,13 -8,8 -14,11 -14,9 -21,11 -16,6 -26,6 -28,3 -18,1 -225,1h-656l-55,-1 -37,-2 -21,-3 -17,-5 -21,-9 -18,-10 -16,-12 -10,-9 -14,-14 -11,-14 -9,-14 -7,-12 -8,-19 -6,-20 -3,-16 -3,-26 -5,-70 -7,-107 -7,-113 -11,-180 -6,-90 -7,-115 -8,-126 -11,-179 -6,-91 -5,-83 -6,-98 -3,-51v-8l2,-4z" />
<path
android:fillColor="#FF000000"
android:pathData="m798,24h406l48,1 21,2 13,4 13,8 13,11 10,12 10,16 10,18 16,31 8,16 3,3 3,1 24,1 74,1h366l14,1 10,3 11,6 12,11 7,10 5,10 3,12 1,12v120l-2,17 -6,14 -8,11 -8,9 -10,7 -8,3 -15,2 -20,1 -69,1h-999l-548,-1 -15,-2 -8,-3 -10,-8 -11,-11 -8,-12 -5,-15 -2,-16v-119l2,-15 7,-16 7,-10 7,-7 15,-10 11,-4 101,-1 328,-1 51,-1 5,-3 4,-5 7,-16 18,-38 8,-13 7,-11 11,-13 15,-12 14,-7 15,-4z" />
</vector>

View File

@@ -0,0 +1,26 @@
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/grad_my_list"
tools:context=".shop.views.CartActivity">
<include android:id="@+id/toolbar"
layout="@layout/layout_toolbar"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph_shop_cart"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_marginHorizontal="15dp"
android:layout_marginVertical="5dp"
app:cardCornerRadius="5dp"
app:cardBackgroundColor="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="1"
android:layout_margin="10dp"
android:orientation="horizontal">
<com.woka.utils.AdiImageView
android:id="@+id/image"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="0.25"
app:imageCornerRadius="5dp"
/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.75"
android:layout_marginStart="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/color_primary"
android:fontFamily="@font/exo_2_bold"
android:textSize="@dimen/_11ssp"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="@+id/quantity_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/quantity"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_10ssp"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
android:visibility="gone"
/>
<TextView
android:id="@+id/quantity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_10ssp"
android:layout_marginStart="5dp"
app:layout_constraintTop_toTopOf="@id/quantity_tag"
app:layout_constraintStart_toEndOf="@id/quantity_tag"
app:layout_constraintBottom_toBottomOf="@id/quantity_tag"
/>
<TextView
android:id="@+id/status_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/product_status"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_10ssp"
app:layout_constraintTop_toBottomOf="@id/quantity_tag"
app:layout_constraintStart_toStartOf="parent"
android:visibility="gone"
/>
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_10ssp"
android:layout_marginStart="5dp"
app:layout_constraintTop_toTopOf="@id/status_tag"
app:layout_constraintStart_toEndOf="@id/status_tag"
app:layout_constraintBottom_toBottomOf="@id/status_tag"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/status_tag"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_11ssp"
android:layout_centerVertical="true"
/>
<ImageView
android:id="@+id/delete"
android:visibility="gone"
android:layout_width="20dp"
android:layout_height="20dp"
android:contentDescription="@string/image"
android:src="@drawable/ic_delete"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:paddingHorizontal="2dp"
app:tint="@color/color_primary" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_marginVertical="5dp"
android:layout_marginHorizontal="15dp"
app:cardCornerRadius="1dp"
app:cardBackgroundColor="@color/color_primary">
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="This is the text"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:layout_marginStart="10dp"
android:paddingHorizontal="15dp"
android:paddingVertical="8dp"
android:background="@color/white"
/>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
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="@drawable/grad_my_list"
tools:context=".shop.views.fragments.CartFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_cart"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="0dp"
tools:listitem="@layout/cart_item_view_holder"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/checkout_view"
/>
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/progress_view"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:shimmer_auto_start="true"
app:shimmer_duration="1500"
app:shimmer_highlight_alpha="0.5"
>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/cart_item_view_holder"/>
<include layout="@layout/cart_item_view_holder"/>
<include layout="@layout/cart_item_view_holder"/>
<include layout="@layout/cart_item_view_holder"/>
<include layout="@layout/cart_item_view_holder"/>
</LinearLayout>
</ScrollView>
</com.facebook.shimmer.ShimmerFrameLayout>
<LinearLayout
android:id="@+id/no_data_view"
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/no_items_added_to_cart"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
android:layout_marginTop="10dp"
/>
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/checkout_view"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/white"
android:layout_marginHorizontal="15dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
app:layout_constraintBottom_toTopOf="@id/total_tag"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="@+id/total_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/total"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/checkout"
android:layout_marginStart="15dp"
android:layout_marginBottom="10dp"
/>
<TextView
android:id="@+id/total_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="$ 299"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
app:layout_constraintTop_toTopOf="@id/total_tag"
app:layout_constraintBottom_toBottomOf="@id/total_tag"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="15dp"
/>
<Button
android:id="@+id/checkout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/checkout"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_10ssp"
android:layout_marginHorizontal="15dp"
android:layout_marginBottom="15dp"
android:background="@drawable/round_bg_25"
android:backgroundTint="@color/color_primary"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
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="@drawable/grad_my_list"
tools:context=".shop.views.fragments.OrderSummaryFragment">
<LinearLayout
android:id="@+id/main_layout"
android:visibility="gone"
tools:visibility="visible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/order_summary"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
android:layout_marginHorizontal="15dp"
android:layout_marginBottom="5dp"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_cart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/cart_item_view_holder"
tools:itemCount="2"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="1"
android:layout_marginTop="10dp"
android:layout_marginHorizontal="15dp"
android:orientation="horizontal">
<EditText
android:id="@+id/coupon_code"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="0.75"
android:hint="@string/enter_a_coupon_code"
android:textColorHint="@android:color/darker_gray"
android:fontFamily="@font/exo_2"
android:textColor="@color/black"
android:textSize="@dimen/_12ssp"
android:paddingHorizontal="15dp"
android:background="@drawable/round_25"
android:singleLine="true"
android:autofillHints="@null"
android:inputType="text"
/>
<Button
android:id="@+id/apply_btn"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="0.25"
android:text="@string/apply"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_12ssp"
android:textAllCaps="false"
android:background="@drawable/round_bg_25"
android:backgroundTint="@color/color_primary"
android:layout_marginStart="10dp"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/white"
android:layout_marginTop="15dp"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_offers_coupons"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:layout_centerVertical="true"
/>
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:contentDescription="@string/image"
android:src="@drawable/ic_half_arrow_right"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
app:tint="@color/color_primary" />
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_coupons"
android:visibility="visible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
tools:itemCount="3"
tools:listitem="@layout/coupon_view_holderr"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -0,0 +1,22 @@
<?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_carrt"
app:startDestination="@id/cartFragment">
<fragment
android:id="@+id/cartFragment"
android:name="com.woka.shop.views.fragments.CartFragment"
android:label="fragment_cart"
tools:layout="@layout/fragment_cart" >
<action
android:id="@+id/action_cartFragment_to_orderSummaryFragment"
app:destination="@id/orderSummaryFragment" />
</fragment>
<fragment
android:id="@+id/orderSummaryFragment"
android:name="com.woka.shop.views.fragments.OrderSummaryFragment"
android:label="fragment_order_summary"
tools:layout="@layout/fragment_order_summary" />
</navigation>

View File

@@ -223,4 +223,15 @@
<string name="continue_to_woka">Continue to WOKA</string>
<string name="to_continue_please_sign_in_for_more_adventures_and_features">To continue, please sign in for more adventures and features...</string>
<string name="free_sign_in">Free Sign In</string>
<string name="product_status">Product Status:</string>
<string name="quantity">Quantity:</string>
<string name="checkout">CHECKOUT</string>
<string name="total">Total:</string>
<string name="no_items_added_to_cart">No items added to cart</string>
<string name="removing_item">Removing item</string>
<string name="payment_options">PAYMENT OPTIONS</string>
<string name="order_summary">Order Summary</string>
<string name="enter_a_coupon_code">Enter a coupon code</string>
<string name="apply">Apply</string>
<string name="all_offers_coupons"><![CDATA[All Offers & Coupons]]></string>
</resources>