最近项目中新接到一个需求,对手机截屏进行检测并进行后续操作,类似于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. 【lightoj-1063】Ant Hills(求割点)

    求割点模板题 #include <bits/stdc++.h> using namespace std; const int N = 10004; int dfn[N], low[N]; ...

  2. poj1469

    题解: 二分图匹配 然后判断最大匹配是否是m 代码: #include<cstdio> #include<cmath> #include<algorithm> #i ...

  3. FLASH动作脚本详解

    FLASH动作脚本详解 一.FLASH脚本基础入门讲解 二.按钮AS的编写 三.影片剪辑的AS编写 四.动态文本框 五.影片剪辑的拖拽 六.流程控制与循环语句 七.绘图及颜色的AS的编写 八.声音 A ...

  4. 在Ubuntu 14.04 TLS下openvas V8.0源代码安装过程

    [Qboy原创]详细记录源代码的安装过程 1.下载原代码 在http://www.openvas.org/install-source.html下载 Libraries 8.0.8 Scanner 5 ...

  5. Arcgis for javascript map操作addLayer详解

    本节的内容很简单,说说Arcgis for Javascript里面map对象的addLayer方法.在for JS的API中,addLayer方法有两种,如下图: addLayer方法 在addLa ...

  6. SQL—数据分析

    留存分析——左连接 矩阵分析——group by 结构分析:分析每种产品占据总销售量的比例 排序分析

  7. c语言中指针的一个小错误

    在定义指针后需要给指针赋值然后才能使用*p赋值或被赋值,这是个基础问题,没有理解,导致出问题. 空指针 ,也称悬 游指 针 ,是使 用 未初 始化 的指 针 .指针变量未初始化时它的值不是没有 ,而是 ...

  8. 十七、python沉淀之路--三元表达式、列表解析

    一.三元表达式 a = '骑车' res = '好天气' if a == '骑车' else '睡觉' print(res) 睡觉 解析:res = '好天气'        if a == '骑车' ...

  9. ORA-28595: Extproc 代理: DLL 路径无效解决办法

    报错信息: ORA-28595: Extproc 代理: DLL 路径无效 ORA-06512: 在 "SDE.ST_GEOMETRY_SHAPELIB_PKG", line 70 ...

  10. ReactJS开发环境搭建与相关工具介绍

    现在Web开发的技术几年前相比可谓变化之大.各种各样的框架,各种各样的工具,让Web开发效率更高,开发出来的效果更好.同时带来的是开发环境的复杂度相比以前是成倍的增加.ReatJS框架是现在比较流行的 ...