@@ -111,7 +111,7 @@ class DarkThemeColors : AppThemeData(
|
||||
chatActionColor = Color(0xFF3D3D3D),
|
||||
brandColorsColor = Color(0xffD80264),
|
||||
tabSelectedBackground = Color(0xffffffff),
|
||||
tabUnselectedBackground = Color(0x2E7C7480),
|
||||
tabUnselectedBackground = Color(0xFF1C1C1C),
|
||||
tabSelectedText = Color(0xff000000),
|
||||
tabUnselectedText = Color(0xffffffff),
|
||||
bubbleBackground = Color(0xff2d2c2e),
|
||||
|
||||
@@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.ime
|
||||
@@ -157,6 +159,7 @@ fun CommentModalContent(
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -196,12 +199,12 @@ fun CommentModalContent(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.weight(1f)
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
item {
|
||||
CommentContent(
|
||||
|
||||
@@ -64,6 +64,7 @@ fun AnimatedFavouriteIcon(
|
||||
modifier = modifier.graphicsLayer {
|
||||
rotationZ = animatableRotation.value
|
||||
},
|
||||
colorFilter = if (!isFavourite) ColorFilter.tint(AppColors.text) else null
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -468,6 +468,7 @@ fun MomentOperateBtn(@DrawableRes icon: Int, count: String) {
|
||||
.size(width = 24.dp, height = 24.dp),
|
||||
painter = painterResource(id = icon),
|
||||
contentDescription = "",
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
if (count.isNotEmpty()) {
|
||||
Text(
|
||||
|
||||
@@ -269,7 +269,7 @@ fun IndexScreen() {
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> Agent()
|
||||
1 -> Home()
|
||||
1 -> Home(isPageVisible = pagerState.currentPage == 1)
|
||||
2 -> Add()
|
||||
3 -> Notifications()
|
||||
4 -> Profile()
|
||||
@@ -332,7 +332,9 @@ fun IndexScreen() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Home() {
|
||||
fun Home(
|
||||
isPageVisible: Boolean = true
|
||||
) {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -348,7 +350,7 @@ fun Home() {
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
MomentsList()
|
||||
MomentsList(isPageVisible = isPageVisible)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,9 @@ import com.aiosman.ravenow.ui.index.tabs.moment.tabs.shorts.ShortVideoScreen
|
||||
*/
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun MomentsList() {
|
||||
fun MomentsList(
|
||||
isPageVisible: Boolean = true
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val navController = LocalNavController.current
|
||||
val navigationBarPaddings =
|
||||
@@ -437,7 +439,7 @@ fun MomentsList() {
|
||||
}
|
||||
1 -> {
|
||||
// 短视频页面
|
||||
ShortVideoScreen()
|
||||
ShortVideoScreen(isPageVisible = pagerState.currentPage == 1 && isPageVisible)
|
||||
}
|
||||
2 -> {
|
||||
// 动态页面 - 暂时显示时间线内容
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -21,6 +22,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.composables.MomentCard
|
||||
@@ -36,6 +38,7 @@ fun Dynamic() {
|
||||
val model = DynamicViewModel
|
||||
val moments = model.moments
|
||||
val navController = LocalNavController.current
|
||||
val AppColors = LocalAppTheme.current
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
|
||||
@@ -88,7 +91,7 @@ fun Dynamic() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
||||
.background(AppColors.background)
|
||||
) {
|
||||
Box(Modifier.pullRefresh(state)) {
|
||||
LazyColumn(
|
||||
|
||||
@@ -31,7 +31,9 @@ import kotlinx.coroutines.launch
|
||||
* 短视频页面
|
||||
*/
|
||||
@Composable
|
||||
fun ShortVideoScreen() {
|
||||
fun ShortVideoScreen(
|
||||
isPageVisible: Boolean = true
|
||||
) {
|
||||
val viewModel = ShortVideoViewModel
|
||||
val allMoments = viewModel.moments
|
||||
val navController = LocalNavController.current
|
||||
@@ -187,7 +189,8 @@ fun ShortVideoScreen() {
|
||||
)
|
||||
)
|
||||
},
|
||||
onPageChanged = { idx -> currentIndex.value = idx }
|
||||
onPageChanged = { idx -> currentIndex.value = idx },
|
||||
isPageVisible = isPageVisible
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ fun TimelineMomentsList() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(AppColors.background)
|
||||
.padding(top = 188.dp),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
@@ -126,6 +127,7 @@ fun TimelineMomentsList() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(AppColors.background)
|
||||
.padding(top = 188.dp),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
@@ -176,6 +178,7 @@ fun TimelineMomentsList() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(AppColors.background)
|
||||
) {
|
||||
Box(Modifier.pullRefresh(state)) {
|
||||
LazyColumn(
|
||||
|
||||
@@ -76,12 +76,129 @@ import com.aiosman.ravenow.entity.MomentEntity
|
||||
import com.aiosman.ravenow.ui.comment.CommentModalContent
|
||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
// 激活状态的颜色(点赞/收藏时的红色)
|
||||
private val ActiveIconColor = Color(0xFFD80264)
|
||||
|
||||
/**
|
||||
* 创建视频播放器视图
|
||||
*/
|
||||
private fun createVideoPlayerView(
|
||||
context: android.content.Context,
|
||||
exoPlayer: ExoPlayer,
|
||||
onViewCreated: (PlayerView) -> Unit
|
||||
): FrameLayout {
|
||||
return FrameLayout(context).apply {
|
||||
setBackgroundColor(Color.Black.toArgb())
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
|
||||
val playerView = PlayerView(context).apply {
|
||||
hideController()
|
||||
useController = false
|
||||
player = exoPlayer
|
||||
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||
setShutterBackgroundColor(Color.Black.toArgb())
|
||||
setKeepContentOnPlayerReset(false)
|
||||
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
Gravity.CENTER
|
||||
)
|
||||
|
||||
// 布局完成后固定内容帧尺寸 - 使用更可靠的方法
|
||||
var layoutListener: android.view.ViewTreeObserver.OnGlobalLayoutListener? = null
|
||||
layoutListener = object : android.view.ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
try {
|
||||
val frame = findViewById<AspectRatioFrameLayout>(androidx.media3.ui.R.id.exo_content_frame)
|
||||
if (frame != null && width > 0 && height > 0) {
|
||||
// 确保内容帧的尺寸和缩放模式正确设置
|
||||
val layoutParams = FrameLayout.LayoutParams(
|
||||
width,
|
||||
height,
|
||||
Gravity.CENTER
|
||||
)
|
||||
frame.layoutParams = layoutParams
|
||||
frame.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM)
|
||||
// 强制重新布局
|
||||
frame.requestLayout()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 忽略异常
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 post 确保在布局完成后执行
|
||||
post {
|
||||
viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
|
||||
// 延迟移除监听器,确保多次布局变化都能处理
|
||||
postDelayed({
|
||||
try {
|
||||
layoutListener?.let { viewTreeObserver.removeOnGlobalLayoutListener(it) }
|
||||
} catch (e: Exception) {
|
||||
// 忽略异常
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加视频准备监听,确保视频准备就绪时也设置正确的缩放
|
||||
exoPlayer.addListener(object : Player.Listener {
|
||||
override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
|
||||
playerView.post {
|
||||
try {
|
||||
val frame = playerView.findViewById<AspectRatioFrameLayout>(androidx.media3.ui.R.id.exo_content_frame)
|
||||
if (frame != null && playerView.width > 0 && playerView.height > 0) {
|
||||
frame.layoutParams = FrameLayout.LayoutParams(
|
||||
playerView.width,
|
||||
playerView.height,
|
||||
Gravity.CENTER
|
||||
)
|
||||
frame.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM)
|
||||
frame.requestLayout()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 忽略异常
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onViewCreated(playerView)
|
||||
addView(playerView)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理视频点击事件(播放/暂停)
|
||||
*/
|
||||
private fun handleVideoClick(
|
||||
pauseIconVisibleState: MutableState<Boolean>,
|
||||
exoPlayer: ExoPlayer,
|
||||
scope: CoroutineScope
|
||||
) {
|
||||
scope.launch {
|
||||
if (exoPlayer.isPlaying) {
|
||||
// 正在播放:暂停
|
||||
exoPlayer.pause()
|
||||
pauseIconVisibleState.value = true
|
||||
} else {
|
||||
// 已暂停:恢复播放
|
||||
exoPlayer.playWhenReady = true
|
||||
exoPlayer.play()
|
||||
pauseIconVisibleState.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShortViewCompose(
|
||||
videoItemsUrl: List<String> = emptyList(),
|
||||
@@ -95,7 +212,8 @@ fun ShortViewCompose(
|
||||
onFavoriteClick: ((MomentEntity) -> Unit)? = null,
|
||||
onShareClick: ((MomentEntity) -> Unit)? = null,
|
||||
onAvatarClick: ((MomentEntity) -> Unit)? = null,
|
||||
onPageChanged: ((Int) -> Unit)? = null
|
||||
onPageChanged: ((Int) -> Unit)? = null,
|
||||
isPageVisible: Boolean = true
|
||||
) {
|
||||
// 优先使用 videoMoments,如果没有则使用 videoItemsUrl
|
||||
val items = if (videoMoments.isNotEmpty()) {
|
||||
@@ -144,8 +262,10 @@ fun ShortViewCompose(
|
||||
.clip(RectangleShape),
|
||||
state = pagerState,
|
||||
orientation = Orientation.Vertical,
|
||||
offscreenLimit = 1
|
||||
offscreenLimit = 1 // 只预加载相邻页面,减少内存占用
|
||||
) {
|
||||
// 使用 key 确保每个页面独立,避免状态混乱
|
||||
androidx.compose.runtime.key(page) {
|
||||
pauseIconVisibleState.value = false
|
||||
val currentMoment = if (videoMoments.isNotEmpty() && page < videoMoments.size) {
|
||||
videoMoments[page]
|
||||
@@ -157,6 +277,7 @@ fun ShortViewCompose(
|
||||
LaunchedEffect(pagerState.currentPage) {
|
||||
onPageChanged?.invoke(pagerState.currentPage)
|
||||
}
|
||||
|
||||
SingleVideoItemContent(
|
||||
videoUrl = items[page],
|
||||
moment = currentMoment,
|
||||
@@ -171,9 +292,11 @@ fun ShortViewCompose(
|
||||
onCommentAdded = onCommentAdded,
|
||||
onFavoriteClick = onFavoriteClick,
|
||||
onShareClick = onShareClick,
|
||||
onAvatarClick = onAvatarClick
|
||||
onAvatarClick = onAvatarClick,
|
||||
isPageVisible = isPageVisible
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(clickItemPosition) {
|
||||
delay(300)
|
||||
@@ -197,7 +320,8 @@ private fun SingleVideoItemContent(
|
||||
onCommentAdded: ((MomentEntity) -> Unit)? = null,
|
||||
onFavoriteClick: ((MomentEntity) -> Unit)? = null,
|
||||
onShareClick: ((MomentEntity) -> Unit)? = null,
|
||||
onAvatarClick: ((MomentEntity) -> Unit)? = null
|
||||
onAvatarClick: ((MomentEntity) -> Unit)? = null,
|
||||
isPageVisible: Boolean = true
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -215,7 +339,8 @@ private fun SingleVideoItemContent(
|
||||
onCommentAdded = onCommentAdded,
|
||||
onFavoriteClick = onFavoriteClick,
|
||||
onShareClick = onShareClick,
|
||||
onAvatarClick = onAvatarClick
|
||||
onAvatarClick = onAvatarClick,
|
||||
isPageVisible = isPageVisible
|
||||
)
|
||||
VideoHeader.invoke()
|
||||
if (moment != null && VideoBottom != null) {
|
||||
@@ -247,22 +372,31 @@ fun VideoPlayer(
|
||||
onFavoriteClick: ((MomentEntity) -> Unit)? = null,
|
||||
onShareClick: ((MomentEntity) -> Unit)? = null,
|
||||
onAvatarClick: ((MomentEntity) -> Unit)? = null,
|
||||
isPageVisible: Boolean = true
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val configuration = androidx.compose.ui.platform.LocalConfiguration.current
|
||||
val screenHeight = configuration.screenHeightDp.dp
|
||||
val sheetHeight = screenHeight * 0.7f // 屏幕的一大半高度
|
||||
val sheetHeight = screenHeight * 0.7f // 屏幕的70%高度
|
||||
var showCommentModal by remember { mutableStateOf(false) }
|
||||
var sheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = true
|
||||
)
|
||||
val exoPlayer = remember {
|
||||
ExoPlayer.Builder(context)
|
||||
.build()
|
||||
.apply {
|
||||
// 创建带有认证头的 HttpDataSource.Factory
|
||||
|
||||
// 确保弹窗从下往上展开
|
||||
LaunchedEffect(showCommentModal) {
|
||||
if (showCommentModal) {
|
||||
sheetState.expand()
|
||||
} else {
|
||||
sheetState.hide()
|
||||
}
|
||||
}
|
||||
// 使用 key 确保每个视频的 ExoPlayer 完全独立,避免状态残留
|
||||
val exoPlayer = remember(videoUrl) {
|
||||
ExoPlayer.Builder(context).build().apply {
|
||||
// 创建带有认证头的数据源
|
||||
val httpDataSourceFactory = DefaultHttpDataSource.Factory()
|
||||
.setUserAgent(Util.getUserAgent(context, context.packageName))
|
||||
.setDefaultRequestProperties(
|
||||
@@ -272,70 +406,55 @@ fun VideoPlayer(
|
||||
)
|
||||
)
|
||||
|
||||
// 创建 DataSource.Factory,使用自定义的 HttpDataSource.Factory
|
||||
val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(
|
||||
context,
|
||||
httpDataSourceFactory
|
||||
)
|
||||
val dataSourceFactory = DefaultDataSourceFactory(context, httpDataSourceFactory)
|
||||
val source = ProgressiveMediaSource.Factory(dataSourceFactory)
|
||||
.createMediaSource(MediaItem.fromUri(Uri.parse(videoUrl)))
|
||||
|
||||
this.prepare(source)
|
||||
prepare(source)
|
||||
videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
|
||||
repeatMode = Player.REPEAT_MODE_ONE
|
||||
}
|
||||
}
|
||||
if (pager == pagerState.currentPage) {
|
||||
|
||||
// 根据页面状态控制播放
|
||||
LaunchedEffect(pager, pagerState.currentPage, isPageVisible) {
|
||||
val isCurrentPage = pager == pagerState.currentPage && isPageVisible
|
||||
if (isCurrentPage) {
|
||||
// 当前页面且页面可见:恢复播放
|
||||
exoPlayer.playWhenReady = true
|
||||
exoPlayer.play()
|
||||
pauseIconVisibleState.value = false
|
||||
} else {
|
||||
// 非当前页面或页面不可见:立即暂停并停止准备播放
|
||||
exoPlayer.playWhenReady = false
|
||||
exoPlayer.pause()
|
||||
pauseIconVisibleState.value = false
|
||||
}
|
||||
exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT
|
||||
exoPlayer.repeatMode = Player.REPEAT_MODE_ONE
|
||||
// player box
|
||||
}
|
||||
// 视频播放器容器 - 确保每个视频页面都被正确裁剪
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
.fillMaxSize()
|
||||
.clip(RectangleShape)
|
||||
) {
|
||||
var playerView by remember { mutableStateOf<PlayerView?>(null) } // Store reference to PlayerView
|
||||
var playerView by remember { mutableStateOf<PlayerView?>(null) }
|
||||
|
||||
// 使用 key 强制每个视频的 PlayerView 完全独立,避免布局状态残留
|
||||
androidx.compose.runtime.key(videoUrl) {
|
||||
AndroidView(
|
||||
factory = { context ->
|
||||
// 创建一个 FrameLayout 作为容器
|
||||
FrameLayout(context).apply {
|
||||
// 设置背景颜色为黑色,用于显示黑边
|
||||
setBackgroundColor(Color.Black.toArgb())
|
||||
|
||||
// 创建 PlayerView 并添加到 FrameLayout 中
|
||||
val view = PlayerView(context).apply {
|
||||
hideController()
|
||||
useController = false
|
||||
player = exoPlayer
|
||||
resizeMode =
|
||||
AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT // 或 RESIZE_MODE_ZOOM
|
||||
layoutParams = FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
Gravity.CENTER
|
||||
)
|
||||
}
|
||||
addView(view)
|
||||
factory = { ctx ->
|
||||
createVideoPlayerView(ctx, exoPlayer) { view ->
|
||||
playerView = view
|
||||
}
|
||||
},
|
||||
modifier = Modifier.noRippleClickable {
|
||||
pauseIconVisibleState.value = true
|
||||
exoPlayer.pause()
|
||||
scope.launch {
|
||||
delay(100)
|
||||
if (exoPlayer.isPlaying) {
|
||||
exoPlayer.pause()
|
||||
} else {
|
||||
pauseIconVisibleState.value = false
|
||||
exoPlayer.play()
|
||||
}
|
||||
}
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(RectangleShape)
|
||||
.noRippleClickable {
|
||||
handleVideoClick(pauseIconVisibleState, exoPlayer, scope)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (pauseIconVisibleState.value) {
|
||||
Icon(
|
||||
@@ -348,11 +467,16 @@ fun VideoPlayer(
|
||||
}
|
||||
|
||||
// Release ExoPlayer when the videoUrl changes or the composable leaves composition
|
||||
// 使用 key 后,DisposableEffect 会在 videoUrl 变化时自动触发
|
||||
DisposableEffect(videoUrl) {
|
||||
onDispose {
|
||||
try {
|
||||
exoPlayer.release()
|
||||
playerView?.player = null
|
||||
playerView = null // Release the reference to the PlayerView
|
||||
} catch (e: Exception) {
|
||||
// 忽略释放时的异常
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,12 +484,16 @@ fun VideoPlayer(
|
||||
val observer = LifecycleEventObserver { _, event ->
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_PAUSE -> {
|
||||
exoPlayer.pause() // 应用进入后台时暂停
|
||||
// 应用进入后台时暂停
|
||||
exoPlayer.playWhenReady = false
|
||||
exoPlayer.pause()
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
// 返回前台且为当前页面时恢复播放
|
||||
if (pager == pagerState.currentPage) {
|
||||
exoPlayer.play() // 返回前台且为当前页面时恢复播放
|
||||
exoPlayer.playWhenReady = true
|
||||
exoPlayer.play()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,9 +624,17 @@ fun VideoPlayer(
|
||||
if (showCommentModal && moment != null) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { showCommentModal = false },
|
||||
onDismissRequest = {
|
||||
scope.launch {
|
||||
sheetState.hide()
|
||||
}
|
||||
showCommentModal = false
|
||||
},
|
||||
containerColor = AppColors.background,
|
||||
sheetState = sheetState,
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(sheetHeight)
|
||||
@@ -512,6 +648,7 @@ fun VideoPlayer(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -74,7 +74,7 @@ fun PointsBottomSheet(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(0.98f)
|
||||
.fillMaxHeight(0.9f)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
) {
|
||||
// 头部
|
||||
@@ -162,18 +162,21 @@ fun PointsBottomSheet(
|
||||
TabItem(
|
||||
text = stringResource(R.string.transaction_history),
|
||||
isSelected = tab == 0,
|
||||
onClick = { tab = 0 }
|
||||
onClick = { tab = 0 },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
TabSpacer()
|
||||
TabItem(
|
||||
text = stringResource(R.string.how_to_earn),
|
||||
isSelected = tab == 1,
|
||||
onClick = { tab = 1 }
|
||||
onClick = { tab = 1 },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// 列表区域
|
||||
if (tab == 0) {
|
||||
PointsHistoryList(
|
||||
items = PointsViewModel.logs,
|
||||
@@ -183,8 +186,6 @@ fun PointsBottomSheet(
|
||||
} else {
|
||||
HowToEarnList()
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,7 +211,10 @@ private fun PointsHistoryList(
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val numberFormat = remember { NumberFormat.getNumberInstance(Locale.getDefault()) }
|
||||
LazyColumn {
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
items(items) { item ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -260,6 +264,7 @@ private fun PointsHistoryList(
|
||||
@Composable
|
||||
private fun HowToEarnList() {
|
||||
val AppColors = LocalAppTheme.current
|
||||
|
||||
@Composable
|
||||
fun RowItem(title: String, desc: String, amount: String) {
|
||||
Row(
|
||||
@@ -288,13 +293,23 @@ private fun HowToEarnList() {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
item {
|
||||
RowItem(stringResource(R.string.new_user_reward), stringResource(R.string.new_user_reward_desc), "+500")
|
||||
}
|
||||
item {
|
||||
RowItem(stringResource(R.string.daily_check_in), stringResource(R.string.daily_check_in_desc), "+10-50")
|
||||
}
|
||||
item {
|
||||
RowItem(stringResource(R.string.invite_friends), stringResource(R.string.invite_friends_desc), "+100")
|
||||
}
|
||||
item {
|
||||
RowItem(stringResource(R.string.complete_tasks), stringResource(R.string.complete_tasks_desc), "+20-200")
|
||||
}
|
||||
item {
|
||||
RowItem(stringResource(R.string.recharge_pai_coin), stringResource(R.string.recharge_pai_coin_desc), ">")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user