Skip to content

Commit

Permalink
feat: add the focus requester support
Browse files Browse the repository at this point in the history
  • Loading branch information
succlz123 committed Nov 1, 2022
1 parent 472b3e5 commit 45a59e3
Show file tree
Hide file tree
Showing 29 changed files with 1,060 additions and 225 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<br/>
<br/>
<div align="center">
<img src="https://img.shields.io/static/v1?label=version&message=1.0.4&color=success"/>
<img src="https://img.shields.io/static/v1?label=version&message=1.0.5&color=success"/>
<img src="https://img.shields.io/static/v1?label=platform&message=Android&color=green"/> <img src="https://img.shields.io/static/v1?label=platform&message=Desktop&color=blue"/>
</div>
<br/>
Expand Down Expand Up @@ -77,11 +77,10 @@ https://github.com/succlz123/AcFun-Client-Multiplatform/releases
- 首页展示,分区内容展示,视频详情展示,UP主投稿视频查看。
- 视频播放,直播,弹幕 (简易弹幕-实验性质),播放功能增强,变速等。
- 搜索,下载。
- Android Phone, Android Pad,Desktop 适配。
- Android Phone, Android Pad, Android TV, Desktop 适配。

## 开发中功能

- 支持播放选集功能。
- Android TV 适配。
- DLNA 投屏。
- 番剧,文章区。
6 changes: 3 additions & 3 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

group = "io.github.succlz123"
version = "1.0.4"
version = "1.0.5"

repositories {
mavenCentral()
Expand All @@ -27,8 +27,8 @@ android {
applicationId = "org.succlz123.app.acfun"
minSdk = 21
targetSdk = 30
versionCode = 4
versionName = "1.0.4"
versionCode = 5
versionName = "1.0.5"

resourceConfigurations += mutableSetOf("en", "zh")
}
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group = "io.github.succlz123"
version = "1.0.4"
version = "1.0.5"

allprojects {
repositories {
Expand Down
4 changes: 2 additions & 2 deletions desktop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
}

group = "io.github.succlz123"
version = "1.0.4"
version = "1.0.5"

kotlin {
jvm {
Expand Down Expand Up @@ -42,7 +42,7 @@ compose.desktop {
iconFile.set(project.file("ic_acfun.png"))
}
packageName = "AcFun"
packageVersion = "1.0.4"
packageVersion = "1.0.5"
copyright = "Copyright © 2022"

modules("java.sql", "jdk.unsupported")
Expand Down
2 changes: 1 addition & 1 deletion shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = "io.github.succlz123"
version = "1.0.4"
version = "1.0.5"

kotlin {
android()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fun SharedApp() {
Box(modifier = Modifier.fillMaxSize()) {
ScreenHost(screenNavigator = screenNavigator, rootScreenName = Manifest.MainScreen) {
groupScreen(screenName = (Manifest.MainScreen)) {
MainScreen(modifier = Modifier.fillMaxSize())
MainScreen()
}
groupScreen(screenName = (Manifest.VideoDetailScreen)) {
VideoDetailScreen()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package org.succlz123.app.acfun.theme
import androidx.compose.ui.graphics.Color

object ColorResource {
val white = Color(0xFFFFFFFF)
val background = Color(0xFFF6F7F8)
val acRed = Color(0xFFFF4444)
val acRed30 = Color(0x4dFF4444)
val divider = Color(0xFFF1F2F3)
val text = Color(0xFF000000)
val subText = Color(0xFF9499A0)
val border = Color(0xFFF1F2F3)

val black = Color(0xFF000000)
val black20 = Color(0x33000000)
val black30 = Color(0x4d000000)
val black60 = Color(0x99000000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.succlz123.app.acfun.base.AcBackButton
import org.succlz123.app.acfun.base.LoadingFailView
import org.succlz123.app.acfun.base.LoadingView
import org.succlz123.app.acfun.theme.ColorResource
import org.succlz123.app.acfun.ui.main.GlobalFocusViewModel
import org.succlz123.app.acfun.ui.main.tab.item.MainHomeContentItem
import org.succlz123.lib.click.noRippleClickable
import org.succlz123.lib.screen.LocalScreenNavigator
Expand Down Expand Up @@ -47,6 +48,13 @@ fun AreaContentScreen() {
viewModel.getData(id)
}

val focusVm = viewModel(GlobalFocusViewModel::class) {
GlobalFocusViewModel()
}
LaunchedEffect(Unit) {
focusVm.curFocusRequesterParent.value = viewModel.contentFocusParent
}

Box(modifier = Modifier.fillMaxSize().background(Color.White)) {
val state = viewModel.areaVideosState.collectAsState().value
when (state) {
Expand Down Expand Up @@ -109,6 +117,10 @@ fun AreaContentScreen() {
} else {
MainHomeContentItem(result = ScreenResult.Success(acContentList),
isExpandedScreen = isExpandedScreen,

thisRequester = viewModel.contentFocusParent,
otherRequester = null,

onRefresh = {
viewModel.getData(
id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.succlz123.app.acfun.ui.area

import androidx.compose.ui.focus.FocusRequester
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -40,6 +41,8 @@ class AreaContentViewModel : ScreenPageViewModel() {

val rankSelectIndex: MutableStateFlow<Int> = MutableStateFlow(0)

val contentFocusParent = FocusRequester()

init {
page = 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusTarget
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand All @@ -23,11 +27,16 @@ import org.succlz123.app.acfun.base.AcBackButton
import org.succlz123.app.acfun.base.LoadingFailView
import org.succlz123.app.acfun.base.LoadingView
import org.succlz123.app.acfun.theme.ColorResource
import org.succlz123.app.acfun.ui.main.GlobalFocusViewModel
import org.succlz123.lib.click.noRippleClickable
import org.succlz123.lib.common.getPlatformName
import org.succlz123.lib.filedownloader.core.DownloadRequest
import org.succlz123.lib.filedownloader.core.DownloadStateType
import org.succlz123.lib.filedownloader.core.FileDownLoader
import org.succlz123.lib.focus.FocusNode
import org.succlz123.lib.focus.notAllowMove
import org.succlz123.lib.focus.onFocusKeyEventMove
import org.succlz123.lib.focus.onFocusParent
import org.succlz123.lib.image.AsyncImageUrlMultiPlatform
import org.succlz123.lib.screen.LocalScreenNavigator
import org.succlz123.lib.screen.LocalScreenRecord
Expand All @@ -54,6 +63,7 @@ fun VideoDetailScreen() {
LaunchedEffect(Unit) {
viewModel.getDetail(acContent)
}

Box(modifier = Modifier.fillMaxSize().background(Color.White).noRippleClickable {
screenNavigation.cancelPopupWindow()
}) {
Expand All @@ -74,7 +84,9 @@ fun VideoDetailScreen() {
is ScreenResult.Success -> {
val vc = videoContent.invoke()
Box(contentAlignment = Alignment.Center) {
videoDetailContent(acContent, vc, viewModel)
videoDetailContent(
acContent, vc, viewModel, viewModel.userSpaceFocusParent, viewModel.episodeFocusParent
)

val showPlayerLoading = remember { mutableStateOf(false) }
if (showPlayerLoading.value) {
Expand Down Expand Up @@ -113,7 +125,13 @@ fun VideoDetailScreen() {
}

@Composable
fun videoDetailContent(acContent: AcContent, vContent: VideoContent, viewModel: VideoDetailViewModel) {
fun videoDetailContent(
acContent: AcContent,
vContent: VideoContent,
viewModel: VideoDetailViewModel,
userSpaceFocusParent: FocusRequester,
episodeFocusParent: FocusRequester
) {
val screenNavigation = LocalScreenNavigator.current
Column(modifier = Modifier.fillMaxSize().padding(24.dp, 48.dp, 24.dp, 48.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Expand All @@ -131,18 +149,50 @@ fun videoDetailContent(acContent: AcContent, vContent: VideoContent, viewModel:
)
}
Spacer(modifier = Modifier.width(16.dp))
Text(
modifier = Modifier.noRippleClickable {
screenNavigation.push(
Manifest.UserSpaceScreen,
screenKey = vContent.user?.id.orEmpty(),
arguments = ScreenArgs.putValue("KEY_USER_NAME", vContent.user?.name)
.putValue("KEY_USER_ID", vContent.user?.id),
pushOptions = PushOptions(
removePredicate = PushOptions.RemoveAnyPredicate(Manifest.UserSpaceScreen)
)
val isFocused = remember { mutableStateOf(false) }
val userSpaceFocusNode = remember { FocusNode(tag = "UserSpace") }
SideEffect {
if (viewModel.currentFocusNode.value == userSpaceFocusNode) {
userSpaceFocusParent.requestFocus()
}
}
Text(modifier = Modifier.onFocusParent(userSpaceFocusParent, "video detail - user space") {
isFocused.value = (it.isFocused)
if (it.isFocused) {
viewModel.currentFocusNode.value = userSpaceFocusNode
}
}.onFocusKeyEventMove(leftCanMove = notAllowMove,
rightCanMove = notAllowMove,
upCanMove = notAllowMove,
downCanMove = {
viewModel.currentFocusNode.value = FocusNode(tag = "Episode")
episodeFocusParent.requestFocus()
false
}).noRippleClickable {
screenNavigation.push(
Manifest.UserSpaceScreen,
screenKey = vContent.user?.id.orEmpty(),
arguments = ScreenArgs.putValue("KEY_USER_NAME", vContent.user?.name)
.putValue("KEY_USER_ID", vContent.user?.id),
pushOptions = PushOptions(
removePredicate = PushOptions.RemoveAnyPredicate(Manifest.UserSpaceScreen)
)
}, text = vContent.user?.name.orEmpty(), fontSize = 20.sp, color = ColorResource.acRed
)
userSpaceFocusParent.requestFocus()
}.background(
if (isFocused.value) {
ColorResource.acRed
} else {
Color.Transparent
}, shape = RoundedCornerShape(6.dp)
).padding(12.dp, 6.dp),
text = vContent.user?.name.orEmpty(),
fontSize = 20.sp,
color = if (isFocused.value) {
Color.White
} else {
ColorResource.acRed
}
)
Spacer(modifier = Modifier.width(16.dp))
Column {
Expand Down Expand Up @@ -216,28 +266,77 @@ fun videoDetailContent(acContent: AcContent, vContent: VideoContent, viewModel:
}
val isExpandedScreen = rememberIsWindowExpanded()

val focusVm = viewModel(GlobalFocusViewModel::class) {
GlobalFocusViewModel()
}
LaunchedEffect(Unit) {
if (focusVm.curFocusRequesterParent.value == null) {
viewModel.episodeFocusParent.requestFocus()
}
}

val grid = remember {
if (isExpandedScreen) {
6
} else {
2
}
}
LazyVerticalGrid(
columns = GridCells.Fixed(
if (isExpandedScreen) {
6
} else {
2
}
),
modifier = Modifier.fillMaxSize(),
columns = GridCells.Fixed(grid),
modifier = Modifier.fillMaxSize().onFocusParent(episodeFocusParent, "video detail - episode")
.onFocusKeyEventMove(upCanMove = {
if (viewModel.currentFocusNode.value.index < grid) {
viewModel.currentFocusNode.value = FocusNode(tag = "UserSpace")
userSpaceFocusParent.requestFocus()
}
true
}),
contentPadding = PaddingValues(top = 12.dp, bottom = 12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
itemsIndexed(vContent.videoList.orEmpty()) { index, item ->

val focusRequester = remember { FocusRequester() }
val episodeFocusNode = remember { FocusNode(tag = "Episode", index = index) }

val curParentFocused = focusVm.curFocusRequesterParent.collectAsState()
if (curParentFocused.value == episodeFocusParent && (viewModel.currentFocusNode.value == episodeFocusNode)) {
SideEffect {
focusRequester.requestFocus()
}
}

val isFocused = viewModel.currentFocusNode.collectAsState().value == episodeFocusNode

Box(
modifier = Modifier.weight(1f).height(52.dp).clip(MaterialTheme.shapes.medium)
.background(ColorResource.background)
modifier = Modifier.weight(1f).height(52.dp).clip(MaterialTheme.shapes.medium).background(
if (isFocused) {
ColorResource.acRed
} else {
ColorResource.background
}
)
) {
Box(modifier = Modifier.align(Alignment.Center).noRippleClickable {
viewModel.play(acContent, index + 1)
}, contentAlignment = Alignment.Center) {
Text(text = (index + 1).toString(), style = MaterialTheme.typography.h3)
Box(modifier = Modifier.align(Alignment.Center).fillMaxSize().padding(0.dp, 0.dp, 32.dp, 0.dp)
.focusRequester(focusRequester).onFocusChanged {
if (it.isFocused) {
viewModel.currentFocusNode.value = FocusNode(tag = "Episode", index = index)
}
}.focusTarget().noRippleClickable {
viewModel.play(acContent, index + 1)
}, contentAlignment = Alignment.Center
) {
Text(
text = (index + 1).toString(),
style = MaterialTheme.typography.h3,
color = if (isFocused) {
ColorResource.white
} else {
ColorResource.black
}
)
}
PopupWindowLayout(modifier = Modifier.align(Alignment.CenterEnd), displayContent = {
Card(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.succlz123.app.acfun.ui.detail

import androidx.compose.ui.focus.FocusRequester
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -12,6 +13,7 @@ import org.succlz123.app.acfun.api.bean.AcContent
import org.succlz123.app.acfun.api.bean.PlayList
import org.succlz123.app.acfun.api.bean.VideoContent
import org.succlz123.app.acfun.danmaku.DanmakuBean
import org.succlz123.lib.focus.FocusNode
import org.succlz123.lib.network.HttpX
import org.succlz123.lib.result.screenResultDataNone
import org.succlz123.lib.screen.result.ScreenResult
Expand All @@ -31,6 +33,12 @@ class VideoDetailViewModel : ScreenViewModel() {

private var currentPos = 1

val userSpaceFocusParent = FocusRequester()

val episodeFocusParent = FocusRequester()

var currentFocusNode = MutableStateFlow(FocusNode(tag = "Episode"))

override fun onCleared() {
super.onCleared()
currentPos = 1
Expand Down
Loading

0 comments on commit 45a59e3

Please sign in to comment.