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
This commit is contained in:
2024-07-08 19:57:39 +05:30
parent 6b193b1cc7
commit 294afcb80a
34 changed files with 1787 additions and 217 deletions

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
@@ -14,11 +14,22 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Woka"
tools:targetApi="31" >
tools:targetApi="31">
<activity
android:name=".wokagames.playerr.GamePlayerActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:exported="false"
android:launchMode="singleTask"
android:supportsPictureInPicture="true"
android:theme="@style/FullScreenTheme"/>
<activity
android:name=".wokagames.views.GamesActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".audiobooks.views.AudioBooksActivity"
android:exported="false"
android:screenOrientation="portrait"/>
android:screenOrientation="portrait" />
<activity
android:name=".players.views.PlayerActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
@@ -103,7 +114,7 @@
android:name=".onboard.views.WelcomeActivity"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@style/Theme.App.Starting" >
android:theme="@style/Theme.App.Starting">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -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<AudioBooksAdapter.AudioBookViewHolder>() {
): ListAdapter<AudioBookData, AudioBooksAdapter.AudioBookViewHolder>(ASYNC_DIFF_UTIL) {
inner class AudioBookViewHolder(val binding: ShowViewHolderBinding): ViewHolder(binding.root)
var audioBookList: List<AudioBookData> = ArrayList()
companion object{
val DIFF_UTIL = object : DiffUtil.ItemCallback<AudioBookData>(){
override fun areItemsTheSame(oldItem: AudioBookData, newItem: AudioBookData): Boolean = oldItem.id == newItem.id
@SuppressLint("NotifyDataSetChanged")
fun submitListShowList(it: List<AudioBookData>) {
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 {

View File

@@ -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 {

View File

@@ -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))
}
}
}
}

View File

@@ -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<Intent>? = 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<PlaylistItem>().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<PlaylistItem>().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(
"<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 = gameData.title
description.text = Html.fromHtml(
gameData.description?.replace(
"<br>",
" "
), 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()
}

View File

@@ -21,7 +21,7 @@ import java.util.concurrent.Executors
class FavAudioAdapter(private val context: Context,
config: AsyncDifferConfig<FavAudioBookData>,
private val onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit),
private var onBookClicked: (AudioBookData, Int) -> Unit): ListAdapter<FavAudioBookData, FavoriteViewHolder>(config) {
private var onBookClicked: (FavAudioBookData, Int) -> Unit): ListAdapter<FavAudioBookData, FavoriteViewHolder>(config) {
companion object{
private val DIFF_UTIL = object : DiffUtil.ItemCallback<FavAudioBookData>(){
@@ -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)
}
}
}

View File

@@ -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<FavGameData>,
private val onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit),
private val onGameClicked: ((FavGameData, Int) -> Unit)):
ListAdapter<FavGameData, FavoriteViewHolder>(config) {
companion object{
private val DIFF_UTIL = object : DiffUtil.ItemCallback<FavGameData>(){
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)
}
}
}
}

View File

@@ -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<GameData>,
private val onListEmptyListener: ((postType: PostType, isEng: Boolean) -> Unit)): ListAdapter<GameData, FavoriteViewHolder>(config) {
companion object{
private val DIFF_UTIL = object : DiffUtil.ItemCallback<GameData>(){
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
}
}
}
}
}

View File

@@ -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<ContentMoreDetail?>?,
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
)
}

View File

@@ -2,7 +2,7 @@ package com.woka.home.mylist.models
data class Result(
val audio_data: MutableList<FavAudioBookData>?,
val game_data: MutableList<GameData>?,
val game_data: MutableList<FavGameData>?,
val show_data: MutableList<BookmarkedShowData>?,
val sing_karaoke_data: MutableList<SingKaraokeData>?,
val video_data: MutableList<Any>?

View File

@@ -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<ApiResponse<KaraokeResponse>>
}

View File

@@ -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<ApiResult<KaraokeResponse>>()
val karaokeLiveData: LiveData<ApiResult<KaraokeResponse>>
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
}
}
}
}
}

View File

@@ -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?
)

View File

@@ -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<Any?>?,
val age_range_master_id: String?,
val bookmark_category_ids: String?,
val bookmark_count: Int?,
val category_data: List<Any?>?,
val category_master_id: String?,
// val content_more_details: List<ContentMore>?,
val content_more_details: List<ContentMoreDetail?>?,
val description: String?,
val game_url: String?,
val duration: String?,
val gender_data: List<Any?>?,
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?
)

View File

@@ -0,0 +1,6 @@
package com.woka.karaoke.models.listing
data class KaraokeResponse(
val karaoke_data: List<KaraokeData?>?,
val total_records: Int?
)

View File

@@ -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<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())
}
}
}
}
}
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
}
}
}
}
}

View File

@@ -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<ApiResponse<GamesResponse>>
}

View File

@@ -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<GameData, GamesAdapter.GameViewHolder>(
ASYNC_DIFF_UTIL) {
inner class GameViewHolder(var binding: ShowViewHolderBinding): ViewHolder(binding.root)
companion object{
val DIFF_UTIL = object : DiffUtil.ItemCallback<GameData>(){
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)
}
}
}
}

View File

@@ -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?
)

View File

@@ -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<Any?>?,
val age_range_master_id: String?,
val bookmark_count: Int?,
val category_data: List<Any?>?,
val category_master_id: String?,
val content_more_details: List<ContentMoreDetail?>?,
val description: String?,
val game_url: String?,
val gender_data: List<Any?>?,
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
)
}

View File

@@ -0,0 +1,6 @@
package com.woka.wokagames.models.listing
data class GamesResponse(
val game_data: List<GameData?>?,
val total_records: Int?
)

View File

@@ -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<GamePlayerData?>(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()
}
}

View File

@@ -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

View File

@@ -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(
"<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 = gameData.title
description.text = Html.fromHtml(
gameData.description?.replace(
"<br>",
" "
), 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)
}
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:startColor="#F38600"
android:endColor="#9742E4"
android:centerColor="#9742E4"
android:centerX="0.3"
android:angle="-45"
/>
</shape>

View File

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

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:fitsSystemWindows="true"
tools:context=".wokagames.playerr.GamePlayerActivity"/>

View File

@@ -0,0 +1,255 @@
<?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_woka_games"
android:orientation="vertical"
tools:context=".webseries.views.WebSeriesActivity">
<include android:id="@+id/toolbar"
layout="@layout/layout_toolbar"/>
<RelativeLayout
android:id="@+id/shimmer"
android:visibility="gone"
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"
android:layout_gravity="center_horizontal"
/>
<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"
android:background="@color/color_primary_dark"
>
<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">
<com.woka.utils.AdiImageView
android:id="@+id/trailer_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:id="@+id/trailer_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="@string/masila"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_16ssp"
/>
<Button
android:id="@+id/trailer_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/play"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_12ssp"
android:paddingHorizontal="25dp"
android:layout_marginTop="15dp"
android:background="@drawable/play_btn_bg"
/>
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
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/listen_txt"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/have_a_fun_time"
android:fontFamily="@font/exo_2_bold"
android:textColor="@color/white"
android:textSize="@dimen/_14ssp"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_games"
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"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

View File

@@ -60,18 +60,63 @@
/>
<TextView
android:id="@+id/year"
android:layout_width="wrap_content"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="2022"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="5dp"
/>
android:layout_marginTop="2dp">
<TextView
android:id="@+id/year"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="2022"
android:fontFamily="@font/exo_2"
android:textColor="@color/color_primary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="3dp"
/>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:contentDescription="@string/image"
android:src="@drawable/ic_like_filled"
app:layout_constraintTop_toTopOf="@id/like_count"
app:layout_constraintBottom_toBottomOf="@id/like_count"
app:layout_constraintEnd_toStartOf="@id/like_count"
android:layout_marginBottom="3dp"
android:layout_marginEnd="3dp"
/>
<TextView
android:id="@+id/like_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="10"
android:fontFamily="@font/exo_2_semibold"
android:textColor="@color/color_primary"
android:includeFontPadding="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<ScrollView
android:id="@+id/scrollView"
@@ -106,7 +151,7 @@
android:layout_height="wrap_content"
android:background="@drawable/round_bg_25"
android:backgroundTint="#007AC0"
android:backgroundTint="@color/audio_grad_one"
android:drawableStart="@drawable/ic_play_filled"
android:drawablePadding="15dp"

View File

@@ -0,0 +1,6 @@
<resources>
<declare-styleable name="FullscreenAttrs">
<attr name="fullscreenBackgroundColor" format="color" />
<attr name="fullscreenTextColor" format="color" />
</declare-styleable>
</resources>

View File

@@ -20,4 +20,11 @@
<color name="more_bg">#cbe16f</color>
<color name="orders_bg">#D3EFF8</color>
<color name="game_grad_one">#CC4B1D</color>
<color name="audio_grad_one">#007AC0</color>
<color name="light_blue_600">#FF039BE5</color>
<color name="light_blue_900">#FF01579B</color>
<color name="light_blue_A200">#FF40C4FF</color>
<color name="light_blue_A400">#FF00B0FF</color>
<color name="black_overlay">#66000000</color>
</resources>

View File

@@ -189,4 +189,13 @@
<string name="listen_and_enjoy">LISTEN and ENJOY</string>
<string name="continue_audio_book">CONTINUE AUDIO BOOK</string>
<string name="listen_now">LISTEN NOW</string>
<string name="have_a_fun_time">HAVE A FUN TIME</string>
<string name="play_now">PLAY NOW</string>
<string name="title_activity_game_player">GamePlayerActivity</string>
<string name="dummy_button">Dummy Button</string>
<string name="dummy_content">DUMMY\nCONTENT</string>
<string name="warning">Warning</string>
<string name="back_warning_text">Clicking \"OK\" will exit you from the game.\nAre you sure you want to proceed?</string>
<string name="cancel_caps">CANCEL</string>
<string name="ok_caps">OK</string>
</resources>

View File

@@ -0,0 +1,11 @@
<resources>
<style name="Widget.Theme.Woka.ActionBar.Fullscreen" parent="Widget.AppCompat.ActionBar">
<item name="android:background">@color/black_overlay</item>
</style>
<style name="Widget.Theme.Woka.ButtonBar.Fullscreen" parent="">
<item name="android:background">@color/black_overlay</item>
<item name="android:buttonBarStyle">?android:attr/buttonBarStyle</item>
</style>
</resources>

View File

@@ -14,7 +14,7 @@
<item name="postSplashScreenTheme">@style/Theme.Woka</item>
</style>
<style name="MyDatePicker" >
<style name="MyDatePicker">
<!-- Text color -->
<item name="android:textColorPrimary">@color/color_primary</item>
<!-- Divider color -->
@@ -30,12 +30,14 @@
<item name="android:windowTranslucentStatus">true</item>
</style>
<!-- full screen view-->
<!-- full screen view-->
<style name="FullScreenTheme" parent="Theme.Material3.Light.NoActionBar">
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="o_mr1">shortEdges</item>
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="o_mr1">shortEdges
</item>
<item name="android:windowDisablePreview">true</item>
<item name="android:windowBackground">@color/black</item>
</style>
<style name="Theme.Woka" parent="Base.Theme.Woka" />