最近公司项目需要做一个需求,就是监听app截屏,然后上报埋点信息的需求,但android是没有系统的接口可监听,只能自己实现(不像ios 系统提供了,调用一下几行代码就完事),而自己实现的话就存在各种android机型的适配问题,下面将整个代码贴一下:

package com.hundsun.winner.application.base;import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.FileObserver;
import android.provider.MediaStore;
import android.text.TextUtils;
import java.io.File;public class ScreenshotContentObserver extends ContentObserver {private static final String TAG = "ScreenshotContentObserver";//分享时使用的tagpublic static final String SCREEN_SHOT_SHARE_TAG = "ScreenShotShare";//URI匹配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.DATA, MediaStore.Images.Media.DATE_ADDED};//根据时间降序排序private static final String SORT_ORDER = MediaStore.Images.Media.DATE_ADDED + " DESC";//时间判断间隔标准private static final long TIME_MATCH_MILLISECOND = 1500L;//英文名称判断标准private static final String DISPLAY_NAME_EN = "screenshot";//中文名称判断标准private static final String DISPLAY_NAME_CN = "截屏";public static  final String  MANUFACTURER_OF_HARDWARE_XIAOMI ="xiaomi";public static  final String  MANUFACTURER_OF_HARDWARE_VIVO ="vivo";//媒体内容监听器private ContentResolver mContentResolver;//文件监听器private FileObserver fileObserver;//最后监听到的文件路径,用于去除重复事件private String mLastObservePath;//屏幕宽度private int mScreenWidth;//屏幕高度private int mScreenHeight;//截屏事件回调private Listener mListener;public interface Listener {//截屏事件来源区分int FROM_TYPE_SYSTEM = 1;//来自系统截屏int FROM_TYPE_ACTION_SHEET = 2;//来自sheet的截屏按钮int FROM_TYPE_MULTI_SHARE = 3;//来自多点分享void onDetectScreenshot(Uri uri, String path, int fromType);}//系统截屏文件目录public static final class ScreenShotPath {public static final String NORMAL = Environment.getExternalStorageDirectory()+ File.separator + Environment.DIRECTORY_PICTURES+ File.separator + "Screenshots" + File.separator;public static final String XIAOMI = Environment.getExternalStorageDirectory()+ File.separator + Environment.DIRECTORY_DCIM+ File.separator + "Screenshots" + File.separator;public static final String VIVO = Environment.getExternalStorageDirectory()+ File.separator + "截屏" + File.separator;}public void setListener(Listener mListener) {this.mListener = mListener;}public ScreenshotContentObserver(Context context, int width, int height) {super(null);mScreenWidth = width;mScreenHeight = height;//判断版本是否小于6.0if (Build.VERSION.SDK_INT < 23) {final String screenshotPath;//判断厂商,设置监听目录if (android.os.Build.MANUFACTURER.equalsIgnoreCase(MANUFACTURER_OF_HARDWARE_XIAOMI)) {screenshotPath = ScreenShotPath.XIAOMI;} else if (android.os.Build.MANUFACTURER.equalsIgnoreCase(MANUFACTURER_OF_HARDWARE_VIVO)) {screenshotPath = ScreenShotPath.VIVO;} else {screenshotPath = ScreenShotPath.NORMAL;}//创建文件监听器fileObserver = new FileObserver(screenshotPath, FileObserver.CREATE) {@Overridepublic void onEvent(int event, String path) {
//                    if (QLog.isDevelopLevel()) {
//                        QLog.d(TAG, QLog.CLR, "onEvent->time:" + System.currentTimeMillis() + ", path:" + path);
//                    }//过滤错误参数及事件if (TextUtils.isEmpty(path) || event != FileObserver.CREATE) {return;}//过滤重复事件以及新MIUI截屏临时文件if (path.equalsIgnoreCase(mLastObservePath) || path.contains("temp")) {return;}//通知回调if (mListener != null) {mListener.onDetectScreenshot(null, screenshotPath + path, Listener.FROM_TYPE_SYSTEM);mLastObservePath = path;}}};} else {mContentResolver = context.getContentResolver();}registerToSystem();}/*** 清除资源*/public void onDestroy() {unregisterToSystem();fileObserver = null;mContentResolver = null;mListener = null;}/*** 开始监听*/private void registerToSystem() {//判断版本是否小于6.0if (Build.VERSION.SDK_INT < 23) {fileObserver.startWatching();} else {mContentResolver.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, this);}}/*** 关闭监听*/private void unregisterToSystem() {//判断版本是否小于6.0if (Build.VERSION.SDK_INT < 23) {fileObserver.stopWatching();} else {//不需要监听的时候,一定要把原来的ContentObserver注销掉。mContentResolver.unregisterContentObserver(this);}}/*** 媒体内容监听器回调** @param selfChange*/@Overridepublic void onChange(boolean selfChange) {super.onChange(selfChange);//从API16开始,才有两个参数的onChange方法,所以这里要主动调用下面的onChange方法。onChange(selfChange, null);}/*** 媒体内容监听器回调** @param selfChange* @param uri*/@Overridepublic void onChange(boolean selfChange, Uri uri) {//若调用父类方法就死循环了//super.onChange(selfChange,uri);
//        if (QLog.isDevelopLevel()) {
//            QLog.d(TAG, QLog.CLR, "onChange->time:" + System.currentTimeMillis());
//        }Cursor cursor = null;if (uri == null) { //API16以下版本try {cursor = mContentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, null, null, SORT_ORDER);if (cursor != null && cursor.getCount() > 0) {cursor.moveToFirst();//完整路径String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));//添加图片的时间,单位秒long addTime = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));//加个过滤条件必须是3S内的图片,且路径中包含截图字样“screenshot”if (matchAddTime(addTime) && matchPath(path) /*&& matchSize(path)*/) {
//                        if (QLog.isColorLevel()) {
//                            QLog.d(TAG, QLog.CLR, "onChange->path:" + path + ", uri:null");
//                        }if (path != null) {if (path.equalsIgnoreCase(mLastObservePath)) {return;} else {mLastObservePath = path;}}//这就是系统截屏的图片了,这里测试发现需要等待几百MS,才能加载到图片。因此具体实现时,最好在独立线程,每隔100MS尝试加载一次,做好超时处理。if (mListener != null) {mListener.onDetectScreenshot(uri, path, Listener.FROM_TYPE_SYSTEM);}}}} catch (Exception e) {e.printStackTrace();} finally {if (cursor != null) {try {cursor.close();} catch (Exception e) {e.printStackTrace();}}}} else { //API16及以上版本try {if (uri.toString().startsWith(EXTERNAL_CONTENT_URI_MATCHER)) {cursor = mContentResolver.query(uri, PROJECTION, null, null, SORT_ORDER);if (cursor != null && cursor.getCount() > 0) {cursor.moveToFirst();//完整路径String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));//添加图片的时间,单位秒long addTime = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));if (matchAddTime(addTime) && matchPath(path) /*&& matchSize(path)*/) {
//                            if (QLog.isColorLevel()) {
//                                QLog.d(TAG, QLog.CLR, "onChange->path:" + path + ", uri:" + uri);
//                            }if (path != null) {if (path.equalsIgnoreCase(mLastObservePath)) {return;} else {mLastObservePath = path;}}//这就是系统截屏的图片了if (mListener != null) {mListener.onDetectScreenshot(uri, path, Listener.FROM_TYPE_SYSTEM);}}}}} catch (Exception e) {e.printStackTrace();} finally {if (cursor != null) {try {cursor.close();} catch (Exception e) {e.printStackTrace();}}}}}/*** 添加时间与当前时间不超过1.5s,大部分时候不超过1s。** @param addTime 图片添加时间,单位:秒*/private boolean matchAddTime(long addTime) {return System.currentTimeMillis() - addTime * 1000 < TIME_MATCH_MILLISECOND;}/*** 已调查的手机截屏图片的路径中带有screenshot*/private boolean matchPath(String filePath) {String lower = filePath.toLowerCase();return lower.contains(DISPLAY_NAME_EN) || lower.contains(DISPLAY_NAME_CN);}/*** 尺寸不大于屏幕尺寸(发现360奇酷手机可以对截屏进行裁剪)*/private boolean matchSize(String filePath) {BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeFile(filePath, options);return mScreenWidth >= options.outWidth && mScreenHeight >= options.outHeight;}
}

andriod 监听系统截屏操作相关推荐

  1. Android平台监听系统截屏方案预研及相关知识点

    最近有个针对系统截屏的需求,所以预研了Android平台上捕获系统截屏的方案. 最直接的方式就是监听手机的系统截屏组合键(电源键+音量下键),但是这种方式实现难度大,且有的机型使用特殊手势进行截屏,兼 ...

  2. Android App中监听系统截屏(截屏监听功能)

    功能需求: App内截屏监控功能,当发现用户在我们的app内进行了截屏操作时,进行对图片的二次操作,例如添加二维码,公司logo等一系列操作. 首先来app界面图及截屏监听图添加效果图 主要是利用内容 ...

  3. 监听Android系统截屏

    1. 原理 因为Android系统没有提供截屏的相关API,所以需要我们自己去实现.国内的Android手机都是使用定制系统的,截图方式五花八门,采用对截图按键的监听的方案并不合适.Android系统 ...

  4. Android系统截屏监听工具

    Android系统截屏监听工具 做系统截屏通知的时候,找了很多资料,测试后发现要做挺多机型适配的,而且有时候会没有监听到,有时候又监听到多次,不能实现想要的效果.一下是最终的解决方案,已在足够多的手机 ...

  5. Android 系统截屏与系统内置DVR录像冲突,导致SystemUI重启的问题解决与分享

    上周六加班在解决一个关于SystemUI内嵌的DVR录像与系统截屏操作冲突的问题,介于问题的复杂性,所以我把这个分享出来便 于以后自己更加的理解,又方便以后遇到此问题的同行能够提供一些帮助,若有疑问可 ...

  6. Android 基于4.4系统截屏的三指截屏

    根据上一篇文章Android 4.4系统原生截图解析 ,我们知道系统截屏是调用了TakeScreenshotService,为实现在任何界面都能实现三指截屏,我们就得在PhoneWindow(fram ...

  7. android 4.3 操作源码实现系统截屏(暂无移植性)

    感谢网友cjd6568358的帮助,新版的Android系统截屏功能已经实现,需要的朋友请移步项目主页:https://github.com/Android-ScreenShot/AndroidScr ...

  8. Android系统截屏的实现(附代码)

    1.背景 写博客快两年了,写了100+的文章,最火的文章也是大家最关注的就是如何实现android系统截屏.其实我们google android_screen_shot就会找到很对办法,但那些都是很多 ...

  9. iphone屏幕镜像如何全屏_苹果系统截屏录屏+标记剪辑功能详解( iPhone/iPad/Mac)

    苹果系统中的截屏和录屏.标记和剪辑功能一如它的其他产品设计,做得非常细致.在我们日常的工作中,不免会遇到这些功能,今天小编就给大家详细讲解下苹果系统截屏录屏.标记剪辑功能,希望对大家有所帮助! 001 ...

最新文章

  1. Android Handler 深入学习(1)
  2. 10所“双一流”入选!一批国家自然科学基金立项名单公示
  3. poj(百练) 2743解题报告
  4. 原生 AJAX的相关介绍
  5. 推荐策略产品经理:剖析协同过滤(千人千面推荐的核心 )
  6. Linux 的权限表达式
  7. spring 事务隔离级别和传播行为_Spring事务传播性与隔离性实战
  8. 费诺编码的gui页面设计_GUI设计和UI设计有什么区别?
  9. oracle plm 文件系统,ORACLE-PlM管理方案(标准材料).ppt
  10. Java如何使用IP代理
  11. echarts考勤图表
  12. one class classification
  13. c语言解一元一次方程(及一元高次方程)
  14. 第4讲:C# 入门教程 入门知识(黄菊华NET网站开发、C#网站开发、Razor网站开发教程)
  15. 5G网络正式商用对4G物联网卡造成何种影响?
  16. fortran常见错误汇总
  17. Python爬虫---爬虫介绍,实战案例
  18. Oracle获取上一年的时间
  19. 五年级计算机课做什么电子报,电子工业版五年级信息技术5册教案1、小报版面先设计...
  20. python爬取静态数据详解

热门文章

  1. 为什么在ftp的配置中linux和windowns ping不通_2019年电脑配置推荐,收藏一篇文章就够了...
  2. 【Electron Playground】Electron 窗口问题汇总
  3. 用格式工厂把大图片压缩成小图
  4. 终于有人把 Java代理讲清楚了,万字详解!
  5. VSO Downloader Ultimate 5.0.1.45 中文多语免费版 在线视频下载工具
  6. 无法在别的计算机里显示u盘,为什么我的u盘某些一部分文件夹在一个电脑上能显示而在另一个电脑上不显示?...
  7. php删除到回收站代码,怎么找回回收站删除的文件 php自定义函数之递归删除文件及目录...
  8. 拒绝访问/ 客户端没有所需的特权at java.io.WinNTFileSystem.createFileExclusively(Native Method) ,createTempFile
  9. iPhone新专利增加防骚扰电话功能
  10. python内置开发工具是什么_Python内置工具(tools)总结