Merge lp:~jonas-drange/ubuntu-system-settings/cellular-manual-carrier-persist-fix-1388044 into lp:ubuntu-system-settings

Proposed by Jonas G. Drange on 2014-11-12
Status: Merged
Approved by: Ken VanDine on 2014-12-12
Approved revision: 1193
Merged at revision: 1230
Proposed branch: lp:~jonas-drange/ubuntu-system-settings/cellular-manual-carrier-persist-fix-1388044
Merge into: lp:ubuntu-system-settings
Prerequisite: lp:~ken-vandine/ubuntu-system-settings/lp1390629
Diff against target: 950 lines (+491/-266)
9 files modified
plugins/cellular/CMakeLists.txt (+3/-1)
plugins/cellular/Components/MultiSim.qml (+3/-2)
plugins/cellular/Components/RadioSingleSim.qml (+5/-5)
plugins/cellular/Components/SingleSim.qml (+6/-17)
plugins/cellular/PageCarrierAndApn.qml (+67/-0)
plugins/cellular/PageCarriersAndApns.qml (+49/-20)
plugins/cellular/PageChooseCarrier.qml (+201/-180)
plugins/cellular/carriers.js (+106/-0)
tests/autopilot/ubuntu_system_settings/__init__.py (+51/-41)
To merge this branch: bzr merge lp:~jonas-drange/ubuntu-system-settings/cellular-manual-carrier-persist-fix-1388044
Reviewer Review Type Date Requested Status
Ken VanDine 2014-11-12 Approve on 2014-12-12
PS Jenkins bot continuous-integration 2014-11-12 Needs Fixing on 2014-12-11
Matthew Paul Thomas 2014-11-20 Pending
Review via email: mp+241541@code.launchpad.net

This proposal supersedes a proposal from 2014-11-05.

Commit message

[cellular/carriers] new Carrier & APN entry, as well as a major refactor of carrier selection code warranted by the design change.

Description of the change

This branch fixes multiple issues with carrier selection:
 * Adds "Searching" state per the design
 * Implements the new design where "Manual" registration mode is not an explicit setting, rather the state of not being "Automatic"
 * Refactors code, moving a lot of the carrier business logic out of the QML into its own JS file (see [1]).
 * Using a more declarative approach, which now is possible due to [1].

[1] https://code.launchpad.net/~jonas-drange/ubuntu-system-settings/cellular-manual-carrier-persist-fix-1388044/+merge/241541#diff-line-661

To post a comment you must log in.
Jonas G. Drange (jonas-drange) wrote :

Added comment re: carriers.js

Ken VanDine (ken-vandine) wrote :

See question inline

review: Needs Information
Jonas G. Drange (jonas-drange) wrote :

Setting to WIP to address comment.

1188. By Jonas G. Drange on 2014-12-01

merge trunk

1189. By Jonas G. Drange on 2014-12-02

avoid calling registration() on netop registration failure (use a ui state insteaD)

1190. By Jonas G. Drange on 2014-12-02

remove debug

1191. By Jonas G. Drange on 2014-12-05

avoid changing the strings

1192. By Jonas G. Drange on 2014-12-05

using modeldata now that the new string was scrubbed

Jonas G. Drange (jonas-drange) wrote :

1191—1192 are commits that avoids reverts string changes.

1193. By Jonas G. Drange on 2014-12-11

do not use flickable:null

Ken VanDine (ken-vandine) wrote :

Looks good, tested on mako 51

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/cellular/CMakeLists.txt'
2--- plugins/cellular/CMakeLists.txt 2014-08-27 09:17:50 +0000
3+++ plugins/cellular/CMakeLists.txt 2014-12-11 16:36:15 +0000
4@@ -4,10 +4,12 @@
5 install(FILES settings-cellular.svg DESTINATION ${PLUGIN_MANIFEST_DIR}/icons)
6
7 set(QML_SOURCES
8+ carriers.js
9 CustomApnEditor.qml
10 PageChooseApn.qml
11 PageChooseCarrier.qml
12- PageChooseCarriers.qml
13+ PageCarrierAndApn.qml
14+ PageCarriersAndApns.qml
15 PageComponent.qml
16 Hotspot.qml
17 HotspotSetup.qml
18
19=== modified file 'plugins/cellular/Components/MultiSim.qml'
20--- plugins/cellular/Components/MultiSim.qml 2014-12-08 15:59:50 +0000
21+++ plugins/cellular/Components/MultiSim.qml 2014-12-11 16:36:15 +0000
22@@ -44,6 +44,7 @@
23 anchors { left: parent.left; right: parent.right }
24 }
25
26+
27 ListItem.SingleValue {
28 text : i18n.tr("Hotspot disabled because Wi-Fi is off.")
29 visible: showAllUI && !hotspotItem.visible
30@@ -71,11 +72,11 @@
31 ListItem.SingleValue {
32 text: i18n.tr("Carriers")
33 id: chooseCarrier
34- objectName: "carriers"
35+ objectName: "carrierApnEntry"
36 progression: enabled
37 showDivider: false
38 onClicked: {
39- pageStack.push(Qt.resolvedUrl("../PageChooseCarriers.qml"), {
40+ pageStack.push(Qt.resolvedUrl("../PageCarriersAndApns.qml"), {
41 sims: sims
42 });
43 }
44
45=== modified file 'plugins/cellular/Components/RadioSingleSim.qml'
46--- plugins/cellular/Components/RadioSingleSim.qml 2014-10-31 18:14:35 +0000
47+++ plugins/cellular/Components/RadioSingleSim.qml 2014-12-11 16:36:15 +0000
48@@ -25,7 +25,7 @@
49 Column {
50 height: childrenRect.height
51
52- property alias selector: selector
53+ property bool enabled: sim.radioSettings.technologyPreference !== ""
54
55 SettingsItemTitle { text: i18n.tr("Connection type:") }
56
57@@ -36,8 +36,8 @@
58
59 // an empty string is not a valid preference, which means
60 // we disregard the interace and disable the selector
61- enabled: sim.radioSettings.technologyPreference !== ""
62- model: sim.radioSettings.modemTechnologies
63+ enabled: parent.enabled
64+ model: sim.radioSettings.modemTechnologies || []
65
66 delegate: OptionSelectorDelegate {
67 objectName: sim.path + "_radio_" + modelData
68@@ -50,10 +50,10 @@
69 }
70 showDivider: false
71 }
72- selectedIndex: model.indexOf(sim.radioSettings.technologyPreference)
73+ selectedIndex: model.length ?
74+ model.indexOf(sim.radioSettings.technologyPreference) : -1
75 onDelegateClicked: {
76 sim.radioSettings.technologyPreference = model[index];
77 }
78 }
79-
80 }
81
82=== modified file 'plugins/cellular/Components/SingleSim.qml'
83--- plugins/cellular/Components/SingleSim.qml 2014-11-12 19:26:36 +0000
84+++ plugins/cellular/Components/SingleSim.qml 2014-12-11 16:36:15 +0000
85@@ -78,13 +78,13 @@
86 }
87
88 ListItem.Divider {
89- visible: radio.selector.model.length
90+ visible: radio.visible
91 }
92
93 RadioSingleSim {
94 id: radio
95 anchors { left: parent.left; right: parent.right }
96- visible: radio.selector.model.length
97+ visible: radio.enabled
98 }
99
100 ListItem.Divider {}
101@@ -92,21 +92,10 @@
102 ListItem.SingleValue {
103 text: i18n.tr("Carrier");
104 id: chooseCarrier
105- objectName: "carrier"
106+ objectName: "carrierApnEntry"
107 progression: enabled
108- value: sim.netReg.name || i18n.tr("N/A")
109- enabled: sim.netReg.status !== ""
110- onClicked: {
111- pageStack.push(Qt.resolvedUrl("../PageChooseCarrier.qml"), {
112- sim: sim,
113- title: i18n.tr("Carrier")
114- })
115- }
116- }
117-
118- ListItem.Standard {
119- text: i18n.tr("APN")
120- progression: true
121- visible: showAllUI
122+ onClicked: pageStack.push(Qt.resolvedUrl("../PageCarrierAndApn.qml"), {
123+ sim: sim
124+ })
125 }
126 }
127
128=== added file 'plugins/cellular/PageCarrierAndApn.qml'
129--- plugins/cellular/PageCarrierAndApn.qml 1970-01-01 00:00:00 +0000
130+++ plugins/cellular/PageCarrierAndApn.qml 2014-12-11 16:36:15 +0000
131@@ -0,0 +1,67 @@
132+/*
133+ * Copyright (C) 2014 Canonical Ltd
134+ *
135+ * This program is free software: you can redistribute it and/or modify
136+ * it under the terms of the GNU General Public License version 3 as
137+ * published by the Free Software Foundation.
138+ *
139+ * This program is distributed in the hope that it will be useful,
140+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
141+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
142+ * GNU General Public License for more details.
143+ *
144+ * You should have received a copy of the GNU General Public License
145+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
146+ *
147+ * Authors:
148+ * Jonas G. Drange <jonas.drange@canonical.com>
149+ *
150+*/
151+import QtQuick 2.0
152+import SystemSettings 1.0
153+import Ubuntu.Components 0.1
154+import Ubuntu.Components.ListItems 0.1 as ListItem
155+
156+ItemPage {
157+ id: root
158+ title: i18n.tr("Carrier")
159+ objectName: "carrierApnPage"
160+ flickable: null
161+
162+ property var sim
163+
164+ Flickable {
165+ anchors.fill: parent
166+ contentWidth: parent.width
167+ contentHeight: contentItem.childrenRect.height
168+ boundsBehavior: (contentHeight > root.height) ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
169+
170+ Column {
171+ anchors.left: parent.left
172+ anchors.right: parent.right
173+
174+ ListItem.SingleValue {
175+ text: i18n.tr("Carrier")
176+ objectName: "carrier"
177+ value: sim.netReg.name ? sim.netReg.name : i18n.tr("None")
178+ enabled: (sim.netReg.status !== "") &&
179+ (sim.netReg.mode !== "auto-only")
180+ progression: enabled
181+ onClicked: pageStack.push(Qt.resolvedUrl("PageChooseCarrier.qml"), {
182+ sim: sim,
183+ title: i18n.tr("Carrier")
184+ })
185+ }
186+
187+ ListItem.Standard {
188+ text: i18n.tr("APN")
189+ objectName: "apn"
190+ progression: enabled
191+ enabled: sim.connMan.powered
192+ onClicked: pageStack.push(Qt.resolvedUrl("PageChooseApn.qml"), {
193+ sim: sim
194+ })
195+ }
196+ }
197+ }
198+}
199
200=== renamed file 'plugins/cellular/PageChooseCarriers.qml' => 'plugins/cellular/PageCarriersAndApns.qml'
201--- plugins/cellular/PageChooseCarriers.qml 2014-10-31 18:14:35 +0000
202+++ plugins/cellular/PageCarriersAndApns.qml 2014-12-11 16:36:15 +0000
203@@ -25,7 +25,8 @@
204 ItemPage {
205 id: root
206 title: i18n.tr("Carriers")
207- objectName: "chooseCarriersPage"
208+ objectName: "carrierApnPage"
209+ flickable: null
210
211 property var sims
212
213@@ -39,30 +40,58 @@
214 anchors.left: parent.left
215 anchors.right: parent.right
216
217- ListItem.Standard {
218+ SettingsItemTitle {
219+ text: sims[0].title
220+ }
221+
222+ ListItem.SingleValue {
223+ text: i18n.tr("Carrier")
224 objectName: sims[0].path + "_carriers"
225- text: sims[0].netReg.name ? sims[0].netReg.name : i18n.tr("N/A")
226- enabled: sims[0].netReg.status !== ""
227- progression: true
228- onClicked: {
229- pageStack.push(Qt.resolvedUrl("PageChooseCarrier.qml"), {
230- sim: sims[0],
231- title: sims[0].title
232- })
233- }
234+ value: sims[0].netReg.name ? sims[0].netReg.name :
235+ i18n.tr("None")
236+ enabled: (sims[0].netReg.status !== "") &&
237+ (sims[0].netReg.mode !== "auto-only")
238+ progression: enabled
239+ onClicked: pageStack.push(Qt.resolvedUrl("PageChooseCarrier.qml"), {
240+ sim: sims[0],
241+ title: sims[0].title
242+ })
243 }
244
245 ListItem.Standard {
246+ text: i18n.tr("APN")
247+ progression: enabled
248+ enabled: sims[0].connMan.powered
249+ onClicked: pageStack.push(Qt.resolvedUrl("PageChooseApn.qml"), {
250+ sim: sims[0]
251+ })
252+ }
253+
254+ SettingsItemTitle {
255+ text: sims[1].title
256+ }
257+
258+ ListItem.SingleValue {
259+ text: i18n.tr("Carrier")
260 objectName: sims[1].path + "_carriers"
261- text: sims[1].netReg.name ? sims[1].netReg.name : i18n.tr("N/A")
262- enabled: sims[1].netReg.status !== ""
263- progression: true
264- onClicked: {
265- pageStack.push(Qt.resolvedUrl("PageChooseCarrier.qml"), {
266- sim: sims[1],
267- title: sims[1].title
268- })
269- }
270+ value: sims[1].netReg.name ? sims[1].netReg.name :
271+ i18n.tr("None")
272+ enabled: (sims[1].netReg.status !== "") &&
273+ (sims[1].netReg.mode !== "auto-only")
274+ progression: enabled
275+ onClicked: pageStack.push(Qt.resolvedUrl("PageChooseCarrier.qml"), {
276+ sim: sims[1],
277+ title: sims[1].title
278+ })
279+ }
280+
281+ ListItem.Standard {
282+ text: i18n.tr("APN")
283+ progression: enabled
284+ enabled: sims[1].connMan.powered
285+ onClicked: pageStack.push(Qt.resolvedUrl("PageChooseApn.qml"), {
286+ sim: sims[1]
287+ })
288 }
289 }
290 }
291
292=== modified file 'plugins/cellular/PageChooseCarrier.qml'
293--- plugins/cellular/PageChooseCarrier.qml 2014-10-31 18:14:35 +0000
294+++ plugins/cellular/PageChooseCarrier.qml 2014-12-11 16:36:15 +0000
295@@ -4,6 +4,7 @@
296 * Copyright (C) 2013 Canonical Ltd.
297 *
298 * Contact: Iain Lane <iain.lane@canonical.com>
299+ * Jonas G. Drange <jonas.drange@canonical.com>
300 *
301 * This program is free software: you can redistribute it and/or modify it
302 * under the terms of the GNU General Public License version 3, as published
303@@ -24,215 +25,235 @@
304 import Ubuntu.Components 1.1
305 import Ubuntu.Components.ListItems 1.0 as ListItem
306 import MeeGo.QOfono 0.2
307+import "carriers.js" as CHelper
308
309 ItemPage {
310 id: root
311 title: i18n.tr("Carrier")
312-
313 objectName: "chooseCarrierPage"
314+ flickable: null
315
316 property var sim
317-
318- property variant operatorNames
319- property int mode
320-
321- QtObject {
322- id: d
323- property bool __suppressActivation : true;
324- }
325-
326- Component.onCompleted: {
327- updateNetworkOperators();
328- }
329-
330- Connections {
331- target: sim.netReg
332- onNetworkOperatorsChanged: updateNetworkOperators();
333- onCurrentOperatorPathChanged: buildLists();
334- }
335-
336- // map of operatorPath : netOp
337- property var operators: ({})
338- function updateNetworkOperators()
339- {
340- var tmp = sim.netReg.networkOperators;
341- var added = tmp.filter(function(i) {
342- return operators[i] === undefined;
343- });
344- var removed = Object.keys(operators).filter(function(i) {
345- return tmp.indexOf(i) === -1;
346- })
347-
348- removed.forEach(function(currentValue, index, array) {
349- // just asserting to verify the logic
350- // remove once proven functional
351- if (operators[currentValue] === undefined) {
352- throw "updateNetworkOperators: removed is broken";
353- }
354-
355- operators[currentValue].destroy();
356- delete operators[currentValue];
357- });
358-
359- added.forEach(function(currentValue, index, array) {
360- // just asserting to verify the logic
361- // remove once proven functional
362- if (operators[currentValue] !== undefined) {
363- throw "updateNetworkOperators: added is broken";
364- }
365-
366- operators[currentValue] = netOp.createObject(parent,
367- {
368- "operatorPath": currentValue
369- });
370- });
371-
372- // just asserting to verify the logic
373- // remove once proven functional
374- if (Object.keys(operators).length !== tmp.length) {
375- throw "Object.keys(operators).length !== tmp.length";
376- }
377- tmp.forEach(function(currentValue, index, array) {
378- if (operators[currentValue] === undefined)
379- throw "operators[currentValue] === undefined";
380- });
381-
382- buildLists();
383- }
384-
385- function buildLists()
386- {
387- d.__suppressActivation = true;
388- var oN = new Array();
389-
390- for (var i in operators) {
391- var tempOp = operators[i];
392- if (tempOp.status === "forbidden")
393- continue
394- oN.push(tempOp.name);
395- }
396- operatorNames = oN;
397-
398- var cur = operators[sim.netReg.currentOperatorPath];
399- carrierSelector.selectedIndex = cur === undefined ? -1 : operatorNames.indexOf(cur.name);
400- d.__suppressActivation = false;
401- }
402+ property bool scanning: true
403+ property bool working: false
404+
405+ states: [
406+ State {
407+ name: "auto"
408+ when: sim.netReg.mode === "auto"
409+ PropertyChanges {
410+ target: otherOperators
411+ selectedIndex: -1
412+ }
413+ PropertyChanges {
414+ target: allOperators
415+ height: 0
416+ opacity: 0
417+ }
418+ },
419+ State {
420+ name: "manual"
421+ when: sim.netReg.mode === "manual"
422+ PropertyChanges {
423+ target: otherOperators
424+ height: 0
425+ opacity: 0
426+ }
427+ }
428+ /* Note that we do not consider auto-only since this page is not
429+ reachable in that case (see Carrier & APN page). */
430+ ]
431
432 Component {
433 id: netOp
434 OfonoNetworkOperator {
435 onRegisterComplete: {
436- if (error === OfonoNetworkOperator.InProgressError) {
437- console.warn("registerComplete failed with error: " + errorString);
438- } else if (error !== OfonoNetworkOperator.NoError) {
439- console.warn("registerComplete failed with error: " + errorString + " Falling back to default");
440+ if (error !== OfonoNetworkOperator.NoError) {
441+ console.warn("Register complete:", errorString);
442+ console.warn("Falling back to default operator.");
443 sim.netReg.registration();
444 }
445- }
446- onNameChanged: buildLists();
447- onStatusChanged: buildLists();
448- }
449- }
450+ working = false;
451+ }
452+ }
453+ }
454+
455+ Connections {
456+ /* The following is a hack: If a scan is in progress and we call
457+ scan() on netReg, it emits a scanError _and_ scanFinished.
458+ We look at the scanError message, set this property to true if
459+ it says a scan is in progress. This way we can ignore the bad
460+ scanFinished signal. Filed bug against libqofono[1].
461+ [1] https://github.com/nemomobile/libqofono/issues/52
462+ */
463+ property bool __scanInProgress: false
464+
465+ target: sim.netReg
466+ onScanFinished: {
467+ if (scanning && !__scanInProgress) {
468+ scanning = false;
469+ }
470+ __scanInProgress = false;
471+ }
472+ onScanError: {
473+ if (message === "Operation already in progress") {
474+ console.warn('A scan was already in progress.');
475+ __scanInProgress = true;
476+ } else {
477+ scanning = false;
478+ __scanInProgress = false;
479+ console.warn("onScanError: " + message);
480+ }
481+ }
482+ onModeChanged: modeSelector.selectedIndex = (mode === "auto") ? 0 : -1
483+ }
484+
485+ Component.onCompleted: sim.netReg.scan()
486
487 Flickable {
488 id: scrollWidget
489 anchors.fill: parent
490 contentWidth: parent.width
491- contentHeight: parent.height
492- boundsBehavior: (contentHeight > parent.height) ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
493+ contentHeight: contentItem.childrenRect.height
494+ boundsBehavior: (contentHeight > root.height) ?
495+ Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
496
497- ColumnLayout {
498+ Column {
499 anchors {
500 left: parent.left
501 right: parent.right
502 }
503-
504- SettingsItemTitle { text: i18n.tr("Choose carrier:") }
505+ spacing: 0
506+
507+ SettingsItemTitle {
508+ text: i18n.tr("Carrier")
509+
510+ ActivityIndicator {
511+ anchors {
512+ right: parent.right
513+ top: parent.top
514+ margins: units.gu(1.5)
515+ }
516+ running: true
517+ opacity: scanning || working ? 1 : 0
518+ Behavior on opacity {
519+ NumberAnimation {
520+ duration: UbuntuAnimation.SnapDuration
521+ }
522+ }
523+ }
524+ }
525
526 ListItem.ItemSelector {
527- id: chooseCarrier
528+ id: modeSelector
529 objectName: "mode"
530 expanded: true
531- enabled: sim.netReg.mode !== "auto-only"
532- model: [i18n.tr("Automatically"), i18n.tr("Manually")]
533-
534- delegate: OptionSelectorDelegate { showDivider: false }
535- selectedIndex: sim.netReg.mode === "manual" ? 1 : 0
536-
537- // we only want to do this per user input
538+ enabled: !scanning && !working
539+ opacity: enabled ? 1 : 0.5
540+ model: [i18n.tr("Automatically")]
541+ delegate: OptionSelectorDelegate {
542+ text: {
543+ // modelData is "Automatically"
544+ if (sim.netReg.mode === "auto") {
545+ return sim.netReg.name ?
546+ modelData + " [ " + sim.netReg.name + " ]" :
547+ modelData
548+ } else {
549+ return modelData;
550+ }
551+ }
552+ showDivider: false
553+ }
554+ selectedIndex: sim.netReg.mode === "auto" ? 0 : -1
555+
556+ /* When registration() fails, the UI state may end up in an
557+ undefined state. Issue[1] has been filed against libqofono.
558+ [1] https://github.com/nemomobile/libqofono/issues/54
559+ */
560+ onDelegateClicked: sim.netReg.registration()
561+ }
562+
563+ // In manual mode, all non-forbidden operators are shown.
564+ ListItem.ItemSelector {
565+ id: allOperators
566+ objectName: "allOperators"
567+ expanded: true
568+ enabled: !scanning && !working
569+ opacity: enabled ? 1 : 0.5
570+ model: CHelper.getOps(sim.netReg.networkOperators)
571+ delegate: OptionSelectorDelegate {
572+ objectName: "carrier"
573+ showDivider: false
574+ text: modelData.name
575+ }
576+ onDelegateClicked: {
577+ CHelper.setOp(model[index].operatorPath);
578+ working = true;
579+ }
580+ selectedIndex: {
581+ var curop = sim.netReg.currentOperatorPath;
582+ return model.indexOf(CHelper.getOrCreateOpQml(curop));
583+ }
584+
585+ Behavior on height {
586+ NumberAnimation {
587+ duration: UbuntuAnimation.SnapDuration
588+ }
589+ }
590+
591+ Behavior on opacity {
592+ NumberAnimation {
593+ duration: UbuntuAnimation.SnapDuration
594+ }
595+ }
596+ }
597+
598+ /* When registration mode is "Automatic", this list contains all
599+ the non-forbidden operators except the current one. When the user
600+ taps one of the elements in this selector, it will be hidden,
601+ and the mode will switch to "Manual". */
602+ ListItem.ItemSelector {
603+ id: otherOperators
604+ objectName: "otherOperators"
605+ expanded: true
606+ enabled: !scanning && !working
607+ opacity: enabled ? 1 : 0.5
608+ model: CHelper.getOps(sim.netReg.networkOperators,
609+ [sim.netReg.currentOperatorPath])
610+ delegate: OptionSelectorDelegate {
611+ objectName: "carrier"
612+ showDivider: false
613+ text: modelData.name
614+ }
615+ onDelegateClicked: {
616+ var clickedOp = model[index].operatorPath;
617+ CHelper.setOp(clickedOp);
618+
619+ // Update immediately and do not wait for netReg
620+ allOperators.selectedIndex = allOperators.model.indexOf(
621+ CHelper.getOrCreateOpQml(clickedOp));
622+ working = true;
623+ }
624 onSelectedIndexChanged: {
625- if (selectedIndex === -1 || d.__suppressActivation)
626- return;
627-
628- if (selectedIndex === 0) {
629- sim.netReg.registration();
630- } else if (selectedIndex === 1) {
631- if (sim.netReg.status !== "searching")
632- sim.netReg.scan();
633- }
634- }
635- }
636-
637- ListItem.SingleControl {
638- enabled: chooseCarrier.selectedIndex === 1
639- anchors {
640- left: parent.left
641- leftMargin: units.gu(0)
642- }
643- control: ColumnLayout {
644- id: child
645- width: parent.width - units.gu(4)
646- anchors.left: parent.left
647- RowLayout {
648- id: searchingRow
649- spacing: units.gu(1)
650-
651- visible: sim.netReg.status === "searching"
652- ActivityIndicator {
653- id: activityIndicator
654- anchors.verticalCenter: parent.verticalCenter
655- running: parent.visible
656- }
657- Label {
658- anchors.verticalCenter: parent.verticalCenter
659- text: i18n.tr("Searching for carriers…")
660- }
661- }
662- ListItem.ItemSelector {
663- id: carrierSelector
664- objectName: "carriers"
665- expanded: true
666- enabled: sim.netReg.status !== "searching" && chooseCarrier.selectedIndex === 1
667- // work around ItemSelector not having a visual change depending on being disabled
668- opacity: enabled ? 1.0 : 0.5
669- width: parent.width
670- model: operatorNames
671- delegate: OptionSelectorDelegate { enabled: carrierSelector.enabled; showDivider: false }
672- onSelectedIndexChanged: {
673- if (selectedIndex === -1 || d.__suppressActivation)
674- return;
675-
676- // this assumes operator names are unique,
677- // revise if not so
678- for (var op in operators) {
679- if (operators[op].name === operatorNames[selectedIndex]) {
680- operators[op].registerOperator();
681- return;
682- }
683- }
684- // just asserting to verify the logic
685- // remove once proven functional
686- throw "should not be reached.";
687- }
688- }
689- }
690- }
691- ListItem.Standard {
692- text: i18n.tr("APN")
693- progression: true
694- enabled: sim.connMan.powered
695- onClicked: {
696- pageStack.push(Qt.resolvedUrl("PageChooseApn.qml"), {sim: sim})
697+ /* When e.g. the model changes, the selectedIndex is set to
698+ 0. Ignore this, since we never want the selectedIndex to be
699+ anything other than -1. This component is shown only when
700+ registration is "Automatic". */
701+ if (selectedIndex >= 0) {
702+ selectedIndex = -1;
703+ }
704+ }
705+
706+ Behavior on height {
707+ NumberAnimation {
708+ duration: UbuntuAnimation.SnapDuration
709+ }
710+ }
711+
712+ Behavior on opacity {
713+ NumberAnimation {
714+ duration: UbuntuAnimation.SnapDuration
715+ }
716 }
717 }
718 }
719
720=== added file 'plugins/cellular/carriers.js'
721--- plugins/cellular/carriers.js 1970-01-01 00:00:00 +0000
722+++ plugins/cellular/carriers.js 2014-12-11 16:36:15 +0000
723@@ -0,0 +1,106 @@
724+// Map of path to OfonoNetworkOperator objects
725+var _pathToQml = {}
726+
727+/*
728+Given an array of paths, it will create and associate
729+an OfonoNetworkOperator QML object for each new path.
730+
731+It will also delete any QML that is not in given list of paths.
732+
733+@param {Array} paths - Array of operator paths
734+@return {undefined}
735+*/
736+function updateOperatorQML (paths) {
737+ _garbageCollect(paths);
738+ _createQml(paths);
739+}
740+
741+function _garbageCollect (paths) {
742+ var path;
743+ for (path in _pathToQml) {
744+ if (_pathToQml.hasOwnProperty(path)) {
745+ if (paths.indexOf(path) === -1) {
746+ _pathToQml[path].destroy();
747+ delete _pathToQml[path];
748+ }
749+ }
750+ }
751+}
752+
753+function _createQml (paths) {
754+ paths.forEach(function (path, i) {
755+ if (!_pathToQml.hasOwnProperty(path)) {
756+ _pathToQml[path] = netOp.createObject(root, {
757+ 'operatorPath': path
758+ });
759+ }
760+ });
761+}
762+
763+/*
764+Takes a list of paths and returns
765+OfonoNetworkOperator objects for each path.
766+
767+This function will create OfonoNetworkOperator
768+objects.
769+
770+@param {Array} paths - Array of operator paths
771+@param {Array} ignore - Array of operator paths to ignore
772+@return {Array} of OfonoNetworkOperators
773+*/
774+function getOps (paths, ignore) {
775+ var ret = [];
776+ ignore = ignore || [];
777+ paths.forEach(function (op) {
778+ var ofonoOp = getOrCreateOpQml(op);
779+ if (ignore.indexOf(op) >= 0) {
780+ return;
781+ } else if (ofonoOp.status === "forbidden") {
782+ return;
783+ }
784+ ret.push(ofonoOp);
785+ });
786+ return ret;
787+}
788+
789+/*
790+@param path String an operator path
791+@return {Object|null} OfonoNetworkOperator|null - null if no QML exist for path
792+*/
793+function getOp (path) {
794+ if (_pathToQml.hasOwnProperty(path)) {
795+ return _pathToQml[path];
796+ } else {
797+ return null;
798+ }
799+}
800+
801+/*
802+Returns an operator. Before returning it sees
803+if we have created QML for this operator path
804+before. QML is created if not.
805+
806+It is guaranteed that a QML object will be returned.
807+
808+@param {String} path - an operator path
809+@return {Object} OfonoNetworkOperator - the created qml
810+*/
811+function getOrCreateOpQml (path) {
812+ if (getOp(path)) {
813+ return getOp(path);
814+ } else {
815+ _createQml([path]);
816+ return getOp(path);
817+ }
818+}
819+
820+/*
821+Registers operator on path
822+
823+@param {String} path - operator to register
824+@return {undefined}
825+*/
826+function setOp (path) {
827+ var op = getOrCreateOpQml(path);
828+ op.registerOperator();
829+}
830
831=== modified file 'tests/autopilot/ubuntu_system_settings/__init__.py'
832--- tests/autopilot/ubuntu_system_settings/__init__.py 2014-10-31 14:52:37 +0000
833+++ tests/autopilot/ubuntu_system_settings/__init__.py 2014-12-11 16:36:15 +0000
834@@ -277,28 +277,16 @@
835
836 @autopilot.logging.log_action(logger.debug)
837 def change_carrier(self, carrier, sim=None):
838- if sim:
839- carriersPage = self._click_carriers()
840- carrierPage = carriersPage.select_sim(sim)
841- else:
842- carrierPage = self._click_carrier()
843-
844- carrierPage.set_manual()
845- carrierPage.set_carrier(carrier)
846-
847- @autopilot.logging.log_action(logger.debug)
848- def _click_carrier(self):
849- item = self.select_single(objectName='carrier')
850- self.pointing_device.click_object(item)
851- return self.get_root_instance().wait_select_single(
852- objectName='chooseCarrierPage')
853-
854- @autopilot.logging.log_action(logger.debug)
855- def _click_carriers(self):
856- item = self.select_single(objectName='carriers')
857- self.pointing_device.click_object(item)
858- return self.get_root_instance().wait_select_single(
859- objectName='chooseCarriersPage')
860+ carrierApnPage = self._click_carrier_apn()
861+ chooseCarrierPage = carrierApnPage.open_carrier(sim)
862+ chooseCarrierPage.set_carrier(carrier)
863+
864+ @autopilot.logging.log_action(logger.debug)
865+ def _click_carrier_apn(self):
866+ item = self.select_single(objectName='carrierApnEntry')
867+ self.pointing_device.click_object(item)
868+ return self.get_root_instance().wait_select_single(
869+ objectName='carrierApnPage')
870
871 @autopilot.logging.log_action(logger.debug)
872 def select_sim_for_data(self, sim):
873@@ -340,30 +328,42 @@
874 self.pointing_device.click_object(ok)
875
876
877-class PageChooseCarriers(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
878-
879- """Autopilot helper for carrier selection page (multisim)."""
880-
881- @autopilot.logging.log_action(logger.debug)
882- def select_sim(self, sim):
883- return self._select_sim(sim)
884-
885- @autopilot.logging.log_action(logger.debug)
886- def _select_sim(self, sim):
887- item = self.select_single(objectName='%s_carriers' % sim)
888- self.pointing_device.click_object(item)
889+class PageCarrierAndApn(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
890+
891+ """Autopilot helper for carrier/apn entry page (singlesim)."""
892+ @autopilot.logging.log_action(logger.debug)
893+ def open_carrier(self, sim):
894+ return self._click_carrier(sim)
895+
896+ @autopilot.logging.log_action(logger.debug)
897+ def _click_carrier(self, sim):
898+ obj = self.select_single(
899+ objectName='carrier')
900+ self.pointing_device.click_object(obj)
901+ return self.get_root_instance().wait_select_single(
902+ objectName='chooseCarrierPage')
903+
904+
905+class PageCarriersAndApns(
906+ ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
907+ """Autopilot helper for carrier/apn entry page (multisim)."""
908+ """Autopilot helper for carrier/apn entry page (singlesim)."""
909+ @autopilot.logging.log_action(logger.debug)
910+ def open_carrier(self, sim):
911+ return self._click_carrier(sim)
912+
913+ @autopilot.logging.log_action(logger.debug)
914+ def _click_carrier(self, sim):
915+ obj = self.select_single(
916+ objectName='%s_carriers' % sim)
917+ self.pointing_device.click_object(obj)
918 return self.get_root_instance().wait_select_single(
919 objectName='chooseCarrierPage')
920
921
922 class PageChooseCarrier(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
923
924- """Autopilot helper for carrier selection page (singlesim)."""
925-
926- @autopilot.logging.log_action(logger.debug)
927- def set_manual(self):
928- item = self.select_single(text='Manually')
929- self.pointing_device.click_object(item)
930+ """Autopilot helper for carrier selection page"""
931
932 @autopilot.logging.log_action(logger.debug)
933 def set_automatic(self):
934@@ -374,7 +374,17 @@
935 # wait for animation, since page.animationRunning.wait_for(False)
936 # does not work?
937 sleep(0.5)
938- item = self.select_single(text=carrier)
939+ allOperators = self.select_single(objectName="allOperators")
940+ otherOperators = self.select_single(objectName="otherOperators")
941+
942+ if allOperators.visible:
943+ opList = allOperators
944+ elif otherOperators.visible:
945+ opList = otherOperators
946+ else:
947+ raise Exception("No operator list visible.")
948+
949+ item = opList.select_single(text=carrier, objectName="carrier")
950 self.pointing_device.click_object(item)
951
952

Subscribers

People subscribed via source and target branches