Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions src/qml/WiFiConnectionDialog.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*
* Copyright (C) 2023 - Arseniy Movshev <[email protected]>
* 2022 - Ed Beroset <github.com/beroset>
* 2017-2022 - Chupligin Sergey <[email protected]>
* 2021 - Darrel Griët <[email protected]>
* 2016 - Sylvia van Os <[email protected]>
* 2015 - Florent Revest <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* A large proportion of this code has been referenced from Nemomobile-UX Glacier-Settings, published at https://github.com/nemomobile-ux/glacier-settings/blob/master/src/plugins/wifi/WifiSettings.qml
*/

import QtQuick 2.9
import org.asteroid.controls 1.0
import org.asteroid.utils 1.0
import Connman 0.2
import QtQuick.VirtualKeyboard 2.4

Item {
id: dialogItem
property var modelData

property real rowHeight: Dims.h(25)
property real rowMargin: Dims.w(15)

InputPanel {
id: inputPanel
z: 99
visible: active
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: -Dims.h(25)
height: Dims.h(100)

width: Dims.w(100)
externalLanguageSwitchEnabled: false
}

UserAgent {
id: userAgent
onUserInputRequested: {
var view = {
"fields": []
}
for (var key in fields) {
view.fields.push({
"name": key,
"id": key.toLowerCase(),
"type": fields[key]["Type"],
"requirement": fields[key]["Requirement"]
})
console.log(key + ":")
for (var inkey in fields[key]) {
console.log(" " + inkey + ": " + fields[key][inkey])
}
}
userAgent.sendUserReply({"Passphrase": passphraseField.text})
}

onErrorReported: {
console.log("Got error from model: " + error)
failDialog.subLabelText = error
failDialog.open()
}
}
Connections {
target: modelData
function onConnectRequestFailed(error) {
console.log(error)
}

function onConnectedChanged(connected) {
if(connected) {
layerStack.pop(layerStack.currentLayer)
}
}
}
Flickable {
anchors.fill: parent
contentHeight: contentColumn.implicitHeight
Column {
id: contentColumn
width: parent.width
Item {height: Dims.h(15); width: parent.width}
Marquee {
anchors {
left: parent.left
right: parent.right
leftMargin: dialogItem.rowMargin
rightMargin: dialogItem.rowMargin
}
text: modelData.name
font.pixelSize: Dims.l(6)
height: Dims.l(8)
}
Column {
id: loginFieldsColumn
visible: !(modelData.connected || modelData.favorite)
anchors {
left: parent.left
right: parent.right
leftMargin: dialogItem.rowMargin
rightMargin: dialogItem.rowMargin
}
Label {
id: identityLabel
text: qsTrId("id-wifi-login")+":"
font.pixelSize: Dims.l(6)
visible: modelData.securityType === NetworkService.SecurityIEEE802
}

TextField {
id: identityField
text: modelData.identity
width: parent.width
visible: modelData.securityType === NetworkService.SecurityIEEE802
}

Label {
id: passphraseLabel
text: qsTrId("id-wifi-password")+":"
font.pixelSize: Dims.l(6)
visible: !(modelData.securityType == NetworkService.SecurityNone)
}

TextField {
id: passphraseField
text: modelData.passphrase
echoMode: TextInput.Password //smartwatches are hard to type on. it is worth adding a 'show password' button for this field
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delegating all UX decisions to @eLtMosen but I don't think we gain very much from hiding the password here. Imho these password placeholders are for situation where the user is sitting in a crowded space and types a password that they don't want anyone around to see. I don't really see this happening with a user typing their Wi-Fi passphrase once on their watch. I don't feel particularly strongly either way but this echoMode feels a bit overkill to me.

width: parent.width
height: dialogItem.rowHeight
visible: !(modelData.securityType == NetworkService.SecurityNone)
}
}
Column {
visible: modelData.connected
width: parent.width
Label {
anchors {
left: parent.left
right: parent.right
leftMargin: dialogItem.rowMargin
rightMargin: dialogItem.rowMargin
}
text: "IP: " + modelData.ipv4["Address"]
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Dims.l(6)
}
LabeledActionButton {
width: parent.width
height: dialogItem.rowHeight
text: qsTrId("id-wifi-disconnect")
icon: "ios-close-circle-outline"
onClicked: {
modelData.requestDisconnect()
layerStack.pop(layerStack.currentLayer)
}
}
}
LabeledSwitch {
id: autoConnectCheckBox
width: parent.width
height: dialogItem.rowHeight
text: qsTrId("id-wifi-autoconnect")
checked: modelData.autoConnect
}
LabeledActionButton {
visible: modelData.connected || modelData.favorite
width: parent.width
height: dialogItem.rowHeight
text: qsTrId("id-wifi-removenetwork")
icon: "ios-remove-circle-outline"
onClicked: {
modelData.remove()
layerStack.pop(layerStack.currentLayer)
}
}

IconButton {
iconName: "ios-checkmark-circle-outline"
height: width
width: Dims.w(20)
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
if(!modelData.connected) {
modelData.passphrase = passphraseField.text
modelData.identity = identityField.text
}
modelData.autoConnect = autoConnectCheckBox.checked
modelData.requestConnect()
}
}
}
}
}
149 changes: 149 additions & 0 deletions src/qml/WiFiPage.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright (C) 2023 - Arseniy Movshev <[email protected]>
* 2017-2022 - Chupligin Sergey <[email protected]>
* 2021 - Darrel Griët <[email protected]>
* 2016 - Sylvia van Os <[email protected]>
* 2015 - Florent Revest <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* A large proportion of this code has been referenced from Nemomobile-UX Glacier-Settings, published at https://github.com/nemomobile-ux/glacier-settings/blob/master/src/plugins/wifi/WifiSettings.qml
*/

import QtQuick 2.9
import org.asteroid.controls 1.0
import org.asteroid.utils 1.0
import Connman 0.2
import QtQuick.VirtualKeyboard 2.4

Item {
id: root
TechnologyModel {
id: wifiModel
name: "wifi"
onCountChanged: {
console.log("COUNT CHANGE " + count)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is a draft but since I'm at it, let me think out loud. I believe we should try to keep the console.logs that make it to production ~minimal and "to the point". There's a sweet spot between not providing any debug info in our logs (basically the current situation) and drowning useful information in tons of logs (what would happen if we start to do this sort of things)

}
onScanRequestFinished: {
console.log("SCAN FINISH")
}
}

NetworkTechnology {
id: wifiStatus
path: "/net/connman/technology/wifi"
onPoweredChanged: {
console.log("POWER CHANGE ") + powered
if (powered)
wifiModel.requestScan()
}
}

ListView {
id: wifiList
model: wifiModel
width: parent.width*0.7
anchors.horizontalCenter: parent.horizontalCenter
height: parent.height
header: Item {
//this is literally a statuspage
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked at the exact diff but any reason why you're not re-using/extending the existing statuspage instead ?
Not having had time to look into it yet is a valid question, I'm just curious

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, the comment here is old and misleading. Basically, this is/was the visual components of a statuspage, but they're then moved and resized to make way for the rest of the menu items when wifi is enabled. I'll add this to my cleanup todo list.

width: root.width
height: wifiStatus.powered ? width*0.6 : root.height
Behavior on height { NumberAnimation { duration: 100 } }
anchors.horizontalCenter: parent.horizontalCenter
Rectangle {
id: statusIconBackground
anchors.centerIn: parent
anchors.verticalCenterOffset: -parent.width*0.13
color: "black"
radius: width/2
opacity: wifiStatus.powered ? 0.4 : 0.2
width: parent.width*0.25
height: width
Icon {
id: statusIcon
anchors.fill: statusIconBackground
anchors.margins: parent.width*0.12
name: wifiStatus.powered ? "ios-wifi" : "ios-wifi-outline"
}
MouseArea {
id: statusMA
enabled: true
anchors.fill: parent
onClicked: wifiStatus.powered = !wifiStatus.powered
}
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spacing between components isn't consistent with sometimes 2 spaces, sometimes 1, sometimes 0.

The QML convention seems to be to leave 1 empty line between two consecutive blocks at the same depth. I believe that @eLtMosen uses a QML linter. Whatever canonical coding style is outputted by a sane QML linter should be our project-wide default.

Label {
id: statusLabel
font.pixelSize: parent.width*0.07
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
wrapMode: Text.Wrap
anchors.left: parent.left; anchors.right: parent.right
anchors.leftMargin: parent.width*0.04; anchors.rightMargin: anchors.leftMargin
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: parent.width*0.15
text: "<h3>" + (wifiStatus.powered ? qsTrId("id-wifi-on"): qsTrId("id-wifi-off")) + "</h3>\n" + (wifiStatus.connected ? qsTrId("id-wifi-connected") : qsTrId("id-wifi-disconnected"))
}
}

footer: Item {height: wifiStatus.powered ? root.height*0.15 : 0; width: parent.width}

delegate: MouseArea {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should use a https://github.com/AsteroidOS/qml-asteroid/blob/master/src/controls/qml/ListItem.qml here instead of re-inventing that wheel.

We can also have the ListItem use a Marquee, I think it's a sane default anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does show two bits of text instead of one, and doesn't make use of an icon, so ListItem isn't relevant here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

qml-asteroid is not set in stone. We can expand ListItem to handle a primary and a secondary text and no icon.

The reason why I think this is relevant is that this sort of code factorization also keeps the design consistent across the OS. For example, ListItem has a HighlightBar that isn't used here. The more we factorize code the better it is to keep the UI sharp and to conduct refactorings/redesigns in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The highlightbar is something I wanted to add, but I wanted to push the code to prompt the security conversation before I invested too much time.
I want to keep this code here without abstracting it away, but I'm open to the conversation. However:

  • while adding components to org.asteroid.controls can improve consistency, it won't necessarily improve maintainability. Recent changes which added highlightbar immediately broke asteroid-settings, but that was fixed - however, asteroid-map still hasn't, and the new highlightbar changes have broken the UI significantly. I think future changes, apart from miniscule tweaks to font size and the like, will probably bring the same kind of breakage and the same kind of cross-repo maintenance tasks.
  • qml-asteroid won't account for all the edge cases, and I think this might be an edge case. This chunk of code definitely needs cleaning up and making more consistent, but I'm not sure it will be used anywhere else, and I think 'potential to be used somewhere else' is the bar we should set for adding things to qml-asteroid.

property var wifiName: modelData.name
visible: wifiStatus.powered
width: wifiList.width
height: wifiStatus.powered ? width*0.23 : 0
Marquee {
id: wifiNameLabel
text: wifiName
height: parent.height*0.6
width: parent.width
}
Label {
anchors.top: wifiNameLabel.bottom
anchors.horizontalCenter: parent.horizontalCenter
opacity: 0.8
font.pixelSize: parent.width*0.07
font.weight: Font.Thin
text: {
if (modelData.connected) {
qsTrId("id-wifi-connected")
} else if (modelData.favorite){
qsTrId("id-wifi-saved")
} else {
qsTrId("id-wifi-notsetup")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you'd need to provide the string in a specially formatted comment just above these qsTrId

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a final PR would need to update the translation files, but you can keep this for when that's undrafted

}
}
}
onClicked: {
if (modelData.favorite && !modelData.connected) {
modelData.requestConnect()
} else {
layerStack.push(connectionDialog, {modelData: modelData})
}
}
onPressAndHold: layerStack.push(connectionDialog, {modelData: modelData})
}
}

Component {
id: connectionDialog
WiFiConnectionDialog {}
}
}

Loading