CopyPastor

Detecting plagiarism made easy.

Score: 1; Reported for: Exact paragraph match Open both answers

Possible Plagiarism

Plagiarized on 2026-03-13
by arenaq

Original Post

Original - Posted on 2023-08-21
by riggaroo



            
Present in both answers; Present only in the new answer; Present only in the old answer;

From official Android [documentation](https://developer.android.com/develop/ui/compose/graphics/draw/modifiers):
``` 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) } ```
Full example in official [GItHub repo](https://github.com/android/snippets/blob/latest/compose/snippets/src/main/java/com/example/compose/snippets/graphics/AdvancedGraphicsSnippets.kt#L123).
**Latest Mechanism (Compose 1.7.0-beta02+)** ---------------------------------------
A new mechanism for drawing a composable into a bitmap has been made available. Using `graphicsLayer.record{}` you can redirect drawing commands to a certain graphics layer, and then call `DrawScope#drawLayer()` to draw the layer to screen.
GraphicsLayers now also have the ability to call `graphicsLayer.toImageBitmap()` to get a screenshot of the composable.
``` 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) }
``` More information can be found here: https://developer.android.com/develop/ui/compose/graphics/draw/modifiers#composable-to-bitmap
Full snippet: ``` @OptIn(ExperimentalPermissionsApi::class) @Preview @Composable fun BitmapFromComposableFullSnippet() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } val graphicsLayer = rememberGraphicsLayer()
val writeStorageAccessState = rememberMultiplePermissionsState( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // No permissions are needed on Android 10+ to add files in the shared storage emptyList() } else { 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 = graphicsLayer.toImageBitmap() val uri = bitmap.asAndroidBitmap().saveToDisk(context) shareBitmap(context, uri) } } else if (writeStorageAccessState.shouldShowRationale) { coroutineScope.launch { val result = snackbarHostState.showSnackbar( message = "The storage permission is needed to save the image", actionLabel = "Grant Access" )
if (result == SnackbarResult.ActionPerformed) { writeStorageAccessState.launchMultiplePermissionRequest() } } } else { writeStorageAccessState.launchMultiplePermissionRequest() } }
Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) }, floatingActionButton = { FloatingActionButton(onClick = { shareBitmapFromComposable() }) { Icon(Icons.Default.Share, "share") } } ) { padding -> Column( modifier = Modifier .padding(padding) .fillMaxSize() .drawWithCache { onDrawWithContent { graphicsLayer.record { this@onDrawWithContent.drawContent() } drawLayer(graphicsLayer) } }
) { ScreenContentToCapture() } } }
@Composable private fun ScreenContentToCapture() { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, modifier = Modifier .fillMaxSize() .background( Brush.linearGradient( listOf( Color(0xFFF5D5C0), Color(0xFFF8E8E3) ) ) ) ) { Image( painterResource(id = R.drawable.sunset), contentDescription = null, modifier = Modifier .aspectRatio(1f) .padding(32.dp), contentScale = ContentScale.Crop ) Text( "Into the Ocean depths", fontSize = 18.sp ) } }
private suspend fun Bitmap.saveToDisk(context: Context): Uri { val file = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "screenshot-${System.currentTimeMillis()}.png" )
file.writeBitmap(this, Bitmap.CompressFormat.PNG, 100)
return scanFilePath(context, file.path) ?: throw Exception("File could not be saved") }
/** * We call [MediaScannerConnection] to index the newly created image inside MediaStore to be visible * for other apps, as well as returning its [MediaStore] Uri */ private suspend fun scanFilePath(context: Context, filePath: String): Uri? { return suspendCancellableCoroutine { continuation -> MediaScannerConnection.scanFile( context, arrayOf(filePath), arrayOf("image/png") ) { _, scannedUri -> if (scannedUri == null) { continuation.cancel(Exception("File $filePath could not be scanned")) } else { continuation.resume(scannedUri) } } } }
private fun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int) { outputStream().use { out -> bitmap.compress(format, quality, out) out.flush() } }
private fun shareBitmap(context: Context, uri: Uri) { val intent = Intent(Intent.ACTION_SEND).apply { type = "image/png" putExtra(Intent.EXTRA_STREAM, uri) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity(context, createChooser(intent, "Share your image"), null) } ```

**OLDER WAY (Pre Compose 1.7.0-alpha07)** -----------------------------------------
You can use `drawIntoCanvas` method and delegate to a `android.graphics.Picture` class.
```kotlin val picture = remember { android.graphics.Picture() } 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 ) ) draw(this, this.layoutDirection, pictureCanvas, this.size) { this@onDrawWithContent.drawContent() } picture.endRecording()
drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) } } } ) { ScreenContentToCapture() } ```
You can then save the `Picture` to disk using the following code: ```kotlin private fun createBitmapFromPicture(picture: Picture): Bitmap { val bitmap = Bitmap.createBitmap( picture.width, picture.height, Bitmap.Config.ARGB_8888 )
val canvas = Canvas(bitmap) canvas.drawColor(android.graphics.Color.WHITE) canvas.drawPicture(picture) return bitmap }
private suspend fun Bitmap.saveToDisk(context: Context): Uri { val file = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "screenshot-${System.currentTimeMillis()}.png" )
file.writeBitmap(this, Bitmap.CompressFormat.PNG, 100)
return scanFilePath(context, file.path) ?: throw Exception("File could not be saved") } ```
The full snippet put together: ```kotlin import android.Manifest import android.content.Context import android.content.Intent import android.content.Intent.createChooser import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Picture import android.media.MediaScannerConnection import android.net.Uri import android.os.Build import android.os.Environment import android.provider.MediaStore import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Share import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithCache 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.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat.startActivity import com.example.compose.snippets.R import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberMultiplePermissionsState import java.io.File import kotlin.coroutines.resume import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine
/* * 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 * * http://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. */ @OptIn(ExperimentalPermissionsApi::class) @Preview @Composable fun BitmapFromComposableSnippet() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } val picture = remember { Picture() }
val writeStorageAccessState = rememberMultiplePermissionsState( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // No permissions are needed on Android 10+ to add files in the shared storage emptyList() } else { 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(Dispatchers.IO) { val bitmap = createBitmapFromPicture(picture) val uri = bitmap.saveToDisk(context) shareBitmap(context, uri) } } else if (writeStorageAccessState.shouldShowRationale) { coroutineScope.launch { val result = snackbarHostState.showSnackbar( message = "The storage permission is needed to save the image", actionLabel = "Grant Access" )
if (result == SnackbarResult.ActionPerformed) { writeStorageAccessState.launchMultiplePermissionRequest() } } } else { writeStorageAccessState.launchMultiplePermissionRequest() } }
Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) }, floatingActionButton = { FloatingActionButton(onClick = { shareBitmapFromComposable() }) { Icon(Icons.Default.Share, "share") } } ) { 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 ) ) draw(this, this.layoutDirection, pictureCanvas, this.size) { this@onDrawWithContent.drawContent() } picture.endRecording()
drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) } } } ) { ScreenContentToCapture() } // [END android_compose_draw_into_bitmap] } }
@Composable private fun ScreenContentToCapture() { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, modifier = Modifier .fillMaxSize() .background( Brush.linearGradient( listOf( Color(0xFFF5D5C0), Color(0xFFF8E8E3) ) ) ) ) { Image( painterResource(id = R.drawable.sunset), contentDescription = null, modifier = Modifier .aspectRatio(1f) .padding(32.dp), contentScale = ContentScale.Crop ) Text( "Into the Ocean depths", fontSize = 18.sp ) } }
private fun createBitmapFromPicture(picture: Picture): Bitmap { val bitmap = Bitmap.createBitmap( picture.width, picture.height, Bitmap.Config.ARGB_8888 )
val canvas = Canvas(bitmap) canvas.drawColor(android.graphics.Color.WHITE) canvas.drawPicture(picture) return bitmap }
private suspend fun Bitmap.saveToDisk(context: Context): Uri { val file = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "screenshot-${System.currentTimeMillis()}.png" )
file.writeBitmap(this, Bitmap.CompressFormat.PNG, 100)
return scanFilePath(context, file.path) ?: throw Exception("File could not be saved") }
/** * We call [MediaScannerConnection] to index the newly created image inside MediaStore to be visible * for other apps, as well as returning its [MediaStore] Uri */ private suspend fun scanFilePath(context: Context, filePath: String): Uri? { return suspendCancellableCoroutine { continuation -> MediaScannerConnection.scanFile( context, arrayOf(filePath), arrayOf("image/png") ) { _, scannedUri -> if (scannedUri == null) { continuation.cancel(Exception("File $filePath could not be scanned")) } else { continuation.resume(scannedUri) } } } }
private fun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int) { outputStream().use { out -> bitmap.compress(format, quality, out) out.flush() } }
private fun shareBitmap(context: Context, uri: Uri) { val intent = Intent(Intent.ACTION_SEND).apply { type = "image/png" putExtra(Intent.EXTRA_STREAM, uri) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity(context, createChooser(intent, "Share your image"), null) } ```

        
Present in both answers; Present only in the new answer; Present only in the old answer;