通过无障碍服务(AccessibilityService)实现Android设备全局水印显示

一、无障碍功能简介

首先我们先来了解下无障碍功能的官方介绍:

         无障碍服务仅应用于帮助残障用户使用 Android 设备和应用。它们在后台运行,并在触发 AccessibilityEvents 时接收系统的回调。此类事件表示用户界面中的某些状态转换,例如焦点已更改、按钮已被点击等。此类服务可以选择性地请求查询活动窗口内容的功能。开发无障碍服务需要扩展此类并实现其抽象方法。

        从以上介绍我们得知可以通过这个系统服务,添加全局的活动窗口,并对窗口状态做监听。那么我们今天就借助系统的无障碍服务来实现的设备全局水印效果,具体的需求内容是添加一个全局的文字透明层,同时确保不抢占页面焦点,并允许用户在其他应用上继续操作界面。说白了,我们要实现的全局水印效果就是实现一个顶层的透明层VIEW,只显示出来并不影响应用用户设备操作,同时给设备加水印,防止设备具体内容被剽窃,以保证达到设备安全的目的。

二、设备水印功能实现

在内容实现之前,我们先来了解一下几个Window窗口相关的属性。

窗口类型:WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY ;这种类型的窗口专门为无障碍服务设计,能覆盖在所有应用之上而不会影响用户操作。

窗口标志:WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;让窗口永远不会获得按键输入焦点,因此用户无法向其发送按键或其他按钮事件。

窗口标志:WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;让窗口永远不能接收触摸事件。此标志的目的是让位于此窗口下方的某个窗口(按 Z 顺序)来处理触摸。

窗口标志:WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;该标志可以将窗口放置在整个屏幕内,忽略来自父窗口的任何限制。

1、 创建无障碍服务

首先创建一个无障碍服务(Accessibility Service),该服务将用于显示全局的透明水印文字层。

class CustomOverlayService: AccessibilityService() {private lateinit var overlayWindowManager: WindowManagerprivate var overlayView: View? = nulloverride fun onServiceConnected() {super.onServiceConnected()// 初始化WindowManageroverlayWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager// 加载布局overlayView = LayoutInflater.from(this).inflate(R.layout.layout_watermark, null)// 设置布局参数val params: WindowManager.LayoutParams = WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,  // 无障碍服务专用类型WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or  // 不抢占焦点WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or  // 不可触摸WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,  // 显示在屏幕上PixelFormat.TRANSLUCENT)// 设置位置,例如屏幕中央params.gravity = Gravity.CENTER// 添加到WindowManageroverlayWindowManager.addView(overlayView, params)}override fun onDestroy() {super.onDestroy()if (overlayView != null) {overlayWindowManager.removeView(overlayView)}}@RequiresApi(Build.VERSION_CODES.N)override fun onAccessibilityEvent(event: AccessibilityEvent?) {// 无障碍事件处理,不做任何操作}override fun onInterrupt() {// 中断时的处理}
}

2、定义布局文件

创建显示在Window顶层的透明水印显示布局。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><LinearLayoutandroid:id="@+id/layout1"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:layout_marginTop="20dp"><TextViewandroid:layout_width="0dp"android:layout_height="100dp"android:layout_weight="1"android:gravity="center"android:rotation="50.0"android:text="Hello 2025"android:textColor="@color/light_gray"android:textSize="20sp" /><TextViewandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:text="Hello 2025"android:rotation="50.0"android:textColor="@color/light_gray"android:textSize="20sp" /><TextViewandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:text="Hello 2025"android:rotation="50.0"android:textColor="@color/light_gray"android:textSize="20sp" /></LinearLayout><LinearLayoutandroid:id="@+id/layout2"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:layout_marginTop="50dp"><TextViewandroid:layout_width="0dp"android:layout_height="119dp"android:layout_weight="1"android:gravity="center"android:rotation="50.0"android:text="Hello 2025"android:textColor="@color/light_gray"android:textSize="20sp" /><TextViewandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:text="Hello 2025"android:rotation="50.0"android:textColor="@color/light_gray"android:textSize="20sp" /><TextViewandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:text="Hello 2025"android:rotation="50.0"android:textColor="@color/light_gray"android:textSize="20sp" /></LinearLayout><LinearLayoutandroid:id="@+id/layout3"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:layout_marginTop="50dp"><TextViewandroid:layout_width="0dp"android:layout_height="119dp"android:layout_weight="1"android:gravity="center"android:rotation="50.0"android:text="Hello 2025"android:textColor="@color/light_gray"android:textSize="20sp" /><TextViewandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:text="Hello 2025"android:rotation="50.0"android:textColor="@color/light_gray"android:textSize="20sp" /><TextViewandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:text="Hello 2025"android:rotation="50.0"android:textColor="@color/light_gray"android:textSize="20sp" /></LinearLayout><LinearLayoutandroid:id="@+id/layout4"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:layout_marginTop="50dp"><TextViewandroid:layout_width="0dp"android:layout_height="119dp"android:layout_weight="1"android:gravity="center"android:rotation="50.0"android:text="Hello 2025"android:textColor="@color/light_gray"android:textSize="20sp" /><TextViewandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:text="Hello 2025"android:rotation="50.0"android:textColor="@color/light_gray"android:textSize="20sp" /><TextViewandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:text="Hello 2025"android:rotation="50.0"android:textColor="@color/light_gray"android:textSize="20sp" /></LinearLayout></LinearLayout>

3、在AndroidManifest.xml 中声明无障碍服务

在应用清单文件AndroidManifest.xml 中声明无障碍服务,并配置相关的无障碍权限。

        <serviceandroid:name=".CustomOverlayService"android:exported="true"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /></intent-filter><meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/accessibility_service_config" /></service>

另外,上面的android:resource引用的无障碍属性配置文件,需要在 res/xml/ 文件夹中创建 accessibility_service_config.xml 文件,去配置无障碍服务的相关属性。

<accessibility-servicexmlns:android="http://schemas.android.com/apk/res/android"android:description="@string/accessibility_service_descriptions"android:accessibilityEventTypes="typeAllMask"android:canRetrieveWindowContent="true"android:packageNames=""android:notificationTimeout="100"android:accessibilityFeedbackType="feedbackAllMask"android:accessibilityFlags="flagDefault"android:settingsActivity=""/>

4、启用无障碍服务

写一个页面,添加一个开启无障碍服务的按钮,点击按钮跳转到系统设置中的辅助功能,可以去开启对应应用的无障碍功能开关。具体操作路径为:设置 -> 辅助功能 -> 已安装的服务 中找到你的服务并开启它。

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContent {Greeting()}}
}@Composable
fun Greeting() {Column(modifier = Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {val context: Context = LocalContext.currentButton(onClick = {if (!isAccessibilityServiceEnabled(context, CustomOverlayService::class.java)) {Log.i("AccessibilityService", "AccessibilityService disabled .")context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))} else {Log.i("AccessibilityService", "AccessibilityService enabled .")context.startService(Intent(context,CustomOverlayService::class.java))}}, shape = ButtonDefaults.textShape) {Text(text = "启动无障碍功能")}}
}/*** 判断无障碍服务是否开启*/
private fun isAccessibilityServiceEnabled(context: Context, accessibilityServiceClass: Class<*>): Boolean {var accessibilityEnabled = 0val service: String = context.packageName.toString() + "/" + accessibilityServiceClass.canonicalNametry {accessibilityEnabled = Settings.Secure.getInt(context.contentResolver, Settings.Secure.ACCESSIBILITY_ENABLED)} catch (e: Settings.SettingNotFoundException) {e.printStackTrace()}val colonSplitter = TextUtils.SimpleStringSplitter(':')if (accessibilityEnabled == 1) {val settingValue = Settings.Secure.getString(context.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)if (settingValue != null) {colonSplitter.setString(settingValue)while (colonSplitter.hasNext()) {val componentName = colonSplitter.next()if (componentName.equals(service, ignoreCase = true)) {return true}}}}return false
}

5、设备全局水印效果展示

系统设置页面水印显示效果
系统设置页面水印

设备锁屏页面水印

三、总结

         无障碍服务权限是一个非常强大的工具,开启后可以实现很多你意想不到的效果或者功能。比如,还可以通过无障碍服务获取设备上运行的最上层应用的包名以及VIEW。后面有空的话,我也会深挖更多的无障碍服务相关的功能来展示给大家,敬请期待!

demo源码,请查看文章开头资源链接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/64681.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java中多线程的一些常见操作

Java 中的多线程是通过并发编程来提高应用程序的效率和响应速度。Java 提供了多个机制和类来支持多线程编程&#xff0c;包括继承 Thread 类、实现 Runnable 接口、使用线程池等。以下是 Java 中一些常见的多线程操作和应用场景。 1. 创建线程 1.1 通过继承 Thread 类创建线程…

使用 Docker 搭建 Hadoop 集群

1.1. 启用 WSL 与虚拟机平台 1.1.1. 启用功能 启用 WSL并使用 Moba 连接-CSDN博客 1.2 安装 Docker Desktop 最新版本链接&#xff1a;Docker Desktop: The #1 Containerization Tool for Developers | Docker 指定版本链接&#xff1a;Docker Desktop release notes | Do…

【每日学点鸿蒙知识】广告ID、NFC手机充值、CSS支持语法、PC与模拟器交互、SO热更新等

1、HamonyOS 样机获取成功返回Oaid为00000000-0000-0000-0000-000000000000&#xff1f; 请求授权时需要触发动态授权弹窗,看一下是不是没有触发授权弹窗。 可以参考以下代码以及文档&#xff1a; // ets import identifier from ohos.identifier.oaid; import hilog from oh…

【YOLO 项目实战】(12)红外/可见光多模态目标检测

欢迎关注『youcans动手学模型』系列 本专栏内容和资源同步到 GitHub/youcans 【YOLO 项目实战】&#xff08;10&#xff09;YOLO8 环境配置与推理检测 【YOLO 项目实战】&#xff08;11&#xff09;YOLO8 数据集与模型训练 【YOLO 项目实战】&#xff08;12&#xff09;红外/可…

logback日志框架源码分析

目录 (一)入口:slf4j选择日志框架 (二)日志框架初始化 (1)logback的3种配置方式 a、BasicConfigurator默认配置 b、SPI方式配置的Configurator实现类 c、通过配置文件初始化 (2)xml配置文件初始化 (三)Logger的创建 (四)打印日志 本文源码基于:logback版…

国产数据库OceanBase从入门到放弃教程

1. 介绍 是由蚂蚁集团&#xff08;Ant Group&#xff0c;原蚂蚁金服&#xff09;自主研发的分布式关系型数据库。它旨在解决海量数据存储和高并发访问的问题&#xff0c;特别适合金融级应用场景&#xff0c;如支付宝等对数据一致性、可靠性和性能有极高要求的服务。以下是关于…

连接Milvus

连接到Milvus 验证Milvus服务器正在侦听哪个本地端口。将容器名称替换为您自己的名称。 docker port milvus-standalone 19530/tcp docker port milvus-standalone 2379/tcp docker port milvus-standalone 192.168.1.242:9091/api/v1/health 使用浏览器访问连接地址htt…

机器学习中的欠拟合

当模型不能够准确地表达输入与输出的关系时&#xff0c;就是欠拟合。它在训练集和未见过的数据都会产生高误差率。过度拟合则在训练集表现出低误差率&#xff0c;只有对未见过的数据表现出高误差率。 当模型太过于简单时&#xff0c;它需要更多的训练时间、更多的输入特征、更…

安卓入门二 Kotlin基础

Kotlin Kotlin的历史 Kotlin由Jet Brains公司开发设计&#xff0c;2011年公布第一版&#xff0c;2012年开源。 2016年发布1.0正式版&#xff0c;并且Jet Brains在IDEA加入对Kotlin的支持&#xff0c;安卓自此又有新的选择。 2019年谷歌宣布Kotlin成为安卓第一开发语言&#x…

淺談Cocos2djs逆向

前言 簡單聊一下cocos2djs手遊的逆向&#xff0c;有任何相關想法歡迎和我討論^^ 一些概念 列出一些個人認為比較有用的概念&#xff1a; Cocos遊戲的兩大開發工具分別是CocosCreator和CocosStudio&#xff0c;區別是前者是cocos2djs專用的開發工具&#xff0c;後者則是coco…

STM32驱动NRF24L01

一、NRF24L01的相关介绍 1.2 引脚的介绍 关于SPI的引脚就不再说了&#xff0c;这里介绍其余的两个引脚&#xff1a; CE 模块控制引脚&#xff1a;芯片开启信号&#xff0c;激活RX或TX模式 IRQ 模块中断信号输出引脚&#xff1a;其低电平有效&#xff0c;也就是中断时变为低电平…

【Python】 glob批处理模块的学习

1.什么是glob模块&#xff1f; 在 Python 中&#xff0c;glob模块是一个用于文件路径名的模式匹配的工具。它使用简单的通配符规则来匹配文件和目录的路径&#xff0c;这些通配符规则类似于在命令行中使用的文件搜索规则。这使得在处理文件系统中的多个文件或目录时非常方便&am…

Android 系统 AlarmManager 系统层深度定制

Android 系统 AlarmManager 系统层深度定制 目录 引言AlarmManager 概述AlarmManager 系统架构AlarmManager 核心代码解读AlarmManager 深度定制方法 修改 AlarmManagerService 修改定时任务调度策略增加定时任务类型定制内核层 修改定时触发精度增加定时触发类型优化定时任务…

解决vue-i18n在非.vue文件中,在其他js文件中无法使用的问题

其实很简单&#xff0c;把i18n直接挂载到window上&#xff0c;全局可使用。下面请看详细。 一、安装 npm install vue-i18n9二、在vue的main.js中引入 import Vue from "vue" import VueI18n from vue-i18n Vue.use(VueI18n)//注入到所有的子组件&#xff0c;就是…

线性代数期末复习 [基础篇]

关于第六点: AXB 在期末考试中一般A都是可逆的 我们可以先把A的逆求出来,X A − 1 B A^-1B A−1B,或者 (A,B) -> r (E, A − 1 B A^-1B A−1B) 如果A矩阵不可逆,转变为方程组求解问题,假设都是二维矩阵 A(x1,x2) (b1,b2) Ax1 b1,Ax2 b2 XAB 如果A可逆,直接XB A − 1 A^-…

C++ —— 数据类型转换和数据类型的别名

数据类型转换 引言自动类型转换强制类型转换数据类型的别名 引言 计算机进行运算时&#xff0c;要求各操作数的数据类型、大小和存储方式都要相同。&#xff08;例如&#xff1a;8字节的整数和8字节的浮点数&#xff0c;虽然占用内存大小一样&#xff0c;但是存储方式不同&…

Kali 自动化换源脚本编写与使用

1. 背景与需求 在使用 Kali Linux 的过程中&#xff0c;软件源的配置对系统的更新与软件安装速度至关重要。 Kali 的默认官方源提供了安全且最新的软件包&#xff0c;但有时由于网络条件或地理位置的限制&#xff0c;使用官方源可能会出现速度较慢的问题。 为了解决这一问题&a…

设计模式-创建型-工厂方法模式

什么是工厂方法模式&#xff1f; 工厂方法模式&#xff08;Factory Method Pattern&#xff09;是 创建型设计模式之一&#xff0c;目的是通过定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪个类。简而言之&#xff0c;工厂方法模式通过延迟对象的创建过程到子类来…

【Unity3D】ECS入门学习(十二)IJob、IJobFor、IJobParallelFor

IJob&#xff1a;开启单个线程进行计算&#xff0c;线程内不允许对同一个数据进行操作&#xff0c;也就是如果你想用多个IJob分别计算&#xff0c;将其结果存储到同一个NativeArray<int>数组是不允许的&#xff0c;所以不要这样做&#xff0c;如下例子就是反面教材&#…

Spring 创建和管理 Bean 的原理,以及Spring 的单例模式是否线程安全?(有无状态Bean)

Spring 是一个轻量级的开源框架&#xff0c;广泛应用于 Java 企业级应用的开发。它提供了一个全面的、基于 IOC&#xff08;控制反转&#xff09;和 AOP&#xff08;面向切面编程&#xff09;的容器&#xff0c;可以帮助开发者更好地管理应用程序中的对象。 Spring 创建和管理…