Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

large screen support (WIP) #560

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -177,6 +177,7 @@ dependencies {

// https://developer.android.com/jetpack/androidx/releases/compose-material3
implementation "androidx.compose.material3:material3:$material3"
implementation "androidx.compose.material3:material3-window-size-class:$material3"

// https://github.com/google/accompanist/releases
implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist"
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@ import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.core.app.NotificationManagerCompat
@@ -28,6 +30,7 @@ import me.ash.reader.infrastructure.preference.SettingsProvider
import me.ash.reader.ui.ext.languages
import me.ash.reader.ui.page.common.HomeEntry
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
import me.ash.reader.ui.theme.palette.core.LocalWidthWindowSizeClass
import java.lang.reflect.Field
import javax.inject.Inject

@@ -43,6 +46,7 @@ class MainActivity : AppCompatActivity() {
@Inject
lateinit var accountDao: AccountDao

@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
@@ -83,8 +87,10 @@ class MainActivity : AppCompatActivity() {


setContent {
val widthSizeClass = calculateWindowSizeClass(this).widthSizeClass
CompositionLocalProvider(
LocalImageLoader provides imageLoader,
LocalWidthWindowSizeClass provides widthSizeClass,
) {
AccountSettingsProvider(accountDao) {
SettingsProvider {
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ import me.ash.reader.ui.theme.palette.onDark
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RYScaffold(
modifier: Modifier = Modifier,
containerColor: Color = MaterialTheme.colorScheme.surface,
topBarTonalElevation: Dp = 0.dp,
containerTonalElevation: Dp = 0.dp,
@@ -27,7 +28,7 @@ fun RYScaffold(
content: @Composable () -> Unit = {},
) {
Scaffold(
modifier = Modifier
modifier = modifier
.background(
MaterialTheme.colorScheme.surfaceColorAtElevation(
topBarTonalElevation,
17 changes: 6 additions & 11 deletions app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ import me.ash.reader.ui.ext.isFirstLaunch
import me.ash.reader.ui.page.home.HomeViewModel
import me.ash.reader.ui.page.home.feeds.FeedsPage
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
import me.ash.reader.ui.page.home.flow.FlowPage
import me.ash.reader.ui.page.home.flow.FlowRoute
import me.ash.reader.ui.page.home.reading.ReadingPage
import me.ash.reader.ui.page.settings.SettingsPage
import me.ash.reader.ui.page.settings.accounts.AccountDetailsPage
@@ -97,12 +97,12 @@ fun HomeEntry(
)

// This is finally
navController.currentBackStackEntryFlow.collectLatest {
homeViewModel.homeUiState.collectLatest {
Log.i("RLog", "currentBackStackEntry: ${navController.currentDestination?.route}")
// Animation duration takes 310 ms
delay(310L)
isReadingPage =
navController.currentDestination?.route == "${RouteName.READING}/{articleId}"
navController.currentDestination?.route == RouteName.FLOW && it.isArticleOpen
}
}

@@ -126,9 +126,7 @@ fun HomeEntry(
navController.navigate(RouteName.FLOW) {
launchSingleTop = true
}
navController.navigate("${RouteName.READING}/${openArticleId}") {
launchSingleTop = true
}
homeViewModel.selectArticle(openArticleId)
openArticleId = ""
}
}
@@ -169,14 +167,11 @@ fun HomeEntry(
)
}
forwardAndBackwardComposable(route = RouteName.FLOW) {
FlowPage(
FlowRoute(
navController = navController,
homeViewModel = homeViewModel,
homeViewModel = homeViewModel
)
}
forwardAndBackwardComposable(route = "${RouteName.READING}/{articleId}") {
ReadingPage(navController = navController, homeViewModel = homeViewModel)
}

// Settings
forwardAndBackwardComposable(route = RouteName.SETTINGS) {
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ object RouteName {
// Home
const val FEEDS = "feeds"
const val FLOW = "flow"
const val READING = "reading"

// Settings
const val SETTINGS = "settings"
15 changes: 15 additions & 0 deletions app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt
Original file line number Diff line number Diff line change
@@ -101,6 +101,18 @@ class HomeViewModel @Inject constructor(
_homeUiState.update { it.copy(searchContent = content) }
fetchArticles()
}

fun selectArticle(articleId: String) {
_homeUiState.update {
it.copy(isArticleOpen = true, articleId = articleId)
}
}

fun backToFeed() {
_homeUiState.update {
it.copy(isArticleOpen = false, articleId = null)
}
}
}

data class FilterState(
@@ -112,4 +124,7 @@ data class FilterState(
data class HomeUiState(
val pagingData: Flow<PagingData<ArticleFlowItem>> = emptyFlow(),
val searchContent: String = "",

val isArticleOpen: Boolean = false,
val articleId: String? = null,
)
34 changes: 31 additions & 3 deletions app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package me.ash.reader.ui.page.home.flow

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -24,6 +26,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.platform.LocalContext
@@ -59,6 +62,8 @@ import me.ash.reader.ui.component.base.SwipeRefresh
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.home.HomeViewModel
import me.ash.reader.ui.page.home.reading.ReadingPage
import me.ash.reader.ui.theme.palette.core.LocalWidthWindowSizeClass

@OptIn(
com.google.accompanist.pager.ExperimentalPagerApi::class,
@@ -69,6 +74,7 @@ fun FlowPage(
navController: NavHostController,
flowViewModel: FlowViewModel = hiltViewModel(),
homeViewModel: HomeViewModel,
onArticleClick: (String) -> Unit,
) {
val keyboardController = LocalSoftwareKeyboardController.current
val topBarTonalElevation = LocalFlowTopBarTonalElevation.current
@@ -182,7 +188,10 @@ fun FlowPage(
onSearch = false
}

val isExpandedScreen = LocalWidthWindowSizeClass.current == WindowWidthSizeClass.Expanded
val width = if (isExpandedScreen) Modifier.width(334.dp) else Modifier
RYScaffold(
modifier = width,
topBarTonalElevation = topBarTonalElevation.value.dp,
containerTonalElevation = articleListTonalElevation.value.dp,
navigationIcon = {
@@ -328,9 +337,7 @@ fun FlowPage(
isSwipeEnabled = { listState.isScrollInProgress },
onClick = {
onSearch = false
navController.navigate("${RouteName.READING}/${it.article.id}") {
launchSingleTop = true
}
onArticleClick(it.article.id)
},
onToggleStarred = onToggleStarred,
onToggleRead = onToggleRead,
@@ -368,3 +375,24 @@ fun FlowPage(
}
)
}

@Composable
fun FlowWithArticleDetailsScreen(
navController: NavHostController,
homeViewModel: HomeViewModel,
flowViewModel: FlowViewModel,
onArticleClick: (String) -> Unit,
) {
val homeUiState = homeViewModel.homeUiState.collectAsStateValue()
Row {
FlowPage(
navController = navController,
onArticleClick = onArticleClick,
homeViewModel = homeViewModel,
flowViewModel = flowViewModel,
)
if (homeUiState.isArticleOpen) {
ReadingPage(navController = navController, homeViewModel = homeViewModel)
}
}
}
77 changes: 77 additions & 0 deletions app/src/main/java/me/ash/reader/ui/page/home/flow/FlowRoute.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package me.ash.reader.ui.page.home.flow

import androidx.activity.compose.BackHandler
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.page.home.HomeUiState
import me.ash.reader.ui.page.home.HomeViewModel
import me.ash.reader.ui.page.home.reading.ReadingPage
import me.ash.reader.ui.theme.palette.core.LocalWidthWindowSizeClass

enum class FlowScreenType {
FlowWithArticleDetails,
Flow,
ArticleDetails,
}

fun getFlowScreenType(isExpanded: Boolean, homeUiState: HomeUiState): FlowScreenType = when (isExpanded) {
true -> {
FlowScreenType.FlowWithArticleDetails
}
false -> {
if (homeUiState.isArticleOpen) {
FlowScreenType.ArticleDetails
} else {
FlowScreenType.Flow
}
}
}

@Composable
fun FlowRoute(
navController: NavHostController,
homeViewModel: HomeViewModel,
flowViewModel: FlowViewModel = hiltViewModel(),
) {
val homeUiState = homeViewModel.homeUiState.collectAsStateValue()
val isExpandedScreen = LocalWidthWindowSizeClass.current == WindowWidthSizeClass.Expanded
val flowScreenType = getFlowScreenType(isExpandedScreen, homeUiState)

val selectArticle: (String) -> Unit = { articleId ->
homeViewModel.selectArticle(articleId)
}

when (flowScreenType) {
FlowScreenType.FlowWithArticleDetails -> {
FlowWithArticleDetailsScreen(
navController = navController,
homeViewModel = homeViewModel,
flowViewModel = flowViewModel,
onArticleClick = selectArticle,
)

BackHandler {
homeViewModel.backToFeed()
navController.popBackStack()
}
}
FlowScreenType.Flow -> {
FlowPage(
navController = navController,
homeViewModel = homeViewModel,
flowViewModel = flowViewModel,
onArticleClick = selectArticle,
)
}
FlowScreenType.ArticleDetails -> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Article -> Flow -> Home backstack seems broken now

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, we can put this change on hold since even Google haven't agree on the pattern of making a adaptive navigation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Article -> Flow -> Home backstack seems broken now

Could you please elaborate on the work flow?

ReadingPage(navController = navController, homeViewModel = homeViewModel)

BackHandler {
homeViewModel.backToFeed()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import me.ash.reader.domain.model.article.ArticleFlowItem
import me.ash.reader.domain.model.article.ArticleWithFeed
Original file line number Diff line number Diff line change
@@ -75,10 +75,12 @@ fun ReadingPage(
}

val pagingItems = homeUiState.pagingData.collectAsLazyPagingItems().itemSnapshotList
val articleId = homeViewModel.homeUiState.collectAsStateValue().articleId

LaunchedEffect(Unit) {
LaunchedEffect(articleId) {
navController.currentBackStackEntryFlow.collect {
it.arguments?.getString("articleId")?.let { articleId ->
val getArticleId : String? = articleId ?: it.arguments?.getString("articleId")
getArticleId?.let { articleId ->
if (readerState.articleId != articleId) {
readingViewModel.initData(articleId)
}
17 changes: 11 additions & 6 deletions app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -27,6 +28,7 @@ import me.ash.reader.ui.component.base.FeedbackIconButton
import me.ash.reader.ui.component.base.RYExtensibleVisibility
import me.ash.reader.ui.ext.surfaceColorAtElevation
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.theme.palette.core.LocalWidthWindowSizeClass

@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -41,6 +43,7 @@ fun TopBar(
val context = LocalContext.current
val tonalElevation = LocalReadingPageTonalElevation.current
val sharedContent = LocalSharedContent.current
val isExpandedScreen = LocalWidthWindowSizeClass.current == WindowWidthSizeClass.Expanded

Box(
modifier = Modifier
@@ -54,12 +57,14 @@ fun TopBar(
modifier = Modifier,
windowInsets = windowInsets,
navigationIcon = {
FeedbackIconButton(
imageVector = Icons.Rounded.Close,
contentDescription = stringResource(R.string.close),
tint = MaterialTheme.colorScheme.onSurface
) {
onClose()
if (!isExpandedScreen) {
FeedbackIconButton(
imageVector = Icons.Rounded.Close,
contentDescription = stringResource(R.string.close),
tint = MaterialTheme.colorScheme.onSurface
) {
onClose()
}
}
},
actions = {
Original file line number Diff line number Diff line change
@@ -7,8 +7,10 @@

package me.ash.reader.ui.theme.palette.core

import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.staticCompositionLocalOf
import me.ash.reader.ui.theme.palette.colorspace.cielab.CieLab
import me.ash.reader.ui.theme.palette.colorspace.ciexyz.CieXyz
@@ -32,6 +34,10 @@ val LocalZcamViewingConditions = staticCompositionLocalOf {
createZcamViewingConditions()
}

val LocalWidthWindowSizeClass = compositionLocalOf {
WindowWidthSizeClass.Compact
}

@Composable
fun ProvideZcamViewingConditions(
whitePoint: CieXyz = Illuminant.D65,