Skip to content

feat(widget): 新增 1x1 桌面小组件入口,点击不经过 Activity - #109

Closed
xrh0905 wants to merge 5 commits into
0penPublic:mainfrom
xrh0905:widgets
Closed

xrh0905 wants to merge 5 commits into
0penPublic:mainfrom
xrh0905:widgets

Conversation

@xrh0905

@xrh0905 xrh0905 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

背景

桌面快捷方式(pinned shortcut)在平台层面必须是 Activity intent(LauncherApps.startShortcut → startActivity)。在 HyperOS 4 上,从桌面点开这种"瞬态 Activity"(ShortcutHandleActivity:透明、noHistory、发完广播立刻 finishAndRemoveTask())之后,系统会把紧接着的 com.android.nfc/.NfcRootActivity 启动判定为自启动并直接中止:

ActivityStarterImpl: home is launching mba.vm.onhit is playing now, so avoid app self start
ActivityTaskManager: hasBackToHome return START_ABORTED for com.android.nfc/.NfcRootActivity
START u0 {cmp=com.android.nfc/.NfcRootActivity …} … result code=102

而 NfcRootActivity 是 NFC 栈用来取得"可见窗口"的一步——只有它正常起来,后续目标 Activity 的启动才能通过后台启动校验(BAL_ALLOW_VISIBLE_WINDOW)。它被中止后,AAR 指定的目标应用不会被拉起,用户侧表现就是"点击有声(NFC 音效)但没有拉起应用"。

实测对照(同一标签、同一模块、同一 NFC 栈,仅触发方式不同):

触发方式 NfcRootActivity 结果
桌面快捷方式(瞬态 Activity) START_ABORTED (code=102) ❌ 只有音效
同一 intent、前台为普通 App(am start) code=0 ✅ 目标被拉起
纯广播触发(桌面在前台、零 Activity) code=0 ✅ 目标被拉起

方案:新增 1×1 桌面小组件入口,点击完全不经 Activity

  • TagWidgetProvider:RemoteViews + PendingIntent.getBroadcast,点击由宿主(launcher)直接发广播;
  • WidgetClickReceiver(非导出):读取配置的 NDEF 文件 → 复用 TagEmulatorManager.sendEmulateBroadcast() 发送 mba.vm.onhit.TAG_EMULATOR_REQUEST,与既有模块完全同路;
  • WidgetConfigActivity:添加小组件时选择文件,并复用"名称 / 图标"底栏;
  • TagEmulatorManager 由 Activity 改为 Context(供接收器调用,不改变既有调用方语义);
  • appwidget-provider:minWidth/minHeight = 40dp、targetCellWidth/Height = 1(1×1)。

顺带升级了名称/图标底栏(bottom_dialog_shortcut)并参数化:标题与名称提示可由调用方传入(小组件流程显示"创建小组件 / 名称",快捷方式流程保持原文案);圆形图标预览 + "+"角标、圆角输入框,中文/英文文案同步补齐。

Solve #108

@xrh0905
xrh0905 deployed to pull_requests_build October 16, 2026 16:02 — with GitHub Actions Active

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Configuration lifecycle, restoration, input validation, and resource-handling issues remain unresolved.

6 open issues
What changed in this PR

Adds a configurable 1×1 home-screen widget for tag emulation, addressing #108 without launching an Activity on click.

Changes:

  • Adds widget configuration, persistent settings, and broadcast-only click handling.
  • Allows TagEmulatorManager to accept a Context.
  • Refreshes the shared name/icon sheet and adds widget-specific translations.
File Description
app/​src/​main/​res/​xml/​tag_widget_info.xml Defines the 1×1 widget.
app/​src/​main/​res/​values/​strings.xml Adds English widget labels.
app/​src/​main/​res/​values-zh/​strings.xml Adds Chinese widget labels.
app/​src/​main/​res/​layout/​widget_tag.xml Defines widget icon and label layout.
app/​src/​main/​res/​layout/​bottom_dialog_shortcut.xml Refreshes name/icon controls.
app/​src/​main/​res/​drawable/​widget_tag_bg.xml Adds a widget background drawable.
app/​src/​main/​res/​drawable/​bg_icon_preview.xml Adds circular preview styling.
app/​src/​main/​res/​drawable/​bg_icon_badge.xml Adds icon badge styling.
app/​src/​main/​java/​mba/​vm/​onhit/​ui/​widget/​WidgetConfigActivity.kt Configures the file, name, and icon.
app/​src/​main/​java/​mba/​vm/​onhit/​ui/​widget/​WidgetClickReceiver.kt Handles broadcast-only emulation clicks.
app/​src/​main/​java/​mba/​vm/​onhit/​ui/​widget/​TagWidgetProvider.kt Stores configuration and renders widgets.
app/​src/​main/​java/​mba/​vm/​onhit/​ui/​manager/​TagEmulatorManager.kt Supports receiver contexts.
app/​src/​main/​java/​mba/​vm/​onhit/​ui/​dialog/​DialogHelper.kt Parameterizes sheet title and name hint.
app/​src/​main/​AndroidManifest.xml Registers widget components.

Review effort: Balanced

Comment thread app/src/main/java/mba/vm/onhit/ui/widget/TagWidgetProvider.kt
Comment thread app/src/main/java/mba/vm/onhit/ui/widget/WidgetClickReceiver.kt Outdated
Comment thread app/src/main/java/mba/vm/onhit/ui/widget/WidgetConfigActivity.kt Outdated
Comment thread app/src/main/java/mba/vm/onhit/ui/widget/WidgetConfigActivity.kt
Comment thread app/src/main/java/mba/vm/onhit/ui/widget/WidgetConfigActivity.kt Outdated
Comment thread app/src/main/java/mba/vm/onhit/ui/widget/WidgetConfigActivity.kt Outdated
@VictorModi
VictorModi deployed to pull_requests_build October 17, 2026 01:22 — with GitHub Actions Active
@VictorModi
VictorModi self-requested a review October 17, 2026 01:22
Comment thread app/src/main/java/mba/vm/onhit/ui/widget/WidgetClickReceiver.kt
xrh0905 and others added 4 commits October 17, 2026 10:30
Shortcuts are Activity intents by platform design. Launching the transient ShortcutHandleActivity from the launcher makes HyperOS abort the next com.android.nfc/.NfcRootActivity start ("home is launching ... is playing now, so avoid app self start" -> hasBackToHome, START_ABORTED), so the AAR target never gets a visible window and only the NFC sound is played.

A widget click is delivered by the host as a broadcast PendingIntent, so the NFC chain runs from a clean context and the target app launches normally.

- TagWidgetProvider + 1x1 RemoteViews layout + appwidget info
- WidgetConfigActivity reuses the shortcut name/icon bottom sheet
- WidgetClickReceiver reads the picked file and sends the existing TAG_EMULATOR_REQUEST broadcast (TagEmulatorManager is now Context-based)
- showShortcutBottomSheet() now accepts an optional title and name hint, so the
  widget flow says "Create Widget" / "创建小组件" while the shortcut flow keeps
  its existing wording
- circular icon preview with a "+" badge instead of a bare square image
- rounded input field; zh + en strings for the new labels
…ation

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@xrh0905
xrh0905 deployed to pull_requests_build October 17, 2026 03:01 — with GitHub Actions Active
Comment on lines +23 to +29
// Both packages are declared in xposed_scope; keep Samsung's NFC stack working.
val isNfcServiceProcess = NFC_SERVICE_PACKAGES.any {
lpparam.processName == it || lpparam.processName.startsWith("$it:")
}
LogUtils.setNfcServiceProcess(isNfcServiceProcess)
if (!isNfcServiceProcess) return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

原本这一段就是为了能够不用再维护一个NFC包名列表所以这里没有做限制,现在你又给限制加回来了?🤔

if (isNfcServiceProcess) {
action("[ onHit ] [ $currentCallerClassName ] [$tag] $msg", thr)
} else {
val formattedMessage = "[ $currentCallerClassName ] [$tag] $msg"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

既然如此为什么不把这个formattedMessage放在上面去给action调用而不是写两遍

@VictorModi

Copy link
Copy Markdown
Member

我MainHook这样写的初衷本就是为了避免OEM可能在以后更换NFC服务的包名导致其实hook点可以使用但因为代码里的NFC服务包名列表主动停止了hook,我在xposed_scope写出来只是因为xposed_scope是一个推荐列表而不是确定hook的目标,用户仍可在xposed管理器自行配置自己手机的NFC服务。

@VictorModi

Copy link
Copy Markdown
Member

如果真要做限制还真不如检测包名是否含有能nfc(

@xrh0905

xrh0905 commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

我重构一下(

This branch was successfully deployed

1 active deployment
pull_requests_build — 3dd78753 Deployed Oct 17, 2026 by xrh0905 via build #68
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants