仅使用FileObserver Android检测屏幕截图

Nac*_*ing 14 android screenshot detect capture

我目前正在开发Android应用程序,并想知道如何检测屏幕截图.我尝试使用FileObserver,但问题是检测到所有事件(当设备进入睡眠状态,消息等).如何只检测截图?

先感谢您 !

ali*_*dro 24

你是如何FileObserver用来检测屏幕截图的?使用时FileObserver,仅监视屏幕截图目录中的文件创建事件.

    String path = Environment.getExternalStorageDirectory()
            + File.separator + Environment.DIRECTORY_PICTURES
            + File.separator + "Screenshots" + File.separator;
    Log.d(TAG, path);

    FileObserver fileObserver = new FileObserver(path, FileObserver.CREATE) {
        @Override
        public void onEvent(int event, String path) {
            Log.d(TAG, event + " " + path);
        }
    };

    fileObserver.startWatching();
Run Code Online (Sandbox Code Playgroud)

不要忘记声明相应的权限来访问SD卡中的内容.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Run Code Online (Sandbox Code Playgroud)

另一种检测屏幕截图的方法是使用ContentObserver,因为屏幕截图后会有一条记录插入系统媒体数据库.以下是ContentObserver用于监视事件的代码段.通过使用ContentObserver,没有必要声明write/read external storage权限,但你必须对文件名做一些过滤,以确保它是一个屏幕截图事件.

    HandlerThread handlerThread = new HandlerThread("content_observer");
    handlerThread.start();
    final Handler handler = new Handler(handlerThread.getLooper()) {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    getContentResolver().registerContentObserver(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            true,
            new ContentObserver(handler) {
                @Override
                public boolean deliverSelfNotifications() {
                    Log.d(TAG, "deliverSelfNotifications");
                    return super.deliverSelfNotifications();
                }

                @Override
                public void onChange(boolean selfChange) {
                    super.onChange(selfChange);
                }

                @Override
                public void onChange(boolean selfChange, Uri uri) {
                    Log.d(TAG, "onChange " + uri.toString());
                    if (uri.toString().matches(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/[0-9]+")) {

                        Cursor cursor = null;
                        try {
                            cursor = getContentResolver().query(uri, new String[] {
                                    MediaStore.Images.Media.DISPLAY_NAME,
                                    MediaStore.Images.Media.DATA
                            }, null, null, null);
                            if (cursor != null && cursor.moveToFirst()) {
                                final String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
                                final String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                                // TODO: apply filter on the file name to ensure it's screen shot event
                                Log.d(TAG, "screen shot added " + fileName + " " + path);
                            }
                        } finally {
                            if (cursor != null)  {
                                cursor.close();
                            }
                        }
                    }
                    super.onChange(selfChange, uri);
                }
            }
    );
Run Code Online (Sandbox Code Playgroud)

更新

如果您使用第二种方法,则必须READ_EXTERNAL_STORAGE在Android M版本之后请求,否则它将抛出SecurityException.有关如何请求运行时权限的更多信息,请参阅此处.


Ake*_*ist 5

我改进了 alijandro 评论中的代码,使其易于使用,并修复了内容观察器检测到来自相机的图像时的问题(应该只是屏幕截图图像)。然后将其包装到委托类中以方便使用。

\n\n
\n\n

\xe2\x80\xa2 ScreenshotDetectionDelegate.java

\n\n
public class ScreenshotDetectionDelegate {\n    private WeakReference<Activity> activityWeakReference;\n    private ScreenshotDetectionListener listener;\n\n    public ScreenshotDetectionDelegate(Activity activityWeakReference, ScreenshotDetectionListener listener) {\n        this.activityWeakReference = new WeakReference<>(activityWeakReference);\n        this.listener = listener;\n    }\n\n    public void startScreenshotDetection() {\n        activityWeakReference.get()\n                .getContentResolver()\n                .registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, contentObserver);\n    }\n\n    public void stopScreenshotDetection() {\n        activityWeakReference.get().getContentResolver().unregisterContentObserver(contentObserver);\n    }\n\n    private ContentObserver contentObserver = new ContentObserver(new Handler()) {\n        @Override\n        public boolean deliverSelfNotifications() {\n            return super.deliverSelfNotifications();\n        }\n\n        @Override\n        public void onChange(boolean selfChange) {\n            super.onChange(selfChange);\n        }\n\n        @Override\n        public void onChange(boolean selfChange, Uri uri) {\n            super.onChange(selfChange, uri);\n            if (isReadExternalStoragePermissionGranted()) {\n                String path = getFilePathFromContentResolver(activityWeakReference.get(), uri);\n                if (isScreenshotPath(path)) {\n                    onScreenCaptured(path);\n                }\n            } else {\n                onScreenCapturedWithDeniedPermission();\n            }\n        }\n    };\n\n    private void onScreenCaptured(String path) {\n        if (listener != null) {\n            listener.onScreenCaptured(path);\n        }\n    }\n\n    private void onScreenCapturedWithDeniedPermission() {\n        if (listener != null) {\n            listener.onScreenCapturedWithDeniedPermission();\n        }\n    }\n\n    private boolean isScreenshotPath(String path) {\n        return path != null && path.toLowerCase().contains("screenshots");\n    }\n\n    private String getFilePathFromContentResolver(Context context, Uri uri) {\n        try {\n            Cursor cursor = context.getContentResolver().query(uri, new String[]{\n                    MediaStore.Images.Media.DISPLAY_NAME,\n                    MediaStore.Images.Media.DATA\n            }, null, null, null);\n            if (cursor != null && cursor.moveToFirst()) {\n                String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));\n                cursor.close();\n                return path;\n            }\n        } catch (IllegalStateException ignored) {\n        }\n        return null;\n    }\n\n    private boolean isReadExternalStoragePermissionGranted() {\n        return ContextCompat.checkSelfPermission(activityWeakReference.get(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;\n    }\n\n    public interface ScreenshotDetectionListener {\n        void onScreenCaptured(String path);\n\n        void onScreenCapturedWithDeniedPermission();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

\xe2\x80\xa2 ScreenshotDetectionActivity.java

\n\n
import android.Manifest;\nimport android.content.pm.PackageManager;\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.app.AppCompatActivity;\nimport android.widget.Toast;\n\npublic abstract class ScreenshotDetectionActivity extends AppCompatActivity implements ScreenshotDetectionDelegate.ScreenshotDetectionListener {\n    private static final int REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION = 3009;\n\n    private ScreenshotDetectionDelegate screenshotDetectionDelegate = new ScreenshotDetectionDelegate(this, this);\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        checkReadExternalStoragePermission();\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        screenshotDetectionDelegate.startScreenshotDetection();\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        screenshotDetectionDelegate.stopScreenshotDetection();\n    }\n\n    @Override\n    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n        switch (requestCode) {\n            case REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION:\n                if (grantResults[0] == PackageManager.PERMISSION_DENIED) {\n                    showReadExternalStoragePermissionDeniedMessage();\n                }\n                break;\n            default:\n                super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n        }\n    }\n\n    @Override\n    public void onScreenCaptured(String path) {\n        // Do something when screen was captured\n    }\n\n    @Override\n    public void onScreenCapturedWithDeniedPermission() {\n        // Do something when screen was captured but read external storage permission has denied\n    }\n\n    private void checkReadExternalStoragePermission() {\n        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {\n            requestReadExternalStoragePermission();\n        }\n    }\n\n    private void requestReadExternalStoragePermission() {\n        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION);\n    }\n\n    private void showReadExternalStoragePermissionDeniedMessage() {\n        Toast.makeText(this, "Read external storage permission has denied", Toast.LENGTH_SHORT).show();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

\xe2\x80\xa2 MainActivity.java

\n\n
import android.os.Bundle;\nimport android.view.View;\nimport android.widget.Toast;\n\npublic class MainActivity extends ScreenshotDetectionActivity {\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n    }\n\n    @Override\n    public void onScreenCaptured(String path) {\n        Toast.make(this, path, Toast.LENGTH_SHORT).show();\n    }\n\n    @Override\n    public void onScreenCapturedWithDeniedPermission() {\n        Toast.make(this, "Please grant read external storage permission for screenshot detection", Toast.LENGTH_SHORT).show();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n