Skip to content

Commit

Permalink
feat(ui): large screen support
Browse files Browse the repository at this point in the history
  • Loading branch information
abcghy committed Mar 29, 2024
1 parent 826819a commit f5193c8
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 24 deletions.
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,
)
32 changes: 29 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
Expand Up @@ -24,6 +24,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 +60,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 +72,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 +186,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 +335,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 +373,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 -> {
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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,6 +34,10 @@ val LocalZcamViewingConditions = staticCompositionLocalOf {
createZcamViewingConditions()
}

val LocalWidthWindowSizeClass = compositionLocalOf {
WindowWidthSizeClass.Compact
}

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

0 comments on commit f5193c8

Please sign in to comment.