From 0ab52e8f4d3b54a1d44782cfbdf9ed38faf8dd8c Mon Sep 17 00:00:00 2001
From: Koen <koen@pop-os.localdomain>
Date: Wed, 13 Dec 2023 10:34:14 +0100
Subject: [PATCH] Fixed notification implementation.

---
 app/build.gradle                              |   2 +-
 .../platformplayer/downloads/VideoDownload.kt |   2 +-
 .../mainactivity/main/VideoDetailView.kt      |  11 +-
 .../platformplayer/helpers/VideoHelper.kt     |  25 +++-
 .../services/MediaPlaybackService.kt          | 128 ++++++++++--------
 .../futo/platformplayer/states/StatePlayer.kt |   4 +-
 .../platformplayer/video/PlayerManager.kt     |  11 ++
 .../platformplayer/views/casting/CastView.kt  |  16 ++-
 .../main/res/layout/fragview_video_detail.xml |   2 +-
 .../main/res/layout/thumbnail_player_ui.xml   |   2 +-
 .../main/res/layout/thumbnail_video_view.xml  |   4 +-
 app/src/main/res/layout/video_player_ui.xml   |   4 +-
 .../main/res/layout/video_player_ui_bar.xml   |   2 +-
 .../res/layout/video_player_ui_fullscreen.xml |   2 +-
 app/src/main/res/layout/video_view.xml        |   8 +-
 app/src/main/res/layout/view_cast.xml         |   2 +-
 app/src/main/res/values/strings.xml           |   1 +
 17 files changed, 140 insertions(+), 86 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index 8214f0b6..68c7e905 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -175,13 +175,13 @@ dependencies {
     implementation 'androidx.media3:media3-exoplayer:1.2.0'
     implementation 'androidx.media3:media3-exoplayer-dash:1.2.0'
     implementation 'androidx.media3:media3-ui:1.2.0'
-    implementation 'androidx.media3:media3-session:1.2.0'
     implementation 'androidx.media3:media3-exoplayer-hls:1.2.0'
     implementation 'androidx.media3:media3-exoplayer-rtsp:1.2.0'
     implementation 'androidx.media3:media3-exoplayer-smoothstreaming:1.2.0'
     implementation 'androidx.media3:media3-transformer:1.2.0'
     implementation 'androidx.navigation:navigation-fragment-ktx:2.7.5'
     implementation 'androidx.navigation:navigation-ui-ktx:2.7.5'
+    implementation 'androidx.media:media:1.7.0'
 
     //Other
     implementation 'org.jmdns:jmdns:3.5.1'
diff --git a/app/src/main/java/com/futo/platformplayer/downloads/VideoDownload.kt b/app/src/main/java/com/futo/platformplayer/downloads/VideoDownload.kt
index 7f2b20ee..fe1dad58 100644
--- a/app/src/main/java/com/futo/platformplayer/downloads/VideoDownload.kt
+++ b/app/src/main/java/com/futo/platformplayer/downloads/VideoDownload.kt
@@ -67,7 +67,7 @@ class VideoDownload {
     val videoEither: IPlatformVideo get() = videoDetails ?: video ?: throw IllegalStateException("Missing video?");
     val id: PlatformID get() = videoEither.id
     val name: String get() = videoEither.name;
-    val thumbnail: String? get() = videoDetails?.thumbnails?.getHQThumbnail() ?: video?.thumbnails?.getHQThumbnail();
+    val thumbnail: String? get() = videoDetails?.thumbnails?.getHQThumbnail();
 
     var targetPixelCount: Long? = null;
     var targetBitrate: Long? = null;
diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt
index 4be113ed..66f01215 100644
--- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt
+++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt
@@ -12,6 +12,7 @@ import android.graphics.drawable.BitmapDrawable
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.net.Uri
+import android.support.v4.media.session.PlaybackStateCompat
 import android.text.Spanned
 import android.util.AttributeSet
 import android.util.Log
@@ -571,9 +572,9 @@ class VideoDetailView : ConstraintLayout {
         }
 
         _playerProgress.player = _player.exoPlayer?.player;
-        _playerProgress.setProgressUpdateListener { _, _ ->
-            StatePlayer.instance.updateMediaSessionPlaybackState();
-        }
+        _playerProgress.setProgressUpdateListener { position, _ ->
+            StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, position);
+        };
 
         StatePlayer.instance.onQueueChanged.subscribe(this) {
             if(!_destroyed) {
@@ -1358,11 +1359,9 @@ class VideoDetailView : ConstraintLayout {
             }
         }
 
-
         StatePlayer.instance.startOrUpdateMediaSession(context, video);
         StatePlayer.instance.setCurrentlyPlaying(video);
 
-
         if(video.isLive && video.live != null) {
             loadLiveChat(video);
         }
@@ -1791,7 +1790,7 @@ class VideoDetailView : ConstraintLayout {
             _cast.setIsPlaying(playing);
         } else {
             StatePlayer.instance.updateMediaSession( null);
-            StatePlayer.instance.updateMediaSessionPlaybackState();
+            StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, _player.exoPlayer?.player?.currentPosition ?: 0);
         }
 
         if(playing) {
diff --git a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt
index 7cf603ec..44045431 100644
--- a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt
+++ b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt
@@ -2,6 +2,13 @@ package com.futo.platformplayer.helpers
 
 import android.net.Uri
 import androidx.annotation.OptIn
+import androidx.media3.common.MediaItem
+import androidx.media3.common.MediaMetadata
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.datasource.ResolvingDataSource
+import androidx.media3.exoplayer.dash.DashMediaSource
+import androidx.media3.exoplayer.dash.manifest.DashManifestParser
+import androidx.media3.exoplayer.source.MediaSource
 import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
 import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
 import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
@@ -14,12 +21,6 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
 import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource
 import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource
 import com.futo.platformplayer.logging.Logger
-import androidx.media3.common.MediaItem
-import androidx.media3.common.util.UnstableApi
-import androidx.media3.datasource.ResolvingDataSource
-import androidx.media3.exoplayer.dash.DashMediaSource
-import androidx.media3.exoplayer.dash.manifest.DashManifestParser
-import androidx.media3.exoplayer.source.MediaSource
 import kotlin.math.abs
 
 class VideoHelper {
@@ -146,6 +147,18 @@ class VideoHelper {
             })).createMediaSource(manifest, MediaItem.Builder().setUri(Uri.parse(videoSource.getVideoUrl())).build())
         }
 
+        fun getMediaMetadata(media: IPlatformVideoDetails): MediaMetadata {
+            val builder = MediaMetadata.Builder()
+                .setArtist(media.author.name)
+                .setTitle(media.name)
+
+            media.thumbnails.getHQThumbnail()?.let {
+                builder.setArtworkUri(Uri.parse(it))
+            }
+
+            return builder.build()
+        }
+
         @OptIn(UnstableApi::class)
         fun convertItagSourceToChunkedDashSource(audioSource: JSAudioUrlRangeSource) : MediaSource {
             val manifestConfig = ProgressiveDashManifestCreator.fromAudioProgressiveStreamingUrl(audioSource.getAudioUrl(),
diff --git a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt
index 437fb960..4a205283 100644
--- a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt
+++ b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt
@@ -13,14 +13,15 @@ import android.graphics.drawable.Drawable
 import android.media.AudioFocusRequest
 import android.media.AudioManager
 import android.media.AudioManager.OnAudioFocusChangeListener
+import android.media.MediaMetadata
 import android.os.Build
 import android.os.IBinder
+import android.os.SystemClock
+import android.support.v4.media.MediaMetadataCompat
+import android.support.v4.media.session.MediaSessionCompat
+import android.support.v4.media.session.PlaybackStateCompat
 import android.util.Log
-import androidx.annotation.OptIn
 import androidx.core.app.NotificationCompat
-import androidx.media3.common.util.UnstableApi
-import androidx.media3.session.MediaSession
-import androidx.media3.session.MediaStyleNotificationHelper
 import com.bumptech.glide.Glide
 import com.bumptech.glide.request.target.CustomTarget
 import com.bumptech.glide.request.transition.Transition
@@ -50,7 +51,7 @@ class MediaPlaybackService : Service() {
     private var _audioManager: AudioManager? = null;
     private var _notificationManager: NotificationManager? = null;
     private var _notificationChannel: NotificationChannel? = null;
-    private var _mediaSession: MediaSession? = null;
+    private var _mediaSession: MediaSessionCompat? = null;
     private var _hasFocus: Boolean = false;
     private var _focusRequest: AudioFocusRequest? = null;
     private var _audioFocusLossTime_ms: Long? = null
@@ -81,7 +82,6 @@ class MediaPlaybackService : Service() {
 
         return START_STICKY;
     }
-    @OptIn(UnstableApi::class)
     fun setupNotificationRequirements() {
         _audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager;
         _notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
@@ -91,54 +91,47 @@ class MediaPlaybackService : Service() {
         };
         _notificationManager!!.createNotificationChannel(_notificationChannel!!);
 
-        _mediaSession = MediaSession.Builder(this, StatePlayer.instance.getPlayerOrCreate(this).player)
-
-            .setCallback(object: MediaSession.Callback {
-                override fun onMediaButtonEvent(session: MediaSession, controllerInfo: MediaSession.ControllerInfo, intent: Intent): Boolean {
-                    //TODO: Reimplement
-                    return super.onMediaButtonEvent(session, controllerInfo, intent)
-                }
-
-                /*override fun onSeekTo(pos: Long) {
-                    super.onSeekTo(pos)
-                    Logger.i(TAG, "Media session callback onSeekTo(pos = $pos)");
-                    MediaControlReceiver.onSeekToReceived.emit(pos);
-                }
+        _mediaSession = MediaSessionCompat(this, "PlayerState");
+        _mediaSession?.setPlaybackState(PlaybackStateCompat.Builder()
+            .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f)
+            .build());
+        _mediaSession?.setCallback(object: MediaSessionCompat.Callback() {
+            override fun onSeekTo(pos: Long) {
+                super.onSeekTo(pos)
+                Logger.i(TAG, "Media session callback onSeekTo(pos = $pos)");
+                MediaControlReceiver.onSeekToReceived.emit(pos);
+            }
 
-                override fun onPlay() {
-                    super.onPlay();
-                    Logger.i(TAG, "Media session callback onPlay()");
-                    MediaControlReceiver.onPlayReceived.emit();
-                }
+            override fun onPlay() {
+                super.onPlay();
+                Logger.i(TAG, "Media session callback onPlay()");
+                MediaControlReceiver.onPlayReceived.emit();
+            }
 
-                override fun onPause() {
-                    super.onPause();
-                    Logger.i(TAG, "Media session callback onPause()");
-                    MediaControlReceiver.onPauseReceived.emit();
-                }
+            override fun onPause() {
+                super.onPause();
+                Logger.i(TAG, "Media session callback onPause()");
+                MediaControlReceiver.onPauseReceived.emit();
+            }
 
-                override fun onStop() {
-                    super.onStop();
-                    Logger.i(TAG, "Media session callback onStop()");
-                    MediaControlReceiver.onCloseReceived.emit();
-                }
+            override fun onStop() {
+                super.onStop();
+                Logger.i(TAG, "Media session callback onStop()");
+                MediaControlReceiver.onCloseReceived.emit();
+            }
 
-                override fun onSkipToPrevious() {
-                    super.onSkipToPrevious();
-                    Logger.i(TAG, "Media session callback onSkipToPrevious()");
-                    MediaControlReceiver.onPreviousReceived.emit();
-                }
+            override fun onSkipToPrevious() {
+                super.onSkipToPrevious();
+                Logger.i(TAG, "Media session callback onSkipToPrevious()");
+                MediaControlReceiver.onPreviousReceived.emit();
+            }
 
-                override fun onSkipToNext() {
-                    super.onSkipToNext()
-                    Logger.i(TAG, "Media session callback onSkipToNext()");
-                    MediaControlReceiver.onNextReceived.emit();
-                }*/
-            })
-            .build();
-        /*_mediaSession?.setPlaybackState(PlaybackStateCompat.Builder()
-            .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f)
-            .build());*/
+            override fun onSkipToNext() {
+                super.onSkipToNext()
+                Logger.i(TAG, "Media session callback onSkipToNext()");
+                MediaControlReceiver.onNextReceived.emit();
+            }
+        });
     }
 
     override fun onCreate() {
@@ -193,7 +186,15 @@ class MediaPlaybackService : Service() {
         if(_notificationChannel == null || _mediaSession == null)
             setupNotificationRequirements();
 
+        _mediaSession?.setMetadata(
+            MediaMetadataCompat.Builder()
+                .putString(MediaMetadata.METADATA_KEY_ARTIST, video.author.name)
+                .putString(MediaMetadata.METADATA_KEY_TITLE, video.name)
+                .putLong(MediaMetadata.METADATA_KEY_DURATION, video.duration * 1000)
+                .build());
+
         val thumbnail = video.thumbnails.getHQThumbnail();
+
         _notif_last_video = video;
 
         if(isUpdating)
@@ -220,7 +221,6 @@ class MediaPlaybackService : Service() {
     private fun generateMediaAction(icon: Int, title: String, intent: PendingIntent) : NotificationCompat.Action {
         return NotificationCompat.Action.Builder(icon, title, intent).build();
     }
-    @OptIn(UnstableApi::class)
     private fun notifyMediaSession(video: IPlatformVideo?, desiredBitmap: Bitmap?) {
         val channel = _notificationChannel ?: return;
         val session = _mediaSession ?: return;
@@ -249,9 +249,14 @@ class MediaPlaybackService : Service() {
             .setOngoing(true)
             .setSilent(true)
             .setContentIntent(PendingIntent.getActivity(this, 5, bringUpIntent, PendingIntent.FLAG_IMMUTABLE))
-            .setStyle(
-                if(hasQueue) MediaStyleNotificationHelper.MediaStyle(session)//.setShowActionsInCompactView(0, 1, 2)
-                else MediaStyleNotificationHelper.MediaStyle(session))//.setShowActionsInCompactView(0))
+            .setStyle(if(hasQueue)
+                androidx.media.app.NotificationCompat.MediaStyle()
+                    .setMediaSession(session.sessionToken)
+                    .setShowActionsInCompactView(0, 1, 2)
+            else
+                androidx.media.app.NotificationCompat.MediaStyle()
+                    .setMediaSession(session.sessionToken)
+                    .setShowActionsInCompactView(0))
             .setDeleteIntent(deleteIntent)
             .setChannelId(channel.id)
 
@@ -298,7 +303,7 @@ class MediaPlaybackService : Service() {
         val notif = builder.build();
         notif.flags = notif.flags or NotificationCompat.FLAG_ONGOING_EVENT or NotificationCompat.FLAG_NO_CLEAR;
 
-        Logger.i(TAG, "Updating notification bitmap=${if (bitmap != null) "yes" else "no."} channelId=${channel.id} icon=${icon} video=${video?.name ?: ""} playWhenReady=${playWhenReady} session.sessionToken=${session.token}");
+        Logger.i(TAG, "Updating notification bitmap=${if (bitmap != null) "yes" else "no."} channelId=${channel.id} icon=${icon} video=${video?.name ?: ""} playWhenReady=${playWhenReady} session.sessionToken=${session.sessionToken}");
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             // For API 29 and above
@@ -311,7 +316,20 @@ class MediaPlaybackService : Service() {
         _notif_last_bitmap = bitmap;
     }
 
-    fun updateMediaSessionPlaybackState() {
+    fun updateMediaSessionPlaybackState(state: Int, pos: Long) {
+        _mediaSession?.setPlaybackState(
+            PlaybackStateCompat.Builder()
+                .setActions(
+                    PlaybackStateCompat.ACTION_SEEK_TO or
+                            PlaybackStateCompat.ACTION_PLAY or
+                            PlaybackStateCompat.ACTION_PAUSE or
+                            PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
+                            PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
+                            PlaybackStateCompat.ACTION_PLAY_PAUSE
+                )
+                .setState(state, pos, 1f, SystemClock.elapsedRealtime())
+                .build());
+
         if(_focusRequest == null)
             setAudioFocus();
     }
diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt
index 6f2ed300..7d642ac4 100644
--- a/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt
+++ b/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt
@@ -107,8 +107,8 @@ class StatePlayer {
     fun updateMediaSession(videoUpdated: IPlatformVideoDetails?) {
         MediaPlaybackService.getService()?.updateMediaSession(videoUpdated);
     }
-    fun updateMediaSessionPlaybackState() {
-        MediaPlaybackService.getService()?.updateMediaSessionPlaybackState();
+    fun updateMediaSessionPlaybackState(state: Int, pos: Long) {
+        MediaPlaybackService.getService()?.updateMediaSessionPlaybackState(state, pos);
     }
     fun closeMediaSession() {
         MediaPlaybackService.getService()?.closeMediaSession();
diff --git a/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt b/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt
index 05a6eef0..22926841 100644
--- a/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt
+++ b/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt
@@ -1,5 +1,7 @@
 package com.futo.platformplayer.video
 
+import android.media.session.PlaybackState
+import android.support.v4.media.session.PlaybackStateCompat
 import androidx.media3.common.Player
 import androidx.media3.exoplayer.ExoPlayer
 import androidx.media3.ui.PlayerView
@@ -21,6 +23,15 @@ class PlayerManager {
         this.player = exoPlayer;
     }
 
+
+    fun getPlaybackStateCompat() : Int {
+        return when(player.playbackState) {
+            ExoPlayer.STATE_READY -> if(player.playWhenReady) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED;
+            ExoPlayer.STATE_BUFFERING -> PlaybackState.STATE_BUFFERING;
+            else -> PlaybackState.STATE_NONE
+        }
+    }
+
     @Synchronized
     fun attach(view: PlayerView, stateName: String) {
         if(view != _currentView) {
diff --git a/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt b/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt
index 52106dd6..aeca7334 100644
--- a/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt
+++ b/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt
@@ -1,6 +1,8 @@
 package com.futo.platformplayer.views.casting
 
 import android.content.Context
+import android.media.session.PlaybackState
+import android.support.v4.media.session.PlaybackStateCompat
 import android.util.AttributeSet
 import android.util.TypedValue
 import android.view.LayoutInflater
@@ -147,9 +149,10 @@ class CastView : ConstraintLayout {
             _buttonPlay.visibility = View.VISIBLE;
         }
 
+        val position = StateCasting.instance.activeDevice?.expectedCurrentTime?.times(1000.0)?.toLong();
         if(StatePlayer.instance.hasMediaSession()) {
             StatePlayer.instance.updateMediaSession(null);
-            StatePlayer.instance.updateMediaSessionPlaybackState();
+            StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), (position ?: 0));
         }
     }
 
@@ -195,7 +198,7 @@ class CastView : ConstraintLayout {
     fun setTime(ms: Long) {
         _textPosition.text = ms.toHumanTime(true);
         _timeBar.setPosition(ms / 1000);
-        StatePlayer.instance.updateMediaSessionPlaybackState();
+        StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), ms);
     }
 
     fun cleanup() {
@@ -205,4 +208,13 @@ class CastView : ConstraintLayout {
         _updateTimeJob = null;
         _scope.cancel();
     }
+
+    private fun getPlaybackStateCompat(): Int {
+        val d = StateCasting.instance.activeDevice ?: return PlaybackState.STATE_NONE;
+
+        return when(d.isPlaying) {
+            true -> PlaybackStateCompat.STATE_PLAYING;
+            else -> PlaybackStateCompat.STATE_PAUSED;
+        }
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragview_video_detail.xml b/app/src/main/res/layout/fragview_video_detail.xml
index 607de6ed..7652198e 100644
--- a/app/src/main/res/layout/fragview_video_detail.xml
+++ b/app/src/main/res/layout/fragview_video_detail.xml
@@ -39,7 +39,7 @@
             android:elevation="4dp"
             android:layout_marginBottom="6dp" />
 
-        <com.google.android.exoplayer2.ui.PlayerControlView
+        <androidx.media3.ui.PlayerControlView
             android:id="@+id/videodetail_progress"
             android:layout_width="match_parent"
             android:layout_height="12dp"
diff --git a/app/src/main/res/layout/thumbnail_player_ui.xml b/app/src/main/res/layout/thumbnail_player_ui.xml
index 8ce8ba99..adfc7e9d 100644
--- a/app/src/main/res/layout/thumbnail_player_ui.xml
+++ b/app/src/main/res/layout/thumbnail_player_ui.xml
@@ -103,7 +103,7 @@
                 android:textStyle="normal" />
         </LinearLayout>
 
-        <com.google.android.exoplayer2.ui.DefaultTimeBar
+        <androidx.media3.ui.DefaultTimeBar
             android:id="@id/exo_progress"
             android:layout_width="match_parent"
             android:layout_height="16dp"
diff --git a/app/src/main/res/layout/thumbnail_video_view.xml b/app/src/main/res/layout/thumbnail_video_view.xml
index b63e6715..e553e385 100644
--- a/app/src/main/res/layout/thumbnail_video_view.xml
+++ b/app/src/main/res/layout/thumbnail_video_view.xml
@@ -4,7 +4,7 @@
     android:layout_height="wrap_content"
     android:background="@color/transparent"
     xmlns:app="http://schemas.android.com/apk/res-auto">
-    <com.google.android.exoplayer2.ui.PlayerView
+    <androidx.media3.ui.PlayerView
         android:id="@+id/video_player"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -15,7 +15,7 @@
         app:resize_mode="fit"
         app:show_buffering="when_playing"
         android:layout_marginBottom="6dp" />
-    <com.google.android.exoplayer2.ui.PlayerControlView
+    <androidx.media3.ui.PlayerControlView
         android:id="@+id/video_player_controller"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/app/src/main/res/layout/video_player_ui.xml b/app/src/main/res/layout/video_player_ui.xml
index 15c78f2a..ae797f0f 100644
--- a/app/src/main/res/layout/video_player_ui.xml
+++ b/app/src/main/res/layout/video_player_ui.xml
@@ -198,12 +198,12 @@
     </TextView>
 
 
-    <com.google.android.exoplayer2.ui.SubtitleView
+    <androidx.media3.ui.SubtitleView
         android:id="@id/exo_subtitles"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-    <com.google.android.exoplayer2.ui.DefaultTimeBar
+    <androidx.media3.ui.DefaultTimeBar
         android:id="@id/exo_progress"
         android:layout_width="match_parent"
         android:layout_height="16dp"
diff --git a/app/src/main/res/layout/video_player_ui_bar.xml b/app/src/main/res/layout/video_player_ui_bar.xml
index 33766bb4..2bd2f277 100644
--- a/app/src/main/res/layout/video_player_ui_bar.xml
+++ b/app/src/main/res/layout/video_player_ui_bar.xml
@@ -16,7 +16,7 @@
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent">
 
-        <com.google.android.exoplayer2.ui.DefaultTimeBar
+        <androidx.media3.ui.DefaultTimeBar
             android:id="@id/exo_progress"
             android:layout_width="match_parent"
             android:layout_height="12dp"
diff --git a/app/src/main/res/layout/video_player_ui_fullscreen.xml b/app/src/main/res/layout/video_player_ui_fullscreen.xml
index 7fa4e188..57b663af 100644
--- a/app/src/main/res/layout/video_player_ui_fullscreen.xml
+++ b/app/src/main/res/layout/video_player_ui_fullscreen.xml
@@ -227,7 +227,7 @@
 
     </TextView>
 
-    <com.google.android.exoplayer2.ui.DefaultTimeBar
+    <androidx.media3.ui.DefaultTimeBar
         android:id="@id/exo_progress"
         android:layout_width="match_parent"
         android:layout_height="12dp"
diff --git a/app/src/main/res/layout/video_view.xml b/app/src/main/res/layout/video_view.xml
index 3cb0bc59..0a35c2f7 100644
--- a/app/src/main/res/layout/video_view.xml
+++ b/app/src/main/res/layout/video_view.xml
@@ -6,7 +6,7 @@
     android:id="@+id/videoview_root"
     android:background="@color/transparent"
     xmlns:app="http://schemas.android.com/apk/res-auto">
-    <com.google.android.exoplayer2.ui.PlayerView
+    <androidx.media3.ui.PlayerView
         android:id="@+id/video_player"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -16,7 +16,7 @@
         app:show_buffering="always"
         android:layout_marginBottom="6dp" />
     <!--
-    <com.google.android.exoplayer2.ui.PlayerControlView
+    <androidx.media3.ui.PlayerControlView
         android:id="@+id/video_player_bar"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -48,7 +48,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <com.google.android.exoplayer2.ui.PlayerControlView
+        <androidx.media3.ui.PlayerControlView
             android:id="@+id/video_player_controller"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
@@ -56,7 +56,7 @@
             android:layout_marginRight="-6dp"
             app:show_timeout="-1"
             app:controller_layout_id="@layout/video_player_ui" />
-        <com.google.android.exoplayer2.ui.PlayerControlView
+        <androidx.media3.ui.PlayerControlView
             android:id="@+id/video_player_controller_fullscreen"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/app/src/main/res/layout/view_cast.xml b/app/src/main/res/layout/view_cast.xml
index bab3ec6f..9253b3e3 100644
--- a/app/src/main/res/layout/view_cast.xml
+++ b/app/src/main/res/layout/view_cast.xml
@@ -143,7 +143,7 @@
         app:layout_constraintTop_toTopOf="@id/text_position"
         app:layout_constraintBottom_toBottomOf="@id/text_position"/>
 
-    <com.google.android.exoplayer2.ui.DefaultTimeBar
+    <androidx.media3.ui.DefaultTimeBar
         android:id="@+id/time_progress"
         android:layout_width="match_parent"
         android:layout_height="16dp"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d60f4d4e..893729c3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -712,6 +712,7 @@
     <string name="fcast_technical_documentation">FCast Technical Documentation</string>
     <string name="login_to_view_your_comments">Login to view your comments</string>
     <string name="polycentric_is_disabled">Polycentric is disabled</string>
+    <string name="play_pause">Play Pause</string>
     <string-array name="home_screen_array">
         <item>Recommendations</item>
         <item>Subscriptions</item>
-- 
GitLab