From 294afcb80adfb651ebbc83e7e58cac551b6f35a1 Mon Sep 17 00:00:00 2001 From: AdityaGaikwad Date: Mon, 8 Jul 2024 19:57:39 +0530 Subject: [PATCH] Created Games module: UI and backend Integrated game listing and data caching Data syncing with adapters and MyListFragment Dialog showing for game. GamePlayerActivity created - loading url, keeping screen on, hiding system bars. Integrated karaoke api and caching data syncing for like and fav data --- app/src/main/AndroidManifest.xml | 19 +- .../audiobooks/adapters/AudioBooksAdapter.kt | 32 +- .../audiobooks/views/AudioBooksActivity.kt | 19 +- .../com/woka/home/fragments/Home1Fragment.kt | 7 + .../com/woka/home/fragments/MyListFragment.kt | 290 +++++++++++----- .../home/mylist/adapters/FavAudioAdapter.kt | 6 +- .../home/mylist/adapters/FavGamesAdapter.kt | 128 +++++++ .../woka/home/mylist/adapters/GamesAdapter.kt | 91 ----- .../woka/home/mylist/models/FavGameData.kt | 46 +++ .../com/woka/home/mylist/models/Result.kt | 2 +- .../com/woka/karaoke/KaraokeApiService.kt | 14 + .../com/woka/karaoke/KaraokeRepository.kt | 123 +++++++ .../models/listing/ContentMoreDetail.kt | 12 + .../models/listing/KaraokeData.kt} | 14 +- .../karaoke/models/listing/KaraokeResponse.kt | 6 + .../com/woka/wokagames/GamesRepository.kt | 169 ++++++++++ .../com/woka/wokagames/WokaGamesApiService.kt | 14 + .../woka/wokagames/adapters/GamesAdapter.kt | 121 +++++++ .../models/listing/ContentMoreDetail.kt | 12 + .../woka/wokagames/models/listing/GameData.kt | 47 +++ .../wokagames/models/listing/GamesResponse.kt | 6 + .../wokagames/playerr/GamePlayerActivity.kt | 107 ++++++ .../woka/wokagames/playerr/GamePlayerData.kt | 10 + .../com/woka/wokagames/views/GamesActivity.kt | 317 ++++++++++++++++++ .../main/res/drawable/gradient_woka_games.xml | 11 + app/src/main/res/drawable/play_btn_bg.xml | 11 + .../main/res/layout/activity_game_player.xml | 9 + app/src/main/res/layout/activity_games.xml | 255 ++++++++++++++ ...udio_book.xml => dialog_module_shower.xml} | 65 +++- app/src/main/res/values/attrs.xml | 6 + app/src/main/res/values/colors.xml | 7 + app/src/main/res/values/strings.xml | 9 + app/src/main/res/values/styles.xml | 11 + app/src/main/res/values/themes.xml | 8 +- 34 files changed, 1787 insertions(+), 217 deletions(-) create mode 100644 app/src/main/java/com/woka/home/mylist/adapters/FavGamesAdapter.kt delete mode 100644 app/src/main/java/com/woka/home/mylist/adapters/GamesAdapter.kt create mode 100644 app/src/main/java/com/woka/home/mylist/models/FavGameData.kt create mode 100644 app/src/main/java/com/woka/karaoke/KaraokeApiService.kt create mode 100644 app/src/main/java/com/woka/karaoke/KaraokeRepository.kt create mode 100644 app/src/main/java/com/woka/karaoke/models/listing/ContentMoreDetail.kt rename app/src/main/java/com/woka/{home/mylist/models/GameData.kt => karaoke/models/listing/KaraokeData.kt} (60%) create mode 100644 app/src/main/java/com/woka/karaoke/models/listing/KaraokeResponse.kt create mode 100644 app/src/main/java/com/woka/wokagames/GamesRepository.kt create mode 100644 app/src/main/java/com/woka/wokagames/WokaGamesApiService.kt create mode 100644 app/src/main/java/com/woka/wokagames/adapters/GamesAdapter.kt create mode 100644 app/src/main/java/com/woka/wokagames/models/listing/ContentMoreDetail.kt create mode 100644 app/src/main/java/com/woka/wokagames/models/listing/GameData.kt create mode 100644 app/src/main/java/com/woka/wokagames/models/listing/GamesResponse.kt create mode 100644 app/src/main/java/com/woka/wokagames/playerr/GamePlayerActivity.kt create mode 100644 app/src/main/java/com/woka/wokagames/playerr/GamePlayerData.kt create mode 100644 app/src/main/java/com/woka/wokagames/views/GamesActivity.kt create mode 100644 app/src/main/res/drawable/gradient_woka_games.xml create mode 100644 app/src/main/res/drawable/play_btn_bg.xml create mode 100644 app/src/main/res/layout/activity_game_player.xml create mode 100644 app/src/main/res/layout/activity_games.xml rename app/src/main/res/layout/{dialog_audio_book.xml => dialog_module_shower.xml} (82%) create mode 100644 app/src/main/res/values/attrs.xml create mode 100644 app/src/main/res/values/styles.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 32a007e..19262f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> @@ -14,11 +14,22 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Woka" - tools:targetApi="31" > + tools:targetApi="31"> + + + android:screenOrientation="portrait" /> + android:theme="@style/Theme.App.Starting"> diff --git a/app/src/main/java/com/woka/audiobooks/adapters/AudioBooksAdapter.kt b/app/src/main/java/com/woka/audiobooks/adapters/AudioBooksAdapter.kt index aadbdfa..5c8e859 100644 --- a/app/src/main/java/com/woka/audiobooks/adapters/AudioBooksAdapter.kt +++ b/app/src/main/java/com/woka/audiobooks/adapters/AudioBooksAdapter.kt @@ -1,10 +1,11 @@ package com.woka.audiobooks.adapters -import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.AsyncDifferConfig +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.woka.R import com.woka.WokaApp.Companion.userPrefs @@ -14,21 +15,32 @@ import com.woka.databinding.ShowViewHolderBinding import com.woka.utils.isNetworkConnected import com.woka.utils.show import com.woka.utils.toast +import java.util.concurrent.Executors class AudioBooksAdapter( private val context: Context, private var onBookClicked: (AudioBookData) -> Unit, private var onBookChanged: (id: Int) -> Unit -): RecyclerView.Adapter() { +): ListAdapter(ASYNC_DIFF_UTIL) { inner class AudioBookViewHolder(val binding: ShowViewHolderBinding): ViewHolder(binding.root) - var audioBookList: List = ArrayList() + companion object{ + val DIFF_UTIL = object : DiffUtil.ItemCallback(){ + override fun areItemsTheSame(oldItem: AudioBookData, newItem: AudioBookData): Boolean = oldItem.id == newItem.id - @SuppressLint("NotifyDataSetChanged") - fun submitListShowList(it: List) { - this.audioBookList = it - notifyDataSetChanged() + override fun areContentsTheSame(oldItem: AudioBookData, newItem: AudioBookData): Boolean { + return oldItem.thumbnail_path == newItem.thumbnail_path && + oldItem.title == newItem.title && + oldItem.is_liked == newItem.is_liked && + oldItem.mark_as_favourite == newItem.mark_as_favourite && + oldItem.likes_count == newItem.likes_count + } + } + + val ASYNC_DIFF_UTIL = AsyncDifferConfig.Builder(DIFF_UTIL) + .setBackgroundThreadExecutor(Executors.newSingleThreadExecutor()) + .build() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AudioBookViewHolder { @@ -41,10 +53,8 @@ class AudioBooksAdapter( ) } - override fun getItemCount(): Int = audioBookList.size - override fun onBindViewHolder(holder: AudioBookViewHolder, position: Int) { - val audioBook = audioBookList[holder.absoluteAdapterPosition] + val audioBook = getItem(holder.absoluteAdapterPosition) holder.binding.apply { audioBook.thumbnail_path?.let { diff --git a/app/src/main/java/com/woka/audiobooks/views/AudioBooksActivity.kt b/app/src/main/java/com/woka/audiobooks/views/AudioBooksActivity.kt index 90bb434..d1d0749 100644 --- a/app/src/main/java/com/woka/audiobooks/views/AudioBooksActivity.kt +++ b/app/src/main/java/com/woka/audiobooks/views/AudioBooksActivity.kt @@ -7,7 +7,6 @@ import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable import android.os.Bundle import android.text.Html -import android.util.Log import android.view.WindowManager import androidx.activity.enableEdgeToEdge import androidx.core.view.ViewCompat @@ -24,18 +23,14 @@ import com.woka.audiobooks.adapters.ContinueAudioAdapter import com.woka.audiobooks.models.audiodata.AudioBookData import com.woka.audiobooks.models.continuedata.ContinueAudioData import com.woka.databinding.ActivityAudioBooksBinding -import com.woka.databinding.DialogAudioBookBinding +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.utils.TAG import com.woka.utils.WokaBaseActivity import com.woka.utils.hide import com.woka.utils.show -import com.woka.webseries.WebSeriesRepository import java.text.SimpleDateFormat -import java.time.LocalDate -import java.time.format.DateTimeFormatter import java.util.Calendar import java.util.Locale @@ -46,7 +41,7 @@ class AudioBooksActivity : WokaBaseActivity() { private lateinit var audioBookAdapter: AudioBooksAdapter private lateinit var continueAudioAdapter: ContinueAudioAdapter - private lateinit var dialogBinding: DialogAudioBookBinding + private lateinit var dialogBinding: DialogModuleShowerBinding private lateinit var audioDialog: Dialog override fun onCreate(savedInstanceState: Bundle?) { @@ -68,7 +63,7 @@ class AudioBooksActivity : WokaBaseActivity() { continueAudioAdapter = ContinueAudioAdapter(this, ::onContinueBookClicked, ::onContinueBookChanged) - dialogBinding = DialogAudioBookBinding.inflate(layoutInflater) + dialogBinding = DialogModuleShowerBinding.inflate(layoutInflater) audioDialog = Dialog(this) audioDialog.setContentView(dialogBinding.root) @@ -163,7 +158,7 @@ class AudioBooksActivity : WokaBaseActivity() { binding.rvAudioBooks.show() binding.listenTxt.show() - audioBookAdapter.submitListShowList(audioBookData) + audioBookAdapter.submitList(audioBookData) } } } @@ -253,8 +248,8 @@ class AudioBooksActivity : WokaBaseActivity() { private fun onContinueBookChanged(id: Int) { // updating book list - val position = audioBookAdapter.audioBookList.indexOfFirst { it.id == id } - if (position >= 0 && position < audioBookAdapter.audioBookList.size) { + val position = audioBookAdapter.currentList.indexOfFirst { it.id == id } + if (position >= 0 && position < audioBookAdapter.currentList.size) { audioBookAdapter.notifyItemChanged(position) } } @@ -311,6 +306,7 @@ class AudioBooksActivity : WokaBaseActivity() { fav.isSelected = audioBookData.mark_as_favourite == true like.isSelected = audioBookData.is_liked == true + likeCount.text = "${audioBookData.likes_count}" year.text = try { audioBookData.release_date?.let { @@ -373,6 +369,7 @@ class AudioBooksActivity : WokaBaseActivity() { onContinueBookChanged(it) } like.isSelected = !like.isSelected + likeCount.text = "${audioBookData.likes_count}" } fav.setOnClickListener { diff --git a/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt b/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt index 8fce4f7..6de2ac8 100644 --- a/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt +++ b/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt @@ -33,6 +33,7 @@ import com.woka.utils.hide import com.woka.utils.scaleAnimate import com.woka.utils.show import com.woka.webseries.views.WebSeriesActivity +import com.woka.wokagames.views.GamesActivity class Home1Fragment : Fragment(), Listener { @@ -186,6 +187,12 @@ class Home1Fragment : Fragment(), Listener { startActivity(Intent(it, AudioBooksActivity::class.java)) } } + + play.setOnClickListener { + activity?.let { + startActivity(Intent(it, GamesActivity::class.java)) + } + } } } diff --git a/app/src/main/java/com/woka/home/fragments/MyListFragment.kt b/app/src/main/java/com/woka/home/fragments/MyListFragment.kt index fcfc57a..cd7df64 100644 --- a/app/src/main/java/com/woka/home/fragments/MyListFragment.kt +++ b/app/src/main/java/com/woka/home/fragments/MyListFragment.kt @@ -2,12 +2,12 @@ package com.woka.home.fragments import android.app.Dialog import android.content.Intent +import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.graphics.drawable.InsetDrawable import android.os.Bundle import android.text.Html -import android.util.Log import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View @@ -24,25 +24,29 @@ import com.woka.R import com.woka.WokaApp import com.woka.audiobooks.AudioBookRepository import com.woka.audiobooks.models.audiodata.AudioBookData -import com.woka.databinding.DialogAudioBookBinding +import com.woka.databinding.DialogModuleShowerBinding import com.woka.databinding.FragmentMyListBinding import com.woka.home.mylist.MyListRepository import com.woka.home.mylist.adapters.FavAudioAdapter -import com.woka.home.mylist.adapters.GamesAdapter +import com.woka.home.mylist.adapters.FavGamesAdapter import com.woka.home.mylist.adapters.KaraokeAdapter import com.woka.home.mylist.adapters.WebSeriesAdapter import com.woka.home.mylist.models.PostType import com.woka.home.mylist.models.BookmarkedShowData import com.woka.home.mylist.models.FavAudioBookData +import com.woka.home.mylist.models.FavGameData import com.woka.networking.ApiResult import com.woka.players.models.VideoPlayList import com.woka.players.views.PlayerActivity -import com.woka.utils.TAG import com.woka.utils.hide import com.woka.utils.isNetworkConnected import com.woka.utils.show import com.woka.utils.toast import com.woka.webseries.views.SeasonActivity +import com.woka.wokagames.GamesRepository +import com.woka.wokagames.models.listing.GameData +import com.woka.wokagames.playerr.GamePlayerActivity +import com.woka.wokagames.playerr.GamePlayerData import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.Calendar @@ -56,12 +60,12 @@ class MyListFragment : Fragment() { private lateinit var webSeriesHAdapter: WebSeriesAdapter private lateinit var audioBooksAdapter: FavAudioAdapter private lateinit var karaokeAdapter: KaraokeAdapter - private lateinit var gamesAdapter: GamesAdapter + private lateinit var gamesAdapter: FavGamesAdapter private var webShowIntentLauncher: ActivityResultLauncher? = null - private lateinit var dialogBinding: DialogAudioBookBinding - private lateinit var audioDialog: Dialog + private lateinit var dialogBinding: DialogModuleShowerBinding + private lateinit var moduleShowerDialog: Dialog override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -74,14 +78,14 @@ class MyListFragment : Fragment() { webSeriesHAdapter = WebSeriesAdapter(requireContext(),"18", ::onListGotEmpty) audioBooksAdapter = FavAudioAdapter(requireContext(), ::onListGotEmpty, ::onAudioBookClicked) karaokeAdapter = KaraokeAdapter(requireContext(), ::onListGotEmpty) - gamesAdapter = GamesAdapter(requireContext(), ::onListGotEmpty) + gamesAdapter = FavGamesAdapter(requireContext(), ::onListGotEmpty, ::onGameClicked) // dialogs - dialogBinding = DialogAudioBookBinding.inflate(layoutInflater) - audioDialog = Dialog(requireContext()) - audioDialog.setContentView(dialogBinding.root) + dialogBinding = DialogModuleShowerBinding.inflate(layoutInflater) + moduleShowerDialog = Dialog(requireContext()) + moduleShowerDialog.setContentView(dialogBinding.root) - initAudioDialog() + initModuleShowerDialog() initViews() @@ -118,7 +122,7 @@ class MyListFragment : Fragment() { private fun clickEvents(){ binding.apply { backBtn.setOnClickListener { - activity?.onBackPressed() + activity?.onBackPressedDispatcher?.onBackPressed() } } } @@ -242,78 +246,25 @@ class MyListFragment : Fragment() { } } - private fun initAudioDialog() { + private fun initModuleShowerDialog() { try { val back = ColorDrawable(Color.TRANSPARENT) val inset = InsetDrawable(back, 50) - audioDialog.window!!.setBackgroundDrawable(inset) + moduleShowerDialog.window!!.setBackgroundDrawable(inset) } catch (e: Exception) { // do nothing } try { - val layoutParams = audioDialog.window!!.attributes + val layoutParams = moduleShowerDialog.window!!.attributes layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT - audioDialog.window!!.setAttributes(layoutParams) + moduleShowerDialog.window!!.setAttributes(layoutParams) } catch (e: Exception) { // do nothing } - dialogBinding.close.setOnClickListener { audioDialog.dismiss() } - } - - private fun onAudioItemChanged(id: Int, isRemoved: Boolean) { - // updating book list - val position = audioBooksAdapter.currentList.indexOfFirst { it.id == id } - if (position >= 0 && position < audioBooksAdapter.currentList.size) { - if (!isRemoved) audioBooksAdapter.notifyItemChanged(position) - else audioBooksAdapter.notifyItemRemoved(position) - } - } - - private fun onAudioListenClicked(audioBookData: AudioBookData) { - - var url: String? = null - var title = "" - - audioBookData.content_more_details?.let {moreDetails -> - if (moreDetails.isNotEmpty()){ - if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetails.size > 1){ - url = moreDetails[1]?.url - title = moreDetails[1]?.title?:"" - }else{ - url = moreDetails[0]?.url - title = moreDetails[0]?.title?:"" - } - }else{ - url = audioBookData.audio_url - title = audioBookData.title?:"" - } - } - - activity?.let {activity -> - url?.let { - startActivity(Intent(activity, PlayerActivity::class.java).apply { - putExtra( - PlayerActivity.EXTRA_PLAY_LIST, - VideoPlayList( - ArrayList().apply { - add( - PlaylistItem.Builder() - .file(it) - .image(it) - .title(title) - .build() - ) - }, - null - ) - ) - putExtra(PlayerActivity.EXTRA_PLAY_INDEX, 0) - }) - } - } + dialogBinding.close.setOnClickListener { moduleShowerDialog.dismiss() } } private fun onListGotEmpty(postType: PostType, isEng: Boolean = true){ @@ -364,7 +315,7 @@ class MyListFragment : Fragment() { } // audio books - private fun onAudioBookClicked(audioBookData: AudioBookData, position: Int){ + private fun onAudioBookClicked(audioBookData: FavAudioBookData, position: Int){ dialogBinding.apply { audioBookData.content_more_details?.let { moreDetailsList -> @@ -374,6 +325,7 @@ class MyListFragment : Fragment() { fav.isSelected = audioBookData.mark_as_favourite == true like.isSelected = audioBookData.is_liked == true + likeCount.text = "${audioBookData.likes_count}" year.text = try { audioBookData.release_date?.let { @@ -421,6 +373,10 @@ class MyListFragment : Fragment() { ) } + activity?.let { + watchCard.backgroundTintList = ColorStateList.valueOf(it.getColor(R.color.audio_grad_one)) + } + watchCard.text = getString(R.string.listen_now) watchCard.setOnClickListener { onAudioListenClicked(audioBookData) } @@ -432,9 +388,10 @@ class MyListFragment : Fragment() { ) audioBookData.id?.let { - onAudioItemChanged(it, false) + onAudioItemChanged(it) } like.isSelected = !like.isSelected + likeCount.text = "${audioBookData.likes_count}" } fav.setOnClickListener { @@ -444,7 +401,7 @@ class MyListFragment : Fragment() { } AudioBookRepository.updateFavShow( - audioBookData, + AudioBookData(audioBookData), !fav.isSelected ) @@ -458,18 +415,197 @@ class MyListFragment : Fragment() { onListGotEmpty(PostType.AUDIO_BOOKS, true) } - audioDialog.dismiss() + moduleShowerDialog.dismiss() } close.setOnClickListener { - audioDialog.dismiss() + moduleShowerDialog.dismiss() } - audioDialog.show() + moduleShowerDialog.show() } } } + private fun onAudioItemChanged(id: Int) { + // updating book list + val position = audioBooksAdapter.currentList.indexOfFirst { it.id == id } + if (position >= 0 && position < audioBooksAdapter.currentList.size) { + audioBooksAdapter.notifyItemChanged(position) + } + } + + private fun onAudioListenClicked(audioBookData: FavAudioBookData) { + + var url: String? = null + var title = "" + + audioBookData.content_more_details?.let {moreDetails -> + if (moreDetails.isNotEmpty()){ + if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetails.size > 1){ + url = moreDetails[1]?.url + title = moreDetails[1]?.title?:"" + }else{ + url = moreDetails[0]?.url + title = moreDetails[0]?.title?:"" + } + }else{ + url = audioBookData.audio_url + title = audioBookData.title?:"" + } + } + + activity?.let {activity -> + url?.let { + startActivity(Intent(activity, PlayerActivity::class.java).apply { + putExtra( + PlayerActivity.EXTRA_PLAY_LIST, + VideoPlayList( + ArrayList().apply { + add( + PlaylistItem.Builder() + .file(it) + .image(it) + .title(title) + .build() + ) + }, + null + ) + ) + putExtra(PlayerActivity.EXTRA_PLAY_INDEX, 0) + }) + } + } + } + + // woka games + private fun onGameClicked(gameData: FavGameData, position: Int){ + dialogBinding.apply { + gameData.content_more_details?.let { moreDetailsList -> + + gameData.thumbnail_path?.let { + image.loadImage(it) + } + + fav.isSelected = gameData.mark_as_favourite == true + like.isSelected = gameData.is_liked == true + likeCount.text = "${gameData.likes_count}" + + year.text = try { + gameData.release_date?.let { + val cal = Calendar.getInstance() + cal.time = + SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).parse( + gameData.release_date + )!! + "${cal.get(Calendar.YEAR)}" + } ?: throw Exception() + } catch (e: Exception) { + "${gameData.release_date}" + } + + if (moreDetailsList.isNotEmpty()) { + + if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) { + moreDetailsList[1]?.let { data -> + title.text = data.title + description.text = Html.fromHtml( + data.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + } + } else { + moreDetailsList[0]?.let { data -> + title.text = data.title + description.text = Html.fromHtml( + data.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + } + } + } else { + title.text = gameData.title + description.text = Html.fromHtml( + gameData.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + } + + activity?.let { + watchCard.backgroundTintList = ColorStateList.valueOf(it.getColor(R.color.game_grad_one)) + } + watchCard.text = getString(R.string.play_now) + watchCard.setOnClickListener { + activity?.let {activity -> + gameData.game_url?.let { + startActivity(Intent(activity, GamePlayerActivity::class.java).apply { + putExtra(GamePlayerActivity.EXTRA_GAME_PLAYER_DATA, GamePlayerData(it, gameData.screen_orientation == "Landscape")) + }) + } + } + } + + like.setOnClickListener { + GamesRepository.likeUnLikeGame( + "${gameData.id}", + !like.isSelected + ) + + gameData.id?.let { + onGameChanged(it) + } + like.isSelected = !like.isSelected + likeCount.text = "${gameData.likes_count}" + } + + fav.setOnClickListener { + if (context?.isNetworkConnected() == false){ + toast(getString(R.string.no_internet)) + return@setOnClickListener + } + + GamesRepository.updateFavShow( + GameData(gameData), + !fav.isSelected + ) + + try { + gamesAdapter.notifyItemRemoved(position) + } catch (e: Exception) { + // do nothing + } + + if (gamesAdapter.currentList.isEmpty()){ + onListGotEmpty(PostType.GAMES, true) + } + + moduleShowerDialog.dismiss() + } + + close.setOnClickListener { + moduleShowerDialog.dismiss() + } + + moduleShowerDialog.show() + } + } + } + + private fun onGameChanged(id: Int) { + // updating continue book list + val position = gamesAdapter.currentList.indexOfFirst { it.id == id } + if (position >= 0 && position < gamesAdapter.currentList.size) { + gamesAdapter.notifyItemChanged(position) + } + } + companion object { fun newInstance() = MyListFragment() } diff --git a/app/src/main/java/com/woka/home/mylist/adapters/FavAudioAdapter.kt b/app/src/main/java/com/woka/home/mylist/adapters/FavAudioAdapter.kt index 5388f41..7fff146 100644 --- a/app/src/main/java/com/woka/home/mylist/adapters/FavAudioAdapter.kt +++ b/app/src/main/java/com/woka/home/mylist/adapters/FavAudioAdapter.kt @@ -21,7 +21,7 @@ import java.util.concurrent.Executors class FavAudioAdapter(private val context: Context, config: AsyncDifferConfig, private val onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit), - private var onBookClicked: (AudioBookData, Int) -> Unit): ListAdapter(config) { + private var onBookClicked: (FavAudioBookData, Int) -> Unit): ListAdapter(config) { companion object{ private val DIFF_UTIL = object : DiffUtil.ItemCallback(){ @@ -41,7 +41,7 @@ class FavAudioAdapter(private val context: Context, constructor(context: Context, onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit), - onBookClicked: (AudioBookData, Int) -> Unit): this(context, DIFF_CONFIG, onListEmptyListener, onBookClicked) + onBookClicked: (FavAudioBookData, Int) -> Unit): this(context, DIFF_CONFIG, onListEmptyListener, onBookClicked) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavoriteViewHolder { return FavoriteViewHolder( @@ -121,7 +121,7 @@ class FavAudioAdapter(private val context: Context, } root.setOnClickListener { - onBookClicked(AudioBookData(audioBook), holder.absoluteAdapterPosition) + onBookClicked(audioBook, holder.absoluteAdapterPosition) } } } diff --git a/app/src/main/java/com/woka/home/mylist/adapters/FavGamesAdapter.kt b/app/src/main/java/com/woka/home/mylist/adapters/FavGamesAdapter.kt new file mode 100644 index 0000000..9ca592d --- /dev/null +++ b/app/src/main/java/com/woka/home/mylist/adapters/FavGamesAdapter.kt @@ -0,0 +1,128 @@ +package com.woka.home.mylist.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.AsyncDifferConfig +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import com.woka.R +import com.woka.WokaApp +import com.woka.databinding.FavViewHolderBinding +import com.woka.home.mylist.models.FavGameData +import com.woka.home.mylist.models.PostType +import com.woka.utils.isNetworkConnected +import com.woka.utils.show +import com.woka.utils.toast +import com.woka.wokagames.GamesRepository +import com.woka.wokagames.models.listing.GameData +import java.util.concurrent.Executors + +class FavGamesAdapter(private val context: Context, + config: AsyncDifferConfig, + private val onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit), + private val onGameClicked: ((FavGameData, Int) -> Unit)): + ListAdapter(config) { + + companion object{ + private val DIFF_UTIL = object : DiffUtil.ItemCallback(){ + override fun areItemsTheSame(oldItem: FavGameData, newItem: FavGameData): Boolean = oldItem.id == newItem.id + override fun areContentsTheSame(oldItem: FavGameData, newItem: FavGameData): Boolean { + return oldItem.title == newItem.title && + oldItem.is_liked == newItem.is_liked && + oldItem.likes_count == newItem.likes_count + } + } + + private val DIFF_CONFIG = AsyncDifferConfig.Builder(DIFF_UTIL) + .setBackgroundThreadExecutor(Executors.newSingleThreadExecutor()) + .build() + } + + constructor(context: Context, + onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit), + onGameClicked: ((gameData: FavGameData, pos: Int) -> Unit)): this(context, DIFF_CONFIG, onListEmptyListener, onGameClicked) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavoriteViewHolder { + return FavoriteViewHolder( + FavViewHolderBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: FavoriteViewHolder, pos: Int) { + val gameData = getItem(holder.absoluteAdapterPosition) + + holder.binding.apply { + gameData.thumbnail_path?.let { + image.loadImage(it) + } + + gameData.content_more_details?.let {moreDetailsList -> + title.text = if (moreDetailsList.isNotEmpty()){ + if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1){ + moreDetailsList[1]?.title + }else{ + moreDetailsList[0]?.title + } + }else{ + gameData.title + } + } + + like.show() + likeCount.show() + fav.show() + + gameData.likes_count?.let { + likeCount.text = "$it" + } + + gameData.is_liked?.let { + like.isSelected = it + } + + like.setOnClickListener { + if (!context.isNetworkConnected()){ + context.toast(context.getString(R.string.no_internet)) + return@setOnClickListener + } + + GamesRepository.likeUnLikeGame( + "${gameData.id}", + !like.isSelected + ) + + like.isSelected = !like.isSelected + likeCount.text = "${gameData.likes_count}" + } + + fav.isSelected = gameData.mark_as_favourite == true + + fav.setOnClickListener { + if (!context.isNetworkConnected()){ + context.toast(context.getString(R.string.no_internet)) + return@setOnClickListener + } + + GamesRepository.updateFavShow( + GameData(gameData), + !fav.isSelected + ) + + notifyItemRemoved(holder.absoluteAdapterPosition) + + if (currentList.isEmpty()){ + onListEmptyListener(PostType.GAMES, true) + } + } + + root.setOnClickListener { + onGameClicked(gameData, holder.absoluteAdapterPosition) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/mylist/adapters/GamesAdapter.kt b/app/src/main/java/com/woka/home/mylist/adapters/GamesAdapter.kt deleted file mode 100644 index 6a4baf4..0000000 --- a/app/src/main/java/com/woka/home/mylist/adapters/GamesAdapter.kt +++ /dev/null @@ -1,91 +0,0 @@ -package com.woka.home.mylist.adapters - -import android.content.Context -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.AsyncDifferConfig -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import com.bumptech.glide.Glide -import com.woka.R -import com.woka.databinding.FavViewHolderBinding -import com.woka.home.mylist.MyListRepository -import com.woka.home.mylist.models.GameData -import com.woka.home.mylist.models.PostType -import com.woka.utils.isNetworkConnected -import com.woka.utils.toast -import java.util.concurrent.Executors -import kotlin.math.max - -class GamesAdapter(private val context: Context, - config: AsyncDifferConfig, - private val onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit)): ListAdapter(config) { - - companion object{ - private val DIFF_UTIL = object : DiffUtil.ItemCallback(){ - override fun areItemsTheSame(oldItem: GameData, newItem: GameData): Boolean = oldItem.id == newItem.id - override fun areContentsTheSame(oldItem: GameData, newItem: GameData): Boolean { - return oldItem.title == newItem.title && - oldItem.is_liked == newItem.is_liked && - oldItem.likes_count == newItem.likes_count - } - } - - private val DIFF_CONFIG = AsyncDifferConfig.Builder(DIFF_UTIL) - .setBackgroundThreadExecutor(Executors.newSingleThreadExecutor()) - .build() - } - - constructor(context: Context, - onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit)): this(context, DIFF_CONFIG, onListEmptyListener) - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavoriteViewHolder { - return FavoriteViewHolder( - FavViewHolderBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) - ) - } - - override fun onBindViewHolder(holder: FavoriteViewHolder, pos: Int) { - val gameData = getItem(holder.absoluteAdapterPosition) - - holder.binding.apply { - gameData.thumbnail_path?.let { - image.loadImage(it) - } - - title.text = gameData.title - - gameData.likes_count?.let { - likeCount.text = "$it" - } - - gameData.is_liked?.let { - like.isSelected = it - } - - like.setOnClickListener { - if (!context.isNetworkConnected()){ - context.toast(context.getString(R.string.no_internet)) - return@setOnClickListener - } - - - } - - fav.isSelected = true - - fav.setOnClickListener { - if (!context.isNetworkConnected()){ - context.toast(context.getString(R.string.no_internet)) - return@setOnClickListener - } - - - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/mylist/models/FavGameData.kt b/app/src/main/java/com/woka/home/mylist/models/FavGameData.kt new file mode 100644 index 0000000..4044665 --- /dev/null +++ b/app/src/main/java/com/woka/home/mylist/models/FavGameData.kt @@ -0,0 +1,46 @@ +package com.woka.home.mylist.models + +import com.woka.wokagames.models.listing.ContentMoreDetail +import com.woka.wokagames.models.listing.GameData + +data class FavGameData( + val age_range_master_id: String?, + val bookmark_category_ids: String?, + val bookmark_count: Int?, + val category_master_id: String?, + val content_more_details: List?, + val description: String?, + val game_url: String?, + val gender_master_id: String?, + val id: Int?, + var is_liked: Boolean?, + val language_master_id: Int?, + var likes_count: Int?, + val mark_as_favourite: Boolean?, + val release_date: String?, + val screen_orientation: String?, + val thumbnail_path: String?, + val title: String?, + val views_count: Int? +){ + constructor(gameData: GameData): this( + gameData.age_range_master_id, + null, + gameData.bookmark_count, + gameData.category_master_id, + gameData.content_more_details, + gameData.description, + gameData.game_url, + gameData.gender_master_id, + gameData.id, + gameData.is_liked, + gameData.language_master_id, + gameData.likes_count, + gameData.mark_as_favourite, + gameData.release_date, + gameData.screen_orientation, + gameData.thumbnail_path, + gameData.title, + null + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/mylist/models/Result.kt b/app/src/main/java/com/woka/home/mylist/models/Result.kt index 0360523..a2292f8 100644 --- a/app/src/main/java/com/woka/home/mylist/models/Result.kt +++ b/app/src/main/java/com/woka/home/mylist/models/Result.kt @@ -2,7 +2,7 @@ package com.woka.home.mylist.models data class Result( val audio_data: MutableList?, - val game_data: MutableList?, + val game_data: MutableList?, val show_data: MutableList?, val sing_karaoke_data: MutableList?, val video_data: MutableList? diff --git a/app/src/main/java/com/woka/karaoke/KaraokeApiService.kt b/app/src/main/java/com/woka/karaoke/KaraokeApiService.kt new file mode 100644 index 0000000..2484a4b --- /dev/null +++ b/app/src/main/java/com/woka/karaoke/KaraokeApiService.kt @@ -0,0 +1,14 @@ +package com.woka.karaoke + +import com.woka.karaoke.models.listing.KaraokeResponse +import com.woka.networking.ApiResponse +import okhttp3.FormBody +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.POST + +interface KaraokeApiService { + + @POST("sing_karaoke_listing") + suspend fun karaokeListing(@Body formBody: FormBody): Response> +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/karaoke/KaraokeRepository.kt b/app/src/main/java/com/woka/karaoke/KaraokeRepository.kt new file mode 100644 index 0000000..b6900f6 --- /dev/null +++ b/app/src/main/java/com/woka/karaoke/KaraokeRepository.kt @@ -0,0 +1,123 @@ +package com.woka.karaoke + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.woka.home.mylist.MyListRepository +import com.woka.home.mylist.models.PostType +import com.woka.karaoke.models.listing.KaraokeResponse +import com.woka.networking.ApiResult +import com.woka.networking.RetrofitHelper +import com.woka.networking.RetrofitHelper.handleApiCall +import com.woka.userdata.UserActionApiService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.FormBody +import kotlin.math.max + +object KaraokeRepository { + // api services + private val apiService = RetrofitHelper.getRetrofit().create(KaraokeApiService::class.java) + private val userActionApiService = RetrofitHelper.getRetrofit().create(UserActionApiService::class.java) + + // live data + + // audio books data (loose caching) + private val _karaokeLiveData = MutableLiveData>() + val karaokeLiveData: LiveData> + get() = _karaokeLiveData + + private var karaokeData: KaraokeResponse? = null + + fun loadGames(){ + if (karaokeData != null){ + _karaokeLiveData.postValue(ApiResult.Success(karaokeData)) + return + } + + CoroutineScope(Dispatchers.IO).launch{ + _karaokeLiveData.postValue(ApiResult.Loading()) + + val response = RetrofitHelper.handleApiCall { + apiService.karaokeListing( + FormBody.Builder() + .build() + ) + } + + when (response){ + is ApiResult.Error -> _karaokeLiveData.postValue(ApiResult.Error(response.errorMessage, response.error)) + is ApiResult.Loading -> _karaokeLiveData.postValue(ApiResult.Loading()) + is ApiResult.Success -> { + response.data?.let { + karaokeData = it + _karaokeLiveData.postValue(ApiResult.Success(it)) + }?:{ + _karaokeLiveData.postValue(ApiResult.Error()) + } + } + } + } + } + + fun likeUnLikeGame(postId: String, likeIt: Boolean){ + CoroutineScope(Dispatchers.IO).launch { + handleApiCall { + if (likeIt){ + userActionApiService.likePost( + FormBody.Builder() + .add("post_id", postId) + .add("post_type", PostType.KARAOKE.value) + .build() + ) + }else{ + userActionApiService.unLikePost( + FormBody.Builder() + .add("post_id", postId) + .add("post_type", PostType.KARAOKE.value) + .build() + ) + } + } + } + + changeLikeLocally(postId, likeIt) + } + + private fun changeLikeLocally(id: String, isLiked: Boolean){ + // changing in karaoke locally + karaokeData?.karaoke_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?.sing_karaoke_data?.let { + for (audioData in it){ + if ("${audioData.id}" == id){ + audioData.is_liked = isLiked + audioData.likes_count?.let { count -> + audioData.likes_count = if (isLiked) count + 1 + else max(0, count - 1) + } + break + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/karaoke/models/listing/ContentMoreDetail.kt b/app/src/main/java/com/woka/karaoke/models/listing/ContentMoreDetail.kt new file mode 100644 index 0000000..264ee53 --- /dev/null +++ b/app/src/main/java/com/woka/karaoke/models/listing/ContentMoreDetail.kt @@ -0,0 +1,12 @@ +package com.woka.karaoke.models.listing + +data class ContentMoreDetail( + val content_id: Int?, + val description: String?, + val id: Int?, + val language_master_id: Int?, + val post_type: Int?, + val tags_keywords: String?, + val title: String?, + val url: String? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/mylist/models/GameData.kt b/app/src/main/java/com/woka/karaoke/models/listing/KaraokeData.kt similarity index 60% rename from app/src/main/java/com/woka/home/mylist/models/GameData.kt rename to app/src/main/java/com/woka/karaoke/models/listing/KaraokeData.kt index a27eed8..402dc49 100644 --- a/app/src/main/java/com/woka/home/mylist/models/GameData.kt +++ b/app/src/main/java/com/woka/karaoke/models/listing/KaraokeData.kt @@ -1,13 +1,15 @@ -package com.woka.home.mylist.models +package com.woka.karaoke.models.listing -data class GameData( +data class KaraokeData( + val age_range_data: List?, val age_range_master_id: String?, - val bookmark_category_ids: String?, val bookmark_count: Int?, + val category_data: List?, val category_master_id: String?, -// val content_more_details: List?, + val content_more_details: List?, val description: String?, - val game_url: String?, + val duration: String?, + val gender_data: List?, val gender_master_id: String?, val id: Int?, var is_liked: Boolean?, @@ -15,8 +17,8 @@ data class GameData( var likes_count: Int?, val mark_as_favourite: Boolean?, val release_date: String?, - val screen_orientation: String?, val thumbnail_path: String?, val title: String?, + val video_url: String?, val views_count: Int? ) \ No newline at end of file diff --git a/app/src/main/java/com/woka/karaoke/models/listing/KaraokeResponse.kt b/app/src/main/java/com/woka/karaoke/models/listing/KaraokeResponse.kt new file mode 100644 index 0000000..de210bc --- /dev/null +++ b/app/src/main/java/com/woka/karaoke/models/listing/KaraokeResponse.kt @@ -0,0 +1,6 @@ +package com.woka.karaoke.models.listing + +data class KaraokeResponse( + val karaoke_data: List?, + val total_records: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/GamesRepository.kt b/app/src/main/java/com/woka/wokagames/GamesRepository.kt new file mode 100644 index 0000000..27a8452 --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/GamesRepository.kt @@ -0,0 +1,169 @@ +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 +import com.woka.networking.RetrofitHelper +import com.woka.networking.RetrofitHelper.handleApiCall +import com.woka.userdata.UserActionApiService +import com.woka.wokagames.models.listing.GameData +import com.woka.wokagames.models.listing.GamesResponse +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.FormBody +import kotlin.math.max + +object GamesRepository { + // api services + 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>() + val gamesLiveData: LiveData> + 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()) + } + } + } + } + } + + fun likeUnLikeGame(postId: String, likeIt: Boolean){ + CoroutineScope(Dispatchers.IO).launch { + handleApiCall { + if (likeIt){ + userActionApiService.likePost( + FormBody.Builder() + .add("post_id", postId) + .add("post_type", PostType.GAMES.value) + .build() + ) + }else{ + userActionApiService.unLikePost( + FormBody.Builder() + .add("post_id", postId) + .add("post_type", PostType.GAMES.value) + .build() + ) + } + } + } + + changeLikeLocally(postId, likeIt) + } + + 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){ + if ("${audioData.id}" == id){ + audioData.is_liked = isLiked + audioData.likes_count?.let { count -> + audioData.likes_count = if (isLiked) count + 1 + else max(0, count - 1) + } + break + } + } + } + } + + fun updateFavShow(gameData: GameData, addToBookmark: Boolean){ + CoroutineScope(Dispatchers.IO).launch { + handleApiCall { + if (addToBookmark){ + userActionApiService.addToFav( + FormBody.Builder() + .add("post_id", "${gameData.id}") + .add("post_type", PostType.GAMES.value) + .build() + ) + }else{ + userActionApiService.removeFromFav( + FormBody.Builder() + .add("id", "${gameData.id}") + .add("post_type", PostType.GAMES.value) + .build() + ) + } + } + } + + MyListRepository.myFavData.result?.game_data?.let {favAudioData -> + gameData.mark_as_favourite = addToBookmark + if (addToBookmark){ + favAudioData.add(FavGameData(gameData)) + }else{ + 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 + } + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/WokaGamesApiService.kt b/app/src/main/java/com/woka/wokagames/WokaGamesApiService.kt new file mode 100644 index 0000000..3e3b31b --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/WokaGamesApiService.kt @@ -0,0 +1,14 @@ +package com.woka.wokagames + +import com.woka.networking.ApiResponse +import com.woka.wokagames.models.listing.GamesResponse +import okhttp3.FormBody +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.POST + +interface WokaGamesApiService { + + @POST("game_listing") + suspend fun gamesListing(@Body formBody: FormBody): Response> +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/adapters/GamesAdapter.kt b/app/src/main/java/com/woka/wokagames/adapters/GamesAdapter.kt new file mode 100644 index 0000000..51ea1d0 --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/adapters/GamesAdapter.kt @@ -0,0 +1,121 @@ +package com.woka.wokagames.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.AsyncDifferConfig +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.woka.R +import com.woka.WokaApp +import com.woka.databinding.ShowViewHolderBinding +import com.woka.utils.isNetworkConnected +import com.woka.utils.show +import com.woka.utils.toast +import com.woka.wokagames.GamesRepository +import com.woka.wokagames.models.listing.GameData +import java.util.concurrent.Executors + +class GamesAdapter(var context: Context, + private var onGameClicked: (GameData) -> Unit): ListAdapter( + ASYNC_DIFF_UTIL) { + inner class GameViewHolder(var binding: ShowViewHolderBinding): ViewHolder(binding.root) + + companion object{ + val DIFF_UTIL = object : DiffUtil.ItemCallback(){ + override fun areItemsTheSame(oldItem: GameData, newItem: GameData): Boolean = oldItem.id == newItem.id + + override fun areContentsTheSame(oldItem: GameData, newItem: GameData): Boolean { + return oldItem.thumbnail_path == newItem.thumbnail_path && + oldItem.title == newItem.title && + oldItem.is_liked == newItem.is_liked && + oldItem.mark_as_favourite == newItem.mark_as_favourite && + oldItem.likes_count == newItem.likes_count + } + } + + val ASYNC_DIFF_UTIL = AsyncDifferConfig.Builder(DIFF_UTIL) + .setBackgroundThreadExecutor(Executors.newSingleThreadExecutor()) + .build() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder { + return GameViewHolder( + ShowViewHolderBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: GameViewHolder, position: Int) { + val gameData = getItem(holder.absoluteAdapterPosition) + + holder.binding.apply { + gameData.thumbnail_path?.let { + image.loadImage(it) + } + + gameData.content_more_details?.let {moreDetailsList -> + title.text = if (moreDetailsList.isNotEmpty()){ + if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1){ + moreDetailsList[1]?.title + }else{ + moreDetailsList[0]?.title + } + }else{ + gameData.title + } + } + + like.show() + likeCount.show() + fav.show() + + gameData.likes_count?.let { + likeCount.text = "$it" + } + + gameData.is_liked?.let { + like.isSelected = it + } + + like.setOnClickListener { + if (!context.isNetworkConnected()){ + context.toast(context.getString(R.string.no_internet)) + return@setOnClickListener + } + + GamesRepository.likeUnLikeGame( + "${gameData.id}", + !like.isSelected + ) + + like.isSelected = !like.isSelected + likeCount.text = "${gameData.likes_count}" + } + + fav.isSelected = gameData.mark_as_favourite == true + + fav.setOnClickListener { + if (!context.isNetworkConnected()){ + context.toast(context.getString(R.string.no_internet)) + return@setOnClickListener + } + + GamesRepository.updateFavShow( + gameData, + !fav.isSelected + ) + + fav.isSelected = gameData.mark_as_favourite == true + } + + root.setOnClickListener { + onGameClicked(gameData) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/models/listing/ContentMoreDetail.kt b/app/src/main/java/com/woka/wokagames/models/listing/ContentMoreDetail.kt new file mode 100644 index 0000000..46dc191 --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/models/listing/ContentMoreDetail.kt @@ -0,0 +1,12 @@ +package com.woka.wokagames.models.listing + +data class ContentMoreDetail( + val content_id: Int?, + val description: String?, + val id: Int?, + val language_master_id: Int?, + val post_type: Int?, + val tags_keywords: String?, + val title: String?, + val url: String? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/models/listing/GameData.kt b/app/src/main/java/com/woka/wokagames/models/listing/GameData.kt new file mode 100644 index 0000000..484fba4 --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/models/listing/GameData.kt @@ -0,0 +1,47 @@ +package com.woka.wokagames.models.listing + +import com.woka.home.mylist.models.FavGameData + +data class GameData( + val age_range_data: List?, + val age_range_master_id: String?, + val bookmark_count: Int?, + val category_data: List?, + val category_master_id: String?, + val content_more_details: List?, + val description: String?, + val game_url: String?, + val gender_data: List?, + val gender_master_id: String?, + val id: Int?, + var is_liked: Boolean?, + val language_master_id: Int?, + var likes_count: Int?, + var mark_as_favourite: Boolean?, + val release_date: String?, + val screen_orientation: String?, + val thumbnail_path: String?, + val title: String? +){ + constructor(favGame: FavGameData): this( + null, + favGame.age_range_master_id, + favGame.bookmark_count, + null, + favGame.category_master_id, + favGame.content_more_details, + favGame.description, + favGame.game_url, + null, + favGame.gender_master_id, + favGame.id, + favGame.is_liked, + favGame.language_master_id, + favGame.likes_count, + favGame.mark_as_favourite, + favGame.release_date, + favGame.screen_orientation, + favGame.thumbnail_path, + favGame.title + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/models/listing/GamesResponse.kt b/app/src/main/java/com/woka/wokagames/models/listing/GamesResponse.kt new file mode 100644 index 0000000..30b3343 --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/models/listing/GamesResponse.kt @@ -0,0 +1,6 @@ +package com.woka.wokagames.models.listing + +data class GamesResponse( + val game_data: List?, + val total_records: Int? +) \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/playerr/GamePlayerActivity.kt b/app/src/main/java/com/woka/wokagames/playerr/GamePlayerActivity.kt new file mode 100644 index 0000000..f37bb82 --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/playerr/GamePlayerActivity.kt @@ -0,0 +1,107 @@ +package com.woka.wokagames.playerr + +import android.annotation.SuppressLint +import android.content.pm.ActivityInfo +import android.graphics.Color +import android.os.Build +import android.os.Bundle +import android.view.WindowManager +import android.webkit.WebSettings +import android.webkit.WebViewClient +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import com.woka.R +import com.woka.databinding.ActivityGamePlayerBinding +import com.woka.utils.DecisionDialog +import com.woka.utils.WokaBaseActivity +import com.woka.utils.lightStatusBar + +class GamePlayerActivity : WokaBaseActivity() { + + companion object{ + const val EXTRA_GAME_PLAYER_DATA = "extra_game_url" + } + + private lateinit var binding: ActivityGamePlayerBinding + private lateinit var decisionDialog: DecisionDialog + + private var gamePlayerData: GamePlayerData? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityGamePlayerBinding.inflate(layoutInflater) + setContentView(binding.root) + + val windowInsetsController = + WindowCompat.getInsetsController(window, window.decorView) + windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) + + decisionDialog = DecisionDialog(this).apply { + title = getString(R.string.warning) + message = getString(R.string.back_warning_text) + } + + intent.getParcelableExtra(EXTRA_GAME_PLAYER_DATA)?.let { + gamePlayerData = it + requestedOrientation = if (it.landscape){ + ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE + }else{ + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + initWebView(it.gameUrl) + } + } + + override fun onStart() { + super.onStart() + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + + override fun onStop() { + super.onStop() + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + + override fun onResume() { + super.onResume() + binding.webView.onResume() + } + + override fun onPause() { + super.onPause() + binding.webView.onPause() + } + + override fun onDestroy() { + super.onDestroy() + binding.webView.destroy() + } + + @SuppressLint("SetJavaScriptEnabled") + private fun initWebView(it: String){ + val webSettings: WebSettings = binding.webView.getSettings() + webSettings.javaScriptEnabled = true + + webSettings.allowFileAccess = false; + webSettings.allowContentAccess = false; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + webSettings.safeBrowsingEnabled = true + }; + + binding.webView.setWebViewClient(WebViewClient()) + + binding.webView.loadUrl(it) + } + + @Deprecated("Deprecated in Java") + override fun onBackPressed() { + decisionDialog.setPositiveButton(getString(R.string.ok_caps)){ + super.onBackPressed() + } + decisionDialog.setNegativeButton(getString(R.string.cancel_caps)){ + decisionDialog.dismiss() + } + decisionDialog.show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/playerr/GamePlayerData.kt b/app/src/main/java/com/woka/wokagames/playerr/GamePlayerData.kt new file mode 100644 index 0000000..db3b917 --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/playerr/GamePlayerData.kt @@ -0,0 +1,10 @@ +package com.woka.wokagames.playerr + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class GamePlayerData( + val gameUrl: String, + val landscape: Boolean = false +): Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/woka/wokagames/views/GamesActivity.kt b/app/src/main/java/com/woka/wokagames/views/GamesActivity.kt new file mode 100644 index 0000000..375b336 --- /dev/null +++ b/app/src/main/java/com/woka/wokagames/views/GamesActivity.kt @@ -0,0 +1,317 @@ +package com.woka.wokagames.views + +import android.app.Dialog +import android.content.Intent +import android.content.res.ColorStateList +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.WindowManager +import androidx.activity.enableEdgeToEdge +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.recyclerview.widget.SimpleItemAnimator +import com.google.android.material.appbar.CollapsingToolbarLayout +import com.woka.R +import com.woka.WokaApp +import com.woka.databinding.ActivityGamesBinding +import com.woka.databinding.DialogModuleShowerBinding +import com.woka.networking.ApiResult +import com.woka.utils.WokaBaseActivity +import com.woka.utils.hide +import com.woka.utils.show +import com.woka.wokagames.GamesRepository +import com.woka.wokagames.adapters.GamesAdapter +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 java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale + +class GamesActivity : WokaBaseActivity() { + + private lateinit var binding: ActivityGamesBinding + + private lateinit var gameAdapter: GamesAdapter + + private lateinit var dialogBinding: DialogModuleShowerBinding + private lateinit var gameDialog: Dialog + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityGamesBinding.inflate(layoutInflater) + enableEdgeToEdge() + setContentView(binding.root) + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> + val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) + insets + } + + window.apply { + navigationBarColor = getColor(R.color.color_primary_dark) + } + + gameAdapter = GamesAdapter(this, ::onGameClicked) + + dialogBinding = DialogModuleShowerBinding.inflate(layoutInflater) + gameDialog = Dialog(this) + gameDialog.setContentView(dialogBinding.root) + + initViews() + + initGameDialog() + + clickEvents() + + setObservers() + + GamesRepository.loadGames() + } + + private fun initViews() { + binding.apply { + adjustTrailerImage() + + toolbar.title.text = getString(R.string.games) + + rvGames.adapter = gameAdapter + (rvGames.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false + } + } + + private fun initGameDialog() { + try { + val back = ColorDrawable(Color.TRANSPARENT) + val inset = InsetDrawable(back, 50) + gameDialog.window!!.setBackgroundDrawable(inset) + } catch (e: Exception) { + // do nothing + } + + try { + val layoutParams = gameDialog.window!!.attributes + layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT + layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT + gameDialog.window!!.setAttributes(layoutParams) + } catch (e: Exception) { + // do nothing + } + + dialogBinding.close.setOnClickListener { gameDialog.dismiss() } + + dialogBinding.watchCard.backgroundTintList = ColorStateList.valueOf(getColor(R.color.game_grad_one)) + dialogBinding.watchCard.text = getString(R.string.play_now) + } + + private fun clickEvents() { + binding.apply { + toolbar.backBtn.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + } + + retryBtn.setOnClickListener { + GamesRepository.loadGames() + } + } + } + + private fun onGameClicked(gameData: GameData) { + loadTrailerData(gameData) + showGameDialog(gameData) + } + + private fun loadTrailerData(gameData: GameData) { + binding.apply { + trailerView.show() + adjustTrailerImage() + + gameData.thumbnail_path?.let { + trailerImage.loadImage(it) + } + + gameData.content_more_details?.let { moreDetailsList -> + trailerName.text = if (moreDetailsList.isNotEmpty()) { + if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) { + moreDetailsList[1]?.title + } else { + moreDetailsList[0]?.title + } + } else { + gameData.title + } + } + + trailerBtn.setOnClickListener { + gameData.game_url?.let { + startActivity(Intent(this@GamesActivity, GamePlayerActivity::class.java).apply { + putExtra(EXTRA_GAME_PLAYER_DATA, GamePlayerData(it, gameData.screen_orientation == "Landscape")) + }) + } + } + } + } + + private fun adjustTrailerImage() { + // making space for Trailer image + binding.apply { + topPinnedView.post { + val imgParams = trailerImage.layoutParams as CollapsingToolbarLayout.LayoutParams + imgParams.setMargins(0, 0, 0, topPinnedView.height) + trailerImage.layoutParams = imgParams + } + } + } + + private fun showGameDialog(gameData: GameData) { + dialogBinding.apply { + gameData.content_more_details?.let { moreDetailsList -> + + gameData.thumbnail_path?.let { + image.loadImage(it) + } + + fav.isSelected = gameData.mark_as_favourite == true + like.isSelected = gameData.is_liked == true + likeCount.text = "${gameData.likes_count}" + + year.text = try { + gameData.release_date?.let { + val cal = Calendar.getInstance() + cal.time = + SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).parse( + gameData.release_date + )!! + "${cal.get(Calendar.YEAR)}" + } ?: throw Exception() + } catch (e: Exception) { + "${gameData.release_date}" + } + + if (moreDetailsList.isNotEmpty()) { + + if (WokaApp.userPrefs?.appLanguage == "hi" && moreDetailsList.size > 1) { + moreDetailsList[1]?.let { data -> + title.text = data.title + description.text = Html.fromHtml( + data.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + } + } else { + moreDetailsList[0]?.let { data -> + title.text = data.title + description.text = Html.fromHtml( + data.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + } + } + } else { + title.text = gameData.title + description.text = Html.fromHtml( + gameData.description?.replace( + "
", + " " + ), Html.FROM_HTML_MODE_LEGACY + ) + } + + watchCard.setOnClickListener { + gameData.game_url?.let { + startActivity(Intent(this@GamesActivity, GamePlayerActivity::class.java).apply { + putExtra(EXTRA_GAME_PLAYER_DATA, GamePlayerData(it, gameData.screen_orientation == "Landscape")) + }) + } + } + + like.setOnClickListener { + GamesRepository.likeUnLikeGame( + "${gameData.id}", + !like.isSelected + ) + + gameData.id?.let { + onGameChanged(it) + } + like.isSelected = !like.isSelected + likeCount.text = "${gameData.likes_count}" + } + + fav.setOnClickListener { + GamesRepository.updateFavShow( + gameData, + !fav.isSelected + ) + + gameData.id?.let { + onGameChanged(it) + } + + fav.isSelected = !fav.isSelected + } + + close.setOnClickListener { + gameDialog.dismiss() + } + + gameDialog.show() + } + } + } + + private fun onGameChanged(id: Int) { + // updating continue book list + val position = gameAdapter.currentList.indexOfFirst { it.id == id } + if (position >= 0 && position < gameAdapter.currentList.size) { + gameAdapter.notifyItemChanged(position) + } + } + + private fun setObservers(){ + GamesRepository.gamesLiveData.observe(this) { + when (it) { + is ApiResult.Error -> { + binding.shimmer.hide() + binding.rvGames.hide() + binding.listenTxt.hide() + binding.trailerView.hide() + + binding.errorView.show() + } + + is ApiResult.Loading -> { + binding.shimmer.show() + + binding.rvGames.hide() + binding.listenTxt.hide() + binding.trailerView.hide() + binding.errorView.hide() + } + + is ApiResult.Success -> { + it.data?.game_data?.filterNotNull()?.let { gameData -> + if (gameData.isNotEmpty()) { + binding.shimmer.hide() + binding.errorView.hide() + + loadTrailerData(gameData[0]) + binding.rvGames.show() + binding.listenTxt.show() + + gameAdapter.submitList(gameData) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/gradient_woka_games.xml b/app/src/main/res/drawable/gradient_woka_games.xml new file mode 100644 index 0000000..ec483cf --- /dev/null +++ b/app/src/main/res/drawable/gradient_woka_games.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/play_btn_bg.xml b/app/src/main/res/drawable/play_btn_bg.xml new file mode 100644 index 0000000..275beec --- /dev/null +++ b/app/src/main/res/drawable/play_btn_bg.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_game_player.xml b/app/src/main/res/layout/activity_game_player.xml new file mode 100644 index 0000000..7a6eed1 --- /dev/null +++ b/app/src/main/res/layout/activity_game_player.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_games.xml b/app/src/main/res/layout/activity_games.xml new file mode 100644 index 0000000..83767b5 --- /dev/null +++ b/app/src/main/res/layout/activity_games.xml @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +