### Complete Capture Flow Example Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/architecture.md Demonstrates the setup, request, and usage phases for capturing a composable into a bitmap. This flow involves setting up a controller, applying a modifier, making an asynchronous capture request, and processing the resulting bitmap. ```kotlin // 1. Setup phase @Composable fun MyScreen() { val controller = rememberCaptureController() // O(1) Column(Modifier.capturable(controller)) { // O(1) Text("Content") } // 2. Request phase Button(onClick = { scope.launch { // 3. Async call (non-blocking) val deferred = controller.captureAsync() // O(1) // 4. Await on coroutine (can block here) val bitmap = deferred.await() // O(n) for rendering // 5. Use bitmap saveToFile(bitmap) } }) } ``` -------------------------------- ### Example: Capture with Button (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt A straightforward example demonstrating capture triggered by a button click. ```kotlin val controller = rememberCaptureController() Button(onClick = { controller.captureAsync() }) { Text("Capture") } MyComposable(modifier = Modifier.capturable(controller)) ``` -------------------------------- ### Example: Auto Capture (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example showing how to enable automatic capture when a composable enters the composition. ```kotlin val controller = rememberCaptureController() MyComposable(modifier = Modifier.capturable(controller, autoCapture = true)) ``` -------------------------------- ### Testing Configuration Example Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example test case for verifying capture configuration options. ```kotlin composeTestRule.setContent { /* ... composable with controller ... */ } val bitmap = controller.captureAsync(format = Bitmap.CompressFormat.WEBP).await() // Assert bitmap format or properties ``` -------------------------------- ### Basic Capture Controller Setup Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/api-reference/rememberCaptureController.md Demonstrates the basic setup of rememberCaptureController() within a Composable function. The returned controller is then applied to the capturable modifier on a Column. ```kotlin @Composable fun SimpleCapture() { val captureController = rememberCaptureController() Column(modifier = Modifier.capturable(captureController)) { Text("Capturable content") } } ``` -------------------------------- ### Basic Capture Example (README) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt A minimal example showing how to capture a composable using CaptureController and the capturable modifier. ```kotlin val controller = rememberCaptureController() MyComposable(modifier = Modifier.capturable(controller)) Button(onClick = { controller.captureAsync() }) { Text("Capture") } ``` -------------------------------- ### Example: Save to File (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt An example of capturing a composable and saving the resulting bitmap to a file. ```kotlin val controller = rememberCaptureController() Button(onClick = { controller.captureAsync().let { saveBitmapToFile(it, "capture.png") } }) { Text("Save") } MyComposable(modifier = Modifier.capturable(controller)) ``` -------------------------------- ### Example: Dialog Preview Capture (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Captures the content of a dialog window. ```kotlin var showDialog by remember { mutableStateOf(false) } val controller = rememberCaptureController() Button(onClick = { showDialog = true }) if (showDialog) { AlertDialog(onDismissRequest = { showDialog = false }, title = { Text("Dialog", Modifier.capturable(controller)) }, confirmButton = { /* ... */ }) } ``` -------------------------------- ### FAQ: Debugging Question Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example of a question and answer providing debugging tips. ```kotlin // Q: My capture is blank, what could be wrong? // A: Check if the modifier is applied correctly, if the composable is visible, and if there are any rendering issues. ``` -------------------------------- ### FAQ: Integration Question Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example of a question and answer concerning integration with other libraries or frameworks. ```kotlin // Q: How to integrate with Jetpack Navigation? // A: Ensure the CaptureController is managed correctly within the lifecycle of the composable screen. ``` -------------------------------- ### FAQ: Performance Question Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example of a question and answer related to performance considerations. ```kotlin // Q: Is capturing computationally expensive? // A: Yes, capturing can be resource-intensive. Optimize by capturing only when necessary and consider image format/quality. ``` -------------------------------- ### Capturable Composable Usage Example Source: https://github.com/patilshreyas/capturable/blob/master/docs/capturable/dev.shreyaspatil.capturable/-capturable-kt/-capturable.html Demonstrates how to use the Capturable composable to capture an ImageBitmap from its content. It shows the setup with a rememberCaptureController, the Capturable composable itself, and a Button to trigger the capture. ```kotlin val captureController = rememberCaptureController() Capturable( controller = captureController, onCaptured = { bitmap -> // Do something with [bitmap] } ) { // Composable content } Button(onClick = { // Capture content captureController.capture() }) { ... } ``` -------------------------------- ### Example: Capture with Loading State (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Shows how to integrate capture with a loading indicator to provide user feedback. ```kotlin var isLoading by remember { mutableStateOf(false) } val controller = rememberCaptureController() Button(onClick = { isLoading = true; controller.captureAsync().invokeOnCompletion { isLoading = false } }) { if (isLoading) CircularProgressIndicator() else Text("Capture") } MyComposable(modifier = Modifier.capturable(controller)) ``` -------------------------------- ### Example: Multiple Sections (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Illustrates capturing specific, named sections within a composable hierarchy. ```kotlin val controller = rememberCaptureController() Row { Column(Modifier.capturable(controller, "header")) { /* Header */ } Column(Modifier.capturable(controller, "content")) { /* Content */ } } controller.captureAsync("header") ``` -------------------------------- ### Example: Batch Capture (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Captures multiple composables simultaneously in a single operation. ```kotlin val controller1 = rememberCaptureController() val controller2 = rememberCaptureController() Row { ComposableA(Modifier.capturable(controller1)) ComposableB(Modifier.capturable(controller2)) } val results = listOf(controller1.captureAsync(), controller2.captureAsync()) // Process results ``` -------------------------------- ### FAQ: Limitations Question Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example of a question and answer addressing limitations of the library. ```kotlin // Q: Can I capture content rendered off-screen? // A: Capturing off-screen content might be limited depending on the platform's rendering capabilities. ``` -------------------------------- ### Capturable Composable Usage Example Source: https://github.com/patilshreyas/capturable/blob/master/docs/capturable/dev.shreyaspatil.capturable/-capturable.html Demonstrates how to use the Capturable composable with a CaptureController to capture composable content. It shows the setup of the controller, the Capturable composable itself, and a button to trigger the capture. The onCaptured callback handles the resulting bitmap or any errors. ```kotlin val captureController = rememberCaptureController() Capturable( controller = captureController, onCaptured = { bitmap, error -> // Do something with [bitmap] // Handle [error] if required }) { // Composable content } Button(onClick = { // Capture content captureController.capture() }) { ... } ``` -------------------------------- ### Example: Share Image (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Demonstrates capturing a composable and sharing the resulting image via the platform's share sheet. ```kotlin val controller = rememberCaptureController() Button(onClick = { controller.captureAsync().let { uri -> shareImageUri(context, uri) } }) { Text("Share") } MyComposable(modifier = Modifier.capturable(controller)) ``` -------------------------------- ### Batch Capture Example Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Demonstrates capturing multiple composables in a batch operation. This is efficient for capturing several elements at once. ```kotlin val controller1 = rememberCaptureController() val controller2 = rememberCaptureController() Row { ComposableA(modifier = Modifier.capturable(controller1)) ComposableB(modifier = Modifier.capturable(controller2)) } // Capture both in a batch val results = listOf(controller1.captureAsync(), controller2.captureAsync()) // Process results ``` -------------------------------- ### Example: State-Based Capture (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Triggers capture based on specific changes in the composable's state. ```kotlin var state by remember { mutableStateOf("A") } val controller = rememberCaptureController() MyComposable(state = state, modifier = Modifier.capturable(controller, onCapture = { if (state == "B") { /* capture */ } })) Button(onClick = { state = "B" }) ``` -------------------------------- ### FAQ: Edge Cases Question Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example of a question and answer covering less common scenarios. ```kotlin // Q: How does capture handle rapidly changing UI elements? // A: Capture the state you need at a specific point in time. For animations, consider capturing sequences. ``` -------------------------------- ### Example: Theme-Aware Capture (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Ensures captured content respects the current UI theme (e.g., dark mode). ```kotlin val controller = rememberCaptureController() val isDark = isSystemInDarkTheme() MyComposable(modifier = Modifier.capturable(controller)) // Capture respects current theme ``` -------------------------------- ### Example: Animated Sequence Capture (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Captures frames from an animated composable to create a sequence, like a GIF. ```kotlin val controller = rememberCaptureController() val animatable = remember { Animatable(0f) } LaunchedEffect(Unit) { animatable.animateTo(1f, animationSpec = infiniteRepeatable(tween(1000))) } MyAnimatedComposable(progress = animatable.value, modifier = Modifier.capturable(controller)) // Trigger capture repeatedly in a loop or based on animation events ``` -------------------------------- ### Example: Capture with Error Dialog (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Demonstrates handling capture errors by showing a dialog to the user. ```kotlin var error: Throwable? by remember { mutableStateOf(null) } val controller = rememberCaptureController() controller.captureAsync(onError = { error = it }) if (error != null) { AlertDialog(onDismissRequest = { error = null }, title = { Text("Error") }, confirmButton = { /* ... */ }) } ``` -------------------------------- ### Instrumented Testing Example Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt An example of an instrumented test to verify capture functionality on an Android device. ```kotlin @RunWith(AndroidJUnit4::class) class CaptureIntegrationTest { @get:Rule val composeTestRule = createComposeRule() @Test fun testCaptureWorks() { composeTestRule.setContent { /* ... composable with capturable ... */ } composeTestRule.onNodeWithText("Capture").performClick() // Assertions on captured output } } ``` -------------------------------- ### FAQ: API Usage Question Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example of a question and answer related to using the Capturable API. ```kotlin // Q: How do I capture a specific part of my composable? // A: Use the 'key' parameter in the capturable modifier and pass the key to captureAsync(). ``` -------------------------------- ### Example: Timeout Protection (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Shows how to set a timeout for capture operations to prevent them from running indefinitely. ```kotlin val controller = rememberCaptureController() controller.captureAsync(timeout = 5000L).invokeOnCompletion { if (it is CancellationException) Log.w("Capture", "Timeout") } ``` -------------------------------- ### Example: Retry Logic (API File) Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Implements retry logic for capture operations that might fail intermittently. ```kotlin fun retryCapture(controller: CaptureController, times: Int = 3) { controller.captureAsync(onError = { if (times > 0) retryCapture(controller, times - 1) else Log.e("Capture", "Failed after retries") }) } val controller = rememberCaptureController() Button(onClick = { retryCapture(controller) }) { Text("Retry Capture") } ``` -------------------------------- ### Initialize and Use CaptureController Source: https://github.com/patilshreyas/capturable/blob/master/docs/capturable/dev.shreyaspatil.capturable/capturable.html Demonstrates how to initialize a CaptureController and use the capturable Modifier to make a Composable content capture-able. Includes an example of capturing the Composable content to a Bitmap. ```kotlin val captureController = rememberCaptureController() val uiScope = rememberCoroutineScope() // The content to be captured in to Bitmap Column( modifier = Modifier.capturable(captureController), ) { // Composable content } Button(onClick = { // Capture content val bitmapAsync = captureController.captureAsync() try { val bitmap = bitmapAsync.await() // Do something with `bitmap`. } catch (error: Throwable) { // Error occurred, do something. } }) { ... } ``` -------------------------------- ### Complete Example Application Flow Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/api-reference/rememberCaptureController.md Demonstrates how to use `rememberCaptureController` to capture a composable's content. It includes a button to trigger the capture and displays the resulting bitmap. Ensure the composable you wish to capture is marked with the `.capturable()` modifier. ```kotlin import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.patilshreyas.capturable.capture.rememberCaptureController import com.patilshreyas.capturable.modifier.capturable import kotlinx.coroutines.launch import android.util.Log @Composable fun CompleteExample() { val captureController = rememberCaptureController() val scope = rememberCoroutineScope() var capturedBitmap: ImageBitmap? by remember { mutableStateOf(null) } var isLoading by remember { mutableStateOf(false) } Column( modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { // Capturable content Box( modifier = Modifier .capturable(captureController) .fillMaxWidth() .height(200.dp) .background(Color.LightGray) ) { Text("Capture this content", modifier = Modifier.align(Alignment.Center)) } Spacer(modifier = Modifier.height(16.dp)) // Capture button with loading state Button( enabled = !isLoading, onClick = { scope.launch { isLoading = true try { capturedBitmap = captureController.captureAsync().await() } catch (e: Exception) { Log.e("Capture", "Failed", e) } finally { isLoading = false } } } ) { Text(if (isLoading) "Capturing..." else "Capture") } // Display result capturedBitmap?.let { Image(bitmap = it, contentDescription = "Captured content") } } } ``` -------------------------------- ### Example Usage of CaptureController Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/api-reference/CaptureController.md Demonstrates how to use CaptureController within a Composable function to capture UI content. It shows capturing an ImageBitmap on a button click and handling potential errors. ```kotlin @Composable fun TicketScreen() { val captureController = rememberCaptureController() val scope = rememberCoroutineScope() var capturedImage: ImageBitmap? by remember { mutableStateOf(null) } Column(modifier = Modifier.capturable(captureController)) { // Content to be captured Text("Ticket Content") } Button(onClick = { scope.launch { try { val bitmap = captureController.captureAsync().await() capturedImage = bitmap // Use the bitmap (save, display, share, etc.) } catch (error: Throwable) { // Handle capture errors Log.e("Capture", "Failed to capture", error) } } }) { Text("Capture") } } ``` -------------------------------- ### Save ImageBitmap to File Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/types.md Provides a complete example of capturing an ImageBitmap, converting it to an Android Bitmap, and saving it to a file in PNG format. ```kotlin val imageBitmap = captureController.captureAsync().await() val androidBitmap = imageBitmap.asAndroidBitmap() // Save to file val file = File(context.cacheDir, "capture.png") file.outputStream().use { output -> androidBitmap.compress(Bitmap.CompressFormat.PNG, 100, output) } ``` -------------------------------- ### GitHub Actions CI/CD - Run Unit Tests Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/testing.md Example of a GitHub Actions workflow step to run unit tests using the Gradle wrapper. This ensures unit tests are executed as part of the CI pipeline. ```yaml # GitHub Actions example - name: Run unit tests run: ./gradlew test ``` -------------------------------- ### Fallback Content Strategy Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/errors.md Implement fallback content when an image capture fails. This example shows how to conditionally display placeholder content or the captured image based on the success of the capture operation. ```kotlin var capturedBitmap: ImageBitmap? by remember { mutableStateOf(null) } var useFallback by remember { mutableStateOf(false) } scope.launch { try { capturedBitmap = captureController.captureAsync().await() useFallback = false } catch (e: Exception) { useFallback = true Log.e("Capture", "Using fallback content", e) } } if (useFallback || capturedBitmap == null) { // Show fallback placeholder or retry option PlaceholderContent() } else { Image(bitmap = capturedBitmap!!, contentDescription = "Captured") } ``` -------------------------------- ### Logging and Monitoring Strategy Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/errors.md Log capture events for monitoring and debugging purposes. This example logs the success or failure of a capture, including details like duration, bitmap dimensions, or error information. ```kotlin scope.launch { try { val startTime = System.currentTimeMillis() val bitmap = captureController.captureAsync().await() val duration = System.currentTimeMillis() - startTime // Log successful capture analyticsLogger.log("capture_success", mapOf( "duration_ms" to duration, "bitmap_width" to bitmap.width, "bitmap_height" to bitmap.height )) } catch (e: Exception) { // Log failure analyticsLogger.log("capture_failure", mapOf( "error_type" to e::class.simpleName, "error_message" to e.message )) } } ``` -------------------------------- ### GitHub Actions CI/CD - Run Instrumented Tests Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/testing.md Example of a GitHub Actions workflow step to run instrumented tests using a dedicated Android emulator runner. This integrates instrumented testing into the CI process. ```yaml - name: Run instrumented tests uses: reactivecircus/android-emulator-runner@v2 with: api-level: 31 script: ./gradlew connectedAndroidTest ``` -------------------------------- ### Unit Testing CaptureController Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Example of unit testing the CaptureController's functionality. This involves mocking dependencies and verifying behavior. ```kotlin @Test fun testCaptureAsyncCompletes() = runTest { val controller = CaptureController() // Mock necessary dependencies if any val job = launch { controller.captureAsync().await() } // Assertions about the capture process or result job.cancel() } ``` -------------------------------- ### Mocking GraphicsLayer with MockK Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/testing.md Mock the GraphicsLayer dependency when you need to isolate the CaptureController for unit testing. This example shows how to mock the `toImageBitmap()` method. ```kotlin val mockGraphicsLayer = mockk() every { mockGraphicsLayer.toImageBitmap() } returns mockk() val controller = CaptureController(mockGraphicsLayer) ``` -------------------------------- ### CaptureController API Reference Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/INDEX.md Provides details on the main API, including class overview, constructor, public methods, properties, and internal classes like CaptureRequest. It also covers thread safety, lifecycle, and usage examples. ```APIDOC ## CaptureController ### Description Documentation for the main API surface, detailing its class overview, constructor, public methods, and properties. Includes information on internal classes, thread safety, lifecycle, and usage examples. ### Class Overview Details about the `CaptureController` class. ### Constructor Information on how to instantiate `CaptureController`. ### Public Methods Documentation for all callable methods of `CaptureController`. ### Public Properties Details on accessible properties of `CaptureController`. ### Internal Classes Information on internal classes such as `CaptureRequest`. ### Thread Safety and Lifecycle Guidance on thread safety and the lifecycle management of `CaptureController`. ### Usage Examples Illustrative examples of how to use `CaptureController`. ``` -------------------------------- ### Debug Logging for Capture Operations Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/errors.md Enable detailed logging to troubleshoot capture requests. This snippet demonstrates logging the start, success with dimensions, and failure with stack trace details. ```kotlin scope.launch { try { Log.d("Capture", "Starting capture request") val bitmap = captureController.captureAsync().await() Log.d("Capture", "Capture completed: ${bitmap.width}x${bitmap.height}") } catch (e: Exception) { Log.e("Capture", "Capture failed at: ${e.stackTrace.take(5)}") e.printStackTrace() } } ``` -------------------------------- ### Basic Capture with Button Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Demonstrates how to capture a composable when a button is clicked. Requires a CaptureController. ```kotlin val controller = rememberCaptureController() Button( onClick = { // Capture the composable controller.captureAsync() } ) { Text("Capture") } // The composable to be captured MyComposable(modifier = Modifier.capturable(controller)) ``` -------------------------------- ### Run All Unit Tests Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/testing.md Execute all unit tests for the project using the Gradle wrapper. This is the standard command for running the entire unit test suite. ```bash ./gradlew test ``` -------------------------------- ### Capture ImageBitmap from Controller Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/types.md Demonstrates how to capture an ImageBitmap using the captureAsync() and await() methods from a CaptureController. ```kotlin val imageBitmap: ImageBitmap = captureController.captureAsync().await() ``` -------------------------------- ### rememberCaptureController API Reference Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/INDEX.md Covers the `rememberCaptureController` function, including its signature, return type, lifecycle, implementation details, and complete application flow examples. ```APIDOC ## rememberCaptureController ### Description Documentation for the `rememberCaptureController` function, detailing its signature, return type, and lifecycle. Includes implementation details and examples of complete application flows. ### Function Signature Details on the expected signature of `rememberCaptureController`. ### Return Type and Lifecycle Explanation of the return type and lifecycle management associated with the controller. ### Usage Examples Multiple examples demonstrating the use of `rememberCaptureController`. ### Implementation Details Insights into the internal workings of the function. ### Complete Application Flow Examples illustrating how to integrate `rememberCaptureController` into a full application flow. ``` -------------------------------- ### Capture Composable with Error Handling Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/api-reference/capturable-modifier.md Capture a composable to a bitmap using captureAsync and handle potential exceptions. This example demonstrates capturing a Card composable. ```kotlin Box( modifier = Modifier .capturable(captureController) .size(200.dp) ) { Card { Text("Capture Me") } } Button(onClick = { scope.launch { try { val bitmap = captureController.captureAsync().await() // Process captured bitmap } catch (e: Exception) { // Handle error } } }) { Text("Capture Card") } ``` -------------------------------- ### Architecture: Project Structure Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Overview of the project's directory structure and module organization. ```kotlin // Example structure: // - capturable/ (library module) // - src/main/java/dev/shreyaspatil/capturable/ // - app/ (example app module) // - src/main/java/dev/shreyaspatil/capturableExample/ ``` -------------------------------- ### Error Handling Pattern: Specific Exception Handling Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Provides an example of handling specific exception types that might occur during capture, allowing for tailored responses. ```kotlin controller.captureAsync(onError = { when (it) { is TimeoutCancellationException -> { // Handle timeout specifically } is SecurityException -> { // Handle permission issues } else -> { // Handle general errors } } }) ``` -------------------------------- ### Share Captured Image Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Provides an example of sharing the captured image using the platform's sharing capabilities. This typically involves converting the bitmap to a URI. ```kotlin val controller = rememberCaptureController() val context = LocalContext.current Button( onClick = { controller.captureAsync().let { val uri = saveBitmapToCache(context, it, "share_capture.png") shareImageUri(context, uri) } } ) { Text("Share Image") } MyComposable(modifier = Modifier.capturable(controller)) ``` -------------------------------- ### Mock Strategies for Capture Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Discusses different strategies for mocking dependencies when testing components that utilize Capturable. ```kotlin // Mocking frameworks like MockK or Mockito can be used. // Focus on mocking the CaptureController and its methods. ``` -------------------------------- ### Basic Capture with Jetpack Compose Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/README.md Demonstrates how to capture a composable UI element using the `capturable` modifier and `rememberCaptureController`. Use this for simple image capture scenarios. ```kotlin import androidx.compose.foundation.layout.Column import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import com.patilshreyas.capturable.rememberCaptureController import com.patilshreyas.capturable.capturable import kotlinx.coroutines.launch @Composable fun Example() { val controller = rememberCaptureController() val scope = rememberCoroutineScope() var bitmap: ImageBitmap? by remember { mutableStateOf(null) } Column(modifier = Modifier.capturable(controller)) { Text("Capture me") } Button(onClick = { scope.launch { bitmap = controller.captureAsync().await() } }) { Text("Capture") } } ``` -------------------------------- ### Testing Capture Best Practices Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Outlines best practices for writing effective tests for capture functionality, focusing on reliability and maintainability. ```kotlin // Best practices include: // - Testing edge cases (e.g., empty composables, large content). // - Verifying capture results against known expected outputs. // - Ensuring tests are fast and deterministic. ``` -------------------------------- ### CI/CD Integration Notes Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Considerations for integrating Capturable tests into a Continuous Integration and Continuous Deployment pipeline. ```kotlin // Ensure test environments are correctly configured. // Monitor test results and failures closely. ``` -------------------------------- ### Saving Captured Bitmap to a File Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/README.md Illustrates how to capture a UI element and save the resulting bitmap to a PNG file in the application's cache directory. Ensure necessary file I/O permissions and context are available. ```kotlin import android.graphics.Bitmap import androidx.compose.ui.graphics.asAndroidBitmap import com.patilshreyas.capturable.rememberCaptureController import kotlinx.coroutines.launch import java.io.File // Assuming 'controller', 'scope', and 'context' are available // scope.launch { // val bitmap = controller.captureAsync().await() // val file = File(context.cacheDir, "capture.png") // file.outputStream().use { // output -> // bitmap.asAndroidBitmap() // .compress(Bitmap.CompressFormat.PNG, 100, output) // } // } ``` -------------------------------- ### Initialize Capture Controller Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/architecture.md Initializes the CaptureController by creating a GraphicsLayer and setting up a Channel for capture requests. The Channel is used to create a Flow for receiving capture requests. ```kotlin rememberCaptureController() ├─ rememberGraphicsLayer() │ └─ Creates GraphicsLayer from Compose framework └─ remember(graphicsLayer) └─ Creates CaptureController(graphicsLayer) └─ Initializes Channel(UNLIMITED) └─ Creates Flow via receiveAsFlow() ``` -------------------------------- ### Capture with Loading State Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Illustrates how to manage a loading state during the capture process. The UI can indicate that a capture is in progress. ```kotlin val controller = rememberCaptureController() var isLoading by remember { mutableStateOf(false) } Button( onClick = { isLoading = true controller.captureAsync().invokeOnCompletion { isLoading = false } } ) { if (isLoading) { CircularProgressIndicator() } else { Text("Capture") } } MyComposable(modifier = Modifier.capturable(controller)) ``` -------------------------------- ### Initialize CaptureController Source: https://github.com/patilshreyas/capturable/blob/master/docs/capturable/dev.shreyaspatil.capturable.controller/remember-capture-controller.html Creates and remembers a CaptureController instance. This is a Composable function. ```kotlin rememberCaptureController() ``` -------------------------------- ### Auto Capture with LaunchedEffect Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/api-reference/rememberCaptureController.md Demonstrates an automatic capture scenario using rememberCaptureController() and LaunchedEffect. The capture is initiated immediately upon the Composable's composition, storing the result in 'initialCapture'. ```kotlin @Composable fun AutoCapture() { val captureController = rememberCaptureController() var initialCapture: ImageBitmap? by remember { mutableStateOf(null) } Box(modifier = Modifier.capturable(captureController)) { Text("Content") } LaunchedEffect(Unit) { // Capture immediately on composition initialCapture = captureController.captureAsync().await() } } ``` -------------------------------- ### Basic Capture with Button Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/usage-patterns.md Capture a composable UI element when a button is clicked. Requires a CaptureController and a CoroutineScope. ```kotlin @Composable fun BasicCapture() { val captureController = rememberCaptureController() val scope = rememberCoroutineScope() var bitmap: ImageBitmap? by remember { mutableStateOf(null) } Column(horizontalAlignment = Alignment.CenterHorizontally) { // Content to capture Box( modifier = Modifier .capturable(captureController) .size(200.dp) .background(Color.LightGray) ) { Text("Capture Me", modifier = Modifier.align(Alignment.Center)) } Spacer(modifier = Modifier.height(16.dp)) // Capture button Button(onClick = { scope.launch { bitmap = captureController.captureAsync().await() } }) { Text("Capture") } // Show result bitmap?.let { img -> Image(bitmap = img, contentDescription = "Captured") } } } ``` -------------------------------- ### Error Handling: Logging Techniques Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Provides guidance on effective logging practices for capture-related errors. ```kotlin // Use Log.e, Log.w, or a dedicated logging framework. // Include stack traces and relevant context in log messages. ``` -------------------------------- ### rememberCaptureController() Lifecycle Documentation Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Explains the lifecycle management of the CaptureController instance returned by rememberCaptureController. ```kotlin // The controller is automatically managed by Compose's remember mechanism, // ensuring it persists across recompositions and is disposed of correctly. ``` -------------------------------- ### CaptureController Constructor Source: https://github.com/patilshreyas/capturable/blob/master/docs/capturable/dev.shreyaspatil.capturable.controller/-capture-controller/-capture-controller.html Initializes a new instance of the CaptureController class. ```APIDOC ## CaptureController() ### Description Initializes a new instance of the CaptureController class. ### Method constructor ### Parameters This constructor does not take any parameters. ``` -------------------------------- ### CaptureController.captureAsync() with Parameters Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Captures a specific part of a composable using a key, or applies specific capture options like format and quality. ```kotlin val bitmap = controller.captureAsync("section1", format = Bitmap.CompressFormat.PNG, quality = 0.8f).await() ``` -------------------------------- ### Capturable Constructor Source: https://github.com/patilshreyas/capturable/blob/master/docs/navigation.html Documentation for the main Capturable constructor. ```APIDOC ## Capturable() ### Description Initializes a new instance of the Capturable class. ### Method Capturable() ### Endpoint N/A (Constructor) ``` -------------------------------- ### CaptureController Method Reference Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Lists and describes the methods available on the CaptureController class, including captureAsync and any other utility methods. ```kotlin // Methods include: // - captureAsync(...): Deferred // - cancelCapture() // - ... other methods ``` -------------------------------- ### Testing Best Practices Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Best practices for writing robust and maintainable tests for the Capturable library. ```kotlin // Ensure tests are independent, repeatable, and cover edge cases. // Use clear and descriptive test names. ``` -------------------------------- ### Experimental Compose UI APIs Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/architecture.md These are experimental APIs that may change in future Compose releases. They are used for capturing composable content. ```kotlin @ExperimentalComposeUiApi fun Modifier.capturable(...) ``` ```kotlin @OptIn(ExperimentalCoroutinesApi::class) private suspend fun observeCaptureRequestsAndServe() { currentController .flatMapLatest { it.captureRequests } // EXPERIMENTAL .collect { request -> ... } } ``` -------------------------------- ### Capture Controller with Capture Logic Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/api-reference/rememberCaptureController.md Shows how to integrate rememberCaptureController() with capture logic triggered by a button click. It uses rememberCoroutineScope() to launch a coroutine for capturing the bitmap asynchronously and handles potential errors. ```kotlin @Composable fun CaptureWithButton() { val captureController = rememberCaptureController() val scope = rememberCoroutineScope() var bitmap: ImageBitmap? by remember { mutableStateOf(null) } Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(16.dp) ) { // Content to be captured Card(modifier = Modifier.capturable(captureController)) { Column(modifier = Modifier.padding(16.dp)) { Text("Movie Ticket") Text("Screen: JET 01") Text("Seats: A1, A2, A3") } } Spacer(modifier = Modifier.height(16.dp)) // Capture button Button(onClick = { scope.launch { try { bitmap = captureController.captureAsync().await() } catch (error: Exception) { Log.e("Capture", "Error: ${error.message}") } } }) { Text("Capture Ticket") } // Display captured bitmap bitmap?.let { image -> Image(bitmap = image, contentDescription = "Captured ticket") } } } ``` -------------------------------- ### Error Handling: Recovery Strategies Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Outlines strategies for recovering from capture errors. ```kotlin // Strategies: Retry the capture, inform the user, use a fallback image, or cancel the operation. ``` -------------------------------- ### Architecture: High-Level Overview Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt A high-level explanation of the Capturable library's architecture and its core components. ```kotlin // Core components: CaptureController, capturable Modifier, and underlying platform capture APIs. ``` -------------------------------- ### captureAsync Source: https://github.com/patilshreyas/capturable/blob/master/docs/capturable/dev.shreyaspatil.capturable.controller/-capture-controller/index.html Creates and requests for a Bitmap capture asynchronously with a specified configuration, returning an ImageBitmap. ```APIDOC ## captureAsync ### Description Creates and requests for a Bitmap capture asynchronously with a specified configuration, returning an ImageBitmap. ### Method Signature `fun captureAsync(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Deferred` ### Parameters - **config** (Bitmap.Config) - Optional - The configuration for the Bitmap capture. Defaults to `Bitmap.Config.ARGB_8888`. ### Returns - **Deferred** - An asynchronous computation that will eventually yield an ImageBitmap. ``` -------------------------------- ### Capture UI at Different Animation Frames Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/usage-patterns.md Use this pattern to capture multiple snapshots of a UI element as it animates. Ensure sufficient delay between captures to allow the animation to progress. ```kotlin import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size import androidx.compose.material.Button import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.unit.dp import kotlinx.coroutines.delay import kotlinx.coroutines.launch import com.github.capturable.rememberCaptureController import com.github.capturable.capturable @Composable fun AnimatedCapture() { val captureController = rememberCaptureController() val scope = rememberCoroutineScope() var progress by remember { mutableStateOf(0f) } var captures: List by remember { mutableStateStateOf(emptyList()) } Column { Box( modifier = Modifier .capturable(captureController) .size(200.dp) .background(Color.Blue) ) { Box( modifier = Modifier .size((200 * progress).dp) .background(Color.Red) ) } Button(onClick = { scope.launch { captures = emptyList() // Capture at multiple points during animation repeat(5) { step -> progress = step / 4f delay(200) // Let animation complete val bitmap = captureController.captureAsync().await() captures = captures + bitmap } } }) { Text("Capture Animation") } } } ``` -------------------------------- ### Android Build Configuration for Mocking Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/testing.md Configure your `android` block in `build.gradle` to enable `returnDefaultValues` for unit tests. This is crucial for mocking Jetpack Compose components. ```gradle android { testOptions { unitTests.returnDefaultValues = true // Required for mocked Compose } } ``` -------------------------------- ### Architecture: Lifecycle Events Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Explains how the library integrates with and responds to Compose lifecycle events. ```kotlin // CaptureController instances managed by remember are tied to composable lifecycle. // Capture operations should be cancelled if the composable is disposed. ``` -------------------------------- ### Save Capture to File Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Demonstrates saving the captured composable content directly to a file. This involves handling file I/O operations. ```kotlin val controller = rememberCaptureController() Button( onClick = { controller.captureAsync().let { bitmap -> // Save bitmap to file (implementation depends on platform) saveBitmapToFile(bitmap, "capture.png") } } ) { Text("Save to File") } MyComposable(modifier = Modifier.capturable(controller)) ``` -------------------------------- ### Multiple Sections Capture Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Shows how to capture multiple distinct composable sections simultaneously. Each section needs to be marked as capturable. ```kotlin val controller = rememberCaptureController() Row { Column(Modifier.capturable(controller, "section1")) { // Content for section 1 } Column(Modifier.capturable(controller, "section2")) { // Content for section 2 } } // To capture both sections: controller.captureAsync(listOf("section1", "section2")) ``` -------------------------------- ### ImageBitmap Lifecycle Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/architecture.md Describes the creation, usage, and garbage collection of ImageBitmaps generated by `toImageBitmap()`. ```text graphicsLayer.toImageBitmap() ↓ New ImageBitmap created (allocates memory) ↓ Returned to caller ↓ When no longer referenced ↓ Garbage collected (memory freed) ``` -------------------------------- ### Basic Error Handling with Try-Catch Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/errors.md Implement a basic try-catch block to handle potential exceptions during the capture process. This pattern logs errors and displays a user-friendly message. ```kotlin import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Box import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.foundation.layout.size import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import android.util.Log import com.example.capturable.rememberCaptureController import com.example.capturable.capturable @Composable fun CaptureWithErrorHandling() { val captureController = rememberCaptureController() val scope = rememberCoroutineScope() var errorMessage by remember { mutableStateOf(null) } var bitmap by remember { mutableStateOf(null) } Column { Box(modifier = Modifier.capturable(captureController)) { Text("Content to capture") } Button(onClick = { scope.launch { try { bitmap = captureController.captureAsync().await() errorMessage = null } catch (e: Exception) { errorMessage = "Failed to capture: ${e.message}" Log.e("Capture", "Error", e) } } }) { Text("Capture") } errorMessage?.let { Text(it, color = Color.Red) } } } ``` -------------------------------- ### CaptureController Constructor Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Initializes a new instance of CaptureController. This controller manages the capture process for composables. ```kotlin val controller = CaptureController() ``` -------------------------------- ### Architecture: Design Patterns Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Highlights the design patterns employed within the Capturable library. ```kotlin // Patterns might include Observer, Strategy (for capture formats), and Dependency Injection. ``` -------------------------------- ### Batch Capture Multiple Items Sequentially Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/usage-patterns.md Capture multiple composable elements in sequence using a list of capture controllers. This pattern is useful when you need to capture several distinct areas of your UI as separate images in a single operation. ```kotlin import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import com.patilshreyas.capturable.captureController.CaptureController import com.patilshreyas.capturable.rememberCaptureController import com.patilshreyas.capturable.capturable import kotlinx.coroutines.launch import android.util.Log @Composable fun BatchCapture() { val controllers = List(3) { rememberCaptureController() } val scope = rememberCoroutineScope() var results: List by remember { mutableStateOf(emptyList()) } var isProcessing by remember { mutableStateOf(false) } Column { items.forEachIndexed { index, item -> Box( modifier = Modifier .capturable(controllers[index]) .background(Color.LightGray) ) { Text(item) } } Button( enabled = !isProcessing, onClick = { scope.launch { isProcessing = true results = captureAllSequentially(controllers) isProcessing = false } } ) { Text(if (isProcessing) "Processing..." else "Capture All") } } } private suspend fun captureAllSequentially( controllers: List ): List { return controllers.mapNotNull { controller -> try { controller.captureAsync().await() } catch (e: Exception) { Log.w("Batch", "Failed to capture item", e) null } } } ``` -------------------------------- ### captureAsync Source: https://github.com/patilshreyas/capturable/blob/master/docs/capturable/dev.shreyaspatil.capturable.controller/-capture-controller/capture-async.html Creates and requests for a Bitmap capture with specified config and returns an ImageBitmap asynchronously. This method is safe to be called from the "main" thread directly. Make sure to call this method as a part of callback function and not as a part of the Composable function itself. ```APIDOC ## captureAsync ### Description Creates and requests for a Bitmap capture with specified config and returns an ImageBitmap asynchronously. This method is safe to be called from the "main" thread directly. Make sure to call this method as a part of callback function and not as a part of the Composable function itself. ### Method fun captureAsync(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Deferred ### Parameters #### Path Parameters - **config** (Bitmap.Config) - Optional - Bitmap config of the desired bitmap. Defaults to Bitmap.Config.ARGB_8888 ``` -------------------------------- ### Testing Capture Configuration Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Tests how different capture configurations (e.g., format, quality) affect the output. Ensures configuration options work as expected. ```kotlin val controller = CaptureController() val bitmap = controller.captureAsync(format = Bitmap.CompressFormat.JPEG, quality = 0.5f).await() // Assert properties of the resulting bitmap or its file representation ``` -------------------------------- ### Composable Function with Capture Controller Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/architecture.md Demonstrates how to use the `rememberCaptureController` within a Composable function to manage capture operations. ```kotlin @Composable fun MyScreen() { val controller = rememberCaptureController() } ``` -------------------------------- ### Run Specific Unit Tests Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/testing.md Execute specific unit tests by providing the test class name to the Gradle wrapper. This allows for focused testing of individual components. ```bash ./gradlew test --tests CaptureControllerTest ``` -------------------------------- ### Mocking ImageBitmap with MockK Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/testing.md Mock an ImageBitmap to control its properties like width and height during tests. This is useful when the capture process depends on these dimensions. ```kotlin val mockBitmap = mockk() every { mockBitmap.width } returns 100 every { mockBitmap.height } returns 100 ``` -------------------------------- ### Architecture: Component Interactions Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Describes how the main components of the library interact with each other. ```kotlin // CaptureController manages capture requests. // Modifier registers composables with the controller. // Platform APIs perform the actual bitmap capture. ``` -------------------------------- ### capture Source: https://github.com/patilshreyas/capturable/blob/master/docs/capturable/dev.shreyaspatil.capturable.controller/-capture-controller/index.html Creates and sends a Bitmap capture request with a specified configuration. The capture is performed synchronously. ```APIDOC ## capture ### Description Creates and sends a Bitmap capture request with a specified configuration. The capture is performed synchronously. ### Method Signature `fun capture(config: Bitmap.Config = Bitmap.Config.ARGB_8888)` ### Parameters - **config** (Bitmap.Config) - Optional - The configuration for the Bitmap capture. Defaults to `Bitmap.Config.ARGB_8888`. ``` -------------------------------- ### Capture UI Based on Current State Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/usage-patterns.md Use this pattern to capture a snapshot of the UI corresponding to its current state. This is useful for debugging or saving specific UI configurations. ```kotlin import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size import androidx.compose.material.Button import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.unit.dp import kotlinx.coroutines.delay import kotlinx.coroutines.launch import com.github.capturable.rememberCaptureController import com.github.capturable.capturable @Composable fun StateBasedCapture() { val controller = rememberCaptureController() val scope = rememberCoroutineScope() var uiState by remember { mutableStateOf(UiState.Empty) } var capture: ImageBitmap? by remember { mutableStateOf(null) } Column { Box(modifier = Modifier.capturable(controller)) { when (uiState) { UiState.Empty -> Text("Empty state") UiState.Loading -> CircularProgressIndicator() is UiState.Success -> Text((uiState as UiState.Success).data) is UiState.Error -> Text("Error: ${(uiState as UiState.Error).message}") } } Row { Button(onClick = { uiState = UiState.Empty }) { Text("Empty") } Button(onClick = { uiState = UiState.Loading }) { Text("Loading") } Button(onClick = { uiState = UiState.Success("Data") }) { Text("Success") } Button(onClick = { uiState = UiState.Error("Oops") }) { Text("Error") } } Button(onClick = { scope.launch { capture = controller.captureAsync().await() } }) { Text("Capture State") } } } sealed class UiState { object Empty : UiState() object Loading : UiState() data class Success(val data: String) : UiState() data class Error(val message: String) : UiState() } ``` -------------------------------- ### Mocking CaptureController for Tests Source: https://github.com/patilshreyas/capturable/blob/master/_autodocs/MANIFEST.txt Demonstrates strategies for mocking the CaptureController in tests to isolate the component under test and control its behavior. ```kotlin val mockController = mockk() // Stubbing behavior alternative(mockController) { every { mockController.captureAsync() } returns CompletableDeferred(mockBitmap) } // Use mockController in your composable test setup ```