Bookmarking web series locally and online

integrating categories api and adding spinner for it

loading and syncing bookmark and like data with both categories and with bookmark list
This commit is contained in:
2024-06-19 21:14:17 +05:30
parent 24b3bcfb71
commit 3527906e07
17 changed files with 426 additions and 76 deletions

View File

@@ -11,6 +11,7 @@ import com.woka.home.mylist.models.SingKaraokeData
import com.woka.networking.ApiResult
import com.woka.networking.RetrofitHelper
import com.woka.userdata.UserRepository
import com.woka.webseries.WebSeriesRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -94,7 +95,9 @@ object MyListRepository {
}
}
fun markAsFavShowLocally(showData: BookmarkedShowData, addToFav: Boolean){
fun markAsFavShowLocally(showData: BookmarkedShowData,
addToFav: Boolean,
categoryId: String){
CoroutineScope(Dispatchers.IO).launch {
val response = RetrofitHelper.handleApiCall {
@@ -103,6 +106,7 @@ object MyListRepository {
FormBody.Builder()
.add("post_id", "${showData.id}")
.add("post_type", PostType.WEB_SERIES.value)
.add("category_id", categoryId)
.build()
)
}else{
@@ -110,6 +114,7 @@ object MyListRepository {
FormBody.Builder()
.add("id", "${showData.id}")
.add("post_type", PostType.WEB_SERIES.value)
.add("category_id", categoryId)
.build()
)
}
@@ -119,11 +124,59 @@ object MyListRepository {
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
myFavData?.result?.show_data?.let {
if (addToFav){
it.add(showData)
}else{
it.remove(showData)
if (myFavData == null && myFavListLiveData.isInitialized){
loadMyFavList()
}else{
myFavData?.result?.show_data?.let {
if (addToFav){
it.add(showData)
}else{
it.removeIf { bShow ->
bShow.id == showData.id
}
}
}
}
if (categoryId == "1"){
WebSeriesRepository.englishWebSeriesData?.show_data?.let {
for (show in it){
var found = false
show?.let {data ->
if (showData.id == data.id){
found = true
if (addToFav){
show.addAsBookMark(categoryId)
}else{
show.removeAsBookMark(categoryId)
}
}
}
if (found) break
}
}
}else{
WebSeriesRepository.hindiWebSeriesData?.show_data?.let {
for (show in it){
var found = false
show?.let {data ->
if (showData.id == data.id){
found = true
if (addToFav){
show.addAsBookMark(categoryId)
}else{
show.removeAsBookMark(categoryId)
}
}
}
if (found) break
}
}
}
}

View File

@@ -112,7 +112,8 @@ class WebSeriesAdapter(private val context: Context,
if (fav.isSelected){
MyListRepository.markAsFavShowLocally(
showData,
false
false,
categoryId
)
val currentList = currentList.toMutableList()

View File

@@ -1,11 +1,14 @@
package com.woka.home.mylist.models
import com.woka.webseries.models.ContentMoreDetail
import com.woka.webseries.models.ShowData
data class BookmarkedShowData(
val age_range_master_id: String?,
val bookmark_category_ids: String?,
val bookmark_count: Int?,
val category_master_id: String?,
val content_more_details: List<ContentMoreDetailXX>?,
val content_more_details: List<ContentMoreDetail?>?,
val description: String?,
val gender_master_id: String?,
val id: Int?,
@@ -18,4 +21,24 @@ data class BookmarkedShowData(
val total_episodes: Int?,
val total_seasons: Int?,
val views_count: Int?
)
){
constructor(showData: ShowData, categoryIds: String): this(
showData.age_range_master_id,
categoryIds,
showData.bookmark_count,
showData.category_master_id,
showData.content_more_details,
showData.description,
showData.gender_master_id,
showData.id,
showData.is_liked,
showData.likes_count,
showData.mark_as_favourite,
showData.show_type,
showData.thumbnail_path,
showData.title,
showData.total_episodes,
showData.total_seasons,
showData.views_count
)
}

View File

@@ -1,11 +0,0 @@
package com.woka.home.mylist.models
data class ContentMoreDetailXX(
val content_id: Int?,
val description: String?,
val id: Int?,
val language_master_id: Int?,
val post_type: Int?,
val tags_keywords: String?,
val title: String?
)

View File

@@ -1,6 +1,7 @@
package com.woka.modules
import com.woka.modules.blogs.models.BlogsResponse
import com.woka.modules.categorymodels.CategoriesResponse
import com.woka.modules.faqs.models.FaqResponse
import com.woka.modules.wokasongs.models.WokaSongsResponse
import com.woka.networking.ApiResponse
@@ -26,4 +27,7 @@ interface ModuleApiService {
@POST("song_listing")
suspend fun getWokaSongs(): Response<ApiResponse<WokaSongsResponse>>
@POST("category_listing")
suspend fun categoryListing(@Body body: FormBody): Response<ApiResponse<CategoriesResponse>>
}

View File

@@ -1,7 +1,13 @@
package com.woka.modules
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.woka.modules.categorymodels.CategoriesResponse
import com.woka.networking.ApiResult
import com.woka.networking.RetrofitHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.FormBody
object ModuleRepository {
@@ -33,4 +39,24 @@ object ModuleRepository {
}
}
// categories listing
private val _showCategoryLiveData = MutableLiveData<ApiResult<CategoriesResponse>>()
val showCategoryLiveData: LiveData<ApiResult<CategoriesResponse>>
get() = _showCategoryLiveData
fun showCategories(){
CoroutineScope(Dispatchers.IO).launch {
_showCategoryLiveData.postValue(ApiResult.Loading())
_showCategoryLiveData.postValue(
RetrofitHelper.handleApiCall {
moduleApiService.categoryListing(
FormBody.Builder()
.add("module_id", "7")
.build()
)
}
)
}
}
}

View File

@@ -0,0 +1,6 @@
package com.woka.modules.categorymodels
data class CategoriesResponse(
val result: List<Category?>?,
val total_records: Int?
)

View File

@@ -0,0 +1,7 @@
package com.woka.modules.categorymodels
data class Category(
val category_name: String?,
val category_thumbnail: String?,
val id: Int?
)

View File

@@ -2,11 +2,13 @@ package com.woka.webseries
import com.woka.networking.ApiResponse
import com.woka.webseries.models.WebSeriesResponse
import okhttp3.FormBody
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.POST
interface WebSeriesApiService {
@POST("watch_show_listing")
suspend fun getWebSeries(): Response<ApiResponse<WebSeriesResponse>>
suspend fun getWebSeries(@Body formBody: FormBody): Response<ApiResponse<WebSeriesResponse>>
}

View File

@@ -3,9 +3,12 @@ package com.woka.webseries
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.woka.home.mylist.MyFavApiService
import com.woka.home.mylist.MyListRepository
import com.woka.home.mylist.models.BookmarkedShowData
import com.woka.home.mylist.models.PostType
import com.woka.networking.ApiResult
import com.woka.networking.RetrofitHelper
import com.woka.webseries.models.ShowData
import com.woka.webseries.models.WebSeriesResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -17,41 +20,67 @@ object WebSeriesRepository {
private val apiService = RetrofitHelper.getRetrofit().create(WebSeriesApiService::class.java)
private val myFavApiService = RetrofitHelper.getRetrofit().create(MyFavApiService::class.java)
private val _webSeriesLiveData = MutableLiveData<ApiResult<WebSeriesResponse>>()
val webSeriesLiveData: LiveData<ApiResult<WebSeriesResponse>>
get() = _webSeriesLiveData
private val _englishWebSeriesLiveData = MutableLiveData<ApiResult<WebSeriesResponse>>()
val englishWebSeriesLiveData: LiveData<ApiResult<WebSeriesResponse>>
get() = _englishWebSeriesLiveData
private var webSeriesData: WebSeriesResponse? = null
var englishWebSeriesData: WebSeriesResponse? = null
init {
loadWebSeries()
}
private val _hindiWebSeriesLiveData = MutableLiveData<ApiResult<WebSeriesResponse>>()
val hindiWebSeriesLiveData: LiveData<ApiResult<WebSeriesResponse>>
get() = _hindiWebSeriesLiveData
private fun loadWebSeries(){
var hindiWebSeriesData: WebSeriesResponse? = null
fun loadEnglishWebSeries(){
CoroutineScope(Dispatchers.IO).launch {
if (webSeriesData != null){
_webSeriesLiveData.postValue(ApiResult.Success(webSeriesData))
}else{
_webSeriesLiveData.postValue(ApiResult.Loading())
}
_englishWebSeriesLiveData.postValue(ApiResult.Loading())
val response = RetrofitHelper.handleApiCall {
apiService.getWebSeries()
apiService.getWebSeries(
FormBody.Builder()
.add("category_id", "1")
.build()
)
}
when (response){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
webSeriesData = response.data
englishWebSeriesData = response.data
}
}
_webSeriesLiveData.postValue(response)
_englishWebSeriesLiveData.postValue(response)
}
}
fun likeUnLikeShow(postId: String, likeIt: Boolean){
fun loadHindiWebSeries(){
CoroutineScope(Dispatchers.IO).launch {
_hindiWebSeriesLiveData.postValue(ApiResult.Loading())
val response = RetrofitHelper.handleApiCall {
apiService.getWebSeries(
FormBody.Builder()
.add("category_id", "18")
.build()
)
}
when (response){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
hindiWebSeriesData = response.data
}
}
_hindiWebSeriesLiveData.postValue(response)
}
}
fun likeUnLikeShow(postId: String, likeIt: Boolean, categoryId: String){
CoroutineScope(Dispatchers.IO).launch {
val response = RetrofitHelper.handleApiCall {
if (likeIt){
@@ -75,30 +104,62 @@ object WebSeriesRepository {
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
changeLikeLocally(postId, likeIt)
changeLikeLocally(postId, likeIt, categoryId)
}
}
}
}
private fun changeLikeLocally(id: String, isLiked: Boolean){
private fun changeLikeLocally(id: String, isLiked: Boolean, categoryId: String){
webSeriesData?.show_data?.let{
for (show in it){
var found = false
show?.let {data ->
if ("${data.id}" == id){
data.is_liked = isLiked
data.likes_count?.let { count ->
data.likes_count = if (isLiked) count + 1
else max(0, count - 1)
if (categoryId == "1"){
englishWebSeriesData?.show_data?.let{
for (show in it){
var found = false
show?.let {data ->
if ("${data.id}" == id){
data.is_liked = isLiked
data.likes_count?.let { count ->
data.likes_count = if (isLiked) count + 1
else max(0, count - 1)
}
found = true
}
found = true
}
if (found) break
}
}
}else{
hindiWebSeriesData?.show_data?.let{
for (show in it){
var found = false
show?.let {data ->
if ("${data.id}" == id){
data.is_liked = isLiked
data.likes_count?.let { count ->
data.likes_count = if (isLiked) count + 1
else max(0, count - 1)
}
found = true
}
}
if (found) break
}
if (found) break
}
}
}
fun updateFavShow(showData: ShowData, addToBookmark: Boolean, categoryId: String){
MyListRepository.markAsFavShowLocally(
BookmarkedShowData(showData, categoryId),
addToBookmark,
categoryId
)
}
}

View File

@@ -9,6 +9,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.woka.R
import com.woka.databinding.ShowViewHolderBinding
import com.woka.utils.TAG
@@ -19,17 +20,15 @@ import com.woka.webseries.models.ShowData
import java.util.concurrent.Executors
import kotlin.math.max
class WebSeriesShowAdapter(private val context: Context,
class WebSeriesShowAdapter private constructor(private val context: Context,
private var categoryId: String,
config: AsyncDifferConfig<ShowData>): ListAdapter<ShowData, WebSeriesShowAdapter.ShowViewHolder>(config) {
companion object{
private val DIFF_UTIL = object : DiffUtil.ItemCallback<ShowData>(){
override fun areItemsTheSame(oldItem: ShowData, newItem: ShowData): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: ShowData, newItem: ShowData): Boolean {
val value = oldItem.title == newItem.title &&
oldItem.is_liked == newItem.is_liked &&
oldItem.likes_count == newItem.likes_count
Log.d(TAG, "areContentsTheSame: $value")
return value
return true
}
}
@@ -40,7 +39,7 @@ class WebSeriesShowAdapter(private val context: Context,
inner class ShowViewHolder(val binding: ShowViewHolderBinding): ViewHolder(binding.root)
constructor(context: Context): this(context, DIFF_CONFIG)
constructor(context: Context, categoryId: String): this(context, categoryId, DIFF_CONFIG)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShowViewHolder {
return ShowViewHolder(
@@ -57,8 +56,10 @@ class WebSeriesShowAdapter(private val context: Context,
holder.binding.apply {
showData.thumbnail_path?.let {
Glide.with(root.context)
Glide.with(context.applicationContext)
.load(it)
.error(android.R.color.darker_gray)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(image)
}
@@ -79,13 +80,13 @@ class WebSeriesShowAdapter(private val context: Context,
}
if (like.isSelected){
WebSeriesRepository.likeUnLikeShow("${showData.id}", false)
WebSeriesRepository.likeUnLikeShow("${showData.id}", false, categoryId)
showData.likes_count?.let {
likeCount.text = "${max(it - 1, 0)}"
}
}else{
WebSeriesRepository.likeUnLikeShow("${showData.id}", true)
WebSeriesRepository.likeUnLikeShow("${showData.id}", true, categoryId)
showData.likes_count?.let {
val value = it + 1
@@ -96,13 +97,21 @@ class WebSeriesShowAdapter(private val context: Context,
like.isSelected = !like.isSelected
}
fav.isSelected = showData.mark_as_favourite == true
fav.isSelected = showData.isBookMarked(categoryId)
fav.setOnClickListener {
if (!context.isNetworkConnected()){
context.toast(context.getString(R.string.no_internet))
return@setOnClickListener
}
WebSeriesRepository.updateFavShow(
showData,
!fav.isSelected,
categoryId
)
fav.isSelected = !fav.isSelected
}
}
}

View File

@@ -8,14 +8,14 @@ data class ShowData(
val category_master_id: String?,
val content_more_details: List<ContentMoreDetail?>?,
val description: String?,
val favourite_category_ids: Int?,
var favourite_category_ids: String?,
val gender_data: List<GenderData?>?,
val gender_master_id: String?,
val id: Int?,
var is_liked: Boolean?,
val liked_category_ids: Int?,
var likes_count: Int?,
val mark_as_favourite: Boolean?,
var mark_as_favourite: Boolean?,
val season_data: List<SeasonData?>?,
val show_type: String?,
val thumbnail_path: String?,
@@ -23,4 +23,25 @@ data class ShowData(
val total_episodes: Int?,
val total_seasons: Int?,
val views_count: Int?
)
){
fun isBookMarked(categoryId: String): Boolean{
favourite_category_ids?.split(",")?.toMutableList()?.let {
return it.contains(categoryId)
}
return false
}
fun addAsBookMark(categoryId: String){
val categories = favourite_category_ids?.split(",")?.toMutableList()?: mutableListOf()
categories.add(categoryId)
favourite_category_ids = categories.joinToString(",")
}
fun removeAsBookMark(categoryId: String){
favourite_category_ids?.split(",")?.toMutableList()?.let {
it.remove(categoryId)
favourite_category_ids = it.joinToString(",")
}
}
}

View File

@@ -8,6 +8,9 @@ import com.woka.webseries.models.WebSeriesResponse
class WebSeriesViewModel: ViewModel() {
val webSeriesData: LiveData<ApiResult<WebSeriesResponse>>
get() = WebSeriesRepository.webSeriesLiveData
val englishWebSeriesData: LiveData<ApiResult<WebSeriesResponse>>
get() = WebSeriesRepository.englishWebSeriesLiveData
val hindiWebSeriesData: LiveData<ApiResult<WebSeriesResponse>>
get() = WebSeriesRepository.hindiWebSeriesLiveData
}

View File

@@ -1,25 +1,40 @@
package com.woka.webseries.views
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.AdapterView
import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.woka.R
import com.woka.databinding.ActivityWebSeriesBinding
import com.woka.modules.ModuleRepository
import com.woka.networking.ApiResult
import com.woka.utils.TAG
import com.woka.utils.WokaBaseActivity
import com.woka.utils.hide
import com.woka.utils.show
import com.woka.utils.toast
import com.woka.webseries.WebSeriesRepository
import com.woka.webseries.adapters.WebSeriesShowAdapter
import com.woka.webseries.models.WebSeriesResponse
import com.woka.webseries.viewmodel.WebSeriesViewModel
class WebSeriesActivity : WokaBaseActivity() {
class WebSeriesActivity : WokaBaseActivity(), Observer<ApiResult<WebSeriesResponse>> {
private lateinit var binding: ActivityWebSeriesBinding
private lateinit var viewmodel: WebSeriesViewModel
private lateinit var showAdapter: WebSeriesShowAdapter
private lateinit var engShowAdapter: WebSeriesShowAdapter
private lateinit var hinShowAdapter: WebSeriesShowAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -41,6 +56,10 @@ class WebSeriesActivity : WokaBaseActivity() {
clickEvents()
setObservers()
if(!ModuleRepository.showCategoryLiveData.isInitialized){
ModuleRepository.showCategories()
}
}
private fun clickEvents() {
@@ -54,7 +73,8 @@ class WebSeriesActivity : WokaBaseActivity() {
private fun initViews(){
// adapters
showAdapter = WebSeriesShowAdapter(this)
engShowAdapter = WebSeriesShowAdapter(this, "1")
hinShowAdapter = WebSeriesShowAdapter(this, "18")
binding.apply {
// making space for masila image
@@ -66,19 +86,86 @@ class WebSeriesActivity : WokaBaseActivity() {
toolbar.title.text = getString(R.string.web_series)
rvWebSeries.adapter = showAdapter
rvHindiWebSeries.adapter = hinShowAdapter
rvEngWebSeries.adapter = engShowAdapter
((rvHindiWebSeries.itemAnimator) as SimpleItemAnimator).supportsChangeAnimations = false
((rvEngWebSeries.itemAnimator) as SimpleItemAnimator).supportsChangeAnimations = false
}
}
private fun setObservers(){
viewmodel.webSeriesData.observe(this){
viewmodel.hindiWebSeriesData.observe(this, this)
viewmodel.englishWebSeriesData.observe(this, this)
ModuleRepository.showCategoryLiveData.observe(this){
when(it){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
it.data?.show_data?.let { showData ->
showAdapter.submitList(showData)
val categories = mutableListOf<String>()
it.data?.result?.let {catList ->
for (cat in catList){
cat?.category_name?.let { category ->
categories.add(category)
}
}
}
val adapter: ArrayAdapter<*> = ArrayAdapter(
this,
android.R.layout.simple_spinner_item,
categories
)
adapter.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item
)
binding.categorySpinner.setAdapter(adapter)
}
}
}
binding.categorySpinner.onItemSelectedListener = object : OnItemSelectedListener{
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
if (binding.categorySpinner.selectedItemId == 0L){
WebSeriesRepository.loadHindiWebSeries()
}else{
WebSeriesRepository.loadEnglishWebSeries()
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
override fun onChanged(value: ApiResult<WebSeriesResponse>) {
when (value){
is ApiResult.Error -> {}
is ApiResult.Loading -> {}
is ApiResult.Success -> {
value.data?.show_data?.let {
if (binding.categorySpinner.selectedItemPosition <= 0){
// hindi
binding.rvHindiWebSeries.show()
binding.rvEngWebSeries.hide()
hinShowAdapter.submitList(it)
}else{
// English
binding.rvHindiWebSeries.hide()
binding.rvEngWebSeries.show()
engShowAdapter.submitList(it)
}
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp"/>
<solid android:color="@color/white"/>
</shape>

View File

@@ -103,12 +103,61 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/select_video_language"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_12ssp"
android:layout_marginHorizontal="15dp"
/>
<androidx.cardview.widget.CardView
android:layout_width="@dimen/_120sdp"
android:layout_height="@dimen/_40sdp"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="10dp"
app:cardCornerRadius="5dp"
>
<Spinner
android:id="@+id/category_spinner"
android:layout_width="match_parent"
android:layout_height="@dimen/_37sdp"
android:layout_marginStart="5dp"
android:popupBackground="@drawable/round_bg_5_white"
android:layout_gravity="center_vertical"
/>
</androidx.cardview.widget.CardView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_web_series"
android:id="@+id/rv_hindi_web_series"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/show_view_holder"
android:layout_marginTop="5dp"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_eng_web_series"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/show_view_holder"
android:layout_marginTop="5dp"
/>
</LinearLayout>

View File

@@ -175,4 +175,5 @@
<string name="_0">0</string>
<string name="no_internet">No internet</string>
<string name="trailer">TRAILER</string>
<string name="select_video_language">Select video language</string>
</resources>