|
|
|
|
@ -17,10 +17,9 @@
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import QtQuick 2.4
|
|
|
|
|
import QtQuick.Controls 2.4
|
|
|
|
|
import QtQuick.Layouts 1.3
|
|
|
|
|
import Cutefish.Settings 1.0
|
|
|
|
|
import QtQuick
|
|
|
|
|
import QtQuick.Controls
|
|
|
|
|
import QtQuick.Layouts
|
|
|
|
|
import FishUI 1.0 as FishUI
|
|
|
|
|
import Cutefish.Bluetooth 1.0 as Bluetooth
|
|
|
|
|
import "../"
|
|
|
|
|
@ -28,51 +27,43 @@ import "../"
|
|
|
|
|
ItemPage {
|
|
|
|
|
id: control
|
|
|
|
|
headerTitle: qsTr("Bluetooth")
|
|
|
|
|
readonly property bool bluetoothEnabled: Bluetooth.Manager.enabled
|
|
|
|
|
|
|
|
|
|
property bool bluetoothDisConnected: Bluetooth.Manager.bluetoothBlocked
|
|
|
|
|
|
|
|
|
|
onBluetoothDisConnectedChanged: {
|
|
|
|
|
bluetoothSwitch.checked = !bluetoothDisConnected
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setBluetoothEnabled(enabled) {
|
|
|
|
|
Bluetooth.Manager.bluetoothBlocked = !enabled
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < Bluetooth.Manager.adapters.length; ++i) {
|
|
|
|
|
var adapter = Bluetooth.Manager.adapters[i]
|
|
|
|
|
adapter.powered = enabled
|
|
|
|
|
}
|
|
|
|
|
function updateActivity() {
|
|
|
|
|
Bluetooth.Manager.agentEnabled = visible
|
|
|
|
|
Bluetooth.Manager.discoveryRequested = visible && Bluetooth.Manager.enabled
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bluetooth.DevicesProxyModel {
|
|
|
|
|
id: devicesProxyModel
|
|
|
|
|
sourceModel: devicesModel
|
|
|
|
|
onVisibleChanged: updateActivity()
|
|
|
|
|
onBluetoothEnabledChanged: {
|
|
|
|
|
Bluetooth.Manager.discoveryRequested = visible && bluetoothEnabled
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bluetooth.DevicesModel {
|
|
|
|
|
id: devicesModel
|
|
|
|
|
Component.onCompleted: updateActivity()
|
|
|
|
|
Component.onDestruction: {
|
|
|
|
|
Bluetooth.Manager.discoveryRequested = false
|
|
|
|
|
Bluetooth.Manager.agentEnabled = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bluetooth.BluetoothManager {
|
|
|
|
|
id: bluetoothMgr
|
|
|
|
|
|
|
|
|
|
onShowPairDialog: {
|
|
|
|
|
_pairDialog.title = name
|
|
|
|
|
_pairDialog.pin = pin
|
|
|
|
|
_pairDialog.visible = true
|
|
|
|
|
Bluetooth.DevicesModel { id: devicesModel }
|
|
|
|
|
PairDialog { }
|
|
|
|
|
|
|
|
|
|
Dialog {
|
|
|
|
|
id: forgetDialog
|
|
|
|
|
property var device: null
|
|
|
|
|
title: qsTr("Forget this device?")
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
modal: true
|
|
|
|
|
standardButtons: Dialog.Cancel | Dialog.Ok
|
|
|
|
|
Label {
|
|
|
|
|
text: forgetDialog.device ? forgetDialog.device.name : ""
|
|
|
|
|
textFormat: Text.PlainText
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onPairFailed: {
|
|
|
|
|
rootWindow.showPassiveNotification(qsTr("Pairing unsuccessful"), 3000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onConnectFailed: {
|
|
|
|
|
rootWindow.showPassiveNotification(qsTr("Connecting Unsuccessful"), 3000)
|
|
|
|
|
onAccepted: {
|
|
|
|
|
if (device)
|
|
|
|
|
device.forget()
|
|
|
|
|
device = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PairDialog {
|
|
|
|
|
id: _pairDialog
|
|
|
|
|
onRejected: device = null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Scrollable {
|
|
|
|
|
@ -81,188 +72,200 @@ ItemPage {
|
|
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
id: layout
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.bottomMargin: FishUI.Units.largeSpacing
|
|
|
|
|
width: parent.contentWidth
|
|
|
|
|
spacing: FishUI.Units.largeSpacing
|
|
|
|
|
|
|
|
|
|
RoundedItem {
|
|
|
|
|
id: mainItem
|
|
|
|
|
spacing: FishUI.Units.largeSpacing
|
|
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
Label {
|
|
|
|
|
text: qsTr("Bluetooth")
|
|
|
|
|
color: FishUI.Theme.disabledTextColor
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Switch {
|
|
|
|
|
id: bluetoothSwitch
|
|
|
|
|
Layout.fillHeight: true
|
|
|
|
|
rightPadding: 0
|
|
|
|
|
checked: !Bluetooth.Manager.bluetoothBlocked
|
|
|
|
|
onCheckedChanged: setBluetoothEnabled(checked)
|
|
|
|
|
checked: Bluetooth.Manager.enabled
|
|
|
|
|
enabled: Bluetooth.Manager.operational && Bluetooth.Manager.adapters.length > 0
|
|
|
|
|
onClicked: Bluetooth.Manager.enabled = checked
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ListView {
|
|
|
|
|
id: _listView
|
|
|
|
|
visible: count > 0
|
|
|
|
|
interactive: false
|
|
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
|
|
Label {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
|
|
|
|
|
Layout.preferredHeight: {
|
|
|
|
|
var totalHeight = 0
|
|
|
|
|
for (var i = 0; i < _listView.visibleChildren.length; ++i) {
|
|
|
|
|
totalHeight += _listView.visibleChildren[i].height
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
visible: !Bluetooth.Manager.operational || !Bluetooth.Manager.adapters.length
|
|
|
|
|
|| Bluetooth.Manager.hardwareBlocked || !Bluetooth.Manager.enabled
|
|
|
|
|
text: !Bluetooth.Manager.operational ? qsTr("Bluetooth service is unavailable")
|
|
|
|
|
: Bluetooth.Manager.hardwareBlocked ? qsTr("Bluetooth is blocked by a hardware switch")
|
|
|
|
|
: !Bluetooth.Manager.adapters.length ? qsTr("No Bluetooth adapters found")
|
|
|
|
|
: qsTr("Bluetooth is disabled")
|
|
|
|
|
}
|
|
|
|
|
RowLayout {
|
|
|
|
|
visible: Bluetooth.Manager.enabled
|
|
|
|
|
Label {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
text: Bluetooth.Manager.scanning ? qsTr("Searching for devices…") : qsTr("Devices")
|
|
|
|
|
}
|
|
|
|
|
BusyIndicator {
|
|
|
|
|
running: Bluetooth.Manager.scanning
|
|
|
|
|
implicitWidth: 28
|
|
|
|
|
implicitHeight: 28
|
|
|
|
|
}
|
|
|
|
|
Button {
|
|
|
|
|
text: Bluetooth.Manager.discoveryRequested ? qsTr("Stop searching") : qsTr("Search")
|
|
|
|
|
onClicked: {
|
|
|
|
|
Bluetooth.Manager.clearError()
|
|
|
|
|
Bluetooth.Manager.discoveryRequested = !Bluetooth.Manager.discoveryRequested
|
|
|
|
|
}
|
|
|
|
|
return totalHeight
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model: Bluetooth.Manager.bluetoothOperational ? devicesProxyModel : []
|
|
|
|
|
RoundedItem {
|
|
|
|
|
visible: Bluetooth.Manager.errorText.length > 0
|
|
|
|
|
Label {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
textFormat: Text.PlainText
|
|
|
|
|
text: Bluetooth.Manager.errorText
|
|
|
|
|
}
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Dismiss")
|
|
|
|
|
onClicked: Bluetooth.Manager.clearError()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
section.property: "Section"
|
|
|
|
|
section.criteria: ViewSection.FullString
|
|
|
|
|
section.delegate: Label {
|
|
|
|
|
color: FishUI.Theme.disabledTextColor
|
|
|
|
|
topPadding: FishUI.Units.largeSpacing
|
|
|
|
|
bottomPadding: FishUI.Units.largeSpacing
|
|
|
|
|
text: section == "My devices" ? qsTr("My devices")
|
|
|
|
|
: qsTr("Other devices")
|
|
|
|
|
Repeater {
|
|
|
|
|
model: Bluetooth.Manager.adapters
|
|
|
|
|
delegate: RoundedItem {
|
|
|
|
|
required property var modelData
|
|
|
|
|
Label {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
text: modelData.name + " · " + modelData.address
|
|
|
|
|
textFormat: Text.PlainText
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
}
|
|
|
|
|
RowLayout {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
TextField {
|
|
|
|
|
id: adapterName
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
placeholderText: modelData.name
|
|
|
|
|
maximumLength: 248
|
|
|
|
|
}
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Rename")
|
|
|
|
|
enabled: adapterName.text.length > 0
|
|
|
|
|
onClicked: {
|
|
|
|
|
modelData.name = adapterName.text
|
|
|
|
|
adapterName.clear()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RowLayout {
|
|
|
|
|
Switch {
|
|
|
|
|
text: qsTr("Powered")
|
|
|
|
|
checked: modelData.powered
|
|
|
|
|
onClicked: modelData.powered = checked
|
|
|
|
|
}
|
|
|
|
|
Switch {
|
|
|
|
|
text: qsTr("Visible to other devices")
|
|
|
|
|
enabled: modelData.powered
|
|
|
|
|
checked: modelData.discoverable
|
|
|
|
|
onClicked: modelData.discoverable = checked
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Switch {
|
|
|
|
|
text: qsTr("Allow incoming pairing")
|
|
|
|
|
enabled: modelData.powered
|
|
|
|
|
checked: modelData.pairable
|
|
|
|
|
onClicked: modelData.pairable = checked
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delegate: Item {
|
|
|
|
|
width: ListView.view.width
|
|
|
|
|
height: _itemLayout.implicitHeight + FishUI.Units.largeSpacing
|
|
|
|
|
|
|
|
|
|
property bool paired: model.Connected && model.Paired
|
|
|
|
|
Label {
|
|
|
|
|
visible: Bluetooth.Manager.enabled && devicesModel.count === 0
|
|
|
|
|
text: qsTr("No devices found. Make your device discoverable and search again.")
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
|
model: devicesModel
|
|
|
|
|
delegate: RoundedItem {
|
|
|
|
|
required property var device
|
|
|
|
|
visible: Bluetooth.Manager.enabled
|
|
|
|
|
RowLayout {
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
id: _itemLayout
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.leftMargin: 0
|
|
|
|
|
anchors.rightMargin: 0
|
|
|
|
|
anchors.topMargin: FishUI.Units.smallSpacing
|
|
|
|
|
anchors.bottomMargin: FishUI.Units.smallSpacing
|
|
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Label {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
height: _contentLayout.implicitHeight + FishUI.Units.largeSpacing
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
radius: FishUI.Theme.smallRadius
|
|
|
|
|
color: FishUI.Theme.textColor
|
|
|
|
|
opacity: mouseArea.pressed ? 0.15 : mouseArea.containsMouse ? 0.1 : 0.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
|
id: mouseArea
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
hoverEnabled: true
|
|
|
|
|
acceptedButtons: Qt.LeftButton
|
|
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
|
if (model.Connected || model.Paired){
|
|
|
|
|
additionalSettings.toggle()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (model.Paired) {
|
|
|
|
|
bluetoothMgr.connectToDevice(model.Address)
|
|
|
|
|
} else {
|
|
|
|
|
bluetoothMgr.requestParingConnection(model.Address)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
id: _contentLayout
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.rightMargin: FishUI.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
Image {
|
|
|
|
|
width: 16
|
|
|
|
|
height: 16
|
|
|
|
|
sourceSize: Qt.size(16, 16)
|
|
|
|
|
source: FishUI.Theme.darkMode ? "qrc:/images/sidebar/dark/bluetooth.svg"
|
|
|
|
|
: "qrc:/images/sidebar/light/bluetooth.svg"
|
|
|
|
|
Layout.alignment: Qt.AlignVCenter
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Label {
|
|
|
|
|
text: model.DeviceFullName
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.alignment: Qt.AlignVCenter
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Label {
|
|
|
|
|
visible: model.Paired
|
|
|
|
|
text: model.Connected ? qsTr("Connected") : qsTr("Not Connected")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
text: device.name
|
|
|
|
|
textFormat: Text.PlainText
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Hideable {
|
|
|
|
|
id: additionalSettings
|
|
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
Item {
|
|
|
|
|
height: FishUI.Units.largeSpacing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
spacing: FishUI.Units.largeSpacing
|
|
|
|
|
Layout.leftMargin: FishUI.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Connect")
|
|
|
|
|
visible: !model.Connected
|
|
|
|
|
onClicked: {
|
|
|
|
|
if (model.Paired) {
|
|
|
|
|
bluetoothMgr.connectToDevice(model.Address)
|
|
|
|
|
} else {
|
|
|
|
|
bluetoothMgr.requestParingConnection(model.Address)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Disconnect")
|
|
|
|
|
visible: model.Connected
|
|
|
|
|
onClicked: {
|
|
|
|
|
bluetoothMgr.deviceDisconnect(model.Address)
|
|
|
|
|
additionalSettings.hide()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Forget This Device")
|
|
|
|
|
flat: true
|
|
|
|
|
onClicked: {
|
|
|
|
|
bluetoothMgr.deviceRemoved(model.Address)
|
|
|
|
|
additionalSettings.hide()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HorizontalDivider {}
|
|
|
|
|
Label {
|
|
|
|
|
text: device.address + " · " + device.adapterPath.split("/").pop()
|
|
|
|
|
color: FishUI.Theme.disabledTextColor
|
|
|
|
|
}
|
|
|
|
|
Label {
|
|
|
|
|
text: device.connected ? qsTr("Connected")
|
|
|
|
|
: device.paired ? qsTr("Paired") : qsTr("Not paired")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Label {
|
|
|
|
|
visible: device.batteryPercentage >= 0
|
|
|
|
|
text: qsTr("%1% battery").arg(device.batteryPercentage)
|
|
|
|
|
}
|
|
|
|
|
BusyIndicator {
|
|
|
|
|
running: device.busy
|
|
|
|
|
visible: running
|
|
|
|
|
implicitWidth: 28
|
|
|
|
|
implicitHeight: 28
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RowLayout {
|
|
|
|
|
Button {
|
|
|
|
|
text: device.connected ? qsTr("Disconnect")
|
|
|
|
|
: device.paired ? qsTr("Connect") : qsTr("Pair")
|
|
|
|
|
enabled: !device.busy && (device.paired || Bluetooth.Manager.agentReady)
|
|
|
|
|
onClicked: {
|
|
|
|
|
if (device.connected)
|
|
|
|
|
device.disconnectFromDevice()
|
|
|
|
|
else if (device.paired)
|
|
|
|
|
device.connectToDevice()
|
|
|
|
|
else
|
|
|
|
|
device.pair()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Button {
|
|
|
|
|
visible: device.busy && !device.paired
|
|
|
|
|
text: qsTr("Cancel pairing")
|
|
|
|
|
onClicked: device.cancelPairing()
|
|
|
|
|
}
|
|
|
|
|
Button {
|
|
|
|
|
visible: device.paired
|
|
|
|
|
enabled: !device.busy
|
|
|
|
|
text: qsTr("Forget")
|
|
|
|
|
onClicked: {
|
|
|
|
|
forgetDialog.device = device
|
|
|
|
|
forgetDialog.open()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CheckBox {
|
|
|
|
|
visible: device.paired
|
|
|
|
|
text: qsTr("Trusted")
|
|
|
|
|
checked: device.trusted
|
|
|
|
|
enabled: !device.busy
|
|
|
|
|
onClicked: device.trusted = checked
|
|
|
|
|
}
|
|
|
|
|
CheckBox {
|
|
|
|
|
text: qsTr("Blocked")
|
|
|
|
|
checked: device.blocked
|
|
|
|
|
enabled: !device.busy
|
|
|
|
|
onClicked: device.blocked = checked
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
height: FishUI.Units.largeSpacing * 2
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|