diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f8c8ba2..aaa7c99 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + >() - val karaokeLiveData: LiveData> - get() = _karaokeLiveData - - private var karaokeData: KaraokeResponse? = null - // continue sing karaoke data private val _continueKaraokeLiveData = MutableLiveData>() val continueKaraokeLiveData: LiveData> @@ -39,37 +31,15 @@ object KaraokeRepository { private var continueKaraokeData: ContinueKaraokeResponse? = null - fun loadKaraokeSongs(){ - if (karaokeData != null){ - _karaokeLiveData.postValue(ApiResult.Success(karaokeData)) - return - } - - CoroutineScope(Dispatchers.IO).launch{ - _karaokeLiveData.postValue(ApiResult.Loading()) - - val response = handleApiCall { - apiService.karaokeListing( - FormBody.Builder() - .add("api_version", "v2") - .add("start", "0") - .add("limit", "10") - .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()) - } - } - } + suspend fun loadKaraokeSongs(pageNo: Int, quantity: Int): ApiResult { + return handleApiCall { + apiService.karaokeListing( + FormBody.Builder() + .add("api_version", "v2") + .add("start", "$pageNo") + .add("limit", "$quantity") + .build() + ) } } @@ -127,26 +97,6 @@ object KaraokeRepository { } 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 - } - } // continue Karaoke data update continueKaraokeData?.result?.let { @@ -214,15 +164,6 @@ object KaraokeRepository { } } - karaokeData?.karaoke_data?.let { - for (audio in it){ - if (audio?.id == karaoke.id){ - audio?.mark_as_favourite = addToBookmark - break - } - } - } - continueKaraokeData?.result?.let { for (audio in it){ if (audio?.id == karaoke.id){ @@ -235,7 +176,6 @@ object KaraokeRepository { } fun clearData(){ - karaokeData = null continueKaraokeData = null } } \ 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 index de210bc..6fe90f4 100644 --- a/app/src/main/java/com/woka/karaoke/models/listing/KaraokeResponse.kt +++ b/app/src/main/java/com/woka/karaoke/models/listing/KaraokeResponse.kt @@ -1,6 +1,6 @@ package com.woka.karaoke.models.listing data class KaraokeResponse( - val karaoke_data: List?, + val karaoke_data: MutableList?, val total_records: Int? ) \ No newline at end of file diff --git a/app/src/main/java/com/woka/karaoke/player/KaraokePlayerActivity.kt b/app/src/main/java/com/woka/karaoke/player/KaraokePlayerActivity.kt index 21ea594..17153fa 100644 --- a/app/src/main/java/com/woka/karaoke/player/KaraokePlayerActivity.kt +++ b/app/src/main/java/com/woka/karaoke/player/KaraokePlayerActivity.kt @@ -1,9 +1,8 @@ package com.woka.karaoke.player import android.Manifest -import android.R.attr.mimeType +import android.app.AlertDialog import android.content.ContentValues -import android.content.Context import android.content.pm.PackageManager import android.media.MediaMetadataRetriever import android.media.MediaPlayer @@ -15,7 +14,6 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Environment -import android.os.ParcelFileDescriptor import android.provider.MediaStore import android.util.Log import android.view.WindowManager @@ -23,7 +21,6 @@ import androidx.activity.enableEdgeToEdge import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.OptIn -import androidx.annotation.RequiresApi import androidx.core.app.ActivityCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat @@ -92,6 +89,7 @@ class KaraokePlayerActivity : WokaBaseActivity() { private var audioPlayer: MediaPlayer? = null private lateinit var permissionLauncher: ActivityResultLauncher + private lateinit var storagePermissionLauncher: ActivityResultLauncher override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -149,20 +147,6 @@ class KaraokePlayerActivity : WokaBaseActivity() { } - override fun onStart() { - super.onStart() - if (player?.isPlaying == false) { - player?.play() - } - } - - override fun onStop() { - super.onStop() - if (player?.isPlaying == true) { - player?.pause() - } - } - override fun onDestroy() { super.onDestroy() window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) @@ -178,11 +162,18 @@ class KaraokePlayerActivity : WokaBaseActivity() { (getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager).unregisterNetworkCallback( networkCallback ) + + File(recordingOutputPath).deleteOnExit() + File(karaokeFinalPath).deleteOnExit() + File(karaokeMusicPath).deleteOnExit() } private fun initViews() { binding.apply { title.text = karaokePlayerData?.title + + binding.audioBtn.isEnabled = false + binding.downloadBtn.isEnabled = false } } @@ -207,26 +198,26 @@ class KaraokePlayerActivity : WokaBaseActivity() { } audioBtn.setOnClickListener { - playStopPlayingAudio() + if (audioBtn.isEnabled){ + playStopPlayingAudio() + } } downloadBtn.setOnClickListener { - try { - val cal = Calendar.getInstance() - val fileName = "${karaokePlayerData?.title}_${ - SimpleDateFormat( - "dd MMM yyyy hh:mm a", - Locale.getDefault() - ).format(cal.time) - }.mp3" + if (audioBtn.isEnabled){ + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ + saveFileUsingMediaStore() + }else{ + storagePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } - saveFileUsingMediaStore(fileName) - - } catch (e: FileNotFoundException) { - toast(getString(R.string.no_recorded_audio_found)) - } catch (e: Exception) { - Log.d(TAG, "clickEvents: $e") - toast(getString(R.string.something_went_wrong)) + } catch (e: FileNotFoundException) { + toast(getString(R.string.no_recorded_audio_found)) + } catch (e: Exception) { + Log.d(TAG, "clickEvents: $e") + toast(getString(R.string.something_went_wrong)) + } } } } @@ -242,6 +233,16 @@ class KaraokePlayerActivity : WokaBaseActivity() { toast(getString(R.string.permission_deniedd)) } } + + storagePermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { + if (it){ + saveFileUsingMediaStore() + }else{ + toast(getString(R.string.permission_deniedd)) + } + } } private fun updateRecordingUI() { @@ -260,15 +261,17 @@ class KaraokePlayerActivity : WokaBaseActivity() { playerView.useController = false audioBtn.isEnabled = false + audioBtn.alpha = 0.5f audioBtn.text = getString(R.string.play) audioBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_play, 0, 0, 0) downloadBtn.isEnabled = false + downloadBtn.alpha = 0.5f } RecordingState.NOT_RECORDING -> { - recordBtn.text = getString(R.string.stop_recording) + recordBtn.text = getString(R.string.start_recording) recordBtn.setCompoundDrawablesWithIntrinsicBounds( R.drawable.ic_rec_mic, 0, @@ -278,11 +281,8 @@ class KaraokePlayerActivity : WokaBaseActivity() { playerView.useController = true - audioBtn.isEnabled = true audioBtn.text = getString(R.string.play) audioBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_play, 0, 0, 0) - - downloadBtn.isEnabled = true } RecordingState.PLAYING_AUDIO -> { @@ -297,7 +297,6 @@ class KaraokePlayerActivity : WokaBaseActivity() { playerView.useController = true - audioBtn.isEnabled = true if (audioPlayer?.isPlaying == true) { audioBtn.text = getString(R.string.pause) audioBtn.setCompoundDrawablesWithIntrinsicBounds( @@ -315,8 +314,6 @@ class KaraokePlayerActivity : WokaBaseActivity() { 0 ) } - - downloadBtn.isEnabled = true } } } @@ -397,10 +394,9 @@ class KaraokePlayerActivity : WokaBaseActivity() { if (player?.isPlaying == true) { player?.pause() } - audioPlayer?.start() + start() recordingState = RecordingState.PLAYING_AUDIO - updateRecordingUI() setOnCompletionListener { audioPlayer?.release() @@ -413,14 +409,14 @@ class KaraokePlayerActivity : WokaBaseActivity() { toast(getString(R.string.no_recorded_audio_found)) recordingState = RecordingState.NOT_RECORDING audioPlayer = null - updateRecordingUI() } catch (e: Exception) { toast(getString(R.string.something_went_wrong)) recordingState = RecordingState.NOT_RECORDING audioPlayer = null - updateRecordingUI() } } + + updateRecordingUI() } RecordingState.RECORDING -> { @@ -562,8 +558,15 @@ class KaraokePlayerActivity : WokaBaseActivity() { binding.recorderView.show() binding.mixingProgressView.hide() binding.mixingProgressBar.progress = 0 + binding.mixingProgressPercent.text = null toast(getString(R.string.audio_is_ready_to_play)) + binding.audioBtn.alpha = 1f + binding.audioBtn.isEnabled = true + + binding.downloadBtn.alpha = 1f + binding.downloadBtn.isEnabled = true + audioMixer.release() } @@ -605,7 +608,16 @@ class KaraokePlayerActivity : WokaBaseActivity() { } } - private fun saveFileUsingMediaStore(fileName: String) { + private fun saveFileUsingMediaStore() { + + val cal = Calendar.getInstance() + val fileName = "${karaokePlayerData?.title}_${ + SimpleDateFormat( + "dd MMM yyyy hh:mm a", + Locale.getDefault() + ).format(cal.time) + }.mp3" + val resolver = applicationContext.contentResolver val audioCollection = @@ -618,8 +630,12 @@ class KaraokePlayerActivity : WokaBaseActivity() { } val songDetails = ContentValues().apply { - put(MediaStore.Audio.Media.DISPLAY_NAME, fileName) - put(MediaStore.Audio.Media.IS_PENDING, 1) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + put(MediaStore.Audio.Media.DATA, File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), fileName).absolutePath) + }else{ + put(MediaStore.Audio.Media.DISPLAY_NAME, fileName) + put(MediaStore.Images.Media.IS_PENDING, 1) + } } resolver.insert(audioCollection, songDetails)?.let {songContentUri -> @@ -639,14 +655,31 @@ class KaraokePlayerActivity : WokaBaseActivity() { } } } + + AlertDialog.Builder(this@KaraokePlayerActivity) + .setMessage(getString(R.string.file_saved_to_your_music_folder)) + .setPositiveButton(getString(R.string.ok_caps)) { dialog, which -> + dialog.dismiss() + } + .create() + .show() }catch (e: Exception){ resolver.delete(songContentUri, null, null) + + AlertDialog.Builder(this@KaraokePlayerActivity) + .setMessage(getString(R.string.file_saved_to_your_music_folder)) + .setPositiveButton(getString(R.string.ok_caps)) { dialog, which -> + dialog.dismiss() + } + .create() + .show() } - songDetails.clear() - songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0) - resolver.update(songContentUri, songDetails, null, null) - toast("Done") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + songDetails.clear() + songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0) + resolver.update(songContentUri, songDetails, null, null) + } } } diff --git a/app/src/main/java/com/woka/karaoke/viewmodels/KaraokeViewModel.kt b/app/src/main/java/com/woka/karaoke/viewmodels/KaraokeViewModel.kt new file mode 100644 index 0000000..a8c0219 --- /dev/null +++ b/app/src/main/java/com/woka/karaoke/viewmodels/KaraokeViewModel.kt @@ -0,0 +1,35 @@ +package com.woka.karaoke.viewmodels + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.woka.karaoke.KaraokeRepository +import com.woka.karaoke.models.listing.KaraokeData +import com.woka.networking.ApiResult +import kotlinx.coroutines.launch + +class KaraokeViewModel: ViewModel() { + + private val _karaokeLiveData = MutableLiveData>>() + val karaokeLiveData: LiveData>> + get() = _karaokeLiveData + + var nextPageToLoad: Int = 0 + private var quantityPerPage: Int = 10 + private var lastPage = false + + fun loadKaraokeSongs(currentItemCount: Int){ + viewModelScope.launch { + when (val value = KaraokeRepository.loadKaraokeSongs(nextPageToLoad, quantityPerPage)){ + is ApiResult.Error -> _karaokeLiveData.postValue(ApiResult.Error(value.errorMessage, value.error)) + is ApiResult.Loading -> _karaokeLiveData.postValue(ApiResult.Loading()) + is ApiResult.Success -> { + value.data?.karaoke_data?.filterNotNull()?.let { + + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/karaoke/views/KaraokeActivity.kt b/app/src/main/java/com/woka/karaoke/views/KaraokeActivity.kt index 96ceb4f..1f8102a 100644 --- a/app/src/main/java/com/woka/karaoke/views/KaraokeActivity.kt +++ b/app/src/main/java/com/woka/karaoke/views/KaraokeActivity.kt @@ -12,10 +12,12 @@ import android.view.WindowManager import androidx.activity.enableEdgeToEdge import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.SimpleItemAnimator import com.google.android.material.appbar.CollapsingToolbarLayout import com.woka.R import com.woka.WokaApp +import com.woka.WokaApp.Companion.userPrefs import com.woka.databinding.ActivityKaraokeBinding import com.woka.databinding.DialogModuleShowerBinding import com.woka.karaoke.KaraokeRepository @@ -25,9 +27,12 @@ import com.woka.karaoke.models.listing.KaraokeData import com.woka.karaoke.player.KaraokePlayerActivity import com.woka.karaoke.player.KaraokePlayerActivity.Companion.EXTRA_KARAOKE_DATA import com.woka.karaoke.player.KaraokePlayerData +import com.woka.karaoke.viewmodels.KaraokeViewModel import com.woka.networking.ApiResult +import com.woka.userPreference.UserType import com.woka.utils.WokaBaseActivity import com.woka.utils.hide +import com.woka.utils.setVisibility import com.woka.utils.show import java.text.SimpleDateFormat import java.util.Calendar @@ -37,6 +42,8 @@ class KaraokeActivity : WokaBaseActivity() { private lateinit var binding: ActivityKaraokeBinding + private lateinit var viewModel: KaraokeViewModel + // adapters private lateinit var karaokeAdapter: KaraokeAdapter private lateinit var continueKaraokeAdapter: ContinueKaraokeAdapter @@ -59,8 +66,11 @@ class KaraokeActivity : WokaBaseActivity() { navigationBarColor = getColor(R.color.color_primary_dark) } + viewModel = ViewModelProvider(this)[KaraokeViewModel::class.java] + karaokeAdapter = KaraokeAdapter(this, ::onKaraokeClicked, ::onKaraokeChanged) - continueKaraokeAdapter = ContinueKaraokeAdapter(this, ::onKaraokeClicked, ::onKaraokeChanged) + continueKaraokeAdapter = + ContinueKaraokeAdapter(this, ::onKaraokeClicked, ::onKaraokeChanged) dialogBinding = DialogModuleShowerBinding.inflate(layoutInflater) karaokeDialog = Dialog(this) @@ -73,8 +83,6 @@ class KaraokeActivity : WokaBaseActivity() { clickEvents() setObservers() - - KaraokeRepository.loadKaraokeSongs() } private fun initViews() { @@ -112,7 +120,8 @@ class KaraokeActivity : WokaBaseActivity() { dialogBinding.close.setOnClickListener { karaokeDialog.dismiss() } dialogBinding.watchCard.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_mic, 0, 0, 0) - dialogBinding.watchCard.backgroundTintList = ColorStateList.valueOf(getColor(R.color.game_grad_one)) + dialogBinding.watchCard.backgroundTintList = + ColorStateList.valueOf(getColor(R.color.game_grad_one)) dialogBinding.watchCard.text = getString(R.string.sing_now) } @@ -123,7 +132,11 @@ class KaraokeActivity : WokaBaseActivity() { } retryBtn.setOnClickListener { - KaraokeRepository.loadKaraokeSongs() + + } + + loadMoreBtn.setOnClickListener { + } } } @@ -155,7 +168,7 @@ class KaraokeActivity : WokaBaseActivity() { } } - private fun onSingClicked(karaokeData: KaraokeData){ + private fun onSingClicked(karaokeData: KaraokeData) { karaokeData.video_url?.let { startActivity(Intent(this@KaraokeActivity, KaraokePlayerActivity::class.java).apply { putExtra(EXTRA_KARAOKE_DATA, KaraokePlayerData(it, karaokeData.title)) @@ -274,13 +287,13 @@ class KaraokeActivity : WokaBaseActivity() { } private fun onKaraokeChanged(id: Int, isFromContinue: Boolean) { - if (isFromContinue){ + if (isFromContinue) { // updating karaoke list val position = karaokeAdapter.currentList.indexOfFirst { it.id == id } if (position >= 0 && position < karaokeAdapter.currentList.size) { karaokeAdapter.notifyItemChanged(position) } - }else{ + } else { // updating continue karaoke list val continuePos = continueKaraokeAdapter.currentList.indexOfFirst { it.id == id } if (continuePos >= 0 && continuePos < continueKaraokeAdapter.currentList.size) { @@ -295,44 +308,65 @@ class KaraokeActivity : WokaBaseActivity() { } private fun setObservers() { - KaraokeRepository.karaokeLiveData.observe(this) { - when (it) { - is ApiResult.Error -> { - binding.shimmer.hide() - binding.rvKaraoke.hide() - binding.singTxt.hide() - binding.trailerView.hide() - - binding.errorView.show() - } - - is ApiResult.Loading -> { - binding.shimmer.show() - - binding.rvKaraoke.hide() - binding.singTxt.hide() - binding.trailerView.hide() - binding.errorView.hide() - } - - is ApiResult.Success -> { - it.data?.karaoke_data?.filterNotNull()?.let { audioBookData -> - if (audioBookData.isNotEmpty()) { - binding.shimmer.hide() - binding.errorView.hide() - - KaraokeRepository.loadContinueKaraoke() - - loadTrailerData(audioBookData[0]) - binding.rvKaraoke.show() - binding.singTxt.show() - - karaokeAdapter.submitList(audioBookData) - } - } - } - } - } +// viewModel.karaokeLiveData.observe(this) { +// when (it) { +// is ApiResult.Error -> { +// binding.shimmer.hide() +// if (karaokeAdapter.currentList.size == 0) { +// binding.rvKaraoke.hide() +// binding.singTxt.hide() +// binding.trailerView.hide() +// +// binding.errorView.show() +// } else { +// // error in loading more +// binding.loadMoreProgress.hide() +// binding.loadMoreBtn.show() +// } +// } +// +// is ApiResult.Loading -> { +// if (karaokeAdapter.currentList.size == 0) { +// binding.shimmer.show() +// +// binding.rvKaraoke.hide() +// binding.singTxt.hide() +// binding.trailerView.hide() +// binding.errorView.hide() +// } else { +// // error in loading more +// binding.loadMoreProgress.show() +// binding.loadMoreBtn.hide() +// } +// } +// +// is ApiResult.Success -> { +// it.data?.let { data -> +// binding.loadMoreProgress.hide() +// +// data.karaoke_data?.filterNotNull()?.let { karaokeData -> +// if (karaokeData.isNotEmpty()) { +// binding.shimmer.hide() +// binding.errorView.hide() +// +// if (userPrefs?.userType != UserType.GUEST) { +// KaraokeRepository.loadContinueKaraoke() +// } +// +// loadTrailerData(karaokeData[0]) +// binding.rvKaraoke.show() +// binding.singTxt.show() +// +// karaokeAdapter.submitList(karaokeData) +// } +// +// } +// +// binding.loadMoreBtn.setVisibility(karaokeAdapter.currentList.size != data.total_records) +// } +// } +// } +// } KaraokeRepository.continueKaraokeLiveData.observe(this) { when (it) { @@ -343,7 +377,7 @@ class KaraokeActivity : WokaBaseActivity() { is ApiResult.Loading -> {} is ApiResult.Success -> { - it.data?.result?.filterNotNull()?.let {continueData -> + it.data?.result?.filterNotNull()?.let { continueData -> if (continueData.isNotEmpty()) { binding.continueSingTxt.show() binding.rvContinueSing.show() diff --git a/app/src/main/java/com/woka/utils/Extensions.kt b/app/src/main/java/com/woka/utils/Extensions.kt index 2f2bc51..25dc943 100644 --- a/app/src/main/java/com/woka/utils/Extensions.kt +++ b/app/src/main/java/com/woka/utils/Extensions.kt @@ -47,6 +47,11 @@ fun View.hide(){ this.visibility = GONE } +fun View.setVisibility(show: Boolean){ + if (show) show() + else hide() +} + fun Context.changeLocale(language: String){ val locale = Locale(language) Locale.setDefault(locale) diff --git a/app/src/main/res/layout/activity_karaoke.xml b/app/src/main/res/layout/activity_karaoke.xml index 1f8ff9f..17f0380 100644 --- a/app/src/main/res/layout/activity_karaoke.xml +++ b/app/src/main/res/layout/activity_karaoke.xml @@ -274,6 +274,42 @@ tools:listitem="@layout/show_view_holder" /> + + +