Android监听用户的截屏、投屏、录屏行为
Android监听用户的截屏、投屏、录屏行为
一.截屏
方案一:使用系统广播监听截屏操作
从Android Q(10.0)开始,Intent.ACTION_SCREEN_CAPTURED_CHANGED字段不再被支持。这是因为Google在安卓10 中引入了一个新的隐私限制,即限制应用在用户开启了屏幕录制功能或截屏功能时获取相应的广播。
- 创建一个BroadcastReceiver类来接收截屏广播:
public class ScreenCaptureReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (Intent.ACTION_SCREEN_CAPTURES_CHANGED.equals(action)) {// 截屏操作// 在这里执行你的逻辑操作}}
}
- 在AndroidManifest.xml文件中声明截屏广播接收器:
<receiverandroid:name=".ScreenCaptureReceiver"android:enabled="true"><intent-filter><action android:name="android.intent.action.SCREEN_CAPTURE_CHANGED" /></intent-filter>
</receiver>
- 注册截屏广播接收器,开始监听截屏操作:
ScreenCaptureReceiver receiver = new ScreenCaptureReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_CAPTURE_CHANGED);
registerReceiver(receiver, filter);
- 在不需要监听时,记得取消注册截屏广播接收器:
unregisterReceiver(receiver);
- 在BroadcastReceiver中的onReceive方法中可以执行一些逻辑操作,例如显示一个提示消息:
public class ScreenCaptureReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (Intent.ACTION_SCREEN_CAPTURE_CHANGED.equals(action)) {// 截屏操作Toast.makeText(context, "用户进行了截屏操作", Toast.LENGTH_SHORT).show();}}
}
方案二:使用ContentObserver监听截屏操作
另一种监听截屏操作的方法是使用ContentObserver来监听系统截屏文件的变化。以下是一个示例代码:
public class ScreenCaptureObserver extends ContentObserver {private static final String TAG = "ScreenCaptureObserver";private static final String SCREENSHOTS_DIR = Environment.getExternalStorageDirectory().toString() + "/Pictures/Screenshots";private Context mContext;public ScreenCaptureObserver(Context context) {super(null);mContext = context;}@Overridepublic void onChange(boolean selfChange) {super.onChange(selfChange);Log.d(TAG, "Screen capture detected");if (uri.toString().matches(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/[0-9]+")) {Cursor cursor = null;try {cursor = mContext.getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null);if (cursor != null && cursor.moveToFirst()) {String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));if (path != null && path.startsWith(SCREENSHOTS_PATH)) {// 此处为用户截屏行为的响应逻辑}}} finally {if (cursor != null) {cursor.close();}}}}public void start() {ContentResolver contentResolver = mContext.getContentResolver();contentResolver.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, this);}public void stop() {ContentResolver contentResolver = mContext.getContentResolver();contentResolver.unregisterContentObserver(this);}
}
使用示例:
ScreenCaptureObserver observer = new ScreenCaptureObserver(this);
observer.start();
对比与选型建议
使用 BroadcastReceiver
方案一的优点:
- 直接监听 Intent.ACTION_SCREENSHOT,无需处理 Uri 的变化。
- 适用于监听应用内的截屏操作。
方案一的缺点:
- 只能监听到整个屏幕的截屏操作,无法获取到具体的截屏内容。
- 有版本限制。从Android Q(10.0)开始,Intent.ACTION_SCREEN_CAPTURED_CHANGED字段不再被支持。
使用 ContentObserver
方案二的优点:
- 可以监听到截屏操作发生的具体 Uri,可以进一步获取截屏的文件路径和信息。
- 适用于监听系统级别的截屏操作。
方案二的缺点:
- 需要指定监听的 Uri,可能需要考虑兼容性问题。
- 需要在代码中处理 Uri 的变化,并解析截屏的文件路径。
选型建议
根据需求,如果只关心截屏的发生与否,并不需要获取截屏的具体内容,方案一可以考虑。如果需要获取截屏内容的具体信息,方案二比较适合。如果只需要监听应用内的截屏操作,方案一比较方便。如果需要监听系统级别的截屏操作,需要使用方案二。
二.录屏
在 Android 开发中,要监听用户的录屏操作,可以使用以下两种方案:
方案一:使用 MediaProjection API
Android 录屏 - 简书 (jianshu.com)
Android Q之后又前台服务限制
- 首先,在你的应用中创建一个
MediaProjectionManager
对象来获取用户录屏的权限。
private MediaProjectionManager mediaProjectionManager;
private MediaProjection mediaProjection;
private MediaProjection.Callback projectionCallback;mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
- 请求用户授权录屏权限,在回调中获取
MediaProjection
对象。
private static final int REQUEST_CODE_SCREEN_CAPTURE = 1;Intent intent = mediaProjectionManager.createScreenCaptureIntent();
startActivityForResult(intent, REQUEST_CODE_SCREEN_CAPTURE);@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == RESULT_OK) {mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);startScreenCapture();}
}
- 开始屏幕录制,并通过
MediaProjection.Callback
监听录屏状态。
private VirtualDisplay virtualDisplay;
private MediaRecorder mediaRecorder;private void startScreenCapture() {DisplayMetrics metrics = getResources().getDisplayMetrics();int screenWidth = metrics.widthPixels;int screenHeight = metrics.heightPixels;int screenDensity = metrics.densityDpi;mediaRecorder = new MediaRecorder();mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);mediaRecorder.setVideoSize(screenWidth, screenHeight);mediaRecorder.setVideoFrameRate(30);mediaRecorder.setOutputFile(getOutputFilePath());Surface surface = mediaRecorder.getSurface();virtualDisplay = mediaProjection.createVirtualDisplay("ScreenCapture",screenWidth,screenHeight,screenDensity,DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,surface,null,null);mediaRecorder.prepare();mediaRecorder.start();projectionCallback = new MediaProjection.Callback() {@Overridepublic void onStop() {stopScreenCapture();}};mediaProjection.registerCallback(projectionCallback, null);
}private void stopScreenCapture() {if (virtualDisplay != null) {virtualDisplay.release();virtualDisplay = null;}if (mediaRecorder != null) {mediaRecorder.stop();mediaRecorder.reset();mediaRecorder.release();mediaRecorder = null;}if (mediaProjection != null) {mediaProjection.unregisterCallback(projectionCallback);mediaProjection.stop();mediaProjection = null;}
}
方案二:使用 ContentObserver 监听屏幕录制状态变化
- 创建一个继承自
ContentObserver
的观察者类,并重写onChange()
方法。
public class ScreenRecordObserver extends ContentObserver {private static final String SCREEN_RECORD_STATE_PATH = "/sys/class/graphics/fb0/screen_state";private OnScreenRecordStateChangedListener listener;public ScreenRecordObserver(Handler handler) {super(handler);}public void setOnScreenRecordStateChangedListener(OnScreenRecordStateChangedListener listener) {this.listener = listener;}@Overridepublic void onChange(boolean selfChange, Uri uri) {if (listener != null) {boolean isRecording = isScreenRecording();listener.onScreenRecordStateChanged(isRecording);}}private boolean isScreenRecording() {try {FileInputStream fis = new FileInputStream(SCREEN_RECORD_STATE_PATH);BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fis));String state = bufferedReader.readLine();bufferedReader.close();return !"0".equals(state);} catch (Exception e) {e.printStackTrace();}return false;}
}public interface OnScreenRecordStateChangedListener {void onScreenRecordStateChanged(boolean isRecording);
}
- 注册观察者,监听屏幕录制状态变化。
private ScreenRecordObserver screenRecordObserver;screenRecordObserver = new ScreenRecordObserver(new Handler());
screenRecordObserver.setOnScreenRecordStateChangedListener(new OnScreenRecordStateChangedListener() {@Overridepublic void onScreenRecordStateChanged(boolean isRecording) {if (isRecording) {// 屏幕开始录制} else {// 屏幕停止录制}}
});getContentResolver().registerContentObserver(Uri.parse("content://" + SCREEN_RECORD_STATE_PATH), true, screenRecordObserver);
对比与选型建议
使用 MediaProjection API
方案一的优点:
- 监听到的录屏操作更加准确可靠,可以监听到具体的录屏开始和结束时间,同时可以获取到录屏内容。
- 可以对录屏过程进行更加灵活的控制,如录制分辨率、帧率等。
方案一的缺点:
- 需要申请录屏权限,用户需要给予应用访问屏幕内容的权限。
- 需要编写更多的代码进行屏幕录制的控制。
使用 ContentObserver 监听屏幕录制状态变化
方案二的优点:
- 不需要申请额外的权限。
- 监听录屏状态变化的实现相对简单。
方案二的缺点:
-
不能精确确定录屏的开始和结束时间。
-
无法获取到录屏的具体内容。
选型建议
根据需求,如果需要准确监听录屏的开始和结束时间,并且需要获取到录屏的具体内容,推荐使用方案一。
但如果只是需要知道录屏状态变化,而不关心具体的时间和内容,方案二比较简单方便。
三.投屏
在 Android 开发中,要监听用户的投屏操作,可以使用以下两种方案:
方案一:使用 MediaRouter API
首先,在你的 AndroidManifest.xml 文件中添加以下权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- 创建一个
MediaRouter.Callback
对象,并重写onRouteSelected()
和onRouteUnselected()
方法。
private MediaRouter mediaRouter;
private MediaRouter.Callback mediaRouterCallback;mediaRouter = (MediaRouter) getSystemService(Context.MEDIA_ROUTER_SERVICE);mediaRouterCallback = new MediaRouter.Callback() {@Overridepublic void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {// 投屏开始}@Overridepublic void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {// 投屏结束}
};
- 注册监听器,并指定监听的路由类型。
mediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mediaRouterCallback);
方案二:使用 DisplayManager API
首先,在 AndroidManifest.xml 文件中添加以下权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- 创建一个
DisplayManager.DisplayListener
对象,并重写onDisplayAdded()
、onDisplayRemoved()
和onDisplayChanged()
方法。
private DisplayManager displayManager;
private DisplayManager.DisplayListener displayListener;displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);displayListener = new DisplayManager.DisplayListener() {@Overridepublic void onDisplayAdded(int displayId) {// 投屏开始}@Overridepublic void onDisplayRemoved(int displayId) {// 投屏结束}@Overridepublic void onDisplayChanged(int displayId) {// 投屏状态变化}
};
- 注册监听器。
displayManager.registerDisplayListener(displayListener, null);
对比与选型建议
使用 MediaRouter API
方案一的优点:
- 使用方便,只需要注册一个回调即可监听到投屏操作。
- 可以监听到投屏的开始和结束时间。
方案一的缺点:
- 只能监听到路由类型为
ROUTE_TYPE_LIVE_VIDEO
的投屏操作,其他类型的投屏操作无法监听到。
使用 DisplayManager API
方案二的优点:
- 可以监听到所有的投屏操作,不限制特定的路由类型。
- 可以监听到投屏的开始和结束时间。
方案二的缺点:
- 需要注册更多的监听器和处理投屏状态变化的逻辑。
- 无法获取到具体的投屏内容。
选型建议
根据需求,如果只需要监听特定类型的投屏操作,并且需要获取投屏的具体内容,推荐使用方案一。
如果需要监听所有类型的投屏操作,方案二比较适合。
同时,如果只关心投屏的开始和结束时间,并不需要具体的投屏内容,两种方案都可以考虑。
相关文章:
Android监听用户的截屏、投屏、录屏行为
Android监听用户的截屏、投屏、录屏行为 一.截屏 方案一:使用系统广播监听截屏操作 从Android Q(10.0)开始,Intent.ACTION_SCREEN_CAPTURED_CHANGED字段不再被支持。这是因为Google在安卓10 中引入了一个新的隐私限制&#…...
MATLAB算法实战应用案例精讲-【路径规划】 图搜索算法
目录 前言 几个高频面试题目 运动规划、路径规划、轨迹规划对比 1. 运动规划 2. 路径规划VS轨迹规划...
Elasticsearch-Kibana使用教程
1.索引操作 1.1创建索引 PUT /employee {"settings": {"index": {"refresh_interval": "1s","number_of_shards": 1,"max_result_window": "10000","number_of_replicas": 0}},"mappi…...

mysql(八)docker版Mysql8.x设置大小写忽略
Mysql 5.7设置大小写忽略可以登录到Docker内部,修改/etc/my.cnf添加lower_case_table_names1,并重启docker使之忽略大小写。但MySQL8.0后不允许这样,官方文档记录: lower_case_table_names can only be configured when initializ…...
KALI LINUX攻击与渗透测试
预计更新 第一章 入门 1.1 什么是Kali Linux? 1.2 安装Kali Linux 1.3 Kali Linux桌面环境介绍 1.4 基本命令和工具 第二章 信息收集 1.1 网络扫描 1.2 端口扫描 1.3 漏洞扫描 1.4 社交工程学 第三章 攻击和渗透测试 1.1 密码破解 1.2 暴力破解 1.3 漏洞利用 1.4 …...

vue之mixin混入
vue之mixin混入 mixin是什么? 官方的解释: 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的…...
[ffmpeg] find 编码器
背景 整理 ffmpeg 中,如何通过名字或者 id 找到对应编码器的。 具体流程 搜索函数 avcodec_find_encoder // 通过 ID 搜索编码器 avcodec_find_encoder_by_name // 通过名字搜索编码器源码分析 ffmpeg 中所有支持的编码器都会注册到 codec_list.c 文件中&…...

Android CardView基础使用
目录 一、CardView 1.1 导入material库 1.2 属性 二、使用(效果) 2.1 圆角卡片效果 2.2 阴影卡片效果 2.3 背景 2.3.1 设置卡片背景(app:cardBackgroundColor) 2.3.2 内嵌布局,给布局设置背景色 2.4 进阶版 2.4.1 带透明度 2.4.2 无透明度 一、CardView 顾名…...
云原生Kubernetes系列 | init container初始化容器的作用
云原生Kubernetes系列 | init container初始化容器的作用 kubernetes 1.3版本引入了init container初始化容器特性。主要用于在启动应用容器(app container)前来启动一个或多个初始化容器,作为应用容器的一个基础。只有init container运行正常后,app container才会正常运行…...
汽车电子芯片介绍之Aurix TC系列
Infineon的AURIX TC系列芯片是专为汽车电子系统设计的,采用了32位TriCore处理器架构。该系列芯片具有高性能、低功耗和丰富的外设接口,适用于广泛的汽车电子应用。以下是AURIX TC系列芯片的主要特性: 1. 高性能处理器 AURIX TC芯片采用了高…...
Linux 设置程序开机自启动的方法
目录 前言开机自启动参考 前言 CentOS Linux release 7.9.2009 (Core) 开机自启动 shell> vim /etc/rc.d/rc.local添加开机后执行的命令 sh /xxx/xxx.sh参考 https://www.cnblogs.com/xlmeng1988/archive/2013/05/22/3092447.html...

java企业财务管理系统springboot+jsp
1、基本内容 (1)搭建基础环境,下载JDK、开发工具eclipse/idea。 (2)通过HTML/CSS/JS搭建前端框架。 (3)下载MySql数据库,设计数据库表,用于存储系统数据。 (4…...

【Windows】如何实现 Windows 上面的C盘默认文件夹的完美迁移
如何实现 Windows 上面的C盘默认文件夹的完美迁移 1. 遇到的问题 在我想迁移C盘的 下载 和 视频 文件夹的时候,遇到了这样的问题,在迁移之后,我显卡录像的视频还是保存到了C盘默认位置里,以及我迁移了 下载 之后下载的盘依然是在…...

kubernetes七层负载Ingress搭建(K8S1.23.5)
首先附上K8S版本及Ingress版本对照 Ingress介绍 NotePort:该方式的缺点是会占用很多集群机器的端口,当集群服务变多时,这个缺点就愈发的明显(srevice变多,需要的端口就需要多) LoadBalancer:该方式的缺点是每个servi…...

二维粒子群算法航线规划
GitHub - gabrielegilardi/PathPlanning: Implementation of particle swarm optimization (PSO) for path planning when the environment is known....
uniapp长按图片识别二维码
引用:https://blog.csdn.net/weixin_48596030/article/details/125405779 <image :src"url" mode"widthFix" click.self"previewImage" show-menu-by-longpress"true" style"width: 350rpx;"></image…...

智能优化算法应用:基于和声算法无线传感器网络(WSN)覆盖优化 - 附代码
智能优化算法应用:基于和声算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于和声算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.和声算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…...

Gitee拉取代码报错You hasn‘t joined this enterprise! fatal unable to access
文章目录 一、问题二、解决2.1、进入**控制面板**2.2、进入**用户账户**2.3、进入**管理Windows凭据**2.4、**普通凭据**2.4.1、添加2.4.2、编辑 2.5、重新拉取|推送代码 三、最后 一、问题 Gitee拉取仓库代码的时候报错You hasnt joined this enterprise! fatal unable to ac…...

算法通关村第十六关-白银挑战滑动窗口经典题目
大家好我是苏麟 , 今天带来滑动窗口经典的一些题目 . 我们继续来研究一些热门的、高频的滑动窗口问题 大纲 最长子串专题无重复字符的最长子串 长度最小的子数组盛最多水的容器 最长子串专题 无重复字符的最长子串 描述 : 给定一个字符串 s ,请你找出其中不含有重…...

springBoot整合task
springBoot整合task 文章目录 springBoot整合task开开关设置任务,并设置执行周期定时任务的相关配置 开开关 设置任务,并设置执行周期 Component public class MyBean {Scheduled(cron "0/1 * * * * ?")public void print(){System.out.prin…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...