From f4e49876ac3fad90376323fa23b6991581d547c7 Mon Sep 17 00:00:00 2001 From: Tram Bui <62119967+trambui09@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:27:59 -0800 Subject: [PATCH 1/3] add in predictive back snippets for NavHost and predictive back handler (#394) * add in predictive back snippets * Apply Spotless * add in basic predictivebackhandler composable example * Apply Spotless --------- Co-authored-by: trambui09 --- .../predictiveback/PredictiveBackSnippets.kt | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 compose/snippets/src/main/java/com/example/compose/snippets/predictiveback/PredictiveBackSnippets.kt diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/predictiveback/PredictiveBackSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/predictiveback/PredictiveBackSnippets.kt new file mode 100644 index 00000000..88ec5c4a --- /dev/null +++ b/compose/snippets/src/main/java/com/example/compose/snippets/predictiveback/PredictiveBackSnippets.kt @@ -0,0 +1,190 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.compose.snippets.predictiveback + +import android.os.SystemClock +import androidx.activity.BackEventCompat +import androidx.activity.compose.PredictiveBackHandler +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.scaleOut +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.input.pointer.util.VelocityTracker +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import kotlin.coroutines.cancellation.CancellationException +import kotlinx.coroutines.flow.Flow + +@Composable +private fun PredictiveBackOverrideExit( + modifier: Modifier, +) { + val navController = rememberNavController() + + // [START android_compose_predictiveback_navhost] + NavHost( + navController = navController, + startDestination = "home", + popExitTransition = { + scaleOut( + targetScale = 0.9f, + transformOrigin = TransformOrigin(pivotFractionX = 0.5f, pivotFractionY = 0.5f) + ) + }, + popEnterTransition = { + EnterTransition.None + }, + modifier = modifier, + ) + // [END android_compose_predictiveback_navhost] + { + composable("home") { + HomeScreen( + modifier = modifier, + navController = navController, + ) + } + composable("settings") { + SettingsScreen( + modifier = modifier, + navController = navController, + ) + } + } +} + +@Composable +private fun HomeScreen( + modifier: Modifier = Modifier, + navController: NavHostController +) { +} + +@Composable +private fun SettingsScreen( + modifier: Modifier = Modifier, + navController: NavHostController +) { +} + +@Composable +private fun PredictiveBackHandlerBasicExample() { + + var boxScale by remember { mutableFloatStateOf(1F) } + + Box( + modifier = Modifier + .fillMaxSize(boxScale) + .background(Color.Blue) + ) + + // [START android_compose_predictivebackhandler_basic] + PredictiveBackHandler(true) { progress: Flow -> + // code for gesture back started + try { + progress.collect { backEvent -> + // code for progress + boxScale = 1F - (1F * backEvent.progress) + } + // code for completion + } catch (e: CancellationException) { + // code for cancellation + boxScale = 1F + } + } + // [END android_compose_predictivebackhandler_basic] +} + +@Composable +private fun PredictiveBackHandlerManualProgress() { + + Surface( + modifier = Modifier.fillMaxSize() + ) { + var drawerState by remember { + mutableStateOf(DrawerState.Closed) + } + + val translationX = remember { + Animatable(0f) + } + + val drawerWidth = with(LocalDensity.current) { + DrawerWidth.toPx() + } + translationX.updateBounds(0f, drawerWidth) + + val coroutineScope = rememberCoroutineScope() + + suspend fun closeDrawer(velocity: Float = 0f) { + translationX.animateTo(targetValue = 0f, initialVelocity = velocity) + drawerState = DrawerState.Closed + } + suspend fun openDrawer(velocity: Float = 0f) { + translationX.animateTo(targetValue = drawerWidth, initialVelocity = velocity) + drawerState = DrawerState.Open + } + + val velocityTracker = remember { + VelocityTracker() + } + + // [START android_compose_predictivebackhandler_manualprogress] + PredictiveBackHandler(drawerState == DrawerState.Open) { progress -> + try { + progress.collect { backEvent -> + val targetSize = (drawerWidth - (drawerWidth * backEvent.progress)) + translationX.snapTo(targetSize) + velocityTracker.addPosition( + SystemClock.uptimeMillis(), + Offset(backEvent.touchX, backEvent.touchY) + ) + } + closeDrawer(velocityTracker.calculateVelocity().x) + } catch (e: CancellationException) { + openDrawer(velocityTracker.calculateVelocity().x) + } + velocityTracker.resetTracking() + } + // [END android_compose_predictivebackhandler_manualprogress] + } +} + +private enum class DrawerState { + Open, + Closed +} + +private val DrawerWidth = 300.dp From 515b08ec9726abb958c04b3e62b6f9b2250438dc Mon Sep 17 00:00:00 2001 From: Jake Roseman <122034773+jakeroseman@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:53:52 +0000 Subject: [PATCH 2/3] Add region tags to auto advance pager examples (#398) --- .../com/example/compose/snippets/layouts/PagerSnippets.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/layouts/PagerSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/layouts/PagerSnippets.kt index 45361ef8..89916336 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/layouts/PagerSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/layouts/PagerSnippets.kt @@ -413,6 +413,7 @@ fun PagerIndicator() { } } +// [START android_compose_autoadvancepager] @Composable fun AutoAdvancePager(pageItems: List, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { @@ -457,6 +458,7 @@ fun AutoAdvancePager(pageItems: List, modifier: Modifier = Modifier) { PagerIndicator(pageItems.size, pagerState.currentPage) } } +// [END android_compose_autoadvancepager] @Preview @Composable @@ -470,6 +472,7 @@ private fun AutoAdvancePagerPreview() { AutoAdvancePager(pageItems = pageItems) } +// [START android_compose_pagerindicator] @Composable fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { @@ -494,6 +497,7 @@ fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = M } } } +// [END android_compose_pagerindicator] @Preview @Composable From fb654a90218dec10d36fc058b7350505f391bc30 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Wed, 27 Nov 2024 13:45:18 +0000 Subject: [PATCH 3/3] Init Wear Tiles snippets (#400) * Init Wear Tiles snippets * Add snippet markers * Add manifest snippet markers * Rename manifest snippet markers --- gradle/libs.versions.toml | 22 ++++++++ wear/build.gradle.kts | 11 ++++ wear/src/main/AndroidManifest.xml | 18 ++++++ .../com/example/wear/snippets/tile/Tile.kt | 53 ++++++++++++++++++ wear/src/main/res/drawable/tile_preview.png | Bin 0 -> 7335 bytes wear/src/main/res/values/strings.xml | 2 + 6 files changed, 106 insertions(+) create mode 100644 wear/src/main/java/com/example/wear/snippets/tile/Tile.kt create mode 100644 wear/src/main/res/drawable/tile_preview.png diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6d03c099..6c83d9d7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,6 +30,7 @@ coroutines = "1.9.0" glide = "1.0.0-beta01" google-maps = "19.0.0" gradle-versions = "0.51.0" +guava = "33.2.1-android" hilt = "2.52" horologist = "0.6.20" junit = "4.13.2" @@ -44,12 +45,22 @@ media3 = "1.4.1" # @keep minSdk = "21" playServicesWearable = "18.2.0" +protolayout = "1.3.0-alpha04" +protolayoutExpression = "1.3.0-alpha04" +protolayoutMaterial = "1.3.0-alpha04" recyclerview = "1.3.2" # @keep targetSdk = "34" +tiles = "1.5.0-alpha04" +tilesRenderer = "1.5.0-alpha04" +tilesTesting = "1.5.0-alpha04" +tilesTooling = "1.5.0-alpha04" +tilesToolingPreview = "1.5.0-alpha04" version-catalog-update = "0.8.5" +wear = "1.3.0" wearComposeFoundation = "1.4.0" wearComposeMaterial = "1.4.0" +wearToolingPreview = "1.0.0" [libraries] accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" } @@ -103,10 +114,20 @@ androidx-media3-common = { module = "androidx.media3:media3-common", version.ref androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" } androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "androidx-paging" } +androidx-protolayout = { module = "androidx.wear.protolayout:protolayout", version.ref = "protolayout" } +androidx-protolayout-expression = { module = "androidx.wear.protolayout:protolayout-expression", version.ref = "protolayoutExpression" } +androidx-protolayout-material = { module = "androidx.wear.protolayout:protolayout-material", version.ref = "protolayoutMaterial" } androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" } androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" } androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" } androidx-test-runner = "androidx.test:runner:1.6.2" +androidx-tiles = { module = "androidx.wear.tiles:tiles", version.ref = "tiles" } +androidx-tiles-renderer = { module = "androidx.wear.tiles:tiles-renderer", version.ref = "tilesRenderer" } +androidx-tiles-testing = { module = "androidx.wear.tiles:tiles-testing", version.ref = "tilesTesting" } +androidx-tiles-tooling = { module = "androidx.wear.tiles:tiles-tooling", version.ref = "tilesTooling" } +androidx-tiles-tooling-preview = { module = "androidx.wear.tiles:tiles-tooling-preview", version.ref = "tilesToolingPreview" } +androidx-wear = { module = "androidx.wear:wear", version.ref = "wear" } +androidx-wear-tooling-preview = { module = "androidx.wear:wear-tooling-preview", version.ref = "wearToolingPreview" } androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window" } androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.0" coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } @@ -117,6 +138,7 @@ glide-compose = { module = "com.github.bumptech.glide:compose", version.ref = "g google-android-material = { module = "com.google.android.material:material", version.ref = "material" } googlemaps-compose = { module = "com.google.maps.android:maps-compose", version.ref = "maps-compose" } googlemaps-maps = { module = "com.google.android.gms:play-services-maps", version.ref = "google-maps" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } horologist-compose-layout = { module = "com.google.android.horologist:horologist-compose-layout", version.ref = "horologist" } diff --git a/wear/build.gradle.kts b/wear/build.gradle.kts index 13fff794..154e7d37 100644 --- a/wear/build.gradle.kts +++ b/wear/build.gradle.kts @@ -55,6 +55,17 @@ dependencies { implementation(libs.compose.ui.tooling) implementation(libs.play.services.wearable) + implementation(libs.androidx.tiles) + implementation(libs.androidx.wear) + implementation(libs.androidx.protolayout) + implementation(libs.androidx.protolayout.material) + implementation(libs.androidx.protolayout.expression) + debugImplementation(libs.androidx.tiles.renderer) + testImplementation(libs.androidx.tiles.testing) + implementation(libs.androidx.wear.tooling.preview) + implementation(libs.androidx.tiles.tooling.preview) + debugImplementation(libs.androidx.tiles.tooling) + implementation(libs.guava) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.ui.tooling.preview) diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml index f0d2328e..84c4785a 100644 --- a/wear/src/main/AndroidManifest.xml +++ b/wear/src/main/AndroidManifest.xml @@ -34,6 +34,24 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/wear/src/main/java/com/example/wear/snippets/tile/Tile.kt b/wear/src/main/java/com/example/wear/snippets/tile/Tile.kt new file mode 100644 index 00000000..74a29483 --- /dev/null +++ b/wear/src/main/java/com/example/wear/snippets/tile/Tile.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.wear.snippets.tile + +import androidx.wear.protolayout.ColorBuilders.argb +import androidx.wear.protolayout.ResourceBuilders.Resources +import androidx.wear.protolayout.TimelineBuilders.Timeline +import androidx.wear.protolayout.material.Text +import androidx.wear.protolayout.material.Typography +import androidx.wear.tiles.RequestBuilders +import androidx.wear.tiles.RequestBuilders.ResourcesRequest +import androidx.wear.tiles.TileBuilders.Tile +import androidx.wear.tiles.TileService +import com.google.common.util.concurrent.Futures + +private const val RESOURCES_VERSION = "1" + +// [START android_wear_tile_mytileservice] +class MyTileService : TileService() { + + override fun onTileRequest(requestParams: RequestBuilders.TileRequest) = + Futures.immediateFuture(Tile.Builder() + .setResourcesVersion(RESOURCES_VERSION) + .setTileTimeline( + Timeline.fromLayoutElement( + Text.Builder(this, "Hello World!") + .setTypography(Typography.TYPOGRAPHY_BODY1) + .setColor(argb(0xFFFFFFFF.toInt())) + .build())) + .build()) + + override fun onTileResourcesRequest(requestParams: ResourcesRequest) = + Futures.immediateFuture(Resources.Builder() + .setVersion(RESOURCES_VERSION) + .build() + ) + +} +// [END android_wear_tile_mytileservice] \ No newline at end of file diff --git a/wear/src/main/res/drawable/tile_preview.png b/wear/src/main/res/drawable/tile_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a254a16c2d32f4b8df60587fd240228e1f0505 GIT binary patch literal 7335 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 Input Voice Text Entry Message List + Hello Tile + Hello Tile Description \ No newline at end of file