MVVM structure for all:
Karaoke, audiobooks, games (Completed) Started implementing new ui structure for web series (Single activity and multiple fragments) Completed first web series fragment Completed implementing mvvm for web series
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name=".WokaApp"
|
||||
@@ -17,18 +17,21 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Woka"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".webseries.views.SeriesActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".karaoke.player.KaraokePlayerActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode|fontScale"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTask"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:screenOrientation="portrait"
|
||||
/>
|
||||
android:supportsPictureInPicture="true" />
|
||||
<activity
|
||||
android:name=".karaoke.views.KaraokeActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait"/>
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".wokagames.playerr.GamePlayerActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
||||
|
||||
@@ -2,11 +2,13 @@ package com.woka.audiobooks
|
||||
|
||||
import com.woka.audiobooks.models.audiodata.AudioBooksResponse
|
||||
import com.woka.networking.ApiResponse
|
||||
import okhttp3.FormBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.POST
|
||||
|
||||
interface AudioBookApiService {
|
||||
|
||||
@POST("listen_audio_listing")
|
||||
suspend fun audioBookListing(): Response<ApiResponse<AudioBooksResponse>>
|
||||
suspend fun audioBookListing(@Body formBody: FormBody): Response<ApiResponse<AudioBooksResponse>>
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.woka.audiobooks
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.woka.audiobooks.models.audiodata.AudioBookData
|
||||
import com.woka.audiobooks.models.audiodata.AudioBooksResponse
|
||||
import com.woka.audiobooks.models.continuedata.ContinueAudioResponse
|
||||
@@ -12,7 +10,6 @@ import com.woka.networking.ApiResult
|
||||
import com.woka.networking.RetrofitHelper
|
||||
import com.woka.networking.RetrofitHelper.handleApiCall
|
||||
import com.woka.userdata.UserActionApiService
|
||||
import com.woka.utils.TAG
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -25,75 +22,25 @@ object AudioBookRepository {
|
||||
private val apiService = RetrofitHelper.getRetrofit().create(AudioBookApiService::class.java)
|
||||
private val userActionApiService = RetrofitHelper.getRetrofit().create(UserActionApiService::class.java)
|
||||
|
||||
// live data
|
||||
// audio books data (loose caching)
|
||||
private val _audioBooksLiveData = MutableLiveData<ApiResult<AudioBooksResponse>>()
|
||||
val audioBooksLiveData: LiveData<ApiResult<AudioBooksResponse>>
|
||||
get() = _audioBooksLiveData
|
||||
|
||||
private var audioBooksData: AudioBooksResponse? = null
|
||||
|
||||
// continue watch audio books data
|
||||
private val _continueAudioBooksLiveData = MutableLiveData<ApiResult<ContinueAudioResponse>>()
|
||||
val continueAudioBooksLiveData: LiveData<ApiResult<ContinueAudioResponse>>
|
||||
get() = _continueAudioBooksLiveData
|
||||
|
||||
private var continueAudioData: ContinueAudioResponse? = null
|
||||
|
||||
fun loadAudioBooks(){
|
||||
if (audioBooksData != null){
|
||||
_audioBooksLiveData.postValue(ApiResult.Success(audioBooksData))
|
||||
return
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch{
|
||||
_audioBooksLiveData.postValue(ApiResult.Loading())
|
||||
|
||||
val response = handleApiCall{
|
||||
apiService.audioBookListing()
|
||||
}
|
||||
|
||||
when (response){
|
||||
is ApiResult.Error -> _audioBooksLiveData.postValue(ApiResult.Error(response.errorMessage, response.error))
|
||||
is ApiResult.Loading -> _audioBooksLiveData.postValue(ApiResult.Loading())
|
||||
is ApiResult.Success -> {
|
||||
response.data?.let {
|
||||
audioBooksData = it
|
||||
_audioBooksLiveData.postValue(ApiResult.Success(it))
|
||||
}?:{
|
||||
_audioBooksLiveData.postValue(ApiResult.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
suspend fun loadAudioBooks(pageNo: Int, quantity: Int): ApiResult<AudioBooksResponse> {
|
||||
return handleApiCall {
|
||||
apiService.audioBookListing(
|
||||
FormBody.Builder()
|
||||
.add("api_version", "v2")
|
||||
.add("start", "$pageNo")
|
||||
.add("limit", "$quantity")
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadContinueListening(){
|
||||
if (continueAudioData != null){
|
||||
_continueAudioBooksLiveData.postValue(ApiResult.Success(continueAudioData))
|
||||
return
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val response = handleApiCall {
|
||||
userActionApiService.continueAudioBookListing(
|
||||
FormBody.Builder()
|
||||
.add("post_type", PostType.AUDIO_BOOKS.value)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
when (response){
|
||||
is ApiResult.Error -> {}
|
||||
is ApiResult.Loading -> {}
|
||||
is ApiResult.Success -> {
|
||||
response.data?.let {
|
||||
continueAudioData = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_continueAudioBooksLiveData.postValue(response)
|
||||
suspend fun loadContinueLiveData(): ApiResult<ContinueAudioResponse> {
|
||||
return handleApiCall {
|
||||
userActionApiService.continueAudioBookListing(
|
||||
FormBody.Builder()
|
||||
.add("post_type", PostType.AUDIO_BOOKS.value)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,48 +69,6 @@ object AudioBookRepository {
|
||||
}
|
||||
|
||||
private fun changeLikeLocally(id: String, isLiked: Boolean){
|
||||
// changing in audio books locally
|
||||
audioBooksData?.audio_data?.let {audioBooks ->
|
||||
for (audio in audioBooks){
|
||||
var found = false
|
||||
audio?.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
|
||||
}
|
||||
}
|
||||
|
||||
// continue audio books data update
|
||||
continueAudioData?.result?.let {
|
||||
for (audio in it){
|
||||
var found = false
|
||||
audio?.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
|
||||
}
|
||||
}
|
||||
|
||||
// changing in fav list locally
|
||||
MyListRepository.myFavData.result?.audio_data?.let {
|
||||
for (audioData in it){
|
||||
@@ -208,29 +113,5 @@ object AudioBookRepository {
|
||||
favAudioData.removeIf{it.id == audioBookData.id}
|
||||
}
|
||||
}
|
||||
|
||||
audioBooksData?.audio_data?.let {
|
||||
for (audio in it){
|
||||
if (audio?.id == audioBookData.id){
|
||||
audio?.mark_as_favourite = addToBookmark
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continueAudioData?.result?.let {
|
||||
for (audio in it){
|
||||
if (audio?.id == audioBookData.id){
|
||||
audio?.mark_as_favourite = addToBookmark
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun clearData(){
|
||||
audioBooksData = null
|
||||
continueAudioData = null
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,12 @@ import com.woka.utils.isNetworkConnected
|
||||
import com.woka.utils.show
|
||||
import com.woka.utils.toast
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.max
|
||||
|
||||
class AudioBooksAdapter(
|
||||
private val context: Context,
|
||||
private var onBookClicked: (AudioBookData) -> Unit,
|
||||
private var onBookChanged: (id: Int) -> Unit
|
||||
private var onBookChanged: (id: Int, AudioBookData) -> Unit
|
||||
): ListAdapter<AudioBookData, AudioBooksAdapter.AudioBookViewHolder>(ASYNC_DIFF_UTIL) {
|
||||
|
||||
inner class AudioBookViewHolder(val binding: ShowViewHolderBinding): ViewHolder(binding.root)
|
||||
@@ -96,10 +97,24 @@ class AudioBooksAdapter(
|
||||
!like.isSelected
|
||||
)
|
||||
|
||||
audioBook.likes_count?.let {
|
||||
audioBook.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
audioBook.is_liked = !like.isSelected
|
||||
|
||||
like.isSelected = !like.isSelected
|
||||
likeCount.text = "${audioBook.likes_count}"
|
||||
|
||||
audioBook.id?.let(onBookChanged)
|
||||
audioBook.id?.let{
|
||||
onBookChanged(it, audioBook)
|
||||
}
|
||||
}
|
||||
|
||||
fav.isSelected = audioBook.mark_as_favourite == true
|
||||
@@ -115,8 +130,12 @@ class AudioBooksAdapter(
|
||||
!fav.isSelected
|
||||
)
|
||||
|
||||
audioBook.mark_as_favourite = !fav.isSelected
|
||||
|
||||
fav.isSelected = audioBook.mark_as_favourite == true
|
||||
audioBook.id?.let(onBookChanged)
|
||||
audioBook.id?.let{
|
||||
onBookChanged(it, audioBook)
|
||||
}
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
|
||||
@@ -17,10 +17,11 @@ import com.woka.utils.isNetworkConnected
|
||||
import com.woka.utils.show
|
||||
import com.woka.utils.toast
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.max
|
||||
|
||||
class ContinueAudioAdapter(private val context: Context,
|
||||
private var onBookClicked: (ContinueAudioData) -> Unit,
|
||||
private var onContinueBookChanged: (id: Int) -> Unit): ListAdapter<ContinueAudioData, FavoriteViewHolder>(DIFF_CONFIG) {
|
||||
private var onContinueBookChanged: (id: Int, AudioBookData) -> Unit): ListAdapter<ContinueAudioData, FavoriteViewHolder>(DIFF_CONFIG) {
|
||||
|
||||
companion object{
|
||||
private val DIFF_UTIL = object : DiffUtil.ItemCallback<ContinueAudioData>(){
|
||||
@@ -91,10 +92,22 @@ class ContinueAudioAdapter(private val context: Context,
|
||||
!like.isSelected
|
||||
)
|
||||
|
||||
audioBook.likes_count?.let {
|
||||
audioBook.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
audioBook.is_liked = !like.isSelected
|
||||
|
||||
like.isSelected = !like.isSelected
|
||||
likeCount.text = "${audioBook.likes_count}"
|
||||
|
||||
audioBook?.id?.let(onContinueBookChanged)
|
||||
audioBook?.id?.let{onContinueBookChanged(it, AudioBookData(audioBook))}
|
||||
}
|
||||
|
||||
fav.isSelected = audioBook.mark_as_favourite == true
|
||||
@@ -110,8 +123,11 @@ class ContinueAudioAdapter(private val context: Context,
|
||||
!fav.isSelected
|
||||
)
|
||||
|
||||
audioBook.mark_as_favourite = !fav.isSelected
|
||||
|
||||
fav.isSelected = audioBook.mark_as_favourite == true
|
||||
audioBook?.id?.let(onContinueBookChanged)
|
||||
|
||||
audioBook?.id?.let{onContinueBookChanged(it, AudioBookData(audioBook))}
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.woka.audiobooks.viewmodels
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.woka.audiobooks.AudioBookRepository
|
||||
import com.woka.audiobooks.models.audiodata.AudioBookData
|
||||
import com.woka.audiobooks.models.continuedata.ContinueAudioData
|
||||
import com.woka.networking.ApiResult
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AudioBookViewModel: ViewModel() {
|
||||
|
||||
private val _audioBookData = MutableLiveData<ApiResult<MutableList<AudioBookData>>>()
|
||||
val audioBookLiveData: LiveData<ApiResult<MutableList<AudioBookData>>>
|
||||
get() = _audioBookData
|
||||
|
||||
private val audioBookList = ArrayList<AudioBookData>()
|
||||
|
||||
private var nextPageToLoad: Int = 0
|
||||
private var quantityPerPage: Int = 6
|
||||
var lastPage = false
|
||||
|
||||
private val _audioContinueLiveData = MutableLiveData<ApiResult<List<ContinueAudioData>>>()
|
||||
val audioContinueLiveData: LiveData<ApiResult<List<ContinueAudioData>>>
|
||||
get() = _audioContinueLiveData
|
||||
|
||||
fun loadKaraokeSongs() {
|
||||
viewModelScope.launch {
|
||||
_audioBookData.postValue(ApiResult.Loading())
|
||||
|
||||
when (val value = AudioBookRepository.loadAudioBooks(nextPageToLoad, quantityPerPage)) {
|
||||
is ApiResult.Error -> _audioBookData.postValue(
|
||||
ApiResult.Error(
|
||||
value.errorMessage,
|
||||
value.error
|
||||
)
|
||||
)
|
||||
|
||||
is ApiResult.Loading -> _audioBookData.postValue(ApiResult.Loading())
|
||||
is ApiResult.Success -> {
|
||||
value.data?.let {
|
||||
it.audio_data?.filterNotNull()?.let { newList ->
|
||||
audioBookList.addAll(newList)
|
||||
|
||||
lastPage = audioBookList.size == it.total_records
|
||||
|
||||
_audioBookData.postValue(ApiResult.Success(audioBookList))
|
||||
nextPageToLoad++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadContinueData(){
|
||||
viewModelScope.launch {
|
||||
_audioContinueLiveData.postValue(ApiResult.Loading())
|
||||
|
||||
when (val value = AudioBookRepository.loadContinueLiveData()) {
|
||||
is ApiResult.Error -> _audioContinueLiveData.postValue(
|
||||
ApiResult.Error(
|
||||
value.errorMessage,
|
||||
value.error
|
||||
)
|
||||
)
|
||||
|
||||
is ApiResult.Loading -> _audioContinueLiveData.postValue(ApiResult.Loading())
|
||||
is ApiResult.Success -> {
|
||||
value.data?.let {
|
||||
it.result?.filterNotNull()?.let { newList ->
|
||||
_audioContinueLiveData.postValue(ApiResult.Success(newList))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,32 +11,37 @@ import android.view.WindowManager
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.jwplayer.pub.api.media.playlists.PlaylistItem
|
||||
import com.woka.R
|
||||
import com.woka.WokaApp
|
||||
import com.woka.WokaApp.Companion.userPrefs
|
||||
import com.woka.audiobooks.AudioBookRepository
|
||||
import com.woka.audiobooks.adapters.AudioBooksAdapter
|
||||
import com.woka.audiobooks.adapters.ContinueAudioAdapter
|
||||
import com.woka.audiobooks.models.audiodata.AudioBookData
|
||||
import com.woka.audiobooks.models.continuedata.ContinueAudioData
|
||||
import com.woka.audiobooks.viewmodels.AudioBookViewModel
|
||||
import com.woka.databinding.ActivityAudioBooksBinding
|
||||
import com.woka.databinding.DialogModuleShowerBinding
|
||||
import com.woka.networking.ApiResult
|
||||
import com.woka.players.models.VideoPlayList
|
||||
import com.woka.players.views.PlayerActivity
|
||||
import com.woka.userPreference.UserType
|
||||
import com.woka.utils.WokaBaseActivity
|
||||
import com.woka.utils.hide
|
||||
import com.woka.utils.setVisibility
|
||||
import com.woka.utils.show
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
|
||||
class AudioBooksActivity : WokaBaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityAudioBooksBinding
|
||||
private lateinit var viewModel: AudioBookViewModel
|
||||
|
||||
private lateinit var audioBookAdapter: AudioBooksAdapter
|
||||
private lateinit var continueAudioAdapter: ContinueAudioAdapter
|
||||
@@ -59,6 +64,8 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
navigationBarColor = getColor(R.color.color_primary_dark)
|
||||
}
|
||||
|
||||
viewModel = ViewModelProvider(this)[AudioBookViewModel::class.java]
|
||||
|
||||
audioBookAdapter = AudioBooksAdapter(this, ::onBookClicked, ::onBookChanged)
|
||||
continueAudioAdapter =
|
||||
ContinueAudioAdapter(this, ::onContinueBookClicked, ::onContinueBookChanged)
|
||||
@@ -75,7 +82,9 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
|
||||
setObservers()
|
||||
|
||||
AudioBookRepository.loadAudioBooks()
|
||||
if (!viewModel.audioBookLiveData.isInitialized){
|
||||
viewModel.loadKaraokeSongs()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
@@ -120,52 +129,81 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
}
|
||||
|
||||
retryBtn.setOnClickListener {
|
||||
AudioBookRepository.loadAudioBooks()
|
||||
viewModel.loadKaraokeSongs()
|
||||
}
|
||||
|
||||
loadMoreBtn.setOnClickListener {
|
||||
viewModel.loadKaraokeSongs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setObservers() {
|
||||
AudioBookRepository.audioBooksLiveData.observe(this) {
|
||||
when (it) {
|
||||
viewModel.audioBookLiveData.observe(this){
|
||||
when(it){
|
||||
is ApiResult.Error -> {
|
||||
binding.shimmer.hide()
|
||||
binding.rvAudioBooks.hide()
|
||||
binding.listenTxt.hide()
|
||||
binding.trailerView.hide()
|
||||
if (audioBookAdapter.currentList.size == 0) {
|
||||
// none of the data is yet loaded
|
||||
binding.rvAudioBooks.hide()
|
||||
binding.listenTxt.hide()
|
||||
binding.trailerView.hide()
|
||||
|
||||
binding.errorView.show()
|
||||
binding.errorView.show()
|
||||
} else {
|
||||
// error in loading more
|
||||
binding.loadMoreProgress.hide()
|
||||
binding.loadMoreBtn.text = getString(R.string.retry)
|
||||
binding.loadMoreBtn.show()
|
||||
}
|
||||
}
|
||||
|
||||
is ApiResult.Loading -> {
|
||||
binding.shimmer.show()
|
||||
|
||||
binding.rvAudioBooks.hide()
|
||||
binding.listenTxt.hide()
|
||||
binding.trailerView.hide()
|
||||
binding.errorView.hide()
|
||||
if (audioBookAdapter.currentList.size == 0) {
|
||||
// loading first data
|
||||
binding.shimmer.show()
|
||||
binding.errorView.hide()
|
||||
} else {
|
||||
// loading more data
|
||||
binding.loadMoreProgress.show()
|
||||
binding.loadMoreBtn.hide()
|
||||
}
|
||||
}
|
||||
|
||||
is ApiResult.Success -> {
|
||||
it.data?.audio_data?.filterNotNull()?.let { audioBookData ->
|
||||
if (audioBookData.isNotEmpty()) {
|
||||
binding.shimmer.hide()
|
||||
binding.errorView.hide()
|
||||
|
||||
AudioBookRepository.loadContinueListening()
|
||||
|
||||
loadTrailerData(audioBookData[0])
|
||||
it.data?.let {newList ->
|
||||
if (newList.isNotEmpty()){
|
||||
binding.rvAudioBooks.show()
|
||||
binding.listenTxt.show()
|
||||
binding.trailerView.show()
|
||||
|
||||
audioBookAdapter.submitList(audioBookData)
|
||||
binding.errorView.hide()
|
||||
binding.shimmer.hide()
|
||||
|
||||
binding.loadMoreProgress.hide()
|
||||
binding.loadMoreBtn.text = getString(R.string.load_more)
|
||||
binding.loadMoreBtn.setVisibility(!viewModel.lastPage)
|
||||
|
||||
if (audioBookAdapter.currentList.isEmpty()){
|
||||
// first data load
|
||||
if (userPrefs?.userType != UserType.GUEST && !viewModel.audioContinueLiveData.isInitialized){
|
||||
viewModel.loadContinueData()
|
||||
}
|
||||
|
||||
loadTrailerData(newList[0])
|
||||
audioBookAdapter.submitList(newList)
|
||||
}else{
|
||||
// loaded more data
|
||||
audioBookAdapter.notifyItemRangeInserted(
|
||||
audioBookAdapter.currentList.size,
|
||||
newList.size
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioBookRepository.continueAudioBooksLiveData.observe(this) {
|
||||
viewModel.audioContinueLiveData.observe(this) {
|
||||
when (it) {
|
||||
is ApiResult.Error -> {
|
||||
binding.continueListenTxt.hide()
|
||||
@@ -174,12 +212,12 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
|
||||
is ApiResult.Loading -> {}
|
||||
is ApiResult.Success -> {
|
||||
it.data?.result?.filterNotNull()?.let { continueData ->
|
||||
if (continueData.isNotEmpty()) {
|
||||
it.data?.let { continueList ->
|
||||
if (continueList.isNotEmpty()) {
|
||||
binding.continueListenTxt.show()
|
||||
binding.rvContinueListen.show()
|
||||
|
||||
continueAudioAdapter.submitList(continueData.toMutableList())
|
||||
continueAudioAdapter.submitList(continueList)
|
||||
} else {
|
||||
binding.continueListenTxt.hide()
|
||||
binding.rvContinueListen.hide()
|
||||
@@ -201,7 +239,7 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
|
||||
audioBookData.content_more_details?.let { moreDetailsList ->
|
||||
trailerName.text = if (moreDetailsList.isNotEmpty()) {
|
||||
if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
|
||||
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
|
||||
moreDetailsList[1]?.title
|
||||
} else {
|
||||
moreDetailsList[0]?.title
|
||||
@@ -238,18 +276,32 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
showAudioDialog(AudioBookData(audioBookData))
|
||||
}
|
||||
|
||||
private fun onBookChanged(id: Int) {
|
||||
private fun onBookChanged(id: Int, audioBookData: AudioBookData) {
|
||||
// updating continue book list
|
||||
val position = continueAudioAdapter.currentList.indexOfFirst { it.id == id }
|
||||
if (position >= 0 && position < continueAudioAdapter.currentList.size) {
|
||||
val currKaraoke = continueAudioAdapter.currentList[position]
|
||||
|
||||
// updating possible data changes
|
||||
currKaraoke.is_liked = audioBookData.is_liked
|
||||
currKaraoke.likes_count = audioBookData.likes_count
|
||||
currKaraoke.mark_as_favourite = audioBookData.mark_as_favourite
|
||||
|
||||
continueAudioAdapter.notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onContinueBookChanged(id: Int) {
|
||||
private fun onContinueBookChanged(id: Int, audioBookData: AudioBookData) {
|
||||
// updating book list
|
||||
val position = audioBookAdapter.currentList.indexOfFirst { it.id == id }
|
||||
if (position >= 0 && position < audioBookAdapter.currentList.size) {
|
||||
val currKaraoke = audioBookAdapter.currentList[position]
|
||||
|
||||
// updating possible data changes
|
||||
currKaraoke.is_liked = audioBookData.is_liked
|
||||
currKaraoke.likes_count = audioBookData.likes_count
|
||||
currKaraoke.mark_as_favourite = audioBookData.mark_as_favourite
|
||||
|
||||
audioBookAdapter.notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
@@ -323,7 +375,7 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
|
||||
if (moreDetailsList.isNotEmpty()) {
|
||||
|
||||
if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
|
||||
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
|
||||
moreDetailsList[1]?.let { data ->
|
||||
title.text = data.title
|
||||
description.text = Html.fromHtml(
|
||||
@@ -364,9 +416,21 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
!like.isSelected
|
||||
)
|
||||
|
||||
audioBookData.likes_count?.let {
|
||||
audioBookData.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
audioBookData.is_liked = !like.isSelected
|
||||
|
||||
audioBookData.id?.let {
|
||||
onBookChanged(it)
|
||||
onContinueBookChanged(it)
|
||||
onBookChanged(it, audioBookData)
|
||||
onContinueBookChanged(it, audioBookData)
|
||||
}
|
||||
like.isSelected = !like.isSelected
|
||||
likeCount.text = "${audioBookData.likes_count}"
|
||||
@@ -378,9 +442,11 @@ class AudioBooksActivity : WokaBaseActivity() {
|
||||
!fav.isSelected
|
||||
)
|
||||
|
||||
audioBookData.mark_as_favourite = !fav.isSelected
|
||||
|
||||
audioBookData.id?.let {
|
||||
onBookChanged(it)
|
||||
onContinueBookChanged(it)
|
||||
onBookChanged(it, audioBookData)
|
||||
onContinueBookChanged(it, audioBookData)
|
||||
}
|
||||
|
||||
fav.isSelected = !fav.isSelected
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.woka.userPreference.UserType
|
||||
import com.woka.utils.hide
|
||||
import com.woka.utils.scaleAnimate
|
||||
import com.woka.utils.show
|
||||
import com.woka.webseries.views.SeriesActivity
|
||||
import com.woka.webseries.views.WebSeriesActivity
|
||||
import com.woka.wokagames.views.GamesActivity
|
||||
|
||||
@@ -179,7 +180,7 @@ class Home1Fragment : Fragment(), Listener {
|
||||
|
||||
webSeries.setOnClickListener {
|
||||
activity?.let {
|
||||
startActivity(Intent(it, WebSeriesActivity::class.java))
|
||||
startActivity(Intent(it, SeriesActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -464,6 +464,7 @@ class HomeActivity : WokaBaseActivity(),
|
||||
.replace(R.id.fc_home, Home2Fragment.newInstance())
|
||||
.runOnCommit {
|
||||
binding.notifications.show()
|
||||
binding.notiCountView.show()
|
||||
window.lightStatusBar()
|
||||
|
||||
binding.root.backgroundTintList = ColorStateList.valueOf(getColor(R.color.color_primary))
|
||||
@@ -477,6 +478,7 @@ class HomeActivity : WokaBaseActivity(),
|
||||
.replace(R.id.fc_home, Home1Fragment.newInstance())
|
||||
.runOnCommit {
|
||||
binding.notifications.show()
|
||||
binding.notiCountView.show()
|
||||
window.lightStatusBar()
|
||||
|
||||
binding.root.backgroundTintList = null
|
||||
@@ -508,6 +510,7 @@ class HomeActivity : WokaBaseActivity(),
|
||||
|
||||
window.lightStatusBar()
|
||||
binding.notifications.hide()
|
||||
binding.notiCountView.hide()
|
||||
binding.root.background = ContextCompat.getDrawable(this, R.drawable.grad_my_list)
|
||||
}
|
||||
.commit()
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.woka.karaoke
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.woka.home.mylist.MyListRepository
|
||||
import com.woka.home.mylist.models.FavKaraokeData
|
||||
import com.woka.home.mylist.models.PostType
|
||||
@@ -24,13 +22,6 @@ object KaraokeRepository {
|
||||
private val apiService = RetrofitHelper.getRetrofit().create(KaraokeApiService::class.java)
|
||||
private val userActionApiService = RetrofitHelper.getRetrofit().create(UserActionApiService::class.java)
|
||||
|
||||
// continue sing karaoke data
|
||||
private val _continueKaraokeLiveData = MutableLiveData<ApiResult<ContinueKaraokeResponse>>()
|
||||
val continueKaraokeLiveData: LiveData<ApiResult<ContinueKaraokeResponse>>
|
||||
get() = _continueKaraokeLiveData
|
||||
|
||||
private var continueKaraokeData: ContinueKaraokeResponse? = null
|
||||
|
||||
suspend fun loadKaraokeSongs(pageNo: Int, quantity: Int): ApiResult<KaraokeResponse> {
|
||||
return handleApiCall {
|
||||
apiService.karaokeListing(
|
||||
@@ -43,32 +34,13 @@ object KaraokeRepository {
|
||||
}
|
||||
}
|
||||
|
||||
fun loadContinueKaraoke(){
|
||||
if (continueKaraokeData != null){
|
||||
_continueKaraokeLiveData.postValue(ApiResult.Success(continueKaraokeData))
|
||||
return
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val response = handleApiCall {
|
||||
userActionApiService.continueKaraokeListing(
|
||||
FormBody.Builder()
|
||||
.add("post_type", PostType.KARAOKE.value)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
when (response){
|
||||
is ApiResult.Error -> {}
|
||||
is ApiResult.Loading -> {}
|
||||
is ApiResult.Success -> {
|
||||
response.data?.let {
|
||||
continueKaraokeData = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_continueKaraokeLiveData.postValue(response)
|
||||
suspend fun loadContinueLiveData(): ApiResult<ContinueKaraokeResponse> {
|
||||
return handleApiCall {
|
||||
userActionApiService.continueKaraokeListing(
|
||||
FormBody.Builder()
|
||||
.add("post_type", PostType.KARAOKE.value)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,28 +69,6 @@ object KaraokeRepository {
|
||||
}
|
||||
|
||||
private fun changeLikeLocally(id: String, isLiked: Boolean){
|
||||
|
||||
// continue Karaoke data update
|
||||
continueKaraokeData?.result?.let {
|
||||
for (audio in it){
|
||||
var found = false
|
||||
audio?.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
|
||||
}
|
||||
}
|
||||
|
||||
// changing in fav list locally
|
||||
MyListRepository.myFavData.result?.sing_karaoke_data?.let {
|
||||
for (audioData in it){
|
||||
@@ -164,18 +114,5 @@ object KaraokeRepository {
|
||||
}
|
||||
}
|
||||
|
||||
continueKaraokeData?.result?.let {
|
||||
for (audio in it){
|
||||
if (audio?.id == karaoke.id){
|
||||
audio?.mark_as_favourite = addToBookmark
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun clearData(){
|
||||
continueKaraokeData = null
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,12 @@ import com.woka.utils.isNetworkConnected
|
||||
import com.woka.utils.show
|
||||
import com.woka.utils.toast
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.max
|
||||
|
||||
class ContinueKaraokeAdapter(
|
||||
private val context: Context,
|
||||
private var onKaraokeClicked: (KaraokeData) -> Unit,
|
||||
private var onKaraokeChanged: (id: Int, isFromContinue: Boolean) -> Unit
|
||||
private var onKaraokeChanged: (id: Int, isFromContinue: Boolean, KaraokeData) -> Unit
|
||||
): ListAdapter<ContinueKaraokeData, ContinueKaraokeAdapter.AudioBookViewHolder>(ASYNC_DIFF_UTIL) {
|
||||
|
||||
inner class AudioBookViewHolder(val binding: FavViewHolderBinding): ViewHolder(binding.root)
|
||||
@@ -97,6 +98,18 @@ class ContinueKaraokeAdapter(
|
||||
!like.isSelected
|
||||
)
|
||||
|
||||
karaokeData.likes_count?.let {
|
||||
karaokeData.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
karaokeData.is_liked = !like.isSelected
|
||||
|
||||
karaokeData.likes_count?.let {
|
||||
likeCount.text = "$it"
|
||||
}
|
||||
@@ -105,7 +118,7 @@ class ContinueKaraokeAdapter(
|
||||
like.isSelected = it
|
||||
}
|
||||
|
||||
karaokeData.id?.let{onKaraokeChanged(it, true)}
|
||||
karaokeData.id?.let{onKaraokeChanged(it, true, KaraokeData(karaokeData))}
|
||||
}
|
||||
|
||||
fav.isSelected = karaokeData.mark_as_favourite == true
|
||||
@@ -121,9 +134,11 @@ class ContinueKaraokeAdapter(
|
||||
!fav.isSelected
|
||||
)
|
||||
|
||||
karaokeData.mark_as_favourite = !fav.isSelected
|
||||
|
||||
fav.isSelected = karaokeData.mark_as_favourite == true
|
||||
|
||||
karaokeData.id?.let{onKaraokeChanged(it, true)}
|
||||
karaokeData.id?.let{onKaraokeChanged(it, true, KaraokeData(karaokeData))}
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
|
||||
@@ -16,11 +16,12 @@ import com.woka.utils.isNetworkConnected
|
||||
import com.woka.utils.show
|
||||
import com.woka.utils.toast
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.max
|
||||
|
||||
class KaraokeAdapter(
|
||||
private val context: Context,
|
||||
private var onKaraokeClicked: (KaraokeData) -> Unit,
|
||||
private var onKaraokeChanged: (id: Int, isContinue: Boolean) -> Unit
|
||||
private var onKaraokeChanged: (id: Int, isContinue: Boolean, KaraokeData) -> Unit
|
||||
): ListAdapter<KaraokeData, KaraokeAdapter.AudioBookViewHolder>(ASYNC_DIFF_UTIL) {
|
||||
|
||||
inner class AudioBookViewHolder(val binding: ShowViewHolderBinding): ViewHolder(binding.root)
|
||||
@@ -96,6 +97,18 @@ class KaraokeAdapter(
|
||||
!like.isSelected
|
||||
)
|
||||
|
||||
karaokeData.likes_count?.let {
|
||||
karaokeData.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
karaokeData.is_liked = !like.isSelected
|
||||
|
||||
karaokeData.likes_count?.let {
|
||||
likeCount.text = "$it"
|
||||
}
|
||||
@@ -104,7 +117,7 @@ class KaraokeAdapter(
|
||||
like.isSelected = it
|
||||
}
|
||||
|
||||
karaokeData.id?.let{onKaraokeChanged(it, false)}
|
||||
karaokeData.id?.let{onKaraokeChanged(it, false, karaokeData)}
|
||||
}
|
||||
|
||||
fav.isSelected = karaokeData.mark_as_favourite == true
|
||||
@@ -120,9 +133,11 @@ class KaraokeAdapter(
|
||||
!fav.isSelected
|
||||
)
|
||||
|
||||
karaokeData.mark_as_favourite = !fav.isSelected
|
||||
|
||||
fav.isSelected = karaokeData.mark_as_favourite == true
|
||||
|
||||
karaokeData.id?.let{onKaraokeChanged(it, false)}
|
||||
karaokeData.id?.let{onKaraokeChanged(it, false, karaokeData)}
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
|
||||
@@ -5,28 +5,73 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.woka.karaoke.KaraokeRepository
|
||||
import com.woka.karaoke.models.continuesing.ContinueKaraokeData
|
||||
import com.woka.karaoke.models.listing.KaraokeData
|
||||
import com.woka.networking.ApiResult
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class KaraokeViewModel: ViewModel() {
|
||||
class KaraokeViewModel : ViewModel() {
|
||||
|
||||
private val _karaokeLiveData = MutableLiveData<ApiResult<List<KaraokeData>>>()
|
||||
val karaokeLiveData: LiveData<ApiResult<List<KaraokeData>>>
|
||||
private val _karaokeLiveData = MutableLiveData<ApiResult<MutableList<KaraokeData>>>()
|
||||
val karaokeLiveData: LiveData<ApiResult<MutableList<KaraokeData>>>
|
||||
get() = _karaokeLiveData
|
||||
|
||||
var nextPageToLoad: Int = 0
|
||||
private var quantityPerPage: Int = 10
|
||||
private var lastPage = false
|
||||
private val karaokeList = ArrayList<KaraokeData>()
|
||||
|
||||
fun loadKaraokeSongs(currentItemCount: Int){
|
||||
private var nextPageToLoad: Int = 0
|
||||
private var quantityPerPage: Int = 6
|
||||
var lastPage = false
|
||||
|
||||
private val _karaokeContinueLiveData = MutableLiveData<ApiResult<List<ContinueKaraokeData>>>()
|
||||
val karaokeContinueLiveData: LiveData<ApiResult<List<ContinueKaraokeData>>>
|
||||
get() = _karaokeContinueLiveData
|
||||
|
||||
fun loadKaraokeSongs() {
|
||||
viewModelScope.launch {
|
||||
when (val value = KaraokeRepository.loadKaraokeSongs(nextPageToLoad, quantityPerPage)){
|
||||
is ApiResult.Error -> _karaokeLiveData.postValue(ApiResult.Error(value.errorMessage, value.error))
|
||||
_karaokeLiveData.postValue(ApiResult.Loading())
|
||||
when (val value = KaraokeRepository.loadKaraokeSongs(nextPageToLoad, quantityPerPage)) {
|
||||
is ApiResult.Error -> _karaokeLiveData.postValue(
|
||||
ApiResult.Error(
|
||||
value.errorMessage,
|
||||
value.error
|
||||
)
|
||||
)
|
||||
|
||||
is ApiResult.Loading -> _karaokeLiveData.postValue(ApiResult.Loading())
|
||||
is ApiResult.Success -> {
|
||||
value.data?.karaoke_data?.filterNotNull()?.let {
|
||||
value.data?.let {
|
||||
it.karaoke_data?.filterNotNull()?.let { newList ->
|
||||
karaokeList.addAll(newList)
|
||||
|
||||
lastPage = karaokeList.size == it.total_records
|
||||
|
||||
_karaokeLiveData.postValue(ApiResult.Success(karaokeList))
|
||||
nextPageToLoad++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadContinueData(){
|
||||
viewModelScope.launch {
|
||||
_karaokeContinueLiveData.postValue(ApiResult.Loading())
|
||||
|
||||
when (val value = KaraokeRepository.loadContinueLiveData()) {
|
||||
is ApiResult.Error -> _karaokeContinueLiveData.postValue(
|
||||
ApiResult.Error(
|
||||
value.errorMessage,
|
||||
value.error
|
||||
)
|
||||
)
|
||||
|
||||
is ApiResult.Loading -> _karaokeContinueLiveData.postValue(ApiResult.Loading())
|
||||
is ApiResult.Success -> {
|
||||
value.data?.let {
|
||||
it.result?.filterNotNull()?.let { newList ->
|
||||
_karaokeContinueLiveData.postValue(ApiResult.Success(newList))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.woka.R
|
||||
import com.woka.WokaApp
|
||||
import com.woka.WokaApp.Companion.userPrefs
|
||||
import com.woka.databinding.ActivityKaraokeBinding
|
||||
import com.woka.databinding.DialogModuleShowerBinding
|
||||
@@ -37,6 +36,7 @@ import com.woka.utils.show
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
|
||||
class KaraokeActivity : WokaBaseActivity() {
|
||||
|
||||
@@ -83,6 +83,10 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
clickEvents()
|
||||
|
||||
setObservers()
|
||||
|
||||
if (!viewModel.karaokeLiveData.isInitialized){
|
||||
viewModel.loadKaraokeSongs()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
@@ -132,11 +136,11 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
}
|
||||
|
||||
retryBtn.setOnClickListener {
|
||||
|
||||
viewModel.loadKaraokeSongs()
|
||||
}
|
||||
|
||||
loadMoreBtn.setOnClickListener {
|
||||
|
||||
viewModel.loadKaraokeSongs()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,7 +156,7 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
|
||||
karaokeData.content_more_details?.let { moreDetailsList ->
|
||||
trailerName.text = if (moreDetailsList.isNotEmpty()) {
|
||||
if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
|
||||
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
|
||||
moreDetailsList[1]?.title
|
||||
} else {
|
||||
moreDetailsList[0]?.title
|
||||
@@ -214,7 +218,7 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
|
||||
if (moreDetailsList.isNotEmpty()) {
|
||||
|
||||
if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
|
||||
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) {
|
||||
moreDetailsList[1]?.let { data ->
|
||||
title.text = data.title
|
||||
description.text = Html.fromHtml(
|
||||
@@ -255,9 +259,21 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
!like.isSelected
|
||||
)
|
||||
|
||||
karaokeData.likes_count?.let {
|
||||
karaokeData.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
karaokeData.is_liked = !like.isSelected
|
||||
|
||||
karaokeData.id?.let {
|
||||
onKaraokeChanged(it, false)
|
||||
onKaraokeChanged(it, true)
|
||||
onKaraokeChanged(it, false, karaokeData)
|
||||
onKaraokeChanged(it, true, karaokeData)
|
||||
}
|
||||
like.isSelected = !like.isSelected
|
||||
likeCount.text = "${karaokeData.likes_count}"
|
||||
@@ -269,9 +285,11 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
!fav.isSelected
|
||||
)
|
||||
|
||||
karaokeData.mark_as_favourite = !fav.isSelected
|
||||
|
||||
karaokeData.id?.let {
|
||||
onKaraokeChanged(it, false)
|
||||
onKaraokeChanged(it, true)
|
||||
onKaraokeChanged(it, false, karaokeData)
|
||||
onKaraokeChanged(it, true, karaokeData)
|
||||
}
|
||||
|
||||
fav.isSelected = !fav.isSelected
|
||||
@@ -286,17 +304,31 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun onKaraokeChanged(id: Int, isFromContinue: Boolean) {
|
||||
private fun onKaraokeChanged(id: Int, isFromContinue: Boolean, karaokeData: KaraokeData) {
|
||||
if (isFromContinue) {
|
||||
// updating karaoke list
|
||||
val position = karaokeAdapter.currentList.indexOfFirst { it.id == id }
|
||||
if (position >= 0 && position < karaokeAdapter.currentList.size) {
|
||||
val currKaraoke = karaokeAdapter.currentList[position]
|
||||
|
||||
// updating possible data changes
|
||||
currKaraoke.is_liked = karaokeData.is_liked
|
||||
currKaraoke.likes_count = karaokeData.likes_count
|
||||
currKaraoke.mark_as_favourite = karaokeData.mark_as_favourite
|
||||
|
||||
karaokeAdapter.notifyItemChanged(position)
|
||||
}
|
||||
} else {
|
||||
// updating continue karaoke list
|
||||
val continuePos = continueKaraokeAdapter.currentList.indexOfFirst { it.id == id }
|
||||
if (continuePos >= 0 && continuePos < continueKaraokeAdapter.currentList.size) {
|
||||
val currKaraoke = continueKaraokeAdapter.currentList[continuePos]
|
||||
|
||||
// updating possible data changes
|
||||
currKaraoke.is_liked = karaokeData.is_liked
|
||||
currKaraoke.likes_count = karaokeData.likes_count
|
||||
currKaraoke.mark_as_favourite = karaokeData.mark_as_favourite
|
||||
|
||||
continueKaraokeAdapter.notifyItemChanged(continuePos)
|
||||
}
|
||||
}
|
||||
@@ -308,67 +340,71 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
}
|
||||
|
||||
private fun setObservers() {
|
||||
// viewModel.karaokeLiveData.observe(this) {
|
||||
// when (it) {
|
||||
// is ApiResult.Error -> {
|
||||
// binding.shimmer.hide()
|
||||
// if (karaokeAdapter.currentList.size == 0) {
|
||||
// binding.rvKaraoke.hide()
|
||||
// binding.singTxt.hide()
|
||||
// binding.trailerView.hide()
|
||||
//
|
||||
// binding.errorView.show()
|
||||
// } else {
|
||||
// // error in loading more
|
||||
// binding.loadMoreProgress.hide()
|
||||
// binding.loadMoreBtn.show()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// is ApiResult.Loading -> {
|
||||
// if (karaokeAdapter.currentList.size == 0) {
|
||||
// binding.shimmer.show()
|
||||
//
|
||||
// binding.rvKaraoke.hide()
|
||||
// binding.singTxt.hide()
|
||||
// binding.trailerView.hide()
|
||||
// binding.errorView.hide()
|
||||
// } else {
|
||||
// // error in loading more
|
||||
// binding.loadMoreProgress.show()
|
||||
// binding.loadMoreBtn.hide()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// is ApiResult.Success -> {
|
||||
// it.data?.let { data ->
|
||||
// binding.loadMoreProgress.hide()
|
||||
//
|
||||
// data.karaoke_data?.filterNotNull()?.let { karaokeData ->
|
||||
// if (karaokeData.isNotEmpty()) {
|
||||
// binding.shimmer.hide()
|
||||
// binding.errorView.hide()
|
||||
//
|
||||
// if (userPrefs?.userType != UserType.GUEST) {
|
||||
// KaraokeRepository.loadContinueKaraoke()
|
||||
// }
|
||||
//
|
||||
// loadTrailerData(karaokeData[0])
|
||||
// binding.rvKaraoke.show()
|
||||
// binding.singTxt.show()
|
||||
//
|
||||
// karaokeAdapter.submitList(karaokeData)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// binding.loadMoreBtn.setVisibility(karaokeAdapter.currentList.size != data.total_records)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
viewModel.karaokeLiveData.observe(this){
|
||||
when(it){
|
||||
is ApiResult.Error -> {
|
||||
binding.shimmer.hide()
|
||||
if (karaokeAdapter.currentList.size == 0) {
|
||||
// none of the data is yet loaded
|
||||
binding.rvKaraoke.hide()
|
||||
binding.singTxt.hide()
|
||||
binding.trailerView.hide()
|
||||
|
||||
KaraokeRepository.continueKaraokeLiveData.observe(this) {
|
||||
binding.errorView.show()
|
||||
} else {
|
||||
// error in loading more
|
||||
binding.loadMoreProgress.hide()
|
||||
binding.loadMoreBtn.text = getString(R.string.retry)
|
||||
binding.loadMoreBtn.show()
|
||||
}
|
||||
}
|
||||
is ApiResult.Loading -> {
|
||||
if (karaokeAdapter.currentList.size == 0) {
|
||||
// loading first data
|
||||
binding.shimmer.show()
|
||||
binding.errorView.hide()
|
||||
} else {
|
||||
// loading more data
|
||||
binding.loadMoreProgress.show()
|
||||
binding.loadMoreBtn.hide()
|
||||
}
|
||||
}
|
||||
is ApiResult.Success -> {
|
||||
it.data?.let {newList ->
|
||||
if (newList.isNotEmpty()){
|
||||
binding.rvKaraoke.show()
|
||||
binding.singTxt.show()
|
||||
binding.trailerView.show()
|
||||
|
||||
binding.errorView.hide()
|
||||
binding.shimmer.hide()
|
||||
|
||||
binding.loadMoreProgress.hide()
|
||||
binding.loadMoreBtn.text = getString(R.string.load_more)
|
||||
binding.loadMoreBtn.setVisibility(!viewModel.lastPage)
|
||||
|
||||
if (karaokeAdapter.currentList.isEmpty()){
|
||||
// first data load
|
||||
if (userPrefs?.userType != UserType.GUEST && !viewModel.karaokeContinueLiveData.isInitialized){
|
||||
viewModel.loadContinueData()
|
||||
}
|
||||
|
||||
loadTrailerData(newList[0])
|
||||
karaokeAdapter.submitList(newList)
|
||||
}else{
|
||||
// loaded more data
|
||||
karaokeAdapter.notifyItemRangeInserted(
|
||||
karaokeAdapter.currentList.size,
|
||||
newList.size
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.karaokeContinueLiveData.observe(this) {
|
||||
when (it) {
|
||||
is ApiResult.Error -> {
|
||||
binding.continueSingTxt.hide()
|
||||
@@ -377,12 +413,12 @@ class KaraokeActivity : WokaBaseActivity() {
|
||||
|
||||
is ApiResult.Loading -> {}
|
||||
is ApiResult.Success -> {
|
||||
it.data?.result?.filterNotNull()?.let { continueData ->
|
||||
if (continueData.isNotEmpty()) {
|
||||
it.data?.let { continueList ->
|
||||
if (continueList.isNotEmpty()) {
|
||||
binding.continueSingTxt.show()
|
||||
binding.rvContinueSing.show()
|
||||
|
||||
continueKaraokeAdapter.submitList(continueData)
|
||||
continueKaraokeAdapter.submitList(continueList)
|
||||
} else {
|
||||
binding.continueSingTxt.hide()
|
||||
binding.rvContinueSing.hide()
|
||||
|
||||
@@ -117,7 +117,5 @@ class UserPreference(val context: Context) {
|
||||
private fun clearData(){
|
||||
WebSeriesRepository.clearData()
|
||||
MyListRepository.clearData()
|
||||
AudioBookRepository.clearData()
|
||||
KaraokeRepository.clearData()
|
||||
}
|
||||
}
|
||||
189
app/src/main/java/com/woka/webseries/Repository.kt
Normal file
189
app/src/main/java/com/woka/webseries/Repository.kt
Normal file
@@ -0,0 +1,189 @@
|
||||
package com.woka.webseries
|
||||
|
||||
import com.woka.home.mylist.MyListRepository
|
||||
import com.woka.home.mylist.models.BookmarkedShowData
|
||||
import com.woka.home.mylist.models.PostType
|
||||
import com.woka.modules.categorymodels.CategoriesResponse
|
||||
import com.woka.networking.ApiResult
|
||||
import com.woka.networking.RetrofitHelper
|
||||
import com.woka.networking.RetrofitHelper.handleApiCall
|
||||
import com.woka.userdata.UserActionApiService
|
||||
import com.woka.webseries.models.ContinueEpisodeResponse
|
||||
import com.woka.webseries.models.ShowData
|
||||
import com.woka.webseries.models.WebSeriesResponse
|
||||
import com.woka.webseries.models.episodedata.EpisodeResponseData
|
||||
import com.woka.webseries.models.seasondata.SeasonDataResponse
|
||||
import com.woka.webseries.models.teaserdata.TeaserResponseData
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.FormBody
|
||||
import kotlin.math.max
|
||||
|
||||
object Repository {
|
||||
// api services
|
||||
private val apiService = RetrofitHelper.getRetrofit().create(WebSeriesApiService::class.java)
|
||||
private val userActionApiService = RetrofitHelper.getRetrofit().create(UserActionApiService::class.java)
|
||||
|
||||
suspend fun loadCategories(): ApiResult<CategoriesResponse> {
|
||||
return handleApiCall {
|
||||
apiService.categoryListing(
|
||||
FormBody.Builder()
|
||||
.add("module_id", "7")
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadTeaserData(showId: Int, seasonId: Int): ApiResult<TeaserResponseData> {
|
||||
return handleApiCall{
|
||||
apiService.teaserListing(
|
||||
FormBody.Builder()
|
||||
.add("watch_show_master_id", "$showId")
|
||||
.add("season_master_id", "$seasonId")
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadEpisodeData(showId: Int, seasonId: Int): ApiResult<EpisodeResponseData> {
|
||||
return handleApiCall {
|
||||
apiService.episodeListing(
|
||||
FormBody.Builder()
|
||||
.add("watch_show_master_id", "$showId")
|
||||
.add("season_master_id", "$seasonId")
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadSeasonListing(showId: Int, categoryId: String): ApiResult<SeasonDataResponse> {
|
||||
return handleApiCall {
|
||||
apiService.seasonListing(
|
||||
FormBody.Builder()
|
||||
.add("watch_show_id", "$showId")
|
||||
.add("category_id", categoryId)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadContinueWatchData(): ApiResult<ContinueEpisodeResponse> {
|
||||
return handleApiCall {
|
||||
userActionApiService.continueWatchingShowListing(
|
||||
FormBody.Builder()
|
||||
.add("post_type", "3")
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadWebSeries(categoryId: String, pageNo: Int, quantity: Int): ApiResult<WebSeriesResponse> {
|
||||
return handleApiCall {
|
||||
apiService.getWebSeries(
|
||||
FormBody.Builder()
|
||||
.add("category_id", categoryId)
|
||||
.add("api_version", "v2")
|
||||
.add("start", "$pageNo")
|
||||
.add("limit", "$quantity")
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun likeUnLikeShow(postId: String, likeIt: Boolean){
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
handleApiCall {
|
||||
if (likeIt){
|
||||
userActionApiService.likePost(
|
||||
FormBody.Builder()
|
||||
.add("post_id", postId)
|
||||
.add("post_type", PostType.WEB_SERIES.value)
|
||||
.build()
|
||||
)
|
||||
}else{
|
||||
userActionApiService.unLikePost(
|
||||
FormBody.Builder()
|
||||
.add("post_id", postId)
|
||||
.add("post_type", PostType.WEB_SERIES.value)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changeLikeLocally(postId, likeIt)
|
||||
}
|
||||
|
||||
private fun changeLikeLocally(id: String, isLiked: Boolean){
|
||||
// changing in fav list locally
|
||||
MyListRepository.myFavData.result?.show_data?.let {
|
||||
for (show in it){
|
||||
if ("${show.id}" == id){
|
||||
show.is_liked = isLiked
|
||||
show.likes_count?.let { count ->
|
||||
show.likes_count = if (isLiked) count + 1
|
||||
else max(0, count - 1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFavShow(showData: ShowData, addToBookmark: Boolean, categoryId: String){
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
handleApiCall {
|
||||
if (addToBookmark){
|
||||
userActionApiService.addToFav(
|
||||
FormBody.Builder()
|
||||
.add("post_id", "${showData.id}")
|
||||
.add("post_type", PostType.WEB_SERIES.value)
|
||||
.add("category_id", categoryId)
|
||||
.build()
|
||||
)
|
||||
}else{
|
||||
userActionApiService.removeFromFav(
|
||||
FormBody.Builder()
|
||||
.add("id", "${showData.id}")
|
||||
.add("post_type", PostType.WEB_SERIES.value)
|
||||
.add("category_id", categoryId)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MyListRepository.myFavData.result?.show_data?.let {
|
||||
var found = false
|
||||
var showFound: BookmarkedShowData? = null
|
||||
|
||||
for (show in it){
|
||||
if (showData.id == show.id){
|
||||
|
||||
if (addToBookmark){
|
||||
show.addAsBookMark(categoryId)
|
||||
}else{
|
||||
show.removeAsBookMark(categoryId)
|
||||
}
|
||||
|
||||
showFound = show
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && addToBookmark){
|
||||
MyListRepository.myFavData.result?.show_data?.add(BookmarkedShowData(showData, categoryId))
|
||||
}
|
||||
|
||||
if (found){
|
||||
showFound?.let {bookMarkShowData ->
|
||||
if (!bookMarkShowData.isBookMarked()){
|
||||
it.remove(bookMarkShowData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.woka.webseries
|
||||
|
||||
import com.woka.modules.categorymodels.CategoriesResponse
|
||||
import com.woka.networking.ApiResponse
|
||||
import com.woka.webseries.models.WebSeriesResponse
|
||||
import com.woka.webseries.models.episodedata.EpisodeResponseData
|
||||
@@ -23,4 +24,7 @@ interface WebSeriesApiService {
|
||||
|
||||
@POST("teaser_listing")
|
||||
suspend fun teaserListing(@Body formBody: FormBody): Response<ApiResponse<TeaserResponseData>>
|
||||
|
||||
@POST("category_listing")
|
||||
suspend fun categoryListing(@Body body: FormBody): Response<ApiResponse<CategoriesResponse>>
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import com.woka.databinding.ShowViewHolderBinding
|
||||
import com.woka.utils.isNetworkConnected
|
||||
import com.woka.utils.show
|
||||
import com.woka.utils.toast
|
||||
import com.woka.webseries.WebSeriesRepository
|
||||
import com.woka.webseries.Repository
|
||||
import com.woka.webseries.models.ShowData
|
||||
import kotlin.math.max
|
||||
|
||||
@@ -84,12 +84,20 @@ class WebSeriesShowAdapter(
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (like.isSelected){
|
||||
WebSeriesRepository.likeUnLikeShow("${showData.id}", false)
|
||||
}else{
|
||||
WebSeriesRepository.likeUnLikeShow("${showData.id}", true)
|
||||
Repository.likeUnLikeShow("${showData.id}", !like.isSelected)
|
||||
|
||||
showData.likes_count?.let {
|
||||
showData.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
showData.is_liked = !like.isSelected
|
||||
|
||||
like.isSelected = !like.isSelected
|
||||
showData.likes_count?.let {
|
||||
likeCount.text = "$it"
|
||||
@@ -105,12 +113,14 @@ class WebSeriesShowAdapter(
|
||||
}
|
||||
|
||||
categoryId?.let {
|
||||
WebSeriesRepository.updateFavShow(
|
||||
Repository.updateFavShow(
|
||||
showData,
|
||||
!fav.isSelected,
|
||||
it
|
||||
)
|
||||
|
||||
showData.addAsBookMark(it)
|
||||
|
||||
fav.isSelected = !fav.isSelected
|
||||
}
|
||||
}
|
||||
|
||||
111
app/src/main/java/com/woka/webseries/viewmodel/ViewModel.kt
Normal file
111
app/src/main/java/com/woka/webseries/viewmodel/ViewModel.kt
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.woka.webseries.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.woka.modules.categorymodels.CategoriesResponse
|
||||
import com.woka.networking.ApiResult
|
||||
import com.woka.utils.TAG
|
||||
import com.woka.webseries.Repository
|
||||
import com.woka.webseries.models.ContinueEpisodeResponse
|
||||
import com.woka.webseries.models.ShowData
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ViewModel : ViewModel() {
|
||||
|
||||
private val repository = Repository
|
||||
|
||||
// categories listing
|
||||
data class PagingData(
|
||||
var nextPageToLoad: Int = 0, var quantityPerPage: Int = 6,
|
||||
var lastPage: Boolean = false
|
||||
)
|
||||
|
||||
private val _showCategoryLiveData = MutableLiveData<ApiResult<CategoriesResponse>>()
|
||||
val showCategoryLiveData: LiveData<ApiResult<CategoriesResponse>>
|
||||
get() = _showCategoryLiveData
|
||||
|
||||
var categoryPagingData = HashMap<String, PagingData>()
|
||||
|
||||
fun loadCategories() {
|
||||
viewModelScope.launch {
|
||||
_showCategoryLiveData.postValue(ApiResult.Loading())
|
||||
_showCategoryLiveData.postValue(repository.loadCategories())
|
||||
}
|
||||
}
|
||||
|
||||
// continue watching
|
||||
private val _continueWatchLiveData = MutableLiveData<ApiResult<ContinueEpisodeResponse>>()
|
||||
val continueWatchLiveData: LiveData<ApiResult<ContinueEpisodeResponse>>
|
||||
get() = _continueWatchLiveData
|
||||
|
||||
fun loadContinueWatching() {
|
||||
viewModelScope.launch {
|
||||
_continueWatchLiveData.postValue(ApiResult.Loading())
|
||||
_continueWatchLiveData.postValue(repository.loadContinueWatchData())
|
||||
}
|
||||
}
|
||||
|
||||
// show data
|
||||
private val _webSeriesLiveData =
|
||||
MutableLiveData<ApiResult<MutableList<ShowData>>>()
|
||||
val webSeriesLiveData: LiveData<ApiResult<MutableList<ShowData>>>
|
||||
get() = _webSeriesLiveData
|
||||
|
||||
// map of series data where key is category
|
||||
private var webSeriesData = HashMap<String, MutableList<ShowData>>()
|
||||
|
||||
fun loadWebSeries(categoryId: String){
|
||||
if (webSeriesData.containsKey(categoryId) && webSeriesData[categoryId]?.isNotEmpty() == true){
|
||||
_webSeriesLiveData.postValue(ApiResult.Success(webSeriesData[categoryId]))
|
||||
}else{
|
||||
loadMoreWebSeries(categoryId)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadMoreWebSeries(categoryId: String) {
|
||||
viewModelScope.launch {
|
||||
_webSeriesLiveData.postValue(ApiResult.Loading())
|
||||
|
||||
val pagingData = if (categoryPagingData.containsKey(categoryId)){
|
||||
categoryPagingData[categoryId]!!
|
||||
}else{
|
||||
val pagingData = PagingData()
|
||||
categoryPagingData[categoryId] = pagingData
|
||||
pagingData
|
||||
}
|
||||
|
||||
when (val response = repository.loadWebSeries(categoryId, pagingData.nextPageToLoad, pagingData.quantityPerPage)) {
|
||||
is ApiResult.Error -> {
|
||||
_webSeriesLiveData.postValue(
|
||||
ApiResult.Error(
|
||||
response.errorMessage,
|
||||
response.error
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is ApiResult.Loading -> {
|
||||
_webSeriesLiveData.postValue(ApiResult.Loading())
|
||||
}
|
||||
|
||||
is ApiResult.Success -> {
|
||||
response.data?.let { data ->
|
||||
data.show_data?.filterNotNull()?.let { newList ->
|
||||
val currentList = webSeriesData.getOrDefault(categoryId, ArrayList())
|
||||
currentList.addAll(newList)
|
||||
webSeriesData[categoryId] = currentList
|
||||
|
||||
pagingData.lastPage = webSeriesData[categoryId]?.size == data.total_records
|
||||
|
||||
_webSeriesLiveData.postValue(ApiResult.Success(webSeriesData[categoryId]))
|
||||
pagingData.nextPageToLoad++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
app/src/main/java/com/woka/webseries/views/SeriesActivity.kt
Normal file
37
app/src/main/java/com/woka/webseries/views/SeriesActivity.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.woka.webseries.views
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.woka.R
|
||||
import com.woka.databinding.ActivitySeriesBinding
|
||||
import com.woka.utils.WokaBaseActivity
|
||||
import com.woka.webseries.views.fragments.WebSeriesFragment
|
||||
|
||||
class SeriesActivity : WokaBaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivitySeriesBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivitySeriesBinding.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
|
||||
}
|
||||
|
||||
window.navigationBarColor = getColor(R.color.color_primary_dark)
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(R.id.fcv_web_series, WebSeriesFragment.newInstance())
|
||||
.runOnCommit {
|
||||
window.setBackgroundDrawable(AppCompatResources.getDrawable(this, R.drawable.gradient_web_series))
|
||||
}
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
package com.woka.webseries.views.fragments
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.InsetDrawable
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.AdapterView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.jwplayer.pub.api.media.playlists.PlaylistItem
|
||||
import com.woka.R
|
||||
import com.woka.WokaApp.Companion.userPrefs
|
||||
import com.woka.databinding.DialogContinueEpisodeBinding
|
||||
import com.woka.databinding.FragmentWebSeriesBinding
|
||||
import com.woka.networking.ApiResult
|
||||
import com.woka.players.models.VideoPlayList
|
||||
import com.woka.players.models.VideoViewRequestData
|
||||
import com.woka.players.views.PlayerActivity
|
||||
import com.woka.players.views.PlayerActivity.Companion.EXTRA_PLAY_INDEX
|
||||
import com.woka.players.views.PlayerActivity.Companion.EXTRA_PLAY_LIST
|
||||
import com.woka.userPreference.UserType
|
||||
import com.woka.utils.hide
|
||||
import com.woka.utils.setVisibility
|
||||
import com.woka.utils.show
|
||||
import com.woka.webseries.adapters.ContinueEpisodeAdapter
|
||||
import com.woka.webseries.adapters.SpinnerAdapter
|
||||
import com.woka.webseries.adapters.WebSeriesShowAdapter
|
||||
import com.woka.webseries.models.ContinueEpisodeData
|
||||
import com.woka.webseries.models.ShowData
|
||||
import com.woka.webseries.viewmodel.ViewModel
|
||||
import java.util.Collections
|
||||
|
||||
class WebSeriesFragment private constructor(): Fragment() {
|
||||
|
||||
private lateinit var binding: FragmentWebSeriesBinding
|
||||
|
||||
private lateinit var viewModel: ViewModel
|
||||
|
||||
private lateinit var showAdapter: WebSeriesShowAdapter
|
||||
|
||||
private lateinit var continueWatchAdapter: ContinueEpisodeAdapter
|
||||
|
||||
private var catSpinnerAdapter: SpinnerAdapter? = null
|
||||
|
||||
private lateinit var episodeDialogBinding: DialogContinueEpisodeBinding
|
||||
private lateinit var episodeDialog: Dialog
|
||||
|
||||
companion object {
|
||||
fun newInstance() = WebSeriesFragment()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentWebSeriesBinding.inflate(inflater, container, false)
|
||||
viewModel = ViewModelProvider(this)[ViewModel::class.java]
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
initViews()
|
||||
|
||||
initEpisodeDialog()
|
||||
|
||||
clickEvents()
|
||||
|
||||
setObservers()
|
||||
|
||||
if (!viewModel.showCategoryLiveData.isInitialized){
|
||||
viewModel.loadCategories()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clickEvents() {
|
||||
binding.apply {
|
||||
toolbar.backBtn.setOnClickListener {
|
||||
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||
}
|
||||
|
||||
retryBtn.setOnClickListener {
|
||||
binding.shimmer.show()
|
||||
binding.errorView.hide()
|
||||
|
||||
viewModel.loadCategories()
|
||||
}
|
||||
|
||||
loadMoreBtn.setOnClickListener {
|
||||
catSpinnerAdapter?.selectedCategoryType?.let {
|
||||
viewModel.loadMoreWebSeries(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun initViews(){
|
||||
|
||||
// adapters
|
||||
activity?.let {
|
||||
showAdapter = WebSeriesShowAdapter(it, ::onShowClicked)
|
||||
continueWatchAdapter = ContinueEpisodeAdapter(it)
|
||||
}
|
||||
|
||||
binding.apply {
|
||||
adjustMasilaImage()
|
||||
|
||||
toolbar.title.text = getString(R.string.web_series)
|
||||
|
||||
rvWebSeries.adapter = showAdapter
|
||||
rvContinueWatch.adapter = continueWatchAdapter
|
||||
continueWatchAdapter.onEpisodeClicked = ::onContinueEpisodeClicked
|
||||
|
||||
((rvWebSeries.itemAnimator) as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
((rvContinueWatch.itemAnimator) as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun adjustMasilaImage() {
|
||||
// making space for masila image
|
||||
binding.apply {
|
||||
topPinnedView.post {
|
||||
val imgParams = masilaImage.layoutParams as CollapsingToolbarLayout.LayoutParams
|
||||
imgParams.setMargins(0, 0, 0, topPinnedView.height)
|
||||
masilaImage.layoutParams = imgParams
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initEpisodeDialog(){
|
||||
episodeDialogBinding = DialogContinueEpisodeBinding.inflate(layoutInflater)
|
||||
episodeDialog = Dialog(requireContext())
|
||||
|
||||
episodeDialog.setContentView(episodeDialogBinding.root)
|
||||
|
||||
try {
|
||||
val back = ColorDrawable(Color.TRANSPARENT)
|
||||
val inset = InsetDrawable(back, 50)
|
||||
episodeDialog.window!!.setBackgroundDrawable(inset)
|
||||
} catch (e: Exception) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
try {
|
||||
val layoutParams = episodeDialog.window!!.attributes
|
||||
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT
|
||||
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
layoutParams.horizontalMargin = 0F
|
||||
episodeDialog.window!!.setAttributes(layoutParams)
|
||||
} catch (e: Exception) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
episodeDialogBinding.close.setOnClickListener { episodeDialog.dismiss() }
|
||||
}
|
||||
|
||||
private fun loadShowData(){
|
||||
catSpinnerAdapter?.selectedCategoryType?.let {
|
||||
showAdapter.submitListShowList(Collections.emptyList(), it)
|
||||
viewModel.loadWebSeries(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setObservers(){
|
||||
viewModel.showCategoryLiveData.observe(viewLifecycleOwner){
|
||||
when(it){
|
||||
is ApiResult.Error -> {
|
||||
binding.errorView.show()
|
||||
binding.shimmer.hide()
|
||||
}
|
||||
is ApiResult.Loading -> {}
|
||||
is ApiResult.Success -> {
|
||||
it.data?.result?.let {catList ->
|
||||
binding.shimmer.hide()
|
||||
|
||||
binding.trailerView.show()
|
||||
|
||||
adjustMasilaImage()
|
||||
|
||||
binding.spinnerCard.show()
|
||||
binding.selectLangTxt.show()
|
||||
|
||||
activity?.let {activity ->
|
||||
catSpinnerAdapter = SpinnerAdapter(activity, catList.filterNotNull())
|
||||
binding.categorySpinner.setAdapter(catSpinnerAdapter)
|
||||
|
||||
loadShowData()
|
||||
viewModel.loadContinueWatching()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.continueWatchLiveData.observe(viewLifecycleOwner){
|
||||
when (it){
|
||||
is ApiResult.Error -> {
|
||||
binding.rvContinueWatch.hide()
|
||||
binding.continueWatchTxt.hide()
|
||||
}
|
||||
is ApiResult.Loading -> {}
|
||||
is ApiResult.Success -> {
|
||||
it.data?.result?.let {continueWatchList ->
|
||||
if (continueWatchList.isNotEmpty()){
|
||||
binding.rvContinueWatch.show()
|
||||
binding.continueWatchTxt.show()
|
||||
continueWatchAdapter.submitList(continueWatchList.asReversed())
|
||||
}else{
|
||||
binding.rvContinueWatch.hide()
|
||||
binding.continueWatchTxt.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.webSeriesLiveData.observe(viewLifecycleOwner){
|
||||
when (it){
|
||||
is ApiResult.Error -> {
|
||||
binding.shimmerShowData.hide()
|
||||
if (showAdapter.showList.isNotEmpty()){
|
||||
// load more
|
||||
binding.loadMoreBtn.text = getString(R.string.retry)
|
||||
binding.loadMoreBtn.show()
|
||||
}else{
|
||||
// first time load
|
||||
binding.rvWebSeries.hide()
|
||||
binding.shimmerShowData.hide()
|
||||
binding.loadMoreBtn.hide()
|
||||
}
|
||||
}
|
||||
is ApiResult.Loading -> {
|
||||
binding.shimmerShowData.show()
|
||||
binding.loadMoreBtn.hide()
|
||||
binding.rvWebSeries.setVisibility(showAdapter.showList.isNotEmpty())
|
||||
}
|
||||
is ApiResult.Success -> {
|
||||
catSpinnerAdapter?.selectedCategoryType?.let { categoryType ->
|
||||
it.data?.let {showList ->
|
||||
|
||||
binding.rvWebSeries.show()
|
||||
binding.shimmerShowData.hide()
|
||||
|
||||
binding.loadMoreBtn.text = getString(R.string.load_more)
|
||||
binding.loadMoreBtn.setVisibility(viewModel.categoryPagingData[categoryType]?.lastPage == false)
|
||||
|
||||
if (showAdapter.showList.isNotEmpty()){
|
||||
// loaded more data
|
||||
showAdapter.notifyItemRangeInserted(
|
||||
showAdapter.showList.size,
|
||||
showList.size
|
||||
)
|
||||
}else{
|
||||
// new category data load
|
||||
showAdapter.submitListShowList(showList, categoryType)
|
||||
}
|
||||
|
||||
if (userPrefs?.userType != UserType.GUEST && !viewModel.continueWatchLiveData.isInitialized){
|
||||
viewModel.loadContinueWatching()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.categorySpinner.onItemSelectedListener = object :
|
||||
AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(
|
||||
parent: AdapterView<*>?,
|
||||
view: View?,
|
||||
position: Int,
|
||||
id: Long
|
||||
) {
|
||||
if (position != catSpinnerAdapter?.currentSelection){
|
||||
catSpinnerAdapter?.let {
|
||||
it.selectPosition(position)
|
||||
loadShowData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onShowClicked(showData: ShowData, position: Int, categoryId: String){
|
||||
|
||||
}
|
||||
|
||||
private fun onContinueEpisodeClicked(episodeData: ContinueEpisodeData){
|
||||
episodeDialogBinding.apply {
|
||||
episodeData.content_more_details?.let {moreDetailsList ->
|
||||
|
||||
episodeData.thumbnail_path?.let {
|
||||
image.loadImage(it)
|
||||
}
|
||||
|
||||
if (moreDetailsList.isNotEmpty()){
|
||||
|
||||
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1){
|
||||
moreDetailsList[1].let {data ->
|
||||
title.text = data.title
|
||||
description.text = Html.fromHtml(
|
||||
data.description?.replace(
|
||||
"<br>",
|
||||
" "
|
||||
), Html.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
}else{
|
||||
moreDetailsList[0].let {data ->
|
||||
title.text = data.title
|
||||
description.text = Html.fromHtml(
|
||||
data.description?.replace(
|
||||
"<br>",
|
||||
" "
|
||||
), Html.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
}
|
||||
}else{
|
||||
title.text = episodeData.episode_title
|
||||
description.text = Html.fromHtml(
|
||||
episodeData.episode_description?.replace(
|
||||
"<br>",
|
||||
" "
|
||||
), Html.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
|
||||
watchCard.setOnClickListener {
|
||||
episodeData.content_more_details.let {moreDetailsList ->
|
||||
val videoPlayList = VideoPlayList(ArrayList(), ArrayList())
|
||||
|
||||
if (moreDetailsList.isNotEmpty()){
|
||||
if (userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1){
|
||||
videoPlayList.playlist.add(
|
||||
PlaylistItem.Builder()
|
||||
.file(moreDetailsList[1].content_hd_url)
|
||||
.title(moreDetailsList[1].title)
|
||||
.image(moreDetailsList[1].content_hd_url)
|
||||
.build()
|
||||
)
|
||||
|
||||
videoPlayList.videoViewRequestDataList?.add(
|
||||
VideoViewRequestData(
|
||||
"${episodeData.id}",
|
||||
"3",
|
||||
null,
|
||||
category_id = "18"
|
||||
)
|
||||
)
|
||||
}else{
|
||||
videoPlayList.playlist.add(
|
||||
PlaylistItem.Builder()
|
||||
.file(moreDetailsList[0].content_hd_url)
|
||||
.title(moreDetailsList[0].title)
|
||||
.image(moreDetailsList[0].content_hd_url)
|
||||
.build()
|
||||
)
|
||||
|
||||
videoPlayList.videoViewRequestDataList?.add(
|
||||
VideoViewRequestData(
|
||||
"${episodeData.id}",
|
||||
"3",
|
||||
null,
|
||||
"1"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
activity?.let {
|
||||
startActivity(Intent(it, PlayerActivity::class.java)
|
||||
.apply {
|
||||
putExtra(EXTRA_PLAY_LIST, videoPlayList)
|
||||
putExtra(EXTRA_PLAY_INDEX, 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close.setOnClickListener {
|
||||
episodeDialog.dismiss()
|
||||
}
|
||||
|
||||
episodeDialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,6 @@
|
||||
package com.woka.wokagames
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.woka.audiobooks.AudioBookRepository
|
||||
import com.woka.audiobooks.models.audiodata.AudioBookData
|
||||
import com.woka.home.mylist.MyListRepository
|
||||
import com.woka.home.mylist.models.FavAudioBookData
|
||||
import com.woka.home.mylist.models.FavGameData
|
||||
import com.woka.home.mylist.models.PostType
|
||||
import com.woka.networking.ApiResult
|
||||
@@ -25,43 +20,15 @@ object GamesRepository {
|
||||
private val apiService = RetrofitHelper.getRetrofit().create(WokaGamesApiService::class.java)
|
||||
private val userActionApiService = RetrofitHelper.getRetrofit().create(UserActionApiService::class.java)
|
||||
|
||||
// live data
|
||||
|
||||
// audio books data (loose caching)
|
||||
private val _gamesLiveData = MutableLiveData<ApiResult<GamesResponse>>()
|
||||
val gamesLiveData: LiveData<ApiResult<GamesResponse>>
|
||||
get() = _gamesLiveData
|
||||
|
||||
private var gamesData: GamesResponse? = null
|
||||
|
||||
fun loadGames(){
|
||||
if (gamesData != null){
|
||||
_gamesLiveData.postValue(ApiResult.Success(gamesData))
|
||||
return
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch{
|
||||
_gamesLiveData.postValue(ApiResult.Loading())
|
||||
|
||||
val response = RetrofitHelper.handleApiCall {
|
||||
apiService.gamesListing(
|
||||
FormBody.Builder()
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
when (response){
|
||||
is ApiResult.Error -> _gamesLiveData.postValue(ApiResult.Error(response.errorMessage, response.error))
|
||||
is ApiResult.Loading -> _gamesLiveData.postValue(ApiResult.Loading())
|
||||
is ApiResult.Success -> {
|
||||
response.data?.let {
|
||||
gamesData = it
|
||||
_gamesLiveData.postValue(ApiResult.Success(it))
|
||||
}?:{
|
||||
_gamesLiveData.postValue(ApiResult.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
suspend fun loadGames(pageNo: Int, quantity: Int): ApiResult<GamesResponse> {
|
||||
return handleApiCall {
|
||||
apiService.gamesListing(
|
||||
FormBody.Builder()
|
||||
.add("api_version", "v2")
|
||||
.add("start", "$pageNo")
|
||||
.add("limit", "$quantity")
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,27 +57,6 @@ object GamesRepository {
|
||||
}
|
||||
|
||||
private fun changeLikeLocally(id: String, isLiked: Boolean){
|
||||
// changing in games locally
|
||||
gamesData?.game_data?.let {audioBooks ->
|
||||
for (audio in audioBooks){
|
||||
var found = false
|
||||
audio?.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
|
||||
}
|
||||
}
|
||||
|
||||
// changing in fav list locally
|
||||
MyListRepository.myFavData.result?.game_data?.let {
|
||||
for (audioData in it){
|
||||
@@ -155,15 +101,5 @@ object GamesRepository {
|
||||
favAudioData.removeIf{it.id == gameData.id}
|
||||
}
|
||||
}
|
||||
|
||||
gamesData?.game_data?.let {
|
||||
for (game in it){
|
||||
if (game?.id == gameData.id){
|
||||
game?.mark_as_favourite = addToBookmark
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import com.woka.utils.toast
|
||||
import com.woka.wokagames.GamesRepository
|
||||
import com.woka.wokagames.models.listing.GameData
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.max
|
||||
|
||||
class GamesAdapter(var context: Context,
|
||||
private var onGameClicked: (GameData) -> Unit): ListAdapter<GameData, GamesAdapter.GameViewHolder>(
|
||||
@@ -93,6 +94,18 @@ class GamesAdapter(var context: Context,
|
||||
!like.isSelected
|
||||
)
|
||||
|
||||
gameData.likes_count?.let {
|
||||
gameData.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
gameData.is_liked = !like.isSelected
|
||||
|
||||
like.isSelected = !like.isSelected
|
||||
likeCount.text = "${gameData.likes_count}"
|
||||
}
|
||||
@@ -110,6 +123,8 @@ class GamesAdapter(var context: Context,
|
||||
!fav.isSelected
|
||||
)
|
||||
|
||||
gameData.mark_as_favourite = !fav.isSelected
|
||||
|
||||
fav.isSelected = gameData.mark_as_favourite == true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.woka.wokagames.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.wokagames.GamesRepository
|
||||
import com.woka.wokagames.models.listing.GameData
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class GamesViewModel: ViewModel() {
|
||||
|
||||
private val _gamesLiveData = MutableLiveData<ApiResult<MutableList<GameData>>>()
|
||||
val gamesLiveData: LiveData<ApiResult<MutableList<GameData>>>
|
||||
get() = _gamesLiveData
|
||||
|
||||
private val gamesList = ArrayList<GameData>()
|
||||
|
||||
private var nextPageToLoad: Int = 0
|
||||
private var quantityPerPage: Int = 6
|
||||
var lastPage = false
|
||||
|
||||
fun loadGames() {
|
||||
viewModelScope.launch {
|
||||
_gamesLiveData.postValue(ApiResult.Loading())
|
||||
|
||||
when (val value = GamesRepository.loadGames(nextPageToLoad, quantityPerPage)) {
|
||||
is ApiResult.Error -> _gamesLiveData.postValue(
|
||||
ApiResult.Error(
|
||||
value.errorMessage,
|
||||
value.error
|
||||
)
|
||||
)
|
||||
|
||||
is ApiResult.Loading -> _gamesLiveData.postValue(ApiResult.Loading())
|
||||
is ApiResult.Success -> {
|
||||
value.data?.let {
|
||||
it.game_data?.filterNotNull()?.let { newList ->
|
||||
gamesList.addAll(newList)
|
||||
|
||||
lastPage = gamesList.size == it.total_records
|
||||
|
||||
_gamesLiveData.postValue(ApiResult.Success(gamesList))
|
||||
nextPageToLoad++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import android.view.WindowManager
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.woka.R
|
||||
@@ -21,6 +22,7 @@ import com.woka.databinding.DialogModuleShowerBinding
|
||||
import com.woka.networking.ApiResult
|
||||
import com.woka.utils.WokaBaseActivity
|
||||
import com.woka.utils.hide
|
||||
import com.woka.utils.setVisibility
|
||||
import com.woka.utils.show
|
||||
import com.woka.wokagames.GamesRepository
|
||||
import com.woka.wokagames.adapters.GamesAdapter
|
||||
@@ -28,13 +30,16 @@ import com.woka.wokagames.models.listing.GameData
|
||||
import com.woka.wokagames.playerr.GamePlayerActivity
|
||||
import com.woka.wokagames.playerr.GamePlayerActivity.Companion.EXTRA_GAME_PLAYER_DATA
|
||||
import com.woka.wokagames.playerr.GamePlayerData
|
||||
import com.woka.wokagames.viewmodels.GamesViewModel
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
|
||||
class GamesActivity : WokaBaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityGamesBinding
|
||||
private lateinit var viewModel: GamesViewModel
|
||||
|
||||
private lateinit var gameAdapter: GamesAdapter
|
||||
|
||||
@@ -56,6 +61,8 @@ class GamesActivity : WokaBaseActivity() {
|
||||
navigationBarColor = getColor(R.color.color_primary_dark)
|
||||
}
|
||||
|
||||
viewModel = ViewModelProvider(this)[GamesViewModel::class.java]
|
||||
|
||||
gameAdapter = GamesAdapter(this, ::onGameClicked)
|
||||
|
||||
dialogBinding = DialogModuleShowerBinding.inflate(layoutInflater)
|
||||
@@ -70,7 +77,9 @@ class GamesActivity : WokaBaseActivity() {
|
||||
|
||||
setObservers()
|
||||
|
||||
GamesRepository.loadGames()
|
||||
if (!viewModel.gamesLiveData.isInitialized){
|
||||
viewModel.loadGames()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
@@ -115,7 +124,11 @@ class GamesActivity : WokaBaseActivity() {
|
||||
}
|
||||
|
||||
retryBtn.setOnClickListener {
|
||||
GamesRepository.loadGames()
|
||||
viewModel.loadGames()
|
||||
}
|
||||
|
||||
loadMoreBtn.setOnClickListener {
|
||||
viewModel.loadGames()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,8 +252,20 @@ class GamesActivity : WokaBaseActivity() {
|
||||
!like.isSelected
|
||||
)
|
||||
|
||||
gameData.likes_count?.let {
|
||||
gameData.likes_count = if (like.isSelected){
|
||||
// unlike
|
||||
max(0, it - 1)
|
||||
}else{
|
||||
// like
|
||||
it + 1
|
||||
}
|
||||
}
|
||||
|
||||
gameData.is_liked = !like.isSelected
|
||||
|
||||
gameData.id?.let {
|
||||
onGameChanged(it)
|
||||
onGameChanged(it, gameData)
|
||||
}
|
||||
like.isSelected = !like.isSelected
|
||||
likeCount.text = "${gameData.likes_count}"
|
||||
@@ -252,8 +277,10 @@ class GamesActivity : WokaBaseActivity() {
|
||||
!fav.isSelected
|
||||
)
|
||||
|
||||
gameData.mark_as_favourite = !fav.isSelected
|
||||
|
||||
gameData.id?.let {
|
||||
onGameChanged(it)
|
||||
onGameChanged(it, gameData)
|
||||
}
|
||||
|
||||
fav.isSelected = !fav.isSelected
|
||||
@@ -268,46 +295,75 @@ class GamesActivity : WokaBaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun onGameChanged(id: Int) {
|
||||
private fun onGameChanged(id: Int, gameData: GameData) {
|
||||
// updating continue book list
|
||||
val position = gameAdapter.currentList.indexOfFirst { it.id == id }
|
||||
if (position >= 0 && position < gameAdapter.currentList.size) {
|
||||
val currKaraoke = gameAdapter.currentList[position]
|
||||
|
||||
// updating possible data changes
|
||||
currKaraoke.is_liked = gameData.is_liked
|
||||
currKaraoke.likes_count = gameData.likes_count
|
||||
currKaraoke.mark_as_favourite = gameData.mark_as_favourite
|
||||
gameAdapter.notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setObservers(){
|
||||
GamesRepository.gamesLiveData.observe(this) {
|
||||
when (it) {
|
||||
viewModel.gamesLiveData.observe(this){
|
||||
when(it){
|
||||
is ApiResult.Error -> {
|
||||
binding.shimmer.hide()
|
||||
binding.rvGames.hide()
|
||||
binding.listenTxt.hide()
|
||||
binding.trailerView.hide()
|
||||
if (gameAdapter.currentList.size == 0) {
|
||||
// none of the data is yet loaded
|
||||
binding.rvGames.hide()
|
||||
binding.listenTxt.hide()
|
||||
binding.trailerView.hide()
|
||||
|
||||
binding.errorView.show()
|
||||
binding.errorView.show()
|
||||
} else {
|
||||
// error in loading more
|
||||
binding.loadMoreProgress.hide()
|
||||
binding.loadMoreBtn.text = getString(R.string.retry)
|
||||
binding.loadMoreBtn.show()
|
||||
}
|
||||
}
|
||||
|
||||
is ApiResult.Loading -> {
|
||||
binding.shimmer.show()
|
||||
|
||||
binding.rvGames.hide()
|
||||
binding.listenTxt.hide()
|
||||
binding.trailerView.hide()
|
||||
binding.errorView.hide()
|
||||
if (gameAdapter.currentList.size == 0) {
|
||||
// loading first data
|
||||
binding.shimmer.show()
|
||||
binding.errorView.hide()
|
||||
} else {
|
||||
// loading more data
|
||||
binding.loadMoreProgress.show()
|
||||
binding.loadMoreBtn.hide()
|
||||
}
|
||||
}
|
||||
|
||||
is ApiResult.Success -> {
|
||||
it.data?.game_data?.filterNotNull()?.let { gameData ->
|
||||
if (gameData.isNotEmpty()) {
|
||||
binding.shimmer.hide()
|
||||
binding.errorView.hide()
|
||||
|
||||
loadTrailerData(gameData[0])
|
||||
it.data?.let {newList ->
|
||||
if (newList.isNotEmpty()){
|
||||
binding.rvGames.show()
|
||||
binding.listenTxt.show()
|
||||
binding.trailerView.show()
|
||||
|
||||
gameAdapter.submitList(gameData)
|
||||
binding.errorView.hide()
|
||||
binding.shimmer.hide()
|
||||
|
||||
binding.loadMoreProgress.hide()
|
||||
binding.loadMoreBtn.text = getString(R.string.load_more)
|
||||
binding.loadMoreBtn.setVisibility(!viewModel.lastPage)
|
||||
|
||||
if (gameAdapter.currentList.isEmpty()){
|
||||
// first data load
|
||||
loadTrailerData(newList[0])
|
||||
gameAdapter.submitList(newList)
|
||||
}else{
|
||||
// loaded more data
|
||||
gameAdapter.notifyItemRangeInserted(
|
||||
gameAdapter.currentList.size,
|
||||
newList.size
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,6 +274,42 @@
|
||||
tools:listitem="@layout/show_view_holder"
|
||||
/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/load_more_progress"
|
||||
android:visibility="gone"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="@color/white"
|
||||
|
||||
android:layout_gravity="center_horizontal"
|
||||
|
||||
android:layout_marginVertical="15dp"
|
||||
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/load_more_btn"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="@string/load_more"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_12ssp"
|
||||
|
||||
android:paddingHorizontal="25dp"
|
||||
android:layout_marginVertical="15dp"
|
||||
|
||||
android:background="@drawable/round_25"
|
||||
android:backgroundTint="@color/night_status"
|
||||
|
||||
android:layout_gravity="center_horizontal"
|
||||
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
@@ -246,6 +246,42 @@
|
||||
tools:listitem="@layout/show_view_holder"
|
||||
/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/load_more_progress"
|
||||
android:visibility="gone"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="@color/white"
|
||||
|
||||
android:layout_gravity="center_horizontal"
|
||||
|
||||
android:layout_marginVertical="15dp"
|
||||
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/load_more_btn"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="@string/load_more"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_12ssp"
|
||||
|
||||
android:paddingHorizontal="25dp"
|
||||
android:layout_marginVertical="15dp"
|
||||
|
||||
android:background="@drawable/round_25"
|
||||
android:backgroundTint="@color/night_status"
|
||||
|
||||
android:layout_gravity="center_horizontal"
|
||||
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
7
app/src/main/res/layout/activity_series.xml
Normal file
7
app/src/main/res/layout/activity_series.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/fcv_web_series"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".webseries.views.SeriesActivity"/>
|
||||
346
app/src/main/res/layout/fragment_web_series.xml
Normal file
346
app/src/main/res/layout/fragment_web_series.xml
Normal file
@@ -0,0 +1,346 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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/gradient_web_series"
|
||||
android:orientation="vertical"
|
||||
tools:context=".webseries.views.WebSeriesActivity">
|
||||
|
||||
<include android:id="@+id/toolbar"
|
||||
layout="@layout/layout_toolbar"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/shimmer"
|
||||
android:visibility="visible"
|
||||
android:background="@color/color_primary_dark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="none"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/_160sdp"
|
||||
android:background="@color/white_50"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:layout_width="@dimen/_100sdp"
|
||||
android:layout_height="@dimen/_37sdp"
|
||||
android:background="@color/white_50"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
android:layout_marginVertical="10dp"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/_140sdp"
|
||||
android:background="@color/white_50"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/_140sdp"
|
||||
android:background="@color/white_50"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
android:layout_marginTop="15dp"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/_140sdp"
|
||||
android:background="@color/white_50"
|
||||
android:layout_marginVertical="15dp"
|
||||
android:layout_marginHorizontal="15dp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/error_view"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/color_primary_dark"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
>
|
||||
|
||||
<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/white"
|
||||
android:textSize="@dimen/_14ssp"
|
||||
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/retry_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="@string/retry"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_14ssp"
|
||||
|
||||
android:paddingHorizontal="25dp"
|
||||
android:layout_marginTop="15dp"
|
||||
|
||||
android:background="@drawable/grad_btn_bg_4"
|
||||
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/trailer_view"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|snap|exitUntilCollapsed">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/masila_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/_160sdp"
|
||||
android:contentDescription="@string/image"
|
||||
android:src="@drawable/img_masila_full"
|
||||
android:scaleType="fitXY"
|
||||
app:layout_collapseMode="parallax"
|
||||
android:layout_gravity="top"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/top_pinned_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/color_primary_dark"
|
||||
app:layout_collapseMode="pin"
|
||||
app:contentInsetStart="0dp"
|
||||
app:contentInsetLeft="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
android:layout_gravity="bottom">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp"
|
||||
android:gravity="center"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="@string/masila"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_16ssp"
|
||||
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="@string/trailer"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_12ssp"
|
||||
|
||||
android:paddingHorizontal="25dp"
|
||||
android:layout_marginTop="15dp"
|
||||
|
||||
android:background="@drawable/trailer_btn_bg"
|
||||
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/nested_scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/color_primary_dark"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/continue_watch_txt"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="@string/continue_watching"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_14ssp"
|
||||
|
||||
android:layout_marginHorizontal="15dp"
|
||||
|
||||
/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_continue_watch"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/continue_show_view_holder"
|
||||
android:orientation="horizontal"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/select_lang_txt"
|
||||
android:visibility="gone"
|
||||
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/_14ssp"
|
||||
|
||||
android:layout_marginHorizontal="15dp"
|
||||
|
||||
/>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/spinner_card"
|
||||
android:visibility="gone"
|
||||
android:layout_width="@dimen/_160sdp"
|
||||
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/_40sdp"
|
||||
|
||||
android:popupBackground="@drawable/round_bg_5_white"
|
||||
android:textAlignment="textStart"
|
||||
/>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_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="15dp"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/load_more_btn"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="@string/load_more"
|
||||
android:fontFamily="@font/exo_2_bold"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/_12ssp"
|
||||
|
||||
android:paddingHorizontal="25dp"
|
||||
android:layout_marginVertical="15dp"
|
||||
|
||||
android:background="@drawable/round_25"
|
||||
android:backgroundTint="@color/night_status"
|
||||
|
||||
android:layout_gravity="center_horizontal"
|
||||
|
||||
/>
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:id="@+id/shimmer_show_data"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:shimmer_base_alpha="0.8"
|
||||
app:shimmer_auto_start="true"
|
||||
app:shimmer_highlight_alpha="0.5"
|
||||
app:shimmer_duration="1500"
|
||||
android:layout_marginVertical="10dp"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/show_view_holder"/>
|
||||
<include layout="@layout/show_view_holder"/>
|
||||
<include layout="@layout/show_view_holder"/>
|
||||
<include layout="@layout/show_view_holder"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user