diff --git a/ComposeStarter/app/build.gradle b/ComposeStarter/app/build.gradle index bc3765d18..7f2a3cd52 100644 --- a/ComposeStarter/app/build.gradle +++ b/ComposeStarter/app/build.gradle @@ -43,6 +43,7 @@ android { compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 + coreLibraryDesugaringEnabled true } kotlinOptions { jvmTarget = JavaVersion.VERSION_17.majorVersion @@ -67,7 +68,7 @@ dependencies { // General compose dependencies implementation composeBom implementation libs.androidx.activity.compose - implementation libs.compose.ui.tooling.preview + implementation libs.androidx.splashscreen // Compose for Wear OS Dependencies // NOTE: DO NOT INCLUDE a dependency on androidx.compose.material:material. @@ -79,10 +80,22 @@ dependencies { // Foundation is additive, so you can use the mobile version in your Wear OS app. implementation libs.wear.compose.foundation + implementation(libs.androidx.material.icons.core) + + // Horologist for correct Compose layout + implementation libs.horologist.composables + implementation libs.horologist.compose.layout + implementation libs.horologist.compose.material + + // 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.navigation + + coreLibraryDesugaring libs.desugar.jdk.libs // Testing testImplementation libs.junit diff --git a/ComposeStarter/app/src/main/java/com/example/android/wearable/composestarter/presentation/MainActivity.kt b/ComposeStarter/app/src/main/java/com/example/android/wearable/composestarter/presentation/MainActivity.kt index 84261c98f..fcec01f84 100644 --- a/ComposeStarter/app/src/main/java/com/example/android/wearable/composestarter/presentation/MainActivity.kt +++ b/ComposeStarter/app/src/main/java/com/example/android/wearable/composestarter/presentation/MainActivity.kt @@ -13,27 +13,48 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(ExperimentalHorologistApi::class) + package com.example.android.wearable.composestarter.presentation import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.foundation.background +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Build import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Devices -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.wear.compose.material.ListHeader import androidx.wear.compose.material.MaterialTheme import androidx.wear.compose.material.Text +import androidx.wear.compose.material.TitleCard +import androidx.wear.compose.navigation.SwipeDismissableNavHost +import androidx.wear.compose.navigation.composable +import androidx.wear.compose.navigation.rememberSwipeDismissableNavController +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.WearAppTheme +import com.google.android.horologist.annotations.ExperimentalHorologistApi +import com.google.android.horologist.compose.layout.AppScaffold +import com.google.android.horologist.compose.layout.ScalingLazyColumn +import com.google.android.horologist.compose.layout.ScreenScaffold +import com.google.android.horologist.compose.layout.rememberColumnState +import com.google.android.horologist.compose.material.Button +import com.google.android.horologist.compose.material.Chip +import com.google.android.horologist.compose.rotaryinput.rotaryWithScroll /** * Simple "Hello, World" app meant as a starting point for a new project using Compose for Wear OS. @@ -41,7 +62,7 @@ import com.example.android.wearable.composestarter.presentation.theme.WearAppThe * Displays only a centered [Text] composable, and the actual text varies based on the shape of the * device (round vs. square/rectangular). * - * If you plan to have multiple screens, use the Wear version of Compose Navigation. You can carry + * 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 * back action). For more information, go here: * https://developer.android.com/reference/kotlin/androidx/wear/compose/navigation/package-summary @@ -51,26 +72,88 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { - WearApp("Android") + installSplashScreen() + + WearApp() + + setTheme(android.R.style.Theme_DeviceDefault) } } } @Composable -fun WearApp(greetingName: String) { +fun WearApp() { + val navController = rememberSwipeDismissableNavController() + WearAppTheme { - /* If you have enough items in your list, use [ScalingLazyColumn] which is an optimized - * version of LazyColumn for wear devices with some added features. For more information, - * see d.android.com/wear/compose. - */ + AppScaffold { + SwipeDismissableNavHost(navController = navController, startDestination = "menu") { + composable("menu") { + GreetingScreen("Android", onShowList = { navController.navigate("list") }) + } + composable("list") { + ListScreen() + } + } + } + } +} + +@Composable +fun GreetingScreen(greetingName: String, onShowList: () -> Unit) { + val scrollState = ScrollState(0) + + val horizontalPadding = (0.052f * LocalConfiguration.current.screenWidthDp).dp + + /* If you have enough items in your list, use [ScalingLazyColumn] which is an optimized + * version of LazyColumn for wear devices with some added features. For more information, + * see d.android.com/wear/compose. + */ + ScreenScaffold(scrollState = scrollState) { Column( modifier = Modifier .fillMaxSize() - .background(MaterialTheme.colors.background) - .selectableGroup(), + .verticalScroll(scrollState) + .rotaryWithScroll(scrollState) + .padding(horizontal = horizontalPadding), verticalArrangement = Arrangement.Center ) { Greeting(greetingName = greetingName) + Chip(label = "Show List", onClick = onShowList) + } + } +} + +@Composable +fun ListScreen() { + val columnState = rememberColumnState() + + ScreenScaffold(scrollState = columnState) { + ScalingLazyColumn( + columnState = columnState, + modifier = Modifier + .fillMaxSize() + ) { + item { + ListHeader { + Text("Header") + } + } + item { + Chip(label = "Example Chip", onClick = { }) + } + item { + TitleCard(title = { Text("Example Title") }, onClick = { }) { + Text("Example Content\nMore Lines\nAnd More") + } + } + item { + Button( + imageVector = Icons.Default.Build, + contentDescription = "Example Button", + onClick = { } + ) + } } } } @@ -85,8 +168,16 @@ fun Greeting(greetingName: String) { ) } -@Preview(device = Devices.WEAR_OS_SMALL_ROUND, showSystemUi = true) +@WearPreviewDevices +@WearPreviewFontScales +@Composable +fun GreetingScreenPreview() { + GreetingScreen("Preview Android", onShowList = {}) +} + +@WearPreviewDevices +@WearPreviewFontScales @Composable -fun DefaultPreview() { - WearApp("Preview Android") +fun ListScreenPreview() { + ListScreen() } diff --git a/ComposeStarter/gradle/libs.versions.toml b/ComposeStarter/gradle/libs.versions.toml index 0cdc8570a..c8441f8f2 100644 --- a/ComposeStarter/gradle/libs.versions.toml +++ b/ComposeStarter/gradle/libs.versions.toml @@ -2,33 +2,35 @@ android-gradle-plugin = "8.2.2" androidx-activity = "1.8.2" androidx-compose-bom = "2024.02.01" -androidx-lifecycle = "2.4.1" -androidx-test = "1.4.0" androidx-wear-compose = "1.3.0" -androidx-wear-tiles = "1.1.0" -androidx-wear-watchface = "1.0.1" compose-compiler = "1.5.10" +compose-ui-tooling = "1.3.0" +horologist = "0.5.22" ktlint = "0.50.0" org-jetbrains-kotlin = "1.9.22" -org-jetbrains-kotlinx = "1.6.0" [libraries] -android-lint-gradle = "com.android.tools.lint:lint-gradle:31.2.2" androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidx-compose-bom" } +androidx-compose-ui-tooling = { module = "androidx.wear.compose:compose-ui-tooling", version.ref = "compose-ui-tooling" } +androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core" } +androidx-splashscreen = "androidx.core:core-splashscreen:1.0.1" compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compose-compiler" } compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } -jacoco-ant = "org.jacoco:org.jacoco.ant:0.8.11" +desugar-jdk-libs = "com.android.tools:desugar_jdk_libs:2.0.4" +horologist-composables = { module = "com.google.android.horologist:horologist-composables", version.ref = "horologist" } +horologist-compose-layout = { module = "com.google.android.horologist:horologist-compose-layout", version.ref = "horologist" } +horologist-compose-material = { module = "com.google.android.horologist:horologist-compose-material", version.ref = "horologist" } junit = "junit:junit:4.13.2" kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "org-jetbrains-kotlin" } test-espresso-core = "androidx.test.espresso:espresso-core:3.5.1" test-ext-junit = "androidx.test.ext:junit:1.1.5" wear-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "androidx-wear-compose" } wear-compose-material = { module = "androidx.wear.compose:compose-material", version.ref = "androidx-wear-compose" } +wear-compose-navigation = { module = "androidx.wear.compose:compose-navigation", version.ref = "androidx-wear-compose" } [plugins] com-android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" } com-diffplug-spotless = "com.diffplug.spotless:6.25.0" -org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin" }