update deps

This commit is contained in:
otsmr 2026-05-01 00:09:02 +02:00
parent 7c253994e4
commit 793b7f0562
20 changed files with 440 additions and 695 deletions

View file

@ -7,10 +7,10 @@ hand_signature: 1beedb164d093643365b0832277c377353c7464f
hashlib: bc9c2f8dd7bbc72f47ccab0ce1111d40259c49bc hashlib: bc9c2f8dd7bbc72f47ccab0ce1111d40259c49bc
hashlib_codecs: 2a966c37c3b9b1f5541ae88e99ab34acf3fc968b hashlib_codecs: 2a966c37c3b9b1f5541ae88e99ab34acf3fc968b
introduction_screen: 4a90e557630b28834479ed9c64a9d2d0185d8e48 introduction_screen: 4a90e557630b28834479ed9c64a9d2d0185d8e48
libsignal_protocol_dart: 618f0c0b49534245a640a31d204265440cbac9ee libsignal_protocol_dart: c95a1586057022acdbb9c76b1692d94cc549bcc7
lottie: 4f1a5a52bdf1e1c1e12fa97c96174dcb05419e19 lottie: 4f1a5a52bdf1e1c1e12fa97c96174dcb05419e19
mutex: 84ca903a3ac863735e3228c75a212133621f680f mutex: 84ca903a3ac863735e3228c75a212133621f680f
no_screenshot: 1b561a2a87c19e317f3cb225ed023b113c9dd3a1 no_screenshot: daf759e30219224630b4af0b82061d25a457a393
optional: 71c638891ce4f2aff35c7387727989f31f9d877d optional: 71c638891ce4f2aff35c7387727989f31f9d877d
photo_view: a13ca2fc387a3fb1276126959e092c44d0029987 photo_view: a13ca2fc387a3fb1276126959e092c44d0029987
pointycastle: bbd8569f68a7fccbdf0b92d0b44a9219c126c8dd pointycastle: bbd8569f68a7fccbdf0b92d0b44a9219c126c8dd

View file

@ -70,7 +70,7 @@ class NumericFingerprintGenerator implements FingerprintGenerator {
final sortedIdentityKeys = [...identityKeys]..sort(identityKeyComparator); final sortedIdentityKeys = [...identityKeys]..sort(identityKeyComparator);
final keys = <int>[]; final keys = <int>[];
sortedIdentityKeys.forEach((IdentityKey key) { sortedIdentityKeys.forEach((key) {
final publicKeyBytes = key.publicKey.serialize(); final publicKeyBytes = key.publicKey.serialize();
keys.addAll(publicKeyBytes.toList()); keys.addAll(publicKeyBytes.toList());
}); });

View file

@ -62,9 +62,6 @@ class LogicalFingerprint extends $pb.GeneratedMessage {
@override @override
LogicalFingerprint createEmptyInstance() => create(); LogicalFingerprint createEmptyInstance() => create();
static $pb.PbList<LogicalFingerprint> createRepeated() =>
$pb.PbList<LogicalFingerprint>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static LogicalFingerprint getDefault() => _defaultInstance ??= static LogicalFingerprint getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<LogicalFingerprint>(create); $pb.GeneratedMessage.$_defaultFor<LogicalFingerprint>(create);
@ -167,9 +164,6 @@ class CombinedFingerprints extends $pb.GeneratedMessage {
@override @override
CombinedFingerprints createEmptyInstance() => create(); CombinedFingerprints createEmptyInstance() => create();
static $pb.PbList<CombinedFingerprints> createRepeated() =>
$pb.PbList<CombinedFingerprints>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static CombinedFingerprints getDefault() => _defaultInstance ??= static CombinedFingerprints getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<CombinedFingerprints>(create); $pb.GeneratedMessage.$_defaultFor<CombinedFingerprints>(create);

View file

@ -80,9 +80,6 @@ class SessionStructureChainChainKey extends $pb.GeneratedMessage {
@override @override
SessionStructureChainChainKey createEmptyInstance() => create(); SessionStructureChainChainKey createEmptyInstance() => create();
static $pb.PbList<SessionStructureChainChainKey> createRepeated() =>
$pb.PbList<SessionStructureChainChainKey>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SessionStructureChainChainKey getDefault() => _defaultInstance ??= static SessionStructureChainChainKey getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructureChainChainKey>(create); $pb.GeneratedMessage.$_defaultFor<SessionStructureChainChainKey>(create);
@ -214,9 +211,6 @@ class SessionStructureChainMessageKey extends $pb.GeneratedMessage {
@override @override
SessionStructureChainMessageKey createEmptyInstance() => create(); SessionStructureChainMessageKey createEmptyInstance() => create();
static $pb.PbList<SessionStructureChainMessageKey> createRepeated() =>
$pb.PbList<SessionStructureChainMessageKey>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SessionStructureChainMessageKey getDefault() => _defaultInstance ??= static SessionStructureChainMessageKey getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructureChainMessageKey>( $pb.GeneratedMessage.$_defaultFor<SessionStructureChainMessageKey>(
@ -378,9 +372,6 @@ class SessionStructureChain extends $pb.GeneratedMessage {
@override @override
SessionStructureChain createEmptyInstance() => create(); SessionStructureChain createEmptyInstance() => create();
static $pb.PbList<SessionStructureChain> createRepeated() =>
$pb.PbList<SessionStructureChain>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SessionStructureChain getDefault() => _defaultInstance ??= static SessionStructureChain getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructureChain>(create); $pb.GeneratedMessage.$_defaultFor<SessionStructureChain>(create);
@ -565,9 +556,6 @@ class SessionStructurePendingKeyExchange extends $pb.GeneratedMessage {
@override @override
SessionStructurePendingKeyExchange createEmptyInstance() => create(); SessionStructurePendingKeyExchange createEmptyInstance() => create();
static $pb.PbList<SessionStructurePendingKeyExchange> createRepeated() =>
$pb.PbList<SessionStructurePendingKeyExchange>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SessionStructurePendingKeyExchange getDefault() => _defaultInstance ??= static SessionStructurePendingKeyExchange getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructurePendingKeyExchange>( $pb.GeneratedMessage.$_defaultFor<SessionStructurePendingKeyExchange>(
@ -760,9 +748,6 @@ class SessionStructurePendingPreKey extends $pb.GeneratedMessage {
@override @override
SessionStructurePendingPreKey createEmptyInstance() => create(); SessionStructurePendingPreKey createEmptyInstance() => create();
static $pb.PbList<SessionStructurePendingPreKey> createRepeated() =>
$pb.PbList<SessionStructurePendingPreKey>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SessionStructurePendingPreKey getDefault() => _defaultInstance ??= static SessionStructurePendingPreKey getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructurePendingPreKey>(create); $pb.GeneratedMessage.$_defaultFor<SessionStructurePendingPreKey>(create);
@ -1004,9 +989,6 @@ class SessionStructure extends $pb.GeneratedMessage {
@override @override
SessionStructure createEmptyInstance() => create(); SessionStructure createEmptyInstance() => create();
static $pb.PbList<SessionStructure> createRepeated() =>
$pb.PbList<SessionStructure>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SessionStructure getDefault() => _defaultInstance ??= static SessionStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SessionStructure>(create); $pb.GeneratedMessage.$_defaultFor<SessionStructure>(create);
@ -1266,9 +1248,6 @@ class RecordStructure extends $pb.GeneratedMessage {
@override @override
RecordStructure createEmptyInstance() => create(); RecordStructure createEmptyInstance() => create();
static $pb.PbList<RecordStructure> createRepeated() =>
$pb.PbList<RecordStructure>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static RecordStructure getDefault() => _defaultInstance ??= static RecordStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<RecordStructure>(create); $pb.GeneratedMessage.$_defaultFor<RecordStructure>(create);
@ -1379,9 +1358,6 @@ class PreKeyRecordStructure extends $pb.GeneratedMessage {
@override @override
PreKeyRecordStructure createEmptyInstance() => create(); PreKeyRecordStructure createEmptyInstance() => create();
static $pb.PbList<PreKeyRecordStructure> createRepeated() =>
$pb.PbList<PreKeyRecordStructure>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static PreKeyRecordStructure getDefault() => _defaultInstance ??= static PreKeyRecordStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<PreKeyRecordStructure>(create); $pb.GeneratedMessage.$_defaultFor<PreKeyRecordStructure>(create);
@ -1537,9 +1513,6 @@ class SignedPreKeyRecordStructure extends $pb.GeneratedMessage {
@override @override
SignedPreKeyRecordStructure createEmptyInstance() => create(); SignedPreKeyRecordStructure createEmptyInstance() => create();
static $pb.PbList<SignedPreKeyRecordStructure> createRepeated() =>
$pb.PbList<SignedPreKeyRecordStructure>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SignedPreKeyRecordStructure getDefault() => _defaultInstance ??= static SignedPreKeyRecordStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SignedPreKeyRecordStructure>(create); $pb.GeneratedMessage.$_defaultFor<SignedPreKeyRecordStructure>(create);
@ -1690,9 +1663,6 @@ class IdentityKeyPairStructure extends $pb.GeneratedMessage {
@override @override
IdentityKeyPairStructure createEmptyInstance() => create(); IdentityKeyPairStructure createEmptyInstance() => create();
static $pb.PbList<IdentityKeyPairStructure> createRepeated() =>
$pb.PbList<IdentityKeyPairStructure>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static IdentityKeyPairStructure getDefault() => _defaultInstance ??= static IdentityKeyPairStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<IdentityKeyPairStructure>(create); $pb.GeneratedMessage.$_defaultFor<IdentityKeyPairStructure>(create);
@ -1802,9 +1772,6 @@ class SenderKeyStateStructureSenderChainKey extends $pb.GeneratedMessage {
@override @override
SenderKeyStateStructureSenderChainKey createEmptyInstance() => create(); SenderKeyStateStructureSenderChainKey createEmptyInstance() => create();
static $pb.PbList<SenderKeyStateStructureSenderChainKey> createRepeated() =>
$pb.PbList<SenderKeyStateStructureSenderChainKey>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SenderKeyStateStructureSenderChainKey getDefault() => static SenderKeyStateStructureSenderChainKey getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
@ -1915,9 +1882,6 @@ class SenderKeyStateStructureSenderMessageKey extends $pb.GeneratedMessage {
@override @override
SenderKeyStateStructureSenderMessageKey createEmptyInstance() => create(); SenderKeyStateStructureSenderMessageKey createEmptyInstance() => create();
static $pb.PbList<SenderKeyStateStructureSenderMessageKey> createRepeated() =>
$pb.PbList<SenderKeyStateStructureSenderMessageKey>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SenderKeyStateStructureSenderMessageKey getDefault() => static SenderKeyStateStructureSenderMessageKey getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
@ -2027,9 +1991,6 @@ class SenderKeyStateStructureSenderSigningKey extends $pb.GeneratedMessage {
@override @override
SenderKeyStateStructureSenderSigningKey createEmptyInstance() => create(); SenderKeyStateStructureSenderSigningKey createEmptyInstance() => create();
static $pb.PbList<SenderKeyStateStructureSenderSigningKey> createRepeated() =>
$pb.PbList<SenderKeyStateStructureSenderSigningKey>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SenderKeyStateStructureSenderSigningKey getDefault() => static SenderKeyStateStructureSenderSigningKey getDefault() =>
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<
@ -2162,9 +2123,6 @@ class SenderKeyStateStructure extends $pb.GeneratedMessage {
@override @override
SenderKeyStateStructure createEmptyInstance() => create(); SenderKeyStateStructure createEmptyInstance() => create();
static $pb.PbList<SenderKeyStateStructure> createRepeated() =>
$pb.PbList<SenderKeyStateStructure>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SenderKeyStateStructure getDefault() => _defaultInstance ??= static SenderKeyStateStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SenderKeyStateStructure>(create); $pb.GeneratedMessage.$_defaultFor<SenderKeyStateStructure>(create);
@ -2288,9 +2246,6 @@ class SenderKeyRecordStructure extends $pb.GeneratedMessage {
@override @override
SenderKeyRecordStructure createEmptyInstance() => create(); SenderKeyRecordStructure createEmptyInstance() => create();
static $pb.PbList<SenderKeyRecordStructure> createRepeated() =>
$pb.PbList<SenderKeyRecordStructure>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SenderKeyRecordStructure getDefault() => _defaultInstance ??= static SenderKeyRecordStructure getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SenderKeyRecordStructure>(create); $pb.GeneratedMessage.$_defaultFor<SenderKeyRecordStructure>(create);

View file

@ -95,9 +95,6 @@ class SignalMessage extends $pb.GeneratedMessage {
@override @override
SignalMessage createEmptyInstance() => create(); SignalMessage createEmptyInstance() => create();
static $pb.PbList<SignalMessage> createRepeated() =>
$pb.PbList<SignalMessage>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SignalMessage getDefault() => _defaultInstance ??= static SignalMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SignalMessage>(create); $pb.GeneratedMessage.$_defaultFor<SignalMessage>(create);
@ -275,9 +272,6 @@ class PreKeySignalMessage extends $pb.GeneratedMessage {
@override @override
PreKeySignalMessage createEmptyInstance() => create(); PreKeySignalMessage createEmptyInstance() => create();
static $pb.PbList<PreKeySignalMessage> createRepeated() =>
$pb.PbList<PreKeySignalMessage>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static PreKeySignalMessage getDefault() => _defaultInstance ??= static PreKeySignalMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<PreKeySignalMessage>(create); $pb.GeneratedMessage.$_defaultFor<PreKeySignalMessage>(create);
@ -473,9 +467,6 @@ class KeyExchangeMessage extends $pb.GeneratedMessage {
@override @override
KeyExchangeMessage createEmptyInstance() => create(); KeyExchangeMessage createEmptyInstance() => create();
static $pb.PbList<KeyExchangeMessage> createRepeated() =>
$pb.PbList<KeyExchangeMessage>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static KeyExchangeMessage getDefault() => _defaultInstance ??= static KeyExchangeMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<KeyExchangeMessage>(create); $pb.GeneratedMessage.$_defaultFor<KeyExchangeMessage>(create);
@ -632,9 +623,6 @@ class SenderKeyMessage extends $pb.GeneratedMessage {
@override @override
SenderKeyMessage createEmptyInstance() => create(); SenderKeyMessage createEmptyInstance() => create();
static $pb.PbList<SenderKeyMessage> createRepeated() =>
$pb.PbList<SenderKeyMessage>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SenderKeyMessage getDefault() => _defaultInstance ??= static SenderKeyMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SenderKeyMessage>(create); $pb.GeneratedMessage.$_defaultFor<SenderKeyMessage>(create);
@ -779,9 +767,6 @@ class SenderKeyDistributionMessage extends $pb.GeneratedMessage {
@override @override
SenderKeyDistributionMessage createEmptyInstance() => create(); SenderKeyDistributionMessage createEmptyInstance() => create();
static $pb.PbList<SenderKeyDistributionMessage> createRepeated() =>
$pb.PbList<SenderKeyDistributionMessage>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static SenderKeyDistributionMessage getDefault() => _defaultInstance ??= static SenderKeyDistributionMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<SenderKeyDistributionMessage>(create); $pb.GeneratedMessage.$_defaultFor<SenderKeyDistributionMessage>(create);
@ -918,9 +903,6 @@ class DeviceConsistencyCodeMessage extends $pb.GeneratedMessage {
@override @override
DeviceConsistencyCodeMessage createEmptyInstance() => create(); DeviceConsistencyCodeMessage createEmptyInstance() => create();
static $pb.PbList<DeviceConsistencyCodeMessage> createRepeated() =>
$pb.PbList<DeviceConsistencyCodeMessage>();
@$core.pragma('dart2js:noInline') @$core.pragma('dart2js:noInline')
static DeviceConsistencyCodeMessage getDefault() => _defaultInstance ??= static DeviceConsistencyCodeMessage getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<DeviceConsistencyCodeMessage>(create); $pb.GeneratedMessage.$_defaultFor<DeviceConsistencyCodeMessage>(create);

View file

@ -72,7 +72,6 @@ class ByteUtil {
} }
static int compare(Uint8List left, Uint8List right) { static int compare(Uint8List left, Uint8List right) {
// ignore: parameter_assignments, avoid_multiple_declarations_per_line
for (var i = 0, j = 0; i < left.length && j < right.length; i++, j++) { for (var i = 0, j = 0; i < left.length && j < right.length; i++, j++) {
final a = left[i] & 0xff; final a = left[i] & 0xff;
final b = right[j] & 0xff; final b = right[j] & 0xff;

View file

@ -1,6 +1,6 @@
name: libsignal_protocol_dart name: libsignal_protocol_dart
description: Signal Protocol libray for Dart native and Flutter, pure Dart implementation of the the Signal Protocol description: Signal Protocol libray for Dart native and Flutter, pure Dart implementation of the the Signal Protocol
version: 0.7.4 version: 0.8.0
repository: https://github.com/MixinNetwork/libsignal_protocol_dart repository: https://github.com/MixinNetwork/libsignal_protocol_dart
topics: topics:
- crypto - crypto
@ -19,10 +19,10 @@ dependencies:
meta: ^1.16.0 meta: ^1.16.0
optional: ^6.1.0+1 optional: ^6.1.0+1
pointycastle: ^4.0.0 pointycastle: ^4.0.0
protobuf: ^4.0.0 protobuf: ^6.0.0
x25519: ^0.1.1 x25519: ^0.1.1
dev_dependencies: dev_dependencies:
test: ^1.25.8 test: ^1.25.8
very_good_analysis: ^8.0.0 very_good_analysis: ^10.2.0

View file

@ -22,7 +22,6 @@ import android.renderscript.ScriptIntrinsicBlur
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager
import android.view.WindowManager.LayoutParams import android.view.WindowManager.LayoutParams
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
@ -88,7 +87,6 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
private var isScreenRecording: Boolean = false private var isScreenRecording: Boolean = false
private var isRecordingListening: Boolean = false private var isRecordingListening: Boolean = false
private var screenCaptureCallback: Any? = null private var screenCaptureCallback: Any? = null
private var screenRecordingCallback: Any? = null
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext context = flutterPluginBinding.applicationContext
@ -114,12 +112,12 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
activity = binding.activity activity = binding.activity
restoreScreenshotState() restoreScreenshotState()
if (isRecordingListening) { if (isRecordingListening) {
registerScreenRecordingCallbacks() registerScreenCaptureCallback()
} }
} }
override fun onDetachedFromActivityForConfigChanges() { override fun onDetachedFromActivityForConfigChanges() {
unregisterScreenRecordingCallbacks() unregisterScreenCaptureCallback()
removeImageOverlay() removeImageOverlay()
removeBlurOverlay() removeBlurOverlay()
removeColorOverlay() removeColorOverlay()
@ -130,12 +128,12 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
activity = binding.activity activity = binding.activity
restoreScreenshotState() restoreScreenshotState()
if (isRecordingListening) { if (isRecordingListening) {
registerScreenRecordingCallbacks() registerScreenCaptureCallback()
} }
} }
override fun onDetachedFromActivity() { override fun onDetachedFromActivity() {
unregisterScreenRecordingCallbacks() unregisterScreenCaptureCallback()
removeImageOverlay() removeImageOverlay()
removeBlurOverlay() removeBlurOverlay()
removeColorOverlay() removeColorOverlay()
@ -273,7 +271,7 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
private fun showImageOverlay(activity: Activity) { private fun showImageOverlay(activity: Activity) {
if (overlayImageView != null) return if (overlayImageView != null) return
val resId = activity.resources.getIdentifier("no_screenshot_image", "drawable", activity.packageName) val resId = activity.resources.getIdentifier("image", "drawable", activity.packageName)
if (resId == 0) return if (resId == 0) return
activity.runOnUiThread { activity.runOnUiThread {
val imageView = ImageView(activity).apply { val imageView = ImageView(activity).apply {
@ -546,77 +544,24 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
} }
// ── Screen Recording Detection ───────────────────────────────────── // ── Screen Recording Detection ─────────────────────────────────────
//
// API 35+ (Android 15): WindowManager.addScreenRecordingCallback
// → true start/stop detection via SCREEN_RECORDING_STATE_VISIBLE / _NOT_VISIBLE
// API 34 (Android 14): Activity.ScreenCaptureCallback
// → fires on screen capture (start only, no stop event)
// API <34: graceful no-op
private fun startRecordingListening() { private fun startRecordingListening() {
if (isRecordingListening) return if (isRecordingListening) return
isRecordingListening = true isRecordingListening = true
registerScreenRecordingCallbacks() registerScreenCaptureCallback()
updateSharedPreferencesState("") updateSharedPreferencesState("")
} }
private fun stopRecordingListening() { private fun stopRecordingListening() {
if (!isRecordingListening) return if (!isRecordingListening) return
isRecordingListening = false isRecordingListening = false
unregisterScreenRecordingCallbacks() unregisterScreenCaptureCallback()
isScreenRecording = false isScreenRecording = false
updateSharedPreferencesState("") updateSharedPreferencesState("")
} }
private fun registerScreenRecordingCallbacks() {
if (Build.VERSION.SDK_INT >= 35) {
registerScreenRecordingCallback()
} else if (Build.VERSION.SDK_INT >= 34) {
registerScreenCaptureCallback()
}
}
private fun unregisterScreenRecordingCallbacks() {
if (Build.VERSION.SDK_INT >= 35) {
unregisterScreenRecordingCallback()
}
if (Build.VERSION.SDK_INT >= 34) {
unregisterScreenCaptureCallback()
}
}
@Suppress("NewApi")
private fun registerScreenRecordingCallback() {
val act = activity ?: return
if (screenRecordingCallback != null) return
val callback = java.util.function.Consumer<Int> { state ->
val wasRecording = isScreenRecording
isScreenRecording = (state == WindowManager.SCREEN_RECORDING_STATE_VISIBLE)
if (isScreenRecording != wasRecording) {
updateSharedPreferencesState("", System.currentTimeMillis())
}
}
val initialState = act.windowManager.addScreenRecordingCallback(act.mainExecutor, callback)
screenRecordingCallback = callback
// Process initial state
val wasRecording = isScreenRecording
isScreenRecording = (initialState == WindowManager.SCREEN_RECORDING_STATE_VISIBLE)
if (isScreenRecording != wasRecording) {
updateSharedPreferencesState("", System.currentTimeMillis())
}
}
@Suppress("NewApi", "UNCHECKED_CAST")
private fun unregisterScreenRecordingCallback() {
val act = activity ?: return
val callback = screenRecordingCallback as? java.util.function.Consumer<Int> ?: return
act.windowManager.removeScreenRecordingCallback(callback)
screenRecordingCallback = null
}
private fun registerScreenCaptureCallback() { private fun registerScreenCaptureCallback() {
if (Build.VERSION.SDK_INT >= 34) { if (android.os.Build.VERSION.SDK_INT >= 34) {
val act = activity ?: return val act = activity ?: return
if (screenCaptureCallback != null) return if (screenCaptureCallback != null) return
@ -630,7 +575,7 @@ class NoScreenshotPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, Activ
} }
private fun unregisterScreenCaptureCallback() { private fun unregisterScreenCaptureCallback() {
if (Build.VERSION.SDK_INT >= 34) { if (android.os.Build.VERSION.SDK_INT >= 34) {
val act = activity ?: return val act = activity ?: return
val callback = screenCaptureCallback as? Activity.ScreenCaptureCallback ?: return val callback = screenCaptureCallback as? Activity.ScreenCaptureCallback ?: return
act.unregisterScreenCaptureCallback(callback) act.unregisterScreenCaptureCallback(callback)

View file

@ -138,7 +138,7 @@ public class IOSNoScreenshotPlugin: NSObject, FlutterPlugin, FlutterStreamHandle
// visible in the app switcher (otherwise the secure text field // visible in the app switcher (otherwise the secure text field
// would show a blank screen). // would show a blank screen).
disablePreventScreenshot() disablePreventScreenshot()
enableImageScreen(named: "NoScreenshotImage") enableImageScreen(named: "image")
} else if isBlurOverlayModeEnabled { } else if isBlurOverlayModeEnabled {
disablePreventScreenshot() disablePreventScreenshot()
enableBlurScreen(radius: blurRadius) enableBlurScreen(radius: blurRadius)

View file

@ -14,8 +14,7 @@ class NoScreenshot implements NoScreenshotPlatform {
NoScreenshot._(); NoScreenshot._();
@Deprecated( @Deprecated(
"Using this may cause issue\nUse instance directly\ne.g: 'NoScreenshot.instance.screenshotOff()'", "Using this may cause issue\nUse instance directly\ne.g: 'NoScreenshot.instance.screenshotOff()'")
)
NoScreenshot(); NoScreenshot();
static final NoScreenshot instance = NoScreenshot._(); static final NoScreenshot instance = NoScreenshot._();

View file

@ -19,18 +19,15 @@ class MethodChannelNoScreenshot extends NoScreenshotPlatform {
@override @override
Stream<ScreenshotSnapshot> get screenshotStream { Stream<ScreenshotSnapshot> get screenshotStream {
_cachedStream ??= eventChannel.receiveBroadcastStream().map( _cachedStream ??= eventChannel.receiveBroadcastStream().map((event) =>
(event) => ScreenshotSnapshot.fromMap(jsonDecode(event) as Map<String, dynamic>));
ScreenshotSnapshot.fromMap(jsonDecode(event) as Map<String, dynamic>),
);
return _cachedStream!; return _cachedStream!;
} }
@override @override
Future<bool> toggleScreenshot() async { Future<bool> toggleScreenshot() async {
final result = await methodChannel.invokeMethod<bool>( final result =
toggleScreenShotConst, await methodChannel.invokeMethod<bool>(toggleScreenShotConst);
);
return result ?? false; return result ?? false;
} }
@ -54,17 +51,15 @@ class MethodChannelNoScreenshot extends NoScreenshotPlatform {
@override @override
Future<bool> toggleScreenshotWithBlur({double blurRadius = 30.0}) async { Future<bool> toggleScreenshotWithBlur({double blurRadius = 30.0}) async {
final result = await methodChannel.invokeMethod<bool>(screenSetBlur, { final result = await methodChannel
'radius': blurRadius, .invokeMethod<bool>(screenSetBlur, {'radius': blurRadius});
});
return result ?? false; return result ?? false;
} }
@override @override
Future<bool> toggleScreenshotWithColor({int color = 0xFF000000}) async { Future<bool> toggleScreenshotWithColor({int color = 0xFF000000}) async {
final result = await methodChannel.invokeMethod<bool>(screenSetColor, { final result = await methodChannel
'color': color, .invokeMethod<bool>(screenSetColor, {'color': color});
});
return result ?? false; return result ?? false;
} }
@ -76,17 +71,15 @@ class MethodChannelNoScreenshot extends NoScreenshotPlatform {
@override @override
Future<bool> screenshotWithBlur({double blurRadius = 30.0}) async { Future<bool> screenshotWithBlur({double blurRadius = 30.0}) async {
final result = await methodChannel.invokeMethod<bool>(screenEnableBlur, { final result = await methodChannel
'radius': blurRadius, .invokeMethod<bool>(screenEnableBlur, {'radius': blurRadius});
});
return result ?? false; return result ?? false;
} }
@override @override
Future<bool> screenshotWithColor({int color = 0xFF000000}) async { Future<bool> screenshotWithColor({int color = 0xFF000000}) async {
final result = await methodChannel.invokeMethod<bool>(screenEnableColor, { final result = await methodChannel
'color': color, .invokeMethod<bool>(screenEnableColor, {'color': color});
});
return result ?? false; return result ?? false;
} }

View file

@ -43,20 +43,17 @@ abstract class NoScreenshotPlatform extends PlatformInterface {
/// throw `UnmimplementedError` if not implement /// throw `UnmimplementedError` if not implement
Future<bool> toggleScreenshotWithImage() { Future<bool> toggleScreenshotWithImage() {
throw UnimplementedError( throw UnimplementedError(
'toggleScreenshotWithImage() has not been implemented.', 'toggleScreenshotWithImage() has not been implemented.');
);
} }
Future<bool> toggleScreenshotWithBlur({double blurRadius = 30.0}) { Future<bool> toggleScreenshotWithBlur({double blurRadius = 30.0}) {
throw UnimplementedError( throw UnimplementedError(
'toggleScreenshotWithBlur() has not been implemented.', 'toggleScreenshotWithBlur() has not been implemented.');
);
} }
Future<bool> toggleScreenshotWithColor({int color = 0xFF000000}) { Future<bool> toggleScreenshotWithColor({int color = 0xFF000000}) {
throw UnimplementedError( throw UnimplementedError(
'toggleScreenshotWithColor() has not been implemented.', 'toggleScreenshotWithColor() has not been implemented.');
);
} }
/// Always enables image overlay mode (idempotent safe to call repeatedly). /// Always enables image overlay mode (idempotent safe to call repeatedly).
@ -90,31 +87,27 @@ abstract class NoScreenshotPlatform extends PlatformInterface {
throw UnimplementedError('incrementStream has not been implemented.'); throw UnimplementedError('incrementStream has not been implemented.');
} }
// Start listening to screenshot activities // Start listening to screenshot activities
Future<void> startScreenshotListening() { Future<void> startScreenshotListening() {
throw UnimplementedError( throw UnimplementedError(
'startScreenshotListening has not been implemented.', 'startScreenshotListening has not been implemented.');
);
} }
/// Stop listening to screenshot activities /// Stop listening to screenshot activities
Future<void> stopScreenshotListening() { Future<void> stopScreenshotListening() {
throw UnimplementedError( throw UnimplementedError(
'stopScreenshotListening has not been implemented.', 'stopScreenshotListening has not been implemented.');
);
} }
/// Start listening to screen recording activities /// Start listening to screen recording activities
Future<void> startScreenRecordingListening() { Future<void> startScreenRecordingListening() {
throw UnimplementedError( throw UnimplementedError(
'startScreenRecordingListening has not been implemented.', 'startScreenRecordingListening has not been implemented.');
);
} }
/// Stop listening to screen recording activities /// Stop listening to screen recording activities
Future<void> stopScreenRecordingListening() { Future<void> stopScreenRecordingListening() {
throw UnimplementedError( throw UnimplementedError(
'stopScreenRecordingListening has not been implemented.', 'stopScreenRecordingListening has not been implemented.');
);
} }
} }

View file

@ -138,13 +138,11 @@ class NoScreenshotWeb extends NoScreenshotPlatform {
} }
void _emitState({bool wasScreenshotTaken = false}) { void _emitState({bool wasScreenshotTaken = false}) {
_controller.add( _controller.add(ScreenshotSnapshot(
ScreenshotSnapshot(
screenshotPath: '', screenshotPath: '',
isScreenshotProtectionOn: _isProtectionOn, isScreenshotProtectionOn: _isProtectionOn,
wasScreenshotTaken: wasScreenshotTaken, wasScreenshotTaken: wasScreenshotTaken,
), ));
);
} }
// Context menu blocker // Context menu blocker

View file

@ -73,10 +73,7 @@ class SecureNavigatorObserver extends NavigatorObserver {
void _applyPolicyForRoute(Route<dynamic>? route) { void _applyPolicyForRoute(Route<dynamic>? route) {
final name = route?.settings.name; final name = route?.settings.name;
final config = (name != null ? policies[name] : null) ?? defaultConfig; final config = (name != null ? policies[name] : null) ?? defaultConfig;
applyOverlayMode( applyOverlayMode(config.mode,
config.mode, blurRadius: config.blurRadius, color: config.color);
blurRadius: config.blurRadius,
color: config.color,
);
} }
} }

View file

@ -1,6 +1,6 @@
name: no_screenshot name: no_screenshot
description: Flutter plugin to prevent screenshots, detect screen recording, and show blur/color/image overlays in the app switcher on Android, iOS, macOS, Linux, Windows, and Web. description: Flutter plugin to prevent screenshots, detect screen recording, and show blur/color/image overlays in the app switcher on Android, iOS, macOS, Linux, Windows, and Web.
version: 1.1.0 version: 0.10.0
homepage: https://flutterplaza.com homepage: https://flutterplaza.com
repository: https://github.com/FlutterPlaza/no_screenshot repository: https://github.com/FlutterPlaza/no_screenshot

View file

@ -133,8 +133,7 @@ void main() {
expect(result, expected); expect(result, expected);
}); });
test( test('toggleScreenshotWithBlur returns false when channel returns null',
'toggleScreenshotWithBlur returns false when channel returns null',
() async { () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async { .setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -143,8 +142,7 @@ void main() {
final result = await platform.toggleScreenshotWithBlur(); final result = await platform.toggleScreenshotWithBlur();
expect(result, false); expect(result, false);
}, });
);
test('toggleScreenshotWithColor', () async { test('toggleScreenshotWithColor', () async {
const bool expected = true; const bool expected = true;
@ -172,14 +170,12 @@ void main() {
return null; return null;
}); });
final result = await platform.toggleScreenshotWithColor( final result =
color: 0xFFFF0000, await platform.toggleScreenshotWithColor(color: 0xFFFF0000);
);
expect(result, expected); expect(result, expected);
}); });
test( test('toggleScreenshotWithColor returns false when channel returns null',
'toggleScreenshotWithColor returns false when channel returns null',
() async { () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async { .setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -188,11 +184,9 @@ void main() {
final result = await platform.toggleScreenshotWithColor(); final result = await platform.toggleScreenshotWithColor();
expect(result, false); expect(result, false);
}, });
);
test( test('toggleScreenshotWithImage returns false when channel returns null',
'toggleScreenshotWithImage returns false when channel returns null',
() async { () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async { .setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -201,8 +195,7 @@ void main() {
final result = await platform.toggleScreenshotWithImage(); final result = await platform.toggleScreenshotWithImage();
expect(result, false); expect(result, false);
}, });
);
test('screenshotOn returns false when channel returns null', () async { test('screenshotOn returns false when channel returns null', () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
@ -248,8 +241,7 @@ void main() {
expect(result, expected); expect(result, expected);
}); });
test( test('screenshotWithImage returns false when channel returns null',
'screenshotWithImage returns false when channel returns null',
() async { () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async { .setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -258,8 +250,7 @@ void main() {
final result = await platform.screenshotWithImage(); final result = await platform.screenshotWithImage();
expect(result, false); expect(result, false);
}, });
);
test('screenshotWithBlur', () async { test('screenshotWithBlur', () async {
const bool expected = true; const bool expected = true;
@ -291,8 +282,7 @@ void main() {
expect(result, expected); expect(result, expected);
}); });
test( test('screenshotWithBlur returns false when channel returns null',
'screenshotWithBlur returns false when channel returns null',
() async { () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async { .setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -301,8 +291,7 @@ void main() {
final result = await platform.screenshotWithBlur(); final result = await platform.screenshotWithBlur();
expect(result, false); expect(result, false);
}, });
);
test('screenshotWithColor', () async { test('screenshotWithColor', () async {
const bool expected = true; const bool expected = true;
@ -334,8 +323,7 @@ void main() {
expect(result, expected); expect(result, expected);
}); });
test( test('screenshotWithColor returns false when channel returns null',
'screenshotWithColor returns false when channel returns null',
() async { () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async { .setMockMethodCallHandler(channel, (MethodCall methodCall) async {
@ -344,8 +332,7 @@ void main() {
final result = await platform.screenshotWithColor(); final result = await platform.screenshotWithColor();
expect(result, false); expect(result, false);
}, });
);
test('startScreenRecordingListening', () async { test('startScreenRecordingListening', () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
@ -373,8 +360,7 @@ void main() {
expect(true, true); expect(true, true);
}); });
test( test('screenshotStream returns a stream that emits ScreenshotSnapshot',
'screenshotStream returns a stream that emits ScreenshotSnapshot',
() async { () async {
final snapshotMap = { final snapshotMap = {
'screenshot_path': '/test/path', 'screenshot_path': '/test/path',
@ -388,14 +374,11 @@ void main() {
// Mock the event channel by handling the underlying method channel // Mock the event channel by handling the underlying method channel
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockStreamHandler( .setMockStreamHandler(platform.eventChannel, MockStreamHandler.inline(
platform.eventChannel,
MockStreamHandler.inline(
onListen: (arguments, events) { onListen: (arguments, events) {
events.success(encoded); events.success(encoded);
}, },
), ));
);
final stream = platform.screenshotStream; final stream = platform.screenshotStream;
final snapshot = await stream.first; final snapshot = await stream.first;
@ -403,8 +386,7 @@ void main() {
expect(snapshot.screenshotPath, '/test/path'); expect(snapshot.screenshotPath, '/test/path');
expect(snapshot.isScreenshotProtectionOn, true); expect(snapshot.isScreenshotProtectionOn, true);
expect(snapshot.wasScreenshotTaken, true); expect(snapshot.wasScreenshotTaken, true);
}, });
);
test('screenshotStream caches and returns the same stream instance', () { test('screenshotStream caches and returns the same stream instance', () {
final stream1 = platform.screenshotStream; final stream1 = platform.screenshotStream;
@ -635,10 +617,8 @@ void main() {
wasScreenshotTaken: true, wasScreenshotTaken: true,
); );
final string = snapshot.toString(); final string = snapshot.toString();
expect( expect(string,
string, 'ScreenshotSnapshot(\nscreenshotPath: /example/path, \nisScreenshotProtectionOn: true, \nwasScreenshotTaken: true, \nisScreenRecording: false, \ntimestamp: 0, \nsourceApp: \n)');
'ScreenshotSnapshot(\nscreenshotPath: /example/path, \nisScreenshotProtectionOn: true, \nwasScreenshotTaken: true, \nisScreenRecording: false, \ntimestamp: 0, \nsourceApp: \n)',
);
}); });
test('toString with isScreenRecording true', () { test('toString with isScreenRecording true', () {
@ -649,10 +629,8 @@ void main() {
isScreenRecording: true, isScreenRecording: true,
); );
final string = snapshot.toString(); final string = snapshot.toString();
expect( expect(string,
string, 'ScreenshotSnapshot(\nscreenshotPath: /example/path, \nisScreenshotProtectionOn: true, \nwasScreenshotTaken: true, \nisScreenRecording: true, \ntimestamp: 0, \nsourceApp: \n)');
'ScreenshotSnapshot(\nscreenshotPath: /example/path, \nisScreenshotProtectionOn: true, \nwasScreenshotTaken: true, \nisScreenRecording: true, \ntimestamp: 0, \nsourceApp: \n)',
);
}); });
test('toString with metadata', () { test('toString with metadata', () {
@ -688,46 +666,38 @@ void main() {
controller.close(); controller.close();
}); });
test( test('onScreenshotDetected fires when wasScreenshotTaken is true',
'onScreenshotDetected fires when wasScreenshotTaken is true',
() async { () async {
final detected = <ScreenshotSnapshot>[]; final detected = <ScreenshotSnapshot>[];
noScreenshot.onScreenshotDetected = detected.add; noScreenshot.onScreenshotDetected = detected.add;
noScreenshot.startCallbacks(); noScreenshot.startCallbacks();
controller.add( controller.add(ScreenshotSnapshot(
ScreenshotSnapshot(
screenshotPath: '/path', screenshotPath: '/path',
isScreenshotProtectionOn: true, isScreenshotProtectionOn: true,
wasScreenshotTaken: true, wasScreenshotTaken: true,
), ));
);
await Future.delayed(Duration.zero); await Future.delayed(Duration.zero);
expect(detected, hasLength(1)); expect(detected, hasLength(1));
expect(detected.first.wasScreenshotTaken, true); expect(detected.first.wasScreenshotTaken, true);
}, });
);
test( test('onScreenshotDetected does NOT fire when wasScreenshotTaken is false',
'onScreenshotDetected does NOT fire when wasScreenshotTaken is false',
() async { () async {
final detected = <ScreenshotSnapshot>[]; final detected = <ScreenshotSnapshot>[];
noScreenshot.onScreenshotDetected = detected.add; noScreenshot.onScreenshotDetected = detected.add;
noScreenshot.startCallbacks(); noScreenshot.startCallbacks();
controller.add( controller.add(ScreenshotSnapshot(
ScreenshotSnapshot(
screenshotPath: '', screenshotPath: '',
isScreenshotProtectionOn: true, isScreenshotProtectionOn: true,
wasScreenshotTaken: false, wasScreenshotTaken: false,
), ));
);
await Future.delayed(Duration.zero); await Future.delayed(Duration.zero);
expect(detected, isEmpty); expect(detected, isEmpty);
}, });
);
test('onScreenRecordingStarted fires on false→true transition', () async { test('onScreenRecordingStarted fires on false→true transition', () async {
final started = <ScreenshotSnapshot>[]; final started = <ScreenshotSnapshot>[];
@ -735,14 +705,12 @@ void main() {
noScreenshot.startCallbacks(); noScreenshot.startCallbacks();
// Initial state: not recording recording starts // Initial state: not recording recording starts
controller.add( controller.add(ScreenshotSnapshot(
ScreenshotSnapshot(
screenshotPath: '', screenshotPath: '',
isScreenshotProtectionOn: true, isScreenshotProtectionOn: true,
wasScreenshotTaken: false, wasScreenshotTaken: false,
isScreenRecording: true, isScreenRecording: true,
), ));
);
await Future.delayed(Duration.zero); await Future.delayed(Duration.zero);
expect(started, hasLength(1)); expect(started, hasLength(1));
@ -755,33 +723,28 @@ void main() {
noScreenshot.startCallbacks(); noScreenshot.startCallbacks();
// First: recording starts // First: recording starts
controller.add( controller.add(ScreenshotSnapshot(
ScreenshotSnapshot(
screenshotPath: '', screenshotPath: '',
isScreenshotProtectionOn: true, isScreenshotProtectionOn: true,
wasScreenshotTaken: false, wasScreenshotTaken: false,
isScreenRecording: true, isScreenRecording: true,
), ));
);
await Future.delayed(Duration.zero); await Future.delayed(Duration.zero);
// Then: recording stops // Then: recording stops
controller.add( controller.add(ScreenshotSnapshot(
ScreenshotSnapshot(
screenshotPath: '', screenshotPath: '',
isScreenshotProtectionOn: true, isScreenshotProtectionOn: true,
wasScreenshotTaken: false, wasScreenshotTaken: false,
isScreenRecording: false, isScreenRecording: false,
), ));
);
await Future.delayed(Duration.zero); await Future.delayed(Duration.zero);
expect(stopped, hasLength(1)); expect(stopped, hasLength(1));
expect(stopped.first.isScreenRecording, false); expect(stopped.first.isScreenRecording, false);
}); });
test( test('removeAllCallbacks clears all callbacks and stops subscription',
'removeAllCallbacks clears all callbacks and stops subscription',
() async { () async {
final detected = <ScreenshotSnapshot>[]; final detected = <ScreenshotSnapshot>[];
noScreenshot.onScreenshotDetected = detected.add; noScreenshot.onScreenshotDetected = detected.add;
@ -795,18 +758,15 @@ void main() {
expect(noScreenshot.onScreenRecordingStopped, isNull); expect(noScreenshot.onScreenRecordingStopped, isNull);
// Events after removal should not fire // Events after removal should not fire
controller.add( controller.add(ScreenshotSnapshot(
ScreenshotSnapshot(
screenshotPath: '/path', screenshotPath: '/path',
isScreenshotProtectionOn: true, isScreenshotProtectionOn: true,
wasScreenshotTaken: true, wasScreenshotTaken: true,
), ));
);
await Future.delayed(Duration.zero); await Future.delayed(Duration.zero);
expect(detected, isEmpty); expect(detected, isEmpty);
}, });
);
test('hasActiveCallbacks reflects subscription state', () { test('hasActiveCallbacks reflects subscription state', () {
expect(noScreenshot.hasActiveCallbacks, false); expect(noScreenshot.hasActiveCallbacks, false);

View file

@ -84,10 +84,8 @@ void main() {
group('NoScreenshotPlatform', () { group('NoScreenshotPlatform', () {
test('default instance should be MethodChannelNoScreenshot', () { test('default instance should be MethodChannelNoScreenshot', () {
expect( expect(NoScreenshotPlatform.instance,
NoScreenshotPlatform.instance, isInstanceOf<MethodChannelNoScreenshot>());
isInstanceOf<MethodChannelNoScreenshot>(),
);
}); });
test('screenshotOff should return true when called', () async { test('screenshotOff should return true when called', () async {
@ -102,28 +100,21 @@ void main() {
expect(await platform.toggleScreenshot(), isTrue); expect(await platform.toggleScreenshot(), isTrue);
}); });
test( test('screenshotStream should not throw UnimplementedError when accessed',
'screenshotStream should not throw UnimplementedError when accessed',
() { () {
expect( expect(() => platform.screenshotStream, isNot(throwsUnimplementedError));
() => platform.screenshotStream, });
isNot(throwsUnimplementedError),
);
},
);
test( test(
'startScreenshotListening should not throw UnimplementedError when called', 'startScreenshotListening should not throw UnimplementedError when called',
() async { () async {
expect(platform.startScreenshotListening(), completes); expect(platform.startScreenshotListening(), completes);
}, });
);
test( test(
'stopScreenshotListening should not throw UnimplementedError when called', 'stopScreenshotListening should not throw UnimplementedError when called',
() async { () async {
expect(platform.stopScreenshotListening(), completes); expect(platform.stopScreenshotListening(), completes);
}, });
);
test('toggleScreenshotWithImage should return true when called', () async { test('toggleScreenshotWithImage should return true when called', () async {
expect(await platform.toggleScreenshotWithImage(), isTrue); expect(await platform.toggleScreenshotWithImage(), isTrue);
@ -133,12 +124,9 @@ void main() {
'base NoScreenshotPlatform.toggleScreenshotWithImage() throws UnimplementedError', 'base NoScreenshotPlatform.toggleScreenshotWithImage() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(() => basePlatform.toggleScreenshotWithImage(),
() => basePlatform.toggleScreenshotWithImage(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
test('toggleScreenshotWithBlur should return true when called', () async { test('toggleScreenshotWithBlur should return true when called', () async {
expect(await platform.toggleScreenshotWithBlur(), isTrue); expect(await platform.toggleScreenshotWithBlur(), isTrue);
@ -148,12 +136,9 @@ void main() {
'base NoScreenshotPlatform.toggleScreenshotWithBlur() throws UnimplementedError', 'base NoScreenshotPlatform.toggleScreenshotWithBlur() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(() => basePlatform.toggleScreenshotWithBlur(),
() => basePlatform.toggleScreenshotWithBlur(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
test('toggleScreenshotWithColor should return true when called', () async { test('toggleScreenshotWithColor should return true when called', () async {
expect(await platform.toggleScreenshotWithColor(), isTrue); expect(await platform.toggleScreenshotWithColor(), isTrue);
@ -163,12 +148,9 @@ void main() {
'base NoScreenshotPlatform.toggleScreenshotWithColor() throws UnimplementedError', 'base NoScreenshotPlatform.toggleScreenshotWithColor() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(() => basePlatform.toggleScreenshotWithColor(),
() => basePlatform.toggleScreenshotWithColor(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
test('screenshotWithImage should return true when called', () async { test('screenshotWithImage should return true when called', () async {
expect(await platform.screenshotWithImage(), isTrue); expect(await platform.screenshotWithImage(), isTrue);
@ -179,11 +161,8 @@ void main() {
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(
() => basePlatform.screenshotWithImage(), () => basePlatform.screenshotWithImage(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
test('screenshotWithBlur should return true when called', () async { test('screenshotWithBlur should return true when called', () async {
expect(await platform.screenshotWithBlur(), isTrue); expect(await platform.screenshotWithBlur(), isTrue);
@ -193,12 +172,8 @@ void main() {
'base NoScreenshotPlatform.screenshotWithBlur() throws UnimplementedError', 'base NoScreenshotPlatform.screenshotWithBlur() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(() => basePlatform.screenshotWithBlur(), throwsUnimplementedError);
() => basePlatform.screenshotWithBlur(), });
throwsUnimplementedError,
);
},
);
test('screenshotWithColor should return true when called', () async { test('screenshotWithColor should return true when called', () async {
expect(await platform.screenshotWithColor(), isTrue); expect(await platform.screenshotWithColor(), isTrue);
@ -209,100 +184,76 @@ void main() {
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(
() => basePlatform.screenshotWithColor(), () => basePlatform.screenshotWithColor(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
test( test('base NoScreenshotPlatform.screenshotOff() throws UnimplementedError',
'base NoScreenshotPlatform.screenshotOff() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect(() => basePlatform.screenshotOff(), throwsUnimplementedError); expect(() => basePlatform.screenshotOff(), throwsUnimplementedError);
}, });
);
test( test('base NoScreenshotPlatform.screenshotOn() throws UnimplementedError',
'base NoScreenshotPlatform.screenshotOn() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect(() => basePlatform.screenshotOn(), throwsUnimplementedError); expect(() => basePlatform.screenshotOn(), throwsUnimplementedError);
}, });
);
test( test(
'base NoScreenshotPlatform.toggleScreenshot() throws UnimplementedError', 'base NoScreenshotPlatform.toggleScreenshot() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect(() => basePlatform.toggleScreenshot(), throwsUnimplementedError); expect(() => basePlatform.toggleScreenshot(), throwsUnimplementedError);
}, });
);
test( test('base NoScreenshotPlatform.screenshotStream throws UnimplementedError',
'base NoScreenshotPlatform.screenshotStream throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect(() => basePlatform.screenshotStream, throwsUnimplementedError); expect(() => basePlatform.screenshotStream, throwsUnimplementedError);
}, });
);
test( test(
'base NoScreenshotPlatform.startScreenshotListening() throws UnimplementedError', 'base NoScreenshotPlatform.startScreenshotListening() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(() => basePlatform.startScreenshotListening(),
() => basePlatform.startScreenshotListening(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
test( test(
'base NoScreenshotPlatform.stopScreenshotListening() throws UnimplementedError', 'base NoScreenshotPlatform.stopScreenshotListening() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(() => basePlatform.stopScreenshotListening(),
() => basePlatform.stopScreenshotListening(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
test( test(
'startScreenRecordingListening should not throw UnimplementedError when called', 'startScreenRecordingListening should not throw UnimplementedError when called',
() async { () async {
expect(platform.startScreenRecordingListening(), completes); expect(platform.startScreenRecordingListening(), completes);
}, });
);
test( test(
'stopScreenRecordingListening should not throw UnimplementedError when called', 'stopScreenRecordingListening should not throw UnimplementedError when called',
() async { () async {
expect(platform.stopScreenRecordingListening(), completes); expect(platform.stopScreenRecordingListening(), completes);
}, });
);
test( test(
'base NoScreenshotPlatform.startScreenRecordingListening() throws UnimplementedError', 'base NoScreenshotPlatform.startScreenRecordingListening() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(() => basePlatform.startScreenRecordingListening(),
() => basePlatform.startScreenRecordingListening(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
test( test(
'base NoScreenshotPlatform.stopScreenRecordingListening() throws UnimplementedError', 'base NoScreenshotPlatform.stopScreenRecordingListening() throws UnimplementedError',
() { () {
final basePlatform = BaseNoScreenshotPlatform(); final basePlatform = BaseNoScreenshotPlatform();
expect( expect(() => basePlatform.stopScreenRecordingListening(),
() => basePlatform.stopScreenRecordingListening(), throwsUnimplementedError);
throwsUnimplementedError, });
);
},
);
}); });
} }

View file

@ -115,10 +115,8 @@ void main() {
}); });
test('screenshotStream', () async { test('screenshotStream', () async {
expect( expect(NoScreenshot.instance.screenshotStream,
NoScreenshot.instance.screenshotStream, isInstanceOf<Stream<ScreenshotSnapshot>>());
isInstanceOf<Stream<ScreenshotSnapshot>>(),
);
}); });
test('startScreenshotListening', () async { test('startScreenshotListening', () async {
expect(NoScreenshot.instance.startScreenshotListening(), completes); expect(NoScreenshot.instance.startScreenshotListening(), completes);
@ -139,8 +137,7 @@ void main() {
test('toggleScreenshotWithBlur with custom radius', () async { test('toggleScreenshotWithBlur with custom radius', () async {
expect( expect(
await NoScreenshot.instance.toggleScreenshotWithBlur(blurRadius: 50.0), await NoScreenshot.instance.toggleScreenshotWithBlur(blurRadius: 50.0),
true, true);
);
}); });
test('toggleScreenshotWithColor', () async { test('toggleScreenshotWithColor', () async {
@ -149,9 +146,9 @@ void main() {
test('toggleScreenshotWithColor with custom color', () async { test('toggleScreenshotWithColor with custom color', () async {
expect( expect(
await NoScreenshot.instance.toggleScreenshotWithColor(color: 0xFFFF0000), await NoScreenshot.instance
true, .toggleScreenshotWithColor(color: 0xFFFF0000),
); true);
}); });
test('screenshotWithImage', () async { test('screenshotWithImage', () async {
@ -164,9 +161,7 @@ void main() {
test('screenshotWithBlur with custom radius', () async { test('screenshotWithBlur with custom radius', () async {
expect( expect(
await NoScreenshot.instance.screenshotWithBlur(blurRadius: 50.0), await NoScreenshot.instance.screenshotWithBlur(blurRadius: 50.0), true);
true,
);
}); });
test('screenshotWithColor', () async { test('screenshotWithColor', () async {
@ -174,10 +169,8 @@ void main() {
}); });
test('screenshotWithColor with custom color', () async { test('screenshotWithColor with custom color', () async {
expect( expect(await NoScreenshot.instance.screenshotWithColor(color: 0xFFFF0000),
await NoScreenshot.instance.screenshotWithColor(color: 0xFFFF0000), true);
true,
);
}); });
test('NoScreenshot equality operator', () { test('NoScreenshot equality operator', () {

View file

@ -132,7 +132,9 @@ void main() {
test('didPop applies policy for previous route', () async { test('didPop applies policy for previous route', () async {
final observer = SecureNavigatorObserver( final observer = SecureNavigatorObserver(
policies: {'/home': const SecureRouteConfig(mode: OverlayMode.none)}, policies: {
'/home': const SecureRouteConfig(mode: OverlayMode.none),
},
); );
observer.didPop(_fakeRoute('/payment'), _fakeRoute('/home')); observer.didPop(_fakeRoute('/payment'), _fakeRoute('/home'));
@ -143,24 +145,22 @@ void main() {
test('didReplace applies policy for new route', () async { test('didReplace applies policy for new route', () async {
final observer = SecureNavigatorObserver( final observer = SecureNavigatorObserver(
policies: { policies: {
'/profile': const SecureRouteConfig( '/profile':
mode: OverlayMode.blur, const SecureRouteConfig(mode: OverlayMode.blur, blurRadius: 50.0),
blurRadius: 50.0,
),
}, },
); );
observer.didReplace( observer.didReplace(
newRoute: _fakeRoute('/profile'), newRoute: _fakeRoute('/profile'), oldRoute: _fakeRoute('/home'));
oldRoute: _fakeRoute('/home'),
);
await Future<void>.delayed(Duration.zero); await Future<void>.delayed(Duration.zero);
expect(fakePlatform.calls, contains('screenshotWithBlur(50.0)')); expect(fakePlatform.calls, contains('screenshotWithBlur(50.0)'));
}); });
test('didRemove applies policy for previous route', () async { test('didRemove applies policy for previous route', () async {
final observer = SecureNavigatorObserver( final observer = SecureNavigatorObserver(
policies: {'/home': const SecureRouteConfig(mode: OverlayMode.none)}, policies: {
'/home': const SecureRouteConfig(mode: OverlayMode.none),
},
); );
observer.didRemove(_fakeRoute('/payment'), _fakeRoute('/home')); observer.didRemove(_fakeRoute('/payment'), _fakeRoute('/home'));
@ -232,9 +232,7 @@ void main() {
observer.didPush(_fakeRoute('/branded'), null); observer.didPush(_fakeRoute('/branded'), null);
await Future<void>.delayed(Duration.zero); await Future<void>.delayed(Duration.zero);
expect( expect(
fakePlatform.calls, fakePlatform.calls, contains('screenshotWithColor(${0xFF2196F3})'));
contains('screenshotWithColor(${0xFF2196F3})'),
);
}); });
}); });
} }

View file

@ -85,14 +85,15 @@ void main() {
}); });
testWidgets('default mode is OverlayMode.secure', (tester) async { testWidgets('default mode is OverlayMode.secure', (tester) async {
await tester.pumpWidget(const SecureWidget(child: SizedBox())); await tester.pumpWidget(
const SecureWidget(child: SizedBox()),
);
await tester.pump(); await tester.pump();
expect(fakePlatform.calls, contains('screenshotOff')); expect(fakePlatform.calls, contains('screenshotOff'));
}); });
testWidgets('initState calls screenshotOff for OverlayMode.secure', ( testWidgets('initState calls screenshotOff for OverlayMode.secure',
tester, (tester) async {
) async {
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.secure, child: SizedBox()), const SecureWidget(mode: OverlayMode.secure, child: SizedBox()),
); );
@ -100,9 +101,8 @@ void main() {
expect(fakePlatform.calls, contains('screenshotOff')); expect(fakePlatform.calls, contains('screenshotOff'));
}); });
testWidgets('initState calls screenshotWithBlur for OverlayMode.blur', ( testWidgets('initState calls screenshotWithBlur for OverlayMode.blur',
tester, (tester) async {
) async {
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.blur, child: SizedBox()), const SecureWidget(mode: OverlayMode.blur, child: SizedBox()),
); );
@ -110,9 +110,8 @@ void main() {
expect(fakePlatform.calls, contains('screenshotWithBlur(30.0)')); expect(fakePlatform.calls, contains('screenshotWithBlur(30.0)'));
}); });
testWidgets('initState calls screenshotWithColor for OverlayMode.color', ( testWidgets('initState calls screenshotWithColor for OverlayMode.color',
tester, (tester) async {
) async {
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.color, child: SizedBox()), const SecureWidget(mode: OverlayMode.color, child: SizedBox()),
); );
@ -120,9 +119,8 @@ void main() {
expect(fakePlatform.calls, contains('screenshotWithColor(4278190080)')); expect(fakePlatform.calls, contains('screenshotWithColor(4278190080)'));
}); });
testWidgets('initState calls screenshotWithImage for OverlayMode.image', ( testWidgets('initState calls screenshotWithImage for OverlayMode.image',
tester, (tester) async {
) async {
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.image, child: SizedBox()), const SecureWidget(mode: OverlayMode.image, child: SizedBox()),
); );
@ -130,9 +128,8 @@ void main() {
expect(fakePlatform.calls, contains('screenshotWithImage')); expect(fakePlatform.calls, contains('screenshotWithImage'));
}); });
testWidgets('initState calls screenshotOn for OverlayMode.none', ( testWidgets('initState calls screenshotOn for OverlayMode.none',
tester, (tester) async {
) async {
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.none, child: SizedBox()), const SecureWidget(mode: OverlayMode.none, child: SizedBox()),
); );
@ -141,7 +138,9 @@ void main() {
}); });
testWidgets('dispose calls screenshotOn', (tester) async { testWidgets('dispose calls screenshotOn', (tester) async {
await tester.pumpWidget(const SecureWidget(child: SizedBox())); await tester.pumpWidget(
const SecureWidget(child: SizedBox()),
);
await tester.pump(); await tester.pump();
fakePlatform.calls.clear(); fakePlatform.calls.clear();
@ -165,25 +164,18 @@ void main() {
expect(fakePlatform.calls, contains('screenshotWithBlur(30.0)')); expect(fakePlatform.calls, contains('screenshotWithBlur(30.0)'));
}); });
testWidgets('didUpdateWidget re-applies when blurRadius changes', ( testWidgets('didUpdateWidget re-applies when blurRadius changes',
tester, (tester) async {
) async {
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget( const SecureWidget(
mode: OverlayMode.blur, mode: OverlayMode.blur, blurRadius: 30.0, child: SizedBox()),
blurRadius: 30.0,
child: SizedBox(),
),
); );
await tester.pump(); await tester.pump();
fakePlatform.calls.clear(); fakePlatform.calls.clear();
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget( const SecureWidget(
mode: OverlayMode.blur, mode: OverlayMode.blur, blurRadius: 50.0, child: SizedBox()),
blurRadius: 50.0,
child: SizedBox(),
),
); );
await tester.pump(); await tester.pump();
expect(fakePlatform.calls, contains('screenshotWithBlur(50.0)')); expect(fakePlatform.calls, contains('screenshotWithBlur(50.0)'));
@ -198,18 +190,14 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget( const SecureWidget(
mode: OverlayMode.color, mode: OverlayMode.color, color: 0xFFFF0000, child: SizedBox()),
color: 0xFFFF0000,
child: SizedBox(),
),
); );
await tester.pump(); await tester.pump();
expect(fakePlatform.calls, contains('screenshotWithColor(4294901760)')); expect(fakePlatform.calls, contains('screenshotWithColor(4294901760)'));
}); });
testWidgets('didUpdateWidget does not re-apply when nothing changes', ( testWidgets('didUpdateWidget does not re-apply when nothing changes',
tester, (tester) async {
) async {
await tester.pumpWidget( await tester.pumpWidget(
const SecureWidget(mode: OverlayMode.secure, child: SizedBox()), const SecureWidget(mode: OverlayMode.secure, child: SizedBox()),
); );