fix: prefer ToolApp surface back gesture

This commit is contained in:
7015725
2026-05-22 12:41:05 +08:00
parent 736212fd80
commit a9db5faf1b
4 changed files with 66 additions and 25 deletions

View File

@@ -105,6 +105,7 @@ var ConfigValidator = {
ENABLE_TOOLAPP_INNER_BACK_STRIPS: { type: "bool", default: false }, ENABLE_TOOLAPP_INNER_BACK_STRIPS: { type: "bool", default: false },
ENABLE_TOOLAPP_SCREEN_BACK_STRIPS: { type: "bool", default: true }, ENABLE_TOOLAPP_SCREEN_BACK_STRIPS: { type: "bool", default: true },
TOOLAPP_BACK_COMMIT_DISTANCE_DP: { type: "int", min: 1, max: 480, default: 36 }, TOOLAPP_BACK_COMMIT_DISTANCE_DP: { type: "int", min: 1, max: 480, default: 36 },
TOOLAPP_BACK_SURFACE_SLOP_DP: { type: "int", min: 8, max: 96, default: 24 },
TOOLAPP_BACK_PROGRESS_DISTANCE_DP: { type: "int", min: 1, max: 720, default: 96 }, TOOLAPP_BACK_PROGRESS_DISTANCE_DP: { type: "int", min: 1, max: 720, default: 96 },
// 功能开关 // 功能开关
@@ -742,6 +743,7 @@ var ConfigManager = {
ENABLE_TOOLAPP_INNER_BACK_STRIPS: false, ENABLE_TOOLAPP_INNER_BACK_STRIPS: false,
ENABLE_TOOLAPP_SCREEN_BACK_STRIPS: true, ENABLE_TOOLAPP_SCREEN_BACK_STRIPS: true,
TOOLAPP_BACK_COMMIT_DISTANCE_DP: 36, TOOLAPP_BACK_COMMIT_DISTANCE_DP: 36,
TOOLAPP_BACK_SURFACE_SLOP_DP: 24,
TOOLAPP_BACK_PROGRESS_DISTANCE_DP: 96, TOOLAPP_BACK_PROGRESS_DISTANCE_DP: 96,
ENABLE_BOUNCE: true, ENABLE_BOUNCE: true,
BOUNCE_TIMES: 2, BOUNCE_TIMES: 2,
@@ -854,13 +856,14 @@ var ConfigManager = {
{ key: "CLICK_SLOP_DP", name: "点击位移阈值(dp)", type: "int", min: 1, max: 40, step: 1 }, { key: "CLICK_SLOP_DP", name: "点击位移阈值(dp)", type: "int", min: 1, max: 40, step: 1 },
{ key: "TOOLAPP_BACK_GESTURE_MODE", name: "设置页滑动返回模式", type: "single_choice", options: [ { key: "TOOLAPP_BACK_GESTURE_MODE", name: "设置页滑动返回模式", type: "single_choice", options: [
{ label: "全表面横滑", value: "surface" }, { label: "全表面横滑", value: "surface" },
{ label: "仅左右边缘", value: "edge" }, { label: "仅面板内部左右边缘", value: "edge" },
{ label: "关闭", value: "off" } { label: "关闭", value: "off" }
]}, ]},
{ key: "TOOLAPP_BACK_EDGE_WIDTH_DP", name: "边缘模式起手宽度", type: "int", min: 1, max: 120, step: 1 }, { key: "TOOLAPP_BACK_EDGE_WIDTH_DP", name: "边缘模式起手宽度", type: "int", min: 1, max: 120, step: 1 },
{ key: "ENABLE_TOOLAPP_INNER_BACK_STRIPS", name: "启用旧版页面覆盖热区(不推荐)", type: "bool" }, { key: "ENABLE_TOOLAPP_INNER_BACK_STRIPS", name: "启用旧版页面覆盖热区(不推荐)", type: "bool" },
{ key: "ENABLE_TOOLAPP_SCREEN_BACK_STRIPS", name: "启用屏幕空白区返回", type: "bool" }, { key: "ENABLE_TOOLAPP_SCREEN_BACK_STRIPS", name: "启用屏幕空白区返回", type: "bool" },
{ key: "TOOLAPP_BACK_COMMIT_DISTANCE_DP", name: "设置页返回触发距离", type: "int", min: 1, max: 480, step: 1 }, { key: "TOOLAPP_BACK_COMMIT_DISTANCE_DP", name: "设置页返回触发距离", type: "int", min: 1, max: 480, step: 1 },
{ key: "TOOLAPP_BACK_SURFACE_SLOP_DP", name: "表面横滑起手阈值", type: "int", min: 8, max: 96, step: 1 },
{ key: "TOOLAPP_BACK_PROGRESS_DISTANCE_DP", name: "设置页返回动画距离", type: "int", min: 1, max: 720, step: 1 }, { key: "TOOLAPP_BACK_PROGRESS_DISTANCE_DP", name: "设置页返回动画距离", type: "int", min: 1, max: 720, step: 1 },
{ key: "ENABLE_LONG_PRESS", name: "启用长按", type: "bool" }, { key: "ENABLE_LONG_PRESS", name: "启用长按", type: "bool" },
{ key: "LONG_PRESS_MS", name: "长按判定(ms)", type: "int", min: 200, max: 2000, step: 10 }, { key: "LONG_PRESS_MS", name: "长按判定(ms)", type: "int", min: 200, max: 2000, step: 10 },
@@ -899,7 +902,7 @@ var ConfigManager = {
var needReset = false; var needReset = false;
if (s) { if (s) {
var sStr = JSON.stringify(s); var sStr = JSON.stringify(s);
if (sStr.indexOf("ENABLE_SNAP_TO_EDGE") < 0 || sStr.indexOf("ENABLE_ANIMATIONS") < 0 || sStr.indexOf("BALL_IDLE_ALPHA") < 0 || sStr.indexOf("PANEL_POS_GRAVITY") < 0 || sStr.indexOf("single_choice") < 0 || sStr.indexOf("ball_shortx_icon") < 0 || sStr.indexOf("ball_color") < 0 || sStr.indexOf("SETTINGS_THEME") < 0 || sStr.indexOf("BALL_BG_COLOR_HEX") < 0 || sStr.indexOf("BALL_ICON_SIZE_DP") < 0 || sStr.indexOf("TOOLAPP_BACK_GESTURE_MODE") < 0 || sStr.indexOf("TOOLAPP_BACK_EDGE_WIDTH_DP") < 0 || sStr.indexOf("ENABLE_TOOLAPP_INNER_BACK_STRIPS") < 0 || sStr.indexOf("ENABLE_TOOLAPP_SCREEN_BACK_STRIPS") < 0 || sStr.indexOf("TOOLAPP_BACK_COMMIT_DISTANCE_DP") < 0 || sStr.indexOf("TOOLAPP_BACK_PROGRESS_DISTANCE_DP") < 0 || sStr.indexOf("LONG_PRESS_TRIGGERED_MOVE_SLOP_DP") < 0) { if (sStr.indexOf("ENABLE_SNAP_TO_EDGE") < 0 || sStr.indexOf("ENABLE_ANIMATIONS") < 0 || sStr.indexOf("BALL_IDLE_ALPHA") < 0 || sStr.indexOf("PANEL_POS_GRAVITY") < 0 || sStr.indexOf("single_choice") < 0 || sStr.indexOf("ball_shortx_icon") < 0 || sStr.indexOf("ball_color") < 0 || sStr.indexOf("SETTINGS_THEME") < 0 || sStr.indexOf("BALL_BG_COLOR_HEX") < 0 || sStr.indexOf("BALL_ICON_SIZE_DP") < 0 || sStr.indexOf("TOOLAPP_BACK_GESTURE_MODE") < 0 || sStr.indexOf("TOOLAPP_BACK_EDGE_WIDTH_DP") < 0 || sStr.indexOf("ENABLE_TOOLAPP_INNER_BACK_STRIPS") < 0 || sStr.indexOf("ENABLE_TOOLAPP_SCREEN_BACK_STRIPS") < 0 || sStr.indexOf("TOOLAPP_BACK_COMMIT_DISTANCE_DP") < 0 || sStr.indexOf("TOOLAPP_BACK_SURFACE_SLOP_DP") < 0 || sStr.indexOf("TOOLAPP_BACK_PROGRESS_DISTANCE_DP") < 0 || sStr.indexOf("LONG_PRESS_TRIGGERED_MOVE_SLOP_DP") < 0) {
needReset = true; needReset = true;
} }
@@ -935,6 +938,7 @@ var ConfigManager = {
schemaItemDiffers("ENABLE_TOOLAPP_INNER_BACK_STRIPS", ["name", "type"]) || schemaItemDiffers("ENABLE_TOOLAPP_INNER_BACK_STRIPS", ["name", "type"]) ||
schemaItemDiffers("ENABLE_TOOLAPP_SCREEN_BACK_STRIPS", ["name", "type"]) || schemaItemDiffers("ENABLE_TOOLAPP_SCREEN_BACK_STRIPS", ["name", "type"]) ||
schemaItemDiffers("TOOLAPP_BACK_COMMIT_DISTANCE_DP", ["name", "type", "min", "max", "step"]) || schemaItemDiffers("TOOLAPP_BACK_COMMIT_DISTANCE_DP", ["name", "type", "min", "max", "step"]) ||
schemaItemDiffers("TOOLAPP_BACK_SURFACE_SLOP_DP", ["name", "type", "min", "max", "step"]) ||
schemaItemDiffers("TOOLAPP_BACK_PROGRESS_DISTANCE_DP", ["name", "type", "min", "max", "step"]) || schemaItemDiffers("TOOLAPP_BACK_PROGRESS_DISTANCE_DP", ["name", "type", "min", "max", "step"]) ||
schemaItemDiffers("LONG_PRESS_TRIGGERED_MOVE_SLOP_DP", ["name", "type", "min", "max", "step"])) { schemaItemDiffers("LONG_PRESS_TRIGGERED_MOVE_SLOP_DP", ["name", "type", "min", "max", "step"])) {
needReset = true; needReset = true;

View File

@@ -1312,22 +1312,52 @@ FloatBallAppWM.prototype.getToolAppBackGestureMode = function() {
var mode = "surface"; var mode = "surface";
try { mode = String(this.config.TOOLAPP_BACK_GESTURE_MODE || "surface"); } catch(e) { mode = "surface"; } try { mode = String(this.config.TOOLAPP_BACK_GESTURE_MODE || "surface"); } catch(e) { mode = "surface"; }
if (mode !== "edge" && mode !== "surface" && mode !== "off") mode = "surface"; if (mode !== "edge" && mode !== "surface" && mode !== "off") mode = "surface";
// 全面屏手势下物理极致边缘容易被系统抢走;旧配置若停留在 edge运行期优先回退到 surface。
// edge 分支仍保留为 ToolApp 面板内部边缘模式;只有显式把 TOOLAPP_BACK_FORCE_SURFACE 设为 false 时才启用。
if (mode === "edge") {
var forceSurface = true;
try { if (this.config.TOOLAPP_BACK_FORCE_SURFACE === false || String(this.config.TOOLAPP_BACK_FORCE_SURFACE) === "false") forceSurface = false; } catch(eForce) {}
if (forceSurface) {
try { this.config.TOOLAPP_BACK_GESTURE_MODE = "surface"; } catch(eMig) {}
mode = "surface";
}
}
return mode; return mode;
}; };
FloatBallAppWM.prototype.isToolAppBackInteractiveView = function(v) { FloatBallAppWM.prototype.getToolAppBackSurfaceSlopPx = function(commitDistancePx) {
var slopDp = 24;
try { slopDp = Number(this.config.TOOLAPP_BACK_SURFACE_SLOP_DP || 24); } catch(e) { slopDp = 24; }
if (isNaN(slopDp)) slopDp = 24;
if (slopDp < 8) slopDp = 8;
if (slopDp > 96) slopDp = 96;
var px = this.dp(slopDp);
try {
var c = Number(commitDistancePx || 0);
if (!isNaN(c) && c > 0) px = Math.min(px, c);
} catch(e2) {}
return px;
};
FloatBallAppWM.prototype.isToolAppBackInteractiveView = function(v, dx, dy) {
try { try {
if (!v) return false; if (!v) return false;
try { if (v instanceof android.widget.SeekBar) return true; } catch(eSeek) {} var adx = Math.abs(Number(dx || 0));
try { if (v instanceof android.widget.CompoundButton) return true; } catch(eComp) {} var ady = Math.abs(Number(dy || 0));
try { if (v instanceof android.widget.Switch) return true; } catch(eSw) {} var strongHorizontal = adx > 0 && adx >= ady;
try { if (v instanceof android.widget.SeekBar) return strongHorizontal; } catch(eSeek) {}
try { if (v instanceof android.widget.CompoundButton) return strongHorizontal; } catch(eComp) {}
try { if (v instanceof android.widget.Switch) return strongHorizontal; } catch(eSw) {}
try { if (v instanceof android.widget.EditText) return true; } catch(eEdit) {} try { if (v instanceof android.widget.EditText) return true; } catch(eEdit) {}
try { if (v instanceof android.widget.HorizontalScrollView) return true; } catch(eHsv) {} try {
// 普通 Button / 卡片 / 垂直列表 item 不在 DOWN 阶段屏蔽返回;强横滑由 root 在 MOVE 阶段接管。 if (v instanceof android.widget.HorizontalScrollView) {
// 垂直 ListView/RecyclerView 也不作为 blocker保证列表上下滑正常且强横滑可返回。 var dir = Number(dx || 0) > 0 ? -1 : 1;
// 不把所有 clickable/longClickable 都当成阻断项ToolHub 大量卡片/容器为了 ripple 都会 setClickable(true) try { if (v.canScrollHorizontally && v.canScrollHorizontally(dir)) return true; } catch(eCan) {}
// 若在 surface 横滑模式下阻断它们,几乎整页都会 rootBackBlocked=true导致滑动返回一直触发不了。 return false;
// DOWN 已经放行给子控件,只有超过横滑阈值后才拦截;这里只保留 SeekBar/Switch/EditText/HorizontalScrollView 等强交互控件。 }
} catch(eHsv) {}
// 普通 Button / 卡片 / 垂直列表 item 不作为 blocker点击和上下滑继续交给子控件强横滑由 root 接管。
// 只有 SeekBar/Switch/EditText/可继续横向滚动的 HorizontalScrollView 在 MOVE 阶段按 dx/dy 细粒度阻断。
/* try { if (v.isClickable && v.isClickable()) return true; } catch(eClick) {} */ /* try { if (v.isClickable && v.isClickable()) return true; } catch(eClick) {} */
/* try { if (v.isLongClickable && v.isLongClickable()) return true; } catch(eLong) {} */ /* try { if (v.isLongClickable && v.isLongClickable()) return true; } catch(eLong) {} */
} catch(e) {} } catch(e) {}
@@ -1358,11 +1388,11 @@ FloatBallAppWM.prototype.findToolAppTouchedChild = function(v, rawX, rawY) {
return null; return null;
}; };
FloatBallAppWM.prototype.isToolAppBackBlockedAt = function(root, rawX, rawY) { FloatBallAppWM.prototype.isToolAppBackBlockedAt = function(root, rawX, rawY, dx, dy) {
try { try {
var v = this.findToolAppTouchedChild(root, rawX, rawY); var v = this.findToolAppTouchedChild(root, rawX, rawY);
while (v && v !== root) { while (v && v !== root) {
if (this.isToolAppBackInteractiveView && this.isToolAppBackInteractiveView(v)) return true; if (this.isToolAppBackInteractiveView && this.isToolAppBackInteractiveView(v, dx, dy)) return true;
try { v = v.getParent ? v.getParent() : null; } catch(eParent) { v = null; } try { v = v.getParent ? v.getParent() : null; } catch(eParent) { v = null; }
} }
} catch(e) {} } catch(e) {}
@@ -1417,13 +1447,13 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac
else if (rw > 0 && rootDownX >= rw - edgeW) { rootEdge = 1; rootBackEligible = true; } else if (rw > 0 && rootDownX >= rw - edgeW) { rootEdge = 1; rootBackEligible = true; }
} else { } else {
rootBackEligible = true; rootBackEligible = true;
rootBackBlocked = !!(self.isToolAppBackBlockedAt && self.isToolAppBackBlockedAt(this, rootDownRawX, rootDownRawY)); rootBackBlocked = false;
} }
// DOWN 必须放行给子控件surface 模式也不抢按钮/列表/Switch/SeekBar 原始点击。 // DOWN 必须放行给子控件surface 模式也不抢按钮/列表/Switch/SeekBar 原始点击。
return false; return false;
} }
if (action === android.view.MotionEvent.ACTION_MOVE) { if (action === android.view.MotionEvent.ACTION_MOVE) {
if (!rootBackEligible || rootBackBlocked || rootBackMode === "off") return false; if (!rootBackEligible || rootBackMode === "off") return false;
if (!(self.hasToolAppBackTarget && self.hasToolAppBackTarget())) return false; if (!(self.hasToolAppBackTarget && self.hasToolAppBackTarget())) return false;
var dx = ev.getX() - rootDownX; var dx = ev.getX() - rootDownX;
var dy = ev.getY() - rootDownY; var dy = ev.getY() - rootDownY;
@@ -1439,7 +1469,14 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac
} }
var shouldIntercept = false; var shouldIntercept = false;
if (rootBackMode === "surface") { if (rootBackMode === "surface") {
shouldIntercept = validDir && adx > self.dp(48) && adx > ady * 1.2; var commitDp0 = Number(self.config.TOOLAPP_BACK_COMMIT_DISTANCE_DP || 36);
if (isNaN(commitDp0)) commitDp0 = 36;
if (commitDp0 < 1) commitDp0 = 1;
if (commitDp0 > 480) commitDp0 = 480;
var surfaceSlop = self.getToolAppBackSurfaceSlopPx ? self.getToolAppBackSurfaceSlopPx(self.dp(commitDp0)) : Math.min(self.dp(24), self.dp(commitDp0));
var blockedNow = !!(self.isToolAppBackBlockedAt && self.isToolAppBackBlockedAt(this, rootDownRawX, rootDownRawY, dx, dy));
rootBackBlocked = blockedNow;
shouldIntercept = (!blockedNow) && validDir && adx > surfaceSlop && adx > ady * 1.08;
} else { } else {
var slopDp = Number(self.config.CLICK_SLOP_DP || 6); var slopDp = Number(self.config.CLICK_SLOP_DP || 6);
if (isNaN(slopDp)) slopDp = 6; if (isNaN(slopDp)) slopDp = 6;
@@ -1482,7 +1519,7 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac
var mx = ev.getX() - rootDownX; var mx = ev.getX() - rootDownX;
var my = ev.getY() - rootDownY; var my = ev.getY() - rootDownY;
var validDir2 = (rootEdge === 0 && mx > 0) || (rootEdge === 1 && mx < 0); var validDir2 = (rootEdge === 0 && mx > 0) || (rootEdge === 1 && mx < 0);
var dominance = rootBackMode === "surface" ? 1.2 : 0.75; var dominance = rootBackMode === "surface" ? 1.08 : 0.75;
if (validDir2 && Math.abs(mx) > Math.abs(my) * dominance) { if (validDir2 && Math.abs(mx) > Math.abs(my) * dominance) {
var triggerDp = Number(self.config.TOOLAPP_BACK_PROGRESS_DISTANCE_DP || 96); var triggerDp = Number(self.config.TOOLAPP_BACK_PROGRESS_DISTANCE_DP || 96);
if (isNaN(triggerDp)) triggerDp = 96; if (isNaN(triggerDp)) triggerDp = 96;
@@ -1503,7 +1540,7 @@ FloatBallAppWM.prototype.buildToolAppShell = function(contentView, title, canBac
if (commitDp > 480) commitDp = 480; if (commitDp > 480) commitDp = 480;
var completeDistance = self.dp(commitDp); var completeDistance = self.dp(commitDp);
var okDir = (rootEdge === 0 && ux > completeDistance) || (rootEdge === 1 && ux < -completeDistance); var okDir = (rootEdge === 0 && ux > completeDistance) || (rootEdge === 1 && ux < -completeDistance);
var ratio = rootBackMode === "surface" ? 1.2 : 0.75; var ratio = rootBackMode === "surface" ? 1.08 : 0.75;
var ok = (action === android.view.MotionEvent.ACTION_UP) && rootBackMoved && okDir && Math.abs(ux) > Math.abs(uy) * ratio; var ok = (action === android.view.MotionEvent.ACTION_UP) && rootBackMoved && okDir && Math.abs(ux) > Math.abs(uy) * ratio;
var edgeDone = rootEdge; var edgeDone = rootEdge;
rootBackActive = false; rootBackActive = false;

View File

@@ -2,8 +2,8 @@
"alg": "SHA256withRSA", "alg": "SHA256withRSA",
"files": { "files": {
"th_01_base.js": { "th_01_base.js": {
"sha256": "c186450c6a64cf51fdf6cc93e45624793596ce9f566d1b18c3e70905459fd618", "sha256": "12c6645e42cb7dd21da495545e69f64d11c9d6b510aabf0d169edf883b57c4ff",
"size": 57722 "size": 58140
}, },
"th_02_core.js": { "th_02_core.js": {
"sha256": "3c5c498d200e961d48fc9ca3f885475e770ecb32b83ec6a89d23df3f88aed1c9", "sha256": "3c5c498d200e961d48fc9ca3f885475e770ecb32b83ec6a89d23df3f88aed1c9",
@@ -58,8 +58,8 @@
"size": 304993 "size": 304993
}, },
"th_15_extra.js": { "th_15_extra.js": {
"sha256": "380272c10e117c2ad09216709d3f7bd3a54ab97b3fa6ce5594ab5fdd252aa48e", "sha256": "8ef8f3b48c324426f9f6926e7e4fbee8cea6f15b6e465c1dad6982bbb00c3af3",
"size": 129287 "size": 130833
}, },
"th_16_entry.js": { "th_16_entry.js": {
"sha256": "6c59d9891cd010647f84c3db93f1cf95c7bbfb758470ea21044bf72eb8ff73d1", "sha256": "6c59d9891cd010647f84c3db93f1cf95c7bbfb758470ea21044bf72eb8ff73d1",
@@ -68,5 +68,5 @@
}, },
"keyId": "toolhub-targets-2026-rsa3072", "keyId": "toolhub-targets-2026-rsa3072",
"schema": 2, "schema": 2,
"version": 20260522043019 "version": 20260522044045
} }

View File

@@ -1 +1 @@
n3ojvi067byhyd0l6vNsePByOJ/dN9Hoa16nhD/9nuU3f0pnNTKjGDS+iHDlO9V2MOsRPRjTlzYR0inJJZaMh2sSjzCmcAOapxqkm2XwItOrtYjgk3yNBO7AwcXH5sYDUXGZ2Tsx95z7dS3sLNyavVUSAKyO3ygzTSQL7EgfWu3LumIiATrHPHL71jvbJRfG1tVUilWF3VYZMyULOJievgCApeX6h4xRgh0eCdgtbn6bDlH4E1gHbapQ+beYKDD2GL1H9msuwyzehxUM4QZVrtOwXHVlCHL4x9Kidm35wMp4H21XCr7UD6N2oRvUsXoEtI6Ugj8dN2Ilvbz0/GO4Nf/v522xbFsPZBj06kD2qiGGJ6ztT3KkfVBv53GRBhN+v/aGimO8ww/YtnUn6njTcF36Mnh98CvE/9BXENU98TYA0zWNOop5WxlyHXOf6rQ3ivMYnsbKzvOX5LrX8uExJawjpUvsoY7ahUJ+h6TDPcyB6Fl3644MKq5ukXgVm1jI d31WnUYPRd99BgqOnWKrh+xaCDIjlON13cKvM9s+T4xno3g11qoenoFo9rBI4Mcu63R783cIYS9PtfZ8ii1hlaB2DMNqebzGXFTi+9u/Yd56D5pV84Jd/SjL1yN6hTwXT9t/bHrS3E9npyyFVLVeponFagKHecgk/3yEe5GSmLAp1QX0o7ecca2L0rO534rMHa4Ml/dkrsGCwPAfWzuru/xpO8KV0zvQ4leSU7YT+ZxgU4o8L+scvXKDEp3wnaldqqy6ekIhk75m4qmVAPw4dsp+b5CtEcWNfgy6SgvkXz+PawvHOmWz67TQa5G4jCDP7EWynHFcF26U8xuO1x4x4bpNLdzImis/Dg14/HavNID0Yc72+3uJEGHL6RyCyGhic8+mG/25vSkB+brfy8qZK5K8stqZLIC1wJ/0IL0ppkx2LoDTyuwfk+vshzJDmiLBaJCgOuiR24ya3sFeDFD0OWLxbgQiXg1MoYNb1FP69h+i0f2ziea9zja2qEQ8aN81