From 04797d1e80dc0fd05b68c3f755eb295bed072259 Mon Sep 17 00:00:00 2001 From: "Wei.He" Date: Thu, 14 Dec 2023 14:24:20 +0800 Subject: [PATCH 1/3] feat: Implement Picture-in-Picture Mode in Video Module (fixed #30) --- app/build.gradle.kts | 5 + app/src/main/AndroidManifest.xml | 2 + .../main/java/com/wei/picquest/ui/PqApp.kt | 100 ++++--- .../java/com/wei/picquest/ui/PqAppState.kt | 5 + .../kotlin/com/wei/picquest/KotlinAndroid.kt | 2 +- .../wei/picquest/core/pip/PictureInPicture.kt | 57 ++++ .../core/designsystem/icon/PqIcons.kt | 2 + feature/video/build.gradle.kts | 3 +- .../video/videolibrary/VideoLibraryScreen.kt | 263 +++++++++++++----- .../navigation/VideoLibraryNavigation.kt | 2 + 10 files changed, 338 insertions(+), 103 deletions(-) create mode 100644 core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index caed6b5..021968a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -108,4 +108,9 @@ dependencies { // Timber implementation(libs.timber) + + // ExoPlayer + implementation(libs.media3.exoplayer) + implementation(libs.media3.exoplayer.dash) + implementation(libs.media3.ui) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0a4ddd4..c263779 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,6 +19,8 @@ tools:targetApi="31"> diff --git a/app/src/main/java/com/wei/picquest/ui/PqApp.kt b/app/src/main/java/com/wei/picquest/ui/PqApp.kt index 23bd43f..927dfe3 100644 --- a/app/src/main/java/com/wei/picquest/ui/PqApp.kt +++ b/app/src/main/java/com/wei/picquest/ui/PqApp.kt @@ -119,24 +119,30 @@ fun PqApp( SnackbarHost( hostState = snackbarHostState, snackbar = { snackbarData -> - if (!appState.isFullScreenCurrentDestination) { - val isError = snackbarData.visuals.message.startsWith(ErrorTextPrefix) - PqAppSnackbar(snackbarData, isError) - } + ConditionalContent( + condition = !appState.isInPictureInPicture && !appState.isFullScreenCurrentDestination, + content = { + val isError = snackbarData.visuals.message.startsWith(ErrorTextPrefix) + PqAppSnackbar(snackbarData, isError) + }, + ) }, ) }, bottomBar = { - if (!appState.isFullScreenCurrentDestination && - appState.navigationType == PqNavigationType.BOTTOM_NAVIGATION - ) { - PqBottomBar( - destinations = appState.topLevelDestinations, - onNavigateToDestination = appState::navigateToTopLevelDestination, - currentDestination = appState.currentDestination, - modifier = Modifier.testTag(pqBottomBar), - ) - } + ConditionalContent( + condition = !appState.isInPictureInPicture && + !appState.isFullScreenCurrentDestination && + appState.navigationType == PqNavigationType.BOTTOM_NAVIGATION, + content = { + PqBottomBar( + destinations = appState.topLevelDestinations, + onNavigateToDestination = appState::navigateToTopLevelDestination, + currentDestination = appState.currentDestination, + modifier = Modifier.testTag(pqBottomBar), + ) + }, + ) }, ) { padding -> Row( @@ -150,32 +156,38 @@ fun PqApp( ), ), ) { - if (!appState.isFullScreenCurrentDestination && - appState.navigationType == PqNavigationType.PERMANENT_NAVIGATION_DRAWER - ) { - PqNavDrawer( - destinations = appState.topLevelDestinations, - onNavigateToDestination = appState::navigateToTopLevelDestination, - currentDestination = appState.currentDestination, - modifier = Modifier - .testTag(pqNavDrawer) - .padding(SPACING_LARGE.dp) - .safeDrawingPadding(), - ) - } + ConditionalContent( + condition = !appState.isInPictureInPicture && + !appState.isFullScreenCurrentDestination && + appState.navigationType == PqNavigationType.PERMANENT_NAVIGATION_DRAWER, + content = { + PqNavDrawer( + destinations = appState.topLevelDestinations, + onNavigateToDestination = appState::navigateToTopLevelDestination, + currentDestination = appState.currentDestination, + modifier = Modifier + .testTag(pqNavDrawer) + .padding(SPACING_LARGE.dp) + .safeDrawingPadding(), + ) + }, + ) - if (!appState.isFullScreenCurrentDestination && - appState.navigationType == PqNavigationType.NAVIGATION_RAIL - ) { - PqNavRail( - destinations = appState.topLevelDestinations, - onNavigateToDestination = appState::navigateToTopLevelDestination, - currentDestination = appState.currentDestination, - modifier = Modifier - .testTag(pqNavRail) - .safeDrawingPadding(), - ) - } + ConditionalContent( + condition = !appState.isInPictureInPicture && + !appState.isFullScreenCurrentDestination && + appState.navigationType == PqNavigationType.NAVIGATION_RAIL, + content = { + PqNavRail( + destinations = appState.topLevelDestinations, + onNavigateToDestination = appState::navigateToTopLevelDestination, + currentDestination = appState.currentDestination, + modifier = Modifier + .testTag(pqNavRail) + .safeDrawingPadding(), + ) + }, + ) Column( modifier = Modifier @@ -258,6 +270,16 @@ private fun PqNavRail( } } +@Composable +fun ConditionalContent( + condition: Boolean, + content: @Composable () -> Unit, +) { + if (condition) { + content() + } +} + @Composable private fun PqBottomBar( destinations: List, diff --git a/app/src/main/java/com/wei/picquest/ui/PqAppState.kt b/app/src/main/java/com/wei/picquest/ui/PqAppState.kt index 42d64b6..02d272b 100644 --- a/app/src/main/java/com/wei/picquest/ui/PqAppState.kt +++ b/app/src/main/java/com/wei/picquest/ui/PqAppState.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.platform.LocalContext import androidx.navigation.NavDestination import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController @@ -25,6 +26,7 @@ import com.wei.picquest.core.designsystem.ui.PqNavigationType import com.wei.picquest.core.designsystem.ui.currentDeviceOrientation import com.wei.picquest.core.designsystem.ui.isBookPosture import com.wei.picquest.core.designsystem.ui.isSeparating +import com.wei.picquest.core.pip.isInPictureInPictureMode import com.wei.picquest.feature.contactme.contactme.navigation.contactMeRoute import com.wei.picquest.feature.contactme.contactme.navigation.navigateToContactMe import com.wei.picquest.feature.home.home.navigation.homeRoute @@ -152,6 +154,9 @@ class PqAppState( else -> false } + val isInPictureInPicture: Boolean + @Composable get() = LocalContext.current.isInPictureInPictureMode + val currentTopLevelDestination: TopLevelDestination? @Composable get() = when (currentDestination?.route) { homeRoute -> TopLevelDestination.HOME diff --git a/build-logic/convention/src/main/kotlin/com/wei/picquest/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/com/wei/picquest/KotlinAndroid.kt index ef0a2f1..6b5f144 100644 --- a/build-logic/convention/src/main/kotlin/com/wei/picquest/KotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/com/wei/picquest/KotlinAndroid.kt @@ -20,7 +20,7 @@ internal fun Project.configureKotlinAndroid( compileSdk = 34 defaultConfig { - minSdk = 21 + minSdk = 26 } compileOptions { diff --git a/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt b/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt new file mode 100644 index 0000000..9262609 --- /dev/null +++ b/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt @@ -0,0 +1,57 @@ +package com.wei.picquest.core.pip + +import android.app.Activity +import android.app.PictureInPictureParams +import android.content.Context +import android.content.ContextWrapper +import android.content.pm.PackageManager +import android.graphics.Rect +import android.os.Build +import android.util.Rational + +fun updatedPipParams( + context: Context, + rect: Rect, +) { + if (!context.isPictureInPictureSupported) return + + val aspect = Rational(rect.width(), rect.height()) + val paramsBuilder = PictureInPictureParams.Builder() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + paramsBuilder.setAspectRatio(aspect) + paramsBuilder.setSourceRectHint(rect) + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + paramsBuilder.setSeamlessResizeEnabled(true) + } + context.findActivity().setPictureInPictureParams(paramsBuilder.build()) +} + +@Suppress("DEPRECATION") +fun enterPictureInPicture( + context: Context, +) { + context.findActivity().enterPictureInPictureMode() +} + +val Context.isPictureInPictureSupported: Boolean + get() { + return packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + } + +val Context.isInPictureInPictureMode: Boolean + get() { + val currentActivity = findActivity() + return currentActivity.isInPictureInPictureMode + } + +internal fun Context.findActivity(): Activity { + var context = this + while (context is ContextWrapper) { + if (context is Activity) return context + context = context.baseContext + } + throw IllegalStateException("Activity not found. Unknown error.") +} diff --git a/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/icon/PqIcons.kt b/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/icon/PqIcons.kt index de09ede..6872bfb 100644 --- a/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/icon/PqIcons.kt +++ b/core/designsystem/src/main/java/com/wei/picquest/core/designsystem/icon/PqIcons.kt @@ -21,6 +21,7 @@ import androidx.compose.material.icons.rounded.Menu import androidx.compose.material.icons.rounded.Person import androidx.compose.material.icons.rounded.Phone import androidx.compose.material.icons.rounded.PhotoLibrary +import androidx.compose.material.icons.rounded.PictureInPicture import androidx.compose.material.icons.rounded.Search import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Star @@ -59,4 +60,5 @@ object PqIcons { val Add = Icons.Rounded.Add val ListView = Icons.Rounded.List val GridView = Icons.Rounded.GridView + val PictureInPicture = Icons.Rounded.PictureInPicture } diff --git a/feature/video/build.gradle.kts b/feature/video/build.gradle.kts index 0bb8a1f..371cd44 100644 --- a/feature/video/build.gradle.kts +++ b/feature/video/build.gradle.kts @@ -9,7 +9,8 @@ android { } dependencies { - // Navigation + + // ExoPlayer implementation(libs.media3.exoplayer) implementation(libs.media3.exoplayer.dash) implementation(libs.media3.ui) diff --git a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt index 47ee8ee..11cb0a5 100644 --- a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt +++ b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/VideoLibraryScreen.kt @@ -1,6 +1,7 @@ package com.wei.picquest.feature.video.videolibrary -import android.net.Uri +import android.content.Context +import android.graphics.Rect import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -15,6 +16,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.windowInsetsTopHeight +import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.VerticalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape @@ -27,11 +29,14 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.boundsInWindow +import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription @@ -57,10 +62,12 @@ import androidx.paging.compose.collectAsLazyPagingItems import coil.compose.AsyncImage import coil.request.ImageRequest import com.wei.picquest.core.data.model.VideoDetail -import com.wei.picquest.core.designsystem.component.FunctionalityNotAvailablePopup import com.wei.picquest.core.designsystem.icon.PqIcons import com.wei.picquest.core.designsystem.theme.SPACING_MEDIUM import com.wei.picquest.core.designsystem.theme.SPACING_SMALL +import com.wei.picquest.core.pip.enterPictureInPicture +import com.wei.picquest.core.pip.isInPictureInPictureMode +import com.wei.picquest.core.pip.updatedPipParams import com.wei.picquest.feature.video.R /** @@ -92,69 +99,77 @@ import com.wei.picquest.feature.video.R * * */ +@ExperimentalFoundationApi @Composable internal fun VideoLibraryRoute( navController: NavController, viewModel: VideoLibraryViewModel = hiltViewModel(), ) { val lazyPagingItems = viewModel.videosState.collectAsLazyPagingItems() + val isInPiPMode = LocalContext.current.isInPictureInPictureMode - Surface(modifier = Modifier.fillMaxSize()) { - Box { - VideoLibraryScreen(lazyPagingItems) + val pagerState = rememberPagerState( + initialPage = 0, + initialPageOffsetFraction = 0f, + pageCount = { lazyPagingItems.itemCount }, + ) - TopBarActions( - onBackClick = navController::popBackStack, - ) - } - } + VideoLibraryScreen( + lazyPagingItems = lazyPagingItems, + pagerState = pagerState, + isInPiPMode = isInPiPMode, + onBackClick = navController::popBackStack, + ) } @OptIn(ExperimentalFoundationApi::class) @Composable internal fun VideoLibraryScreen( lazyPagingItems: LazyPagingItems, + pagerState: PagerState, + isInPiPMode: Boolean = false, + onBackClick: () -> Unit, withTopSpacer: Boolean = true, withBottomSpacer: Boolean = true, ) { - val showPopup = remember { mutableStateOf(false) } + Surface(modifier = Modifier.fillMaxSize()) { + Box { + VideoPager( + lazyPagingItems = lazyPagingItems, + pagerState = pagerState, + ) - if (showPopup.value) { - FunctionalityNotAvailablePopup( - onDismiss = { - showPopup.value = false - }, - ) + if (!isInPiPMode) { + TopBarActions(onBackClick = onBackClick) + } + } } +} - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background, +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun VideoPager( + lazyPagingItems: LazyPagingItems, + pagerState: PagerState, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - val pagerState = rememberPagerState( - initialPage = 0, - initialPageOffsetFraction = 0f, - pageCount = { lazyPagingItems.itemCount }, - ) + VerticalPager( + state = pagerState, + modifier = Modifier.weight(1f), + ) { page -> + val videoDetail = lazyPagingItems[page] - VerticalPager( - state = pagerState, - modifier = Modifier.weight(1f), - ) { page -> - val videoDetail = lazyPagingItems[page] - videoDetail?.let { - VideoPlayer( - uri = it.videos.tiny.url.toUri(), - previewUrl = "https://i.vimeocdn.com/video/${it.pictureId}_100x75.jpg", - ) - } + videoDetail?.let { + VideoPlayer( + videoDetail = videoDetail, + isCurrentPage = pagerState.currentPage == page, + ) } - - PagingStateHandling(lazyPagingItems) } + + PagingStateHandling(lazyPagingItems = lazyPagingItems) } } @@ -192,11 +207,58 @@ fun BackButton( @androidx.annotation.OptIn(UnstableApi::class) @Composable -fun VideoPlayer(uri: Uri, previewUrl: String) { +fun VideoPlayer( + videoDetail: VideoDetail, + isCurrentPage: Boolean, +) { + if (!isCurrentPage) return + val context = LocalContext.current + val isInPiPMode = context.isInPictureInPictureMode val isPlayerReady = remember { mutableStateOf(false) } + val playerViewBounds = remember { mutableStateOf(null) } + + val exoPlayer = rememberExoPlayer( + context = context, + videoDetail = videoDetail, + isPlayerReady = isPlayerReady, + playerViewBounds = playerViewBounds, + ) + + DisposableEffect(videoDetail.id) { + onDispose { exoPlayer.release() } + } + + Box { + PlayerViewContainer( + exoPlayer = exoPlayer, + isPlayerReady = isPlayerReady, + playerViewBounds = playerViewBounds, + ) + + if (!isInPiPMode) { + PiPButtonLayout(context = context) + } + + if (!isPlayerReady.value) { + val previewUrl = "https://i.vimeocdn.com/video/${videoDetail.pictureId}_100x75.jpg" + LoadingView(previewUrl = previewUrl) + } + } +} + +@androidx.annotation.OptIn(UnstableApi::class) +@Composable +fun rememberExoPlayer( + context: Context, + videoDetail: VideoDetail, + isPlayerReady: MutableState, + playerViewBounds: MutableState, +): ExoPlayer { + val uri = videoDetail.videos.tiny.url.toUri() + val mediaItem = MediaItem.Builder().setUri(uri).setMediaId(videoDetail.id.toString()).build() - val exoPlayer = remember(uri) { + return remember(videoDetail.id) { ExoPlayer.Builder(context).build().apply { playWhenReady = true videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT @@ -205,6 +267,9 @@ fun VideoPlayer(uri: Uri, previewUrl: String) { addListener(object : Player.Listener { override fun onPlaybackStateChanged(state: Int) { isPlayerReady.value = (state == Player.STATE_READY) + playerViewBounds.value?.let { bounds -> + calculateAndSetPiPParams(context, bounds, videoDetail) + } } }) @@ -216,30 +281,105 @@ fun VideoPlayer(uri: Uri, previewUrl: String) { prepare() } } +} + +@androidx.annotation.OptIn(UnstableApi::class) +@Composable +fun PlayerViewContainer( + exoPlayer: ExoPlayer, + isPlayerReady: MutableState, + playerViewBounds: MutableState, +) { + AndroidView( + factory = { + PlayerView(it).apply { + useController = false + resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT + player = exoPlayer + alpha = if (isPlayerReady.value) 0f else 1f + } + }, + modifier = Modifier + .fillMaxSize() + .onGloballyPositioned { layoutCoordinates -> + playerViewBounds.value = layoutCoordinates + .boundsInWindow() + .toAndroidGraphicsRect() + }, + ) +} - DisposableEffect(uri) { - onDispose { - exoPlayer.release() +@Composable +fun PiPButtonLayout(context: Context) { + Column { + Spacer(Modifier.windowInsetsTopHeight(WindowInsets.safeDrawing)) + Row(modifier = Modifier.padding(SPACING_MEDIUM.dp)) { + Spacer(modifier = Modifier.weight(1f)) + PiPButton( + onPipClick = { + enterPictureInPicture( + context = context, + ) + }, + ) } } +} - Box { - AndroidView( - factory = { - PlayerView(it).apply { - useController = false - resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT - player = exoPlayer - alpha = if (isPlayerReady.value) 0f else 1f - } - }, - modifier = Modifier.fillMaxSize(), +@Composable +fun PiPButton( + onPipClick: () -> Unit, +) { + IconButton( + onClick = { onPipClick() }, + modifier = Modifier + .clip(CircleShape) + .background(MaterialTheme.colorScheme.surfaceVariant) + .semantics { contentDescription = "PictureInPicture" }, + ) { + Icon( + imageVector = PqIcons.PictureInPicture, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, ) + } +} - if (!isPlayerReady.value) { - LoadingView(previewUrl = previewUrl) - } +fun androidx.compose.ui.geometry.Rect.toAndroidGraphicsRect(): android.graphics.Rect { + return android.graphics.Rect( + left.toInt(), + top.toInt(), + right.toInt(), + bottom.toInt(), + ) +} + +fun calculateAndSetPiPParams(context: Context, viewBounds: Rect, videoDetail: VideoDetail) { + val videoDetailWidth = videoDetail.videos.tiny.width + val videoDetailHeight = videoDetail.videos.tiny.height + val videoAspectRatio = videoDetailWidth.toFloat() / videoDetailHeight.toFloat() + + val finalWidth: Float + val finalHeight: Float + if (videoAspectRatio > viewBounds.width().toFloat() / viewBounds.height().toFloat()) { + finalWidth = viewBounds.width().toFloat() + finalHeight = finalWidth / videoAspectRatio + } else { + finalHeight = viewBounds.height().toFloat() + finalWidth = finalHeight * videoAspectRatio } + + val offsetX = (viewBounds.width() - finalWidth) / 2 + val offsetY = (viewBounds.height() - finalHeight) / 2 + + val rect = Rect( + (viewBounds.left + offsetX).toInt(), + (viewBounds.top + offsetY).toInt(), + (viewBounds.left + offsetX + finalWidth).toInt(), + (viewBounds.top + offsetY + finalHeight).toInt(), + ) + + updatedPipParams(context, rect) } @Composable @@ -325,8 +465,7 @@ private fun LoadingView(previewUrl: String) { .crossfade(true) .build(), contentDescription = "", - modifier = Modifier - .fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) CircularProgressIndicator(modifier = Modifier.size(30.dp)) } diff --git a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/navigation/VideoLibraryNavigation.kt b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/navigation/VideoLibraryNavigation.kt index 119c637..b85f697 100644 --- a/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/navigation/VideoLibraryNavigation.kt +++ b/feature/video/src/main/java/com/wei/picquest/feature/video/videolibrary/navigation/VideoLibraryNavigation.kt @@ -1,6 +1,7 @@ package com.wei.picquest.feature.video.videolibrary.navigation import androidx.annotation.VisibleForTesting +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.lifecycle.SavedStateHandle import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder @@ -37,6 +38,7 @@ fun NavController.navigateToVideoLibrary(videoSearchQuery: String) { } } +@OptIn(ExperimentalFoundationApi::class) fun NavGraphBuilder.videoLibraryGraph( navController: NavController, ) { From e45b063d753eca8a4288046a3c78025bbf6effb9 Mon Sep 17 00:00:00 2001 From: "Wei.He" Date: Thu, 14 Dec 2023 14:36:13 +0800 Subject: [PATCH 2/3] chore: Raise minSdk version to 26 --- .../src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt b/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt index 9262609..e506366 100644 --- a/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt +++ b/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt @@ -38,7 +38,7 @@ fun enterPictureInPicture( val Context.isPictureInPictureSupported: Boolean get() { return packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O } val Context.isInPictureInPictureMode: Boolean From 32cb4ab9b502e483e9ac8d84c1fadeead81118de Mon Sep 17 00:00:00 2001 From: "Wei.He" Date: Thu, 14 Dec 2023 14:58:13 +0800 Subject: [PATCH 3/3] fix: Adjust PiP aspect ratio check to avoid crashes (#32) --- .../java/com/wei/picquest/core/pip/PictureInPicture.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt b/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt index e506366..d0f763a 100644 --- a/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt +++ b/core/common/src/main/java/com/wei/picquest/core/pip/PictureInPicture.kt @@ -18,9 +18,11 @@ fun updatedPipParams( val aspect = Rational(rect.width(), rect.height()) val paramsBuilder = PictureInPictureParams.Builder() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - paramsBuilder.setAspectRatio(aspect) - paramsBuilder.setSourceRectHint(rect) + if (aspect.toFloat() in 0.418410..2.390000) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + paramsBuilder.setAspectRatio(aspect) + paramsBuilder.setSourceRectHint(rect) + } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { paramsBuilder.setSeamlessResizeEnabled(true)