refactor(android): drop Accompanist SystemUI Controller dependency

This commit is contained in:
Harsh Shandilya 2023-08-16 11:25:13 +05:30
parent bc2365b08b
commit bc3a1266f4
No known key found for this signature in database
8 changed files with 192 additions and 244 deletions

View file

@ -64,7 +64,6 @@ whetstone {
dependencies { dependencies {
implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.androidx.compose.bom))
implementation(platform(libs.okhttp.bom)) implementation(platform(libs.okhttp.bom))
implementation(libs.accompanist.sysuicontroller)
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.material) implementation(libs.androidx.compose.material)
implementation(libs.androidx.compose.material.icons.extended) implementation(libs.androidx.compose.material.icons.extended)

View file

@ -11,14 +11,16 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.ui.platform.LocalUriHandler
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import com.deliveryhero.whetstone.Whetstone import com.deliveryhero.whetstone.Whetstone
import com.deliveryhero.whetstone.activity.ContributesActivityInjector import com.deliveryhero.whetstone.activity.ContributesActivityInjector
import dev.msfjarvis.claw.android.ui.LobstersApp import dev.msfjarvis.claw.android.ui.LobstersApp
import dev.msfjarvis.claw.common.comments.HTMLConverter import dev.msfjarvis.claw.common.comments.HTMLConverter
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
import javax.inject.Inject import javax.inject.Inject
@ -34,16 +36,20 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
installSplashScreen() installSplashScreen()
Whetstone.inject(this) Whetstone.inject(this)
WindowCompat.setDecorFitsSystemWindows(window, false) enableEdgeToEdge()
setContent { setContent {
val windowSizeClass = calculateWindowSizeClass(this) val windowSizeClass = calculateWindowSizeClass(this)
LobstersTheme(
LobstersApp( dynamicColor = true,
urlLauncher = urlLauncher, providedValues = arrayOf(LocalUriHandler provides urlLauncher),
htmlConverter = htmlConverter, ) {
windowSizeClass = windowSizeClass, LobstersApp(
setWebUri = { url -> webUri = url }, urlLauncher = urlLauncher,
) htmlConverter = htmlConverter,
windowSizeClass = windowSizeClass,
setWebUri = { url -> webUri = url },
)
}
} }
} }

View file

@ -39,7 +39,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.semantics.testTagsAsResourceId
@ -58,7 +57,6 @@ import dev.msfjarvis.claw.android.ui.datatransfer.DataTransferScreen
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationRail import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationRail
import dev.msfjarvis.claw.android.ui.decorations.NavigationItem import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
import dev.msfjarvis.claw.android.ui.decorations.TransparentSystemBars
import dev.msfjarvis.claw.android.ui.lists.DatabasePosts import dev.msfjarvis.claw.android.ui.lists.DatabasePosts
import dev.msfjarvis.claw.android.ui.lists.NetworkPosts import dev.msfjarvis.claw.android.ui.lists.NetworkPosts
import dev.msfjarvis.claw.android.ui.lists.SearchList import dev.msfjarvis.claw.android.ui.lists.SearchList
@ -68,7 +66,6 @@ import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
import dev.msfjarvis.claw.api.LobstersApi import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.common.comments.CommentsPage import dev.msfjarvis.claw.common.comments.CommentsPage
import dev.msfjarvis.claw.common.comments.HTMLConverter import dev.msfjarvis.claw.common.comments.HTMLConverter
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.ui.decorations.ClawAppBar import dev.msfjarvis.claw.common.ui.decorations.ClawAppBar
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
import dev.msfjarvis.claw.common.user.UserProfile import dev.msfjarvis.claw.common.user.UserProfile
@ -104,193 +101,186 @@ fun LobstersApp(
val navigationType = ClawNavigationType.fromSize(windowSizeClass.widthSizeClass) val navigationType = ClawNavigationType.fromSize(windowSizeClass.widthSizeClass)
LobstersTheme( val navItems =
dynamicColor = true, persistentListOf(
providedValues = arrayOf(LocalUriHandler provides urlLauncher), NavigationItem(
) { label = "Hottest",
val navItems = route = Destinations.Hottest.route,
persistentListOf( icon = Icons.Outlined.Whatshot,
NavigationItem( selectedIcon = Icons.Filled.Whatshot,
label = "Hottest", ) {
route = Destinations.Hottest.route, coroutineScope.launch { hottestListState.animateScrollToItem(index = 0) }
icon = Icons.Outlined.Whatshot, },
selectedIcon = Icons.Filled.Whatshot, NavigationItem(
) { label = "Newest",
coroutineScope.launch { hottestListState.animateScrollToItem(index = 0) } route = Destinations.Newest.route,
}, icon = Icons.Outlined.NewReleases,
NavigationItem( selectedIcon = Icons.Filled.NewReleases,
label = "Newest", ) {
route = Destinations.Newest.route, coroutineScope.launch { newestListState.animateScrollToItem(index = 0) }
icon = Icons.Outlined.NewReleases, },
selectedIcon = Icons.Filled.NewReleases, NavigationItem(
) { label = "Saved",
coroutineScope.launch { newestListState.animateScrollToItem(index = 0) } route = Destinations.Saved.route,
}, icon = Icons.Outlined.FavoriteBorder,
NavigationItem( selectedIcon = Icons.Filled.Favorite,
label = "Saved", ) {
route = Destinations.Saved.route, coroutineScope.launch { savedListState.animateScrollToItem(index = 0) }
icon = Icons.Outlined.FavoriteBorder, },
selectedIcon = Icons.Filled.Favorite, NavigationItem(
) { label = "Search",
coroutineScope.launch { savedListState.animateScrollToItem(index = 0) } route = Destinations.Search.route,
}, icon = Icons.Outlined.Search,
NavigationItem( selectedIcon = Icons.Filled.Search,
label = "Search", ) {
route = Destinations.Search.route, coroutineScope.launch { searchListState.animateScrollToItem(index = 0) }
icon = Icons.Outlined.Search, },
selectedIcon = Icons.Filled.Search, )
) {
coroutineScope.launch { searchListState.animateScrollToItem(index = 0) }
},
)
TransparentSystemBars() Scaffold(
topBar = {
Scaffold( if (currentDestination != Destinations.Search.route) {
topBar = { ClawAppBar(
if (currentDestination != Destinations.Search.route) { navigationIcon = {
ClawAppBar( if (
navigationIcon = { navController.previousBackStackEntry != null &&
if ( navItems.none { it.route == currentDestination }
navController.previousBackStackEntry != null && ) {
navItems.none { it.route == currentDestination } IconButton(
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() },
) { ) {
IconButton( Icon(
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() }, imageVector = Icons.Filled.ArrowBack,
) { contentDescription = "Go back to previous screen",
Icon( )
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Go back to previous screen",
)
}
} }
}, }
title = { },
if (navItems.any { it.route == currentDestination }) { title = {
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold) if (navItems.any { it.route == currentDestination }) {
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
}
},
actions = {
if (navItems.any { it.route == currentDestination }) {
IconButton(onClick = { navController.navigate(Destinations.DataTransfer.route) }) {
Icon(
imageVector = Icons.Filled.ImportExport,
contentDescription = "Data transfer options",
)
} }
}, }
actions = { },
if (navItems.any { it.route == currentDestination }) { )
IconButton(onClick = { navController.navigate(Destinations.DataTransfer.route) }) { }
Icon( },
imageVector = Icons.Filled.ImportExport, bottomBar = {
contentDescription = "Data transfer options", AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) {
) ClawNavigationBar(
} navController = navController,
} items = navItems,
}, isVisible = navItems.any { it.route == currentDestination },
) )
} }
}, },
bottomBar = { snackbarHost = { SnackbarHost(snackbarHostState) },
AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) { modifier = modifier.semantics { testTagsAsResourceId = true },
ClawNavigationBar( ) { paddingValues ->
navController = navController, Row(modifier = Modifier.padding(paddingValues)) {
items = navItems, AnimatedVisibility(visible = navigationType == ClawNavigationType.NAVIGATION_RAIL) {
isVisible = navItems.any { it.route == currentDestination }, ClawNavigationRail(
) navController = navController,
} items = navItems,
}, isVisible = navItems.any { it.route == currentDestination },
snackbarHost = { SnackbarHost(snackbarHostState) }, )
modifier = modifier.semantics { testTagsAsResourceId = true }, }
) { paddingValues ->
Row(modifier = Modifier.padding(paddingValues)) {
AnimatedVisibility(visible = navigationType == ClawNavigationType.NAVIGATION_RAIL) {
ClawNavigationRail(
navController = navController,
items = navItems,
isVisible = navItems.any { it.route == currentDestination },
)
}
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = Destinations.startDestination.route, startDestination = Destinations.startDestination.route,
// Make animations 2x faster than default specs // Make animations 2x faster than default specs
enterTransition = { fadeIn(animationSpec = tween(350)) }, enterTransition = { fadeIn(animationSpec = tween(350)) },
exitTransition = { fadeOut(animationSpec = tween(350)) }, exitTransition = { fadeOut(animationSpec = tween(350)) },
) { ) {
val uri = LobstersApi.BASE_URL val uri = LobstersApi.BASE_URL
composable(route = Destinations.Hottest.route) { composable(route = Destinations.Hottest.route) {
setWebUri("https://lobste.rs/") setWebUri("https://lobste.rs/")
NetworkPosts( NetworkPosts(
lazyPagingItems = hottestPosts, lazyPagingItems = hottestPosts,
listState = hottestListState, listState = hottestListState,
isPostSaved = viewModel::isPostSaved, isPostSaved = viewModel::isPostSaved,
isPostRead = viewModel::isPostRead, isPostRead = viewModel::isPostRead,
postActions = postActions, postActions = postActions,
) )
} }
composable(route = Destinations.Newest.route) { composable(route = Destinations.Newest.route) {
setWebUri("https://lobste.rs/") setWebUri("https://lobste.rs/")
NetworkPosts( NetworkPosts(
lazyPagingItems = newestPosts, lazyPagingItems = newestPosts,
listState = newestListState, listState = newestListState,
isPostSaved = viewModel::isPostSaved, isPostSaved = viewModel::isPostSaved,
isPostRead = viewModel::isPostRead, isPostRead = viewModel::isPostRead,
postActions = postActions, postActions = postActions,
) )
} }
composable(route = Destinations.Saved.route) { composable(route = Destinations.Saved.route) {
setWebUri(null) setWebUri(null)
DatabasePosts( DatabasePosts(
items = savedPosts, items = savedPosts,
listState = savedListState, listState = savedListState,
postActions = postActions, postActions = postActions,
) )
} }
composable(Destinations.Search.route) { composable(Destinations.Search.route) {
setWebUri("https://lobste.rs/search") setWebUri("https://lobste.rs/search")
SearchList( SearchList(
items = viewModel.searchResults, items = viewModel.searchResults,
listState = searchListState, listState = searchListState,
isPostSaved = viewModel::isPostSaved, isPostSaved = viewModel::isPostSaved,
postActions = postActions, postActions = postActions,
searchQuery = viewModel.searchQuery, searchQuery = viewModel.searchQuery,
setSearchQuery = { query -> viewModel.searchQuery = query }, setSearchQuery = { query -> viewModel.searchQuery = query },
) )
} }
composable( composable(
route = Destinations.Comments.route, route = Destinations.Comments.route,
arguments = listOf(navArgument("postId") { type = NavType.StringType }), arguments = listOf(navArgument("postId") { type = NavType.StringType }),
deepLinks = deepLinks =
listOf( listOf(
navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}/.*" }, navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}/.*" },
navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}" }, navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}" },
), ),
) { backStackEntry -> ) { backStackEntry ->
val postId = requireNotNull(backStackEntry.arguments?.getString("postId")) val postId = requireNotNull(backStackEntry.arguments?.getString("postId"))
setWebUri("https://lobste.rs/s/$postId") setWebUri("https://lobste.rs/s/$postId")
CommentsPage( CommentsPage(
postId = postId, postId = postId,
postActions = postActions, postActions = postActions,
htmlConverter = htmlConverter, htmlConverter = htmlConverter,
getSeenComments = viewModel::getSeenComments, getSeenComments = viewModel::getSeenComments,
markSeenComments = viewModel::markSeenComments, markSeenComments = viewModel::markSeenComments,
) )
} }
composable( composable(
route = Destinations.User.route, route = Destinations.User.route,
arguments = listOf(navArgument("username") { type = NavType.StringType }), arguments = listOf(navArgument("username") { type = NavType.StringType }),
deepLinks = deepLinks =
listOf(navDeepLink { uriPattern = "$uri/u/${Destinations.User.placeholder}" }), listOf(navDeepLink { uriPattern = "$uri/u/${Destinations.User.placeholder}" }),
) { backStackEntry -> ) { backStackEntry ->
val username = requireNotNull(backStackEntry.arguments?.getString("username")) val username = requireNotNull(backStackEntry.arguments?.getString("username"))
setWebUri("https://lobste.rs/u/$username") setWebUri("https://lobste.rs/u/$username")
UserProfile( UserProfile(
username = username, username = username,
getProfile = viewModel::getUserProfile, getProfile = viewModel::getUserProfile,
) )
} }
composable(route = Destinations.DataTransfer.route) { composable(route = Destinations.DataTransfer.route) {
DataTransferScreen( DataTransferScreen(
context = context, context = context,
importPosts = viewModel::importPosts, importPosts = viewModel::importPosts,
exportPosts = viewModel::exportPosts, exportPosts = viewModel::exportPosts,
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,
) )
}
} }
} }
} }

View file

@ -1,25 +0,0 @@
/*
* Copyright © 2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android.ui.decorations
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.graphics.Color
import com.google.accompanist.systemuicontroller.rememberSystemUiController
@Composable
fun TransparentSystemBars() {
val systemUiController = rememberSystemUiController()
val useDarkIcons = !isSystemInDarkTheme()
DisposableEffect(systemUiController, useDarkIcons) {
systemUiController.setSystemBarsColor(color = Color.Transparent, darkIcons = useDarkIcons)
onDispose {}
}
}

View file

@ -1,9 +0,0 @@
<!--
~ Copyright © 2021-2023 Harsh Shandilya.
~ Use of this source code is governed by an MIT-style
~ license that can be found in the LICENSE file or at
~ https://opensource.org/licenses/MIT.
-->
<resources>
<bool name="use_light_status_bar">false</bool>
</resources>

View file

@ -1,9 +0,0 @@
<!--
~ Copyright © 2021-2023 Harsh Shandilya.
~ Use of this source code is governed by an MIT-style
~ license that can be found in the LICENSE file or at
~ https://opensource.org/licenses/MIT.
-->
<resources>
<bool name="use_light_status_bar">true</bool>
</resources>

View file

@ -19,7 +19,5 @@
<item name="android:windowActionModeOverlay">true</item> <item name="android:windowActionModeOverlay">true</item>
</style> </style>
<style name="Theme.Claw" parent="Base.Theme.Claw"> <style name="Theme.Claw" parent="Base.Theme.Claw" />
<item name="android:windowLightStatusBar">@bool/use_light_status_bar</item>
</style>
</resources> </resources>

View file

@ -1,5 +1,4 @@
[versions] [versions]
accompanist = "0.33.0-alpha"
agp = "8.2.0-alpha16" agp = "8.2.0-alpha16"
benchmark = "1.2.0-beta03" benchmark = "1.2.0-beta03"
coil = "2.4.0" coil = "2.4.0"
@ -18,8 +17,7 @@ whetstone = "0.6.0"
workmanager = "2.9.0-alpha02" workmanager = "2.9.0-alpha02"
[libraries] [libraries]
accompanist-sysuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } androidx-activity-compose = "androidx.activity:activity-compose:1.8.0-alpha06"
androidx-activity-compose = "androidx.activity:activity-compose:1.7.2"
androidx-benchmark-macro-junit4 = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "benchmark" } androidx-benchmark-macro-junit4 = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "benchmark" }
androidx-browser = "androidx.browser:browser:1.6.0" androidx-browser = "androidx.browser:browser:1.6.0"
androidx-compose-animation = { module = "androidx.compose.animation:animation" } androidx-compose-animation = { module = "androidx.compose.animation:animation" }