2026-05-07 14:34:40 +03:00
import QtQuick
import QtQuick . Controls
import QtQuick . Layouts
2026-05-07 19:15:28 +03:00
import QRCodeReader 1.0
2026-05-07 14:34:40 +03:00
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
import "../Components"
PageType {
id: root
property int qrImageIndex: 0
2026-05-07 19:15:28 +03:00
property bool pairingCameraOpen: false
2026-05-07 22:50:14 +03:00
Timer {
id: pairingCameraKickTimer
interval: 180
repeat: false
onTriggered: root . restartPairingIosCamera ( )
}
function restartPairingIosCamera ( ) {
if ( Qt . platform . os !== "ios" || ! root . pairingCameraOpen ) {
return
}
if ( cameraSlot . width < 32 || cameraSlot . height < 32 ) {
console . info ( "[PairingQr] cameraSlot too small wxh=" , cameraSlot . width , cameraSlot . height , "retry" )
pairingCameraKickTimer . restart ( )
return
}
var p = cameraSlot . mapToItem ( root , 0 , 0 )
console . info ( "[PairingQr] start preview frame" , p . x , p . y , cameraSlot . width , cameraSlot . height )
pairingQrReader . stopReading ( )
pairingQrReader . setCameraSize ( Qt . rect ( Math . round ( p . x ) , Math . round ( p . y ) , Math . round ( cameraSlot . width ) , Math . round ( cameraSlot . height ) ) )
pairingQrReader . startReading ( )
}
2026-05-07 19:15:28 +03:00
Connections {
target: root
function onVisibleChanged ( ) {
if ( ! root . visible ) {
2026-05-07 22:50:14 +03:00
pairingCameraKickTimer . stop ( )
2026-05-07 19:15:28 +03:00
pairingQrReader . stopReading ( )
root . pairingCameraOpen = false
PairingUiController . cancelAllPairingActivity ( )
}
}
}
2026-05-07 14:34:40 +03:00
2026-05-07 22:50:14 +03:00
Connections {
target: root
function onPairingCameraOpenChanged ( ) {
if ( ! root . pairingCameraOpen ) {
pairingCameraKickTimer . stop ( )
pairingQrReader . stopReading ( )
return
}
if ( Qt . platform . os === "ios" ) {
pairingCameraKickTimer . restart ( )
}
}
}
Connections {
target: cameraSlot
enabled: Qt . platform . os === "ios" && root . pairingCameraOpen
function onWidthChanged ( ) {
pairingCameraKickTimer . restart ( )
}
function onHeightChanged ( ) {
pairingCameraKickTimer . restart ( )
}
}
2026-05-07 14:34:40 +03:00
FlickableType {
anchors.fill: parent
contentHeight: layout . implicitHeight
ColumnLayout {
id: layout
width: root . width
spacing: 8
BackButtonType {
Layout.topMargin: 20 + PageController . safeAreaTopMargin
}
Label {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 8
2026-05-07 21:51:39 +03:00
text: qsTr ( "QR pairing (dev — single device)" )
2026-05-07 14:34:40 +03:00
font.pixelSize: 28
font.bold: true
color: AmneziaStyle . color . paleGray
wrapMode: Text . Wrap
}
2026-05-07 21:51:39 +03:00
ParagraphTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
color: AmneziaStyle . color . goldenApricot
text: qsTr ( "Developer / QA: receive and send on one device (e.g. with local gateway). Not shown in production menus unless opened from Dev menu." )
wrapMode: Text . Wrap
}
2026-05-07 14:34:40 +03:00
ParagraphTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr ( "Experimental: transfer API configuration to another device via gateway. Use “Receive” on the device that shows the QR code, and “Send” on the premium device." )
wrapMode: Text . Wrap
}
Label {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 16
text: qsTr ( "Receive configuration (TV / second device)" )
font.pixelSize: 18
font.bold: true
color: AmneziaStyle . color . mutedGray
}
BasicButtonType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: PairingUiController . tvPairingBusy ? qsTr ( "Waiting…" ) : qsTr ( "Start and show QR" )
enabled: ! PairingUiController . tvPairingBusy && ! PairingUiController . phonePairingBusy
clickedFunc: function ( ) {
PairingUiController . startTvQrSession ( )
}
}
BasicButtonType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr ( "Cancel receive" )
enabled: PairingUiController . tvPairingBusy
clickedFunc: function ( ) {
PairingUiController . cancelTvQrSession ( )
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: PairingUiController . tvStatusMessage . length > 0
text: PairingUiController . tvStatusMessage
wrapMode: Text . Wrap
}
2026-05-07 19:15:28 +03:00
Item {
id: qrBox
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
2026-05-07 14:34:40 +03:00
Layout.topMargin: 8
2026-05-07 19:15:28 +03:00
implicitHeight: width
2026-05-07 14:34:40 +03:00
visible: PairingUiController . tvQrCodesCount > 0
2026-05-07 19:15:28 +03:00
Image {
id: qrImage
2026-05-07 14:34:40 +03:00
anchors.fill: parent
2026-05-07 19:15:28 +03:00
fillMode: Image . PreserveAspectFit
sourceSize: Qt . size ( 2048 , 2048 )
source: PairingUiController . tvQrCodesCount > 0 ? PairingUiController . tvQrCodes [ root . qrImageIndex ] : ""
MouseArea {
anchors.fill: parent
enabled: PairingUiController . tvQrCodesCount > 1
onClicked: {
root . qrImageIndex = ( root . qrImageIndex + 1 ) % PairingUiController . tvQrCodesCount
}
2026-05-07 14:34:40 +03:00
}
}
}
Label {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 24
text: qsTr ( "Send configuration (premium device)" )
font.pixelSize: 18
font.bold: true
color: AmneziaStyle . color . mutedGray
}
TextFieldWithHeaderType {
id: uuidField
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr ( "QR session UUID" )
textField.placeholderText: qsTr ( "Paste UUID from TV QR" )
}
2026-05-07 19:15:28 +03:00
BasicButtonType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: Qt . platform . os === "android" || Qt . platform . os === "ios"
text: {
if ( Qt . platform . os === "ios" && root . pairingCameraOpen ) {
return qsTr ( "Hide camera" )
}
return qsTr ( "Scan QR code" )
}
enabled: ! PairingUiController . phonePairingBusy
clickedFunc: function ( ) {
if ( Qt . platform . os === "android" ) {
PairingUiController . openPairingQrScanner ( )
} else {
root . pairingCameraOpen = ! root . pairingCameraOpen
}
}
}
Item {
id: cameraSlot
Layout.fillWidth: true
Layout.preferredHeight: ( root . pairingCameraOpen && Qt . platform . os === "ios" ) ? 220 : 0
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: Layout . preferredHeight > 0
clip: true
QRCodeReader {
id: pairingQrReader
2026-05-07 22:50:14 +03:00
anchors.fill: parent
2026-05-07 19:15:28 +03:00
onCodeReaded: function ( code ) {
if ( PairingUiController . applyScannedTextAsPairingUuid ( code ) ) {
pairingQrReader . stopReading ( )
root . pairingCameraOpen = false
PageController . showNotificationMessage ( qsTr ( "Session ID filled from QR" ) )
}
}
}
onVisibleChanged: {
if ( ! visible ) {
pairingQrReader . stopReading ( )
return
}
if ( Qt . platform . os === "ios" ) {
2026-05-07 22:50:14 +03:00
pairingCameraKickTimer . restart ( )
2026-05-07 19:15:28 +03:00
}
}
}
2026-05-07 14:34:40 +03:00
BasicButtonType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: PairingUiController . phonePairingBusy ? qsTr ( "Sending…" ) : qsTr ( "Send from current subscription" )
2026-05-07 19:15:28 +03:00
enabled: ! PairingUiController . phonePairingBusy
2026-05-07 14:34:40 +03:00
clickedFunc: function ( ) {
PairingUiController . submitPhonePairing ( uuidField . textField . text , ServersUiController . getProcessedServerIndex ( ) )
}
}
ParagraphTextType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 24 + PageController . safeAreaBottomMargin
visible: PairingUiController . phoneStatusMessage . length > 0
text: PairingUiController . phoneStatusMessage
wrapMode: Text . Wrap
}
}
}
Connections {
target: PairingUiController
function onTvQrCodesChanged ( ) {
root . qrImageIndex = 0
}
2026-05-07 19:15:28 +03:00
function onTvSessionUuidChanged ( ) {
root . qrImageIndex = 0
uuidField . textField . text = PairingUiController . tvSessionUuid
}
2026-05-07 14:34:40 +03:00
function onTvPairingConfigReceived ( ) {
2026-05-07 21:51:39 +03:00
root . pairingCameraOpen = false
pairingQrReader . stopReading ( )
qrImage . source = ""
2026-05-07 14:34:40 +03:00
PageController . showNotificationMessage ( qsTr ( "Configuration received from gateway" ) )
2026-05-07 21:51:39 +03:00
Qt . callLater ( function ( ) {
PageController . closePage ( )
} )
2026-05-07 14:34:40 +03:00
}
function onPhonePairingSucceeded ( ) {
2026-05-07 21:51:39 +03:00
root . pairingCameraOpen = false
pairingQrReader . stopReading ( )
2026-05-07 14:34:40 +03:00
PageController . showNotificationMessage ( qsTr ( "Configuration sent" ) )
2026-05-07 21:51:39 +03:00
Qt . callLater ( function ( ) {
PageController . closePage ( )
} )
2026-05-07 14:34:40 +03:00
}
2026-05-07 19:15:28 +03:00
function onPairingUuidFromScan ( uuid ) {
uuidField . textField . text = uuid
}
2026-05-07 14:34:40 +03:00
}
}