import QtQuick 2.0 import QtQuick.Controls 2.9 import QtQuick.Layouts 1.0 import de.itsblue.omobidisplayapp 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: mainLayout.height * 0.05 Layout.alignment: Layout.Center color: "white" onClicked: { backend.bleClient.startScanningForDevices() } RowLayout { spacing: mainLayout.anchors.margins anchors.fill: parent anchors.leftMargin: width * 0.05 anchors.rightMargin: 0 Text { Layout.fillHeight: true Layout.fillWidth: true verticalAlignment: Text.AlignVCenter font.pixelSize: parent.height * 0.4 text: root.statusText } BusyIndicator { Layout.fillHeight: true Layout.preferredWidth: height id: busyIndicator 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) === OmobiDisplayBackend.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) === OmobiDisplayBackend.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: OmobiDisplayBackend.Idle PropertyChanges { target: root statusText: qsTr("Tap here to scan") working: false } PropertyChanges { target: noPermissionItem opacity: 0 } }, State { name: OmobiDisplayBackend.BluetoothOff PropertyChanges { target: bluetoothOffItem opacity: 1 } PropertyChanges { target: root statusText: qsTr("Bluetooth is turned off") working: false } PropertyChanges { target: noDisplaysItem opacity: 0 } }, State { name: OmobiDisplayBackend.LocationPermissionDenied PropertyChanges { target: noPermissionItem opacity: 1 } PropertyChanges { target: root statusText: qsTr("Tap here to continue") working: false } PropertyChanges { target: noDisplaysItem opacity: 0 } }, State { name: OmobiDisplayBackend.Scanning PropertyChanges { target: root statusText: qsTr("Scanning...") working: true } PropertyChanges { target: bluetoothOffItem opacity: 0 } PropertyChanges { target: noPermissionItem opacity: 0 } }, State { name: OmobiDisplayBackend.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: OmobiDisplayBackend.AuthenticationRequired PropertyChanges { target: authenticationDialog shouldBeOpened: true } PropertyChanges { target: availableDisplaysListView enabled: false } PropertyChanges { target: root statusText: qsTr("trying to authenticate...") working: true } }, State { name: OmobiDisplayBackend.Authenticating PropertyChanges { target: availableDisplaysListView enabled: false } PropertyChanges { target: root statusText: qsTr("trying to authenticate...") working: true } }, State { name: OmobiDisplayBackend.Connecting PropertyChanges { target: availableDisplaysListView enabled: false } PropertyChanges { target: root statusText: qsTr("trying to connect...") working: true } }, State { name: OmobiDisplayBackend.Initing PropertyChanges { target: availableDisplaysListView enabled: false } PropertyChanges { target: root statusText: qsTr("loading data...") working: true } } ] }