web-dev-qa-db-ja.com

FileObserver Android)でスクリーンショットのみを検出する

私は現在Androidのアプリケーションを開発していて、スクリーンショットを検出する方法を知りたいと思っていました。FileObserverで試しましたが、問題はすべてのイベントが検出されることです(デバイスがスリープ状態になったとき、メッセージなど) 。)。スクリーンショットのみを検出する方法は?

前もって感謝します !

15
Nachding

スクリーンショットの作成を検出するために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();

SDカードのコンテンツにアクセスするための対応する権限を宣言することを忘れないでください。

<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />

スクリーンショットを検出する別の解決策は、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);
                }
            }
    );

更新

2番目の方法を使用する場合は、バージョンAndroid Mの後にREAD_EXTERNAL_STORAGEをリクエストする必要があります。そうしないと、SecurityExceptionがスローされます。ランタイム権限をリクエストする方法の詳細については、を参照してください。 ここ

23
alijandro

Alijandroのコメントのコードを改善して、使いやすいクラスにし、コンテンツオブザーバーがカメラからの画像を検出したときの問題を修正しました(スクリーンショット画像のみである必要があります)。次に、使いやすいようにそれをデリゲートクラスにラップします。


•ScreenshotDetectionDelegate.Java

public class ScreenshotDetectionDelegate {
    private WeakReference<Activity> activityWeakReference;
    private ScreenshotDetectionListener listener;

    public ScreenshotDetectionDelegate(Activity activityWeakReference, ScreenshotDetectionListener listener) {
        this.activityWeakReference = new WeakReference<>(activityWeakReference);
        this.listener = listener;
    }

    public void startScreenshotDetection() {
        activityWeakReference.get()
                .getContentResolver()
                .registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, contentObserver);
    }

    public void stopScreenshotDetection() {
        activityWeakReference.get().getContentResolver().unregisterContentObserver(contentObserver);
    }

    private ContentObserver contentObserver = new ContentObserver(new Handler()) {
        @Override
        public boolean deliverSelfNotifications() {
            return super.deliverSelfNotifications();
        }

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

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
            if (isReadExternalStoragePermissionGranted()) {
                String path = getFilePathFromContentResolver(activityWeakReference.get(), uri);
                if (isScreenshotPath(path)) {
                    onScreenCaptured(path);
                }
            } else {
                onScreenCapturedWithDeniedPermission();
            }
        }
    };

    private void onScreenCaptured(String path) {
        if (listener != null) {
            listener.onScreenCaptured(path);
        }
    }

    private void onScreenCapturedWithDeniedPermission() {
        if (listener != null) {
            listener.onScreenCapturedWithDeniedPermission();
        }
    }

    private boolean isScreenshotPath(String path) {
        return path != null && path.toLowerCase().contains("screenshots");
    }

    private String getFilePathFromContentResolver(Context context, Uri uri) {
        try {
            Cursor cursor = context.getContentResolver().query(uri, new String[]{
                    MediaStore.Images.Media.DISPLAY_NAME,
                    MediaStore.Images.Media.DATA
            }, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                cursor.close();
                return path;
            }
        } catch (IllegalStateException ignored) {
        }
        return null;
    }

    private boolean isReadExternalStoragePermissionGranted() {
        return ContextCompat.checkSelfPermission(activityWeakReference.get(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
    }

    public interface ScreenshotDetectionListener {
        void onScreenCaptured(String path);

        void onScreenCapturedWithDeniedPermission();
    }
}

•ScreenshotDetectionActivity.Java

import Android.Manifest;
import Android.content.pm.PackageManager;
import Android.os.Bundle;
import Android.support.annotation.NonNull;
import Android.support.annotation.Nullable;
import Android.support.v4.app.ActivityCompat;
import Android.support.v4.content.ContextCompat;
import Android.support.v7.app.AppCompatActivity;
import Android.widget.Toast;

public abstract class ScreenshotDetectionActivity extends AppCompatActivity implements ScreenshotDetectionDelegate.ScreenshotDetectionListener {
    private static final int REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION = 3009;

    private ScreenshotDetectionDelegate screenshotDetectionDelegate = new ScreenshotDetectionDelegate(this, this);

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        checkReadExternalStoragePermission();
    }

    @Override
    protected void onStart() {
        super.onStart();
        screenshotDetectionDelegate.startScreenshotDetection();
    }

    @Override
    protected void onStop() {
        super.onStop();
        screenshotDetectionDelegate.stopScreenshotDetection();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION:
                if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                    showReadExternalStoragePermissionDeniedMessage();
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    @Override
    public void onScreenCaptured(String path) {
        // Do something when screen was captured
    }

    @Override
    public void onScreenCapturedWithDeniedPermission() {
        // Do something when screen was captured but read external storage permission has denied
    }

    private void checkReadExternalStoragePermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestReadExternalStoragePermission();
        }
    }

    private void requestReadExternalStoragePermission() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION);
    }

    private void showReadExternalStoragePermissionDeniedMessage() {
        Toast.makeText(this, "Read external storage permission has denied", Toast.LENGTH_SHORT).show();
    }
}

•MainActivity.Java

import Android.os.Bundle;
import Android.view.View;
import Android.widget.Toast;

public class MainActivity extends ScreenshotDetectionActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onScreenCaptured(String path) {
        Toast.make(this, path, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onScreenCapturedWithDeniedPermission() {
        Toast.make(this, "Please grant read external storage permission for screenshot detection", Toast.LENGTH_SHORT).show();
    }
}
3
Akexorcist

スクリーンショットディレクトリのみを監視し、ファイルまたはディレクトリ作成のイベントのみをトリガーするFileObserverを作成できます。詳細については、 ここ をクリックしてください。

1
Mehul Kanzariya

Android Content Observerを使用したスクリーンショット検出 のgitプロジェクトを作成しました。

API 14から最新バージョン(投稿時)まで正常に動作しています。


1.ScreenShotContentObserver .class
(元のスクリーンショットの削除->撮影したスクリーンショットを通知し、スクリーンショットのビットマップを提供します)

public class ScreenShotContentObserver extends ContentObserver {

    private final String TAG = this.getClass().getSimpleName();
    private static final String[] PROJECTION = new String[]{
            MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATA,
            MediaStore.Images.Media.DATE_ADDED, MediaStore.Images.ImageColumns._ID
    };
    private static final long DEFAULT_DETECT_WINDOW_SECONDS = 10;
    private static final String SORT_ORDER = MediaStore.Images.Media.DATE_ADDED + " DESC";

    public static final String FILE_POSTFIX = "FROM_ASS";
    private static final String WATERMARK = "Scott";
    private ScreenShotListener mListener;
    private ContentResolver mContentResolver;
    private String lastPath;

    public ScreenShotContentObserver(Handler handler, ContentResolver contentResolver, ScreenShotListener listener) {
        super(handler);
        mContentResolver = contentResolver;
        mListener = listener;
    }

    @Override
    public boolean deliverSelfNotifications() {
        Log.e(TAG, "deliverSelfNotifications");
        return super.deliverSelfNotifications();
    }

    @Override
    synchronized public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            //above API 16 Pass~!(duplicated call...)
            return;
        }
        Log.e(TAG, "[Start] onChange : " + selfChange);
        try {
            process(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            Log.e(TAG, "[Finish] general");
        } catch (Exception e) {
            Log.e(TAG, "[Finish] error : " + e.toString(), e);
        }
    }

    @Override
    synchronized public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        Log.e(TAG, "[Start] onChange : " + selfChange + " / uri : " + uri.toString());

        if (uri.toString().startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())) {
            try {
                process(uri);
                Log.e(TAG, "[Finish] general");
            } catch (Exception e) {
                Log.e(TAG, "[Finish] error : " + e.toString(), e);
            }
        } else {
            Log.e(TAG, "[Finish] not EXTERNAL_CONTENT_URI ");
        }
    }

    public void register() {
        Log.d(TAG, "register");
        mContentResolver.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, this);
    }

    public void unregister() {
        Log.d(TAG, "unregister");
        mContentResolver.unregisterContentObserver(this);
    }

    private boolean process(Uri uri) throws Exception {
        Data result = getLatestData(uri);
        if (result == null) {
            Log.e(TAG, "[Result] result is null!!");
            return false;
        }
        if (lastPath != null && lastPath.equals(result.path)) {
            Log.e(TAG, "[Result] duplicate!!");
            return false;
        }
        long currentTime = System.currentTimeMillis() / 1000;
        if (matchPath(result.path) && matchTime(currentTime, result.dateAdded)) {
            lastPath = result.path;
            Uri screenUri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/" + result.id);
            Log.e(TAG, "[Result] This is screenshot!! : " + result.fileName + " | dateAdded : " + result.dateAdded + " / " + currentTime);
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContentResolver, screenUri);
            Bitmap copyBitmap = bitmap.copy(bitmap.getConfig(), true);
            bitmap.recycle();
            int temp = mContentResolver.delete(screenUri, null, null);
            Log.e(TAG, "Delete Result : " + temp);
            if (mListener != null) {
                mListener.onScreenshotTaken(copyBitmap, result.fileName);
            }
            return true;
        } else {
            Log.e(TAG, "[Result] No ScreenShot : " + result.fileName);
        }
        return false;
    }

    private Data getLatestData(Uri uri) throws Exception {
        Data data = null;
        Cursor cursor = null;
        try {
            cursor = mContentResolver.query(uri, PROJECTION, null, null, SORT_ORDER);
            if (cursor != null && cursor.moveToFirst()) {
                long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID));
                String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
                String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                long dateAdded = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));

                if (fileName.contains(FILE_POSTFIX)) {
                    if (cursor.moveToNext()) {
                        id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID));
                        fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
                        path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                        dateAdded = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));
                    } else {
                        return null;
                    }
                }

                data = new Data();
                data.id = id;
                data.fileName = fileName;
                data.path = path;
                data.dateAdded = dateAdded;
                Log.e(TAG, "[Recent File] Name : " + fileName);
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return data;
    }

    private boolean matchPath(String path) {
        return (path.toLowerCase().contains("screenshots/") && !path.contains(FILE_POSTFIX));
    }

    private boolean matchTime(long currentTime, long dateAdded) {
        return Math.abs(currentTime - dateAdded) <= DEFAULT_DETECT_WINDOW_SECONDS;
    }

    class Data {
        long id;
        String fileName;
        String path;
        long dateAdded;
    }
}
  1. Util.class

    public static void saveImage(Context context, Bitmap bitmap, String title) throws Exception {
        OutputStream fOut = null;
        title = title.replaceAll(" ", "+");
        int index = title.lastIndexOf(".png");
        String fileName = title.substring(0, index) + ScreenShotContentObserver.FILE_POSTFIX + ".png";
        final String appDirectoryName = "Screenshots";
        final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appDirectoryName);
        imageRoot.mkdirs();
        final File file = new File(imageRoot, fileName);
        fOut = new FileOutputStream(file);
    
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
        fOut.flush();
        fOut.close();
    
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.TITLE, "XXXXX");
        values.put(MediaStore.Images.Media.DESCRIPTION, "description here");
        values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
        values.put(MediaStore.Images.ImageColumns.BUCKET_ID, file.hashCode());
        values.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, file.getName());
        values.put("_data", file.getAbsolutePath());
        ContentResolver cr = context.getContentResolver();
        Uri newUri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
    }
    
0
Soo Chun Jung