-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ASAA-159 - Move NavWrapper from projects to Launchpad Compose (#4)
* ASAA-159 - Move NavWrapper from projects to Launchpad Compose * ASAA-159 - Run KtLint * ASAA-159 - Fixed KtLint to not remove trailing commas
- Loading branch information
Showing
22 changed files
with
843 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
.../com/bottlerocketstudios/launchpad/compose/example/navigation/NavigationWrapperExample.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.bottlerocketstudios.launchpad.compose.example.navigation | ||
|
||
import android.os.Bundle | ||
import androidx.activity.ComponentActivity | ||
import androidx.activity.compose.setContent | ||
import androidx.compose.runtime.collectAsState | ||
import com.bottlerocketstudios.launchpad.compose.navigation.NavigationWrapper | ||
import com.bottlerocketstudios.launchpad.compose.navigation.util.createDevicePostureFlow | ||
import com.bottlerocketstudios.launchpad.compose.navigation.util.getWindowWidthSize | ||
import moe.tlaster.precompose.PreComposeApp | ||
|
||
class MainActivityExample : ComponentActivity() { | ||
|
||
// Create a flow that emits the current device posture. | ||
private val devicePostureFlow = createDevicePostureFlow() | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
setContent { | ||
// Collects the device posture flow and stores it in a state variable. | ||
val devicePosture = devicePostureFlow.collectAsState() | ||
|
||
PreComposeApp { | ||
// In this example, the NavWrapper for the Precompose Navigation library is being used. | ||
// The app param will extend the wrappers navigation component and bottom bar allowing both to be used in App Composable. | ||
NavigationWrapper( | ||
widthSize = getWindowWidthSize(this), | ||
devicePosture = devicePosture.value, | ||
navigationItems = exampleNavigationItems | ||
) { navigator, bottomBar -> | ||
ExampleApp( | ||
widthSize = getWindowWidthSize(this), | ||
navigator = navigator, | ||
bottomBar = bottomBar | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...src/androidMain/kotlin/com/bottlerocketstudios/launchpad/compose/navigation/.editorconfig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[*.{kt,kts}] | ||
# More info about .editorconfig at https://github.com/pinterest/ktlint#custom-editorconfig-properties | ||
# possible values: number (e.g. 2) OR unset (no quotes - makes ktlint ignore indentation completely) | ||
indent_size=4 | ||
# true (recommended) / false | ||
insert_final_newline=true | ||
# possible values: number (e.g. 120) (package name, imports & comments are ignored) OR off (no quotes) | ||
# it's automatically set to 100 on `ktlint --android ...` (per Android Kotlin Style Guide) | ||
# that is just not long enough IMO - using 200 for now | ||
max_line_length=200 | ||
# Makes git commit history look good and appending items to lists easier | ||
ij_kotlin_allow_trailing_comma=true | ||
ij_kotlin_allow_trailing_comma_on_call_site=true |
101 changes: 101 additions & 0 deletions
101
...n/kotlin/com/bottlerocketstudios/launchpad/compose/navigation/AndroidNavigationWrapper.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package com.bottlerocketstudios.launchpad.compose.navigation | ||
|
||
import androidx.compose.animation.AnimatedVisibility | ||
import androidx.compose.animation.core.Spring | ||
import androidx.compose.animation.core.spring | ||
import androidx.compose.animation.slideInVertically | ||
import androidx.compose.animation.slideOutHorizontally | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.material3.DrawerValue | ||
import androidx.compose.material3.ModalDrawerSheet | ||
import androidx.compose.material3.ModalNavigationDrawer | ||
import androidx.compose.material3.PermanentDrawerSheet | ||
import androidx.compose.material3.PermanentNavigationDrawer | ||
import androidx.compose.material3.rememberDrawerState | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.derivedStateOf | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.Modifier | ||
import androidx.navigation.NavHostController | ||
import androidx.navigation.compose.currentBackStackEntryAsState | ||
import androidx.navigation.compose.rememberNavController | ||
import com.bottlerocketstudios.launchpad.compose.navigation.components.LaunchpadBottomAppBar | ||
import com.bottlerocketstudios.launchpad.compose.navigation.components.LaunchpadDrawerContent | ||
import com.bottlerocketstudios.launchpad.compose.navigation.components.LaunchpadNavigationRail | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.DevicePosture | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.NavigationItem | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.NavigationType | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.WindowWidthSizeClass | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.generateNavItems | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.toNavigationType | ||
|
||
/** | ||
* A composable function that wraps the Androidx Compose Navigation component. | ||
* * | ||
* @param widthSize The current window width size class. | ||
* @param devicePosture The current device posture. | ||
* @param navigationItems The list of navigation items, these will be the items that show up on the navigation rail or bottom navigation bar. | ||
* For an example of this please see [exampleNavigationItems] and the [NavigationItem] class. | ||
* @param app The function that renders the app content. | ||
*/ | ||
@Composable | ||
fun AndroidNavigationWrapper( | ||
widthSize: WindowWidthSizeClass, | ||
devicePosture: DevicePosture, | ||
navigationItems: List<NavigationItem>, | ||
app: @Composable (navHostController: NavHostController?, bottomBar: (@Composable () -> Unit)) -> Unit, | ||
) { | ||
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) | ||
val navController = rememberNavController() | ||
val currentBackStackEntry by navController.currentBackStackEntryAsState() | ||
val currentRoute = currentBackStackEntry?.destination?.route | ||
val navItems = remember { | ||
generateNavItems(navigationItems) { | ||
navController.navigate(it.route) | ||
} | ||
} | ||
val navigationType = remember(widthSize, devicePosture) { | ||
derivedStateOf { | ||
widthSize.toNavigationType(devicePosture) | ||
} | ||
} | ||
|
||
when (navigationType.value) { | ||
NavigationType.PERMANENT_NAVIGATION_DRAWER -> PermanentNavigationDrawer( | ||
drawerContent = { PermanentDrawerSheet { LaunchpadDrawerContent(navItems) { it == currentRoute } } }, | ||
) { app(navController) { } } | ||
|
||
NavigationType.MODAL_NAVIGATION -> ModalNavigationDrawer( | ||
drawerState = drawerState, | ||
drawerContent = { ModalDrawerSheet { LaunchpadDrawerContent(navItems) { it == currentRoute } } }, | ||
) { app(navController) { } } | ||
|
||
else -> Row { | ||
AnimatedVisibility( | ||
visible = navigationType.value == NavigationType.NAVIGATION_RAIL, | ||
enter = slideInVertically(animationSpec = spring(stiffness = Spring.StiffnessHigh)), | ||
exit = slideOutHorizontally(animationSpec = spring(stiffness = Spring.StiffnessHigh)), | ||
) { | ||
LaunchpadNavigationRail(navItems) { it == currentRoute } | ||
} | ||
|
||
Column( | ||
modifier = Modifier | ||
.fillMaxSize(), | ||
) { | ||
app(navController) { | ||
AnimatedVisibility( | ||
visible = navigationType.value == NavigationType.BOTTOM_NAVIGATION, | ||
enter = slideInVertically(animationSpec = spring(stiffness = Spring.StiffnessHigh)), | ||
exit = slideOutHorizontally(animationSpec = spring(stiffness = Spring.StiffnessHigh)), | ||
) { | ||
LaunchpadBottomAppBar(navItems) { it == currentRoute } | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
...n/kotlin/com/bottlerocketstudios/launchpad/compose/navigation/util/CalculateDimensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.bottlerocketstudios.launchpad.compose.navigation.util | ||
|
||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.platform.LocalConfiguration | ||
import com.bottlerocketstudios.launchpad.compose.resources.Dimensions | ||
import com.bottlerocketstudios.launchpad.compose.resources.SMALL_SCREEN_WIDTH_DP | ||
import com.bottlerocketstudios.launchpad.compose.resources.smallDimensions | ||
import com.bottlerocketstudios.launchpad.compose.resources.sw360Dimensions | ||
|
||
/** | ||
* Returns the dimensions of the screen. | ||
* | ||
* If the screen width is less than or equal to 360dp, the small dimensions are returned. | ||
* Otherwise, the sw360 dimensions are returned. | ||
*/ | ||
@Composable | ||
fun getDimensions(): Dimensions = if (LocalConfiguration.current.screenWidthDp <= SMALL_SCREEN_WIDTH_DP) smallDimensions else sw360Dimensions |
17 changes: 17 additions & 0 deletions
17
...lin/com/bottlerocketstudios/launchpad/compose/navigation/util/CalculateWindowWidthSize.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.bottlerocketstudios.launchpad.compose.navigation.util | ||
|
||
import android.app.Activity | ||
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi | ||
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass | ||
import androidx.compose.runtime.Composable | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.WindowWidthSizeClass | ||
|
||
/** | ||
* Gets the window width size class of the given activity. | ||
* | ||
* @param activity The activity to get the window width size class for. | ||
* @return The window width size class of the activity. | ||
*/ | ||
@Composable | ||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class) | ||
fun getWindowWidthSize(activity: Activity): WindowWidthSizeClass = calculateWindowSizeClass(activity).widthSizeClass.toLocalModel() |
60 changes: 60 additions & 0 deletions
60
...in/kotlin/com/bottlerocketstudios/launchpad/compose/navigation/util/DevicePostureUtils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package com.bottlerocketstudios.launchpad.compose.navigation.util | ||
|
||
import androidx.activity.ComponentActivity | ||
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass | ||
import androidx.lifecycle.flowWithLifecycle | ||
import androidx.lifecycle.lifecycleScope | ||
import androidx.window.layout.WindowInfoTracker | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.DevicePosture | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.FoldingFeature | ||
import kotlinx.coroutines.flow.SharingStarted | ||
import kotlinx.coroutines.flow.map | ||
import kotlinx.coroutines.flow.stateIn | ||
|
||
/** | ||
* Creates a flow that emits the current device posture. | ||
* | ||
* The flow emits the following values: | ||
* | ||
* * [DevicePosture.NormalPosture] when the device is not in a book or separating posture. | ||
* * [DevicePosture.BookPosture] when the device is in a book posture. | ||
* * [DevicePosture.Separating] when the device is in a separating posture. | ||
* | ||
* The flow is lifecycle-aware and will stop emitting values when the activity is paused. | ||
* | ||
* @return The flow that emits the current device posture. | ||
*/ | ||
fun ComponentActivity.createDevicePostureFlow() = WindowInfoTracker.getOrCreate(this).windowLayoutInfo(this) | ||
.flowWithLifecycle(this.lifecycle) | ||
.map { layoutInfo -> | ||
val foldingFeature = | ||
layoutInfo.displayFeatures | ||
.filterIsInstance<FoldingFeature>() | ||
.firstOrNull() | ||
when { | ||
isBookPosture(foldingFeature) -> | ||
DevicePosture.BookPosture(foldingFeature.bounds) | ||
|
||
isSeparating(foldingFeature) -> | ||
DevicePosture.Separating(foldingFeature.bounds, foldingFeature.orientation) | ||
|
||
else -> DevicePosture.NormalPosture | ||
} | ||
} | ||
.stateIn( | ||
scope = lifecycleScope, | ||
started = SharingStarted.Eagerly, | ||
initialValue = DevicePosture.NormalPosture | ||
) | ||
|
||
/** | ||
* Converts an Android [WindowWidthSizeClass] to a Launchpad [WindowWidthSizeClass]. | ||
* | ||
* @return The converted [WindowWidthSizeClass]. | ||
*/ | ||
internal fun WindowWidthSizeClass.toLocalModel(): com.bottlerocketstudios.launchpad.compose.navigation.utils.WindowWidthSizeClass = when (this) { | ||
WindowWidthSizeClass.Compact -> com.bottlerocketstudios.launchpad.compose.navigation.utils.WindowWidthSizeClass.Compact | ||
WindowWidthSizeClass.Medium -> com.bottlerocketstudios.launchpad.compose.navigation.utils.WindowWidthSizeClass.Medium | ||
WindowWidthSizeClass.Expanded -> com.bottlerocketstudios.launchpad.compose.navigation.utils.WindowWidthSizeClass.Expanded | ||
else -> com.bottlerocketstudios.launchpad.compose.navigation.utils.WindowWidthSizeClass.Compact | ||
} |
18 changes: 18 additions & 0 deletions
18
...Main/kotlin/com/bottlerocketstudios/launchpad/compose/navigation/util/WindowStateUtils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.bottlerocketstudios.launchpad.compose.navigation.util | ||
|
||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.FoldingFeature | ||
import kotlin.contracts.ExperimentalContracts | ||
import kotlin.contracts.contract | ||
|
||
@OptIn(ExperimentalContracts::class) | ||
internal fun isBookPosture(foldFeature: FoldingFeature?): Boolean { | ||
contract { returns(true) implies (foldFeature != null) } | ||
return foldFeature?.state == FoldingFeature.State.HALF_OPENED && | ||
foldFeature.orientation == FoldingFeature.Orientation.VERTICAL | ||
} | ||
|
||
@OptIn(ExperimentalContracts::class) | ||
internal fun isSeparating(foldFeature: FoldingFeature?): Boolean { | ||
contract { returns(true) implies (foldFeature != null) } | ||
return foldFeature?.state == FoldingFeature.State.FLAT && foldFeature.isSeparating | ||
} |
2 changes: 1 addition & 1 deletion
2
...e/widget/listdetail/AnimatedListDetail.kt → ...e/widget/listdetail/AnimatedListDetail.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
...monMain/kotlin/com/bottlerocketstudios/launchpad/compose/example/navigation/ExampleApp.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.bottlerocketstudios.launchpad.compose.example.navigation | ||
|
||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material3.ExperimentalMaterial3Api | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.material3.Text | ||
import androidx.compose.material3.TopAppBar | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import com.bottlerocketstudios.launchpad.compose.navigation.utils.WindowWidthSizeClass | ||
import moe.tlaster.precompose.navigation.NavHost | ||
import moe.tlaster.precompose.navigation.Navigator | ||
import moe.tlaster.precompose.navigation.transition.NavTransition | ||
|
||
@OptIn(ExperimentalMaterial3Api::class) | ||
@Composable | ||
fun ExampleApp( | ||
widthSize: WindowWidthSizeClass, | ||
navigator: Navigator?, | ||
bottomBar: @Composable () -> Unit | ||
) { | ||
Scaffold( | ||
topBar = { TopAppBar(title = { Text("Example App") }) }, | ||
bottomBar = bottomBar | ||
) { | ||
navigator?.let { navController -> | ||
NavHost( | ||
// Assign the navigator to the NavHost | ||
navigator = navController, | ||
// Navigation transition for the scenes in this NavHost, this is optional | ||
navTransition = NavTransition(), | ||
// The start destination | ||
initialRoute = "/home", | ||
modifier = Modifier.padding(it) | ||
) { | ||
// Navgraph goes here | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.