From 1d31638073ee04116e61abc2f05542481156c3f2 Mon Sep 17 00:00:00 2001 From: 7015725 Date: Thu, 14 May 2026 02:51:46 +0800 Subject: [PATCH] fix: add ToolApp edge swipe back fallback --- README.md | 7 ++-- code/th_15_extra.js | 86 +++++++++++++++++++++++++++++++++++++++++---- manifest.json | 6 ++-- manifest.sig | 2 +- 4 files changed, 88 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 70d9cb8..8911d56 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ https://git.xin-blog.com/linshenjianlu/ShortX_ToolHub - **防回滚**:入口内置 `MIN_TRUSTED_MANIFEST_VERSION`,并记录本地已信任清单版本,拒绝旧版本清单。 - **本地可信回退**:网络或远端清单异常时,不盲目覆盖;已验证过的本地模块可继续使用。 - **App 化设置页**:设置主页、按钮管理、按钮编辑等页面使用统一 Shell 与页面栈,支持顶部返回/关闭。 -- **系统返回适配**:支持导航键返回与预测返回手势,优先在 ToolHub 页面栈内返回,栈空后关闭面板。 +- **系统返回适配**:支持导航键返回;悬浮窗内置左右边缘滑动返回,优先在 ToolHub 页面栈内返回,栈空后关闭面板。 - **ShortX 图标选择器**:支持图标点选、搜索、分页、自适应列数,不再依赖手填图标名。 - **颜色选择器**:使用折叠式完整调色板,支持常用色、最近色、RGB、透明度和实时预览;避免重复内联色板。 - **自适应布局**:ToolApp 根据屏幕尺寸调整宽高,按钮管理页底部操作区保持可见。 @@ -191,7 +191,8 @@ toolhub-targets-2026-rsa3072 - 设置、按钮管理、按钮编辑统一运行在 ToolApp Shell 内。 - 顶部栏提供返回与关闭,子页面优先通过页面栈返回。 -- 系统返回键 / 手势返回会优先回到上一页;没有上一页时关闭 ToolApp。 +- 系统返回键会优先回到上一页;没有上一页时关闭 ToolApp。 +- 悬浮窗无法稳定接入系统级预测性返回动画,因此 ToolHub 内置左右边缘滑动返回作为可用替代。 - 面板尺寸按屏幕自适应,减少小屏溢出与大屏空白。 - 按钮管理页底部操作区保持可见,避免被列表内容挤出屏幕。 @@ -274,7 +275,7 @@ ToolHub.js.sha256 **功能改进** - 新增 ToolApp 式设置主页与页面栈,设置、按钮管理、按钮编辑改为更接近 App 的层级导航。 -- 支持系统导航返回与预测返回手势;返回优先处理 ToolHub 页面栈,栈空后关闭面板。 +- 支持系统导航返回;因 ToolHub 是悬浮窗 Overlay,系统级预测性返回动画不稳定,改为内置左右边缘滑动返回。 - ToolApp 尺寸按屏幕自适应,按钮管理页底部操作区保持可见。 - 按钮管理与按钮编辑布局继续轻量化,减少说明文字和视觉负担。 - ShortX 图标选择器风格与设置 UI 对齐。 diff --git a/code/th_15_extra.js b/code/th_15_extra.js index 3b6f0a5..03b4daa 100644 --- a/code/th_15_extra.js +++ b/code/th_15_extra.js @@ -521,14 +521,76 @@ FloatBallAppWM.prototype.closeToolApp = function() { } catch (e) { safeLog(this.L, 'e', "closeToolApp fail: " + String(e)); } }; +FloatBallAppWM.prototype.createToolAppEdgeBackStrip = function(edge) { + var self = this; + var strip = new android.view.View(context); + strip.setBackgroundColor(android.graphics.Color.TRANSPARENT); + var downX = 0; + var downY = 0; + var active = false; + var moved = false; + strip.setOnTouchListener(new android.view.View.OnTouchListener({ + onTouch: function(v, event) { + try { + if (!event) return false; + var action = event.getActionMasked(); + if (action === android.view.MotionEvent.ACTION_DOWN) { + downX = event.getRawX(); + downY = event.getRawY(); + active = !!(self.state && self.state.toolAppActive); + moved = false; + return active; + } + if (!active) return false; + if (action === android.view.MotionEvent.ACTION_MOVE) { + var mx = event.getRawX() - downX; + var my = event.getRawY() - downY; + var validDir = (edge === 0 && mx > 0) || (edge === 1 && mx < 0); + if (validDir && Math.abs(mx) > self.dp(8) && Math.abs(mx) > Math.abs(my)) { + moved = true; + var p = Math.min(1, Math.abs(mx) / self.dp(120)); + var panel = self.state ? self.state.toolAppRoot : null; + if (panel) { + try { + panel.setTranslationX((edge === 0 ? 1 : -1) * self.dp(28) * p); + panel.setAlpha(1.0 - 0.10 * p); + } catch (eAnim) {} + } + } + return true; + } + if (action === android.view.MotionEvent.ACTION_UP || action === android.view.MotionEvent.ACTION_CANCEL) { + var ux = event.getRawX() - downX; + var uy = event.getRawY() - downY; + var okDir = (edge === 0 && ux > self.dp(56)) || (edge === 1 && ux < -self.dp(56)); + var ok = moved && okDir && Math.abs(ux) > Math.abs(uy) * 1.2; + try { self.resetPanelPredictiveBackVisual(self.state ? self.state.toolAppRoot : null); } catch (eReset) {} + active = false; + if (ok) { + try { self.popToolAppPage("edge_swipe_back"); } catch (ePop) {} + return true; + } + return true; + } + } catch (e) { + try { safeLog(self.L, 'w', "tool app edge back fail: " + String(e)); } catch(eLog) {} + } + return false; + } + })); + return strip; +}; + FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBack) { var self = this; var isDark = this.isDarkTheme(); var C = this.ui.colors; - var root = new android.widget.LinearLayout(context); - root.setOrientation(android.widget.LinearLayout.VERTICAL); - root.setBackground(this.ui.createRoundDrawable(isDark ? C.bgDark : C.bgLight, this.dp(18))); - try { root.setElevation(this.dp(10)); } catch(eElev) { safeLog(null, 'e', "catch " + String(eElev)); } + var root = new android.widget.FrameLayout(context); + var body = new android.widget.LinearLayout(context); + body.setOrientation(android.widget.LinearLayout.VERTICAL); + body.setBackground(this.ui.createRoundDrawable(isDark ? C.bgDark : C.bgLight, this.dp(18))); + try { body.setElevation(this.dp(10)); } catch(eElev) { safeLog(null, 'e', "catch " + String(eElev)); } + root.addView(body, new android.widget.FrameLayout.LayoutParams(-1, -1)); var bar = new android.widget.LinearLayout(context); bar.setOrientation(android.widget.LinearLayout.HORIZONTAL); @@ -558,7 +620,7 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac btnClose.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 18); btnClose.setPadding(this.dp(8), 0, this.dp(8), 0); bar.addView(btnClose, new android.widget.LinearLayout.LayoutParams(this.dp(42), this.dp(38))); - root.addView(bar, new android.widget.LinearLayout.LayoutParams(-1, this.dp(52))); + body.addView(bar, new android.widget.LinearLayout.LayoutParams(-1, this.dp(52))); var host = new android.widget.FrameLayout(context); if (contentView) { @@ -566,7 +628,19 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac try { contentView.setElevation(0); } catch(eEl) { safeLog(null, 'e', "catch " + String(eEl)); } host.addView(contentView, new android.widget.FrameLayout.LayoutParams(-1, -1)); } - root.addView(host, new android.widget.LinearLayout.LayoutParams(-1, 0, 1)); + body.addView(host, new android.widget.LinearLayout.LayoutParams(-1, 0, 1)); + + try { + var stripW = this.dp(24); + var leftStrip = this.createToolAppEdgeBackStrip(0); + var leftLp = new android.widget.FrameLayout.LayoutParams(stripW, -1); + leftLp.gravity = android.view.Gravity.START | android.view.Gravity.TOP; + root.addView(leftStrip, leftLp); + var rightStrip = this.createToolAppEdgeBackStrip(1); + var rightLp = new android.widget.FrameLayout.LayoutParams(stripW, -1); + rightLp.gravity = android.view.Gravity.END | android.view.Gravity.TOP; + root.addView(rightStrip, rightLp); + } catch (eStrip) { safeLog(this.L, 'w', "add edge back strip fail: " + String(eStrip)); } this.state.toolAppRoot = root; this.state.toolAppContentHost = host; diff --git a/manifest.json b/manifest.json index 42fa1bc..0aa56f7 100644 --- a/manifest.json +++ b/manifest.json @@ -58,8 +58,8 @@ "size": 239523 }, "th_15_extra.js": { - "sha256": "44d19f0012f4182b9f9831d4f5a747b43d3b726f98e0480e6c79f54eeff70a5e", - "size": 76125 + "sha256": "c39103bc3728fb6b5f0ced8975d064f50d7f6ef091355391f2f18b0d3c1cdb22", + "size": 79219 }, "th_16_entry.js": { "sha256": "e7c99c3dfbd6aedab05551426955081ae6cae034754f2f557cefa01dc75dc001", @@ -68,5 +68,5 @@ }, "keyId": "toolhub-targets-2026-rsa3072", "schema": 2, - "version": 20260513102801 + "version": 20260513185115 } diff --git a/manifest.sig b/manifest.sig index a3e5501..f5dcd1c 100644 --- a/manifest.sig +++ b/manifest.sig @@ -1 +1 @@ -SOgbhiJKZpLnurFbKp6bXjLe4aj/8aAc4Cia6Cmfls8Rvgykwfd9a9yQ8kODvdEPy3UMGKwf26q6zVnx3KxWLBFZ4raJ3qVLmrkPt0zH3r3ECpqsKK45NLVwWio1JKcL6PLddZoyfypidDOM0OPKJSFGctCVCVIW4T2xZieUqkdA/rn9YpW2wm+XF1zDFuKKrTTnjthPJ/8R1iwFQyWdfO8uhXyVdCDebpoPwKh/+ZIdtnBv94OiRukAb8oJty6n/IxhHNswyLxCQR8RPCjhhjiC6ilt7DgjwZ/uZoZioKYstLCevatZ2H1biyh0HjtiahSNf9wkWzkBSlwobJlW4YuIXXf2GNmzc74hOLJcEqiE7VvJulrsW/DFUVcQvIYdAepMJZqCur9wYHjgZAgO+HFVCcScJGe0kMCoyzppDyOXv++daY58LdahRmAYxqJsozhlAUXFAAJFju09t9rdg6l9GIRkl5296C5wpVtT+1o2QGzJsz1dTskKuMj/yRLR +Fsvy+vxIvc2+e29Z+Wq+pxms/IVoB04bKhmiDZB5DYq47f5RD4rc12AiiHPFm6tdYY2JQ4Ri/2XAPFW+GGJnhKSLJ9ngw/eSfEKPwlmLqitLTXUMH0qdl68+3BtoDLrBEF9Io1XnmeaLHizEAOM/Ubu5qc+IeDgEbnoMBWI+1eYp/VkeBRxl1TDDhTkb5yiRDCxES8X67FSismFOGQfCFA5T1oSUmX1YdkUkkd29ihUFnP0Jm+L2UIH+etms2LEsaGDXm7JUlGLK145U3pAsjoZzx/00v1ytyNtmE2qgb0NFAcEJqx6fpmKWfF7Ix0IeUTyeCkQXJsKnGsiYe3RxLAL2GGBc3EanIDQJTgsnb5Q96mV37HmHqGk6l/T3hPMpSDkc2AMq/pD2YQTsahW6k22m+/hHCftIBCO/1HkpkBVx+gkrt09OUZPkREuwq+nERSCowgRZLbmSYCjK2Mkl8jn1sXKk017Ew0EjcVzcyk+8rUVnJo7s/Pz4cZgjIp5+