Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

532
gaia/apps/ftu/js/navigation.js Executable file
View file

@ -0,0 +1,532 @@
/* global DataMobile, SimManager, IccHelper,
SdManager, UIManager, WifiManager, WifiUI,
ImportIntegration,
OperatorVariant */
/* exported Navigation */
(function(exports) {
'use strict';
const DOGFOODSETTING = 'debug.performance_data.dogfooding';
const METRICSSETTING = 'metrics.selectedMetrics.level';
/*
Steps of the First Time Usage App
*/
var steps = [
{
onlyForward: true,
hash: '#languages',
requireSIM: false
},
{
onlyForward: false,
hash: '#data_3g',
requireSIM: true
},
{
onlyForward: false,
hash: '#wifi',
requireSIM: false
},
{
onlyForward: false,
hash: '#date_and_time',
requireSIM: false
},
{
onlyForward: false,
hash: '#geolocation',
requireSIM: false
},
{
onlyForward: false,
hash: '#import_contacts',
requireSIM: false
},
{
onlyForward: false,
hash: '#firefox_accounts',
requireSIM: false
},
{
onlyForward: false,
hash: '#welcome_browser',
requireSIM: false
},
{
onlyForward: false,
hash: '#browser_privacy',
requireSIM: false
}
];
exports.Navigation = {
get stepCount() {
return this._stepOrder ? this._stepOrder.length : 0;
},
currentStepIndex: 0,
previousStepIndex: 0,
simMandatory: false,
skipMobileDataScreen: false,
skipDateTimeScreen: false,
tzInitialized: false,
_stepsById: null,
_stepOrder: null,
init: function n_init() {
this._stepOrder = [];
this._stepsById = {};
var settings = navigator.mozSettings;
var forward = document.getElementById('forward');
var back = document.getElementById('back');
this._onForwardClick = this.forward.bind(this);
this._onBackClick = this.back.bind(this);
forward.addEventListener('click', this._onForwardClick);
back.addEventListener('click', this._onBackClick);
window.addEventListener('hashchange', this);
this._onActivationScreenClick = this.handleExternalLinksClick.bind(this);
UIManager.activationScreen.addEventListener('click',
this._onActivationScreenClick);
// register pre-defined steps in the oder they appear
steps.forEach(step => {
this.registerStep(step);
});
var self = this;
var reqSIM =
settings && settings.createLock().get('ftu.sim.mandatory') || {};
reqSIM.onsuccess = function onSuccess() {
self.simMandatory = reqSIM.result['ftu.sim.mandatory'] || false;
};
navigator.mozApps.getSelf().onsuccess = function(evt) {
var app = evt.target.result;
app.connect('ftucomms').then(function onConnAccepted(ports) {
self.ports = ports;
ports.forEach(port => port.postMessage('started'));
}, function onConnRejected(reason) {
console.warn('FTU navigation cannot use IAC: ' + reason);
});
};
},
uninit: function() {
this._stepOrder = null;
this._stepsById = null;
this.currentStepIndex = 0;
this.previousStepIndex = 0;
this.ports = null;
var forward = document.getElementById('forward');
var back = document.getElementById('back');
var activationScreen = UIManager && UIManager.activationScreen;
if (forward && this._onForwardClick) {
forward.removeEventListener('click', this._onForwardClick);
}
if (back && this._onBackClick) {
back.removeEventListener('click', this._onBackClick);
}
if (activationScreen && this._onActivationScreenClick) {
activationScreen.removeEventListener('click',
this._onActivationScreenClick);
}
window.removeEventListener('hashchange', this);
},
registerStep: function(step) {
if (!step.id) {
step.id = step.hash.substring(1); // strip off '#' to make an id;
}
var id = step.id;
this._stepsById[id] = step;
var beforeStepIdx = step.hasOwnProperty('beforeStep') ?
this._stepOrder.indexOf(step.beforeStep) : -1;
var afterStepIdx = step.hasOwnProperty('afterStep') ?
this._stepOrder.indexOf(step.afterStep) : -1;
if (beforeStepIdx > -1) {
this._stepOrder.splice(beforeStepIdx, 0, id);
} else if (afterStepIdx > -1) {
this._stepOrder.splice(afterStepIdx+1, 0, id);
} else {
this._stepOrder.push(id);
}
},
stepAt: function(idx) {
var stepId = this._stepOrder[idx];
if (stepId) {
return this._stepsById[stepId];
}
},
indexOfStep: function(step) {
var id = (typeof id === 'string') ? step.id : step;
return this._stepOrder.indexOf(id);
},
back: function n_back(event) {
var currentStep = this.stepAt(this.currentStepIndex);
var actualHash = window.location.hash;
if (actualHash != currentStep.hash) {
window.history.back();
} else {
var self = this;
var goToStep = function() {
self.previousStepIndex = self.currentStepIndex;
self.currentStepIndex--;
if (self.currentStepIndex >= 0) {
self.manageStep();
}
};
goToStep();
}
},
forward: function n_forward(event) {
var self = this;
var goToStepForward = function() {
self.previousStepIndex = self.currentStepIndex;
self.currentStepIndex++;
if (self.currentStepIndex >= self.stepCount) {
// Try to send Newsletter here
UIManager.sendNewsletter(function newsletterSent(result) {
if (result) { // sending process ok, we advance
UIManager.activationScreen.classList.remove('show');
UIManager.changeStatusBarColor(UIManager.DARK_THEME);
UIManager.finishScreen.classList.add('show');
UIManager.hideActivationScreenFromScreenReader();
} else { // error on sending, we stay where we are
self.currentStepIndex--;
}
});
return;
}
self.manageStep();
};
goToStepForward();
},
handleExternalLinksClick: function n_handleExternalLinksClick(e) {
var link = e.target;
if (! link.mozMatchesSelector('a.external')) {
return;
}
e.preventDefault();
var href = link.href,
title = link.getAttribute('title') || link.textContent;
if (navigator.onLine) {
this.displayExternalLink(href, title);
} else {
UIManager.displayOfflineDialog(href, title);
}
},
displayExternalLink: function n_displayExternalLink(href, title) {
window.open(href, '', 'dialog');
},
ensureTZInitialized: function () {
if (!this.tzInitialized) {
return UIManager.initTZ().then(() => {
this.skipDateTimeScreen = !UIManager.timeZoneNeedsConfirmation;
this.tzInitialized = true;
});
} else {
return Promise.resolve();
}
},
handleEvent: function n_handleEvent(event) {
var actualHash = window.location.hash;
switch (actualHash) {
case '#languages':
UIManager.mainTitle.setAttribute('data-l10n-id', 'language');
break;
case '#data_3g':
UIManager.mainTitle.setAttribute('data-l10n-id', '3g');
DataMobile.
getStatus(UIManager.updateDataConnectionStatus.bind(UIManager));
break;
case '#wifi':
DataMobile.removeSVStatusObserver();
UIManager.mainTitle.setAttribute('data-l10n-id', 'selectNetwork');
UIManager.activationScreen.classList.remove('no-options');
if (UIManager.navBar.classList.contains('secondary-menu')) {
UIManager.navBar.classList.remove('secondary-menu');
return;
}
// This might seem like an odd place to call UIManager.initTZ, but
// there's a reason for it. initTZ tries to determine the timezone
// using information about the current mobile network connection.
// We want to call initTZ as late as possible to give the mobile
// network time to connect before the function is called.
// But we have to call initTZ *before* the Date & Time page
// appears so that it doesn't delay the appearance of the page.
// This is the last good opportunity to call it.
WifiManager.getNetworks(networks => {
this.ensureTZInitialized().then(() => {
WifiUI.renderNetworks(networks);
});
});
break;
case '#date_and_time':
UIManager.mainTitle.setAttribute('data-l10n-id', 'dateAndTime');
break;
case '#geolocation':
UIManager.mainTitle.setAttribute('data-l10n-id', 'geolocation');
break;
case '#import_contacts':
UIManager.mainTitle.setAttribute('data-l10n-id', 'importContacts3');
// Enabling or disabling SIM import depending on card status
SimManager.checkSIMButton();
// Enabling or disabling SD import depending on card status
SdManager.checkSDButton();
// If we have 3G or Wifi activate online services import
var onlineState;
if (!WifiManager.api) {
// Desktop
ImportIntegration.checkImport('enabled');
break;
}
onlineState = window.navigator.onLine ? 'enabled' : 'disabled';
ImportIntegration.checkImport(onlineState);
break;
case '#firefox_accounts':
UIManager.mainTitle.setAttribute('data-l10n-id', 'firefox-accounts');
break;
case '#welcome_browser':
UIManager.mainTitle.setAttribute('data-l10n-id', 'aboutBrowser');
// Initialize the radio button to preselect the basic metrics.
var settings = navigator.mozSettings;
var basicMetrics = document.getElementById('metrics-basic');
var enhancedMetrics = document.getElementById('metrics-enhanced');
var noneMetrics = document.getElementById('metrics-none');
var defaultLevel = settings &&
settings.createLock().get(METRICSSETTING);
if (defaultLevel) {
defaultLevel.onsuccess = function() {
var result = defaultLevel.result[METRICSSETTING];
if (result === 'Basic') {
basicMetrics.checked = true;
} else if (result === 'Enhanced') {
enhancedMetrics.checked = true;
} else if (result === 'None') {
noneMetrics.checked = true;
}
};
}
// If it's a dogfooder, we don't want them to disable the metrics.
var dogfood = settings && settings.createLock().get(DOGFOODSETTING);
if (dogfood) {
dogfood.onsuccess = function() {
if (dogfood.result[DOGFOODSETTING]) {
basicMetrics.setAttribute('disabled', 'true');
enhancedMetrics.setAttribute('disabled', 'true');
noneMetrics.setAttribute('disabled', 'true');
} else {
basicMetrics.removeAttribute('disabled');
enhancedMetrics.removeAttribute('disabled');
noneMetrics.removeAttribute('disabled');
}
};
}
break;
case '#browser_privacy':
UIManager.mainTitle.setAttribute('data-l10n-id', 'aboutBrowser');
break;
case '#SIM_mandatory':
UIManager.mainTitle.setAttribute('data-l10n-id', 'SIM_mandatory');
break;
case '#about-your-rights':
case '#about-your-privacy':
UIManager.mainTitle.setAttribute('data-l10n-id', 'aboutBrowser');
UIManager.navBar.classList.add('back-only');
break;
case '#sharing-performance-data':
UIManager.mainTitle.setAttribute('data-l10n-id', 'aboutBrowser');
UIManager.navBar.classList.add('back-only');
break;
}
// If SIM card is mandatory, we hide the button skip
if (this.simMandatory) {
UIManager.skipPinButton.classList.add('hidden');
UIManager.backSimButton.classList.remove('hidden');
} else {
UIManager.skipPinButton.classList.remove('hidden');
UIManager.backSimButton.classList.add('hidden');
}
// Managing options button
var lastIndex = this.stepCount - 1;
if (this.currentStepIndex <= lastIndex &&
(this.stepAt(this.currentStepIndex).hash !== '#wifi')) {
UIManager.activationScreen.classList.add('no-options');
}
// Managing nav buttons when coming back from out-of-steps (privacy)
if (this.currentStepIndex <= lastIndex &&
this.stepAt(this.currentStepIndex).hash === actualHash) {
UIManager.navBar.classList.remove('back-only');
}
},
/**
* Posts IAC message about FTU steps passed.
*/
postStepMessage: function n_postStepMessage(stepIndex) {
if (!this.ports) {
return;
}
var step = this.stepAt(stepIndex);
this.ports.forEach(function(port) {
port.postMessage({
type: 'step',
hash: step.hash
});
});
},
skipStep: function n_skipStep() {
var stepIndex = this.currentStepIndex;
stepIndex += stepIndex > this.previousStepIndex ? 1 : -1;
if (stepIndex <= 0) {
this.previousStepIndex = stepIndex = 0;
}
if (stepIndex > this.stepCount) {
this.previousStepIndex = stepIndex = this.stepCount;
}
this.currentStepIndex = stepIndex;
this.manageStep();
},
manageStep: function n_manageStep() {
var self = this;
// If we moved forward in FTU, post iac message about progress.
if (self.currentStepIndex > self.previousStepIndex) {
self.postStepMessage(self.previousStepIndex);
}
//SV - We need remember if phone startup with SIM
if (self.currentStepIndex >= self.stepCount) {
OperatorVariant.setSIMOnFirstBootState();
}
// Reset skip screen flags at beginning of navigation
if (self.currentStepIndex === 0) {
self.skipMobileDataScreen = false;
}
// Retrieve future location
var futureLocation = this.stepAt(self.currentStepIndex);
// If SIMcard is mandatory and no SIM, go to message window
if (this.simMandatory &&
!IccHelper.cardState &&
futureLocation.requireSIM) {
//Send to SIM Mandatory message
futureLocation.hash = '#SIM_mandatory';
futureLocation.requireSIM = false;
futureLocation.onlyBackward = true;
}
// Navigation bar management
if (this.stepAt(this.currentStepIndex).onlyForward) {
UIManager.navBar.classList.add('forward-only');
} else {
UIManager.navBar.classList.remove('forward-only');
}
var nextButton = document.getElementById('forward');
if (this.stepAt(this.currentStepIndex).onlyBackward) {
nextButton.setAttribute('disabled', 'disabled');
} else {
nextButton.removeAttribute('disabled');
}
// Substitute button content on last step
if (this.currentStepIndex === this.stepCount) {
nextButton.setAttribute('data-l10n-id', 'done');
} else {
nextButton.setAttribute('data-l10n-id', 'navbar-next');
}
if (futureLocation.hash === '#firefox_accounts') {
nextButton.setAttribute('data-l10n-id', 'skip');
}
// Change hash to the right location
window.location.hash = futureLocation.hash;
// SIM card management
if (futureLocation.requireSIM) {
var check_cardState = function(response) {
if (!response || (!SimManager.available() &&
// Don't skip it if next step is data 3g
futureLocation.hash !== '#data_3g')) {
self.skipStep();
if (self.currentStepIndex > self.previousStepIndex) {
self.skipMobileDataScreen = true;
}
}
};
// if we are navigating backwards, we do not want to
// show the SIM unlock screens for the data_3g step
var skipUnlockScreens = this.currentStepIndex < this.previousStepIndex;
SimManager.handleCardState(check_cardState, skipUnlockScreens);
}
// Only show the Date & Time screen if we couldn't determine the
// timezone from the network. (We assume that if we can
// determine the timezone, we can determine the time too.)
if (this.stepAt(this.currentStepIndex).hash === '#date_and_time') {
if (!self.tzInitialized) {
self.skipDateTimeScreen = !UIManager.timeZoneNeedsConfirmation;
}
if (self.skipDateTimeScreen) {
self.postStepMessage(self.currentStepIndex);
if(navigator.onLine) {
//if you are online you can get a more accurate guess for the time
//time you just need to trigger it
UIManager.updateSetting(
'time.timezone.automatic-update.enabled',
true
);
UIManager.updateSetting(
'time.clock.automatic-update.enabled',
true
);
}
self.skipStep();
}
}
// if we are not connected we should not try fxa
if ((futureLocation.hash === '#firefox_accounts' &&
!navigator.onLine) ||
(futureLocation.hash === '#firefox_accounts' &&
UIManager.skipFxA)) {
self.postStepMessage(self.currentStepIndex);
self.skipStep();
}
}
};
})(window);