import QtQuick 2.0 import QtQuick.Controls 2.9 import QtQuick.Layouts 1.0 import de.itsblue.LedDisplayController 1.0 import de.itsblue.bluetoothleuart 1.0 import QtQuick.Controls.Material 2.0 import QtGraphicalEffects 1.0 Page { id: root property bool actionButtonVisible: false property bool backButtonVisible: false property string statusText property bool working title: qsTr("Available displays") signal opened() onOpened: { } ColumnLayout { id: mainLayout anchors { fill: parent margins: Math.min(parent.height, parent.width) * 0.05 } Chip { Layout.fillWidth: true Layout.preferredHeight: 35 Layout.alignment: Layout.Center verticalPadding: height * 0.25 horizontalPadding: width * 0.05 color: "#ffffff" text: root.statusText onClicked: { if(parseInt(root.state) === LedDisplayBackend.LocationPermissionDenied && !backend.bleClient.isLocationPermissionGranted()) backend.bleClient.requestLocationPermission() else backend.bleClient.startScanningForDevices() } BusyIndicator { id: busyIndicator anchors { right: parent.right } height: parent.height width: height scale: 0.8 opacity: root.working ? 1:0 } } Item { Layout.fillWidth: true Layout.preferredHeight: mainLayout.height * 0.025 } ListView { id: availableDisplaysListView Layout.preferredWidth: parent.width Layout.fillHeight: true Layout.alignment: Layout.Center clip: true boundsBehavior: Flickable.OvershootBounds model: backend.bleClient.availableDevicesModel add: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 200 } NumberAnimation { property: "scale"; from: 0.9; to: 1; duration: 200 } } remove: Transition { NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 200 } NumberAnimation { property: "scale"; from: 1; to: 0.9; duration: 200 } } spacing: 5 delegate: ItemDelegate { width: parent.width text: name onClicked: backend.bleClient.connectToDevice(device) Rectangle { anchors { top: parent.top left: parent.left right: parent.right topMargin: - availableDisplaysListView.spacing * 0.5 } color: "lightgrey" height: 1 visible: index !== 0 } } Item { id: noDisplaysItem anchors.centerIn: parent width: Math.min(parent.height, parent.width) height: Math.min(parent.height, parent.width) opacity: availableDisplaysListView.model.rowCount === 0 ? 1:0 Behavior on opacity { NumberAnimation {} } Rectangle { id: noDisplaysRect anchors { top: parent.top topMargin: parent.height * 0.2 horizontalCenter: parent.horizontalCenter } width: parent.width * 0.7 height: width * 0.3 color: "transparent" border.width: height * 0.15 border.color: "lightgrey" Text { anchors.centerIn: parent anchors.verticalCenterOffset: text === "..." ? -height * 0.25:0 color: "lightgrey" font.pixelSize: parent.height * 0.6 font.bold: true text: parseInt(root.state) === LedDisplayBackend.Scanning ? "...":"?" } } Text { id: noDisplaysText anchors { top: noDisplaysRect.bottom topMargin: noDisplaysRect.height * 0.15 horizontalCenter: parent.horizontalCenter } font.bold: true font.pixelSize: noDisplaysRect.height * 0.3 color: Qt.darker("lightgrey", 1.1) text: parseInt(root.state) === LedDisplayBackend.Scanning ? qsTr("Still scanning"):qsTr("No displays found") } } Item { id: bluetoothOffItem anchors.centerIn: parent width: Math.min(parent.height, parent.width) height: Math.min(parent.height, parent.width) opacity: 0 Behavior on opacity { NumberAnimation {} } Text { id: bluetoothOffIcon anchors { top: parent.top topMargin: parent.height * 0.1 horizontalCenter: parent.horizontalCenter } anchors.verticalCenterOffset: text === "..." ? -height * 0.25:0 color: "lightgrey" font.pixelSize: parent.height * 0.4 font.family: fontAwesomeBrands.name text: "\uf294" } Text { id: bluetoothOffText anchors { top: bluetoothOffIcon.bottom topMargin: bluetoothOffIcon.height * 0.15 horizontalCenter: parent.horizontalCenter } font.bold: true font.pixelSize: bluetoothOffIcon.height * 0.15 color: Qt.darker("lightgrey", 1.1) text: qsTr("Bluetooth is turned off") } } Item { id: noPermissionItem anchors.centerIn: parent width: Math.min(parent.height, parent.width) height: Math.min(parent.height, parent.width) opacity: 0 Behavior on opacity { NumberAnimation {} } Text { id: noPermissionIcon anchors { top: parent.top topMargin: parent.height * 0 horizontalCenter: parent.horizontalCenter } anchors.verticalCenterOffset: text === "..." ? -height * 0.25:0 color: "lightgrey" font.pixelSize: parent.height * 0.4 font.family: fontAwesome.name text: "\uf3ed" } Text { id: noPermissionText anchors { top: noPermissionIcon.bottom topMargin: noPermissionIcon.height * 0.15 horizontalCenter: parent.horizontalCenter } width: parent.width * 0.9 font.bold: true font.pixelSize: noPermissionIcon.height * 0.15 horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap color: Qt.darker("lightgrey", 1.1) text: qsTr("Error:\nLocation permission denied!") } Text { id: noPermissionDetailText anchors { top: noPermissionText.bottom topMargin: noPermissionText.height * 0.15 horizontalCenter: parent.horizontalCenter } width: parent.width * 0.9 font.bold: true font.pixelSize: noPermissionText.font.pixelSize * 0.7 horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap color: Qt.darker("lightgrey", 1.1) text: qsTr("This app requires location permission in order for Bluetooth to work, it will not actually access your location.") } } } } Dialog { id: authenticationDialog property bool shouldBeOpened: false parent: Overlay.overlay x: (parent.width - width) / 2 y: (parent.height - height) / 2 width: parent.width * 0.9 modal: true closePolicy: Popup.NoAutoClose standardButtons: Dialog.Ok | Dialog.Cancel title: qsTr("Input code") onShouldBeOpenedChanged: { if(shouldBeOpened) open() else close() } onAccepted: { backend.authenticate(secretTextInput.text) } onRejected: { backend.bleClient.disconnectFromDevice() } contentItem: TextField { id: secretTextInput placeholderText: qsTr("code") Keys.onReturnPressed: authenticationDialog.accept() } } states: [ State { name: LedDisplayBackend.Idle PropertyChanges { target: root statusText: qsTr("Tap here to scan") working: false } PropertyChanges { target: noPermissionItem opacity: 0 } }, State { name: LedDisplayBackend.BluetoothOff PropertyChanges { target: bluetoothOffItem opacity: 1 } PropertyChanges { target: root statusText: qsTr("Bluetooth is turned off") working: false } PropertyChanges { target: noDisplaysItem opacity: 0 } }, State { name: LedDisplayBackend.LocationPermissionDenied PropertyChanges { target: noPermissionItem opacity: 1 } PropertyChanges { target: root statusText: qsTr("Tap here to continue") working: false } PropertyChanges { target: noDisplaysItem opacity: 0 } }, State { name: LedDisplayBackend.Scanning PropertyChanges { target: root statusText: qsTr("Scanning...") working: true } PropertyChanges { target: bluetoothOffItem opacity: 0 } PropertyChanges { target: noPermissionItem opacity: 0 } }, State { name: LedDisplayBackend.ReadyToConnect PropertyChanges { target: root statusText: availableDisplaysListView.model.rowCount > 0 ? qsTr("Please select a device or tap to scan again"):qsTr("No displays found. Tap to scan again") working: false } }, State { name: LedDisplayBackend.AuthenticationRequired PropertyChanges { target: authenticationDialog shouldBeOpened: true } PropertyChanges { target: availableDisplaysListView enabled: false } PropertyChanges { target: root statusText: qsTr("trying to authenticate...") working: true } }, State { name: LedDisplayBackend.Authenticating PropertyChanges { target: availableDisplaysListView enabled: false } PropertyChanges { target: root statusText: qsTr("trying to authenticate...") working: true } }, State { name: LedDisplayBackend.Connecting PropertyChanges { target: availableDisplaysListView enabled: false } PropertyChanges { target: root statusText: qsTr("trying to connect...") working: true } }, State { name: LedDisplayBackend.Initing PropertyChanges { target: availableDisplaysListView enabled: false } PropertyChanges { target: root statusText: qsTr("loading data...") working: true } } ] }