mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Further improvements to resuming the app (#577)
## Description Fixes issues when the app is resumed, such as after the screensaver is dismissed. For example, if you pause during playback and the screensaver starts, Wholphin will stop playback and go back to the previous page. But with this PR, instead of focus being lost and defaulting to teh nav drawer, focus will restore on the play button or episode card. ### Related issues Related to #552 & https://github.com/damontecres/Wholphin/issues/552#issuecomment-3690614491
This commit is contained in:
parent
bdec72752d
commit
a797bd8267
9 changed files with 127 additions and 94 deletions
|
|
@ -230,7 +230,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
Timber.i("onResume")
|
Timber.d("onResume")
|
||||||
lifecycleScope.launchIO {
|
lifecycleScope.launchIO {
|
||||||
appUpgradeHandler.run()
|
appUpgradeHandler.run()
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +238,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onRestart() {
|
override fun onRestart() {
|
||||||
super.onRestart()
|
super.onRestart()
|
||||||
Timber.i("onRestart")
|
Timber.d("onRestart")
|
||||||
viewModel.appStart()
|
viewModel.appStart()
|
||||||
// val signInAutomatically =
|
// val signInAutomatically =
|
||||||
// runBlocking { userPreferencesDataStore.data.firstOrNull()?.signInAutomatically } ?: true
|
// runBlocking { userPreferencesDataStore.data.firstOrNull()?.signInAutomatically } ?: true
|
||||||
|
|
@ -250,35 +250,35 @@ class MainActivity : AppCompatActivity() {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// override fun onStop() {
|
override fun onStop() {
|
||||||
// super.onStop()
|
super.onStop()
|
||||||
// Timber.i("onStop")
|
Timber.d("onStop")
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// override fun onPause() {
|
override fun onPause() {
|
||||||
// super.onPause()
|
super.onPause()
|
||||||
// Timber.i("onPause")
|
Timber.d("onPause")
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// override fun onStart() {
|
override fun onStart() {
|
||||||
// super.onStart()
|
super.onStart()
|
||||||
// Timber.i("onStart")
|
Timber.d("onStart")
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
// super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
// Timber.i("onSaveInstanceState")
|
Timber.d("onSaveInstanceState")
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||||
// super.onRestoreInstanceState(savedInstanceState)
|
super.onRestoreInstanceState(savedInstanceState)
|
||||||
// Timber.i("onRestoreInstanceState")
|
Timber.d("onRestoreInstanceState")
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
// super.onDestroy()
|
super.onDestroy()
|
||||||
// Timber.i("onDestroy")
|
Timber.d("onDestroy")
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.compose.LifecycleEventEffect
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import coil3.request.ErrorResult
|
import coil3.request.ErrorResult
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
|
@ -167,6 +169,26 @@ fun OneTimeLaunchedEffect(runOnceBlock: suspend CoroutineScope.() -> Unit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls [tryRequestFocus] on the provided [FocusRequester] when this composable launches or resumes
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun RequestOrRestoreFocus(
|
||||||
|
focusRequester: FocusRequester?,
|
||||||
|
debugKey: String? = null,
|
||||||
|
) {
|
||||||
|
if (focusRequester != null) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
debugKey?.let { Timber.v("RequestOrRestoreFocus: %s", it) }
|
||||||
|
focusRequester.tryRequestFocus()
|
||||||
|
}
|
||||||
|
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
|
||||||
|
debugKey?.let { Timber.v("RequestOrRestoreFocus onResume: %s", it) }
|
||||||
|
focusRequester.tryRequestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Modifier.enableMarquee(focused: Boolean) =
|
fun Modifier.enableMarquee(focused: Boolean) =
|
||||||
if (focused) {
|
if (focused) {
|
||||||
basicMarquee(
|
basicMarquee(
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
|
|
@ -67,7 +68,7 @@ import com.github.damontecres.wholphin.services.BackdropService
|
||||||
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
||||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.cards.GridCard
|
import com.github.damontecres.wholphin.ui.cards.GridCard
|
||||||
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
||||||
|
|
@ -85,17 +86,18 @@ import com.github.damontecres.wholphin.ui.playback.scale
|
||||||
import com.github.damontecres.wholphin.ui.rememberInt
|
import com.github.damontecres.wholphin.ui.rememberInt
|
||||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||||
import com.github.damontecres.wholphin.ui.toServerString
|
import com.github.damontecres.wholphin.ui.toServerString
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
|
||||||
import com.github.damontecres.wholphin.util.ApiRequestPager
|
import com.github.damontecres.wholphin.util.ApiRequestPager
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||||
import com.github.damontecres.wholphin.util.GetPersonsHandler
|
import com.github.damontecres.wholphin.util.GetPersonsHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
|
|
@ -114,13 +116,13 @@ import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.TreeSet
|
import java.util.TreeSet
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel(assistedFactory = CollectionFolderViewModel.Factory::class)
|
||||||
class CollectionFolderViewModel
|
class CollectionFolderViewModel
|
||||||
@Inject
|
@AssistedInject
|
||||||
constructor(
|
constructor(
|
||||||
|
private val savedStateHandle: SavedStateHandle,
|
||||||
api: ApiClient,
|
api: ApiClient,
|
||||||
@param:ApplicationContext private val context: Context,
|
@param:ApplicationContext private val context: Context,
|
||||||
private val serverRepository: ServerRepository,
|
private val serverRepository: ServerRepository,
|
||||||
|
|
@ -128,7 +130,25 @@ class CollectionFolderViewModel
|
||||||
private val favoriteWatchManager: FavoriteWatchManager,
|
private val favoriteWatchManager: FavoriteWatchManager,
|
||||||
private val backdropService: BackdropService,
|
private val backdropService: BackdropService,
|
||||||
val navigationManager: NavigationManager,
|
val navigationManager: NavigationManager,
|
||||||
|
@Assisted itemId: String,
|
||||||
|
@Assisted initialSortAndDirection: SortAndDirection?,
|
||||||
|
@Assisted("recursive") private val recursive: Boolean,
|
||||||
|
@Assisted private val collectionFilter: CollectionFolderFilter,
|
||||||
|
@Assisted("useSeriesForPrimary") private val useSeriesForPrimary: Boolean,
|
||||||
|
@Assisted defaultViewOptions: ViewOptions,
|
||||||
) : ItemViewModel(api) {
|
) : ItemViewModel(api) {
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(
|
||||||
|
itemId: String,
|
||||||
|
initialSortAndDirection: SortAndDirection?,
|
||||||
|
@Assisted("recursive") recursive: Boolean,
|
||||||
|
collectionFilter: CollectionFolderFilter,
|
||||||
|
@Assisted("useSeriesForPrimary") useSeriesForPrimary: Boolean,
|
||||||
|
defaultViewOptions: ViewOptions,
|
||||||
|
): CollectionFolderViewModel
|
||||||
|
}
|
||||||
|
|
||||||
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
||||||
val backgroundLoading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
val backgroundLoading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
||||||
val pager = MutableLiveData<List<BaseItem?>>(listOf())
|
val pager = MutableLiveData<List<BaseItem?>>(listOf())
|
||||||
|
|
@ -136,26 +156,19 @@ class CollectionFolderViewModel
|
||||||
val filter = MutableLiveData<GetItemsFilter>(GetItemsFilter())
|
val filter = MutableLiveData<GetItemsFilter>(GetItemsFilter())
|
||||||
val viewOptions = MutableLiveData<ViewOptions>()
|
val viewOptions = MutableLiveData<ViewOptions>()
|
||||||
|
|
||||||
private var useSeriesForPrimary: Boolean = true
|
var position: Int
|
||||||
private lateinit var collectionFilter: CollectionFolderFilter
|
get() = savedStateHandle.get<Int>("position") ?: 0
|
||||||
|
set(value) {
|
||||||
|
savedStateHandle["position"] = value
|
||||||
|
}
|
||||||
|
|
||||||
fun init(
|
init {
|
||||||
itemId: String,
|
|
||||||
initialSortAndDirection: SortAndDirection?,
|
|
||||||
recursive: Boolean,
|
|
||||||
collectionFilter: CollectionFolderFilter,
|
|
||||||
useSeriesForPrimary: Boolean,
|
|
||||||
defaultViewOptions: ViewOptions,
|
|
||||||
): Job =
|
|
||||||
viewModelScope.launch(
|
viewModelScope.launch(
|
||||||
LoadingExceptionHandler(
|
LoadingExceptionHandler(
|
||||||
loading,
|
loading,
|
||||||
context.getString(R.string.error_loading_collection, itemId),
|
context.getString(R.string.error_loading_collection, itemId),
|
||||||
) + Dispatchers.IO,
|
) + Dispatchers.IO,
|
||||||
) {
|
) {
|
||||||
this@CollectionFolderViewModel.collectionFilter = collectionFilter
|
|
||||||
this@CollectionFolderViewModel.useSeriesForPrimary = useSeriesForPrimary
|
|
||||||
this@CollectionFolderViewModel.itemId = itemId
|
|
||||||
itemId.toUUIDOrNull()?.let {
|
itemId.toUUIDOrNull()?.let {
|
||||||
fetchItem(it)
|
fetchItem(it)
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +197,7 @@ class CollectionFolderViewModel
|
||||||
|
|
||||||
loadResults(true, sortAndDirection, recursive, filterToUse, useSeriesForPrimary)
|
loadResults(true, sortAndDirection, recursive, filterToUse, useSeriesForPrimary)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun saveLibraryDisplayInfo(
|
private fun saveLibraryDisplayInfo(
|
||||||
newFilter: GetItemsFilter = this.filter.value!!,
|
newFilter: GetItemsFilter = this.filter.value!!,
|
||||||
|
|
@ -537,25 +551,27 @@ fun CollectionFolderGrid(
|
||||||
playEnabled: Boolean,
|
playEnabled: Boolean,
|
||||||
defaultViewOptions: ViewOptions,
|
defaultViewOptions: ViewOptions,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: CollectionFolderViewModel = hiltViewModel(key = itemId),
|
|
||||||
playlistViewModel: AddPlaylistViewModel = hiltViewModel(),
|
|
||||||
initialSortAndDirection: SortAndDirection? = null,
|
initialSortAndDirection: SortAndDirection? = null,
|
||||||
showTitle: Boolean = true,
|
showTitle: Boolean = true,
|
||||||
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
||||||
useSeriesForPrimary: Boolean = true,
|
useSeriesForPrimary: Boolean = true,
|
||||||
filterOptions: List<ItemFilterBy<*>> = DefaultFilterOptions,
|
filterOptions: List<ItemFilterBy<*>> = DefaultFilterOptions,
|
||||||
|
playlistViewModel: AddPlaylistViewModel = hiltViewModel(),
|
||||||
|
viewModel: CollectionFolderViewModel =
|
||||||
|
hiltViewModel<CollectionFolderViewModel, CollectionFolderViewModel.Factory>(
|
||||||
|
key = itemId,
|
||||||
|
) {
|
||||||
|
it.create(
|
||||||
|
itemId = itemId,
|
||||||
|
initialSortAndDirection = initialSortAndDirection,
|
||||||
|
recursive = recursive,
|
||||||
|
collectionFilter = initialFilter,
|
||||||
|
useSeriesForPrimary = useSeriesForPrimary,
|
||||||
|
defaultViewOptions = defaultViewOptions,
|
||||||
|
)
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
OneTimeLaunchedEffect {
|
|
||||||
viewModel.init(
|
|
||||||
itemId,
|
|
||||||
initialSortAndDirection,
|
|
||||||
recursive,
|
|
||||||
initialFilter,
|
|
||||||
useSeriesForPrimary,
|
|
||||||
defaultViewOptions,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val sortAndDirection by viewModel.sortAndDirection.observeAsState(SortAndDirection.DEFAULT)
|
val sortAndDirection by viewModel.sortAndDirection.observeAsState(SortAndDirection.DEFAULT)
|
||||||
val filter by viewModel.filter.observeAsState(initialFilter.filter)
|
val filter by viewModel.filter.observeAsState(initialFilter.filter)
|
||||||
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
||||||
|
|
@ -589,6 +605,7 @@ fun CollectionFolderGrid(
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
CollectionFolderGridContent(
|
CollectionFolderGridContent(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
|
initialPosition = viewModel.position,
|
||||||
item = item,
|
item = item,
|
||||||
title = title,
|
title = title,
|
||||||
pager = pager,
|
pager = pager,
|
||||||
|
|
@ -611,7 +628,10 @@ fun CollectionFolderGrid(
|
||||||
},
|
},
|
||||||
showTitle = showTitle,
|
showTitle = showTitle,
|
||||||
sortOptions = sortOptions,
|
sortOptions = sortOptions,
|
||||||
positionCallback = positionCallback,
|
positionCallback = { columns, position ->
|
||||||
|
viewModel.position = position
|
||||||
|
positionCallback?.invoke(columns, position)
|
||||||
|
},
|
||||||
letterPosition = { viewModel.positionOfLetter(it) ?: -1 },
|
letterPosition = { viewModel.positionOfLetter(it) ?: -1 },
|
||||||
viewOptions = viewOptions,
|
viewOptions = viewOptions,
|
||||||
defaultViewOptions = defaultViewOptions,
|
defaultViewOptions = defaultViewOptions,
|
||||||
|
|
@ -728,6 +748,7 @@ fun CollectionFolderGridContent(
|
||||||
onClickPlayAll: (shuffle: Boolean) -> Unit,
|
onClickPlayAll: (shuffle: Boolean) -> Unit,
|
||||||
onClickPlay: (Int, BaseItem) -> Unit,
|
onClickPlay: (Int, BaseItem) -> Unit,
|
||||||
onChangeBackdrop: (BaseItem) -> Unit,
|
onChangeBackdrop: (BaseItem) -> Unit,
|
||||||
|
initialPosition: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
showTitle: Boolean = true,
|
showTitle: Boolean = true,
|
||||||
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
||||||
|
|
@ -742,10 +763,10 @@ fun CollectionFolderGridContent(
|
||||||
var viewOptions by remember { mutableStateOf(viewOptions) }
|
var viewOptions by remember { mutableStateOf(viewOptions) }
|
||||||
|
|
||||||
val gridFocusRequester = remember { FocusRequester() }
|
val gridFocusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) { gridFocusRequester.tryRequestFocus() }
|
RequestOrRestoreFocus(gridFocusRequester)
|
||||||
var backdropImageUrl by remember { mutableStateOf<String?>(null) }
|
var backdropImageUrl by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
var position by rememberInt(0)
|
var position by rememberInt(initialPosition)
|
||||||
val focusedItem = pager.getOrNull(position)
|
val focusedItem = pager.getOrNull(position)
|
||||||
if (viewOptions.showDetails) {
|
if (viewOptions.showDetails) {
|
||||||
LaunchedEffect(focusedItem) {
|
LaunchedEffect(focusedItem) {
|
||||||
|
|
@ -861,7 +882,7 @@ fun CollectionFolderGridContent(
|
||||||
showJumpButtons = false, // TODO add preference
|
showJumpButtons = false, // TODO add preference
|
||||||
showLetterButtons = sortAndDirection.sort == ItemSortBy.SORT_NAME,
|
showLetterButtons = sortAndDirection.sort == ItemSortBy.SORT_NAME,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
initialPosition = 0,
|
initialPosition = initialPosition,
|
||||||
positionCallback = { columns, newPosition ->
|
positionCallback = { columns, newPosition ->
|
||||||
showHeader = newPosition < columns
|
showHeader = newPosition < columns
|
||||||
position = newPosition
|
position = newPosition
|
||||||
|
|
|
||||||
|
|
@ -117,12 +117,16 @@ fun <T : CardGridItem> CardGrid(
|
||||||
val startPosition = initialPosition.coerceIn(0, (pager.size - 1).coerceAtLeast(0))
|
val startPosition = initialPosition.coerceIn(0, (pager.size - 1).coerceAtLeast(0))
|
||||||
|
|
||||||
val fractionCacheWindow = LazyLayoutCacheWindow(aheadFraction = 1f, behindFraction = 0.5f)
|
val fractionCacheWindow = LazyLayoutCacheWindow(aheadFraction = 1f, behindFraction = 0.5f)
|
||||||
val gridState = rememberLazyGridState(cacheWindow = fractionCacheWindow)
|
var focusedIndex by rememberSaveable { mutableIntStateOf(initialPosition) }
|
||||||
|
val gridState =
|
||||||
|
rememberLazyGridState(
|
||||||
|
cacheWindow = fractionCacheWindow,
|
||||||
|
initialFirstVisibleItemIndex = focusedIndex,
|
||||||
|
)
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val firstFocus = remember { FocusRequester() }
|
val firstFocus = remember { FocusRequester() }
|
||||||
val zeroFocus = remember { FocusRequester() }
|
val zeroFocus = remember { FocusRequester() }
|
||||||
var previouslyFocusedIndex by rememberSaveable { mutableIntStateOf(0) }
|
var previouslyFocusedIndex by rememberSaveable { mutableIntStateOf(0) }
|
||||||
var focusedIndex by rememberSaveable { mutableIntStateOf(initialPosition) }
|
|
||||||
|
|
||||||
var alphabetFocus by remember { mutableStateOf(false) }
|
var alphabetFocus by remember { mutableStateOf(false) }
|
||||||
val focusOn = { index: Int ->
|
val focusOn = { index: Int ->
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
@ -31,6 +30,7 @@ import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.ChosenStreams
|
import com.github.damontecres.wholphin.data.ChosenStreams
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
|
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogParams
|
import com.github.damontecres.wholphin.ui.components.DialogParams
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
|
@ -48,7 +48,6 @@ import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
||||||
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItems
|
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItems
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.rememberInt
|
import com.github.damontecres.wholphin.ui.rememberInt
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -291,9 +290,7 @@ fun EpisodeDetailsContent(
|
||||||
val resumePosition = dto.userData?.playbackPositionTicks?.ticks ?: Duration.ZERO
|
val resumePosition = dto.userData?.playbackPositionTicks?.ticks ?: Duration.ZERO
|
||||||
|
|
||||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||||
LaunchedEffect(Unit) {
|
RequestOrRestoreFocus(focusRequesters.getOrNull(position))
|
||||||
focusRequesters.getOrNull(position)?.tryRequestFocus()
|
|
||||||
}
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
@ -49,6 +48,7 @@ import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.services.TrailerService
|
import com.github.damontecres.wholphin.services.TrailerService
|
||||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
import com.github.damontecres.wholphin.ui.Cards
|
import com.github.damontecres.wholphin.ui.Cards
|
||||||
|
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
||||||
import com.github.damontecres.wholphin.ui.cards.ChapterRow
|
import com.github.damontecres.wholphin.ui.cards.ChapterRow
|
||||||
import com.github.damontecres.wholphin.ui.cards.ExtrasRow
|
import com.github.damontecres.wholphin.ui.cards.ExtrasRow
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
|
|
@ -73,7 +73,6 @@ import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
|
||||||
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson
|
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.rememberInt
|
import com.github.damontecres.wholphin.ui.rememberInt
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -391,9 +390,9 @@ fun MovieDetailsContent(
|
||||||
val resumePosition = dto.userData?.playbackPositionTicks?.ticks ?: Duration.ZERO
|
val resumePosition = dto.userData?.playbackPositionTicks?.ticks ?: Duration.ZERO
|
||||||
|
|
||||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
focusRequesters.getOrNull(position)?.tryRequestFocus()
|
RequestOrRestoreFocus(focusRequesters.getOrNull(position))
|
||||||
}
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
@ -46,6 +45,7 @@ import com.github.damontecres.wholphin.data.model.Trailer
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.services.TrailerService
|
import com.github.damontecres.wholphin.services.TrailerService
|
||||||
import com.github.damontecres.wholphin.ui.Cards
|
import com.github.damontecres.wholphin.ui.Cards
|
||||||
|
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
||||||
import com.github.damontecres.wholphin.ui.cards.ExtrasRow
|
import com.github.damontecres.wholphin.ui.cards.ExtrasRow
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
import com.github.damontecres.wholphin.ui.cards.PersonRow
|
import com.github.damontecres.wholphin.ui.cards.PersonRow
|
||||||
|
|
@ -74,7 +74,6 @@ import com.github.damontecres.wholphin.ui.detail.movie.TrailerRow
|
||||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.rememberInt
|
import com.github.damontecres.wholphin.ui.rememberInt
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -313,9 +312,7 @@ fun SeriesDetailsContent(
|
||||||
var position by rememberInt()
|
var position by rememberInt()
|
||||||
val focusRequesters = remember { List(SIMILAR_ROW + 1) { FocusRequester() } }
|
val focusRequesters = remember { List(SIMILAR_ROW + 1) { FocusRequester() } }
|
||||||
val playFocusRequester = remember { FocusRequester() }
|
val playFocusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) {
|
RequestOrRestoreFocus(focusRequesters.getOrNull(position))
|
||||||
focusRequesters.getOrNull(position)?.tryRequestFocus()
|
|
||||||
}
|
|
||||||
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.Person
|
import com.github.damontecres.wholphin.data.model.Person
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
||||||
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
||||||
import com.github.damontecres.wholphin.ui.cards.PersonRow
|
import com.github.damontecres.wholphin.ui.cards.PersonRow
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
|
@ -181,13 +181,9 @@ fun SeriesOverviewContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
is EpisodeList.Success -> {
|
is EpisodeList.Success -> {
|
||||||
val state = rememberLazyListState()
|
val state = rememberLazyListState(position.episodeRowIndex)
|
||||||
OneTimeLaunchedEffect {
|
RequestOrRestoreFocus(firstItemFocusRequester)
|
||||||
if (state.firstVisibleItemIndex != position.episodeRowIndex) {
|
|
||||||
state.scrollToItem(position.episodeRowIndex)
|
|
||||||
}
|
|
||||||
firstItemFocusRequester.tryRequestFocus()
|
|
||||||
}
|
|
||||||
LazyRow(
|
LazyRow(
|
||||||
state = state,
|
state = state,
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import com.github.damontecres.wholphin.data.model.ItemPlayback
|
||||||
import com.github.damontecres.wholphin.data.model.Person
|
import com.github.damontecres.wholphin.data.model.Person
|
||||||
import com.github.damontecres.wholphin.data.model.Trailer
|
import com.github.damontecres.wholphin.data.model.Trailer
|
||||||
import com.github.damontecres.wholphin.preferences.ThemeSongVolume
|
import com.github.damontecres.wholphin.preferences.ThemeSongVolume
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
import com.github.damontecres.wholphin.services.ExtrasService
|
import com.github.damontecres.wholphin.services.ExtrasService
|
||||||
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
||||||
|
|
@ -98,7 +97,6 @@ class SeriesViewModel
|
||||||
): SeriesViewModel
|
): SeriesViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var prefs: UserPreferences
|
|
||||||
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
||||||
val seasons = MutableLiveData<List<BaseItem?>>(listOf())
|
val seasons = MutableLiveData<List<BaseItem?>>(listOf())
|
||||||
val episodes = MutableLiveData<EpisodeList>(EpisodeList.Loading)
|
val episodes = MutableLiveData<EpisodeList>(EpisodeList.Loading)
|
||||||
|
|
@ -119,7 +117,6 @@ class SeriesViewModel
|
||||||
"Error loading series $seriesId",
|
"Error loading series $seriesId",
|
||||||
) + Dispatchers.IO,
|
) + Dispatchers.IO,
|
||||||
) {
|
) {
|
||||||
this@SeriesViewModel.prefs = userPreferencesService.getCurrent()
|
|
||||||
Timber.v("Start")
|
Timber.v("Start")
|
||||||
val item = fetchItem(seriesId)
|
val item = fetchItem(seriesId)
|
||||||
backdropService.submit(item)
|
backdropService.submit(item)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue