fix focus for android tv
This commit is contained in:
+1
@@ -39,6 +39,7 @@ fun AppBarWithSearchWithBackPreview() {
|
||||
@Composable
|
||||
fun InternalAppBarPreview() {
|
||||
SearchAppBar(
|
||||
Modifier,
|
||||
TextFieldValue(""),
|
||||
FocusRequester(),
|
||||
{},
|
||||
|
||||
+20
-2
@@ -18,7 +18,7 @@ import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -33,9 +33,12 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.FocusState
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusEvent
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -142,6 +145,7 @@ fun VideosInfo(
|
||||
emptyResource: StringResource,
|
||||
onClick: (item: VideoItem) -> Unit
|
||||
) {
|
||||
val requester = remember { FocusRequester() }
|
||||
val blur = LocalBlurState.current
|
||||
val gridState = rememberLazyGridState()
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
@@ -160,11 +164,19 @@ fun VideosInfo(
|
||||
),
|
||||
state = gridState
|
||||
) {
|
||||
items(items) { item ->
|
||||
itemsIndexed(
|
||||
items,
|
||||
key = { _, item -> item.id }
|
||||
) { index, item ->
|
||||
Poster(
|
||||
imageUrl = item.poster,
|
||||
label = item.title,
|
||||
onClick = { onClick(item) },
|
||||
modifier = if (index == 0) {
|
||||
Modifier.focusRequester(requester)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -172,6 +184,12 @@ fun VideosInfo(
|
||||
VideosNotFound(emptyResource)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(items) {
|
||||
if (items.isNotEmpty()) {
|
||||
withFrameNanos {}
|
||||
requester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
+10
-1
@@ -35,6 +35,7 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -42,9 +43,12 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
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.layout.ContentScale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -240,6 +244,7 @@ fun DetailsInfo(
|
||||
paddingValues: PaddingValues,
|
||||
onWatchClicked: () -> Unit
|
||||
) {
|
||||
val requester = remember { FocusRequester() }
|
||||
val blur = LocalBlurState.current
|
||||
val showDetails = rememberSaveable { mutableStateOf(false) }
|
||||
LazyColumn(
|
||||
@@ -251,8 +256,12 @@ fun DetailsInfo(
|
||||
item {
|
||||
Button(
|
||||
onClick = onWatchClicked,
|
||||
modifier = Modifier.fillMaxWidth().padding(8.dp)
|
||||
modifier = Modifier.fillMaxWidth().padding(8.dp).focusRequester(requester)
|
||||
) { Text(stringResource(Res.string.watch)) }
|
||||
LaunchedEffect(null) {
|
||||
withFrameNanos {}
|
||||
requester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-13
@@ -30,14 +30,18 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
@@ -183,26 +187,39 @@ fun EpisodesOverviewList(
|
||||
ErrorAndRetry(errorMsg = errorCode) { loadLinks() }
|
||||
} else if (episodes != null) {
|
||||
val blur = LocalBlurState.current
|
||||
val requester = remember { FocusRequester() }
|
||||
ExpressiveLazyColumn(
|
||||
modifier = Modifier.applyBlurSource(blur).fillMaxSize(),
|
||||
contentPadding = contentPadding,
|
||||
content = {
|
||||
section {
|
||||
episodes.mapIndexed { index, episode ->
|
||||
item(key = episode.episode.episode) {
|
||||
EpisodeInfo(
|
||||
onLaunch,
|
||||
episode,
|
||||
onClipboardAction,
|
||||
index,
|
||||
onOfflineClicked,
|
||||
isSaveMode
|
||||
)
|
||||
}
|
||||
itemsIndexed(
|
||||
episodes,
|
||||
{ it.episode.episode }
|
||||
) { index, episode ->
|
||||
EpisodeInfo(
|
||||
if (index == 0) {
|
||||
Modifier.focusRequester(requester)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
onLaunch,
|
||||
episode,
|
||||
onClipboardAction,
|
||||
index,
|
||||
onOfflineClicked,
|
||||
isSaveMode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
LaunchedEffect(episodes) {
|
||||
if (episodes.isNotEmpty()) {
|
||||
withFrameNanos {}
|
||||
requester.requestFocus()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Loading()
|
||||
}
|
||||
@@ -211,6 +228,7 @@ fun EpisodesOverviewList(
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun EpisodeInfo(
|
||||
modifier: Modifier,
|
||||
onLaunch: (Episode) -> Unit,
|
||||
ep: EpisodeWithOfflineInfo,
|
||||
onClipboardAction: (Episode) -> Unit,
|
||||
@@ -219,8 +237,7 @@ private fun EpisodeInfo(
|
||||
isSaveMode: Boolean
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
modifier = modifier.fillMaxWidth()
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
if (isSaveMode) {
|
||||
|
||||
+23
-4
@@ -14,7 +14,11 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.shadowsparky.ui.LocalBlurState
|
||||
@@ -73,20 +77,35 @@ fun SeasonsInfo(
|
||||
onClick: (Long) -> Unit
|
||||
) {
|
||||
val blur = LocalBlurState.current
|
||||
val requester = remember { FocusRequester() }
|
||||
ExpressiveLazyColumn(
|
||||
Modifier.applyBlurSource(blur).fillMaxWidth(),
|
||||
contentPadding = paddingValues,
|
||||
content = {
|
||||
section {
|
||||
items(seasons) {
|
||||
SeasonInfo(it.title) { onClick(it.id) }
|
||||
itemsIndexed(seasons) { index, season ->
|
||||
SeasonInfo(
|
||||
if (index == 0) {
|
||||
Modifier.focusRequester(requester)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
season.title
|
||||
) { onClick(season.id) }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(seasons) {
|
||||
if (seasons.isNotEmpty()) {
|
||||
withFrameNanos {}
|
||||
requester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SeasonInfo(titleText: String, onClick: () -> Unit) {
|
||||
Text(titleText, modifier = Modifier.fillMaxWidth().clickable { onClick() }.padding(16.dp))
|
||||
fun SeasonInfo(modifier: Modifier, titleText: String, onClick: () -> Unit) {
|
||||
Text(titleText, modifier = modifier.fillMaxWidth().clickable { onClick() }.padding(16.dp))
|
||||
}
|
||||
|
||||
+3
-1
@@ -92,6 +92,7 @@ fun SearchScreen(
|
||||
BaseMainScreen(
|
||||
appBar = {
|
||||
SearchAppBar(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textFieldValue = textFieldValue,
|
||||
onDone = { component.done(textFieldValue.text) },
|
||||
onValueChange = { textFieldValue = it },
|
||||
@@ -213,6 +214,7 @@ expect fun Modifier.requestFocusOnDownEvent(focusRequester: FocusRequester): Mod
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
fun SearchAppBar(
|
||||
modifier: Modifier,
|
||||
textFieldValue: TextFieldValue,
|
||||
focusRequester: FocusRequester,
|
||||
onDone: () -> Unit,
|
||||
@@ -228,7 +230,7 @@ fun SearchAppBar(
|
||||
}
|
||||
val blur = LocalBlurState.current
|
||||
AppBarWithSearch(
|
||||
modifier = Modifier.applyBlurEffect(blur),
|
||||
modifier = modifier.applyBlurEffect(blur),
|
||||
state = rememberSearchBarState(),
|
||||
colors = ComponentDefaults.appBarWithSearchDefaults(),
|
||||
inputField = {
|
||||
|
||||
+12
-1
@@ -12,9 +12,14 @@ import androiddev.apps.vbox.client.shared.generated.resources.user_tags
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.shadowsparky.ui.LocalBlurState
|
||||
import ru.shadowsparky.ui.applyBlurSource
|
||||
@@ -28,6 +33,7 @@ fun SettingsContent(comp: SettingsComponent, paddingValues: PaddingValues) {
|
||||
val tokenInfo by comp.tokenInfo.collectAsState(null)
|
||||
val externalPlayerState by comp.externalPlayerState.collectAsState(null)
|
||||
val blur = LocalBlurState.current
|
||||
val requester = remember { FocusRequester() }
|
||||
ExpressiveLazyColumn(
|
||||
modifier = Modifier.applyBlurSource(blur),
|
||||
contentPadding = paddingValues,
|
||||
@@ -47,7 +53,8 @@ fun SettingsContent(comp: SettingsComponent, paddingValues: PaddingValues) {
|
||||
SwitchPreference(
|
||||
text = stringResource(Res.string.external_player_title),
|
||||
state = it,
|
||||
onStateChanged = { newState -> comp.setExternalPlayerState(newState) }
|
||||
onStateChanged = { newState -> comp.setExternalPlayerState(newState) },
|
||||
modifier = Modifier.focusRequester(requester)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -99,4 +106,8 @@ fun SettingsContent(comp: SettingsComponent, paddingValues: PaddingValues) {
|
||||
}
|
||||
}
|
||||
)
|
||||
LaunchedEffect(null) {
|
||||
withFrameNanos {}
|
||||
requester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user