修改若干bug调整暗色模式适配以及界面尺寸修改

修复进入应用后的深色模式下的底部导航框在不同标签下会出现透明

动态-动态/关注界面暗色模式适配

修复上下滑动切换短视频,部分视频会被缩放

修复点击底部导航栏的标签切换界面后视频还在播放

修改短视频评论框大小以及我的派币界面大小
This commit is contained in:
2025-11-12 18:24:42 +08:00
parent 88968c7437
commit bc647119df
11 changed files with 310 additions and 140 deletions

View File

@@ -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),

View File

@@ -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(

View File

@@ -64,6 +64,7 @@ fun AnimatedFavouriteIcon(
modifier = modifier.graphicsLayer {
rotationZ = animatableRotation.value
},
colorFilter = if (!isFavourite) ColorFilter.tint(AppColors.text) else null
)
}
}

View File

@@ -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(

View File

@@ -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)
}
}

View File

@@ -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 -> {
// 动态页面 - 暂时显示时间线内容

View File

@@ -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(

View File

@@ -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
)
}
}

View File

@@ -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(

View File

@@ -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,35 +262,40 @@ fun ShortViewCompose(
.clip(RectangleShape),
state = pagerState,
orientation = Orientation.Vertical,
offscreenLimit = 1
offscreenLimit = 1 // 只预加载相邻页面,减少内存占用
) {
pauseIconVisibleState.value = false
val currentMoment = if (videoMoments.isNotEmpty() && page < videoMoments.size) {
videoMoments[page]
} else {
null
}
// 使用 key 确保每个页面独立,避免状态混乱
androidx.compose.runtime.key(page) {
pauseIconVisibleState.value = false
val currentMoment = if (videoMoments.isNotEmpty() && page < videoMoments.size) {
videoMoments[page]
} else {
null
}
// 同步页码到外部(用于返回时恢复进度)
LaunchedEffect(pagerState.currentPage) {
onPageChanged?.invoke(pagerState.currentPage)
// 同步页码到外部(用于返回时恢复进度)
LaunchedEffect(pagerState.currentPage) {
onPageChanged?.invoke(pagerState.currentPage)
}
SingleVideoItemContent(
videoUrl = items[page],
moment = currentMoment,
pagerState = pagerState,
pager = page,
initialLayout = initialLayout,
pauseIconVisibleState = pauseIconVisibleState,
VideoHeader = videoHeader,
VideoBottom = videoBottom,
onLikeClick = onLikeClick,
onCommentClick = onCommentClick,
onCommentAdded = onCommentAdded,
onFavoriteClick = onFavoriteClick,
onShareClick = onShareClick,
onAvatarClick = onAvatarClick,
isPageVisible = isPageVisible
)
}
SingleVideoItemContent(
videoUrl = items[page],
moment = currentMoment,
pagerState = pagerState,
pager = page,
initialLayout = initialLayout,
pauseIconVisibleState = pauseIconVisibleState,
VideoHeader = videoHeader,
VideoBottom = videoBottom,
onLikeClick = onLikeClick,
onCommentClick = onCommentClick,
onCommentAdded = onCommentAdded,
onFavoriteClick = onFavoriteClick,
onShareClick = onShareClick,
onAvatarClick = onAvatarClick
)
}
LaunchedEffect(clickItemPosition) {
@@ -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,95 +372,89 @@ 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
val httpDataSourceFactory = DefaultHttpDataSource.Factory()
.setUserAgent(Util.getUserAgent(context, context.packageName))
.setDefaultRequestProperties(
mapOf(
"Authorization" to "Bearer ${com.aiosman.ravenow.AppStore.token ?: ""}",
"DEVICE-OS" to "Android"
)
// 确保弹窗从下往上展开
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(
mapOf(
"Authorization" to "Bearer ${com.aiosman.ravenow.AppStore.token ?: ""}",
"DEVICE-OS" to "Android"
)
// 创建 DataSource.Factory使用自定义的 HttpDataSource.Factory
val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(
context,
httpDataSourceFactory
)
val source = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(Uri.parse(videoUrl)))
this.prepare(source)
}
val dataSourceFactory = DefaultDataSourceFactory(context, httpDataSourceFactory)
val source = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(Uri.parse(videoUrl)))
prepare(source)
videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
repeatMode = Player.REPEAT_MODE_ONE
}
}
if (pager == pagerState.currentPage) {
exoPlayer.playWhenReady = true
exoPlayer.play()
} else {
exoPlayer.pause()
// 根据页面状态控制播放
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) }
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
)
// 使用 key 强制每个视频的 PlayerView 完全独立,避免布局状态残留
androidx.compose.runtime.key(videoUrl) {
AndroidView(
factory = { ctx ->
createVideoPlayerView(ctx, exoPlayer) { view ->
playerView = view
}
addView(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 {
exoPlayer.release()
playerView?.player = null
playerView = null // Release the reference to the PlayerView
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,20 +624,29 @@ 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,
modifier = Modifier
.fillMaxWidth()
.height(sheetHeight)
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
) {
CommentModalContent(
postId = moment.id,
commentCount = moment.commentCount,
onCommentAdded = {
onCommentAdded?.invoke(moment)
}
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(sheetHeight)
) {
CommentModalContent(
postId = moment.id,
commentCount = moment.commentCount,
onCommentAdded = {
onCommentAdded?.invoke(moment)
}
)
}
}
}
}

View File

@@ -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 {
RowItem(stringResource(R.string.new_user_reward), stringResource(R.string.new_user_reward_desc), "+500")
RowItem(stringResource(R.string.daily_check_in), stringResource(R.string.daily_check_in_desc), "+10-50")
RowItem(stringResource(R.string.invite_friends), stringResource(R.string.invite_friends_desc), "+100")
RowItem(stringResource(R.string.complete_tasks), stringResource(R.string.complete_tasks_desc), "+20-200")
RowItem(stringResource(R.string.recharge_pai_coin), stringResource(R.string.recharge_pai_coin_desc), ">")
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), ">")
}
}
}