车载Android应用开发与分析 - 初试 SystemUI Plugin

在前面的视频、文章中我们介绍完了整个车载Android应用开发所需要的基础知识:

  1. 【视频文稿】车载Android应用开发与分析 - 走进车载操作系统 - 掘金
  2. 【视频文稿】车载Android应用开发与分析 - AOSP的下载与编译 - 掘金
  3. 【视频文稿】车载Android应用开发与分析 - 开发系统应用 - 掘金
  4. 【视频文稿】车载Android应用开发与分析 - AIDL实践与封装(上) - 掘金
  5. 【视频文稿】车载Android应用开发与分析 - AIDL实践与封装(下) - 掘金

本期内容,我们介绍原生Android Automotive中车载应用的实现方式和它的原理。首先要介绍的就是车载应用开发中非常重要的一个系统应用,Android系统的UI - SystemUI

由于原生Android系统的SystemUI代码量很大、内容也非常庞杂,这里我会挑选出对车载SystemUI开发具有参考意义的模块进行介绍,大约会有4-5期的内容,主要分为以下几个模块:

  1. 车载Android应用开发与分析 - SystemUI 「功能」与「源码结构」分析 - 掘金
  2. 车载Android应用开发与分析 - 初试 SystemUI Plugin

SystemUI的源代码可能是所有Android原生应用中最复杂的一个,当我们需要定制SystemUI时,庞大的源码量会对的定制化开发带来巨大的潜在风险。所以目前车载SystemUI常见的做法就是,从原生SystemUI中移植少量必须的源码,然后从头定制一个源码、功能完全可控的SystemUI。

重新开发一个SystemUI就是唯一的选项吗?当然不是!Google官方早就注意到了这个问题,所以SystemUI中提供插件化的开发方式 - SystemUI Plugin。

本文源码地址:/frameworks/base/+/refs/heads/main/packages/SystemUI/plugin/ExamplePlugin/

本文源码环境基于Android 13

SystemUI Plugin

SystemUI plugin机制是一种让SystemUI的功能可以被动态替换或修改的方法,它可以让开发者快速创建和迭代SystemUI的原型,而尽可能少的修改SystemUI的主框架。

注意:使用Plugin并不能保证我们完全不需要修改SystemUI的主框架,毕竟需求永远是多变的。

Plugin Hooks

Plugin hooks是一些预定义的插件接口,它们可以让应用实现一些特定的功能,并通过Intent和注解来注册和声明插件的类型和版本。Plugin hooks有多种类型,例如OverlayPlugin, QSFactory, VolumeDialog等,每种类型都有一个对应的action和expected interface,用于标识插件的功能和要求。

Android 13中Plugin hooks预定义接口主要有以下几种:

  • BcSmartspaceDataPlugin:这个plugin可以让应用提供自定义的数据给锁屏界面上的智能空间(BcSmartspace),例如天气、日历、新闻等。
  • ClockProviderPlugin:这个plugin可以让应用提供自定义的时钟样式给锁屏界面和始终应用。
  • DozeServicePlugin:这个plugin可以让应用自定义Doze模式的行为,例如控制屏幕亮度、显示内容、传感器等。
  • FalsingPlugin:这个plugin可以让应用自定义对误触(Falsing)事件的检测和处理,例如判断用户是否真的想滑动通知栏或解锁屏幕等。
  • GlobalActions:这个plugin可以让应用自定义全局操作(GlobalActions)对话框的外观和行为,例如添加新的操作按钮或改变对话框样式。
  • GlobalActionsPanelPlugin:这个plugin可以让应用在全局操作对话框中添加一个可展开的面板,用于显示更多的操作选项或信息。
  • IntentButtonProvider:这个plugin可以让应用在锁屏界面上添加一个自定义的按钮,用于启动一个指定的Intent。
  • NavigationEdgeBackPlugin:这个plugin可以让应用自定义导航栏边缘返回(NavigationEdgeBack)手势的行为,例如改变触发区域或动画效果。
  • NotificationListenerController:这个plugin可以让应用控制通知监听器(NotificationListener)服务的连接和断开,以及获取通知事件和数据。
  • NotificationMenuRowPlugin:这个plugin可以让应用自定义通知菜单栏(NotificationMenuRow)的外观和行为,例如添加新的菜单项或改变菜单样式。
  • NotificationPersonExtractorPlugin:这个plugin可以让应用自定义从通知中提取人物信息(NotificationPersonExtractor)的逻辑,例如识别通知中包含的联系人或头像等。
  • OverlayPlugin:这个plugin可以让应用自定义覆盖在通知栏上方的视图(OverlayView),用于显示一些额外的内容或功能。
  • PluginFragment:这个plugin可以让应用在SystemUI中嵌入一个Fragment,用于显示一些自定义的界面或功能。
  • QSFactory:这个plugin可以让应用提供自定义的快速设置工厂(QSFactory),用于创建快速设置图块或面板。
  • SensorManagerPlugin:这个plugin可以让应用使用SensorManager服务来注册和取消注册传感器监听器,以及获取传感器事件和数据。
  • ToastPlugin:这个plugin可以让应用自定义Toast消息(Toast)的外观和行为,例如改变Toast位置或持续时间等。
  • ViewProvider:这个plugin可以让应用提供一个自定义的视图(View),用于替换SystemUI中某些组件或功能。
  • VolumeDialog:这个plugin可以让应用自定义音量调节对话框(VolumeDialog)的外观和行为,例如添加新的音量控制选项或改变音量条的样式。

Plugin 上手

创建一个AndroidStudio的SystemUI plugin项目,可以参考以下的步骤:

1)编译SystemUIPluginLib.jar

使用Plugin之前我们需要编译出SystemUIPluginLib.jar,在AOSP源码根目录执行下面的指令。

make SystemUIPluginLib

然后就可以在下面的目录中得到SystemUIPluginLib.jar

out/target/product/emulator_x86/obj/JAVA_LIBRARIES/SystemUIPluginLib_intermediates/javalib.jar

在AOSP的文档中建议使用 frameworks/base/packages/SystemUI/plugin/update_plugin_lib.sh 脚本编译 SystemUIPluginLib.jar,不过我编译时出现了环境配置问题。

2)配置系统签名

在build.gradle中配置系统签名。

android {...signingConfigs {sign {storeFile file('system.keystore')storePassword '123456'keyAlias 'cardemo'keyPassword '123456'}}buildTypes {release {minifyEnabled falsesigningConfig signingConfigs.sign}debug {minifyEnabled falsesigningConfig signingConfigs.sign}}
}

关于如何制作系统签名,请参考:车载Android应用开发与分析 - 开发系统应用 - 掘金

3)创建一个Plugin

在plugin项目中定义一个类,实现自Plugin中已经提供的各种插件,并使用Requires注解声明target和version字段,这些字段用于标识插件的类型和版本。

@Requires(target = OverlayPlugin.class, version = OverlayPlugin.VERSION)
public class SampleOverlayPlugin implements OverlayPlugin {private static final String TAG = "SampleOverlayPlugin";private Context mPluginContext;private View mStatusBarView;private View mNavBarView;@Overridepublic void onCreate(Context sysuiContext, Context pluginContext) {Log.d(TAG, "onCreate");mPluginContext = pluginContext;}@Overridepublic void onDestroy() {if (mInputSetup) {mStatusBarView.getViewTreeObserver().removeOnComputeInternalInsetsListener(onComputeInternalInsetsListener);}Log.d(TAG, "onDestroy");if (mStatusBarView != null) {mStatusBarView.post(() -> ((ViewGroup) mStatusBarView.getParent()).removeView(mStatusBarView));}if (mNavBarView != null) {mNavBarView.post(() -> ((ViewGroup) mNavBarView.getParent()).removeView(mNavBarView));}}@Override
public void setup(View notificationShadeWindowView, View navBar) {Log.d(TAG, "Setup");if (notificationShadeWindowView instanceof ViewGroup) {mStatusBarView = LayoutInflater.from(mPluginContext).inflate(R.layout.colored_overlay, (ViewGroup) notificationShadeWindowView, false);((ViewGroup) notificationShadeWindowView).removeAllViews();((ViewGroup) notificationShadeWindowView).addView(mStatusBarView);}if (navBar instanceof ViewGroup) {mNavBarView = LayoutInflater.from(mPluginContext).inflate(R.layout.colored_overlay, (ViewGroup) navBar, false);((ViewGroup) navBar).removeAllViews();((ViewGroup) navBar).addView(mNavBarView);}
}
}

注意:Android不同版本中SystemUI的代码存在不小的差异,例如:Android13中setup(View statusBar, View navBar)中返回的statusBar实际上是NotificationShadeWindowView。

4)注册Plugin

在plugin项目的AndroidManifest中注册一个service,使用action和permission属性指定插件的接口和权限,这样SystemUI就可以通过Intent找到插件。

<uses-permission android:name="com.android.systemui.permission.PLUGIN" /><application><serviceandroid:name=".SampleOverlayPlugin"android:exported="false"android:label="@string/plugin_label"tools:ignore="Instantiatable"><intent-filter><action android:name="com.android.systemui.action.PLUGIN_OVERLAY" /></intent-filter></service></application>

的name可以在我们实现的plugin接口中找到。

SystemUI为了保证系统安全,对于plugin的加载,构筑了两道防线:

第一道防线是Build.IS_DEBUGGABLE检查。SysUI 在扫描或加载设备上的任何插件之前,会检查Build.IS_DEBUGGABLE,以确保构建是可调试的。

第二道防线是就是签名权限。所有插件都必须被系统签名且持有com.android.systemui.permission.PLUGIN权限才能加载其任何代码,否则将记录违规行为,并忽略插件。

5)运行Plugin

将plugin.apk push 到Android 13 模拟器的/system/priv-app/ 目录下,重启。可以看到如下的效果:

  1. NavBar的所有子View被移除,并添加了一个红色的View;
  2. NotificationShadeWindowView的所有子View被移除,并添加了一个红色的View。

总结

本文初试了SystemUI插件机制,在编写本文时发现Plugin相关的资料少的可怜,即使是官方资料有的也过时了。所以就像标题那样,本文只是简单尝试了Plugin,如何使用Plugin来详细定制一个完全符合我们需求的SystemUI呢?这个我们放到以后再写,因为接下来需要先来分析SystemUI Plugin的原理,在资料如此稀少的情况下,不了解原理几乎无法写出符合需求的Plugin。在分析的原理的过程中,我们会逐步补完、理解一些Plugin的概念。

以上就是本文的所有内容,感谢你的阅读,希望对你所有帮助。

参考资料

Sysui plugin

SystemUI Plugin 简介及使用

/SystemUI/docs/plugins.md

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

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

相关文章

Elasticsearch:为具有许多 and/or 高频术语的 top-k 查询带来加速

作者&#xff1a;Adrien Grand Disjunctive queries&#xff08;term_1 OR term_2 OR ... OR term_n&#xff09;非常常用&#xff0c;因此在提高查询评估效率方面它们受到了广泛关注。 Apache Lucene 对于评估 disjunctive queries 有两个主要优化&#xff1a;一方面用于详尽评…

[Android jni] Bitmap与Mat对象的相互转换

1.JNI实现Bitmap到Mat的转换 void bitmap2mat(JNIEnv *env, jobject bitmap,Mat &mat){//锁定画布void *pixels;AndroidBitmap_lockPixels(env,bitmap,&pixels);//获取Bitmap的信息AndroidBitmapInfo bitmapInfo;AndroidBitmap_getInfo(env,bitmap,&bitmapInfo);//…

opencv(python)视频按帧切片/cv2.VideoCapture()用法

一、介绍 cv2.VideoCapture是OpenCV中一个用于捕捉视频的类。它可以访问计算机的摄像头&#xff0c;或从视频文件中读取图像。通过cv2.VideoCapture&#xff0c;用户可以轻松地捕捉、保存、编辑和传输视频流数据。 使用cv2.VideoCapture可以实现以下功能&#xff1a; 1. 打开…

计算机网络第四节 数据链路层

一&#xff0c;引入数据链路层的目的 1.目的意义 数据链路层是体系结构中的第二层&#xff1b; 从发送端来讲&#xff0c;物理层可以将数据链路层交付下来的数据&#xff0c;装换成光&#xff0c;电信号发送到传输介质上了 从接收端来讲&#xff0c;物理层能将传输介质的光&…

【Vue】一文让你进入Vue的大门

Vue简介 官网 ● 英文官网 ● 中文官网 介绍与描述 Vue历史 Vue 是一套用来动态构建用户界面的渐进式JS框架 构建用户界面&#xff1a;把数据通过某种办法变成用户界面 渐进式&#xff1a;Vue可以自底向上逐层的应用&#xff0c;简单应用只需要一个轻量小巧的核心库&#xff0c…

python 语法入门

文章目录 前言python 语法入门1. 语句分隔符2. 注释3. pep8规范4. 变量5. 扩展5.1. 运行此行代码的过程 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会…

Promise,async,await 面试题

目录 5&#xff0c;面试题1234567 推荐先看Promise 相关知识点 5&#xff0c;面试题 1 结果 1&#xff0c;5&#xff0c;2&#xff0c;3&#xff0c;4 const promise new Promise((resolve, reject) > {console.log(1);setTimeout(() > {console.log(2);resolve();c…

【LeetCode刷题篇零】一些基础算法知识和前置技能(下)

数组常用技巧 使用数组代替Map 使用另一个数组来统计每个数字出现的次数&#xff0c;数组的下标作为key, 数组的值作为value&#xff0c; 将数字作为数组的下标索引&#xff0c;数组里的值存储该数字出现的次数&#xff0c;原理有点类似桶排序中使用的计数数组。 比如这里如…

小程序赖加载刷新数据页面数据堆叠问题debug

目录 项目所需 原生写赖加载存在的bug 解决问题思路及代码实现 思路&#xff1a; 代码实现&#xff1a; 列表.wxml 列表.js Wenjain_shanchu.js Wenjain_shanchu.json Wenjain_shanchu.wxml shouye.js ⭐️ 好书推荐 【内容简介】 项目所需 某高校大一新生入学&am…

​LeetCode解法汇总2605. 从两个数字数组里生成最小数字

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; GitHub - September26/java-algorithms: 算法题汇总&#xff0c;包含牛客&#xff0c;leetCode&#xff0c;lintCode等网站题目的解法和代码&#xff0c;以及完整的mode类&#…

EasyPHP-Devserver-17安装和配置mantisBT

文章目录 1、准备工作2、安装easyphp2.1 http://127.0.0.1 无法访问 3、安装mantisBT和phpMyAdmin3.1 配置浏览器的访问url和端口号&#xff08;配置局域网内可访问&#xff09;3.2 安装mantis 4、Administrator 注册新用户时设置登录密码5、附件上传6、邮件配置 文章参考自&am…

【广州华锐互动】煤矿提升机作业VR互动实训平台

在煤矿行业中&#xff0c;安全性是无可忽视的首要任务。传统的煤矿工人培训方法&#xff0c;如理论课堂讲解、实地操作演示&#xff0c;尽管具有一定的效果&#xff0c;但往往无法真实地模拟出煤矿的复杂环境&#xff0c;工作人员在没有真正接触煤矿的情况下&#xff0c;很难理…

【LLM】Windows本地CPU部署民间版中文羊驼模型(Chinese-LLaMA-Alpaca)踩坑记录

目录 前言 准备工作 Git Python3.9 Cmake 下载模型 合并模型 部署模型 前言 想必有小伙伴也想跟我一样体验下部署大语言模型, 但碍于经济实力, 不过民间上出现了大量的量化模型, 我们平民也能体验体验啦~, 该模型可以在笔记本电脑上部署, 确保你电脑至少有16G运行…

【Face Swapping综述】Quick Overview of Face Swap Deep Fakes

【Face Swapping综述】Quick Overview of Face Swap Deep Fakes 0、前言Abstract1. Introduction2. Face Swapping Process2.1. Preprocessing2.2. Identity Extraction2.3. Attributes Extractor2.4. Generator2.5. Postprocessing2.6. Evaluation Methods3. Challenges4. Con…

【GO语言基础】变量常量

系列文章目录 【Go语言学习】ide安装与配置 【GO语言基础】前言 【GO语言基础】变量常量 【GO语言基础】数据类型 文章目录 系列文章目录常量和枚举变量声明全局变量声明大小写敏感 总结 常量和枚举 使用const关键字声明常量&#xff0c;并为每个常量提供显式的值。Go语言没有…

在Windows上通过SSH公私钥实现无密码登录Linux

在Windows上通过SSH公私钥实现无密码登录Linux 在Windows上生成SSH密钥对&#xff1a; 打开命令提示符或PowerShell窗口。 输入以下命令生成SSH密钥对&#xff1a; ssh-keygen -t rsa -b 4096按照提示输入密钥的保存路径和密码&#xff08;可选&#xff09;。 在指定的路径下…

嵌入式学习笔记(26)5S5PV210串行通信编程实战

5.5.1整个流程分析 整个串口通信相关程序包含2部分&#xff1a;uart_init负责初始化串口&#xff0c;uart_putc负责发送一个字节 5.5.2串口初始化关键步骤 &#xff08;1&#xff09;初始化串口的Tx和Rx引脚所对应的GPIO(查原理图可知Tx和Rx分别对应GPA0_1和GPA0_0) &#…

解决“您在 /var/spool/mail/root 中有新邮件”问题

一、发现问题 二、解决问题 1、删除邮件 cat /dev/null > /var/spool/mail/root 2、禁止系统启动邮件检查 echo "unset MAILCHECK" >> /etc/profile 三、解决结果

Matplotlib | 高阶绘图案例【3】- 五大战区高校排名

文章目录 &#x1f3f3;️‍&#x1f308; 1. 导入模块&#x1f3f3;️‍&#x1f308; 2. 数据处理2.1 高效数据2.2 学校排名 &#x1f3f3;️‍&#x1f308; 3. 绘图3.1 绘制图布&#xff0c;设置极坐标系3.2 绘制学校排名柱状图3.3 绘制五大战区扇形区域3.4 添加战区、学校…

关于安卓13中Android/data目录下的文件夹只能查看无法进行删改的问题

前言 因为升级了安卓13&#xff0c;然后有个app需要恢复数据&#xff0c;打算和以前一样直接删除Android/data下对应目录再添加&#xff0c;结果不行&#xff0c;以下是结合网上以及自己手机情况来做的一种解决方案。 解决 准备&#xff1a; 待恢复app&#xff08;包名com.…