Skip to content
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
8 changes: 5 additions & 3 deletions ComposeStarter/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ plugins {
id("com.android.application")
alias(libs.plugins.roborazzi)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.serialization)
}

android {
Expand Down Expand Up @@ -91,15 +92,16 @@ dependencies {
implementation(libs.wear.compose.foundation)
implementation(libs.androidx.material.icons.core)

implementation(libs.horologist.compose.layout)

// Preview Tooling
implementation(libs.compose.ui.tooling.preview)
implementation(libs.androidx.compose.ui.tooling)

// If you are using Compose Navigation, use the Wear OS version (NOT the
// androidx.navigation:navigation-compose version), that is, uncomment the line below.
implementation(libs.wear.compose.navigation)
implementation(libs.wear.compose.navigation3)
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui)
implementation(libs.kotlinx.serialization.json)

implementation(libs.androidx.ui.test.manifest)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,46 +30,49 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.ui.NavDisplay
import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState
import androidx.wear.compose.material3.AlertDialog
import androidx.wear.compose.material3.AlertDialogDefaults
import androidx.wear.compose.material3.AppScaffold
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.ButtonDefaults
import androidx.wear.compose.material3.ButtonGroup
import androidx.wear.compose.material3.EdgeButton
import androidx.wear.compose.material3.EdgeButtonSize
import androidx.wear.compose.material3.FilledIconButton
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.IconButtonDefaults
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.ListHeaderDefaults
import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.material3.SurfaceTransformation
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.TitleCard
import androidx.wear.compose.material3.lazy.rememberTransformationSpec
import androidx.wear.compose.material3.lazy.transformedHeight
import androidx.wear.compose.navigation.SwipeDismissableNavHost
import androidx.wear.compose.navigation.composable
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import androidx.wear.compose.navigation3.rememberSwipeDismissableSceneStrategy
import androidx.wear.compose.ui.tooling.preview.WearPreviewDevices
import androidx.wear.compose.ui.tooling.preview.WearPreviewFontScales
import com.example.android.wearable.composestarter.R
import com.example.android.wearable.composestarter.presentation.theme.AppCardDefaults
import com.example.android.wearable.composestarter.presentation.theme.WearAppTheme
import com.google.android.horologist.compose.layout.ColumnItemType
import com.google.android.horologist.compose.layout.rememberResponsiveColumnPadding
import kotlinx.serialization.Serializable

/**
* Simple "Hello, World" app meant as a starting point for a new project using Compose for Wear OS.
*
* Displays a centered [Text] composable and a list built with [TransformingLazyColumn].
*
* Use the Wear version of Compose Navigation. You can carry
* over your knowledge from mobile and it supports the swipe-to-dismiss gesture (Wear OS's
* over your knowledge from mobile, and it supports the swipe-to-dismiss gesture (Wear OS's
* back action). For more information, go here:
* https://developer.android.com/reference/kotlin/androidx/wear/compose/navigation/package-summary
* https://developer.android.com/reference/kotlin/androidx/wear/compose/navigation3/package-summary
*/
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -81,23 +84,43 @@ class MainActivity : ComponentActivity() {
}
}

@Serializable
sealed interface AppKey : NavKey

@Serializable
data object MenuScreen : AppKey

@Serializable
data object ListNavScreen : AppKey

@Composable
fun WearApp() {
val navController = rememberSwipeDismissableNavController()
val backStack = rememberNavBackStack(MenuScreen)

WearAppTheme {
AppScaffold {
SwipeDismissableNavHost(navController = navController, startDestination = "menu") {
composable("menu") {
GreetingScreen(
"Android",
onShowList = { navController.navigate("list") }
)
}
composable("list") {
ListScreen()
val entryProvider =
remember {
entryProvider<NavKey> {
entry<MenuScreen> {
GreetingScreen(
"Android",
onShowList = { backStack.add(ListNavScreen) }
)
}
entry<ListNavScreen> {
ListScreen()
}
}
}
}

val swipeDismissableSceneStrategy = rememberSwipeDismissableSceneStrategy<NavKey>()

NavDisplay(
backStack = backStack,
entryProvider = entryProvider,
sceneStrategies = listOf(swipeDismissableSceneStrategy)
)
}
}
}
Expand All @@ -123,49 +146,41 @@ fun GreetingScreen(
) {
Text(stringResource(R.string.show_list))
}
},
// The bottom padding value is always ignored when using EdgeButton because this button is
// always placed at the end of the screen.
// The `ScreenScaffold` parameter `edgeButtonSpacing` can be used to specify the
// gap between edgeButton and content.
contentPadding =
rememberResponsiveColumnPadding(
first = ColumnItemType.ListHeader
)
// The `ScreenScaffold` parameter `edgeButtonSpacing` can be used to specify the
// gap between edgeButton and content.
}
) { contentPadding ->
// Use workaround from Horologist for padding or wait until fix lands
TransformingLazyColumn(
state = scrollState,
contentPadding = contentPadding
) {
item { Greeting(greetingName = greetingName, modifier = modifier.fillMaxSize()) }
item {
Greeting(
greetingName = greetingName,
modifier =
modifier
.fillMaxSize()
.minimumVerticalContentPadding(
ListHeaderDefaults.minimumTopListContentPadding
)
)
}
}
}
}

@Composable
fun ListScreen(modifier: Modifier = Modifier) {
var showDialog by remember { mutableStateOf(false) }

/*
* Specifying the types of items that appear at the start and end of the list ensures that the
* appropriate padding is used.
*/
val listState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()

ScreenScaffold(
scrollState = listState,
scrollState = listState
/*
* TransformingLazyColumn takes care of the horizontal and vertical
* padding for the list and handles scrolling.
* Use workaround from Horologist for padding or wait until fix lands
*/
contentPadding =
rememberResponsiveColumnPadding(
first = ColumnItemType.ListHeader,
last = ColumnItemType.IconButton
)
) { contentPadding ->
TransformingLazyColumn(
state = listState,
Expand All @@ -176,7 +191,10 @@ fun ListScreen(modifier: Modifier = Modifier) {
modifier =
Modifier
.fillMaxWidth()
.transformedHeight(this, transformationSpec),
.transformedHeight(this, transformationSpec)
.minimumVerticalContentPadding(
ListHeaderDefaults.minimumTopListContentPadding
),
transformation = SurfaceTransformation(transformationSpec)
) { Text(text = "Header") }
}
Expand Down Expand Up @@ -223,6 +241,9 @@ fun ListScreen(modifier: Modifier = Modifier) {
applyContainerTransformation(scrollProgress)
}
}.transformedHeight(this, transformationSpec)
.minimumVerticalContentPadding(
ButtonDefaults.minimumVerticalListContentPadding
)
) {
FilledIconButton(
onClick = { showDialog = true },
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions ComposeStarter/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ robolectric = "4.16.1"
roborazzi = "1.59.0"
ui-test-junit4 = "1.10.6"
ui-test-manifest = "1.10.6"
androidx-navigation3 = "1.1.0"
kotlinx-serialization = "1.10.0"

[libraries]
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
Expand All @@ -35,11 +37,15 @@ test-espresso-core = "androidx.test.espresso:espresso-core:3.7.0"
test-ext-junit = "androidx.test.ext:junit:1.3.0"
wear-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "androidx-wear-compose" }
wear-compose-material = { module = "androidx.wear.compose:compose-material3", version.ref = "androidx-wear-compose-material3" }
wear-compose-navigation = { module = "androidx.wear.compose:compose-navigation", version.ref = "androidx-wear-compose" }
horologist-compose-layout = { module = "com.google.android.horologist:horologist-compose-layout", version.ref = "horologist" }
wear-compose-navigation3 = { module = "androidx.wear.compose:compose-navigation3", version.ref = "androidx-wear-compose" }
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "androidx-navigation3" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "androidx-navigation3" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }

[plugins]
com-android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
com-diffplug-spotless = "com.diffplug.spotless:8.4.0"
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "org-jetbrains-kotlin" }
org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "org-jetbrains-kotlin" }
Loading