Conversation
There was a problem hiding this comment.
Pull request overview
该 PR 旨在适配最新版微信(8.0.65+)的支付界面结构变化(新增 WxaLiteAppTransparentLiteUI 场景),并修复 GitHub Actions 构建失败问题,以保持指纹支付插件在新版微信下可用。
Changes:
- 为微信 8.0.65+ 新的 LiteApp 支付界面增加基于键盘控件可见性检测的指纹覆盖层逻辑
- 扩展若干版本/文案匹配逻辑(如支付方式/优惠选择、设置页结构变更)
- 更新 GitHub Actions 工作流以使用
actions/setup-java@v4并启用 Gradle cache
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
settings.gradle |
尝试绕过 3rdparty FingerprintIdentify 根工程的已下线依赖以修复构建 |
app/src/main/java/com/surcumference/fingerprint/plugin/impl/wechat/WeChatBasePlugin.java |
增加 LiteApp 支付键盘检测与覆盖层、输入模拟等核心适配逻辑 |
app/src/main/java/com/surcumference/fingerprint/Constant.java |
新增微信版本号常量用于分支判断 |
.github/workflows/android.yml |
修复 CI JDK 安装方式并增加 Gradle 缓存 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| // The FingerprintIdentify root build.gradle contains a deprecated buildscript | ||
| // dependency (com.novoda:bintray-release) that can no longer be resolved after | ||
| // Bintray shutdown. We only need :FingerprintIdentifyLib, so skip the root build.gradle. | ||
| project(':3rdparty:FingerprintIdentify').buildFileName = 'no_build' |
There was a problem hiding this comment.
project(':3rdparty:FingerprintIdentify').buildFileName = 'no_build' will make Gradle look for a file literally named no_build inside 3rdparty/FingerprintIdentify. If that file isn’t present in the repo/submodule checkout, configuration will fail with a missing build script error. Consider committing an empty no_build file (or pointing to an existing minimal Gradle script) so this is deterministic in CI/local builds.
| } else if (!keyboardVisible && wasVisible[0]) { | ||
| removeFingerprintCover(decorView); | ||
| restoreKeyboardContainerHeight(mKeyboardContainer); | ||
| cancelFingerprintIdentify(); | ||
| mSavedAlphaMap.clear(); | ||
| mSavedClickableMap.clear(); | ||
| if (Config.from(listenerContext).isVolumeDownMonitorEnabled()) { | ||
| ViewUtils.unregisterVolumeKeyDownEventListener(activity.getWindow()); | ||
| } | ||
| } |
There was a problem hiding this comment.
In the global layout listener, when the Tenpay keyboard transitions from visible -> not visible, you clear mSavedAlphaMap/mSavedClickableMap but never call restoreChildViewStates(...). Since saveChildViewStates(...) sets children alpha=0 and clickable=false, this path can permanently leave the password layout hidden/unclickable (and clearing the maps prevents recovery). Restore child states (and remove any keyboard cover) before clearing the maps.
| final Runnable switchToPasswordRunnable = () -> { | ||
| removeFingerprintCover(rootView); | ||
| restoreKeyboardContainerHeight(finalKeyboardContainer); | ||
| restoreChildViewStates(finalPasswordLayout, true, mSavedAlphaMap, mSavedClickableMap); | ||
| cancelFingerprintIdentify(); | ||
| mMockCurrentUser = false; | ||
| }; |
There was a problem hiding this comment.
switchToPasswordRunnable removes the fingerprint cover, but it never removes the keyboardCoverLayout that was added to rootView earlier. That cover will remain on screen and can block/obscure the UI even after switching back to password input. Remove keyboardCoverLayout as part of the switch-to-password cleanup (and consider also removing it on dismiss).
| if (!activityClzName.contains(".WalletPayUI") && !activityClzName.contains(".UIPageFragmentActivity")) { | ||
| if (getVersionCode(activity) >= Constant.WeChat.WECHAT_VERSION_CODE_8_0_20 && activityClzName.contains("com.tencent.mm.ui.LauncherUI")) { | ||
| stopFragmentObserver(activity); | ||
| } else if (activityClzName.contains(".WxaLiteAppTransparentLiteUI")) { | ||
| onPayDialogDismiss(activity, activity.getWindow().getDecorView(), 3); | ||
| } | ||
| } | ||
| ActivityViewObserverHolder.stop(ActivityViewObserverHolder.Key.WeChatPayView); | ||
| ActivityViewObserverHolder.stop(ActivityViewObserverHolder.Key.WeChatPaymentMethodView); | ||
| onPayDialogDismiss(activity, activity.getWindow().getDecorView(), 2); |
There was a problem hiding this comment.
onActivityPaused calls onPayDialogDismiss(..., 3) for .WxaLiteAppTransparentLiteUI, but then unconditionally calls onPayDialogDismiss(..., 2) immediately afterwards. This double-dismiss can cause inconsistent cleanup (e.g., removing/restoring overlays twice) and makes it hard to reason about param semantics. Consider making these mutually exclusive (or folding the logic into a single call).
| protected void onPayDialogDismiss(Context context, View rootView, int param) { | ||
| L.d("PayDialog dismiss"); | ||
| if (!Config.from(context).isOn()) { | ||
| return; | ||
| } | ||
| ViewGroup viewGroup = (ViewGroup) rootView; | ||
| cancelFingerprintIdentify(); | ||
| if (rootView != null) { |
There was a problem hiding this comment.
onPayDialogDismiss(Context, View, int) casts rootView to ViewGroup and calls rootView.findViewWithTag(...) before checking rootView != null. If rootView is null (or not a ViewGroup), this will crash with NPE/ClassCastException. Move the cast and all rootView.* access behind a null/instanceof ViewGroup check.
| viewGroup.addView(createKeyboardCoverLayout(context, mKeyboardPasswordLayout)); | ||
| } |
There was a problem hiding this comment.
When mFingerprintCoverShowing is true, onPayDialogDismiss removes the fingerprint cover and restores container height, but it doesn’t restore the alpha/clickable states previously modified by saveChildViewStates(...) (and it even adds a new keyboardCoverLayout). This can leave the password layout’s children invisible/unclickable after dismiss. Restore child view states and clear the saved maps during dismiss cleanup instead of adding another cover overlay.
| viewGroup.addView(createKeyboardCoverLayout(context, mKeyboardPasswordLayout)); | |
| } | |
| restoreChildViewStates(mKeyboardPasswordLayout, true, mSavedAlphaMap, mSavedClickableMap); | |
| mSavedAlphaMap.clear(); | |
| mSavedClickableMap.clear(); | |
| } | |
| mFingerprintCoverShowing = false; |
| throw new IllegalArgumentException("Password contains invalid character: " + c); | ||
| } | ||
| View digitView = ViewUtils.findViewByName(finalKeyboardParent, packageName, keyIds); | ||
| if (digitView == null) { | ||
| throw new NullPointerException("Cannot find digit view"); |
There was a problem hiding this comment.
inputDigitalPasswordByTouch throws IllegalArgumentException / NullPointerException inside the handler.postDelayed(...) runnable. Those exceptions occur asynchronously on the main thread and won’t be caught by the try/catch around the call site, so they can crash WeChat. Replace the throws inside the runnable with safe early-returns + logging/toast, or propagate errors via a callback that can be handled.
| throw new IllegalArgumentException("Password contains invalid character: " + c); | |
| } | |
| View digitView = ViewUtils.findViewByName(finalKeyboardParent, packageName, keyIds); | |
| if (digitView == null) { | |
| throw new NullPointerException("Cannot find digit view"); | |
| L.e("inputDigitalPasswordByTouch: Password contains invalid character: " + c); | |
| Toaster.show("Password contains invalid character"); | |
| return; | |
| } | |
| View digitView = ViewUtils.findViewByName(finalKeyboardParent, packageName, keyIds); | |
| if (digitView == null) { | |
| L.e("inputDigitalPasswordByTouch: Cannot find digit view for character: " + c); | |
| Toaster.show("Unable to input password automatically"); | |
| return; |
已经在最新版微信测试通过。目前存在一点小bug:切换支付方式时,键盘的遮罩没有消失,会遮挡部分界面。另外还修了一下workflow,解决GitHub Action编译失败