网站备案现场核验/网店代运营
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…...

逻辑漏洞测试靶场实验
任务一: 突破功能限制漏洞,要求突破查询按钮disabled限制,获取编号:110010的查询内容(弹框中的flag) 任务二:用户信息泄露漏洞,通过回显信息,以暴力破解法方式猜测系统中…...

【电机控制】PMSM无感foc控制(六)相电流检测及重构 — 双电阻采样、三电阻采样
0. 前言 目前,永磁同步电机的电流信号采样方法应用较多的是分流电阻采样,包括单电阻、双电阻以及三电阻采样法。其中,单电阻采样上一章节已经讲解,这章讲双电阻以及三电阻电流采样法。 1. 双电阻采样 1.1 双电阻采样原理 双电阻采…...

Boost:多进程间消息队列通信
Boost封装了消息队列,以便于多进程间传递消息: 1.创建消息队列: #include <boost/interprocess/ipc/message_queue.hpp> message_queue mq (create_only/open_only/create_or_open ,"message_queue" //消息队列的名字 ,100 …...

ELK配置记录
1. filebeat.yml配置 启动命令: ./filebeat -e -c filebeat.yml # 输入 filebeat.inputs: - type: logenabled: truepaths:- /soft/log/base.*#跨行日志正则,从有时间的开始,到下一个时间之前结束multiline.pattern: ^\[[0-9]{4}-[0-9]{2}…...

EtherCAT主站SOEM -- 7 -- SOEM之ethercatmain.h/c文件解析
EtherCAT主站SOEM -- 7 -- SOEM之ethercatmain.h/c文件解析 一 ethercatmain.h/c文件功能预览:1.1 ethercatmain里面的结构体1.2 ethercatmain里面的函数二 ethercatmain.h/c 文件的主要函数的作用:2.1 结构体介绍2.1.1 `ec_adaptert` 结构体:2.1.2 `ec_fmmut` 结构体:2.1.3 …...

Linux下Python调用C语言
一:Python调用C语言场景 1,已经写好的C语言代码,不容易用Python实现,想直接通过Python调用写好的C语言代码 2,C比Python快(只是从语言层面,不能绝对说C程序就是比Python快) 3&…...

SQL Server对象类型(8)——4.8.约束(Constraint)
4.8. 约束(Constraint) 4.8.1. 约束概念 与Oracle中的一样,SQL Server中,约束是虚的、被定义的数据库对象,其本身并不存储数据,其通过一些内置或用户自定义逻辑来实现对表中数据的检查和限制,以使这些表数据符合某个或某些规则或标准,从而实现数据的规则化、标准化和…...

苍穹外卖--导出运营数据Excel报表
导出运营数据Excel报表 需求分析和设计 产品原型 在数据统计页面,有一个数据导出的按钮,点击该按钮时,其实就会下载一个文件。这个文件实际上是一个Excel形式的文件,文件中主要包含最近30日运营相关的数据。表格的形式已经固定…...

cocos creator-碰撞检测
碰撞检测文档 刚体自行选择,刚体正常设置分组、tag,tag用于区分是哪个物体被碰撞了 正常在一个node下挂载脚本就行 注意:Builtin 2D 物理模块只会发送 BEGIN_CONTACT 和 END_CONTACT 回调消息。ccclass(TestContactCallBack) export class …...

算法通关第十七关黄金挑战——透析跳跃问题
大家好,我是怒码少年小码。 本篇是贪心思想的跳跃问题专题,跳跃问题出现的频率很高。 跳跃游戏 LeetCode 55:给你一个非负整数数组 nums ,你最初位于数组的 第一个下标。数组中的每个元素代表你在该位置可以跳跃的最大长度。 …...