Skip to content

Commit

Permalink
Fix screen caching and transitions (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
GrakovNe authored Dec 20, 2024
1 parent 9e59df4 commit 44c5c4c
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 59 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ android {
applicationId = "org.grakovne.lissen"
minSdk = 28
targetSdk = 35
versionCode = 55
versionName = "1.1.24"
versionCode = 56
versionName = "1.1.25"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@ package org.grakovne.lissen.common
import android.content.Context
import android.content.Context.CONNECTIVITY_SERVICE
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
import javax.inject.Singleton

Expand All @@ -17,34 +13,14 @@ class NetworkQualityService @Inject constructor(
@ApplicationContext private val context: Context,
) {

private val connectivityManager =
context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager

private val _networkStatus = MutableStateFlow(false)
val networkStatus: StateFlow<Boolean> = _networkStatus.asStateFlow()

init {
registerNetworkCallback()
}
private val connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager

fun isNetworkAvailable(): Boolean {
val network = connectivityManager.activeNetwork ?: return false
val networkCapabilities =
connectivityManager.getNetworkCapabilities(network) ?: return false
return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}

private fun registerNetworkCallback() {
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
_networkStatus.value = true
}

override fun onLost(network: Network) {
_networkStatus.value = false
}
}

connectivityManager.registerDefaultNetworkCallback(networkCallback)
val networkCapabilities = connectivityManager
.getNetworkCapabilities(network)
?: return false
return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
}
57 changes: 53 additions & 4 deletions app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package org.grakovne.lissen.ui.navigation

import android.annotation.SuppressLint
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -41,6 +48,26 @@ fun AppNavHost(
else -> "login_screen"
}

val enterTransition: EnterTransition = slideInHorizontally(
initialOffsetX = { it },
animationSpec = tween(),
) + fadeIn(animationSpec = tween())

val exitTransition: ExitTransition = slideOutHorizontally(
targetOffsetX = { -it },
animationSpec = tween(),
) + fadeOut(animationSpec = tween())

val popEnterTransition: EnterTransition = slideInHorizontally(
initialOffsetX = { -it },
animationSpec = tween(),
) + fadeIn(animationSpec = tween())

val popExitTransition: ExitTransition = slideOutHorizontally(
targetOffsetX = { it },
animationSpec = tween(),
) + fadeOut(animationSpec = tween())

Scaffold(modifier = Modifier.fillMaxSize()) { _ ->
NavHost(
navController = navController,
Expand All @@ -55,11 +82,15 @@ fun AppNavHost(
}

composable(
"player_screen/{bookId}?bookTitle={bookTitle}",
route = "player_screen/{bookId}?bookTitle={bookTitle}",
arguments = listOf(
navArgument("bookId") { type = NavType.StringType },
navArgument("bookTitle") { type = NavType.StringType; nullable = true },
),
enterTransition = { enterTransition },
exitTransition = { exitTransition },
popEnterTransition = { popEnterTransition },
popExitTransition = { popExitTransition },
) { navigationStack ->
val bookId = navigationStack.arguments?.getString("bookId") ?: return@composable
val bookTitle = navigationStack.arguments?.getString("bookTitle") ?: ""
Expand All @@ -72,11 +103,23 @@ fun AppNavHost(
)
}

composable("login_screen") {
composable(
route = "login_screen",
enterTransition = { enterTransition },
exitTransition = { exitTransition },
popEnterTransition = { popEnterTransition },
popExitTransition = { popExitTransition },
) {
LoginScreen(navigationService)
}

composable("settings_screen") {
composable(
route = "settings_screen",
enterTransition = { enterTransition },
exitTransition = { exitTransition },
popEnterTransition = { popEnterTransition },
popExitTransition = { popExitTransition },
) {
SettingsScreen(
onBack = {
if (navController.previousBackStackEntry != null) {
Expand All @@ -87,7 +130,13 @@ fun AppNavHost(
)
}

composable("settings_screen/custom_headers") {
composable(
route = "settings_screen/custom_headers",
enterTransition = { enterTransition },
exitTransition = { exitTransition },
popEnterTransition = { popEnterTransition },
popExitTransition = { popExitTransition },
) {
CustomHeadersSettingsScreen(
onBack = {
if (navController.previousBackStackEntry != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,14 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
Expand All @@ -52,7 +51,6 @@ import androidx.paging.compose.collectAsLazyPagingItems
import coil.ImageLoader
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import org.grakovne.lissen.R
import org.grakovne.lissen.channel.common.LibraryType
Expand All @@ -72,13 +70,15 @@ import org.grakovne.lissen.ui.screens.library.composables.placeholder.RecentBook
import org.grakovne.lissen.viewmodel.ContentCachingModelView
import org.grakovne.lissen.viewmodel.LibraryViewModel
import org.grakovne.lissen.viewmodel.PlayerViewModel
import org.grakovne.lissen.viewmodel.SettingsViewModel

@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Composable
fun LibraryScreen(
navController: AppNavigationService,
libraryViewModel: LibraryViewModel = hiltViewModel(),
playerViewModel: PlayerViewModel = hiltViewModel(),
settingsViewModel: SettingsViewModel = hiltViewModel(),
contentCachingModelView: ContentCachingModelView = hiltViewModel(),
imageLoader: ImageLoader,
networkQualityService: NetworkQualityService,
Expand All @@ -89,7 +89,7 @@ fun LibraryScreen(

val recentBooks: List<RecentBook> by libraryViewModel.recentBooks.observeAsState(emptyList())

val networkStatus by networkQualityService.networkStatus.collectAsState()
var currentLibraryId by rememberSaveable { mutableStateOf("") }
var pullRefreshing by remember { mutableStateOf(false) }
val recentBookRefreshing by libraryViewModel.recentBookUpdating.observeAsState(false)
val searchRequested by libraryViewModel.searchRequested.observeAsState(false)
Expand Down Expand Up @@ -131,12 +131,6 @@ fun LibraryScreen(
}
}

LaunchedEffect(Unit) {
snapshotFlow { networkStatus }
.distinctUntilChanged()
.collect { _ -> refreshContent(false) }
}

LaunchedEffect(preparingError) {
if (preparingError) {
playerViewModel.clearPlayingBook()
Expand All @@ -158,15 +152,18 @@ fun LibraryScreen(
val playingBook by playerViewModel.book.observeAsState()
val context = LocalContext.current

fun showRecent(): Boolean {
val fetchAvailable = networkStatus || contentCachingModelView.localCacheUsing()
fun isRecentVisible(): Boolean {
val fetchAvailable = networkQualityService.isNetworkAvailable() || contentCachingModelView.localCacheUsing()
val hasContent = recentBooks.isEmpty().not()
return !searchRequested && hasContent && fetchAvailable
}

LaunchedEffect(Unit) {
libraryViewModel.refreshRecentListening()
libraryViewModel.refreshLibrary()
if (library.itemCount == 0 || currentLibraryId != settingsViewModel.fetchPreferredLibraryId()) {
libraryViewModel.refreshRecentListening()
libraryViewModel.refreshLibrary()
currentLibraryId = settingsViewModel.fetchPreferredLibraryId()
}
}

LaunchedEffect(searchRequested) {
Expand All @@ -187,7 +184,7 @@ fun LibraryScreen(

val navBarTitle by remember {
derivedStateOf {
val showRecent = showRecent()
val showRecent = isRecentVisible()
val recentBlockVisible = libraryListState.layoutInfo.visibleItemsInfo.firstOrNull()?.key == "recent_books"

when {
Expand Down Expand Up @@ -267,7 +264,7 @@ fun LibraryScreen(
contentPadding = PaddingValues(horizontal = 16.dp),
) {
item(key = "recent_books") {
val showRecent = showRecent()
val showRecent = isRecentVisible()

when {
isPlaceholderRequired -> {
Expand All @@ -289,7 +286,7 @@ fun LibraryScreen(
}

item(key = "library_title") {
if (!searchRequested && showRecent()) {
if (!searchRequested && isRecentVisible()) {
AnimatedContent(
targetState = navBarTitle,
transitionSpec = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package org.grakovne.lissen.ui.screens.player

import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
Expand Down Expand Up @@ -49,6 +48,7 @@ import org.grakovne.lissen.ui.screens.player.composable.PlayingQueueComposable
import org.grakovne.lissen.ui.screens.player.composable.TrackControlComposable
import org.grakovne.lissen.ui.screens.player.composable.TrackDetailsComposable
import org.grakovne.lissen.ui.screens.player.composable.placeholder.PlayingQueuePlaceholderComposable
import org.grakovne.lissen.ui.screens.player.composable.placeholder.TrackControlPlaceholderComposable
import org.grakovne.lissen.ui.screens.player.composable.placeholder.TrackDetailsPlaceholderComposable
import org.grakovne.lissen.ui.screens.player.composable.provideNowPlayingTitle
import org.grakovne.lissen.viewmodel.ContentCachingModelView
Expand Down Expand Up @@ -196,10 +196,16 @@ fun PlayerScreen(
)
}

TrackControlComposable(
viewModel = playerViewModel,
modifier = Modifier,
)
if (!isPlaybackReady) {
TrackControlPlaceholderComposable(
modifier = Modifier,
)
} else {
TrackControlComposable(
viewModel = playerViewModel,
modifier = Modifier,
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ fun TrackControlComposable(
modifier: Modifier = Modifier,
) {
val isPlaying by viewModel.isPlaying.observeAsState(false)
val playbackReady by viewModel.isPlaybackReady.observeAsState(false)
val currentTrackIndex by viewModel.currentChapterIndex.observeAsState(0)
val currentTrackPosition by viewModel.currentChapterPosition.observeAsState(0.0)
val currentTrackDuration by viewModel.currentChapterDuration.observeAsState(0.0)
Expand All @@ -55,7 +54,7 @@ fun TrackControlComposable(
var isDragging by remember { mutableStateOf(false) }

LaunchedEffect(currentTrackPosition, currentTrackIndex, currentTrackDuration) {
if (playbackReady && !isDragging) {
if (!isDragging) {
sliderPosition = currentTrackPosition
}
}
Expand Down
Loading

0 comments on commit 44c5c4c

Please sign in to comment.