Android16 原生设置部分问题修改
一、关闭媒体音量睡眠模式中关闭媒体音量但是在我们自己的设置中依然可以调节音量但是调节后会自动恢复静音。先看下原生设置的调用逻辑。//Settings/src/com/android/settings/notification/modes/ZenModeOtherPreferenceController.java Override public boolean onPreferenceChange(Preference preference, Object newValue) { final boolean allow (Boolean) newValue; return savePolicy(policy - policy.allowCategory(getCategory(), allow)); } private int getCategory() { switch (getPreferenceKey()) { case modes_category_alarm: return PRIORITY_CATEGORY_ALARMS; case modes_category_media: return PRIORITY_CATEGORY_MEDIA; case modes_category_system: return PRIORITY_CATEGORY_SYSTEM; case modes_category_reminders: return PRIORITY_CATEGORY_REMINDERS; case modes_category_events: return PRIORITY_CATEGORY_EVENTS; } return -1; }ZenModeOtherPreferenceController继承了AbstractZenModePreferenceController。//Settings/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java /** * Subclasses should call this method (or a more specific one, like {link #savePolicy} from * their {code onPreferenceChange()} or similar, in order to apply changes to the mode being * edited (e.g. {code saveMode(mode - { mode.setX(value); return mode; } }. * * param updater Function to update the {link ZenMode}. Modifying and returning the same * instance is ok. */ protected final boolean saveMode(FunctionZenMode, ZenMode updater) { checkState(mBackend ! null); ZenMode mode mZenMode; if (mode null) { Log.wtf(TAG, Cannot save mode, it hasnt been loaded ( getClass() )); return false; } mode updater.apply(mode); mBackend.updateMode(mode); return true; } protected final boolean savePolicy(FunctionZenPolicy.Builder, ZenPolicy.Builder updater) { return saveMode(mode - { ZenPolicy.Builder policyBuilder new ZenPolicy.Builder(mode.getPolicy()); policyBuilder updater.apply(policyBuilder); mode.setPolicy(policyBuilder.build()); return mode; }); }最终调用到了mBackend.updateMode。//frameworks/base/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java public void updateMode(ZenMode mode) { if (mode.isManualDnd()) { try { NotificationManager.Policy dndPolicy new ZenModeConfig().toNotificationPolicy(mode.getPolicy()); mNotificationManager.setNotificationPolicy(dndPolicy, /* fromUser */ true); mNotificationManager.setManualZenRuleDeviceEffects( mode.getRule().getDeviceEffects()); } catch (Exception e) { Log.w(TAG, Error updating manual mode, e); } } else { mNotificationManager.updateAutomaticZenRule(mode.getId(), mode.getRule(), /* fromUser */ true); } }可以看到是在NotificationManager.setNotificationPolicy这样的话我们就可以在自己的应用里面获取到对应的设置了。private NotificationManager.Policy mPolicy; private final NotificationManager mNotificationManager; mNotificationManager (NotificationManager) context.getSystemService( Context.NOTIFICATION_SERVICE); public boolean isPriorityCategoryEnabled(int categoryType) { updatePolicy(); return (mPolicy.priorityCategories categoryType) ! 0; } public void updatePolicy() { if (mNotificationManager ! null) { mPolicy mNotificationManager.getNotificationPolicy(); } }在我们需要刷新视图的方法中添加isPriorityCategoryEnabled()就可以控制用户能否调节音量了。二、修改默认的拼写检查工具系统-键盘-拼写检查工具默认是AOSP拼写检查工具我们系统内置了Gboard因此希望默认是Gboard的拼写检查工具。先看下设置中的数据是从哪获取的。//Settings/src/com/android/settings/inputmethod/SpellCheckersSettings.java Override public void onCreate(final Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.spellchecker_prefs); mSpellCheckerLanaguagePref findPreference(KEY_SPELL_CHECKER_LANGUAGE); mTsm (TextServicesManager) getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); mCurrentSci mTsm.getCurrentSpellChecker(); mEnabledScis mTsm.getEnabledSpellCheckers(); populatePreferenceScreen(); }//frameworks/base/services/core/java/com/android/server/textservices/TextServicesManagerService.java private static class TextServicesData { ... NonNull private String getSelectedSpellChecker() { return getString(Settings.Secure.SELECTED_SPELL_CHECKER, ); } Nullable public SpellCheckerInfo getCurrentSpellChecker() { final String curSpellCheckerId getSelectedSpellChecker(); if (TextUtils.isEmpty(curSpellCheckerId)) { return null; } return mSpellCheckerMap.get(curSpellCheckerId); } ... }由于SELECTED_SPELL_CHECKER没有配置默认值service中返回的是null但实际上进入系统还是选中了AOSP的选项的那应该是有初始化配置加载了默认选项。//frameworks/base/services/core/java/com/android/server/textservices/TextServicesManagerService.java GuardedBy(mLock) private void initializeInternalStateLocked(UserIdInt int userId) { TextServicesData tsd mUserData.get(userId); if (tsd null) { tsd new TextServicesData(userId, mContext); mUserData.put(userId, tsd); } tsd.initializeTextServicesData(); SpellCheckerInfo sci tsd.getCurrentSpellChecker(); if (sci null) { sci findAvailSystemSpellCheckerLocked(null, tsd); // Set the current spell checker if there is one or more system spell checkers // available. In this case, sci is the first one in the available spell // checkers. setCurrentSpellCheckerLocked(sci, tsd); } } private SpellCheckerInfo findAvailSystemSpellCheckerLocked(String prefPackage, TextServicesData tsd) { // Filter the spell checker list to remove spell checker services that are not pre-installed ArrayListSpellCheckerInfo spellCheckerList new ArrayList(); for (SpellCheckerInfo sci : tsd.mSpellCheckerList) { if ((sci.getServiceInfo().applicationInfo.flags ApplicationInfo.FLAG_SYSTEM) ! 0) { spellCheckerList.add(sci); } } final int spellCheckersCount spellCheckerList.size(); if (spellCheckersCount 0) { Slog.w(TAG, no available spell checker services found); return null; } if (prefPackage ! null) { for (int i 0; i spellCheckersCount; i) { final SpellCheckerInfo sci spellCheckerList.get(i); if (prefPackage.equals(sci.getPackageName())) { if (DBG) { Slog.d(TAG, findAvailSystemSpellCheckerLocked: sci.getPackageName()); } return sci; } } } // Look up a spell checker based on the system locale. // TODO: Still there is a room to improve in the following logic: e.g., check if the package // is pre-installed or not. final Locale systemLocal mContext.getResources().getConfiguration().locale; final ArrayListLocale suitableLocales LocaleUtils.getSuitableLocalesForSpellChecker(systemLocal); if (DBG) { Slog.w(TAG, findAvailSystemSpellCheckerLocked suitableLocales Arrays.toString(suitableLocales.toArray(new Locale[suitableLocales.size()]))); } final int localeCount suitableLocales.size(); for (int localeIndex 0; localeIndex localeCount; localeIndex) { final Locale locale suitableLocales.get(localeIndex); for (int spellCheckersIndex 0; spellCheckersIndex spellCheckersCount; spellCheckersIndex) { final SpellCheckerInfo info spellCheckerList.get(spellCheckersIndex); final int subtypeCount info.getSubtypeCount(); for (int subtypeIndex 0; subtypeIndex subtypeCount; subtypeIndex) { final SpellCheckerSubtype subtype info.getSubtypeAt(subtypeIndex); final Locale subtypeLocale SubtypeLocaleUtils.constructLocaleFromString( subtype.getLocale()); if (locale.equals(subtypeLocale)) { // TODO: We may have more spell checkers that fall into this category. // Ideally we should pick up the most suitable one instead of simply // returning the first found one. return info; } } } } if (spellCheckersCount 1) { Slog.w(TAG, more than one spell checker service found, picking first); } return spellCheckerList.get(0); }在初始化方法中看到当返回null是会去查找可用的拼写检查工具并返回列表中的第一个值。现象分析完毕可以开始进行修改了要么在查找可用拼写工具时设置优先排序将Gboard放在第一个要么配置默认选中值SELECTED_SPELL_CHECKER。这里为了方便我直接配置了默认值。//frameworks/base/packages/SettingsProvider/res/values/defaults.xml string namedef_spell_checker translatablefalse com.google.android.inputmethod.latin/com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService /string//frameworks/base/packages/SettingsProvider/src/com/android/provider/settings/DatabaseHelper.java private void loadSecureSettings(SQLiteDatabase db) { ... loadStringSetting(stmt, Settings.Secure.SELECTED_SPELL_CHECKER, R.string.def_spell_checker); ... }三、隐藏应用列表中的部分应用有些我们自己的系统应用不想让用户看到需要隐藏。原生设置中有两个区域显示应用一个是最近使用的应用一个是全部应用都需要进行隐藏。最近使用应用中已经有一个隐藏的逻辑了直接在对应位置添加应用包名即可。// packages/apps/Settings/src/com/android/settings/applications/RecentAppStatsMixin.java public class RecentAppStatsMixin implements LifecycleObserver, OnStart { private static final String TAG RecentAppStatsMixin; private static final SetString SKIP_SYSTEM_PACKAGES new ArraySet(); VisibleForTesting ListUsageStatsWrapper mRecentApps; private final int mMaximumApps; private final Context mContext; private final PackageManager mPm; private final UserManager mUserManager; private final PowerManager mPowerManager; private final ApplicationsState mApplicationsState; private final ListRecentAppStatsListener mAppStatsListeners; private Calendar mCalendar; static { SKIP_SYSTEM_PACKAGES.addAll(Arrays.asList( android, com.android.phone, SETTINGS_PACKAGE_NAME, SYSTEMUI_PACKAGE_NAME, com.android.providers.calendar, com.android.providers.media, com.your.package.name )); }所有应用显示的逻辑中有一个filter在里面叠加一个我们的包名过滤就可以实现隐藏了。//packages/apps/Settings/src/com/android/settings/spa/app/AllAppList.kt val hiddenPackages setOf( com.your.package.name, ... com.your.package.name2 ) override fun filter( userIdFlow: FlowInt, option: Int, recordListFlow: FlowListAppRecordWithSize, ): FlowListAppRecordWithSize recordListFlow.filterItem { record - // 原有筛选条件 val baseMatch when (SpinnerItem.entries.getOrNull(option)) { SpinnerItem.Enabled - record.app.enabled !record.app.isInstantApp SpinnerItem.Disabled - isDisabled(record) SpinnerItem.Instant - isInstant(record) else - true } // 叠加包名过滤 val packageMatch record.app.packageName !in hiddenPackages baseMatch packageMatch }四、配置默认浏览器在frameworks/base/core/res/res/values/config.xml中可以配置默认浏览器在对应标签中填入想要默认的浏览器包名即可。string namedefault_browser translatablefalsecom.android.chrome/string五、Launcher3中长按应用图标时图标会消失松手后恢复显示这个问题比较奇怪逻辑上都是原生的不知道为什么要让图标消失但是在最新的android16Launcher3上又没有这个问题现象了。这里不分析具体的流程了可能最新上的又有变化感兴趣的可以自己去看下直接贴上修改代码。//packages/apps/Launcher3/src/com/android/launcher3/popup/PopupContainerWithArrow.java /** * Determines when the deferred drag should be started. * * Current behavior: * - Start the drag if the touch passes a certain distance from the original touch down. */ public DragOptions.PreDragCondition createPreDragCondition(boolean updateIconUi) { return new DragOptions.PreDragCondition() { Override public boolean shouldStartDrag(double distanceDragged) { return distanceDragged mStartDragThreshold; } Override public void onPreDragStart(DropTarget.DragObject dragObject) { if (!updateIconUi) { return; } // 这里注释掉控制显示的逻辑 if (mIsAboveIcon) { // Hide only the icon, keep the text visible. //mOriginalIcon.setIconVisible(false); //mOriginalIcon.setVisibility(VISIBLE); } else { // Hide both the icon and text. //mOriginalIcon.setVisibility(INVISIBLE); } } Override public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) { if (!updateIconUi) { return; } mOriginalIcon.setIconVisible(true); if (dragStarted) { // Make sure we keep the original icon hidden while it is being dragged. mOriginalIcon.setVisibility(INVISIBLE); } else { // TODO: add WW logging if want to add logging for long press on popup // container. // mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon); if (!mIsAboveIcon) { // Show the icon but keep the text hidden. mOriginalIcon.setVisibility(VISIBLE); mOriginalIcon.setTextVisibility(false); } } } }; }//packages/apps/Launcher3/src/com/android/launcher3/Workspace.java public void startDrag(CellInfo cellInfo, DragOptions options) { View child cellInfo.cell; mDragInfo cellInfo; // 判断是应用图标时不再进行隐藏 if (!(child instanceof BubbleTextView)) { child.setVisibility(INVISIBLE); } if (options.isAccessibleDrag) { mAccessibilityDragListener new AccessibleDragListenerAdapter(this, WorkspaceAccessibilityHelper::new) { Override protected void enableAccessibleDrag(boolean enable, Nullable DragObject dragObject) { super.enableAccessibleDrag(enable, dragObject); setEnableForLayout(mLauncher.getHotseat(), enable); if (enable dragObject ! null dragObject.dragInfo instanceof LauncherAppWidgetInfo) { mLauncher.getHotseat().setImportantForAccessibility( IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); } } }; } beginDragShared(child, this, options); }

相关新闻

PHPStan 错误标识 requireExtends.deprecatedClass 全解析:当 @phpstan-require-extends 引用了已废弃的类

PHPStan 错误标识 requireExtends.deprecatedClass 全解析:当 @phpstan-require-extends 引用了已废弃的类

开发工具代码质量静态分析 【免费下载链接】phpstan PHP Static Analysis Tool - discover bugs in your code without running it! 项目地址: https://gitcode.com/gh_mirrors/ph/phpstan 点击查看 免费下载 requireExtends.deprecatedClass 是 PHPStan 在 phpsta…

2026/9/24 16:56:04 阅读更多 →
loop-action:在 GitHub Actions 中安全执行自治 Agent 的 Composite Action 实战指南

loop-action:在 GitHub Actions 中安全执行自治 Agent 的 Composite Action 实战指南

人工智能AI AgentAgent 工作流CLI研发协作AI 技能MCP 服务 【免费下载链接】loop-engineering Practical patterns, starters & CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and …

2026/9/24 16:56:04 阅读更多 →
Comp AI CRM 图标设计工程指南:让图标在界面中自然安放的细节法则

Comp AI CRM 图标设计工程指南:让图标在界面中自然安放的细节法则

后端前端CRM人工智能AI Agent 【免费下载链接】crm Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM. 项目地址: https://gitcode.com/gh_mirrors/crm48/crm 点击查看 免费下载 本文以仓库内 .agents/skills/better-ui/icons.md 设…

2026/9/24 16:56:04 阅读更多 →

最新新闻

剪辑气口视频节奏太拖沓?2026剪气口工作流,5款工具怎么选

剪辑气口视频节奏太拖沓?2026剪气口工作流,5款工具怎么选

剪辑气口视频时,手动找停顿、删空白往往占据口播后期大半时间。鲸剪(WhaleClip)是一款面向短视频创作者与团队的 AI 桌面剪辑工具,支持 Windows 与 macOS,其剪辑气口功能可基于音频波形与字幕时间轴自动识别并裁剪冗余…

2026/9/24 17:47:43 阅读更多 →
小白程序员快速入门大模型驾驭工程( Harness Engineering ),让你的AI助手稳定可靠地完成工作!

小白程序员快速入门大模型驾驭工程( Harness Engineering ),让你的AI助手稳定可靠地完成工作!

本文介绍了智能体(Agent)在实际工作中的应用挑战,提出了“智能体驾驭工程”(Harness Engineering)的概念,强调模型能力与大模型执行环境的重要性。文章详细阐述了Harness的四个层次:提示层、上下…

2026/9/24 17:47:43 阅读更多 →
第三章 用户记忆和知识库

第三章 用户记忆和知识库

一、用户记忆与知识库 一句话理解: 用户记忆和知识库都是Agent的外部持久化知识,用于突破单次会话和模型训练数据的限制。 1.1、用户记忆 用户记忆面相单个用户,保存具有长期价值的个性化信息: 用户偏好与习惯身份与背景信息历史…

2026/9/24 17:47:43 阅读更多 →
【Dify】基于LLM的多关卡人机互动游戏应用

【Dify】基于LLM的多关卡人机互动游戏应用

以人机互动为核心的智能游戏已成为AI学习和体验的重要窗口。围绕大型语言模型的博弈模式,通过设计关卡、设置变量、智能节点驱动,实现从对话到攻防的全流程互动。 本文介绍互动游戏应用的核心模型、流程节点与典型场景。通过模拟玩家与大模型的对抗体验,梳理实际操作方法,…

2026/9/24 17:47:43 阅读更多 →
C++ 仿muduo高并发服务器:Socket模块

C++ 仿muduo高并发服务器:Socket模块

C 仿muduo高并发服务器:Socket模块1. 为什么需要封装 Socket? Linux 原生 socket API 是一组 C 函数,直接使用存在以下问题: 参数复杂,容易出错(如 sockaddr 类型转换、字节序转换)。资源管理麻…

2026/9/24 17:47:43 阅读更多 →
第9章:提交项目到GitHub

第9章:提交项目到GitHub

第9章:提交项目到GitHub 本章目标 完整走一遍把项目提交到 GitHub 远程仓库的流程,包括 GitHub 特有的 Personal Access Token(PAT) 认证、代理配置、以及命令行 / TortoiseGit 两套操作。与 第8章:提交项目到Gitee 对…

2026/9/24 17:46:43 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →