feat: support predictive back gesture

This commit is contained in:
7015725
2026-05-12 20:43:42 +08:00
parent c4b886f83d
commit efb14bcde0
4 changed files with 115 additions and 7 deletions

View File

@@ -42,6 +42,7 @@ function FloatBallAppWM(logger) {
viewerPanel: null, viewerPanel: null,
viewerPanelLp: null, viewerPanelLp: null,
viewerPanelType: null, viewerPanelType: null,
panelBackCallbackEntries: [],
// 设置类 UI App 化单窗口页面栈settings -> 子页面 -> 编辑页) // 设置类 UI App 化单窗口页面栈settings -> 子页面 -> 编辑页)
toolAppActive: false, toolAppActive: false,

View File

@@ -127,6 +127,7 @@ FloatBallAppWM.prototype.playBounce = function(v) {
FloatBallAppWM.prototype.safeRemoveView = function(v, whichName) { FloatBallAppWM.prototype.safeRemoveView = function(v, whichName) {
try { try {
if (!v) return { ok: true, skipped: true }; if (!v) return { ok: true, skipped: true };
try { if (this.unregisterPanelPredictiveBack) this.unregisterPanelPredictiveBack(v); } catch (eBack) {}
this.state.wm.removeView(v); this.state.wm.removeView(v);
return { ok: true }; return { ok: true };
} catch (e) { } catch (e) {
@@ -265,6 +266,109 @@ FloatBallAppWM.prototype.handleSystemUiDismiss = function(reason) {
return false; return false;
}; };
FloatBallAppWM.prototype.resetPanelPredictiveBackVisual = function(panel) {
try {
if (!panel) return;
panel.setAlpha(1.0);
panel.setTranslationX(0);
panel.setScaleX(1.0);
panel.setScaleY(1.0);
} catch (e) {}
};
FloatBallAppWM.prototype.applyPanelPredictiveBackProgress = function(panel, event) {
try {
if (!panel || !event) return;
var p = 0;
try { p = Number(event.getProgress()); } catch (eP) { p = 0; }
if (isNaN(p)) p = 0;
if (p < 0) p = 0;
if (p > 1) p = 1;
var edge = 0;
try { edge = Number(event.getSwipeEdge()); } catch (eE) { edge = 0; }
var dir = edge === 1 ? -1 : 1;
panel.setAlpha(1.0 - 0.18 * p);
panel.setTranslationX(dir * this.dp(36) * p);
var s = 1.0 - 0.025 * p;
panel.setScaleX(s);
panel.setScaleY(s);
} catch (e) {}
};
FloatBallAppWM.prototype.unregisterPanelPredictiveBack = function(panel) {
try {
var entries = this.state.panelBackCallbackEntries || [];
var kept = [];
for (var i = 0; i < entries.length; i++) {
var it = entries[i];
if (!it || it.view === panel) {
try { if (it && it.dispatcher && it.callback) it.dispatcher.unregisterOnBackInvokedCallback(it.callback); } catch (eUnreg) {}
} else {
kept.push(it);
}
}
this.state.panelBackCallbackEntries = kept;
this.resetPanelPredictiveBackVisual(panel);
} catch (e) {
safeLog(this.L, 'w', "unregister predictive back fail: " + String(e));
}
};
FloatBallAppWM.prototype.registerPanelPredictiveBack = function(panel, which) {
// Android 13+:注册 OnBackInvokedCallbackAndroid 14+ 优先使用 OnBackAnimationCallback实现预测性返回手势进度动画。
try {
if (!panel) return false;
if (android.os.Build.VERSION.SDK_INT < 33) return false;
this.unregisterPanelPredictiveBack(panel);
var dispatcher = null;
try { dispatcher = panel.findOnBackInvokedDispatcher(); } catch (eFind) { dispatcher = null; }
if (!dispatcher) return false;
var self = this;
var cb = null;
var usedAnimation = false;
if (android.os.Build.VERSION.SDK_INT >= 34) {
try {
var animCls = java.lang.Class.forName("android.window.OnBackAnimationCallback");
cb = new JavaAdapter(animCls, {
onBackStarted: function(event) { self.applyPanelPredictiveBackProgress(panel, event); },
onBackProgressed: function(event) { self.applyPanelPredictiveBackProgress(panel, event); },
onBackCancelled: function() { self.resetPanelPredictiveBackVisual(panel); },
onBackInvoked: function() {
self.resetPanelPredictiveBackVisual(panel);
self.handlePanelBack(which, "predictive_back");
}
});
usedAnimation = true;
} catch (eAnim) {
cb = null;
}
}
if (!cb) {
try {
var cbCls = java.lang.Class.forName("android.window.OnBackInvokedCallback");
cb = new JavaAdapter(cbCls, {
onBackInvoked: function() { self.handlePanelBack(which, "on_back_invoked"); }
});
} catch (eCb) {
cb = null;
}
}
if (!cb) return false;
var priority = 0;
try { priority = android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT; } catch (ePri) { priority = 0; }
dispatcher.registerOnBackInvokedCallback(priority, cb);
if (!this.state.panelBackCallbackEntries) this.state.panelBackCallbackEntries = [];
this.state.panelBackCallbackEntries.push({ view: panel, dispatcher: dispatcher, callback: cb, which: String(which || ""), animation: usedAnimation });
safeLog(this.L, 'i', "predictive back registered which=" + String(which || "") + " animation=" + String(usedAnimation));
return true;
} catch (e) {
safeLog(this.L, 'w', "register predictive back fail which=" + String(which || "") + " err=" + String(e));
}
return false;
};
FloatBallAppWM.prototype.attachPanelSystemKeyHandler = function(panel, which) { FloatBallAppWM.prototype.attachPanelSystemKeyHandler = function(panel, which) {
try { try {
if (!panel) return; if (!panel) return;
@@ -282,7 +386,10 @@ FloatBallAppWM.prototype.attachPanelSystemKeyHandler = function(panel, which) {
return false; return false;
} }
})); }));
panel.post(new java.lang.Runnable({ run: function() { try { panel.requestFocus(); } catch(eFocus) {} } })); panel.post(new java.lang.Runnable({ run: function() {
try { panel.requestFocus(); } catch(eFocus) {}
try { self.registerPanelPredictiveBack(panel, which); } catch(eBack) {}
} }));
} catch (e) { } catch (e) {
safeLog(this.L, 'e', "attachPanelSystemKeyHandler fail which=" + String(which || "") + " err=" + String(e)); safeLog(this.L, 'e', "attachPanelSystemKeyHandler fail which=" + String(which || "") + " err=" + String(e));
} }

View File

@@ -6,8 +6,8 @@
"size": 52546 "size": 52546
}, },
"th_02_core.js": { "th_02_core.js": {
"sha256": "24363adc7156feda5c2f1fcbb84c96a4378ffec4b40251bd27d0123329acf054", "sha256": "2274feddccb87873d81ac0337e14de9998bb9fa8600cccfe16cbc4552d05284b",
"size": 4203 "size": 4237
}, },
"th_03_icon.js": { "th_03_icon.js": {
"sha256": "717f7f37474d3616c2cd944581455f600020a850ec8812100d0546ec1302c987", "sha256": "717f7f37474d3616c2cd944581455f600020a850ec8812100d0546ec1302c987",
@@ -34,8 +34,8 @@
"size": 7938 "size": 7938
}, },
"th_09_animation.js": { "th_09_animation.js": {
"sha256": "c3a9565d4b6c0e48d7b7592fd7d999e78fbec3c19bb3f5862151149c7598ae27", "sha256": "f7083cbbc1b3efad1dcbccf19185cf1ee530172164da40f94dadc50fc5e3d763",
"size": 27832 "size": 31917
}, },
"th_10_shell.js": { "th_10_shell.js": {
"sha256": "0ed793079c2f6ba7d29f4c0d411705cb72419f45f572cbe37ed32ac16527a8bc", "sha256": "0ed793079c2f6ba7d29f4c0d411705cb72419f45f572cbe37ed32ac16527a8bc",
@@ -68,5 +68,5 @@
}, },
"keyId": "toolhub-targets-2026-rsa3072", "keyId": "toolhub-targets-2026-rsa3072",
"schema": 2, "schema": 2,
"version": 20260512123028 "version": 20260512124303
} }

View File

@@ -1 +1 @@
gwwFWHnXNU/QJ+BfqHacNXHFLowDLZ4izcO8S+z4+q25HkClwIazJNMr9LbkXPSszY/1pO82MiIjTVw+ubzHrD1caoml0AoOTUJBRKNVMxqlXqepdodfmmvcg3g3BxC8WmtCONKjnvdX1EI6faYRbkA61gtsy8qQNUDPAR0pre8OqPBvgYc8eo4ZFyR+E+kDE8na5Vl+fsMG2anPUvEUi3ivSUlijTaYWxo4ZLUfIs3oEZxy0b/6leFX+8QhbvtkJcwkq3Jfrycl0Lu8ENTToGheWZqBjLV1TcihR3uOSMJqMoIdTWg01iCK0XOsyZva7Fpa/yQQU4DZYVOrLpBA6nWdmutAwOQ+/JcswCVlmdgogoUu7EmY1v8b/WKVvwaRNXc9LdKMkoveW7sJr3HtArb9qiNqyxoZr/88+/e93F408s3XkB9o43BhrMBMXi7lUp58DCODtBMnyb6eq/5Iu+xYw4xdRVXGDDCudVMtNFp2yc0x1EuG/6l3Ten7EMgv SZay6+P/vnwc/F7V2bwkn3pXI8IQ1aa3fLJI3/LaW3H9lYQuqIErWsbYVIzxvprjgs0KM3iEbynlIkvJDFVggvLcXoo/yZZAu1K10ZhuH7KACG1MGk7ViiGRzW5GMSLvM5zW2qtN2V/+Lj/c9XNZDxIS8hMswKaolXTWYwsiw1NKhWOjRxwzgXuzai2TMQdJRZM3frGsgt9sB/78fDczyFD9NPYkeAjywnAjnH6iLCaAEH8PsrFECDuwYqn4uE3sxO4XGp/JPEX6Un1zQB1QgaN+rTWjyCbAIqapBqb8xulY7/m8wUo9Ei+VnozOFbseldO/2axsFHeXa6XKttTuXKQvcYjWeYpezgDiPsXzoAtJyAS2JZ2lNsOGurHQuBBA6pH93+hP8sCltNzNgtZMT7lJhZxRH4+pFvm6bf3W9ZSSdNGlvAQ6hHok3U+k6YuSoBzT8IZHDyv1iW2p9KB655W0qYVMRn+Y73YXAhdfVlC/m0jhBkfiOLtulHn8x96z