diff --git a/.vscode/launch.json b/.vscode/launch.json index df5a726..995076f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,39 +7,8 @@ { "name": "no_screenshot", "request": "launch", - "type": "dart" - }, - { - "name": "no_screenshot (profile mode)", - "request": "launch", - "type": "dart", - "flutterMode": "profile" - }, - { - "name": "no_screenshot (release mode)", - "request": "launch", - "type": "dart", - "flutterMode": "release" - }, - { - "name": "example", - "cwd": "example", - "request": "launch", - "type": "dart" - }, - { - "name": "example (profile mode)", - "cwd": "example", - "request": "launch", - "type": "dart", - "flutterMode": "profile" - }, - { - "name": "example (release mode)", - "cwd": "example", - "request": "launch", - "type": "dart", - "flutterMode": "release" + "type": "dart", + "program": "example/lib/main.dart" } ] } \ No newline at end of file diff --git a/LICENSE b/LICENSE index 5316aed..8130832 100644 --- a/LICENSE +++ b/LICENSE @@ -1,26 +1,26 @@ -Copyright (c) 2022, FlutterPlaza -All rights reserved. +Copyright (c) 2022, FlutterPlaza +All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of FlutterPlaza nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of FlutterPlaza nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 1d3ed0e..0eae471 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ If you want to prevent user from taking screenshot or recording of your app. You ```dart class _MyHomePageState extends State with WidgetsBindingObserver { - final _noScreenshot = NoScreenshot(); + final _noScreenshot = NoScreenshot.instance; AppLifecycleState? _notification; @override diff --git a/android/src/main/kotlin/com/flutterplaza/no_screenshot/NoScreenshotPlugin.kt b/android/src/main/kotlin/com/flutterplaza/no_screenshot/NoScreenshotPlugin.kt index bd7dc60..57ee82a 100644 --- a/android/src/main/kotlin/com/flutterplaza/no_screenshot/NoScreenshotPlugin.kt +++ b/android/src/main/kotlin/com/flutterplaza/no_screenshot/NoScreenshotPlugin.kt @@ -9,71 +9,89 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.LayoutParams import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +const val SCREENSHOT_ON_CONST = "screenshotOn" +const val SCREENSHOT_OFF_CONST = "screenshotOff" +const val TOGGLE_SCREENSHOT_CONST = "toggleScreenshot" /** NoScreenshotPlugin */ -class NoScreenshotPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private lateinit var channel : MethodChannel - private lateinit var context: Context - private lateinit var activity: Activity - - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.flutterplaza.no_screenshot") - channel.setMethodCallHandler(this) - context = flutterPluginBinding.applicationContext - - } - - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) = - if (call.method == "screenshotOff") { - screenshotOff(); - result.success(true); - } - else if(call.method == "screenshotOn"){ - screenshotOn(); - result.success(true); - } - else if(call.method == "toggleScreenshot"){ - var flags: Int = activity.window.attributes.flags; - if( (flags and LayoutParams.FLAG_SECURE) != 0){ - screenshotOn(); - }else { - screenshotOff(); - } - result.success(true); +class NoScreenshotPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel: MethodChannel + private lateinit var context: Context + private lateinit var activity: Activity + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = + MethodChannel(flutterPluginBinding.binaryMessenger, "com.flutterplaza.no_screenshot") + channel.setMethodCallHandler(this) + context = flutterPluginBinding.applicationContext + } - else { - result.notImplemented() + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) = + when (call.method) { + SCREENSHOT_OFF_CONST -> { + val value: Boolean = screenshotOff() + result.success(value) + } + SCREENSHOT_ON_CONST -> { + val value = screenshotOn() + + result.success(value) + } + TOGGLE_SCREENSHOT_CONST -> { + val flags: Int = activity.window.attributes.flags + if ((flags and LayoutParams.FLAG_SECURE) != 0) { + screenshotOn() + } else { + screenshotOff() + } + result.success(true) + } + else -> { + result.notImplemented() + } + } + + private fun screenshotOff(): Boolean { + try { + activity.window.addFlags(LayoutParams.FLAG_SECURE) + return true + } catch (e: Exception) { + return false + } } - private fun screenshotOff(){ - activity.window.addFlags(LayoutParams.FLAG_SECURE); - } - private fun screenshotOn(){ - activity.window.clearFlags(LayoutParams.FLAG_SECURE); - } + private fun screenshotOn() : Boolean{ + try { + activity.window.clearFlags(LayoutParams.FLAG_SECURE) + return true + } catch (e: Exception) { + return false + } + } - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } - override fun onAttachedToActivity(binding: ActivityPluginBinding) { - activity = binding.activity; - } + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + activity = binding.activity + } override fun onDetachedFromActivityForConfigChanges() {} - override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { - activity = binding.activity; + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + activity = binding.activity - } + } override fun onDetachedFromActivity() {} } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index b4dc4b1..181459e 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -43,7 +43,6 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.flutterplaza.no_screenshot_example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. diff --git a/example/lib/main.dart b/example/lib/main.dart index 8782baa..bed57bb 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -35,21 +35,21 @@ class _MyAppState extends State { child: const Text('Press to toggle screenshot'), onPressed: () async { final result = await _noScreenshot.toggleScreenshot(); - print(result); + debugPrint(result.toString()); }, ), ElevatedButton( child: const Text('Press to turn off screenshot'), onPressed: () async { final result = await _noScreenshot.screenshotOff(); - print(result); + debugPrint(result.toString()); }, ), ElevatedButton( child: const Text('Press to turn on screenshot'), onPressed: () async { final result = await _noScreenshot.screenshotOn(); - print(result); + debugPrint(result.toString()); }, ), ], diff --git a/example/pubspec.lock b/example/pubspec.lock index b5c4ac3..76c0646 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" cupertino_icons: dependency: "direct main" description: @@ -75,14 +75,30 @@ packages: description: flutter source: sdk version: "0.0.0" - js: + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: dependency: transitive description: - name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "3.0.1" lints: dependency: transitive description: @@ -95,26 +111,26 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.12.0" no_screenshot: dependency: "direct main" description: @@ -126,10 +142,10 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.9.0" plugin_platform_interface: dependency: transitive description: @@ -147,26 +163,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -187,10 +203,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.7.0" vector_math: dependency: transitive description: @@ -199,6 +215,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" sdks: - dart: ">=2.18.2 <3.0.0" - flutter: ">=2.5.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/no_screenshot.dart b/lib/no_screenshot.dart index de3e9e5..356b878 100644 --- a/lib/no_screenshot.dart +++ b/lib/no_screenshot.dart @@ -4,6 +4,9 @@ class NoScreenshot implements NoScreenshotPlatform { final _instancePlatform = NoScreenshotPlatform.instance; NoScreenshot._(); + @Deprecated("Using this may cause issue\nUse instance directly\ne.g: 'NoScreenshot.instance.screenshotOff()'") + NoScreenshot(); + /// Made `NoScreenshot` class a singleton static NoScreenshot get instance => NoScreenshot._(); diff --git a/test/no_screenshot_test.dart b/test/no_screenshot_test.dart new file mode 100644 index 0000000..084e66b --- /dev/null +++ b/test/no_screenshot_test.dart @@ -0,0 +1,50 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:no_screenshot/no_screenshot_platform_interface.dart'; +import 'package:no_screenshot/no_screenshot_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockNoScreenshotPlatform + with MockPlatformInterfaceMixin + implements NoScreenshotPlatform { + @override + Future screenshotOff() async { + // Mock implementation or return a fixed value + return Future.value(true); + } + + @override + Future screenshotOn() async { + // Mock implementation or return a fixed value + return Future.value(true); + } + + @override + Future toggleScreenshot() async { + // Mock implementation or return a fixed value + return Future.value(true); + } +} + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + final NoScreenshotPlatform initialPlatform = NoScreenshotPlatform.instance; + MockNoScreenshotPlatform fakePlatform = MockNoScreenshotPlatform(); + + test('$MethodChannelNoScreenshot is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('screenshotOn', () async { + expect(await fakePlatform.screenshotOn(), true); + }); + // screenshotOff + test('screenshotOff', () async { + expect(await fakePlatform.screenshotOff(), true); + }); + + // toggleScreenshot + test('toggleScreenshot', () async { + expect(await fakePlatform.toggleScreenshot(), true); + }); +} diff --git a/test_driver/app.dart b/test_driver/app.dart index 446df9e..f7cb2aa 100644 --- a/test_driver/app.dart +++ b/test_driver/app.dart @@ -1,5 +1,4 @@ import 'package:flutter_driver/driver_extension.dart'; -import 'app_test.dart'; import '../example/lib/main.dart' as app; void main() { diff --git a/test_driver/app_test.dart b/test_driver/app_test.dart index 021a285..675e696 100644 --- a/test_driver/app_test.dart +++ b/test_driver/app_test.dart @@ -13,7 +13,9 @@ void main() { setUp(() async { driver = await FlutterDriver.connect(); - channel.setMockMethodCallHandler((MethodCall methodCall) async { + // Updated deprecated method usage + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { switch (methodCall.method) { case screenShotOffConst: break; @@ -71,7 +73,9 @@ void main() { }); }); tearDown(() { - channel.setMockMethodCallHandler(null); + // Updated deprecated method usage + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); driver.close(); }); } diff --git a/test_driver/no_screenshot_test.dart b/test_driver/no_screenshot_test.dart deleted file mode 100644 index 6329351..0000000 --- a/test_driver/no_screenshot_test.dart +++ /dev/null @@ -1,41 +0,0 @@ -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:no_screenshot/no_screenshot.dart'; -// import 'package:no_screenshot/no_screenshot_platform_interface.dart'; -// import 'package:no_screenshot/no_screenshot_method_channel.dart'; -// import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -// import 'package:screenshots/screenshots.dart'; - -// class MockNoScreenshotPlatform -// with MockPlatformInterfaceMixin -// implements NoScreenshotPlatform { - -// @override -// Future screenshotOff() { - -// } - -// @override -// Future screenshotOn() { -// } - -// @override -// Future toggleScreenshot() { - -// } -// } - -// void main() { -// final NoScreenshotPlatform initialPlatform = NoScreenshotPlatform.instance; - -// test('$MethodChannelNoScreenshot is the default instance', () { -// expect(initialPlatform, isInstanceOf()); -// }); - -// test('getPlatformVersion', () async { -// NoScreenshot noScreenshotPlugin = NoScreenshot(); -// MockNoScreenshotPlatform fakePlatform = MockNoScreenshotPlatform(); -// NoScreenshotPlatform.instance = fakePlatform; - -// expect(await noScreenshotPlugin.getPlatformVersion(), '42'); -// }); -// }