From 84a22e8ec1876d798a29c005cbd477e5940ab2f3 Mon Sep 17 00:00:00 2001 From: kshivang Date: Tue, 12 Nov 2019 13:44:57 +0530 Subject: [PATCH] Goonj error while playing track in background fixed --- .../java/ai/rever/goonjexample/MidActivity.kt | 14 ++-- build.gradle | 2 +- goonj/src/main/java/ai/rever/goonj/Goonj.kt | 47 +++++-------- .../ExoPlayerAnalyticsListenerImp.kt | 11 +++ .../analytics/ExoPlayerEvenListenerImp.kt | 2 + .../goonj/analytics/ItemActionCallbackImp.kt | 2 - .../goonj/download/GoonjDownloadManager.kt | 16 ++++- .../manager/LocalPlayerNotificationManager.kt | 10 ++- .../main/java/ai/rever/goonj/models/Track.kt | 9 ++- .../goonj/player/imp/LocalAudioPlayer.kt | 68 ++++++++++++++----- 10 files changed, 118 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/ai/rever/goonjexample/MidActivity.kt b/app/src/main/java/ai/rever/goonjexample/MidActivity.kt index 593b37c..4359b69 100644 --- a/app/src/main/java/ai/rever/goonjexample/MidActivity.kt +++ b/app/src/main/java/ai/rever/goonjexample/MidActivity.kt @@ -1,8 +1,6 @@ package ai.rever.goonjexample import ai.rever.goonj.Goonj -import ai.rever.goonj.download.GoonjDownloadManager.addDownload -import ai.rever.goonj.download.GoonjDownloadManager.isTrackDownloaded import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle @@ -39,28 +37,28 @@ class MidActivity : AppCompatActivity() { private fun setupDownloads(){ activity_mid_download1_btn.setOnClickListener { - addDownload(SAMPLES[0]) + SAMPLES[0].requestDownload() } activity_mid_download2_btn.setOnClickListener { - addDownload(SAMPLES[1]) + SAMPLES[1].requestDownload() } activity_mid_download3_btn.setOnClickListener { - addDownload(SAMPLES[2]) + SAMPLES[2].requestDownload() } updateDownloadState() } private fun updateDownloadState(){ - if(isTrackDownloaded(SAMPLES[0].url)){ + if(Goonj.isDownloaded(SAMPLES[0].id)){ activity_mid_download1_btn.visibility = View.GONE activity_mid_done1_btn.visibility = View.VISIBLE } - if(isTrackDownloaded(SAMPLES[1].url)){ + if(Goonj.isDownloaded(SAMPLES[1].id)){ activity_mid_download2_btn.visibility = View.GONE activity_mid_done2_btn.visibility = View.VISIBLE } - if(isTrackDownloaded(SAMPLES[2].url)){ + if(Goonj.isDownloaded(SAMPLES[2].id)){ activity_mid_download3_btn.visibility = View.GONE activity_mid_done3_btn.visibility = View.VISIBLE } diff --git a/build.gradle b/build.gradle index 4cb4857..55ce778 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' + classpath 'com.android.tools.build:gradle:3.5.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/goonj/src/main/java/ai/rever/goonj/Goonj.kt b/goonj/src/main/java/ai/rever/goonj/Goonj.kt index 84f9e9e..edc74ee 100644 --- a/goonj/src/main/java/ai/rever/goonj/Goonj.kt +++ b/goonj/src/main/java/ai/rever/goonj/Goonj.kt @@ -47,7 +47,17 @@ object Goonj { runOnSet() } - private var mServiceConnection: ServiceConnection? = null + private var mServiceConnection: ServiceConnection = object : ServiceConnection{ + override fun onServiceConnected(name: ComponentName?, binder: IBinder?) { + (binder as? GoonjService.Binder)?.goonjPlayerServiceInterface?.let { + goonjPlayerServiceInterface = it + } + } + + override fun onServiceDisconnected(name: ComponentName?) { + goonjPlayerServiceInterface = null + } + } private var runs = ArrayList Unit>() @@ -88,6 +98,7 @@ object Goonj { * * Note: GoonjService must added to app manifest */ + fun register(context: Context, activityIntent: Intent, audioServiceClass: Class) { if (appContext == null) { val ctx = if (context.applicationContext == null) context else context.applicationContext @@ -98,38 +109,14 @@ object Goonj { changeActivityIntentForNotification(activityIntent) } - private fun register(audioServiceClass: Class) { - if(mServiceConnection == null){ - mServiceConnection = object : ServiceConnection{ - override fun onServiceConnected(name: ComponentName?, binder: IBinder?) { - (binder as? GoonjService.Binder)?.goonjPlayerServiceInterface?.let { - goonjPlayerServiceInterface = it - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - goonjPlayerServiceInterface = null - } - - override fun onBindingDied(name: ComponentName?) { - unregister() - register(audioServiceClass) - } - } - } - - mServiceConnection?.let { - appContext?.bindService( - Intent(appContext, audioServiceClass), - it, Context.BIND_AUTO_CREATE - ) - } + private fun register(audioServiceClass: Class) { + appContext?.bindService(Intent(appContext, audioServiceClass), + mServiceConnection, Context.BIND_AUTO_CREATE + ) } fun unregister() { - mServiceConnection?.let { - appContext?.unbindService(it) - } + appContext?.unbindService(mServiceConnection) imageLoader = null weakContext = null goonjPlayerServiceInterface = null diff --git a/goonj/src/main/java/ai/rever/goonj/analytics/ExoPlayerAnalyticsListenerImp.kt b/goonj/src/main/java/ai/rever/goonj/analytics/ExoPlayerAnalyticsListenerImp.kt index 0d99b48..a3d1a3c 100644 --- a/goonj/src/main/java/ai/rever/goonj/analytics/ExoPlayerAnalyticsListenerImp.kt +++ b/goonj/src/main/java/ai/rever/goonj/analytics/ExoPlayerAnalyticsListenerImp.kt @@ -1,10 +1,20 @@ package ai.rever.goonj.analytics import ai.rever.goonj.analytics.GoonjAnalytics.logEvent +import android.util.Log.e +import android.view.Surface import com.google.android.exoplayer2.ExoPlaybackException +import com.google.android.exoplayer2.Format +import com.google.android.exoplayer2.PlaybackParameters import com.google.android.exoplayer2.analytics.AnalyticsListener +import com.google.android.exoplayer2.audio.AudioAttributes +import com.google.android.exoplayer2.decoder.DecoderCounters import com.google.android.exoplayer2.metadata.Metadata import com.google.android.exoplayer2.source.MediaSourceEventListener +import com.google.android.exoplayer2.source.TrackGroupArray +import com.google.android.exoplayer2.trackselection.TrackSelectionArray +import java.io.IOException +import java.lang.Exception object ExoPlayerAnalyticsListenerImp: AnalyticsListener { override fun onSeekProcessed(eventTime: AnalyticsListener.EventTime?) { @@ -52,6 +62,7 @@ object ExoPlayerAnalyticsListenerImp: AnalyticsListener { ) } + override fun onLoadCompleted( eventTime: AnalyticsListener.EventTime?, loadEventInfo: MediaSourceEventListener.LoadEventInfo?, diff --git a/goonj/src/main/java/ai/rever/goonj/analytics/ExoPlayerEvenListenerImp.kt b/goonj/src/main/java/ai/rever/goonj/analytics/ExoPlayerEvenListenerImp.kt index 46ae01e..a648052 100644 --- a/goonj/src/main/java/ai/rever/goonj/analytics/ExoPlayerEvenListenerImp.kt +++ b/goonj/src/main/java/ai/rever/goonj/analytics/ExoPlayerEvenListenerImp.kt @@ -1,6 +1,8 @@ package ai.rever.goonj.analytics import ai.rever.goonj.analytics.GoonjAnalytics.logEvent +import android.util.Log.e +import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.PlaybackParameters import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.Timeline diff --git a/goonj/src/main/java/ai/rever/goonj/analytics/ItemActionCallbackImp.kt b/goonj/src/main/java/ai/rever/goonj/analytics/ItemActionCallbackImp.kt index d46b6c5..61a3751 100644 --- a/goonj/src/main/java/ai/rever/goonj/analytics/ItemActionCallbackImp.kt +++ b/goonj/src/main/java/ai/rever/goonj/analytics/ItemActionCallbackImp.kt @@ -9,7 +9,6 @@ import androidx.mediarouter.media.RemotePlaybackClient open class ItemActionCallbackImp(var operation: String, var onSuccess: ((String?, MediaItemStatus?) -> Unit)? = null) : RemotePlaybackClient.ItemActionCallback() { - override fun onResult( data: Bundle?, sessionId: String?, @@ -24,7 +23,6 @@ open class ItemActionCallbackImp(var operation: String, var onSuccess: ((String? override fun onError(error: String?, code: Int, data: Bundle?) { logError("$operation: failed", error , code) - } } diff --git a/goonj/src/main/java/ai/rever/goonj/download/GoonjDownloadManager.kt b/goonj/src/main/java/ai/rever/goonj/download/GoonjDownloadManager.kt index 5abd25f..0d7b546 100644 --- a/goonj/src/main/java/ai/rever/goonj/download/GoonjDownloadManager.kt +++ b/goonj/src/main/java/ai/rever/goonj/download/GoonjDownloadManager.kt @@ -21,7 +21,7 @@ import java.io.File import java.util.* enum class DownloadState{ - IDLE, DOWNLOAD_CHANGE, DOWNLOADING, INITIALIZED, REQUIREMENT_STATE_CHANGED, DOWNLOAD_REMOVED + IDLE, DOWNLOAD_CHANGE, DOWNLOADING, INITIALIZED, REQUIREMENT_STATE_CHANGED, DOWNLOAD_REMOVED, DOWNLOADED } object GoonjDownloadManager { @@ -101,4 +101,18 @@ object GoonjDownloadManager { fun isDownloaded(trackId: String) = downloadManager.downloadIndex .getDownload(trackId)?.state == STATE_COMPLETED + + fun getAllDownloads(): List { + val downloadCursor = downloadManager.downloadIndex.getDownloads(STATE_COMPLETED) + val downloadedTrackList = mutableListOf() + if(downloadCursor.count == 0) { + return downloadedTrackList + } + downloadCursor.moveToFirst() + do { + downloadedTrackList.add(downloadCursor.download.request.id) + } while (downloadCursor.moveToNext()) + + return downloadedTrackList + } } diff --git a/goonj/src/main/java/ai/rever/goonj/manager/LocalPlayerNotificationManager.kt b/goonj/src/main/java/ai/rever/goonj/manager/LocalPlayerNotificationManager.kt index 97002c8..11d7d43 100644 --- a/goonj/src/main/java/ai/rever/goonj/manager/LocalPlayerNotificationManager.kt +++ b/goonj/src/main/java/ai/rever/goonj/manager/LocalPlayerNotificationManager.kt @@ -83,9 +83,9 @@ internal object LocalPlayerNotificationManager { private val notificationListener = object : PlayerNotificationManager.NotificationListener { - override fun onNotificationStarted(notificationId: Int, notification: Notification?) { - Goonj.startForeground(notificationId, notification) - } +// override fun onNotificationStarted(notificationId: Int, notification: Notification?) { +// Goonj.startForeground(notificationId, notification) +// } override fun onNotificationCancelled(notificationId: Int) { Goonj.stopForeground(true) @@ -101,6 +101,10 @@ internal object LocalPlayerNotificationManager { activityIntent, PendingIntent.FLAG_CANCEL_CURRENT ) + + if (ongoing) { + Goonj.startForeground(notificationId, notification) + } } } diff --git a/goonj/src/main/java/ai/rever/goonj/models/Track.kt b/goonj/src/main/java/ai/rever/goonj/models/Track.kt index 37ca68e..3892b6e 100644 --- a/goonj/src/main/java/ai/rever/goonj/models/Track.kt +++ b/goonj/src/main/java/ai/rever/goonj/models/Track.kt @@ -159,7 +159,14 @@ data class TrackState(var index: Int = 0, var playedAt: Date? = Date(), var completedAt: Date? = Date(), var remoteItemId: String? = null): Parcelable { - val progress: Double get() = position.toDouble() / duration.toDouble() + val progress: Double get() = run { + val progress = position.toDouble() / duration.toDouble() + if (progress in 0.0..1.0) { + progress + } else { + 2.0 + } + } } diff --git a/goonj/src/main/java/ai/rever/goonj/player/imp/LocalAudioPlayer.kt b/goonj/src/main/java/ai/rever/goonj/player/imp/LocalAudioPlayer.kt index 02c4161..da3a26b 100644 --- a/goonj/src/main/java/ai/rever/goonj/player/imp/LocalAudioPlayer.kt +++ b/goonj/src/main/java/ai/rever/goonj/player/imp/LocalAudioPlayer.kt @@ -12,6 +12,7 @@ import ai.rever.goonj.manager.GoonjPlayerManager import ai.rever.goonj.manager.LocalPlayerSMCManager import ai.rever.goonj.models.Track import ai.rever.goonj.player.AudioPlayer +import android.util.Log.e import androidx.core.net.toUri import com.google.android.exoplayer2.C import com.google.android.exoplayer2.ExoPlayerFactory @@ -65,6 +66,8 @@ internal class LocalAudioPlayer: AudioPlayer { private val compositeDisposable = CompositeDisposable() private var timerDisposable: Disposable? = null private val trackList get() = GoonjPlayerManager.trackList + private val autoplay get() = GoonjPlayerManager.autoplayTrackSubject.value + private val currentTrack get() = GoonjPlayerManager.currentTrackSubject.value private val cacheDataSourceFactory by lazy { with (DefaultDataSourceFactory(appContext, Util.getUserAgent(appContext, appContext?.getString(R.string.app_name)))) { @@ -111,6 +114,13 @@ internal class LocalAudioPlayer: AudioPlayer { private fun onTrackPositionChange(position: Long = getTrackPosition()) { GoonjPlayerManager.currentTrackSubject.value?.let { track -> track.state.position = position + if (track.state.duration < 2) { + player?.duration?.let { + if (it > 1) { + track.state.duration = it + } + } + } GoonjPlayerManager.currentTrackSubject.onNext(track) } } @@ -118,19 +128,13 @@ internal class LocalAudioPlayer: AudioPlayer { private val eventListener = object: ExoPlayerEvenListenerImp() { override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) { - super.onPlayerStateChanged(playWhenReady, playbackState) updateCurrentlyPlayingTrack() GoonjPlayerManager.playerStateSubject.onNext( when (playbackState) { Player.STATE_BUFFERING -> GoonjPlayerState.BUFFERING - Player.STATE_ENDED -> { - GoonjPlayerManager.currentTrackSubject.value?.let { - GoonjPlayerManager.onTrackComplete(it) - } - GoonjPlayerState.ENDED - } + Player.STATE_ENDED -> GoonjPlayerState.ENDED Player.STATE_IDLE -> GoonjPlayerState.IDLE else -> if (playWhenReady) { GoonjPlayerState.PLAYING @@ -139,21 +143,25 @@ internal class LocalAudioPlayer: AudioPlayer { GoonjPlayerState.PAUSED } }) + super.onPlayerStateChanged(playWhenReady, playbackState) } override fun onPositionDiscontinuity(reason: Int) { - super.onPositionDiscontinuity(reason) updateCurrentlyPlayingTrack() - + super.onPositionDiscontinuity(reason) } + } private fun updateCurrentlyPlayingTrack() { player?.apply { - if (trackList.size <= currentWindowIndex) return - trackList[currentWindowIndex].let { exoTrack -> - val lastKnownTrack = GoonjPlayerManager.currentTrackSubject.value + // todo: figure out, why this could happen + if (trackList.size <= currentWindowIndex) { + return + } + + trackList[currentWindowIndex].let { exoTrack -> if (contentDuration > 0) { exoTrack.state.duration = contentDuration } @@ -164,16 +172,28 @@ internal class LocalAudioPlayer: AudioPlayer { exoTrack.state.position = 0 } - GoonjPlayerManager.currentTrackSubject.onNext(exoTrack) + val lastKnownTrack: Track? = currentTrack - if (exoTrack.id != lastKnownTrack?.id) { + if (exoTrack.id != lastKnownTrack?.id + || exoTrack.state.index != lastKnownTrack.state.index) { exoTrack.state.playedAt = Date() - GoonjPlayerManager.onTrackComplete(lastKnownTrack ?: return) - GoonjPlayerManager.autoplayTrackSubject.value?.let { - if (!it) { + if (lastKnownTrack != null) { // could be null if first track just started to play + if (newSession) { + newSession = false + } else{ + GoonjPlayerManager.onTrackComplete(lastKnownTrack) + } + + if (autoplay != true) { pause() } + GoonjPlayerManager.currentTrackSubject.onNext(exoTrack) + } else { + newSession = false + GoonjPlayerManager.currentTrackSubject.onNext(exoTrack) } + } else { + GoonjPlayerManager.currentTrackSubject.onNext(exoTrack) } } } @@ -189,13 +209,21 @@ internal class LocalAudioPlayer: AudioPlayer { player?.removeListener(eventListener) } + private var newSession = false override fun startNewSession(){ + if (player?.playbackError != null) { + player?.retry() + } + newSession = true concatenatingMediaSource.clear() LocalPlayerNotificationManager.setPlayer(player) } override fun seekTo(positionMs: Long) { + if (player?.playbackError != null) { + player?.retry() + } if (positionMs < player?.duration?: 0) { player?.seekTo(positionMs) } @@ -221,10 +249,16 @@ internal class LocalAudioPlayer: AudioPlayer { } override fun pause() { + if (player?.playbackError != null) { + player?.retry() + } player?.playWhenReady = false } override fun resume() { + if (player?.playbackError != null) { + player?.retry() + } player?.playWhenReady = true LocalPlayerNotificationManager.setPlayer(player) }