Commit c32713df authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇
Browse files

Add QR code scanner for connecting to a WiFi network

Through a button in the toolbar, a viewfinder is opened,
which lets you connect to a WiFi network through a
QR code.

Changes the main view to be a StackView so we can add
subpages similar to the Clipboard plasmoid.
parent ffa1635e
Pipeline #266376 passed with stage
in 2 minutes and 23 seconds
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-FileCopyrightText: 2022 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
PlasmaComponents3.ScrollView {
id: scrollView
property alias view: connectionView
// HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
contentWidth: availableWidth - contentItem.leftMargin - contentItem.rightMargin
Keys.forwardTo: [connectionView]
contentItem: ListView {
id: connectionView
property bool showSeparator: false
Keys.onDownPressed: {
connectionView.incrementCurrentIndex();
connectionView.currentItem.forceActiveFocus();
}
Keys.onUpPressed: {
if (connectionView.currentIndex === 0) {
connectionView.currentIndex = -1;
toolbar.searchTextField.forceActiveFocus();
toolbar.searchTextField.selectAll();
} else {
event.accepted = false;
}
}
Loader {
anchors.centerIn: parent
width: parent.width - (PlasmaCore.Units.largeSpacing * 4)
active: connectionView.count === 0
asynchronous: true
visible: status == Loader.Ready
sourceComponent: PlasmaExtras.PlaceholderMessage {
iconName: {
if (toolbarValues.displayplaneModeMessage) {
return "network-flightmode-on"
}
if (toolbarValues.displayWifiMessage) {
return "network-wireless-off"
}
if (toolbarValues.displayWwanMessage) {
return "network-mobile-off"
}
return "edit-none"
}
text: {
if (toolbarValues.displayplaneModeMessage) {
return i18n("Airplane mode is enabled")
}
if (toolbarValues.displayWifiMessage) {
if (toolbarValues.displayWwanMessage) {
return i18n("Wireless and mobile networks are deactivated")
}
return i18n("Wireless is deactivated")
}
if (toolbarValues.displayWwanMessage) {
return i18n("Mobile network is deactivated")
}
if (toolbar.searchTextField.text.length > 0) {
return i18n("No matches")
}
return i18n("No available connections")
}
}
}
topMargin: PlasmaCore.Units.smallSpacing * 2
bottomMargin: PlasmaCore.Units.smallSpacing * 2
leftMargin: PlasmaCore.Units.smallSpacing * 2
rightMargin: PlasmaCore.Units.smallSpacing * 2
spacing: PlasmaCore.Units.smallSpacing
model: appletProxyModel
currentIndex: -1
boundsBehavior: Flickable.StopAtBounds
section.property: showSeparator ? "Section" : ""
section.delegate: ListItem {
separator: true
}
highlight: PlasmaExtras.Highlight { }
highlightMoveDuration: 0
highlightResizeDuration: 0
delegate: ConnectionItem {
width: connectionView.width - PlasmaCore.Units.smallSpacing * 4
}
}
}
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-FileCopyrightText: 2022 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.15 as QQC2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
......@@ -29,112 +31,73 @@ PlasmaExtras.Representation {
sourceModel: full.connectionModel
}
header: PlasmaExtras.PlasmoidHeading {
focus: true
leftPadding: PlasmaCore.Units.smallSpacing
contentItem: Toolbar {
id: toolbar
width: parent.width
}
}
header: stack.currentItem.header || connectionListHeader
Keys.forwardTo: [connectionView]
Keys.forwardTo: [stack.currentItem]
Keys.onPressed: {
if (event.modifiers & Qt.ControlModifier && event.key == Qt.Key_F) {
toolbar.searchTextField.forceActiveFocus();
toolbar.searchTextField.selectAll();
event.accepted = true;
} else {
event.accepted = false;
} else if (event.key === Qt.Key_Back || (event.modifiers & Qt.AltModifier && event.key == Qt.Key_Left)) {
if (stack.depth > 1) {
stack.pop();
event.accepted = true;
}
}
}
PlasmaComponents3.ScrollView {
id: scrollView
anchors.fill: parent
// HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
event.accepted = false;
}
contentWidth: availableWidth - contentItem.leftMargin - contentItem.rightMargin
PlasmaExtras.PlasmoidHeading {
id: connectionListHeader
focus: true
leftPadding: PlasmaCore.Units.smallSpacing
contentItem: Toolbar {
id: toolbar
width: parent.width
hasConnections: connectionListPage.view.count > 0
qrCodeScanSupported: {
// Checks whether Prison scanner and QtMultimedia imports are available
// and that there is a camera.
try {
const testItem = Qt.createQmlObject(`
import QtQml 2.15
import QtMultimedia 5.15
import org.kde.prison.scanner 1.0 as Prison
QtObject {
readonly property bool hasCamera: QtMultimedia.defaultCamera !== null
}
`, this, "qrCodeScanTest");
contentItem: ListView {
id: connectionView
const supported = testItem && testItem.hasCamera;
property int currentVisibleButtonIndex: -1
property bool showSeparator: false
testItem.destroy();
Keys.onDownPressed: {
connectionView.incrementCurrentIndex();
connectionView.currentItem.forceActiveFocus();
}
Keys.onUpPressed: {
if (connectionView.currentIndex === 0) {
connectionView.currentIndex = -1;
toolbar.searchTextField.forceActiveFocus();
toolbar.searchTextField.selectAll();
} else {
event.accepted = false;
return supported;
} catch (e) {
console.log("QR code scanning is not supported", e);
return false;
}
}
Loader {
anchors.centerIn: parent
width: parent.width - (PlasmaCore.Units.largeSpacing * 4)
active: connectionView.count === 0
asynchronous: true
visible: status == Loader.Ready
sourceComponent: PlasmaExtras.PlaceholderMessage {
iconName: {
if (toolbarValues.displayplaneModeMessage) {
return "network-flightmode-on"
}
if (toolbarValues.displayWifiMessage) {
return "network-wireless-off"
}
if (toolbarValues.displayWwanMessage) {
return "network-mobile-off"
}
return "edit-none"
}
text: {
if (toolbarValues.displayplaneModeMessage) {
return i18n("Airplane mode is enabled")
}
if (toolbarValues.displayWifiMessage) {
if (toolbarValues.displayWwanMessage) {
return i18n("Wireless and mobile networks are deactivated")
}
return i18n("Wireless is deactivated")
}
if (toolbarValues.displayWwanMessage) {
return i18n("Mobile network is deactivated")
}
if (toolbar.searchTextField.text.length > 0) {
return i18n("No matches")
}
return i18n("No available connections")
}
}
}
onScanQrCodeRequested: stack.push("QrCodePage.qml")
}
}
topMargin: PlasmaCore.Units.smallSpacing * 2
bottomMargin: PlasmaCore.Units.smallSpacing * 2
leftMargin: PlasmaCore.Units.smallSpacing * 2
rightMargin: PlasmaCore.Units.smallSpacing * 2
spacing: PlasmaCore.Units.smallSpacing
model: appletProxyModel
currentIndex: -1
boundsBehavior: Flickable.StopAtBounds
section.property: showSeparator ? "Section" : ""
section.delegate: ListItem {
separator: true
}
highlight: PlasmaExtras.Highlight { }
highlightMoveDuration: 0
highlightResizeDuration: 0
delegate: ConnectionItem {
width: connectionView.width - PlasmaCore.Units.smallSpacing * 4
QQC2.StackView {
id: stack
anchors.fill: parent
initialItem: ConnectionListPage {
id: connectionListPage
}
Connections {
target: stack.currentItem
ignoreUnknownSignals: true
function onBackRequested() {
stack.pop();
}
}
}
......@@ -142,13 +105,12 @@ PlasmaExtras.Representation {
Connections {
target: plasmoid
function onExpandedChanged(expanded) {
connectionView.currentVisibleButtonIndex = -1;
if (expanded) {
handler.requestScan();
full.connectionModel = networkModelComponent.createObject(full)
} else {
full.connectionModel.destroy()
stack.pop(null); // go back to start page
}
}
}
......
/*
SPDX-FileCopyrightText: 2022 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.12
import QtMultimedia 5.15 as QtMultimedia
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
import org.kde.prison.scanner 1.0 as Prison
ColumnLayout {
id: qrCodePage
signal backRequested
property Item header: PlasmaExtras.PlasmoidHeading {
RowLayout {
anchors.fill: parent
PlasmaComponents3.Button {
Layout.fillWidth: true
icon.name: "go-previous-view"
text: i18n("Return to Network Connections")
onClicked: qrCodePage.backRequested()
}
// TODO add camera selector?
}
}
QtMultimedia.Camera {
id: camera
focus {
focusMode: QtMultimedia.Camera.FocusContinuous
focusPointMode: QtMultimedia.Camera.FocusPointCenter
}
}
Prison.VideoScanner {
id: scanner
formats: Prison.Format.QRCode | Prison.Format.Aztec | Prison.Format.DataMatrix | Prison.Format.PDF417
onResultContentChanged: {
if (result.hasText) {
// TODO parse result.text
console.log("Found QR code:", result.text);
}
}
}
PlasmaComponents3.Label {
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
text: i18n("Join WiFi network by scanning a QR code.")
}
QtMultimedia.VideoOutput {
Layout.fillWidth: true
Layout.fillHeight: true
source: camera
filters: [scanner]
autoOrientation: true
fillMode: QtMultimedia.VideoOutput.PreserveAspectCrop
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.BackButton
onPressed: {
qrCodePage.backRequested();
}
}
Item {
anchors.fill: parent
// FIXME how is this supposed to work?! All I want is dark background with light text and light icons :(((
PlasmaCore.ColorScope.colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
PlasmaCore.ColorScope.inherit: true
Rectangle {
anchors.fill: parent
color: PlasmaCore.ColorScope.backgroundColor
opacity: camera.cameraStatus === QtMultimedia.Camera.ActiveStatus ? 0 : 1
Behavior on opacity {
OpacityAnimator {
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
}
}
visible: opacity > 0
}
// FIXME why does this not get proper inverted color from ComplementaryColorGroup above?
PlasmaExtras.PlaceholderMessage {
anchors.fill: parent
iconName: "edit-none"
text: camera.errorString
visible: camera.errorCode !== QtMultimedia.Camera.NoError
}
}
}
}
......@@ -17,11 +17,15 @@ import org.kde.kirigami 2.19 as Kirigami
RowLayout {
id: toolbar
signal scanQrCodeRequested
readonly property var displayWifiMessage: !wifiSwitchButton.checked && wifiSwitchButton.visible
readonly property var displayWwanMessage: !wwanSwitchButton.checked && wwanSwitchButton.visible
readonly property var displayplaneModeMessage: planeModeSwitchButton.checked && planeModeSwitchButton.visible
property alias searchTextField: searchTextField
property alias qrCodeScanSupported: qrScanButton.visible
property bool hasConnections: false
PlasmaCore.Svg {
id: lineSvg
......@@ -195,7 +199,7 @@ RowLayout {
Layout.fillWidth: true
enabled: connectionView.count > 0 || text.length > 0
enabled: toolbar.hasConnections || text.length > 0
// This uses expanded to ensure the binding gets reevaluated
// when the plasmoid is shown again and that way ensure we are
......@@ -209,19 +213,35 @@ RowLayout {
}
}
PlasmaComponents3.ToolButton {
id: openEditorButton
visible: mainWindow.kcmAuthorized && !(plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentDrawsPlasmoidHeading)
RowLayout {
spacing: 0
icon.name: "configure"
PlasmaComponents3.ToolButton {
id: qrScanButton
text: i18nc("@action:button", "Scan QR Code")
display: PlasmaComponents3.AbstractButton.IconOnly
icon.name: "view-barcode-qr"
onClicked: toolbar.scanQrCodeRequested()
PlasmaComponents3.ToolTip {
text: i18n("Configure network connections…")
PlasmaComponents3.ToolTip {
text: parent.text
}
}
onClicked: {
KCMShell.openSystemSettings(mainWindow.kcm)
PlasmaComponents3.ToolButton {
id: openEditorButton
visible: mainWindow.kcmAuthorized && !(plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentDrawsPlasmoidHeading)
icon.name: "configure"
PlasmaComponents3.ToolTip {
text: i18n("Configure network connections…")
}
onClicked: {
KCMShell.openSystemSettings(mainWindow.kcm)
}
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment