From 69a8c34a5741cc4d547d878314dd61053398b1d8 Mon Sep 17 00:00:00 2001 From: AdityaGaikwad Date: Tue, 2 Jul 2024 20:56:17 +0530 Subject: [PATCH] Deleting exoplayer from project updating to media player 3 in MoreHomeActivity SongAdapter and Player on HomeFragment 1 and 2 Error handing in PlayerActivity.kt and LiveStreamPlayerActivity.kt --- app/build.gradle | 29 ++++- .../com/woka/home/fragments/Home1Fragment.kt | 5 +- .../com/woka/home/viewmodels/HomeViewModel.kt | 23 ++-- .../com/woka/home/viewmodels/MoreViewModel.kt | 7 - .../com/woka/home/views/MoreHomeActivity.kt | 8 +- .../modules/wokasongs/WokaSongsAdapter.kt | 16 +-- .../com/woka/players/models/PlayBackState.kt | 5 + .../players/views/LiveStreamPlayerActivity.kt | 109 ++++++++++++---- .../com/woka/players/views/PlayerActivity.kt | 122 +++++++++++++----- .../main/res/layout/activity_audio_books.xml | 4 +- .../layout/activity_live_stream_player.xml | 48 +++++++ app/src/main/res/layout/activity_player.xml | 64 ++++++++- app/src/main/res/layout/fragment_home2.xml | 2 +- app/src/main/res/layout/fragment_home_1.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 15 files changed, 340 insertions(+), 105 deletions(-) delete mode 100644 app/src/main/java/com/woka/home/viewmodels/MoreViewModel.kt create mode 100644 app/src/main/java/com/woka/players/models/PlayBackState.kt diff --git a/app/build.gradle b/app/build.gradle index af7db7e..ed7b587 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ android { } ext.jwPlayerVersion = '4.17.0' -ext.exoplayerVersion = '1.1.1' +ext.exoplayerVersion = '1.3.1' dependencies { implementation "com.facebook.shimmer:shimmer:0.5.0" @@ -84,10 +84,29 @@ dependencies { implementation "com.jwplayer:jwplayer-core:$jwPlayerVersion" implementation "com.jwplayer:jwplayer-common:$jwPlayerVersion" - // exoplayer2 - implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1' - implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.1' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.19.1' + // media player 3 + def media3_version = "1.3.1" + implementation "androidx.media3:media3-exoplayer:$media3_version" + // For DASH playback support with ExoPlayer + implementation "androidx.media3:media3-exoplayer-dash:$media3_version" + // For HLS playback support with ExoPlayer + implementation "androidx.media3:media3-exoplayer-hls:$media3_version" + // For SmoothStreaming playback support with ExoPlayer + implementation "androidx.media3:media3-exoplayer-smoothstreaming:$media3_version" + // Common functionality for reading and writing media containers + implementation "androidx.media3:media3-container:$media3_version" + // Common functionality for media database components + implementation "androidx.media3:media3-database:$media3_version" + // Common functionality for media decoders + implementation "androidx.media3:media3-decoder:$media3_version" + // Common functionality for loading data + implementation "androidx.media3:media3-datasource:$media3_version" + // Common functionality used across multiple media libraries + implementation "androidx.media3:media3-common:$media3_version" + // For extracting data from media containers + implementation "androidx.media3:media3-extractor:$media3_version" + // For building media playback UIs + implementation "androidx.media3:media3-ui:$media3_version" implementation libs.androidx.core.ktx implementation libs.androidx.appcompat 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 372e063..8fce4f7 100644 --- a/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt +++ b/app/src/main/java/com/woka/home/fragments/Home1Fragment.kt @@ -16,6 +16,7 @@ import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import androidx.media3.common.Player.Listener import com.woka.R import com.woka.WokaApp.Companion.userPrefs import com.woka.audiobooks.views.AudioBooksActivity @@ -34,7 +35,7 @@ import com.woka.utils.show import com.woka.webseries.views.WebSeriesActivity -class Home1Fragment : Fragment() { +class Home1Fragment : Fragment(), Listener { private lateinit var binding: FragmentHome1Binding private lateinit var viewModel: HomeViewModel @@ -108,6 +109,7 @@ class Home1Fragment : Fragment() { private fun initPlayerView() { binding.playerView.player = viewModel.player binding.playerView.useController = false + viewModel.player?.addListener(this) } private fun setObservers() { @@ -190,6 +192,7 @@ class Home1Fragment : Fragment() { override fun onDestroyView() { super.onDestroyView() activity?.unregisterReceiver(minuteReceiver) + viewModel.player?.removeListener(this) } private fun updateUserData(data: UserDataResponse?) { diff --git a/app/src/main/java/com/woka/home/viewmodels/HomeViewModel.kt b/app/src/main/java/com/woka/home/viewmodels/HomeViewModel.kt index 631b485..c66a72f 100644 --- a/app/src/main/java/com/woka/home/viewmodels/HomeViewModel.kt +++ b/app/src/main/java/com/woka/home/viewmodels/HomeViewModel.kt @@ -2,18 +2,23 @@ package com.woka.home.viewmodels import android.content.Context import android.net.Uri +import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.MediaItem +import androidx.media3.common.MediaItem +import androidx.media3.common.PlaybackException +import androidx.media3.common.Player +import androidx.media3.common.Player.Listener +import androidx.media3.exoplayer.ExoPlayer import com.woka.home.views.BottomNavigation.Companion.HOME import com.woka.networking.ApiResult import com.woka.userdata.UserRepository +import com.woka.utils.TAG import kotlinx.coroutines.launch -class HomeViewModel: ViewModel(){ +class HomeViewModel : ViewModel() { var selectedBottomTab: Int = HOME var isHomeBackgroundBlurred: Boolean = false @@ -30,32 +35,30 @@ class HomeViewModel: ViewModel(){ val deActivateLiveData: LiveData?> get() = _deActivateLiveData - var player: ExoPlayer? = null + var player: Player? = null fun initPlayer(context: Context) { player = ExoPlayer.Builder(context).build() player?.volume = 0f - val videoUri = Uri.parse("https://d3volyx7jx7oal.cloudfront.net/master.m3u8") - val mediaItem = MediaItem.fromUri(videoUri) - player?.setMediaItem(mediaItem) + player?.setMediaItem(MediaItem.fromUri("https://d3volyx7jx7oal.cloudfront.net/master.m3u8")) player?.playWhenReady = true player?.prepare() } - fun sendLocaleChangeEvent(locale: String){ + fun sendLocaleChangeEvent(locale: String) { _localeChangeLiveData.postValue(locale) } - fun logout(){ + fun logout() { viewModelScope.launch { _logoutLiveData.postValue(ApiResult.Loading()) _logoutLiveData.postValue(UserRepository.logout()) } } - fun deActivateAccount(){ + fun deActivateAccount() { viewModelScope.launch { _deActivateLiveData.postValue(ApiResult.Loading()) _deActivateLiveData.postValue(UserRepository.deActivateAccount()) diff --git a/app/src/main/java/com/woka/home/viewmodels/MoreViewModel.kt b/app/src/main/java/com/woka/home/viewmodels/MoreViewModel.kt deleted file mode 100644 index 08cc22d..0000000 --- a/app/src/main/java/com/woka/home/viewmodels/MoreViewModel.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.woka.home.viewmodels - -import androidx.lifecycle.ViewModel - -class MoreViewModel: ViewModel() { - -} \ No newline at end of file diff --git a/app/src/main/java/com/woka/home/views/MoreHomeActivity.kt b/app/src/main/java/com/woka/home/views/MoreHomeActivity.kt index 5bac0c9..a390a2e 100644 --- a/app/src/main/java/com/woka/home/views/MoreHomeActivity.kt +++ b/app/src/main/java/com/woka/home/views/MoreHomeActivity.kt @@ -11,16 +11,14 @@ import android.view.WindowManager import androidx.activity.enableEdgeToEdge import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat -import androidx.lifecycle.ViewModelProvider +import androidx.media3.common.MediaItem import androidx.recyclerview.widget.SimpleItemAnimator import com.bumptech.glide.Glide -import com.google.android.exoplayer2.MediaItem import com.woka.R import com.woka.WokaApp import com.woka.WokaApp.Companion.userPrefs import com.woka.databinding.ActivityMoreHomeBinding import com.woka.databinding.DialogBlogsBinding -import com.woka.home.viewmodels.MoreViewModel import com.woka.modules.blogs.BlogsAdapter import com.woka.modules.blogs.BlogsRepository import com.woka.modules.blogs.models.Blog @@ -39,8 +37,6 @@ class MoreHomeActivity : WokaBaseActivity() { private lateinit var blogsAdapter: BlogsAdapter - private lateinit var viewModel: MoreViewModel - private lateinit var songsAdapter: WokaSongsAdapter private lateinit var blogBinding: DialogBlogsBinding @@ -67,8 +63,6 @@ class MoreHomeActivity : WokaBaseActivity() { lightStatusBar() } - viewModel = ViewModelProvider(this)[MoreViewModel::class.java] - blogsAdapter = BlogsAdapter(::obBlogClicked) songsAdapter = WokaSongsAdapter(this) diff --git a/app/src/main/java/com/woka/modules/wokasongs/WokaSongsAdapter.kt b/app/src/main/java/com/woka/modules/wokasongs/WokaSongsAdapter.kt index 9182494..4862efc 100644 --- a/app/src/main/java/com/woka/modules/wokasongs/WokaSongsAdapter.kt +++ b/app/src/main/java/com/woka/modules/wokasongs/WokaSongsAdapter.kt @@ -3,28 +3,20 @@ package com.woka.modules.wokasongs import android.content.Context import android.os.Handler import android.os.Looper -import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup +import androidx.media3.common.MediaItem +import androidx.media3.common.PlaybackException +import androidx.media3.common.Player +import androidx.media3.exoplayer.ExoPlayer import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.PlaybackException -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.Player.EVENT_IS_LOADING_CHANGED import com.woka.R import com.woka.databinding.WokaSongViewHolderBinding import com.woka.modules.wokasongs.models.SongData -import com.woka.utils.TAG import com.woka.utils.formatTime import com.woka.utils.hide import com.woka.utils.show -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlin.math.log class WokaSongsAdapter(context: Context): RecyclerView.Adapter() { diff --git a/app/src/main/java/com/woka/players/models/PlayBackState.kt b/app/src/main/java/com/woka/players/models/PlayBackState.kt new file mode 100644 index 0000000..0f9e2a6 --- /dev/null +++ b/app/src/main/java/com/woka/players/models/PlayBackState.kt @@ -0,0 +1,5 @@ +package com.woka.players.models + +enum class PlayBackState{ + PLAY, PAUSED, STOPPED; +} \ No newline at end of file diff --git a/app/src/main/java/com/woka/players/views/LiveStreamPlayerActivity.kt b/app/src/main/java/com/woka/players/views/LiveStreamPlayerActivity.kt index 83f52c3..2602ae5 100644 --- a/app/src/main/java/com/woka/players/views/LiveStreamPlayerActivity.kt +++ b/app/src/main/java/com/woka/players/views/LiveStreamPlayerActivity.kt @@ -1,6 +1,12 @@ package com.woka.players.views +import android.net.ConnectivityManager +import android.net.ConnectivityManager.NetworkCallback +import android.net.Network +import android.net.NetworkRequest import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.util.Log import android.view.ViewGroup import androidx.activity.enableEdgeToEdge @@ -9,6 +15,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import com.jwplayer.pub.api.JWPlayer +import com.jwplayer.pub.api.PlayReason import com.jwplayer.pub.api.configuration.PlayerConfig import com.jwplayer.pub.api.events.EventType import com.jwplayer.pub.api.events.listeners.VideoPlayerEvents @@ -16,17 +23,23 @@ import com.jwplayer.pub.api.fullscreen.FullscreenHandler import com.jwplayer.pub.api.media.playlists.PlaylistItem import com.woka.databinding.ActivityLiveStreamPlayerBinding import com.woka.players.KeepScreenOnHandler +import com.woka.players.models.PlayBackState +import com.woka.utils.TAG +import com.woka.utils.hide +import com.woka.utils.show class LiveStreamPlayerActivity : AppCompatActivity(), FullscreenHandler { - companion object{ - private const val TAG = "LiveStreamPlayerActivity_tag" - } - private lateinit var binding: ActivityLiveStreamPlayerBinding private lateinit var player: JWPlayer + private lateinit var playerConfig: PlayerConfig + + private lateinit var networkCallback: NetworkCallback + + private var playbackState: PlayBackState? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -42,31 +55,39 @@ class LiveStreamPlayerActivity : AppCompatActivity(), FullscreenHandler { WindowCompat.getInsetsController(window, window.decorView) windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) + networkCallback = object : NetworkCallback(){ + override fun onAvailable(network: Network) { + super.onAvailable(network) + runOnUiThread { + if (playbackState == PlayBackState.STOPPED){ + binding.playerView.show() + binding.errorView.hide() + configureAndPlay() + } + } + } + } + setUpPlayer() + addListeners() + + configureAndPlay() + + clickEvents() + } override fun onDestroy() { super.onDestroy() player.stop() + (getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager).unregisterNetworkCallback( + networkCallback + ) } - private fun setUpPlayer() { - player = binding.playerView.player - - player.setFullscreenHandler(this) - - player.setFullscreen(true, false) - - player.addListener(EventType.ERROR, VideoPlayerEvents.OnErrorListener { - Log.d(TAG, "onError: ${it.errorCode}") - Log.d(TAG, "onError: ${it.exception}") - Log.d(TAG, "onError: ${it.message}") - }) - - player.addListener(EventType.SETUP_ERROR, VideoPlayerEvents.OnSetupErrorListener { - Log.d(TAG, "setUpPlayer: $it") - }) + private fun setUpPlayer(){ + player = binding.playerView.getPlayer(this) // to keep up the screen om when video is being played KeepScreenOnHandler(player, window) @@ -78,12 +99,52 @@ class LiveStreamPlayerActivity : AppCompatActivity(), FullscreenHandler { val playlist: MutableList = ArrayList() playlist.add(playlistItem) - val config = - PlayerConfig.Builder() + playerConfig = PlayerConfig.Builder() .playlist(playlist) + .autostart(true) .build() - player.setup(config) - player.play() + } + + private fun addListeners(){ + player.addListener(EventType.PLAY, VideoPlayerEvents.OnPlayListener { + playbackState = PlayBackState.PLAY + binding.playerView.show() + binding.errorView.hide() + }) + player.addListener(EventType.PAUSE, VideoPlayerEvents.OnPauseListener { + playbackState = PlayBackState.PAUSED + }) + player.addListener(EventType.ERROR, VideoPlayerEvents.OnErrorListener { + playbackState = PlayBackState.STOPPED + binding.playerView.hide() + binding.errorView.show() + }) + + (getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager).registerNetworkCallback( + NetworkRequest.Builder() + .build(), + networkCallback + ) + } + + private fun configureAndPlay() { + player.setFullscreenHandler(this) + player.setFullscreen(true, false) + player.setup(playerConfig) + } + + private fun clickEvents(){ + binding.apply { + retryBtn.setOnClickListener { + binding.playerView.show() + binding.errorView.hide() + configureAndPlay() + } + + closeBtn.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + } + } } override fun onFullscreenRequested() {} diff --git a/app/src/main/java/com/woka/players/views/PlayerActivity.kt b/app/src/main/java/com/woka/players/views/PlayerActivity.kt index ea67bbb..479d6ea 100644 --- a/app/src/main/java/com/woka/players/views/PlayerActivity.kt +++ b/app/src/main/java/com/woka/players/views/PlayerActivity.kt @@ -1,5 +1,8 @@ package com.woka.players.views +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkRequest import android.os.Bundle import android.view.ViewGroup import androidx.activity.enableEdgeToEdge @@ -9,16 +12,15 @@ import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import com.jwplayer.pub.api.JWPlayer import com.jwplayer.pub.api.configuration.PlayerConfig +import com.jwplayer.pub.api.events.EventType +import com.jwplayer.pub.api.events.listeners.VideoPlayerEvents import com.jwplayer.pub.api.fullscreen.FullscreenHandler import com.woka.databinding.ActivityPlayerBinding -import com.woka.networking.RetrofitHelper import com.woka.players.KeepScreenOnHandler -import com.woka.players.PlayerApiService +import com.woka.players.models.PlayBackState import com.woka.players.models.VideoPlayList -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlin.math.round +import com.woka.utils.hide +import com.woka.utils.show class PlayerActivity : AppCompatActivity(), FullscreenHandler { @@ -29,11 +31,14 @@ class PlayerActivity : AppCompatActivity(), FullscreenHandler { private lateinit var binding: ActivityPlayerBinding private lateinit var player: JWPlayer + private lateinit var config: PlayerConfig + + private lateinit var networkCallback: ConnectivityManager.NetworkCallback + private var playbackState: PlayBackState? = null private var videoPlayList: VideoPlayList? = null private var playingIndex: Int = 0 - - private var apiService = RetrofitHelper.getRetrofit().create(PlayerApiService::class.java) + private var seekPosition: Double = 0.0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -53,51 +58,102 @@ class PlayerActivity : AppCompatActivity(), FullscreenHandler { videoPlayList = intent.getParcelableExtra(EXTRA_PLAY_LIST) playingIndex = intent.getIntExtra(EXTRA_PLAY_INDEX, 0) - player = binding.playerView.getPlayer(this) + networkCallback = object : ConnectivityManager.NetworkCallback(){ + override fun onAvailable(network: Network) { + super.onAvailable(network) + runOnUiThread { + if (playbackState == PlayBackState.STOPPED){ + binding.playerView.show() + binding.errorView.hide() + configAndPlay() + } + } + } + } - setUpPlayer() + if (!videoPlayList?.playlist.isNullOrEmpty()) { + setUpPlayer() + addListeners() + configAndPlay() + } + + clickEvents() } override fun onDestroy() { - makeVideoViewApiCall(player.playlistIndex, round(player.position).toLong()) super.onDestroy() + player.stop() + (getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager).unregisterNetworkCallback( + networkCallback + ) } - private fun setUpPlayer() { - if (videoPlayList == null || videoPlayList?.playlist.isNullOrEmpty()) return + private fun clickEvents(){ + binding.apply { + retryBtn.setOnClickListener { + binding.playerView.show() + binding.errorView.hide() + configAndPlay() + } - player.setFullscreenHandler(this) + closeBtn.setOnClickListener { + onBackPressedDispatcher.onBackPressed() + } + } + } - player.setFullscreen(true, false) + private fun setUpPlayer(){ + player = binding.playerView.getPlayer(this) - // to keep up the screen om when video is being played - KeepScreenOnHandler(player, window) - - val config = PlayerConfig.Builder() + config = PlayerConfig.Builder() .playlist(videoPlayList?.playlist) .build() - + // to keep up the screen om when video is being played + KeepScreenOnHandler(player, window) + } + + private fun configAndPlay() { + player.setFullscreenHandler(this) + + player.setFullscreen(true, false) player.setup(config) player.playlistItem(playingIndex) } - private fun makeVideoViewApiCall(playingPosition: Int, watchedDurationSeconds: Long){ - videoPlayList?.videoViewRequestDataList?.let { - if (playingPosition >= 0 && playingPosition < it.size){ - it[playingPosition].let {videoViewData -> - - if (watchedDurationSeconds > 0){ - videoViewData.total_watched_duration = "$watchedDurationSeconds" - CoroutineScope(Dispatchers.IO).launch { - apiService.userVideoView(videoViewData) - } - } - } + private fun addListeners(){ + player.addListener(EventType.PLAY, VideoPlayerEvents.OnPlayListener { + if (seekPosition > 0){ + player.seek(seekPosition) } - } + seekPosition = 0.0 + playbackState = PlayBackState.PLAY + binding.playerView.show() + binding.errorView.hide() + }) + player.addListener(EventType.PAUSE, VideoPlayerEvents.OnPauseListener { + playbackState = PlayBackState.PAUSED + }) + player.addListener(EventType.ERROR, VideoPlayerEvents.OnErrorListener { + if (player.position != 0.0){ + seekPosition = player.position + } + playbackState = PlayBackState.STOPPED + binding.playerView.hide() + binding.errorView.show() + }) + + player.addListener(EventType.PLAYLIST_ITEM, VideoPlayerEvents.OnPlaylistItemListener { + playingIndex = it.index + }) + + (getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager).registerNetworkCallback( + NetworkRequest.Builder() + .build(), + networkCallback + ) } override fun onFullscreenRequested() {} diff --git a/app/src/main/res/layout/activity_audio_books.xml b/app/src/main/res/layout/activity_audio_books.xml index 3819480..ce2324b 100644 --- a/app/src/main/res/layout/activity_audio_books.xml +++ b/app/src/main/res/layout/activity_audio_books.xml @@ -126,7 +126,9 @@ + android:layout_height="match_parent" + android:background="@color/color_primary_dark" + > + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_player.xml b/app/src/main/res/layout/activity_player.xml index 0412a79..236919e 100644 --- a/app/src/main/res/layout/activity_player.xml +++ b/app/src/main/res/layout/activity_player.xml @@ -1,7 +1,65 @@ - \ No newline at end of file + android:background="@color/black" + tools:context=".players.views.LiveStreamPlayerActivity"> + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home2.xml b/app/src/main/res/layout/fragment_home2.xml index 028f04c..77eaa3e 100644 --- a/app/src/main/res/layout/fragment_home2.xml +++ b/app/src/main/res/layout/fragment_home2.xml @@ -132,7 +132,7 @@ android:translationZ="0dp" /> - - / Notifications Retry + Retry No notifications found Favorites WEB SERIES (HINDI)