Update shared Gaia workflows and Sargo development tooling

Fix camera switching and Contacts/SMS activity routing across shared hosts.
Integrate Marketplace installation and native standalone recovery tools,
Bluetooth runtime packages and trusted certificates.

Update English/French READMEs and the shared changelog with tested scope
and remaining radio/audio/Bluetooth limitations. Preview17 remains the
existing APK; no new native image is released.

Validation: shared JavaScript and native Python tests; Android packaging
and payload parity from an isolated publication checkout.
This commit is contained in:
OverSu 2026-10-08 14:27:17 +02:00
commit 0041cd9660
101 changed files with 3883 additions and 335 deletions

View file

@ -10,7 +10,9 @@
* application installation, crash reporter, and low storage notices.
* @class SystemBanner
*/
function SystemBanner() {}
function SystemBanner() {
this._generation = 0;
}
/**
* Amount of time before banner is automatically dismissed.
@ -77,10 +79,18 @@
* { label: l10nAttrs, callback: ..., dismiss: ... }
*/
show: function(messages, buttonParams) {
var generation = ++this._generation;
clearTimeout(this._dismissTimer);
if (this._cancelRemoval) {
this._cancelRemoval();
}
this._clicked = false;
this._showing = true;
return this.getBanner().then(banner => {
if (generation !== this._generation || !this._showing) {
return;
}
// Clear the button and message containers.
var messageContainer = banner.querySelector('.messages');
var buttonContainer = banner.querySelector('.buttons');
@ -110,13 +120,16 @@
// Hide banner and invoke button dismiss() on click or timeout.
var hideBanner = () => {
if (generation !== this._generation || !this._showing) {
return;
}
this.hide();
if (!this._clicked) {
buttonParams && buttonParams.dismiss && buttonParams.dismiss();
}
};
banner.onclick = hideBanner;
setTimeout(hideBanner, SystemBanner.TIMEOUT);
this._dismissTimer = setTimeout(hideBanner, SystemBanner.TIMEOUT);
document.getElementById('screen').appendChild(banner);
banner.show();
@ -131,15 +144,37 @@
return;
}
this._showing = false;
clearTimeout(this._dismissTimer);
var generation = this._generation;
return this.getBanner().then(banner => {
banner.addEventListener('animationend', function removeBanner() {
banner.removeEventListener('animationend', removeBanner);
// Only remove banner if we haven't called show() in the meantime.
if (!this._showing) {
if (generation !== this._generation || this._showing) {
return;
}
// Animation events inside a modern shadow root do not reach the host.
var animation = banner.shadowRoot &&
banner.shadowRoot.querySelector('.bread') || banner;
var cleanup = () => {
clearTimeout(this._removeTimer);
animation.removeEventListener('animationend', removeBanner);
animation.removeEventListener('animationcancel', removeBanner);
this._cancelRemoval = null;
};
var removeBanner = event => {
if (event && event.target !== animation) {
return;
}
cleanup();
if (!this._showing && generation === this._generation) {
banner.parentNode && banner.parentNode.removeChild(banner);
}
}.bind(this));
};
this._cancelRemoval = cleanup;
animation.addEventListener('animationend', removeBanner);
animation.addEventListener('animationcancel', removeBanner);
// Gaia's exit animation lasts 600 ms. Also dismiss if it is disabled
// or no animation event is produced.
this._removeTimer = setTimeout(removeBanner, 700);
banner.hide();
});
}