【TaskbarDelegate】屏蔽上滑返回桌面手势功能

一、需求描述

基于Android 14 平台,在应用界面中默认使用上滑手势会返回桌面,某些应用要求不能返回到桌面,需要屏蔽上滑手势。

二、需求分析

我们知道手势导航的底部导航横线的逻辑是在 SystemUI 的 NavigationBarView -> NavigationBar -> NavigationBarController -> TaskbarDelegate 等相关的类控制,在手势导航中上滑手势有 2 种功能:

  • 从屏幕底部向上滑动,即可进入主屏幕
  • 从底部向上滑动、按住再松开,可切换应用

如果想要屏蔽,可以直接返回消费掉 InputEvent 事件,就不会走后续的流程了,我尝试在NavigationBarViewonInterceptTouchEvent 返回 true,但是只屏蔽了返回Home的功能,最近任务还是会启动。在 TaskbarDelegate 中有设置相关的 sys ui flag 的相关逻辑,于是我们可以参考 【EdgeBackGesture】屏蔽屏幕边缘返回手势,实现类似的屏蔽效果。

  1. TaskbarDelegate 初始化时添加需要屏蔽上滑功能的 Activity 合集
  2. 创建一个监听器 TaskStackChangeListener,用于监听前台应用是否需要屏蔽,并注册监听
  3. 任务栈变化时候改变 SysuiFlag,如 SYSUI_STATE_OVERVIEW_DISABLEDSYSUI_STATE_HOME_DISABLED

这个方案的优点是,我们不需要去追踪复杂的输入事件流,而是直接通过官方提供的、系统级的状态标志来声明我们的意图。InputDispatcher(负责分发所有触摸事件的底层服务)会尊重这些标志,并从源头上就阻止手势的发生。

三、解决方案

SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java

1.添加需要屏蔽上滑功能的 Activity 合集

privatefinalList<ComponentName> mHomeBlockingActivities =newArrayList<>();publicTaskbarDelegate(Context context,LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,StatusBarKeyguardViewManager statusBarKeyguardViewManager){ mLightBarTransitionsControllerFactory = lightBarTransitionsControllerFactory; mContext = context; mDisplayManager = mContext.getSystemService(DisplayManager.class); mPipListener =(bounds)->{ mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds);}; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mStatusBarKeyguardViewManager.setTaskbarDelegate(this);// 添加要屏蔽的 ActivityString[] disabledActivities = mContext.getResources().getStringArray(R.array.config_blockedHomeGestureActivities);for(String disabledActivity : disabledActivities){ mHomeBlockingActivities.add(ComponentName.unflattenFromString(disabledActivity));}}

在新建的 config_blockedHomeGestureActivities 数组中添加需要屏蔽类名即可

<string-arrayname="config_blockedHomeGestureActivities"><item>com.android.deskclock/.DeskClock</item></string-array>

2.注册并监听任务栈变化

TaskbarDelegate 原本逻辑中就包含了 TaskStackChangeListener 的监听,但是没有 onTaskStackChanged 的重写,只需要加上即可

privateboolean mIsHomeGestureBlocked;privatefinalTaskStackChangeListener mTaskStackListener =newTaskStackChangeListener(){@OverridepublicvoidonLockTaskModeChanged(int mode){ mSysUiState.setFlag(SYSUI_STATE_SCREEN_PINNING, mode == LOCK_TASK_MODE_PINNED).commitUpdate(mDisplayId);}@OverridepublicvoidonTaskStackChanged(){updateHomeGestureBlockState();}};privatevoidupdateHomeGestureBlockState(){boolean shouldBlock =false;ActivityManager.RunningTaskInfo runningTask =ActivityManagerWrapper.getInstance().getRunningTask();ComponentName topActivity = runningTask ==null?null: runningTask.topActivity; shouldBlock = topActivity !=null&& mHomeBlockingActivities.contains(topActivity);if(mIsHomeGestureBlocked != shouldBlock){ mIsHomeGestureBlocked = shouldBlock;updateSysuiFlags();}}

3.更新标志位 updateSysuiFlags

privatevoidupdateSysuiFlags(){android.util.Log.e("maxx","updateSysuiFlags");int a11yFlags = mNavBarHelper.getA11yButtonState();boolean clickable =(a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE)!=0;boolean longClickable =(a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE)!=0;//新增修改:boolean overviewDisabled =(mDisabledFlags &View.STATUS_BAR_DISABLE_RECENT)!=0|| mIsHomeGestureBlocked;boolean homeDisabled =(mDisabledFlags &View.STATUS_BAR_DISABLE_HOME)!=0|| mIsHomeGestureBlocked;//*/ mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable).setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable).setFlag(SYSUI_STATE_IME_SHOWING,(mNavigationIconHints & NAVIGATION_HINT_BACK_ALT)!=0).setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,(mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN)!=0)//新增修改:.setFlag(SYSUI_STATE_OVERVIEW_DISABLED, overviewDisabled /*(mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0*/).setFlag(SYSUI_STATE_HOME_DISABLED, homeDisabled /*(mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0*/)//*/.setFlag(SYSUI_STATE_BACK_DISABLED,(mDisabledFlags &View.STATUS_BAR_DISABLE_BACK)!=0).setFlag(SYSUI_STATE_NAV_BAR_HIDDEN,!isWindowVisible()).setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,allowSystemGestureIgnoringBarVisibility()).setFlag(SYSUI_STATE_IMMERSIVE_MODE,isImmersiveMode()).commitUpdate(mDisplayId);}

还有一种思路是在 Launcher 做屏蔽处理,例如在 Launcher3 的源码中处理,在 TouchInteractionServiceonInputEvent 方法中直接返回即可。

Launcher3/quickstep/src/com/android/quickstep/TouchInteractionService.java publicvoidonInputEvent(InputEvent ev){if(needInterceptHome()){return;}...}

Read more

本地化部署方案:GraphRAG+LangChain+Ollama 驱动 LLaMa 3.1 集成 Neo4j 实战

本地化部署方案:GraphRAG+LangChain+Ollama 驱动 LLaMa 3.1 集成 Neo4j 实战

本文将带您从零开始,用不到50行核心代码实现基于本地大模型 LLaMa 3.1 的 GraphRAG 应用开发。我们将整合 LangChain 工作流、Ollama 模型管理工具与 Neo4j 图数据库,构建一套支持实体关系挖掘与混合检索的增强生成系统,全程无需依赖云端 API,兼顾数据安全与开发效率。 一、先搞懂核心概念:什么是 GraphRAG? 传统 RAG(检索增强生成)依赖向量数据库的语义相似度匹配,容易丢失实体间的关联信息。而 GraphRAG(图检索增强生成) 则通过"节点-关系"的图结构建模数据,将分散的文本块转化为结构化知识网络,让 LLM 能基于实体关联进行推理,输出更具逻辑性的答案。 其核心价值在于: * 结构化上下文:将"蒂姆·库克""苹果公司&

AI 编程新王 Codex 全面上手指南

AI 编程新王 Codex 全面上手指南 一篇文章带你精通 Codex 四大环境 + 免费使用方法 💡 前言:AI 编程的新时代 AI 编程的竞争正进入“第二轮洗牌期”。 过去几个月,Claude Code 一度成为开发者的宠儿,但频繁的限速、封号、降智问题让不少人头疼。 如今,OpenAI 推出的 Codex 迅速崛起,凭借强大的编程能力和超高性价比,成为“AI 编程新王”。 Codex 是什么? 它是基于 GPT-5 模型打造的专用编程环境,支持命令行、VS Code 插件、SDK 集成、云端操作等多种运行模式。 不论你是写脚本、做项目、还是维护仓库,Codex 都能像“AI 结对程序员”一样协助你高效开发。

使用Llama-Factory微调教育领域解题模型的效果评测

使用Llama-Factory微调教育领域解题模型的效果评测 在当前AI驱动的教育变革浪潮中,一个现实问题日益凸显:尽管通用大语言模型如Qwen、LLaMA等在开放对话和常识推理上表现惊艳,但当学生问出“请用初中方法解这个方程”时,模型却常常跳步、漏单位,甚至给出不符合教学规范的答案。这背后反映的是专业性与泛化能力之间的鸿沟——而填补这一鸿沟的关键,正是领域微调。 我们最近在一个中学智能辅导项目中尝试了多种微调方案,最终将目光锁定在 Llama-Factory 上。它不仅让我们用一张RTX 3090就在48小时内完成了对Qwen-7B的数学解题能力定制,更重要的是,它的模块化设计让整个过程变得可复现、可迭代。下面,我将结合实战经验,深入拆解这套框架如何真正解决教育场景下的模型适配难题。 从“跑不通”到“跑得快”:为什么选择Llama-Factory? 早前我们试过手写PyTorch训练脚本做LoRA微调,结果光是环境配置就耗掉三天——HuggingFace Transformers版本不兼容、Peft库加载失败、显存OOM频发……更别说还要自己写数据预处理逻辑和评估代码。对于