diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt b/compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt index 31037647b..56330061b 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt @@ -39,7 +39,7 @@ import com.example.compose.snippets.components.ScaffoldExample import com.example.compose.snippets.components.SliderExamples import com.example.compose.snippets.components.SwitchExamples import com.example.compose.snippets.graphics.ApplyPolygonAsClipImage -import com.example.compose.snippets.graphics.BitmapFromComposableSnippet +import com.example.compose.snippets.graphics.BitmapFromComposableFullSnippet import com.example.compose.snippets.graphics.BrushExamplesScreen import com.example.compose.snippets.images.ImageExamplesScreen import com.example.compose.snippets.landing.LandingScreen @@ -70,7 +70,7 @@ class SnippetsActivity : ComponentActivity() { Destination.BrushExamples -> BrushExamplesScreen() Destination.ImageExamples -> ImageExamplesScreen() Destination.AnimationQuickGuideExamples -> AnimationExamplesScreen() - Destination.ScreenshotExample -> BitmapFromComposableSnippet() + Destination.ScreenshotExample -> BitmapFromComposableFullSnippet() Destination.ComponentsExamples -> ComponentsScreen { navController.navigate( it.route diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/graphics/AdvancedGraphicsSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/graphics/AdvancedGraphicsSnippets.kt index 900f38da6..bc16c96a5 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/graphics/AdvancedGraphicsSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/graphics/AdvancedGraphicsSnippets.kt @@ -21,7 +21,6 @@ import android.content.Context import android.content.Intent import android.content.Intent.createChooser import android.graphics.Bitmap -import android.graphics.Picture import android.media.MediaScannerConnection import android.net.Uri import android.os.Build @@ -29,7 +28,9 @@ import android.os.Environment import android.provider.MediaStore import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize @@ -44,18 +45,17 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.draw -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas -import androidx.compose.ui.graphics.nativeCanvas +import androidx.compose.ui.graphics.asAndroidBitmap +import androidx.compose.ui.graphics.layer.drawLayer +import androidx.compose.ui.graphics.rememberGraphicsLayer import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource @@ -87,14 +87,44 @@ import kotlinx.coroutines.suspendCancellableCoroutine * limitations under the License. */ +@Preview +@Composable +private fun CreateBitmapFromGraphicsLayer() { + // [START android_compose_graphics_layer_bitmap_basics] + val coroutineScope = rememberCoroutineScope() + val graphicsLayer = rememberGraphicsLayer() + Box( + modifier = Modifier + .drawWithContent { + // call record to capture the content in the graphics layer + graphicsLayer.record { + // draw the contents of the composable into the graphics layer + this@drawWithContent.drawContent() + } + // draw the graphics layer on the visible canvas + drawLayer(graphicsLayer) + } + .clickable { + coroutineScope.launch { + val bitmap = graphicsLayer.toImageBitmap() + // do something with the newly acquired bitmap + } + } + .background(Color.White) + ) { + Text("Hello Android", fontSize = 26.sp) + } + // [END android_compose_graphics_layer_bitmap_basics] +} + @OptIn(ExperimentalPermissionsApi::class) @Preview @Composable -fun BitmapFromComposableSnippet() { +fun BitmapFromComposableFullSnippet() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } - val picture = remember { Picture() } + val graphicsLayer = rememberGraphicsLayer() val writeStorageAccessState = rememberMultiplePermissionsState( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -104,14 +134,15 @@ fun BitmapFromComposableSnippet() { listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) } ) + // This logic should live in your ViewModel - trigger a side effect to invoke URI sharing. // checks permissions granted, and then saves the bitmap from a Picture that is already capturing content // and shares it with the default share sheet. fun shareBitmapFromComposable() { if (writeStorageAccessState.allPermissionsGranted) { coroutineScope.launch { - val bitmap = createBitmapFromPicture(picture) - val uri = bitmap.saveToDisk(context) + val bitmap = graphicsLayer.toImageBitmap() + val uri = bitmap.asAndroidBitmap().saveToDisk(context) shareBitmap(context, uri) } } else if (writeStorageAccessState.shouldShowRationale) { @@ -140,39 +171,22 @@ fun BitmapFromComposableSnippet() { } } ) { padding -> - // [START android_compose_draw_into_bitmap] Column( modifier = Modifier .padding(padding) .fillMaxSize() .drawWithCache { - // Example that shows how to redirect rendering to an Android Picture and then - // draw the picture into the original destination - val width = this.size.width.toInt() - val height = this.size.height.toInt() - onDrawWithContent { - val pictureCanvas = - androidx.compose.ui.graphics.Canvas( - picture.beginRecording( - width, - height - ) - ) - // requires at least 1.6.0-alpha01+ - draw(this, this.layoutDirection, pictureCanvas, this.size) { + graphicsLayer.record { this@onDrawWithContent.drawContent() } - picture.endRecording() - - drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) } + drawLayer(graphicsLayer) } } ) { ScreenContentToCapture() } - // [END android_compose_draw_into_bitmap] } } @@ -207,25 +221,6 @@ private fun ScreenContentToCapture() { } } -private fun createBitmapFromPicture(picture: Picture): Bitmap { - // [START android_compose_draw_into_bitmap_convert_picture] - val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - Bitmap.createBitmap(picture) - } else { - val bitmap = Bitmap.createBitmap( - picture.width, - picture.height, - Bitmap.Config.ARGB_8888 - ) - val canvas = android.graphics.Canvas(bitmap) - canvas.drawColor(android.graphics.Color.WHITE) - canvas.drawPicture(picture) - bitmap - } - // [END android_compose_draw_into_bitmap_convert_picture] - return bitmap -} - private suspend fun Bitmap.saveToDisk(context: Context): Uri { val file = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), 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 bc73afb1d..e2bf4c434 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 @@ -426,7 +426,7 @@ private fun CustomSnapDistance() { HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(200.dp), - outOfBoundsPageCount = 10, + beyondViewportPageCount = 10, flingBehavior = fling ) { PagerSampleItem(page = it) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 49a7b25e5..0a47f7031 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,8 @@ accompanist = "0.32.0" androidGradlePlugin = "8.2.2" androidx-activity-compose = "1.9.0-alpha03" androidx-appcompat = "1.6.1" -androidx-compose-bom = "2024.01.00" +androidx-compose-bom = "2024.04.01" +androidx-compose-ui-test = "1.7.0-alpha03" androidx-constraintlayout = "2.1.4" androidx-constraintlayout-compose = "1.0.1" androidx-coordinator-layout = "1.2.0" @@ -13,16 +14,16 @@ androidx-fragment-ktx = "1.6.2" androidx-glance-appwidget = "1.0.0" androidx-lifecycle-compose = "2.7.0" androidx-lifecycle-runtime-compose = "2.7.0" -androidx-navigation = "2.7.6" +androidx-navigation = "2.7.7" androidx-paging = "3.2.1" androidx-test = "1.5.0" androidx-test-espresso = "3.5.1" -androidxHiltNavigationCompose = "1.1.0" +androidxHiltNavigationCompose = "1.2.0" coil = "2.5.0" # @keep compileSdk = "34" compose-compiler = "1.5.4" -compose-latest = "1.7.0-alpha03" +compose-latest = "1.7.0-alpha07" coroutines = "1.7.3" google-maps = "18.2.0" gradle-versions = "0.51.0" @@ -66,7 +67,7 @@ androidx-compose-runtime = { module = "androidx.compose.runtime:runtime" } androidx-compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" } androidx-compose-ui = { module = "androidx.compose.ui:ui" } androidx-compose-ui-googlefonts = { module = "androidx.compose.ui:ui-text-google-fonts" } -androidx-graphics-shapes = "androidx.graphics:graphics-shapes:1.0.0-alpha04" +androidx-graphics-shapes = "androidx.graphics:graphics-shapes:1.0.0-alpha05" androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } androidx-compose-ui-test = { module = "androidx.compose.ui:ui-test" } androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" }