diff --git a/android/build.gradle b/android/build.gradle index bd8fd63..6ec925a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,6 +20,7 @@ rootProject.allprojects { } apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { compileSdkVersion 30 @@ -32,3 +33,10 @@ android { disable 'InvalidPackage' } } +dependencies { + implementation "androidx.core:core-ktx:+" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} +repositories { + mavenCentral() +} diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 3afbad6..31eb36d 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -2,5 +2,5 @@ package="com.flutter.moum.screenshot_callback"> - + diff --git a/android/src/main/java/com/flutter/moum/screenshot_callback/Path.java b/android/src/main/java/com/flutter/moum/screenshot_callback/Path.java deleted file mode 100644 index 4d14724..0000000 --- a/android/src/main/java/com/flutter/moum/screenshot_callback/Path.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.flutter.moum.screenshot_callback; - -import android.os.Environment; - -import java.io.File; - -public enum Path { - DCIM(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + "Screenshots" + File.separator), - PICTURES(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES ) + File.separator + "Screenshots" + File.separator); - - final private String path; - - public String getPath() { - return path; - } - - private Path(String path) { - this. path = path; - - } -} diff --git a/android/src/main/java/com/flutter/moum/screenshot_callback/ScreenshotCallbackPlugin.java b/android/src/main/java/com/flutter/moum/screenshot_callback/ScreenshotCallbackPlugin.java index 4329f2b..c19f9ee 100644 --- a/android/src/main/java/com/flutter/moum/screenshot_callback/ScreenshotCallbackPlugin.java +++ b/android/src/main/java/com/flutter/moum/screenshot_callback/ScreenshotCallbackPlugin.java @@ -1,87 +1,70 @@ package com.flutter.moum.screenshot_callback; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; + 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 io.flutter.plugin.common.PluginRegistry.Registrar; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; -import android.os.Build; -import android.os.FileObserver; - -import android.os.Handler; -import android.os.Looper; //import android.util.Log; -import java.io.File; -import java.util.List; -import java.util.ArrayList; - public class ScreenshotCallbackPlugin implements MethodCallHandler { - private static MethodChannel channel; + ScreenshotCallbackPlugin(Context context) { + _context = context; + } + + private static MethodChannel _channel; + private static final String _tag = "screenshot_callback"; - private Handler handler; - private FileObserver fileObserver; - private String TAG = "tag"; + private final Context _context; + private Handler _handler; + private ScreenshotDetector _detector; + private String _lastScreenshotName; public static void registerWith(Registrar registrar) { - channel = new MethodChannel(registrar.messenger(), "flutter.moum/screenshot_callback"); - channel.setMethodCallHandler(new ScreenshotCallbackPlugin()); + _channel = new MethodChannel(registrar.messenger(), "flutter.moum/screenshot_callback"); + _channel.setMethodCallHandler(new ScreenshotCallbackPlugin(registrar.context())); } @Override public void onMethodCall(MethodCall call, Result result) { - //Log.d(TAG, "onMethodCall: "); - if (call.method.equals("initialize")) { - handler = new Handler(Looper.getMainLooper()); - if (Build.VERSION.SDK_INT >= 29) { - //Log.d(TAG, "android x"); - List files = new ArrayList(); - for (Path path : Path.values()) { - files.add(new File(path.getPath())); - } + _handler = new Handler(Looper.getMainLooper()); - fileObserver = new FileObserver(files, FileObserver.CREATE) { - @Override - public void onEvent(int event, String path) { - //Log.d(TAG, "androidX onEvent"); - if (event == FileObserver.CREATE) { - handler.post(new Runnable() { - @Override - public void run() { - channel.invokeMethod("onCallback", null); - } - }); - } - } - }; - fileObserver.startWatching(); - } else { - //Log.d(TAG, "android others"); - for (Path path : Path.values()) { - //Log.d(TAG, "onMethodCall: "+path.getPath()); - fileObserver = new FileObserver(path.getPath(), FileObserver.CREATE) { - @Override - public void onEvent(int event, String path) { - //Log.d(TAG, "android others onEvent"); - if (event == FileObserver.CREATE) { - handler.post(new Runnable() { - @Override - public void run() { - channel.invokeMethod("onCallback", null); - } - }); + _detector = new ScreenshotDetector(_context, new Function1() { + @Override + public Unit invoke(String screenshotName) { +// Log.d(_tag, "onScreenshotDetected: " + screenshotName); + + if (!screenshotName.equals(_lastScreenshotName)) { + _lastScreenshotName = screenshotName; + _handler.post(new Runnable() { + @Override + public void run() { +// Log.d(_tag, "onCallback: "); + _channel.invokeMethod("onCallback", null); } - } - }; - fileObserver.startWatching(); + }); + } + + return null; } - } + }); + _detector.start(); + result.success("initialize"); } else if (call.method.equals("dispose")) { - fileObserver.stopWatching(); + _detector.stop(); + _detector = null; + _lastScreenshotName = null; + result.success("dispose"); } else { result.notImplemented(); diff --git a/android/src/main/java/com/flutter/moum/screenshot_callback/ScreenshotDetector.kt b/android/src/main/java/com/flutter/moum/screenshot_callback/ScreenshotDetector.kt new file mode 100644 index 0000000..0f4a64e --- /dev/null +++ b/android/src/main/java/com/flutter/moum/screenshot_callback/ScreenshotDetector.kt @@ -0,0 +1,111 @@ +package com.flutter.moum.screenshot_callback + +import android.content.ContentResolver +import android.content.Context +import android.database.ContentObserver +import android.net.Uri +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.provider.MediaStore + +class ScreenshotDetector(private val context: Context, + private val callback: (name: String) -> Unit) { + + private var contentObserver: ContentObserver? = null + + fun start() { + if (contentObserver == null) { + contentObserver = context.contentResolver.registerObserver() + } + } + + fun stop() { + contentObserver?.let { context.contentResolver.unregisterContentObserver(it) } + contentObserver = null + } + + private fun reportScreenshotsUpdate(uri: Uri) { + val screenshots: List = queryScreenshots(uri) + if (screenshots.isNotEmpty()) { + callback.invoke(screenshots.last()); + } + } + + private fun queryScreenshots(uri: Uri): List { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + queryRelativeDataColumn(uri) + } else { + queryDataColumn(uri) + } + } + + private fun queryDataColumn(uri: Uri): List { + val screenshots = mutableListOf() + + val projection = arrayOf( + MediaStore.Images.Media.DATA + ) + context.contentResolver.query( + uri, + projection, + null, + null, + null + )?.use { cursor -> + val dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA) + + while (cursor.moveToNext()) { + val path = cursor.getString(dataColumn) + if (path.contains("screenshot", true)) { + screenshots.add(path) + } + } + } + + return screenshots + } + + private fun queryRelativeDataColumn(uri: Uri): List { + val screenshots = mutableListOf() + + val projection = arrayOf( + MediaStore.Images.Media.DISPLAY_NAME, + MediaStore.Images.Media.RELATIVE_PATH + ) + context.contentResolver.query( + uri, + projection, + null, + null, + null + )?.use { cursor -> + val relativePathColumn = + cursor.getColumnIndex(MediaStore.Images.Media.RELATIVE_PATH) + val displayNameColumn = + cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME) + while (cursor.moveToNext()) { + val name = cursor.getString(displayNameColumn) + val relativePath = cursor.getString(relativePathColumn) + if (name.contains("screenshot", true) or + relativePath.contains("screenshot", true) + ) { + screenshots.add(name) + } + } + } + + return screenshots + } + + private fun ContentResolver.registerObserver(): ContentObserver { + val contentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) { + override fun onChange(selfChange: Boolean, uri: Uri?) { + super.onChange(selfChange, uri) + uri?.let { reportScreenshotsUpdate(it) } + } + } + registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, contentObserver) + return contentObserver + } +} \ No newline at end of file diff --git a/example/android/build.gradle b/example/android/build.gradle index 6de3728..24ab9b4 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,4 +1,5 @@ buildscript { + ext.kotlin_version = '1.5.0' repositories { google() jcenter() @@ -6,6 +7,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.5.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/pubspec.lock b/example/pubspec.lock index c5d0564..d95a3c1 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -87,14 +87,14 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "5.0.1+1" + version: "5.1.0+2" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" plugin_platform_interface: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index 02f6f7c..35e329b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -87,14 +87,14 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "5.0.1+1" + version: "5.1.0+2" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" plugin_platform_interface: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c37b05e..5163dfa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,7 @@ environment: dependencies: flutter: sdk: flutter - permission_handler: ^5.0.1+1 + permission_handler: ^5.1.0+2 dev_dependencies: flutter_test: