一、模块简介
APN界面在Settings应用菜单实现,代码归属gaia/apps/settings。
KaiOS panel 生命周期:onInit->onBeforeShow->onShow->onBeforeHide->onHide
官方参考文档:Gaia/Settings/docs - MozillaWiki
二、KaiO3.1应用通用目录结构
源码路径gaia/apps(感觉就是个网站网页开发),手机目录
文件(夹) | 作用说明 |
manifest.webapp | 该文件作用类似于AndroidManifest.xml文件,在其中进行应用名称,默认语言,所需权限,开发者等信息的设置 |
index.html | 主页,相当于MainActivity.java,应用启动的第一个页面,需要在manifest.webapp文件中配置 |
js/src文件夹 | module; JavaScript源代码、模块化代码文件、模块间交互引用 |
element文件夹 | html文件 |
style文件夹 | 资源文件,如css样式,image图片,icons图标等 |
locales文件夹 | string资源。 如Settings应用用到字符串位置:gaia/locales/en-US/apps/settings/settings.properties |
resources文件夹 | 资源文件,类似Android res目录 |
test文件夹 | 包含单元测试等功能文件 |
三、代码路径
1、【UI】APN列表页:
- gaia/apps/settings/elements/apn_list.html 界面结构和布局
- gaia/apps/settings/js/panels/apn_list/panel.js 逻辑
- createApnSettingsPanel函数创建并返回SettingsPanel面板对象
- gaia/apps/settings/elements/apn_settings.html
- gaia/apps/settings/js/panels/apn_settings/panel.js
Kai基原生行为:apn_list(左)和apn_settings(右)是两个不同的界面
- apn_list:Data Settings,卡匹配的一套APN,以每条APN形式展示
- apn_settings:APN Settings,针对不同类型的APN分类展示
Note:定制APN菜单项registerSoftkey()——比如运营商需求不可添加APN
2、【UI】APN编辑页:新增、修改APN
- gaia/apps/settings/elements/apn_editor.html 界面
- gaia/apps/settings/js/panels/apn_editor/panel.js 逻辑
3、【功能逻辑】
更新数据库APN:/gaia/apps/settings/js/modules/apn/apn_settings.js
gaia/apps/settings/js/modules/apn/apn_settings_manager.js
四 、实现逻辑
KaiOS的APN设置界面是使用HTML和JavaScript动态生成的:
-
"apn_settings.html"文件中包含APN设置界面的HTML代码,其中包括单选按钮的代码。
-
"apn_settings.js"文件中包含用于生成和操作单选按钮的JavaScript代码。这个文件中的代码使用模板引擎来生成HTML代码,然后将其插入到页面中。
(一)【UI】卡APN列表:apn_list
panels.js 实际用APN列表界面
- gaia/apps/settings/elements/apn_lists.html 定义了 APN Settings界面的结构和布局
- gaia/apps/settings/js/panels/apn_list/panel.js
- gaia/apps/settings/js/panels/apn_list/apn_template_factory.js 界面模板
<!--gaia/apps/settings/elements/apn_list.html-->
<element name="apn_list" extends="section">
<template>
<gaia-header data-href="#apn_settings">
<h1 slot="text"></h1>
</gaia-header>
<div class ="panel">
<ul role="menu" class="apn-list"></ul>
<ul>
</ul>
</div>
<panel data-path="panels/apn_list/panel"><panel>
</template>
</element>
apn_lists/panel.js接口 | 说明 |
createApnSettingsPanel() | 最外层返回的方法,其他功能函数都嵌套在内。 |
getFocusPos | |
registerSoftkey | |
resetApnWarningDialog | |
resetApn | |
updateSoftkey | |
updateUI | |
getElementByType | |
onApnDelete | After we deleted an apn , we should remove it from apn-list and reset focus && navigation map && refresh softkeys |
SettingsPanel |
apn_template_factory.js 字段介绍
gaia/apps/settings/js/panels/apn_list/apn_template_factory.js
要注意const变量的定义,避免引起undefined报错。此模板用于创建DOM节点。
- const rawApn = item.apn;
- const input = document.createElement('input'); // Create an <input type="radio"> element
- const radioSpan = document.createElement('span'); //单选按钮
- const radioLabel = document.createElement('label'); //标签
- const nameSpan = document.createElement('span');
- const nameLabel = document.createElement('label');
- const li = document.createElement('li'); //每一行
//item参数包含了APN具体信息
function apnTemplate(apnType, onItemclick, onRadioClick, itemsOBJ, item) {
const rawApn = item.apn; //从item获取APN对象
// Create an <input type="radio"> element
const input = document.createElement('input'); //创建input元素
input.type = 'radio'; //设置input元素类型是radio(Radio Button控件)
input.checked = item.active; //并根据item.active布尔属性值设置是否选中
input.name = apnType;
// Include the radio button element in a list item
const radioSpan = document.createElement('span'); //创建span元素,用于显示Radio Button空间的样式
const radioLabel = document.createElement('label'); //创建lable元素
radioLabel.classList.add('pack-radio-large');
radioLabel.appendChild(input); //将input元素添加到lable元素中
radioLabel.appendChild(radioSpan);
//创建包含APN名称的span元素,并添加其到nameLable元素中
const nameSpan = document.createElement('span');
const nameLabel = document.createElement('label');
nameSpan.classList.add('full-string');
nameLabel.classList.add('pack-radio-large');
nameLabel.classList.add('my_radio');
nameLabel.setAttribute('data-id', item.id); //为nameLabel元素设置一个data-id属性,用于后续操作中能够识别APN列表项
itemsOBJ[item.id] = item;
if (!rawApn.carrier) {
nameSpan.textContent = rawApn.apn;
} else {
nameSpan.textContent = rawApn.carrier;
}
nameLabel.appendChild(input);
nameLabel.appendChild(nameSpan);
const li = document.createElement('li');
li.appendChild(nameLabel); //最后将lable添加到新的列表项返回
return li;
}
apn_list 业务逻辑
/gaia/apps/settings/js/modules/apn/apn_list.js
apn_lists.js接口 | 说明 |
ApnList(key) | |
ApnList.prototype = | 包含下列增删查改的方法 |
schedule(task) | As the operations should not be performed concurrently. We use this function to enusre the operations are performed one by one. |
export() | |
commit() | |
addInternal(apn, category) | |
removeInternal(id) | |
updateInternal(id, apn) | |
items() | |
item(id) | |
add(apn, category) | |
remove(id) | |
update(id, apn) | |
updateByWap(id, apn) | |
apnList(key) |
(二)【UI】分类APN菜单:apn_settings
panels.js
gaia/apps/settings/js/panels/apn_settings/panel.js是KaiOS APN设置界面,包含了生成和操作APN设置界面的所有JavaScript代码,主要功能如下:
-
初始化界面:当APN设置界面被加载时,会调用init函数进行初始化。在init函数中,首先检查是否支持APN设置,如果不支持,则显示错误消息并返回。如果支持,则加载APN列表和默认设置,并将其显示在界面上。
-
加载APN列表:loadAPNs函数用于从设备中读取APN列表。该函数使用navigator.mozMobileConnections API来获取当前设备上的移动连接,并使用其getAvailableNetworks方法获取APN列表。如果成功获取APN列表,则将其存储在apnList变量中。
-
加载默认设置:loadDefaultSetting函数用于加载当前的默认设置。该函数使用navigator.mozSettings API获取当前设备的APN设置,并将其存储在defaultSetting变量中。
-
显示APN设置列表:displayAPNList函数用于将APN设置列表显示在界面上。该函数使用Handlebars.js模板引擎生成每个APN设置项目的HTML代码,并将其插入到HTML文档中的相应元素中。
-
更新选项状态:updateOptionState函数用于根据用户的选择更新APN设置项的状态。该函数将用户选择的APN设置ID存储在selectedAPN变量中,并使用jQuery选择器更新相应的单选按钮状态。
-
保存设置:saveSetting函数用于将用户的选择保存到设备中。该函数使用navigator.mozSettings API将用户选择的APN设置保存到设备的APN设置中。
apn_settings/panels.js接口 | 说明 |
createApnSettingsPanel() | 最外层返回的方法,其他功能函数都嵌套在内,这样内部函数可以访问外部函数createApnSettingsPanel的所有变量和参数。 const APN_KEYS = [ 'ril.data.dm.apnSettings.sim1', 'ril.data.dm.apnSettings.sim2' ]; |
initSoftKey(hasSelect) | 接口=界面右侧菜单Options(Edit&Delete&Reset) menuClassName: 'menu-button', |
updateSoftKey(evt) | |
resetApnWarningDialog() | |
resetApn() | |
browseApnItems(evt) | 用于处理“浏览APN”按钮的点击事件,打开“浏览APN”面板,即APN编辑页详情。 1、首先检查触发事件的元素是否包含数据集中的“apnType”属性。如果没有,则退出函数。 2、如果存在“apnType”属性,则获取该属性的值,并将其与当前设备的服务ID一起传递给 Settings.setCurrentPanel 函数。 3、Settings.setCurrentPanel 函数用于打开“浏览APN”面板,并设置面板的当前类型和服务ID。 NOTE:在打开编辑面板之前,需要先检查当前设备是否支持APN设置。如果不支持,则应禁用Edit选项。 |
addClickEventListener() | 给apnSettingsList中可见的APN添加click监听 apnSettingsList[i].addEventListener('click', browseApnItems); |
removeClickEventListener() | 移除可见apnSettingsList的click监听 |
initUI(panel) | 用于初始化 APN 设置面板的用户界面。可隐藏Message/A-GPS/Tethering类型的APN项。 1、从 ApiManager.connections 对象中获取与当前服务 ID 相对应的连接信息。 promises.push(conn.getSupportedNetworkTypes()); 4、在 Promise.all 方法的回调函数中,获取 getSupportedNetworkTypes 方法返回的值,即当前连接支持的网络类型列表。(Note:使用promise异步方法中获取支持的网络类型列表,首次进入界面可能会存在延迟) Promise.all(promises).then(values => {}); 5、获取界面中 ID 为“ims”的元素,并检查该元素是否存在。如果存在,则根据当前连接支持的网络类型决定是否显示该元素。 |
SettingsPanel() | createApnSettingsPanel最后return SettingsPanel()。 实现了onInit(panel)、onBeforeShow(panel, options)和onHide()接口。 1、onInit:数据查询apnSettingsList = panel.querySelectorAll('a[data-apn-type]'); 2、onBeforeShow:调用initSoftKey和initUI初始化界面,并添加监听addClickEventListener() 3、onHide():调用removeClickEventListener()移除监听 |
apn_settings.js
(三)【功能】增删查改 APN:apn_settings_manager.js
otegaia/apps/settings/js/modules/apn/apn_settings_manager.jsN
Note:JS接口可以形参和实参个数不同,如果只输入一个参数就是第一个参数,第二个参数默认是undefined。
apn_settings_manager.js接口 ApnSettingsManager.prototype = | 功能 |
ready(serviceId) | Ensures the current apn items are up-to-date. When the current plmn does not equal to the cached plmn, we should restore the apn items. 确保当前 apn 项目是最新的。当当前 plmn 不等于缓存的 plmn 时,我们应该恢复 apn 项。 |
addObservers(serviceId) | Register default APN changed observer 注册default APN观察者。 |
getPlmnAndMvnoInfo(serviceId) | Get current mcc/mnc information 获取当前卡MCCMNC信息。 |
deriveActiveApnIdFromItems(serviceId, apnType) | Returns the id of the first preset apn item. 返回第一个预设的 APN 项的ID。 |
deriveActiveApnIdFromSettings(serviceId, apnType) | Returns the id of the apn item that matches the current apn setting of the specified apn type. 从指定的 APN 类型的当前 APN 设置中返回匹配的 APN 项的 ID。 |
getApnAppliedType(serviceId, apnId) | Return the apn type an apn that is actively being used for. 返回正在使用的 APN 类型。 |
storeApnSettingByType(serviceId, apnType) | Store the current apn selection to apn settings to the settings database. 将当前 APN 选择的 APN 设置存储到设置数据库中。 |
apnList(serviceId) | Get the apn item list of a sim slot. 获取 SIM 卡槽的 APN 项列表。 |
apnItems(serviceId, apnType) | Get the apn items of an apn type for a sim slot. 获取 SIM 卡槽指定 APN 类型的 APN 项列表。 |
restoreApnItemsOfCategory(apnList, apnsForRestoring, category) | Restore the apn items of a category. |
getServiceIdMcc(serviceId) | Get current carrier mcc code by service Id |
restore(serviceId, mode) | Restore the apn items and apn settings to the default. Apn items of the category ApnItem.APN_CATEGORY.PRESET and ApnItem.APN_CATEGORY.EU are restored. User created apn items (custom apns) will be delete. Therestored. User created apn items (custom apns) will be delete. The preset apn items are from the apn.json database and client provisioning messages. 将 apn 项和 apn 设置恢复为默认值。类别 ApnItem.APN_CATEGORY 的 APN 项。PRESET 和 ApnItem.APN_CATEGORY.EU 已恢复。用户创建的 apn 项(自定义 apns)将被删除。那里存储。用户创建的 apn 项(自定义 apns)将被删除。预设的 apn 项来自 apn.json 数据库和客户端预配消息。 |
queryApns(serviceId, apnType) | 查询一张卡的APN。查询匹配 apn.json 数据库中的 mcc/mnc 代码和通过客户端配置消息接收到的代码的 APN 项。返回ApnItem数组。 |
addApn(serviceId, apn, category) | 向SIM卡新增一条APN,并指定 APN 项的分类。如果未指定分类,则默认将 APN 项分类为自定义。 |
removeApn(serviceId, id) | 删除一条APN |
updateApn(serviceId, id, apn) | 更新一条APN |
getActiveApnId(serviceId, apnType) | 获取正在激活使用的APN。 |
setActiveApnId(serviceId, apnType, id) | 将指定id的 APN 设置为使用APN。 |
定义名为ApnSettingsManager的类,包含多个属性(this)
/**
* @class ApnSettingsManager
* @requires module:modules/async_storage
* @requires module:modules/apn/apn_const
* @requires module:modules/apn/apn_utils
* @requires module:modules/apn/apn_item
* @requires module:modules/apn/apn_settings
* @requires module:modules/apn/apn_list
* @requires module:modules/apn/apn_selections
* @returns {ApnSettingsManager} 注释指出,该实例会被返回给调用者
*/
function ApnSettingsManager() {
this._apnLists = {}; //APN列表
this._apnSelections = ApnSelections(); //存储APN选择
this._apnSettings = ApnSettings(); //存储APN设置设置
//可自定义新增属性,如下:
//this.apnSelectedId = ''; //存储选中的APN的ID
this._readyPromises = {};
//客制化
//this.defaultApnSettingChanged = {};
//定义RESTORE_MODE只读属性,其值是常量RESTORE_MODE,是一个外部模块或变量的引用。
Object.defineProperty(this, 'RESTORE_MODE', {
configurable: false,
get: function() { //等同 get() {get() {
return RESTORE_MODE;
}
});
}
(四)【UI】APN编辑页:apn_editor
APN编辑页面 apn_editor/apn_editor.js
/**
* The apn editor module
*/
'use strict';
define(function(require) { //eslint-disable-line
const ApnEditorConst = require('panels/apn_editor/apn_editor_const');
const ApnEditorSession = require('panels/apn_editor/apn_editor_session');
const { APN_PROPERTIES } = ApnEditorConst;
const { APN_PROPERTY_DEFAULTS } = ApnEditorConst;
const { VALUE_CONVERTERS } = ApnEditorConst;
function ApnEditor(rootElement) {}
ApnEditor.prototype = {};
return function apnEditor(rootElement) {
return new ApnEditor(rootElement);
};
});
ApnEditor.prototype | 功能 |
convertValue | |
fillInputElements | 定制APN 每一项参数的可编辑性 |
createApn | |
editApn |
编辑业务功能 apn_editor/panels.js
gaia/apps/settings/js/panels/apn_editor/panel.js
//结构组成
'use strict';
define(function(require) { //eslint-disable-line
const SettingsPanel = require('modules/settings_panel');
const ApnSettingsManager = require('modules/apn/apn_settings_manager');
return function apnEditorPanel(){
const DM_PROTOCOL = 'dm.apnSettings.protocol';
//多个业务功能方法,主要都是编辑和保存功能
return SettingsPanel{};
};
});
五、用户功能
将apn_list和apn_settings融合定制
界面文字提示 | 功能介绍 |
Add APN | 左上功能键:添加APN |
Options | 右上功能键:菜单选项(包含编辑、删除、重置APN) |
Seleted | 上下键移动APN列表光标,点击ok可切换选中APN,右边单选按钮变化 |
六、其他
卡和运营商相关文件operator_variant
<!--gaia/shared/resources/apn/operator_variant.xml-->
<variant version="1">
<operator
name="Movistar"
mcc="214"
mnc="07"
enableStrict7BitEncodingForSms="true"
operatorSizeLimitation="512000"
/>
</variant>
apnItem结构
//提交APN信息的结构,apnItem
{
"itemId":"mlxfslzn2",
"itemCategory":"preset",
"itemApn":{
"apn":"tad",
"authtype":"notDefined",
"carrier":"tad",
"category":"custom",
"id":"zdd2mblgk",
"mmsc":"",
"mmsport":"",
"mmsproxy":"",
"password":"",
"port":"",
"protocol":"notDefined",
"proxy":"",
"roaming_protocol":"notDefined",
"types":["default"],
"user":""
}
}
itemId和iitemApn里面的id是不同的