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
Show file tree
Hide file tree
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
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -83,8 +87,10 @@ class MainActivity : AppCompatActivity() {


setContent {
val widthSizeClass = calculateWindowSizeClass(this).widthSizeClass
CompositionLocalProvider(
LocalImageLoader provides imageLoader,
LocalWidthWindowSizeClass provides widthSizeClass,
) {
AccountSettingsProvider(accountDao) {
SettingsProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -27,7 +28,7 @@ fun RYScaffold(
content: @Composable () -> Unit = {},
) {
Scaffold(
modifier = Modifier
modifier = modifier
.background(
MaterialTheme.colorScheme.surfaceColorAtElevation(
topBarTonalElevation,
Expand Down
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
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}

Expand All @@ -126,9 +126,7 @@ fun HomeEntry(
navController.navigate(RouteName.FLOW) {
launchSingleTop = true
}
navController.navigate("${RouteName.READING}/${openArticleId}") {
launchSingleTop = true
}
homeViewModel.selectArticle(openArticleId)
openArticleId = ""
}
}
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ object RouteName {
// Home
const val FEEDS = "feeds"
const val FLOW = "flow"
const val READING = "reading"

// Settings
const val SETTINGS = "settings"
Expand Down
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
Expand Up @@ -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(
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -69,6 +74,7 @@ fun FlowPage(
navController: NavHostController,
flowViewModel: FlowViewModel = hiltViewModel(),
homeViewModel: HomeViewModel,
onArticleClick: (String) -> Unit,
) {
val keyboardController = LocalSoftwareKeyboardController.current
val topBarTonalElevation = LocalFlowTopBarTonalElevation.current
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
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
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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 = {
Expand Down
Loading
Loading