最近项目中新接到一个需求,对手机截屏进行检测并进行后续操作,类似于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。

Android 截屏检测的更多相关文章

  1. Android截屏的几种实现

    Android截屏的几种实现 微信公众号:CodingAndroid CSDN:http://blog.csdn.net/xinpengfei521 最近我们的APP要求需要截屏功能,网上看了看大致有 ...

  2. 【转】Android截屏

     http://blog.csdn.net/xww810319/article/details/17607749 Android截屏浅析 链接:http://blog.sina.com.cn/s/bl ...

  3. android截屏

    截屏是一个常用的操作,经常会有这种需求. 截屏的工具类 package com.fxb.screenshot; import android.app.Activity; import android. ...

  4. Android 截屏与 WebView 长图分享经验总结

    最近在做新业务需求的同时,我们在 Android 上遇到了一些之前没有碰到过的问题,截屏分享. WebView 生成长图以及长图在各个分享渠道分享时图片模糊甚至分享失败等问题,在这过程中踩了很多坑,到 ...

  5. Android 截屏的各种骚操作

    本文公众号「AndroidTraveler」首发. 背景 在实际的应用场景中,Android 手机的截屏其实是很普遍的. 比如说 PPT 演示,比如说技术博客图文并茂讲解. 因此懂得 Android ...

  6. android截屏:保存一个view的内容为图片并存放到SD卡

    项目中偶尔会用到截屏分享,于是就有了下面这个截屏的方法~ 下面得saveImage()方法就是保存当前Activity对应的屏幕所有内容的截屏保存. private void saveImage() ...

  7. 快速简化Android截屏工作

    1.安装Notepad++v6.9 2.插件管理器里Plugin Manager安装AndroidLogger 3.AndroidLogger里的capture功能抓取Android的当前屏幕截图到w ...

  8. Android手机截屏

    刚开始打算做一个简单的截屏程序时,以为很轻松就能搞定. 在Activity上放一个按钮,点击完成截屏操作,并将数据以图片形式保存在手机中. 动手之前,自然是看书和网上各种查资料.结果发现了解的知识越多 ...

  9. Android开发笔记:安卓程序截屏方法

    1,基于Android SDK的截屏方法 (1)主要就是利用SDK提供的View.getDrawingCache()方法.网上已经有很多的实例了.首先创建一个android project,然后进行L ...

随机推荐

  1. 关于SQL Sever连接问题

    错误: 与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误.未找到或无法访问服务器.请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接. (provider: ...

  2. Http权威指南(TCP连接)

    1.HTTP请求的过程 世界上几乎所有的HTTP通信都是由TCP/IP承载的,当发生HTTP请求时,实际上经过了以下几个步骤: ①浏览器从请求的URL中解析主机名 ②浏览器查询这个主机名的IP地址 ③ ...

  3. ng 过滤器

    1.ng中自带的过滤器过滤器:实现对数据的筛选.过滤.格式化. 过滤器是一个有返回值的方法. 过滤器语法:{{ expression |过滤器1:'参数' | 过滤器2:'参数' }} | --> ...

  4. PHP防止mysql注入方法

    本文纲要: 1. 初始化你的变量 2. 一定记得要过滤你的变量 [一.在服务器端配置] 安全,PHP代码编写是一方面,PHP的配置更是非常关键. 我们php手手工安装的,php的默认配置文件在 /us ...

  5. Ant入门之引用外部jar文件

    笔者在java项目开发中经常遇到引用外部Jar包的情况,使用ant打包过程中需要对其引用.现在此简单记忆以飨来者. 此处引用Log4j,具体程序HelloLog4j.java: package oat ...

  6. HDU - 6166:Senior Pan(顶点集合最短路&二进制分组)

    Senior Pan fails in his discrete math exam again. So he asks Master ZKC to give him graph theory pro ...

  7. MySql必知必会实战练习(四)主键、外键、sql约束、联结表

    本博将对主键.外键.MySql数据库约束和联结表的相关特性进行总结和实战 1. 主键 表中的每一行都应该具有可以唯一标识自己的一列(或一组列),而这个承担标识作用的列称为主键 如果没有主键,数据的管理 ...

  8. python之pandas数据筛选和csv操作

    本博主要总结DaraFrame数据筛选方法(loc,iloc,ix,at,iat),并以操作csv文件为例进行说明 1. 数据筛选 a b c (1)单条件筛选 df[df[] # 如果想筛选a列的取 ...

  9. Chrome 的书签太多如何分类整理比较好

    对于Chrome书签太多,如何进行分类整理,下面给出几种方法~ 工具/原料 电脑 Chrome浏览器 delicious.Diigo等 方法/步骤1 1.把书签中常用网站只保存LOGO,放在书签栏最前 ...

  10. C#操作带名称空间的xml

    以前操作xml一般用下面这种方式: 好处是XDocument 能使用linq xmlPath = “path”; XDocument myXDoc = XDocument.Load(xmlPath); ...