转载

RxScreenshotDetector:Android 截屏检测

原文出处: http://blog.piasy.com/Android-Screenshot-Detector/

应PM需求,YOLO可能会对直播过程中的截屏进行检测并通知其他人,类似于Snapchat,此时iOS同事再次表达了先天优势,iOS系统提供了API呀!Google无果之后决定再次造轮子,为了持续表达对Rx的敬意,命名为RxScreenshotDetector, github 源码地址 。

效果有图有真相

RxScreenshotDetector:Android 截屏检测

原理

安卓系统并没有提供任何截屏检测相关的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。

最后,安利一发 YOLO直播APP 。

Written on January 29, 2016

原文  http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0130/3929.html
正文到此结束
Loading...