Completed address Adding Uis and api integration.

Worked on getting callbacks for url change in PaymentActivity.

Integrated sub-category api and its ui with tablyout

Integrated product listing api and cached data in ShopViewModel for every super-category, category and sub-category.

Created ProductFragment to showcase product details: Added viewpager and its adapter to show all images of a product in a sliding manner.
This commit is contained in:
2024-07-29 21:06:32 +05:30
parent 78c309e046
commit 86f0aa47dc
36 changed files with 1670 additions and 121 deletions

View File

@@ -52,6 +52,8 @@ ext.jwPlayerVersion = '4.17.0'
ext.exoplayerVersion = '1.3.1'
dependencies {
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.32'
implementation "com.facebook.shimmer:shimmer:0.5.0"
implementation "com.airbnb.android:lottie:6.4.0"

View File

@@ -20,7 +20,8 @@
<activity
android:name=".shop.views.AddressActivity"
android:exported="false"
android:screenOrientation="portrait" />
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan" />
<activity
android:name=".shop.views.PaymentActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode|fontScale|fontWeightAdjustment|screenLayout"

View File

@@ -76,7 +76,7 @@ object RetrofitHelper {
}
// api response handler
fun <T>handleApiResponse(response: Response<ApiResponse<T>>): ApiResult<T> {
private fun <T>handleApiResponse(response: Response<ApiResponse<T>>): ApiResult<T> {
if (response.isSuccessful) {
val body = response.body() ?: return ApiResult.Error(errorMessage = "Empty Response")
return when (body.success) {

View File

@@ -96,7 +96,7 @@ class OnboardFragment : Fragment() {
private fun initViews() {
binding.apply {
viewPager.adapter = OnboardingAdapter()
TabLayoutMediator(tabLayout, viewPager){tab, position -> }.attach()
TabLayoutMediator(tabLayout, viewPager){_, _ -> }.attach()
}
}

View File

@@ -11,6 +11,7 @@ import com.woka.shop.models.couponlisting.CouponsResponse
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.subcategorylisting.SubCategoryResponse
import com.woka.shop.models.superlisting.SuperCategoryResponse
import okhttp3.FormBody
@@ -31,6 +32,9 @@ interface ShopApiService {
@POST("sub_category_listing")
suspend fun subcategoryListing(@Body formBody: FormBody): Response<ApiResponse<SubCategoryResponse>>
@POST("v2/shop_product_listing")
suspend fun productListing(@Body formBody: FormBody): Response<ApiResponse<ProductListingResponse>>
// cart
@GET("cart_listing")
suspend fun cartListing(): Response<ApiResponse<CartResponse>>

View File

@@ -13,6 +13,7 @@ import com.woka.shop.models.couponlisting.CouponsResponse
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.subcategorylisting.SubCategoryResponse
import com.woka.shop.models.superlisting.SuperCategoryResponse
import okhttp3.FormBody
@@ -51,6 +52,25 @@ object ShopRepository {
}
}
suspend fun productListing(categoryId: String, subcategoryId: String?, pageNo: String, count: String): ApiResult<ProductListingResponse> {
return handleApiCall {
val bodyBuilder = FormBody.Builder()
bodyBuilder.add("category_id", categoryId)
subcategoryId?.let {
bodyBuilder.add("sub_category_id", it)
}
bodyBuilder.add("api_version", "v2")
bodyBuilder.add("start", pageNo)
bodyBuilder.add("limit", count)
apiService.productListing(
bodyBuilder.build()
)
}
}
// cart listing with loose caching
private var cartResponse: CartResponse? = null

View File

@@ -0,0 +1,32 @@
package com.woka.shop.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
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>(){
inner class ImageViewHolder(val binding: ProductImageViewHolderBinding): ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
return ImageViewHolder(
ProductImageViewHolderBinding.inflate(
LayoutInflater.from(parent.context),
parent, false
)
)
}
override fun getItemCount(): Int = imageList.size
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
Glide.with(holder.binding.root.context)
.load(imageList[holder.absoluteAdapterPosition])
.into(holder.binding.root)
}
}

View File

@@ -0,0 +1,54 @@
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.ProductViewHolderBinding
import com.woka.shop.models.productlisting.ShopProduct
import java.util.concurrent.Executors
class ShopProductAdapter: ListAdapter<ShopProduct, ShopProductAdapter.ProductViewHolder>(ASYNC_DIFF_UTIL) {
companion object{
private val DIFF_UTIL = object :DiffUtil.ItemCallback<ShopProduct>(){
override fun areItemsTheSame(oldItem: ShopProduct, newItem: ShopProduct): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: ShopProduct, newItem: ShopProduct): Boolean = oldItem == newItem
}
private val ASYNC_DIFF_UTIL = AsyncDifferConfig.Builder(DIFF_UTIL)
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
.build()
}
inner class ProductViewHolder(val binding: ProductViewHolderBinding): ViewHolder(binding.root)
var onProductClicked: ((ShopProduct) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
return ProductViewHolder(
ProductViewHolderBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
val product = getItem(holder.absoluteAdapterPosition)
with(holder.binding){
if (product.shop_image?.isNotEmpty() == true)
image.loadImage(product.shop_image.first())
title.text = product.product_name
price.text = "${product.product_price}"
root.setOnClickListener {
onProductClicked?.invoke(product)
}
}
}
}

View File

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

View File

@@ -0,0 +1,10 @@
package com.woka.shop.models.productlisting
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,18 @@
package com.woka.shop.models.productlisting
data class ShopProduct(
val added_to_cart: Boolean?,
val category_master_id: Int?,
val id: Int?,
val product_name: String?,
val product_price: String?,
val product_thumbnail: String?,
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

@@ -7,8 +7,10 @@ import androidx.lifecycle.viewModelScope
import com.woka.networking.ApiResult
import com.woka.shop.ShopRepository
import com.woka.shop.models.categorylisting.CategoryData
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 kotlinx.coroutines.launch
class ShopViewModel: ViewModel() {
@@ -72,11 +74,12 @@ class ShopViewModel: ViewModel() {
val subcategoryLiveData: LiveData<ApiResult<MutableList<SubCategoryData>>>
get() = _subcategoryLiveData
private val subcategoryDataMap = HashMap<String, MutableList<SubCategoryData>>()
val subcategoryDataMap = HashMap<String, MutableList<SubCategoryData>>()
fun loadSubCategories(categoryId: String){
if (subcategoryDataMap.containsKey(categoryId)){
_subcategoryLiveData.postValue(ApiResult.Success(subcategoryDataMap[categoryId]))
fun loadSubCategories(superCategoryId: String, categoryId: String){
val key = "${superCategoryId}_$categoryId"
if (subcategoryDataMap.containsKey(key)){
_subcategoryLiveData.postValue(ApiResult.Success(subcategoryDataMap[key]))
return
}
@@ -87,7 +90,9 @@ class ShopViewModel: ViewModel() {
is ApiResult.Loading -> {}
is ApiResult.Success -> {
response.data?.result?.filterNotNull()?.toMutableList()?.let {
subcategoryDataMap[categoryId] = it
// adding a all tag at start
it.add(0, SubCategoryData(null, null, "All", null))
subcategoryDataMap[key] = it
_subcategoryLiveData.postValue(ApiResult.Success(it))
}
}
@@ -98,4 +103,77 @@ class ShopViewModel: ViewModel() {
fun clearSubCategoryLiveData(){
_subcategoryLiveData.postValue(ApiResult.Loading())
}
// product listing
private val _productListingLiveData = MutableLiveData<ApiResult<MutableList<ShopProduct>>>()
val productListingLiveData: LiveData<ApiResult<MutableList<ShopProduct>>>
get() = _productListingLiveData
var productPagingData = HashMap<String, PagingData>()
// product data for every super-category, category and sub-category
private var productDataMap = HashMap<String, MutableList<ShopProduct>>()
fun loadProducts(superCategoryId: String, categoryId: String, subCategoryId: String?) {
val key = "${superCategoryId}_${categoryId}_$subCategoryId"
if (productDataMap.containsKey(key) && productDataMap[key]?.isNotEmpty() == true) {
_productListingLiveData.postValue(ApiResult.Success(productDataMap[key]))
} else {
loadMoreEpisodes(superCategoryId, categoryId, subCategoryId)
}
}
fun loadMoreEpisodes(superCategoryId: String, categoryId: String, subCategoryId: String?) {
viewModelScope.launch {
_productListingLiveData.postValue(ApiResult.Loading())
val key = "${superCategoryId}_${categoryId}_$subCategoryId"
val pagingData = if (productPagingData.containsKey(key)) {
productPagingData[key]!!
} else {
val pagingData = PagingData()
productPagingData[key] = pagingData
pagingData
}
when (val response = repository.productListing(
categoryId,
subCategoryId,
"${pagingData.nextPageToLoad}",
"${pagingData.quantityPerPage}"
)) {
is ApiResult.Error -> {
_productListingLiveData.postValue(
ApiResult.Error(
response.errorMessage,
response.error
)
)
}
is ApiResult.Loading -> {
_productListingLiveData.postValue(ApiResult.Loading())
}
is ApiResult.Success -> {
response.data?.let { data ->
data.result?.filterNotNull()?.let { newList ->
val currentList = productDataMap.getOrDefault(key, ArrayList())
currentList.addAll(newList)
productDataMap[key] = currentList
pagingData.lastPage = productDataMap[key]?.size == data.total_records
pagingData.nextPageToLoad++
_productListingLiveData.postValue(ApiResult.Success(productDataMap[key]))
}
}
}
}
}
}
fun clearProductListingLiveData() {
_productListingLiveData.postValue(ApiResult.Loading())
}
}

View File

@@ -11,6 +11,11 @@ import com.woka.utils.WokaBaseActivity
class AddressActivity : WokaBaseActivity() {
companion object {
const val EXTRA_PRODUCT_IDS = "extra_product_ids"
const val EXTRA_COUPON_CODE = "extra_coupon_code"
}
private lateinit var binding: ActivityAddressBinding
override fun onCreate(savedInstanceState: Bundle?) {
@@ -24,10 +29,32 @@ class AddressActivity : WokaBaseActivity() {
insets
}
binding.toolbar.title.text = getString(R.string.address_details)
initViews()
clickEvents()
supportFragmentManager.beginTransaction()
.add(R.id.fcv_address, PinCodeFragment.newInstance())
.add(
R.id.fcv_address,
PinCodeFragment.newInstance(
intent.getIntegerArrayListExtra(EXTRA_PRODUCT_IDS),
intent.getStringExtra(EXTRA_COUPON_CODE)
)
)
.commit()
}
private fun initViews() {
binding.apply {
toolbar.title.text = getString(R.string.address_details)
}
}
private fun clickEvents() {
binding.apply {
toolbar.backBtn.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
}
}
}

View File

@@ -5,15 +5,19 @@ import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import com.woka.R
import com.woka.databinding.ActivityPaymentBinding
import com.woka.utils.WokaBaseActivity
import com.woka.utils.lightStatusBar
import com.woka.utils.toast
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class PaymentActivity : WokaBaseActivity() {
@@ -69,6 +73,22 @@ class PaymentActivity : WokaBaseActivity() {
binding.webView.setWebViewClient(WebViewClient())
binding.webView.webViewClient = object : WebViewClient(){
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
if (url?.startsWith("https://secure.ccavenue.com/") == true){
if (url.contains("cancelTransaction")){
lifecycleScope.launch {
delay(2000)
runOnUiThread {
finish()
}
}
}
}
}
}
binding.webView.loadUrl(it)
}
}

View File

@@ -0,0 +1,187 @@
package com.woka.shop.views.fragments.address
import android.content.Intent
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.FragmentAddAddressBinding
import com.woka.networking.ApiResult
import com.woka.shop.models.addaddress.AddAddressRequestData
import com.woka.shop.viewmodels.AddressViewModel
import com.woka.shop.viewmodels.CartViewModel
import com.woka.shop.views.PaymentActivity
import com.woka.utils.ProgressView
import com.woka.utils.setAsPhoneInput
import com.woka.utils.toast
import kotlinx.coroutines.launch
class AddAddressFragment private constructor(
private val pinCode: String,
private val productIds: ArrayList<Int>?,
private val couponCode: String?
) : Fragment() {
private lateinit var binding: FragmentAddAddressBinding
private lateinit var viewModel: AddressViewModel
private lateinit var cartViewModel: CartViewModel
private lateinit var progressView: ProgressView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentAddAddressBinding.inflate(inflater, container, false)
viewModel = ViewModelProvider(requireActivity())[AddressViewModel::class.java]
cartViewModel = ViewModelProvider(requireActivity())[CartViewModel::class.java]
progressView = ProgressView(requireContext(), getString(R.string.please_wait))
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
clickEvents()
setObservers()
}
private fun clickEvents() {
binding.apply {
submit.setOnClickListener {
if (allOkay()){
addAddress()
}
}
}
}
private fun addAddress(){
lifecycleScope.launch {
binding.apply {
progressView.show()
val requestData = AddAddressRequestData(
addressName.text.toString(),
"${addressLine1.text}, ${addressLine2.text}",
city.text.toString(),
state.text.toString(),
pinCode,
phone.text.toString()
)
when (val response = viewModel.addAddress(requestData)){
is ApiResult.Error -> {
progressView.hide()
toast(response.errorMessage)
}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
progressView.hide()
if (productIds != null){
cartViewModel.createOrder(
productIds,
couponCode,
"${response.data?.id}"
)
}
}
}
}
}
}
private fun allOkay(): Boolean {
var allOkay = true
binding.apply {
if (addressName.text.trim().isEmpty()){
addressName.error = getString(R.string.please_enter_name)
allOkay = false
}
if (addressLine1.text.trim().length < 15){
addressLine1.error = getString(R.string.at_least_15_characters)
allOkay = false
}
if (addressLine2.text.length < 15){
addressLine2.error = getString(R.string.at_least_15_characters)
allOkay = false
}
if (city.text.trim().length < 2){
city.error = getString(R.string.at_least_2_characters)
allOkay = false
}
if (state.text.trim().length < 2){
state.error = getString(R.string.at_least_2_characters)
allOkay = false
}
if (country.text.trim().length < 2){
country.error = getString(R.string.at_least_2_characters)
allOkay = false
}
if (phone.text.trim().length < 10){
phone.error = getString(R.string.enter_10_digit_phone)
allOkay = false
}
}
return allOkay
}
private fun initViews() {
binding.apply {
phone.setAsPhoneInput()
pincode.setText(pinCode)
}
}
private fun setObservers(){
cartViewModel.createOrderLiveData.observe(viewLifecycleOwner){
when(it){
is ApiResult.Error -> {
progressView.hide()
toast(it.errorMessage)
}
is ApiResult.Loading -> {
progressView.show()
}
is ApiResult.Success -> {
progressView.hide()
it.data?.url?.let {paymentUrl ->
activity?.let {activity ->
activity.startActivity(
Intent(activity, PaymentActivity::class.java).apply {
putExtra(PaymentActivity.EXTRA_PAYMENT_LINK, paymentUrl)
}
)
}
}
}
null -> {}
}
}
}
companion object {
@JvmStatic
fun newInstance(pinCode: String,
productIds: ArrayList<Int>?,
couponCode: String?) = AddAddressFragment(pinCode, productIds, couponCode)
}
}

View File

@@ -1,11 +1,11 @@
package com.woka.shop.views.fragments.address
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.woka.R
@@ -13,10 +13,13 @@ import com.woka.databinding.FragmentPinCodeBinding
import com.woka.networking.ApiResult
import com.woka.shop.viewmodels.AddressViewModel
import com.woka.utils.ProgressView
import com.woka.utils.toast
import kotlinx.coroutines.launch
import java.util.ArrayList
class PinCodeFragment private constructor(): Fragment() {
class PinCodeFragment private constructor(
private val productIds: ArrayList<Int>?,
private val couponCode: String?
): Fragment() {
private lateinit var binding: FragmentPinCodeBinding
private lateinit var progressView: ProgressView
@@ -47,11 +50,15 @@ class PinCodeFragment private constructor(): Fragment() {
binding.apply {
btn.setOnClickListener {
if (selectedPinCode.length == 6){
toast(selectedPinCode)
parentFragmentManager.beginTransaction()
.add(R.id.fcv_address, AddAddressFragment.newInstance(selectedPinCode, productIds, couponCode))
.addToBackStack(null)
.commit()
return@setOnClickListener
}
if (pincode.text.length < 6){
if (pincode.text.length != 6){
pincode.error = getString(R.string.enter_a_valid_pincode)
return@setOnClickListener
}
@@ -73,7 +80,6 @@ class PinCodeFragment private constructor(): Fragment() {
selectedPinCode = pinCode
btn.text = getString(R.string.proceed)
}
}
}
@@ -94,6 +100,8 @@ class PinCodeFragment private constructor(): Fragment() {
companion object {
@JvmStatic
fun newInstance() = PinCodeFragment()
fun newInstance(
productIds: ArrayList<Int>?,
couponCode: String?) = PinCodeFragment(productIds, couponCode)
}
}

View File

@@ -14,6 +14,8 @@ import com.woka.shop.adapters.ParentAddressAdapter
import com.woka.shop.models.cartlisting.CartResponse
import com.woka.shop.viewmodels.CartViewModel
import com.woka.shop.views.AddressActivity
import com.woka.shop.views.AddressActivity.Companion.EXTRA_COUPON_CODE
import com.woka.shop.views.AddressActivity.Companion.EXTRA_PRODUCT_IDS
import com.woka.shop.views.PaymentActivity
import com.woka.shop.views.PaymentActivity.Companion.EXTRA_PAYMENT_LINK
import com.woka.utils.ProgressView
@@ -21,6 +23,7 @@ import com.woka.utils.hide
import com.woka.utils.setVisibility
import com.woka.utils.show
import com.woka.utils.toast
import java.util.ArrayList
class ParentAddressFragment : Fragment() {
@@ -67,17 +70,7 @@ class ParentAddressFragment : Fragment() {
private fun clickEvents(){
binding.apply {
selectAddress.setOnClickListener {
val productIds = ArrayList<Int>()
if (viewModel.cartListLiveData.value is ApiResult.Success){
(viewModel.cartListLiveData.value as ApiResult.Success<CartResponse>).data?.result?.let {
for (cartItem in it){
cartItem?.id?.let {id ->
productIds.add(id)
}
}
}
}
val productIds = getProductIds()
if (productIds.isEmpty()){
toast(getString(R.string.no_products_added_to_cart))
@@ -94,12 +87,33 @@ class ParentAddressFragment : Fragment() {
addNewAddress.setOnClickListener {
activity?.let {
it.startActivity(Intent(it, AddressActivity::class.java))
it.startActivity(
Intent(it, AddressActivity::class.java).apply {
putIntegerArrayListExtra(EXTRA_PRODUCT_IDS, getProductIds())
putExtra(EXTRA_COUPON_CODE, viewModel.appliedCoupon)
}
)
}
}
}
}
private fun getProductIds(): ArrayList<Int> {
val productIds = ArrayList<Int>()
if (viewModel.cartListLiveData.value is ApiResult.Success){
(viewModel.cartListLiveData.value as ApiResult.Success<CartResponse>).data?.result?.let {
for (cartItem in it){
cartItem?.id?.let {id ->
productIds.add(id)
}
}
}
}
return productIds
}
private fun setObservers() {
adapter.addressSelectListener = { addressId ->
binding.selectAddress.show()

View File

@@ -0,0 +1,66 @@
package com.woka.shop.views.fragments.shop
import android.os.Bundle
import android.text.Html
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.tabs.TabLayoutMediator
import com.woka.R
import com.woka.databinding.FragmentProductBinding
import com.woka.shop.adapters.ProductImagesAdapter
import com.woka.shop.models.productlisting.ShopProduct
class ProductFragment private constructor(
private val shopProduct: ShopProduct,
private val category: String
): Fragment() {
companion object {
@JvmStatic
fun newInstance(shopProduct: ShopProduct, categoryName: String) = ProductFragment(shopProduct, categoryName)
}
private lateinit var binding: FragmentProductBinding
private lateinit var imageAdapter: ProductImagesAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentProductBinding.inflate(inflater, container, false)
val imageList = ArrayList<String>()
shopProduct.shop_image?.filterNotNull()?.let {
imageList.addAll(it)
}
imageAdapter = ProductImagesAdapter(imageList)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
}
private fun initViews() {
binding.apply {
vpImages.adapter = imageAdapter
TabLayoutMediator(tabLayout, vpImages){_, _ -> }.attach()
title.text = shopProduct.product_name
categoryName.text = category
skuId.text = shopProduct.sku_id
price.text = shopProduct.product_price
description.text = Html.fromHtml(
shopProduct.shop_master_detail?.description_english,
Html.FROM_HTML_MODE_LEGACY
)
}
}
}

View File

@@ -62,7 +62,7 @@ class ShopFragment2 private constructor(private val superCategoryId: String): Fr
adapter.onCategoryClickListener = {
parentFragmentManager.beginTransaction()
.replace(R.id.fcv_shop, ShopFragment3.newInstance("${it.id}"))
.replace(R.id.fcv_shop, ShopFragment3.newInstance(superCategoryId, "${it.id}", "${it.title}"))
.addToBackStack(null)
.commitAllowingStateLoss()
}

View File

@@ -6,19 +6,33 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.tabs.TabLayout
import com.woka.R
import com.woka.databinding.FragmentShop3Binding
import com.woka.networking.ApiResult
import com.woka.shop.adapters.CategoryAdapter
import com.woka.shop.models.BaseCategory
import com.woka.shop.adapters.ShopProductAdapter
import com.woka.shop.viewmodels.ShopViewModel
import com.woka.utils.hide
import com.woka.utils.setVisibility
import com.woka.utils.show
class ShopFragment3 private constructor(private val categoryId: String): Fragment() {
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)
}
private lateinit var binding: FragmentShop3Binding
private lateinit var viewModel: ShopViewModel
private lateinit var adapter: CategoryAdapter
private lateinit var productAdapter: ShopProductAdapter
private var loadMoreProducts = false
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -26,7 +40,7 @@ class ShopFragment3 private constructor(private val categoryId: String): Fragmen
): View {
binding = FragmentShop3Binding.inflate(inflater, container, false)
viewModel = ViewModelProvider(requireActivity())[ShopViewModel::class.java]
adapter = CategoryAdapter()
productAdapter = ShopProductAdapter()
return binding.root
}
@@ -39,28 +53,53 @@ class ShopFragment3 private constructor(private val categoryId: String): Fragmen
setObservers()
viewModel.loadSubCategories(categoryId)
viewModel.loadSubCategories(superCategoryId, categoryId)
}
override fun onDestroyView() {
super.onDestroyView()
viewModel.clearSubCategoryLiveData()
viewModel.clearProductListingLiveData()
}
private fun initViews() {
binding.apply {
rvCategory.adapter = adapter
categoryTabs.addOnTabSelectedListener(this@ShopFragment3)
rvProducts.adapter = productAdapter
rvProducts.itemAnimator = null
}
}
private fun clickEvents() {
binding.apply {
retryButton.setOnClickListener {
viewModel.loadSubCategories(categoryId)
viewModel.loadSubCategories(superCategoryId, categoryId)
}
adapter.onCategoryClickListener = {
prodLoadMoreBtn.setOnClickListener {
loadMoreProducts = true
val subCategoryId = categoryTabs.getTabAt(categoryTabs.selectedTabPosition)?.tag?.toString()
viewModel.loadMoreEpisodes(superCategoryId, categoryId, subCategoryId)
}
productAdapter.onProductClicked = {
parentFragmentManager.beginTransaction()
.add(R.id.fcv_shop, ProductFragment.newInstance(it, categoryName))
.addToBackStack(null)
.commit()
}
}
}
private fun loadProducts() {
val selectedTabPos = binding.categoryTabs.selectedTabPosition
if (selectedTabPos >= 0) {
viewModel.subcategoryDataMap["${superCategoryId}_$categoryId"]?.let { subCategories ->
if (selectedTabPos < subCategories.size) {
loadMoreProducts = false
viewModel.loadProducts(superCategoryId, categoryId, subCategories[selectedTabPos].id?.toString())
}
}
}
}
@@ -70,31 +109,74 @@ class ShopFragment3 private constructor(private val categoryId: String): Fragmen
viewModel.subcategoryLiveData.observe(viewLifecycleOwner) {
when (it) {
is ApiResult.Error -> {
rvCategory.hide()
shimmer.hide()
categoryTabs.hide()
categoryShimmer.hide()
errorView.show()
}
is ApiResult.Loading -> {
rvCategory.hide()
shimmer.show()
categoryTabs.hide()
categoryShimmer.show()
errorView.hide()
}
is ApiResult.Success -> {
it.data?.let { categoryList ->
adapter.submitList(categoryList.map { category ->
BaseCategory(
category.id,
category.sub_category_thumbnail,
category.sub_category_name
categoryTabs.show()
categoryShimmer.hide()
errorView.hide()
for (category in categoryList) {
binding.categoryTabs.addTab(
binding.categoryTabs.newTab()
.setText("${category.sub_category_name}")
.setTag(category.id)
)
}
) {
rvCategory.show()
}
}
}
}
shimmer.hide()
errorView.hide()
viewModel.productListingLiveData.observe(viewLifecycleOwner){
when (it) {
is ApiResult.Error -> {
binding.productShimmer.hide()
if (loadMoreProducts) {
// load more
binding.prodLoadMoreBtn.text = getString(R.string.retry)
binding.prodLoadMoreBtn.show()
} else {
// first time load
binding.rvProducts.hide()
binding.productShimmer.hide()
binding.prodLoadMoreBtn.hide()
}
}
is ApiResult.Loading -> {
binding.productShimmer.show()
binding.prodLoadMoreBtn.hide()
binding.rvProducts.setVisibility(loadMoreProducts)
}
is ApiResult.Success -> {
it.data?.let { productList ->
binding.rvProducts.show()
binding.productShimmer.hide()
binding.prodLoadMoreBtn.text = getString(R.string.load_more)
val subCategoryId = categoryTabs.getTabAt(categoryTabs.selectedTabPosition)?.tag
binding.prodLoadMoreBtn.setVisibility(viewModel.productPagingData["${superCategoryId}_${categoryId}_$subCategoryId"]?.lastPage == false)
if (loadMoreProducts) {
// loaded more data
productAdapter.notifyItemRangeInserted(
productAdapter.currentList.size,
productList.size
)
} else {
// new category data load
productAdapter.submitList(productList)
}
}
}
@@ -103,8 +185,11 @@ class ShopFragment3 private constructor(private val categoryId: String): Fragmen
}
}
companion object {
@JvmStatic
fun newInstance(categoryId: String) = ShopFragment3(categoryId)
override fun onTabSelected(p0: TabLayout.Tab?) {
loadProducts()
}
override fun onTabUnselected(p0: TabLayout.Tab?) {}
override fun onTabReselected(p0: TabLayout.Tab?) {}
}

View File

@@ -7,14 +7,19 @@ import android.content.Context
import android.content.res.Configuration
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.text.InputFilter
import android.text.Spanned
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.Window
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.Toast
import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment
import com.google.i18n.phonenumbers.PhoneNumberUtil
import com.google.i18n.phonenumbers.Phonenumber
import com.woka.WokaApp.Companion.userPrefs
import java.util.Locale
import kotlin.math.ceil
@@ -100,4 +105,37 @@ fun Context.isNetworkConnected(): Boolean {
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
}
fun EditText.setAsPhoneInput(){
// phone number formatting
// to deal with input pasting in the edit text
val phoneFilter = InputFilter { charSequence: CharSequence, _: Int, _: Int, _: Spanned?, _: Int, _: Int ->
var phoneNumberStr = charSequence.toString()
try {
val phoneNumberUtil: PhoneNumberUtil = PhoneNumberUtil.getInstance()
val phoneNumber: Phonenumber.PhoneNumber = phoneNumberUtil.parse(charSequence, "US")
phoneNumberStr = java.lang.String.valueOf(phoneNumber.nationalNumber)
} catch (e: Exception) {
// nothing
}
if (phoneNumberStr.length > 10) {
// pasted number length is greater than 10
return@InputFilter phoneNumberStr.substring(0, 10)
}
val totalPhoneNumber: String = text.toString() + phoneNumberStr
error = null
if (totalPhoneNumber.length > 10) {
// max length should be 10
return@InputFilter ""
} else {
return@InputFilter phoneNumberStr
}
}
filters = arrayOf(phoneFilter)
}

View File

@@ -0,0 +1,6 @@
package com.woka.utils
data class PagingData(
var nextPageToLoad: Int = 0, var quantityPerPage: Int = 6,
var lastPage: Boolean = false
)

View File

@@ -8,6 +8,7 @@ import com.jwplayer.pub.api.media.playlists.PlaylistItem
import com.woka.modules.categorymodels.CategoriesResponse
import com.woka.networking.ApiResult
import com.woka.players.models.VideoPlayList
import com.woka.utils.PagingData
import com.woka.webseries.WebSeriesRepository
import com.woka.webseries.models.ContinueEpisodeResponse
import com.woka.webseries.models.ShowData
@@ -20,11 +21,6 @@ class WebSeriesViewModel : ViewModel() {
private val repository = WebSeriesRepository
data class PagingData(
var nextPageToLoad: Int = 0, var quantityPerPage: Int = 6,
var lastPage: Boolean = false
)
// categories listing
private val _showCategoryLiveData = MutableLiveData<ApiResult<CategoriesResponse>>()

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="18" android:viewportWidth="18" android:width="24dp">
<path android:fillColor="#ffffff" android:pathData="M0.5,1.25C0.5,0.836 0.836,0.5 1.25,0.5H1.808C2.759,0.5 3.328,1.139 3.653,1.733C3.87,2.129 4.027,2.588 4.15,3.004C4.183,3.001 4.217,3 4.251,3H16.748C17.578,3 18.178,3.794 17.95,4.593L16.122,11.002C15.786,12.183 14.706,12.998 13.478,12.998H7.53C6.291,12.998 5.206,12.17 4.878,10.976L4.117,8.205L2.859,3.956L2.857,3.948C2.701,3.381 2.555,2.85 2.338,2.454C2.127,2.069 1.959,2 1.808,2H1.25C0.836,2 0.5,1.664 0.5,1.25ZM7,18C8.105,18 9,17.105 9,16C9,14.895 8.105,14 7,14C5.895,14 5,14.895 5,16C5,17.105 5.895,18 7,18ZM14,18C15.105,18 16,17.105 16,16C16,14.895 15.105,14 14,14C12.895,14 12,14.895 12,16C12,17.105 12.895,18 14,18Z"/>
</vector>

View File

@@ -2,14 +2,14 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:aapt="http://schemas.android.com/aapt">
<item android:state_selected="true">
<shape android:shape="rectangle">
<stroke android:width="10dp" android:color="@color/color_primary"/>
<stroke android:width="10dp" android:color="@android:color/transparent"/>
<corners android:radius="25dp"/>
<solid android:color="#5e1fc4"/>
</shape>
</item>
<item android:state_selected="false">
<shape android:shape="rectangle">
<stroke android:width="10dp" android:color="@color/color_primary"/>
<stroke android:width="10dp" android:color="@android:color/transparent"/>
<corners android:radius="25dp"/>
<solid android:color="@color/white"/>
</shape>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:aapt="http://schemas.android.com/aapt">
<item android:state_selected="true">
<shape android:shape="rectangle">
<stroke android:width="10dp" android:color="@android:color/transparent"/>
<corners android:radius="25dp"/>
<solid android:color="@color/color_primary"/>
</shape>
</item>
<item android:state_selected="false">
<shape android:shape="rectangle">
<stroke android:width="10dp" android:color="@android:color/transparent"/>
<corners android:radius="25dp"/>
<solid android:color="@color/white"/>
</shape>
</item>
</selector>

View File

@@ -0,0 +1,394 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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"
android:background="@drawable/grad_my_list"
tools:context=".shop.views.fragments.address.AddAddressFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_new_address"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
android:layout_marginTop="10dp"
android:layout_gravity="start"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/address_name_firstname_and_lastname"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
android:layout_marginTop="25dp"
/>
<EditText
android:id="@+id/address_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="15dp"
android:autofillHints="name"
android:background="@drawable/round_25_shadow"
android:digits="@string/alphabets_with_space"
android:elevation="5dp"
android:fontFamily="@font/exo_2"
android:hint="@string/enter_address_name"
android:inputType="textPersonName"
android:nextFocusDown="@id/address_line_1"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
android:singleLine="true"
android:textAlignment="center"
android:textColor="@color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/_12ssp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/address_line_1"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
/>
<EditText
android:id="@+id/address_line_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:fontFamily="@font/exo_2"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
android:hint="@string/enter_address_line_1"
android:textColorHint="@android:color/darker_gray"
android:autofillHints="postalAddress"
android:inputType="textPostalAddress"
android:digits="@string/alphanumeric_special_characters_with_space"
android:singleLine="true"
android:background="@drawable/round_25_shadow"
android:layout_marginTop="10dp"
android:layout_marginBottom="15dp"
android:elevation="5dp"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/address_line_2"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
/>
<EditText
android:id="@+id/address_line_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:fontFamily="@font/exo_2"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
android:hint="@string/enter_address_line_2"
android:textColorHint="@android:color/darker_gray"
android:autofillHints="postalAddress"
android:inputType="textPostalAddress"
android:digits="@string/alphanumeric_special_characters_with_space"
android:singleLine="true"
android:background="@drawable/round_25_shadow"
android:layout_marginTop="10dp"
android:layout_marginBottom="15dp"
android:elevation="5dp"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/city"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
/>
<EditText
android:id="@+id/city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:fontFamily="@font/exo_2"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
android:hint="@string/enter_city"
android:textColorHint="@android:color/darker_gray"
android:autofillHints="postalAddress"
android:inputType="textPostalAddress"
android:digits="@string/alphanumeric_special_characters_with_space"
android:singleLine="true"
android:background="@drawable/round_25_shadow"
android:layout_marginTop="10dp"
android:layout_marginBottom="15dp"
android:elevation="5dp"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/state"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
/>
<EditText
android:id="@+id/state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:fontFamily="@font/exo_2"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
android:hint="@string/enter_state"
android:textColorHint="@android:color/darker_gray"
android:autofillHints="postalAddress"
android:inputType="textPostalAddress"
android:digits="@string/alphanumeric_special_characters_with_space"
android:singleLine="true"
android:background="@drawable/round_25_shadow"
android:layout_marginTop="10dp"
android:layout_marginBottom="15dp"
android:elevation="5dp"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pincode"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
/>
<EditText
android:id="@+id/pincode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:textColor="@color/black"
android:fontFamily="@font/exo_2"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
android:hint="@string/enter_pincode"
android:textColorHint="@android:color/darker_gray"
android:autofillHints="postalAddress"
android:inputType="number"
android:singleLine="true"
android:background="@drawable/round_25_shadow"
android:layout_marginTop="10dp"
android:layout_marginBottom="15dp"
android:elevation="5dp"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
android:maxLength="6"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/country"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
/>
<EditText
android:id="@+id/country"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:fontFamily="@font/exo_2"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
android:hint="@string/enter_coutry"
android:textColorHint="@android:color/darker_gray"
android:autofillHints="postalAddress"
android:inputType="textPostalAddress"
android:digits="@string/alphanumeric_special_characters_with_space"
android:singleLine="true"
android:background="@drawable/round_25_shadow"
android:layout_marginTop="10dp"
android:layout_marginBottom="15dp"
android:elevation="5dp"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/phone_number"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
/>
<EditText
android:id="@+id/phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:fontFamily="@font/exo_2"
android:textSize="@dimen/_12ssp"
android:textAlignment="center"
android:hint="@string/enter_phone_number"
android:textColorHint="@android:color/darker_gray"
android:autofillHints="phone"
android:inputType="phone"
android:singleLine="true"
android:background="@drawable/round_25_shadow"
android:layout_marginTop="10dp"
android:layout_marginBottom="15dp"
android:elevation="5dp"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
android:maxLength="10"
/>
<Button
android:id="@+id/submit"
android:layout_width="match_parent"
android:layout_height="45dp"
android:text="@string/submit"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_10ssp"
android:layout_marginBottom="15dp"
android:layout_marginTop="25dp"
android:background="@drawable/round_bg_25"
android:backgroundTint="@color/color_primary"
/>
</LinearLayout>
</ScrollView>

View File

@@ -11,7 +11,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/round_25"
android:layout_margin="15dp"
android:layout_marginHorizontal="15dp"
android:layout_marginBottom="15dp"
>
<androidx.constraintlayout.widget.ConstraintLayout

View File

@@ -0,0 +1,208 @@
<?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="@color/orders_bg"
tools:context=".shop.views.fragments.shop.ProductFragment">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/g1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
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"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/g1"
/>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="15dp"
android:paddingVertical="10dp"
android:orientation="vertical">
<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"
/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp_images"
android:layout_width="match_parent"
android:layout_height="150dp"
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"
app:tabBackground="@drawable/onboard_indicator_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabPaddingEnd="10dp"
app:tabPaddingStart="10dp"
android:background="@android:color/transparent"
android:layout_marginHorizontal="15dp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/category_name"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
/>
<TextView
android:id="@+id/category_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="This is a random text for preview"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:layout_marginStart="5dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sku_id"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
/>
<TextView
android:id="@+id/sku_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="This is a random text for preview"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:layout_marginStart="5dp"
/>
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp">
<TextView
android:id="@+id/price"
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:text="$ 200"
android:fontFamily="@font/exo_2_bold"
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"
/>
<Button
android:id="@+id/addToCart"
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:paddingVertical="5dp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:textSize="@dimen/_12ssp"
android:layout_marginTop="15dp"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<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"
@@ -7,85 +7,232 @@
android:background="@color/orders_bg"
tools:context=".shop.views.fragments.shop.ShopFragment2">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_category"
android:visibility="gone"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="match_parent">
tools:listitem="@layout/category_viwe_holder"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/>
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:shimmer_auto_start="true"
app:shimmer_duration="1500"
app:shimmer_highlight_alpha="0.5">
<ScrollView
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/category_shimmer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
>
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:visibility="gone"
app:shimmer_auto_start="true"
app:shimmer_base_alpha="0.6"
app:shimmer_highlight_alpha="0.5">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
android:orientation="horizontal">
<include layout="@layout/category_viwe_holder"/>
<include layout="@layout/category_viwe_holder"/>
<include layout="@layout/category_viwe_holder"/>
<include layout="@layout/category_viwe_holder"/>
<View
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginStart="15dp"
android:background="@drawable/round_bg_25"
android:alpha="0.7"
android:backgroundTint="@android:color/darker_gray" />
<View
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginStart="15dp"
android:background="@drawable/round_bg_25"
android:alpha="0.7"
android:backgroundTint="@android:color/darker_gray" />
</LinearLayout>
</ScrollView>
</com.facebook.shimmer.ShimmerFrameLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/category_tabs"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
app:tabBackground="@drawable/sub_category_tab_bg"
app:tabGravity="start"
app:tabIndicatorHeight="0dp"
app:tabMode="scrollable"
app:tabPaddingEnd="25dp"
app:tabPaddingStart="25dp"
app:tabRippleColor="@android:color/transparent"
app:tabSelectedTextColor="@color/white"
app:tabTextColor="@color/color_primary" />
<LinearLayout
android:id="@+id/error_view"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
<LinearLayout
android:id="@+id/error_view"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal"
android:orientation="vertical">
android:text="@string/something_went_wrong"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_14ssp"
<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/_12ssp"
/>
<Button
android:id="@+id/retry_button"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="@string/retry"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_12ssp"
android:background="@drawable/round_bg_25"
android:backgroundTint="@color/game_grad_one"
android:layout_marginTop="5dp"
android:paddingHorizontal="30dp"
/>
</LinearLayout>
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/product_shimmer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/rv_products"
android:layout_marginTop="5dp"
android:visibility="gone"
app:shimmer_auto_start="true"
app:shimmer_base_alpha="0.6"
app:shimmer_highlight_alpha="0.5">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:weightSum="2">
<include
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
layout="@layout/product_view_holder"/>
<include
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
layout="@layout/product_view_holder"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:weightSum="2">
<include
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
layout="@layout/product_view_holder"/>
<include
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
layout="@layout/product_view_holder"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="5dp"
android:weightSum="2">
<include
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
layout="@layout/product_view_holder"/>
<include
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
layout="@layout/product_view_holder"/>
</LinearLayout>
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_products"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/category_tabs"
android:visibility="gone"
tools:listitem="@layout/product_view_holder"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
android:layout_marginHorizontal="5dp"
android:overScrollMode="never"
/>
<Button
android:id="@+id/retry_button"
android:id="@+id/prod_load_more_btn"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/rv_products"
android:text="@string/retry"
android:text="@string/load_more"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
android:textSize="@dimen/_12ssp"
android:background="@drawable/round_bg_25"
android:backgroundTint="@color/game_grad_one"
android:paddingHorizontal="25dp"
android:layout_marginVertical="15dp"
android:layout_marginTop="10dp"
android:paddingHorizontal="30dp"
android:background="@drawable/round_25"
android:backgroundTint="@color/night_status"
android:layout_centerHorizontal="true"
/>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -5,8 +5,7 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_primary"
tools:context=".webseries.views.SeasonActivity">
android:background="@color/color_primary">
<LinearLayout
android:layout_width="match_parent"

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/image"
/>

View File

@@ -0,0 +1,73 @@
<?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="5dp"
android:layout_marginVertical="5dp"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="3dp"
app:cardElevation="3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_marginVertical="10dp"
android:orientation="vertical">
<com.woka.utils.AdiImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="@dimen/_60sdp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="10"
android:layout_marginTop="5dp"
android:orientation="horizontal">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="6.5"
tools:text="Hello there"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_9ssp"
android:maxLines="3"
android:ellipsize="end"
/>
<TextView
android:id="@+id/price"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3.5"
tools:text="$ 200"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:textSize="@dimen/_9ssp"
android:textAlignment="textEnd"
android:maxLines="3"
android:ellipsize="end"
android:layout_marginStart="3dp"
/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -36,7 +36,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/color_primary"
android:textColor="@color/white"
android:fontFamily="@font/exo_2_bold"
android:textSize="@dimen/_12ssp"
@@ -50,7 +50,7 @@
android:layout_height="wrap_content"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/color_primary"
android:textColor="@color/white"
android:textSize="@dimen/_12ssp"
android:layout_centerVertical="true"

View File

@@ -69,6 +69,7 @@
<string name="back_btn" translatable="false">back_btn</string>
<string name="enter_your_password">Enter your password</string>
<string name="please_wait">Please wait...</string>
<string name="alphabets_with_space" translatable="false">ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz</string>
<string name="alphanumeric" translatable="false">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890</string>
<string name="alphanumeric_with_space" translatable="false">ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz1234567890</string>
<string name="resend_otp">Resend OTP?</string>
@@ -88,6 +89,7 @@
<string name="otp_hint" translatable="false">*</string>
<string name="please_select_your_interests">Please select your interests</string>
<string name="alphanumeric_special_characters_without_space" translatable="false"><![CDATA[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!#$%&\'*+/-=^_`{|}~.@]]></string>
<string name="alphanumeric_special_characters_with_space" translatable="false"><![CDATA[ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz1234567890!#$%&\'*+/-=^_`{|}~.@]]></string>
<string name="already_logged_in">Already logged in</string>
<string name="do_you_want_to_continue_log_in">Do you want to continue log in?</string>
<string name="yes">Yes</string>
@@ -253,6 +255,30 @@
<string name="please_enter_your_pincode_below_to_check_service_availability_in_your_area">Please Enter Your Pincode Below to Check Service Availability in Your Area</string>
<string name="enter_your_pincode">Enter your pincode</string>
<string name="check_now">Check Now</string>
<string name="enter_a_valid_pincode">Enter a valid pincode</string>
<string name="enter_a_valid_pincode">Enter a 6 digit pincode</string>
<string name="proceed">Proceed</string>
<string name="address_name_firstname_and_lastname">Address Name (Firstname and Lastname)</string>
<string name="enter_address_name">Enter address name</string>
<string name="address_line_1">Address Line 1</string>
<string name="address_line_2">Address Line 2</string>
<string name="enter_address_line_1">Enter address line 1</string>
<string name="enter_address_line_2">Enter address line 2</string>
<string name="city">City</string>
<string name="enter_city">Enter city</string>
<string name="state">State</string>
<string name="enter_state">Enter state</string>
<string name="pincode">Pincode</string>
<string name="enter_pincode">Enter pincode</string>
<string name="country">Country</string>
<string name="enter_coutry">Enter country</string>
<string name="phone_number">Phone Number</string>
<string name="enter_phone_number">Enter phone number</string>
<string name="please_enter_name">Please enter name</string>
<string name="at_least_15_characters">Enter at least 15 characters</string>
<string name="at_least_2_characters">Enter at least 2 characters</string>
<string name="enter_10_digit_phone">Enter 10 digits phone number</string>
<string name="all">All</string>
<string name="category_name">Category Name :</string>
<string name="sku_id">SKU Id :</string>
<string name="add_to_cart">ADD TO CART</string>
</resources>

View File

@@ -25,6 +25,6 @@ android.enableJetifier=true
# BASE URLS
WOKA_STAGINNG_BASE_URL="https://wokaland.com/admin/api/"
WOKA_STAGINNG_BASE_URL="https://wokaland.com/secret-panel-10102023/hidden-admin-portal-20092023/api/"
WOKA_USER_NAME="admin"
WOKA_PASSWORD="Woka@1234"