feat: support predictive back gesture
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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+:注册 OnBackInvokedCallback;Android 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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user