Что работает:
Android – использование FLAG_SECURE работает отлично:
Код: Выделить всё
actual fun disableScreenshots() {
try {
val activity = activityProvider.invoke() ?: return
activity.window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
} catch (e: Exception) {
logDebug("error disabling screenshots: ${e.message}")
}
}
actual fun enableScreenshots() {
try {
val activity = activityProvider.invoke() ?: return
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
} catch (e: Exception) {
logDebug("error enabling screenshots: ${e.message}")
}
}
iOS. Я попробовал обычный обходной путь безопасного входа UITextField, но он нарушает пользовательский интерфейс при попытке восстановить нормальное состояние:
Код: Выделить всё
actual fun disableScreenshots() {
try {
val window: UIWindow? = UIApplication.sharedApplication.keyWindow
if (secureTextField == null && window != null) {
val textField = UITextField()
textField.setSecureTextEntry(true)
textField.setUserInteractionEnabled(false)
val placeholderView = UIView(frame = textField.frame)
val imageView = UIImageView()
imageView.setFrame(UIScreen.mainScreen.bounds)
imageView.backgroundColor = platform.UIKit.UIColor.blackColor
placeholderView.addSubview(imageView)
window.addSubview(textField)
textField.setLeftView(placeholderView)
textField.setLeftViewMode(UITextFieldViewMode.UITextFieldViewModeAlways)
val superlayer = window.layer.superlayer
if (superlayer != null) {
superlayer.addSublayer(textField.layer)
val sublayers = textField.layer.sublayers
if (sublayers != null) {
val sublayerCount = sublayers.count().toInt()
if (sublayerCount > 0) {
val lastIndex = sublayerCount - 1
val lastLayer = sublayers[lastIndex] as? platform.QuartzCore.CALayer
lastLayer?.addSublayer(window.layer)
}
}
}
secureTextField = textField
}
} catch (e: Exception) {
logDebug("error disabling screenshots: ${e.message}")
}
}
actual fun enableScreenshots() {
try {
secureTextField?.let { textField ->
val window: UIWindow? = UIApplication.sharedApplication.keyWindow
window?.layer?.superlayer?.addSublayer(window.layer)
textField.setSecureTextEntry(false)
textField.setLeftView(null)
textField.layer.removeFromSuperlayer()
textField.removeFromSuperview()
}
secureTextField = null
} catch (e: Exception) {
logDebug("error enabling screenshots: ${e.message}")
}
}
- Подход UITextField из этой статьи Medium
- Решения из этой темы StackOverflow – все используют один и тот же трюк с UITextField
- Репозиторий ScreenshotPreventing-iOS – используйте тот же трюк с UITextField
Когда вызывается метод EnableScreenshots() (пользователь уходит с QR-экрана), слой окна теряется из иерархии представлений, в результате чего весь пользовательский интерфейс прекращает рендеринг.
Примечание. Я относительно новичок в разработке Compose Multiplatform и iOS, поэтому возможно, что в моей реализации есть проблемы с тем, как я управляю иерархией слоев. Если есть лучший подход или я делаю что-то принципиально неправильно, я был бы признателен за любые советы.
Вопросы:
- Существует ли надежный способ предотвратить создание снимков экрана на iOS в мультиплатформе Kotlin или отображать черный экран при съемке снимков экрана (как это делают банковские приложения/Netflix), не нарушая при этом Пользовательский интерфейс?
- Кто-нибудь успешно реализовал это в Compose Multiplatform для iOS?
- Если моя реализация ошибочна, как правильно восстановить иерархию слоев окна после удаления защищенного текстового поля?
- Compose Multiplatform: 1.9.3
- Kotlin: 2.1.0
- Цель: iOS 13+
Подробнее здесь: https://stackoverflow.com/questions/798 ... mpose-mult
Мобильная версия