diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f93b57f..bf4cabb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -52,7 +52,7 @@ android { disable += listOf("MissingTranslation", "ExtraTranslation") } - compileSdk = 31 + compileSdk = 33 defaultConfig { @@ -62,7 +62,7 @@ android { versionName = androidGitVersion.name() minSdk = 24 - targetSdk = 30 + targetSdk = 33 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -167,21 +167,14 @@ dependencies { implementation("com.google.android.material:material:1.6.1") implementation("com.android.support.constraint:constraint-layout:2.0.4") - implementation("com.google.android.exoplayer:exoplayer-core:2.14.2") - implementation("com.google.android.exoplayer:exoplayer-ui:2.14.2") - implementation("com.google.android.exoplayer:extension-mediasession:2.14.2") + implementation("com.google.android.exoplayer:exoplayer-core:2.18.1") + implementation("com.google.android.exoplayer:exoplayer-ui:2.18.1") + implementation("com.google.android.exoplayer:extension-mediasession:2.18.1") implementation("io.insert-koin:koin-core:3.1.2") implementation("io.insert-koin:koin-android:3.1.2") testImplementation("io.insert-koin:koin-test:3.1.2") - implementation("com.github.PaulWoitaschek.ExoPlayer-Extensions:extension-opus:2.14.0") { - isTransitive = false - } - implementation("com.github.PaulWoitaschek.ExoPlayer-Extensions:extension-flac:2.14.0") { - isTransitive = false - } - implementation("com.aliassadi:power-preference-lib:2.0.0") implementation("com.github.kittinunf.fuel:fuel:2.3.1") implementation("com.github.kittinunf.fuel:fuel-coroutines:2.3.1") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b6e4126..a417e9e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,7 +22,8 @@ android:name=".activities.SplashActivity" android:launchMode="singleInstance" android:noHistory="true" - android:screenOrientation="portrait"> + android:screenOrientation="portrait" + android:exported="true"> @@ -61,7 +62,8 @@ + android:foregroundServiceType="mediaPlayback" + android:exported="false"> @@ -80,7 +82,8 @@ - + diff --git a/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt b/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt index 14d8939..ee77457 100644 --- a/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt +++ b/app/src/main/java/audio/funkwhale/ffa/activities/MainActivity.kt @@ -228,8 +228,8 @@ class MainActivity : AppCompatActivity() { item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) item.actionView = View(this) item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { - override fun onMenuItemActionExpand(item: MenuItem?) = false - override fun onMenuItemActionCollapse(item: MenuItem?) = false + override fun onMenuItemActionExpand(item: MenuItem) = false + override fun onMenuItemActionCollapse(item: MenuItem) = false }) item.isChecked = !item.isChecked @@ -359,7 +359,7 @@ class MainActivity : AppCompatActivity() { .alpha(0.0f) .setDuration(400) .setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animator: Animator?) { + override fun onAnimationEnd(animator: Animator) { binding.nowPlaying.visibility = View.GONE } }) diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/MediaControlsManager.kt b/app/src/main/java/audio/funkwhale/ffa/playback/MediaControlsManager.kt index 36b5302..73ee5b5 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/MediaControlsManager.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/MediaControlsManager.kt @@ -2,6 +2,7 @@ package audio.funkwhale.ffa.playback import android.app.Notification import android.app.PendingIntent +import android.app.PendingIntent.FLAG_MUTABLE import android.app.Service import android.content.Intent import android.support.v4.media.session.MediaSessionCompat @@ -42,7 +43,7 @@ class MediaControlsManager(val context: Service, private val scope: CoroutineSco scope.launch(Default) { val openIntent = Intent(context, MainActivity::class.java).apply { action = NOTIFICATION_ACTION_OPEN_QUEUE.toString() } - val openPendingIntent = PendingIntent.getActivity(context, 0, openIntent, 0) + val openPendingIntent = PendingIntent.getActivity(context, 0, openIntent, FLAG_MUTABLE) val coverUrl = maybeNormalizeUrl(track.album?.cover()) diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/MediaSession.kt b/app/src/main/java/audio/funkwhale/ffa/playback/MediaSession.kt index 8898309..5e0a5d5 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/MediaSession.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/MediaSession.kt @@ -9,7 +9,6 @@ import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.PlaybackStateCompat import audio.funkwhale.ffa.utils.Command import audio.funkwhale.ffa.utils.CommandBus -import com.google.android.exoplayer2.ControlDispatcher import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector @@ -43,7 +42,7 @@ class MediaSession(private val context: Context) { MediaSessionConnector(session).also { it.setQueueNavigator(FFAQueueNavigator()) - it.setMediaButtonEventHandler { _, _, intent -> + it.setMediaButtonEventHandler { _, intent -> if (!active) { Intent(context, PlayerService::class.java).let { player -> player.action = intent.action @@ -67,13 +66,14 @@ class MediaSession(private val context: Context) { } class FFAQueueNavigator : MediaSessionConnector.QueueNavigator { - override fun onSkipToQueueItem(player: Player, controlDispatcher: ControlDispatcher, id: Long) { + override fun onSkipToQueueItem(player: Player, id: Long) { CommandBus.send(Command.PlayTrack(id.toInt())) } - override fun onCurrentWindowIndexChanged(player: Player) {} + override fun onCurrentMediaItemIndexChanged(player: Player) {} - override fun onCommand(player: Player, controlDispatcher: ControlDispatcher, command: String, extras: Bundle?, cb: ResultReceiver?) = true + override fun onCommand(player: Player, command: String, extras: Bundle?, cb: ResultReceiver?) = + true override fun getSupportedQueueNavigatorActions(player: Player): Long { return PlaybackStateCompat.ACTION_PLAY_PAUSE or @@ -82,13 +82,13 @@ class FFAQueueNavigator : MediaSessionConnector.QueueNavigator { PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM } - override fun onSkipToNext(player: Player, controlDispatcher: ControlDispatcher) { + override fun onSkipToNext(player: Player) { CommandBus.send(Command.NextTrack) } override fun getActiveQueueItemId(player: Player?) = player?.currentWindowIndex?.toLong() ?: 0 - override fun onSkipToPrevious(player: Player, controlDispatcher: ControlDispatcher) { + override fun onSkipToPrevious(player: Player) { CommandBus.send(Command.PreviousTrack) } diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/PinService.kt b/app/src/main/java/audio/funkwhale/ffa/playback/PinService.kt index f3bd1ce..5993800 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/PinService.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/PinService.kt @@ -80,7 +80,10 @@ class PinService : DownloadService(AppContext.NOTIFICATION_DOWNLOADS) { override fun getScheduler(): Scheduler? = null - override fun getForegroundNotification(downloads: MutableList): Notification { + override fun getForegroundNotification( + downloads: MutableList, + notMetRequirements: Int + ): Notification { val description = resources.getQuantityString(R.plurals.downloads_description, downloads.size, downloads.size) diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/PlayerService.kt b/app/src/main/java/audio/funkwhale/ffa/playback/PlayerService.kt index 9c74da5..f0bf336 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/PlayerService.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/PlayerService.kt @@ -31,11 +31,10 @@ import audio.funkwhale.ffa.utils.log import audio.funkwhale.ffa.utils.maybeNormalizeUrl import audio.funkwhale.ffa.utils.onApi import com.google.android.exoplayer2.C -import com.google.android.exoplayer2.ExoPlaybackException +import com.google.android.exoplayer2.PlaybackException import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.SimpleExoPlayer -import com.google.android.exoplayer2.source.TrackGroupArray -import com.google.android.exoplayer2.trackselection.TrackSelectionArray +import com.google.android.exoplayer2.Tracks import com.squareup.picasso.Picasso import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO @@ -43,7 +42,6 @@ import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.koin.java.KoinJavaComponent.inject @@ -136,6 +134,7 @@ class PlayerService : Service() { playWhenReady = false playerEventListener = PlayerEventListener().also { + addListener(it) } } @@ -419,9 +418,9 @@ class PlayerService : Service() { } @SuppressLint("NewApi") - inner class PlayerEventListener : Player.EventListener { + inner class PlayerEventListener : Player.Listener { override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) { - super.onPlayerStateChanged(playWhenReady, playbackState) + super.onPlayWhenReadyChanged(playWhenReady, playbackState) EventBus.send(Event.StateChanged(playWhenReady)) @@ -469,14 +468,11 @@ class PlayerService : Service() { } } - override fun onTracksChanged( - trackGroups: TrackGroupArray, - trackSelections: TrackSelectionArray - ) { - super.onTracksChanged(trackGroups, trackSelections) + override fun onTracksChanged(tracks: Tracks) { + super.onTracksChanged(tracks) - if (queue.current != player.currentWindowIndex) { - queue.current = player.currentWindowIndex + if (queue.current != player.currentMediaItemIndex) { + queue.current = player.currentMediaItemIndex mediaControlsManager.updateNotification(queue.current(), player.playWhenReady) } @@ -510,7 +506,7 @@ class PlayerService : Service() { } } - override fun onPlayerError(error: ExoPlaybackException) { + override fun onPlayerError(error: PlaybackException) { EventBus.send(Event.PlaybackError(getString(R.string.error_playback))) if (player.playWhenReady) { diff --git a/app/src/main/java/audio/funkwhale/ffa/playback/QueueManager.kt b/app/src/main/java/audio/funkwhale/ffa/playback/QueueManager.kt index fe9bf31..c883390 100644 --- a/app/src/main/java/audio/funkwhale/ffa/playback/QueueManager.kt +++ b/app/src/main/java/audio/funkwhale/ffa/playback/QueueManager.kt @@ -12,6 +12,7 @@ import audio.funkwhale.ffa.utils.FFACache import audio.funkwhale.ffa.utils.log import audio.funkwhale.ffa.utils.mustNormalizeUrl import com.github.kittinunf.fuel.gson.gsonDeserializerOf +import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.source.ConcatenatingMediaSource import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.gson.Gson @@ -37,9 +38,12 @@ class QueueManager(val context: Context) { dataSources.addMediaSources( metadata.map { track -> val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "") - - ProgressiveMediaSource.Factory(factory).setTag(track.title) - .createMediaSource(Uri.parse(url)) + ProgressiveMediaSource.Factory(factory).createMediaSource( + MediaItem.Builder() + .setTag(track.title) + .setUri(Uri.parse(url)) + .build() + ) } ) } @@ -63,8 +67,12 @@ class QueueManager(val context: Context) { val factory = cacheDataSourceFactoryProvider.create(context) val sources = tracks.map { track -> val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "") - - ProgressiveMediaSource.Factory(factory).setTag(track.title).createMediaSource(Uri.parse(url)) + ProgressiveMediaSource.Factory(factory).createMediaSource( + MediaItem.Builder() + .setTag(track.title) + .setUri(Uri.parse(url)) + .build() + ) } metadata = tracks.toMutableList() @@ -84,7 +92,12 @@ class QueueManager(val context: Context) { val sources = missingTracks.map { track -> val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "") - ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(url)) + ProgressiveMediaSource.Factory(factory).createMediaSource( + MediaItem.Builder() + .setTag(track.title) // was this missing on purpose?! + .setUri(Uri.parse(url)) + .build() + ) } metadata.addAll(tracks) @@ -101,7 +114,12 @@ class QueueManager(val context: Context) { val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "") if (metadata.indexOf(track) == -1) { - ProgressiveMediaSource.Factory(factory).createMediaSource(Uri.parse(url)).let { + ProgressiveMediaSource.Factory(factory).createMediaSource( + MediaItem.Builder() + .setTag(track.title) // was this missing on purpose?! + .setUri(Uri.parse(url)) + .build() + ).let { dataSources.addMediaSource(current + 1, it) metadata.add(current + 1, track) } diff --git a/app/src/main/java/audio/funkwhale/ffa/repositories/SearchRepository.kt b/app/src/main/java/audio/funkwhale/ffa/repositories/SearchRepository.kt index ee32d64..60021e1 100644 --- a/app/src/main/java/audio/funkwhale/ffa/repositories/SearchRepository.kt +++ b/app/src/main/java/audio/funkwhale/ffa/repositories/SearchRepository.kt @@ -11,8 +11,8 @@ import audio.funkwhale.ffa.model.Track import audio.funkwhale.ffa.model.TracksCache import audio.funkwhale.ffa.model.TracksResponse import audio.funkwhale.ffa.utils.OAuth -import com.github.kittinunf.fuel.gson.gsonDeserializerOf import audio.funkwhale.ffa.utils.mustNormalizeUrl +import com.github.kittinunf.fuel.gson.gsonDeserializerOf import com.google.android.exoplayer2.offline.DownloadManager import com.google.android.exoplayer2.upstream.cache.Cache import com.google.gson.reflect.TypeToken diff --git a/app/src/main/java/audio/funkwhale/ffa/repositories/TracksRepository.kt b/app/src/main/java/audio/funkwhale/ffa/repositories/TracksRepository.kt index 7858bf0..7a224c6 100644 --- a/app/src/main/java/audio/funkwhale/ffa/repositories/TracksRepository.kt +++ b/app/src/main/java/audio/funkwhale/ffa/repositories/TracksRepository.kt @@ -7,8 +7,8 @@ import audio.funkwhale.ffa.model.TracksCache import audio.funkwhale.ffa.model.TracksResponse import audio.funkwhale.ffa.utils.OAuth import audio.funkwhale.ffa.utils.getMetadata -import com.github.kittinunf.fuel.gson.gsonDeserializerOf import audio.funkwhale.ffa.utils.mustNormalizeUrl +import com.github.kittinunf.fuel.gson.gsonDeserializerOf import com.google.android.exoplayer2.offline.Download import com.google.android.exoplayer2.offline.DownloadManager import com.google.android.exoplayer2.upstream.cache.Cache diff --git a/app/src/main/java/audio/funkwhale/ffa/utils/Extensions.kt b/app/src/main/java/audio/funkwhale/ffa/utils/Extensions.kt index 7fb8123..fbd5e81 100644 --- a/app/src/main/java/audio/funkwhale/ffa/utils/Extensions.kt +++ b/app/src/main/java/audio/funkwhale/ffa/utils/Extensions.kt @@ -10,10 +10,8 @@ import audio.funkwhale.ffa.model.DownloadInfo import audio.funkwhale.ffa.repositories.Repository import com.github.kittinunf.fuel.core.FuelError import com.github.kittinunf.fuel.core.Request -import com.github.kittinunf.fuel.core.ResponseDeserializable import com.google.android.exoplayer2.offline.Download import com.google.gson.Gson -import com.google.gson.reflect.TypeToken import com.squareup.picasso.Picasso import com.squareup.picasso.RequestCreator import kotlinx.coroutines.CompletableDeferred @@ -23,7 +21,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import net.openid.appauth.ClientSecretPost -import java.io.Reader import java.text.SimpleDateFormat import java.util.Date import kotlin.coroutines.CoroutineContext diff --git a/app/src/main/java/audio/funkwhale/ffa/views/NowPlayingView.kt b/app/src/main/java/audio/funkwhale/ffa/views/NowPlayingView.kt index d4133b0..7ad7374 100644 --- a/app/src/main/java/audio/funkwhale/ffa/views/NowPlayingView.kt +++ b/app/src/main/java/audio/funkwhale/ffa/views/NowPlayingView.kt @@ -52,7 +52,7 @@ class NowPlayingView : MaterialCardView { viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { gestureDetectorCallback = OnGestureDetection() - gestureDetector = GestureDetector(context, gestureDetectorCallback) + gestureDetector = GestureDetector(context, gestureDetectorCallback!!) setOnTouchListener { _, motionEvent -> val ret = gestureDetector?.onTouchEvent(motionEvent) ?: false @@ -128,8 +128,8 @@ class NowPlayingView : MaterialCardView { } override fun onFling( - firstMotionEvent: MotionEvent?, - secondMotionEvent: MotionEvent?, + firstMotionEvent: MotionEvent, + secondMotionEvent: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { @@ -195,7 +195,7 @@ class NowPlayingView : MaterialCardView { return true } - override fun onSingleTapUp(e: MotionEvent?): Boolean { + override fun onSingleTapUp(e: MotionEvent): Boolean { layoutParams.let { if (height != minHeight) return true