### Implement Screenshot Protection with DisposableEffect
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
This snippet shows how to combine FLAG_SECURE and setSkipScreenshot within a DisposableEffect. It applies protections on resume and clears them on dispose. Ensure the necessary lifecycleOwner, view, and skipScreenshot state are available.
```kotlin
DisposableEffect(lifecycleOwner, view, skipScreenshot) {
// 1. FLAG_SECURE: prevents system screenshot gesture & shows black in recents
if (skipScreenshot) {
window?.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
} else {
window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
var isApplied = false
// 2. setSkipScreenshot: applied on the first draw after resume
val onDrawListener = ViewTreeObserver.OnDrawListener {
if (isApplied) return@OnDrawListener
try {
val sc = HiddenApiBridge.View_getViewRootImpl(view)?.surfaceControl
if (sc?.isValid == true) {
SurfaceControl.Transaction().use { tx ->
HiddenApiBridge.SurfaceControl_setSkipScreenshot(tx, sc, skipScreenshot).apply()
isApplied = true
Log.v(App.TAG, "Applying skip-screenshot: $skipScreenshot")
}
}
} catch (e: Exception) {
Log.e(App.TAG, "Failed to apply skip-screenshot", e)
}
}
// 3. Lifecycle observer: register/unregister the draw listener on resume/pause
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
isApplied = false
view.viewTreeObserver.addOnDrawListener(onDrawListener)
}
Lifecycle.Event.ON_PAUSE -> {
view.viewTreeObserver.removeOnDrawListener(onDrawListener)
}
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(observer)
// 4. Cleanup: remove observer, remove draw listener, clear both protections
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
view.viewTreeObserver.removeOnDrawListener(onDrawListener)
window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
try {
val sc = HiddenApiBridge.View_getViewRootImpl(view)?.surfaceControl
if (sc?.isValid == true) {
SurfaceControl.Transaction().use { tx ->
HiddenApiBridge.SurfaceControl_setSkipScreenshot(tx, sc, false).apply()
}
}
} catch (e: Exception) {
Log.e(App.TAG, "Failed to clear skip-screenshot", e)
}
}
}
```
--------------------------------
### Mark Surface Layer to Skip Screen Capture with SurfaceControl_setSkipScreenshot
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
Wraps the hidden `SurfaceControl.Transaction.setSkipScreenshot()` method to exclude a surface layer from screen capture or recording. This protection is independent of `FLAG_SECURE`. The change must be committed by calling `.apply()` on the transaction.
```java
// hiddenapi/bridge/src/main/java/hidden/HiddenApiBridge.java
public class HiddenApiBridge {
/**
* @param transaction An open SurfaceControl.Transaction (use try-with-resources).
* @param sc The SurfaceControl obtained from ViewRootImpl.
* @param skipScreenshot true = exclude surface from capture; false = allow capture.
* @return The same transaction for chaining.
*/
public static SurfaceControl.Transaction SurfaceControl_setSkipScreenshot(
SurfaceControl.Transaction transaction,
SurfaceControl sc,
boolean skipScreenshot) {
return transaction.setSkipScreenshot(sc, skipScreenshot);
}
}
```
```kotlin
// Usage in Kotlin — apply inside an OnDrawListener so the SurfaceControl is valid:
val sc = HiddenApiBridge.View_getViewRootImpl(view)?.surfaceControl
if (sc?.isValid == true) {
SurfaceControl.Transaction().use { tx ->
HiddenApiBridge.SurfaceControl_setSkipScreenshot(tx, sc, true).apply()
// Surface layer is now excluded from screenshots and screen recordings
}
}
```
--------------------------------
### Retrieve ViewRootImpl from a View using HiddenApiBridge
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
Wraps the hidden `View.getViewRootImpl()` method to access the `ViewRootImpl` associated with a view. This is necessary to obtain the root `SurfaceControl` for the window. The bridge module compiles against private stubs, and the LSPass exemption enables runtime execution.
```java
// hiddenapi/bridge/src/main/java/hidden/HiddenApiBridge.java
public class HiddenApiBridge {
/**
* @param view Any view attached to a window.
* @return The ViewRootImpl, or null if the view is not yet attached.
*/
public static ViewRootImpl View_getViewRootImpl(View view) {
return view.getViewRootImpl(); // hidden API — accessible via LSPass exemption
}
}
```
```kotlin
// Usage in Kotlin (MainActivity.kt):
val viewRootImpl = HiddenApiBridge.View_getViewRootImpl(view)
val surfaceControl = viewRootImpl?.surfaceControl // SurfaceControl for the window surface
```
--------------------------------
### VerticalBeforeAfterSlider Composable for Image Comparison
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
A full-screen composable that overlays two images, allowing the top image to be clipped by a vertically draggable boundary. It reports the normalized progress of the drag via `onProgressChange`. Handles intrinsic image scaling internally.
```kotlin
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.GenericShape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import kotlin.math.min
@Composable
fun VerticalBeforeAfterSlider(
topImage: Painter,
bottomImage: Painter,
modifier: Modifier = Modifier,
onProgressChange: (Float) -> Unit = {} // 0.0 = top, 1.0 = bottom
) {
var offsetY by remember { mutableFloatStateOf(1.0f) }
BoxWithConstraints(modifier = modifier.fillMaxSize()) {
// Compute actual rendered image bounds (accounting for Fit scaling)
val scale = minOf(
constraints.maxWidth / topImage.intrinsicSize.width,
constraints.maxHeight / topImage.intrinsicSize.height
)
val imageRect = Rect(
left = 0f,
top = (constraints.maxHeight - topImage.intrinsicSize.height * scale) / 2f,
right = topImage.intrinsicSize.width * scale,
bottom = (constraints.maxHeight + topImage.intrinsicSize.height * scale) / 2f
)
// Bottom (reference) image — always fully visible
Image(painter = bottomImage, contentDescription = "Bottom Image",
modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Fit)
// Top (overlay) image — clipped to offsetY fraction of image height
Image(
painter = topImage,
contentDescription = "Top Image",
modifier = Modifier.fillMaxSize().graphicsLayer {
clip = true
shape = GenericShape { size, _ ->
addRect(Rect(0f, 0f, size.width, imageRect.top + imageRect.height * offsetY))
}
},
contentScale = ContentScale.Fit
)
// Drag handle — full-width touch target, visually a rounded pill
Box(
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
.align(Alignment.TopCenter)
.offset(y = with(LocalDensity.current) {
(imageRect.top + imageRect.height * offsetY).toDp() - 24.dp
})
.pointerInput(imageRect) {
detectDragGestures { change, dragAmount ->
change.consume()
val newOffset = (offsetY + dragAmount.y / imageRect.height).coerceIn(0f, 1f)
if (newOffset != offsetY) {
offsetY = newOffset
onProgressChange(offsetY)
}
}
},
contentAlignment = Alignment.Center
) {
Box(Modifier.width(60.dp).height(5.dp)
.background(Color.White.copy(alpha = 0.7f), RoundedCornerShape(100)))
}
}
}
```
```kotlin
// Typical usage in a Scaffold:
VerticalBeforeAfterSlider(
topImage = painterResource(R.drawable.before_image),
bottomImage = painterResource(R.drawable.after_image),
modifier = Modifier.padding(innerPadding),
onProgressChange = { progress ->
// progress == 1.0 → top image fully visible (screenshot allowed)
// progress <= 0.75 → trigger screenshot protection
screenshotProtectionEnabled = (progress * 100).toInt() <= 75
}
)
```
--------------------------------
### Status Composable for Screenshot Protection Indicator
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
A minimal composable that displays the screenshot protection status as a human-readable string based on the slider's progress. Useful for debugging and understanding the protection state.
```kotlin
@Composable
fun Status(progress: Float, modifier: Modifier = Modifier) {
// Protection is active when the top image covers <= 75% of its height
val skipScreenshot = (progress * 100).toInt() <= 75
Text(
text = "skipScreenshot=$skipScreenshot",
modifier = modifier
)
}
// Typical output:
// Slider at 100% (fully down) → "skipScreenshot=false"
// Slider at 75% → "skipScreenshot=true"
// Slider at 0% (fully up) → "skipScreenshot=true"
```
--------------------------------
### HiddenApiBridge.View_getViewRootImpl
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
Retrieves the ViewRootImpl associated with a given View. This is a wrapper for the hidden `View.getViewRootImpl()` method, enabling access to the window's root SurfaceControl.
```APIDOC
## `HiddenApiBridge.View_getViewRootImpl` — Retrieve ViewRootImpl from a View
### Description
Wraps the hidden `View.getViewRootImpl()` method. Because the bridge module is compiled against private stubs (`hiddenapi/stubs-priv`), the call compiles normally; at runtime the HiddenApiBypass exemption makes it execute without a `NoSuchMethodError`. Returns the `ViewRootImpl` associated with the given view, which exposes the root `SurfaceControl` for the window.
### Method Signature
```java
public static ViewRootImpl View_getViewRootImpl(View view)
```
### Parameters
* **view** (View) - Any view attached to a window.
### Returns
* **ViewRootImpl** - The `ViewRootImpl` associated with the view, or null if the view is not yet attached.
### Usage Example
```kotlin
// Usage in Kotlin (MainActivity.kt):
val viewRootImpl = HiddenApiBridge.View_getViewRootImpl(view)
val surfaceControl = viewRootImpl?.surfaceControl // SurfaceControl for the window surface
```
```
--------------------------------
### Bypass Hidden API Restrictions with LSPass.addHiddenApiExemptions
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
Exempts all hidden Android APIs from restriction checks globally. This must be called as early as possible in the application's static initializer, before any Activity or Service accesses a hidden member.
```kotlin
import android.app.Application
import org.lsposed.hiddenapibypass.LSPass
class App : Application() {
companion object {
init {
// Exempt ALL hidden APIs — call as early as possible,
// before any Activity or Service accesses a hidden member.
LSPass.addHiddenApiExemptions("")
}
const val TAG: String = "SkipScreenShotTest"
}
}
```
```xml
// AndroidManifest.xml — register the custom Application subclass
//
```
--------------------------------
### HiddenApiBridge.SurfaceControl_setSkipScreenshot
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
Marks a Surface Layer to skip screen capture. This method wraps the hidden `SurfaceControl.Transaction.setSkipScreenshot()` method, allowing control over whether a surface is included in screen captures or recordings.
```APIDOC
## `HiddenApiBridge.SurfaceControl_setSkipScreenshot` — Mark a Surface Layer to Skip Screen Capture
### Description
Wraps the hidden `SurfaceControl.Transaction.setSkipScreenshot()` method. When `skipScreenshot = true`, the compositor excludes that surface layer from any screen-capture or recording operation—independent of `FLAG_SECURE`. The method returns the same `Transaction` for chaining, so `.apply()` must be called to commit the change.
### Method Signature
```java
public static SurfaceControl.Transaction SurfaceControl_setSkipScreenshot(SurfaceControl.Transaction transaction, SurfaceControl sc, boolean skipScreenshot)
```
### Parameters
* **transaction** (SurfaceControl.Transaction) - An open `SurfaceControl.Transaction` (use try-with-resources).
* **sc** (SurfaceControl) - The `SurfaceControl` obtained from `ViewRootImpl`.
* **skipScreenshot** (boolean) - `true` to exclude the surface from capture; `false` to allow capture.
### Returns
* **SurfaceControl.Transaction** - The same transaction object for chaining.
### Usage Example
```kotlin
// Usage in Kotlin — apply inside an OnDrawListener so the SurfaceControl is valid:
val sc = HiddenApiBridge.View_getViewRootImpl(view)?.surfaceControl
if (sc?.isValid == true) {
SurfaceControl.Transaction().use {
HiddenApiBridge.SurfaceControl_setSkipScreenshot(it, sc, true).apply()
// Surface layer is now excluded from screenshots and screen recordings
}
}
```
```
--------------------------------
### LSPass.addHiddenApiExemptions
Source: https://context7.com/lsposed/skipscreenshottest/llms.txt
Exempts all hidden Android APIs from restriction checks. This method should be called once in the Application class's static initializer, before any Activity or Service accesses a hidden member. Passing an empty string exempts all hidden members globally.
```APIDOC
## `LSPass.addHiddenApiExemptions` — Bypass Hidden API Restrictions
### Description
Called once in the `Application` class static initializer, this exempts all hidden Android APIs from restriction checks before any activity is created. Passing an empty string `""` exempts every hidden member globally. This must run before any hidden API call elsewhere in the app.
### Method Signature
```kotlin
public fun addHiddenApiExemptions(vararg memberNames: String)
```
### Parameters
* **memberNames** (String) - A list of hidden API member names to exempt. An empty string `""` exempts all members.
### Request Example
```kotlin
// App.kt
import android.app.Application
import org.lsposed.hiddenapibypass.LSPass
class App : Application() {
companion object {
init {
// Exempt ALL hidden APIs — call as early as possible,
// before any Activity or Service accesses a hidden member.
LSPass.addHiddenApiExemptions("")
}
const val TAG: String = "SkipScreenShotTest"
}
}
```
### Usage Notes
Ensure the custom `Application` subclass is registered in `AndroidManifest.xml`:
```xml
```
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.