Android 截屏检测

最近项目中新接到一个需求,对手机截屏进行检测并进行后续操作,类似于Snapchat,iOS具有先天优势,因iOS系统提供了相关API!Google无果之后原作者决定再次造轮子,为了持续表达对Rx的敬意,命名为RxScreenshotDetector, github 源码地址 。

效果有图有真相

原理

安卓系统并没有提供任何截屏检测相关的API,网上针对Snapchat的这项功能进行了分析,大致猜测可能有以下几种途径:

  • 使用FileObserver,监听Screenshots目录下的文件变化;

  • 使用ContentObserver,监听MediaStore.Images.Media.EXTERNAL_CONTENT_URI资源的变化;

  • 重载(hook)截屏组合键(不靠谱),有的机型使用的是特殊手势进行截屏;

主要参考了 StackOverflow上面的这个回答 。

核心代码如下:

private static final String TAG = "RxScreenshotDetector";
private static final String EXTERNAL_CONTENT_URI_MATCHER =
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString();
private static final String[] PROJECTION = new String[] {
        MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATA,
        MediaStore.Images.Media.DATE_ADDED
};
private static final String SORT_ORDER = MediaStore.Images.Media.DATE_ADDED + " DESC";
private static final long DEFAULT_DETECT_WINDOW_SECONDS = 10;

final ContentResolver contentResolver = context.getContentResolver();
final ContentObserver contentObserver = new ContentObserver(null) {
    @Override
    public void onChange(boolean selfChange, Uri uri) {
        Log.d(TAG, "onChange: " + selfChange + ", " + uri.toString());
        if (uri.toString().matches(EXTERNAL_CONTENT_URI_MATCHER)) {
            Cursor cursor = null;
            try {
                cursor = contentResolver.query(uri, PROJECTION, null, null,
                        SORT_ORDER);
                if (cursor != null && cursor.moveToFirst()) {
                    String path = cursor.getString(
                            cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                    long dateAdded = cursor.getLong(cursor.getColumnIndex(
                            MediaStore.Images.Media.DATE_ADDED));
                    long currentTime = System.currentTimeMillis() / 1000;
                    Log.d(TAG, "path: " + path + ", dateAdded: " + dateAdded +
                            ", currentTime: " + currentTime);
                    if (path.toLowerCase().contains("screenshot") &&
                            Math.abs(currentTime - dateAdded) <=
                                    DEFAULT_DETECT_WINDOW_SECONDS) {
                        // screenshot added!
                    }
                }
            } catch (Exception e) {
                Log.d(TAG, "open cursor fail");
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        super.onChange(selfChange, uri);
    }
};
contentResolver.registerContentObserver(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, contentObserver);

RxScreenshotDetector.java hosted with ❤ by  GitHub

主要有以下几点需要注意:

  • 权限,读取资源的时候需要READ_EXTERNAL_STORAGE权限,这里我使用了 RxPermissions 来以reactive的方式请求权限;

  • 从ContentResolver查询资源的时候,需要按照资源创建时间降序排列,针对最新的一个资源判断是否为截屏的图片,为contentResolver.query的最后一个参数传递MediaStore.Images.Media.DATE_ADDED + " DESC"即可,而判断图片是否为截图则比较简单,路径包含screenshot关键字,且添加时间在10s之内;

使用示例

RxScreenshotDetector完整使用代码如下:

RxScreenshotDetector.start(getApplicationContext())
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .compose(this.<String>bindUntilEvent(ActivityEvent.PAUSE))
        .subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }

            @Override
            public void onNext(String path) {
                mTextView.setText(mTextView.getText() + "\nScreenshot: " + path);
            }
        });

这里使用了 RxLifecycle ,在Activity onPause之后unsubscribe,以保证不会发生内存泄漏。此外subscribe传入的是完整的Subscriber,是为了防止授权失败时没有onError处理器,导致crash。

转载于:https://www.cnblogs.com/kaidarwang/p/6525878.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值