From f7ad2d0a9b3ae04543c56ce11976ff18b4aee4c5 Mon Sep 17 00:00:00 2001 From: Vini Waki Date: Mon, 22 Jan 2024 16:31:30 -0300 Subject: [PATCH] feat: add video casting --- androidenhancedvideoplayer/build.gradle | 2 + .../src/main/AndroidManifest.xml | 5 + .../CastOptionsProvider.kt | 25 ++++ .../components/EnhancedVideoPlayer.kt | 123 +++++++++++++----- .../components/mediaRouter/MediaRouter.kt | 25 ++++ .../mediaRouter/MediaRouterViewModel.kt | 23 ++++ .../playerOverlay/PlayerControls.kt | 2 + .../components/playerOverlay/SeekHandler.kt | 7 + .../components/playerOverlay/TopControls.kt | 39 ++++-- .../utils/ExoPlayerUtils.kt | 4 +- .../utils/TrackUtils.kt | 4 +- app/build.gradle | 3 +- .../MainActivity.kt | 13 +- 13 files changed, 222 insertions(+), 53 deletions(-) create mode 100644 androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/CastOptionsProvider.kt create mode 100644 androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/mediaRouter/MediaRouter.kt create mode 100644 androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/mediaRouter/MediaRouterViewModel.kt diff --git a/androidenhancedvideoplayer/build.gradle b/androidenhancedvideoplayer/build.gradle index 801a7a58..19999c47 100644 --- a/androidenhancedvideoplayer/build.gradle +++ b/androidenhancedvideoplayer/build.gradle @@ -69,6 +69,8 @@ dependencies { // exoplayer implementation "androidx.media3:media3-exoplayer:$mediaVersion" implementation "androidx.media3:media3-ui:$mediaVersion" + implementation "androidx.media3:media3-cast:$mediaVersion" + implementation 'androidx.appcompat:appcompat:1.3.1' } afterEvaluate { diff --git a/androidenhancedvideoplayer/src/main/AndroidManifest.xml b/androidenhancedvideoplayer/src/main/AndroidManifest.xml index 8bdb7e14..e8f2b3c1 100644 --- a/androidenhancedvideoplayer/src/main/AndroidManifest.xml +++ b/androidenhancedvideoplayer/src/main/AndroidManifest.xml @@ -1,4 +1,9 @@ + + + diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/CastOptionsProvider.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/CastOptionsProvider.kt new file mode 100644 index 00000000..bcaab722 --- /dev/null +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/CastOptionsProvider.kt @@ -0,0 +1,25 @@ +package com.profusion.androidenhancedvideoplayer + +import android.content.Context +import com.google.android.gms.cast.CastMediaControlIntent +import com.google.android.gms.cast.LaunchOptions +import com.google.android.gms.cast.framework.CastOptions +import com.google.android.gms.cast.framework.OptionsProvider +import com.google.android.gms.cast.framework.SessionProvider + +class CastOptionsProvider : OptionsProvider { + override fun getCastOptions(context: Context): CastOptions { + val launchOptions = LaunchOptions.Builder() + .setAndroidReceiverCompatible(true) + .build() + return CastOptions.Builder() + .setLaunchOptions(launchOptions) + .setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID) + .setStopReceiverApplicationWhenEndingSession(true) + .build() + } + + override fun getAdditionalSessionProviders(p0: Context): MutableList? { + return null + } +} diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/EnhancedVideoPlayer.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/EnhancedVideoPlayer.kt index 9d66cadb..33da7f51 100644 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/EnhancedVideoPlayer.kt +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/EnhancedVideoPlayer.kt @@ -10,9 +10,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.* -import androidx.compose.runtime.getValue import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.* @@ -22,6 +20,11 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.* import androidx.compose.ui.viewinterop.AndroidView +import androidx.media3.cast.CastPlayer +import androidx.media3.cast.SessionAvailabilityListener +import androidx.media3.common.BasePlayer +import androidx.media3.common.C +import androidx.media3.common.MediaItem import androidx.media3.common.Player import androidx.media3.common.TrackSelectionParameters import androidx.media3.common.Tracks @@ -29,6 +32,7 @@ import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.PlayerView +import com.google.android.gms.cast.framework.CastContext import com.profusion.androidenhancedvideoplayer.R import com.profusion.androidenhancedvideoplayer.components.playerOverlay.ControlsCustomization import com.profusion.androidenhancedvideoplayer.components.playerOverlay.PlayerControls @@ -74,6 +78,7 @@ fun EnhancedVideoPlayer( zoomToFit: Boolean = true, enableImmersiveMode: Boolean = true, disableControls: Boolean = false, + disableCast: Boolean = false, currentTimeTickInMs: Long = CURRENT_TIME_TICK_IN_MS, controlsVisibilityDurationInMs: Long = PLAYER_CONTROLS_VISIBILITY_DURATION_IN_MS, controlsCustomization: ControlsCustomization = ControlsCustomization(), @@ -86,19 +91,25 @@ fun EnhancedVideoPlayer( val orientation = configuration.orientation val volumeController = remember { VolumeController(context) } - var isPlaying by remember { mutableStateOf(exoPlayer.isPlaying) } + val castContext = CastContext.getSharedInstance() + val castPlayer = castContext?.let { CastPlayer(it) } + val mediaItems: List = listOfNotNull(exoPlayer.currentMediaItem) + var currentPlayer: Player by remember { mutableStateOf(exoPlayer) } + var isCasting by remember { mutableStateOf(false) } + + var isPlaying by remember { mutableStateOf(currentPlayer.isPlaying) } var isBuffering by remember { - mutableStateOf(exoPlayer.playbackState == ExoPlayer.STATE_BUFFERING) + mutableStateOf(currentPlayer.playbackState == BasePlayer.STATE_BUFFERING) } - var hasEnded by remember { mutableStateOf(exoPlayer.playbackState == ExoPlayer.STATE_ENDED) } + var hasEnded by remember { mutableStateOf(currentPlayer.playbackState == BasePlayer.STATE_ENDED) } var isControlsVisible by remember { mutableStateOf(false) } - var speed by remember { mutableStateOf(exoPlayer.playbackParameters.speed) } - var loop by remember { mutableStateOf(exoPlayer.repeatMode == ExoPlayer.REPEAT_MODE_ALL) } - var currentTime by remember { mutableStateOf(exoPlayer.contentPosition) } - var bufferedPosition by remember { mutableStateOf(exoPlayer.bufferedPosition) } - var totalDuration by remember { mutableStateOf(exoPlayer.duration) } + var speed by remember { mutableStateOf(currentPlayer.playbackParameters.speed) } + var loop by remember { mutableStateOf(currentPlayer.repeatMode == BasePlayer.REPEAT_MODE_ALL) } + var currentTime by remember { mutableStateOf(currentPlayer.contentPosition) } + var bufferedPosition by remember { mutableStateOf(currentPlayer.bufferedPosition) } + var totalDuration by remember { mutableStateOf(currentPlayer.duration) } var title by remember { - mutableStateOf(exoPlayer.currentMediaItem?.mediaMetadata?.title?.toString()) + mutableStateOf(currentPlayer.currentMediaItem?.mediaMetadata?.title?.toString()) } var currentImagePreview by remember { mutableStateOf(ImageBitmap(DEFAULT_THUMBNAIL_WIDTH, DEFAULT_THUMBNAIL_HEIGHT)) @@ -129,23 +140,47 @@ fun EnhancedVideoPlayer( ) { mutableStateOf( generateTrackQualityOptions( - exoPlayer.currentTracks, + currentPlayer.currentTracks, autoQualityTrack ) ) } + fun setPlayer(newPlayer: Player) { + if (castContext != null && !disableCast && currentPlayer !== newPlayer) { + var mediaIndex = C.INDEX_UNSET + var playbackPositionMs = C.TIME_UNSET + var playWhenReady = false + val previousPlayer = currentPlayer + + val playbackState = previousPlayer.playbackState + if (playbackState != Player.STATE_ENDED) { + mediaIndex = previousPlayer.currentMediaItemIndex + playbackPositionMs = previousPlayer.currentPosition + playWhenReady = previousPlayer.playWhenReady + + previousPlayer.stop() + } + + newPlayer.setMediaItems(mediaItems, mediaIndex, playbackPositionMs) + newPlayer.playWhenReady = playWhenReady + newPlayer.prepare() + + currentPlayer = newPlayer + } + } + DisposableEffect(context) { - val listener = object : Player.Listener { + val listener = object : Player.Listener, SessionAvailabilityListener { override fun onEvents(player: Player, events: Player.Events) { isPlaying = player.isPlaying - isBuffering = player.playbackState == ExoPlayer.STATE_BUFFERING - hasEnded = player.playbackState == ExoPlayer.STATE_ENDED + isBuffering = player.playbackState == BasePlayer.STATE_BUFFERING + hasEnded = player.playbackState == BasePlayer.STATE_ENDED speed = player.playbackParameters.speed title = player.mediaMetadata.title?.toString() currentTime = player.contentPosition totalDuration = player.duration - loop = player.repeatMode == ExoPlayer.REPEAT_MODE_ALL + loop = player.repeatMode == BasePlayer.REPEAT_MODE_ALL deviceVolume = player.deviceVolume super.onEvents(player, events) } @@ -164,11 +199,26 @@ fun EnhancedVideoPlayer( ) super.onTrackSelectionParametersChanged(parameters) } + + override fun onCastSessionAvailable() { + setPlayer(castPlayer ?: exoPlayer) + isCasting = true + isControlsVisible = true + } + + override fun onCastSessionUnavailable() { + setPlayer(exoPlayer) + isCasting = false + } } exoPlayer.addListener(listener) + castPlayer?.addListener(listener) + castPlayer?.setSessionAvailabilityListener(listener) onDispose { exoPlayer.removeListener(listener) + castPlayer?.removeListener(listener) + castPlayer?.setSessionAvailabilityListener(null) } } @@ -176,8 +226,8 @@ fun EnhancedVideoPlayer( timeoutInMs = currentTimeTickInMs, enabled = isControlsVisible ) { - currentTime = exoPlayer.currentPosition - bufferedPosition = exoPlayer.bufferedPosition + currentTime = currentPlayer.currentPosition + bufferedPosition = currentPlayer.bufferedPosition } LaunchedEffect(isFullScreen) { @@ -198,7 +248,8 @@ fun EnhancedVideoPlayer( isPlaying, isBrightnessSliderDragged, isVolumeSliderDragged, - isTimeBarDragged + isTimeBarDragged, + isCasting ) { if ( isControlsVisible && @@ -206,7 +257,8 @@ fun EnhancedVideoPlayer( controlsVisibilityDurationInMs > 0 && !isBrightnessSliderDragged && !isTimeBarDragged && - !isVolumeSliderDragged + !isVolumeSliderDragged && + !isCasting ) { delay(controlsVisibilityDurationInMs) isControlsVisible = false @@ -258,22 +310,25 @@ fun EnhancedVideoPlayer( } else { AspectRatioFrameLayout.RESIZE_MODE_FIT } + playerView.player = currentPlayer } ) if (!disableControls) { Box(modifier = Modifier.matchParentSize()) { SeekHandler( - seekIncrement = exoPlayer::seekIncrement, + seekIncrement = currentPlayer::seekIncrement, disableSeekForward = hasEnded, + isCasting = isCasting, controlsCustomization = controlsCustomization, toggleControlsVisibility = { - setControlsVisibility(!isControlsVisible) + setControlsVisibility(!isControlsVisible || isCasting) }, setControlsVisibility = ::setControlsVisibility, transformSeekIncrementRatio = transformSeekIncrementRatio ) PlayerControls( title = title, + disableCast = disableCast, isVisible = isControlsVisible, isPlaying = isPlaying, isBuffering = isBuffering, @@ -285,15 +340,15 @@ fun EnhancedVideoPlayer( brightnessMutableInteractionSource = brightnessMutableInteractionSource, volumeMutableInteractionSource = volumeMutableInteractionSource, timeBarMutableInteractionSource = timeBarMutableInteractionSource, - totalDuration = totalDuration, + totalDuration = if (totalDuration > 0) totalDuration else 0, currentTime = { currentTime }, bufferedPosition = { bufferedPosition }, - onPreviousClick = exoPlayer::seekToPrevious, - onNextClick = exoPlayer::seekToNext, + onPreviousClick = currentPlayer::seekToPrevious, + onNextClick = currentPlayer::seekToNext, onPauseToggle = when { - hasEnded -> exoPlayer::seekToDefaultPosition - isPlaying -> exoPlayer::pause - else -> exoPlayer::play + hasEnded -> currentPlayer::seekToDefaultPosition + isPlaying -> currentPlayer::pause + else -> currentPlayer::play }, onFullScreenToggle = { when (isFullScreen) { @@ -304,7 +359,7 @@ fun EnhancedVideoPlayer( onSettingsToggle = { isSettingsOpen = !isSettingsOpen }, onSeekBarValueFinished = { value -> currentTime = value - exoPlayer.seekTo(value) + currentPlayer.seekTo(value) }, onSeekBarValueChange = { if (previewThumbnailBuilder != null) { @@ -324,12 +379,12 @@ fun EnhancedVideoPlayer( onDismissRequest = { isSettingsOpen = false }, speed = speed, isLoopEnabled = loop, - onSpeedSelected = exoPlayer::setPlaybackSpeed, + onSpeedSelected = currentPlayer::setPlaybackSpeed, onIsLoopEnabledSelected = { value -> - exoPlayer.repeatMode = if (value) { - ExoPlayer.REPEAT_MODE_ALL + currentPlayer.repeatMode = if (value) { + BasePlayer.REPEAT_MODE_ALL } else { - ExoPlayer.REPEAT_MODE_OFF + BasePlayer.REPEAT_MODE_OFF } }, selectedQualityTrack = { @@ -339,7 +394,7 @@ fun EnhancedVideoPlayer( trackQualityOptions }, onQualityChanged = { selectedQualityTrack -> - exoPlayer.setVideoQuality(selectedQualityTrack) + currentPlayer.setVideoQuality(selectedQualityTrack) }, customization = settingsControlsCustomization ) diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/mediaRouter/MediaRouter.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/mediaRouter/MediaRouter.kt new file mode 100644 index 00000000..1a51339a --- /dev/null +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/mediaRouter/MediaRouter.kt @@ -0,0 +1,25 @@ +package com.profusion.androidenhancedvideoplayer.components.mediaRouter + +import android.content.Context +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.google.android.gms.cast.framework.CastContext +import com.profusion.androidenhancedvideoplayer.styling.Dimensions + +@Composable +fun MediaRouter(context: Context, modifier: Modifier = Modifier) { + val viewModel = MediaRouterViewModel(context) + val castContext = CastContext.getSharedInstance() + + if (castContext != null) { + Box(modifier = modifier) { + AndroidView( + factory = { viewModel.mediaRouteButton() }, + modifier = Modifier.size(Dimensions.xlarge) + ) + } + } +} diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/mediaRouter/MediaRouterViewModel.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/mediaRouter/MediaRouterViewModel.kt new file mode 100644 index 00000000..0bf14455 --- /dev/null +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/mediaRouter/MediaRouterViewModel.kt @@ -0,0 +1,23 @@ +package com.profusion.androidenhancedvideoplayer.components.mediaRouter + +import android.content.Context +import android.view.View +import androidx.lifecycle.ViewModel +import androidx.mediarouter.app.MediaRouteActionProvider +import androidx.mediarouter.media.MediaControlIntent +import androidx.mediarouter.media.MediaRouteSelector + +class MediaRouterViewModel(context: Context) : ViewModel() { + private val mediaRouteActionProvider = MediaRouteActionProvider(context) + + init { + mediaRouteActionProvider.onCreateMediaRouteButton() + mediaRouteActionProvider.routeSelector = MediaRouteSelector.Builder() + .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) + .build() + } + + fun mediaRouteButton(): View { + return mediaRouteActionProvider.onCreateActionView() + } +} diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControls.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControls.kt index 93c48682..2ff1bf9e 100644 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControls.kt +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControls.kt @@ -33,6 +33,7 @@ data class ControlsCustomization( fun PlayerControls( modifier: Modifier = Modifier, title: String? = null, + disableCast: Boolean = false, isVisible: Boolean, isPlaying: Boolean, isBuffering: Boolean, @@ -70,6 +71,7 @@ fun PlayerControls( TopControls( modifier = it, title = title, + disableCast = disableCast, shouldShowContent = shouldShowContent ) }, diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/SeekHandler.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/SeekHandler.kt index ebda0db6..1c950305 100644 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/SeekHandler.kt +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/SeekHandler.kt @@ -33,6 +33,7 @@ private const val TRANSITION_LABEL = "scaleSeekIcon" fun SeekHandler( seekIncrement: (Long) -> Unit, disableSeekForward: Boolean, + isCasting: Boolean = false, toggleControlsVisibility: () -> Unit, setControlsVisibility: (value: Boolean) -> Unit, transformSeekIncrementRatio: (tapCount: Int) -> Long, @@ -68,6 +69,9 @@ fun SeekHandler( delay(JOB_TIMEOUT) forwardTapCount = 0 + if (isCasting) { + setControlsVisibility(true) + } } } @@ -81,6 +85,9 @@ fun SeekHandler( delay(JOB_TIMEOUT) rewindTapCount = 0 + if (isCasting) { + setControlsVisibility(true) + } } } diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/TopControls.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/TopControls.kt index 0f608ff0..4df58654 100644 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/TopControls.kt +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/TopControls.kt @@ -1,6 +1,7 @@ package com.profusion.androidenhancedvideoplayer.components.playerOverlay import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme @@ -9,14 +10,18 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview +import com.profusion.androidenhancedvideoplayer.components.mediaRouter.MediaRouter import com.profusion.androidenhancedvideoplayer.styling.Dimensions @Composable +@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) fun TopControls( modifier: Modifier = Modifier, title: String? = null, + disableCast: Boolean = false, shouldShowContent: Boolean = true ) { Box( @@ -24,16 +29,30 @@ fun TopControls( .fillMaxWidth() .padding(horizontal = Dimensions.large) ) { - if (title != null && shouldShowContent) { - Text( - text = title, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleLarge.copy( - color = Color.White - ), - modifier = Modifier.align(Alignment.CenterStart) - ) + if (shouldShowContent) { + if (title != null) { + Text( + text = title, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleLarge.copy( + color = Color.White + ), + modifier = Modifier.fillMaxWidth() + ) + } else { + Spacer(modifier = Modifier.fillMaxWidth()) + } + + if (!disableCast) { + LocalContext.current.setTheme( + androidx.appcompat.R.style.Theme_AppCompat_NoActionBar + ) + MediaRouter( + context = LocalContext.current, + modifier = Modifier.align(Alignment.CenterEnd) + ) + } } } } diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/utils/ExoPlayerUtils.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/utils/ExoPlayerUtils.kt index ef1892df..e6b5e918 100644 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/utils/ExoPlayerUtils.kt +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/utils/ExoPlayerUtils.kt @@ -1,7 +1,7 @@ package com.profusion.androidenhancedvideoplayer.utils -import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.common.Player -fun ExoPlayer.seekIncrement(incrementMs: Long) { +fun Player.seekIncrement(incrementMs: Long) { seekTo(currentPosition + incrementMs) } diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/utils/TrackUtils.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/utils/TrackUtils.kt index eca0bb12..5f0bc1a0 100644 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/utils/TrackUtils.kt +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/utils/TrackUtils.kt @@ -3,11 +3,11 @@ package com.profusion.androidenhancedvideoplayer.utils import androidx.annotation.OptIn import androidx.media3.common.C import androidx.media3.common.Format +import androidx.media3.common.Player import androidx.media3.common.TrackSelectionOverride import androidx.media3.common.TrackSelectionParameters import androidx.media3.common.Tracks import androidx.media3.common.util.UnstableApi -import androidx.media3.exoplayer.ExoPlayer import com.google.common.collect.ImmutableList private const val NO_ROLE_FLAGS = 0 @@ -59,7 +59,7 @@ fun TrackSelectionParameters.getSelectedTrackQualityItem( ) } -fun ExoPlayer.setVideoQuality(track: TrackQualityItem) { +fun Player.setVideoQuality(track: TrackQualityItem) { trackSelectionParameters = when (track) { is TrackQualityAuto -> { trackSelectionParameters diff --git a/app/build.gradle b/app/build.gradle index c3628c61..56d75d28 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ plugins { android { namespace 'com.example.androidenhancedvideoplayer' - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "com.example.teste" @@ -56,6 +56,7 @@ dependencies { implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.material3:material3' + implementation 'com.google.android.gms:play-services-cast-framework:21.4.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/java/com/example/androidenhancedvideoplayer/MainActivity.kt b/app/src/main/java/com/example/androidenhancedvideoplayer/MainActivity.kt index 8234b5e1..a43ec0ce 100644 --- a/app/src/main/java/com/example/androidenhancedvideoplayer/MainActivity.kt +++ b/app/src/main/java/com/example/androidenhancedvideoplayer/MainActivity.kt @@ -1,8 +1,8 @@ package com.example.androidenhancedvideoplayer import android.os.Bundle -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.annotation.OptIn import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -11,7 +11,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.platform.LocalConfiguration import androidx.core.view.WindowCompat +import androidx.fragment.app.FragmentActivity import androidx.media3.common.MediaItem +import androidx.media3.common.MimeTypes +import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.trackselection.AdaptiveTrackSelection import androidx.media3.exoplayer.trackselection.DefaultTrackSelector @@ -21,15 +24,16 @@ import com.example.androidenhancedvideoplayer.ui.theme.AndroidEnhancedVideoPlaye import com.example.androidenhancedvideoplayer.utils.ExampleUrl import com.example.androidenhancedvideoplayer.utils.fillMaxSizeInLandscape import com.example.androidenhancedvideoplayer.utils.safePaddingInPortrait +import com.google.android.gms.cast.framework.CastContext import com.profusion.androidenhancedvideoplayer.components.EnhancedVideoPlayer import com.profusion.androidenhancedvideoplayer.components.playerOverlay.SettingsControlsCustomization import kotlinx.coroutines.flow.MutableStateFlow -class MainActivity : ComponentActivity() { +class MainActivity : FragmentActivity() { private lateinit var exoPlayer: ExoPlayer private val isInPictureInPictureMode: MutableStateFlow = MutableStateFlow(false) - @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) + @OptIn(androidx.media3.common.util.UnstableApi::class) private fun initializeExoPlayer() { exoPlayer = ExoPlayer .Builder(applicationContext) @@ -38,7 +42,7 @@ class MainActivity : ComponentActivity() { ) .build() .apply { - setMediaItem(MediaItem.fromUri(ExampleUrl.HLS_MULTI_AUDIO)) + setMediaItem(MediaItem.Builder().setUri(ExampleUrl.DASH).setMimeType(MimeTypes.APPLICATION_MPD).build()) playWhenReady = true addAnalyticsListener(EventLogger()) prepare() @@ -54,6 +58,7 @@ class MainActivity : ComponentActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) initializeExoPlayer() + CastContext.getSharedInstance(this) setContent { AndroidEnhancedVideoPlayerTheme {