diff options
author | Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> | 2021-08-09 12:34:48 +0000 |
---|---|---|
committer | Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> | 2021-08-09 12:34:48 +0000 |
commit | 607edbcd8689ea71ec57340204ed3d908a7cadbb (patch) | |
tree | 91961406adb5143e0c5072094d2d1a4846e6b6b0 | |
parent | 34abe223f7648929f0c6b9132fbdc83e0353b51a (diff) | |
parent | fd5e881fdee35d21fd3e0728e5b927d04e64f459 (diff) | |
download | RWA.Support.DesktopApp-607edbcd8689ea71ec57340204ed3d908a7cadbb.tar.gz RWA.Support.DesktopApp-607edbcd8689ea71ec57340204ed3d908a7cadbb.tar.bz2 RWA.Support.DesktopApp-607edbcd8689ea71ec57340204ed3d908a7cadbb.zip |
Merge branch 'mr/feature/gitlab-ci' into 'master'
Add .gitlab-ci.yml
See merge request remotewebapp/rwa.support.desktopapp!2
44 files changed, 3045 insertions, 1221 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..602b2e3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,38 @@ +image: darkmattercoder/qt-build:latest +#entrypoint: [""] + +stages: + - Build + - Run + +Build:Code: + stage: Build + script: + - apt update && apt -y install make autoconf + - qmake rwa-support-desktopapp.pro + - make + artifacts: + paths: + - rwa-support-desktopapp + expire_in: 2 weeks + +# Disabled because running the application would give an error +# since there are no X.Org server, Wayland or other display servers available +# Run:Code: +# stage: Run +# script: +# - echo "All files in git repo + artifact 'rwa-support-desktopapp':" +# - find +# - echo "Executing rwa-support-desktopapp" +# - ./rwa-support-desktopapp +# dependencies: +# - Build:Code + +# To be implemented. +# Test:Code: +# stage: Test +# script: +# - echo "All files in git repo + artifact 'rwa-support-desktopapp':" +# - find +# dependencies: +# - Build:Code diff --git a/locales/bin/de_DE.qm b/locales/bin/de_DE.qm Binary files differdeleted file mode 100644 index a4d5b5d..0000000 --- a/locales/bin/de_DE.qm +++ /dev/null diff --git a/locales/bin/es_ES.qm b/locales/bin/es_ES.qm Binary files differdeleted file mode 100644 index 0bed16b..0000000 --- a/locales/bin/es_ES.qm +++ /dev/null diff --git a/locales/bin/main_en.qm b/locales/bin/main_en.qm Binary files differdeleted file mode 100644 index 8a9ba24..0000000 --- a/locales/bin/main_en.qm +++ /dev/null diff --git a/locales/de_DE.qm b/locales/de_DE.qm Binary files differnew file mode 100644 index 0000000..453dcd4 --- /dev/null +++ b/locales/de_DE.qm diff --git a/locales/de_DE.ts b/locales/de_DE.ts index 5d8838f..1582fcc 100644 --- a/locales/de_DE.ts +++ b/locales/de_DE.ts @@ -2,7 +2,7 @@ <!DOCTYPE TS> <TS version="2.1" language="de_DE"> <context> - <name>Add_Server_wizard</name> + <name>AddRWAHostWizard</name> <message> <source>Couldn't connect to the specified host!</source> <translation>Konnte nicht zum Server verbinden!</translation> @@ -16,17 +16,68 @@ <translation>Die angegebene Serveradresse enthält einen Fehler!</translation> </message> <message> - <source>This field can't be empty!</source> - <translation>Das Feld darf nicht leer sein!</translation> + <source>The features you expected here are not yet implemented.</source> + <translation>Die Features die Sie erwartet hatten, sind noch nicht implementiert worden.</translation> </message> <message> - <source>The feature you expected here are not yet implemented.</source> - <translation>Die Features die Sie erwartet hatten, sind noch nicht implementiert worden.</translation> + <source>An error occured while adding a new host!</source> + <translation>Ein Fehler trat auf beim Hinzufügen eines RWA-Servers!</translation> + </message> + <message> + <source>Both textfields can't be empty!</source> + <translation>Beide Felder müssen ausgefüllt werden!</translation> + </message> + <message> + <source>The specified host address does not grant access!</source> + <translation>Der angegebene Server gewährt keinen Zugang!</translation> + </message> + <message> + <source>The specified host address is not supported!</source> + <translation>Der angegebene Server wird nicht unterstützt!</translation> + </message> + <message> + <source>Can't connect to underlying session service!</source> + <translation>Es kann keine Verbindung zur Sitzungsverwaltung hergestellt werden!</translation> </message> </context> <context> <name>MainQMLAdaptor</name> <message> + <source>A host object in the response of D-Bus service lacks a necessary value. (host_url or host_uuid)</source> + <translation>Ein Serverobject in der D-Bus Serviceantwort beeinhaltet nicht alle erforderlichen werte. (host_url oder host_uuid)</translation> + </message> + <message> + <source>An error occured while adding a new host:</source> + <translation>Ein Fehler trat auf beim Hinzufügen eines RWA-Servers:</translation> + </message> + <message> + <source>Successfully added new RWAHost '%0'</source> + <translation>Erfolgreich RWA-Server '%0' hinzugefügt</translation> + </message> + <message> + <source>The error is not clear. The session service responded with status type '%0'</source> + <translation>Der Fehler ist nicht klar. Der Sessionservice hat mit Statustype '%0' geantwortet</translation> + </message> + <message> + <source>The error is not clear. The session service responded with no status type!</source> + <translation>Der Fehler ist nicht klar. Der Sessionservice hat mit keinem Statustypen geantwortet!</translation> + </message> + <message> + <source>No RWA host available!</source> + <translation>Kein RWA-Server verfügbar!</translation> + </message> + <message> + <source>Can't connect to underlying session service!</source> + <translation>Es kann keine Verbindung zur Sitzungsverwaltung hergestellt werden!</translation> + </message> + <message> + <source>Removed RWAHost '%0'</source> + <translation>RWA-Server '%0' entfernt</translation> + </message> +</context> +<context> + <name>RemoteControlManager</name> + <message> <source>Stop remote support session</source> <translation>Stoppe Fernwartungssitzung</translation> </message> @@ -34,20 +85,92 @@ <source>Start remote support session</source> <translation>Starte Fernwartungssitzung</translation> </message> -</context> -<context> - <name>QObject</name> <message> - <source>You already have this app running.</source> - <translation>Diese Anwendung läuft bereits.</translation> + <source>Not available yet</source> + <translation>Noch nicht verfügbar</translation> + </message> + <message> + <source>Unknown state of service</source> + <translation>Unbekannter Status des Dienstes</translation> + </message> + <message> + <source>Remote Support session was stopped ungracefully</source> + <translation>Fernwartungssitzung wurde unerwartet beendet</translation> + </message> + <message> + <source>Remote Support session was stopped</source> + <translation>Fernwartungssitzung wurde beendet</translation> + </message> + <message> + <source>Your partner is connected to the Remote Support session</source> + <translation>Fernwartungspartner ist mit dieser Sitzung verbunden</translation> + </message> + <message> + <source>Remote Support session couldn't be started!</source> + <translation>Fernwartungssitzung konnte nicht gestartet werden!</translation> + </message> + <message> + <source>Session was started on '%0' successfully</source> + <translation>Sitzung wurde erfolgreich auf '%0' gestartet</translation> + </message> + <message> + <source>remote support partner could still be connected!</source> + <translation>Es ist möglich, dass Ihr Fernwartungspartner noch verbunden ist!</translation> + </message> + <message> + <source>Session could not be stopped!</source> + <translation>Sitzung konnte nicht gestoppt werden!</translation> + </message> + <message> + <source>Session status could not be refreshed! Your remote support partner could still be connected!</source> + <translation>Sitzungstatus konnte nicht erneuert werden! Es ist möglich, dass der Fernwartungspartner noch verbunden ist!</translation> + </message> + <message> + <source>Couldn't remove current session out of '_sessions' list.</source> + <translation>Konnte aktuelle Sitzung nicht aus der Liste '_sessions' löschen.</translation> + </message> + <message> + <source>currentSessionStartSucceeded(): Current Session is nullptr!</source> + <translation>currentSessionStartSucceeded(): Aktuelle Sitzung ist nullptr!</translation> + </message> + <message> + <source>Remote support session was stopped.</source> + <translation>Fernwartungssitzung wurde beendet.</translation> + </message> + <message> + <source>Remote Support session successfully started! Waiting for your remote support partner to connect.</source> + <translation>Fernwartungssitzung wurde erfolgreich gestartet! Warte auf auf Verbindung vom Fernwartungspartner.</translation> + </message> + <message> + <source>Session stopped successfully.</source> + <translation>Fernwartungssitzung wurde erfolgreich beendet.</translation> + </message> + <message> + <source>Creating a new session object.</source> + <translation>Erstelle neues Sitzungsobjekt.</translation> </message> <message> - <source>Only one instance is allowed.</source> - <translation>Nur eine Instanz ist erlaubt.</translation> + <source>Can't start a remote support session. There is no RWA host is selected!</source> + <translation>Kann keine Fernwartungssitzung starten. Es ist kein RWA-Server ausgewählt!</translation> </message> <message> - <source>Closing application now with an error.</source> - <translation>Die Applikation wird nun mit einem Fehler beendet.</translation> + <source>RemoteControlManager::handleConnectButtonClick(): Current Session is nullptr!</source> + <translation>RemoteControlManager::handleConnectButtonClick(): Aktuelle Sitzung ist nullptr!</translation> + </message> + <message> + <source>Starting a remote support session on host '%0' using the new session object.</source> + <translation>Starte eine Fernwartungssitzung auf Server '%0' mithilfe des neuen Sitzungsobjektes.</translation> + </message> +</context> +<context> + <name>Scene_no_server_available</name> + <message> + <source>Welcome!</source> + <translation>Willkommen!</translation> + </message> + <message> + <source>You need to add and select the remote web app server to which you want to connect. You can see a button to the left in the menu which says 'Add RWA-Server'. Follow the steps listed there and you can start your remote support session afterwards using the buttons above 'Add RWA-Server'.</source> + <translation>Sie müssen zuerst einen RWA-Server hinzufügen und dann auswählen, um sich mit diesen zu verbinden. Auf der linken Seite befindet sich ein Knopf mit der Aufschrift 'RWA-Server hinzufügen'. Folgen Sie den Anweisungen dort und Sie können Ihre Fernwartungssitzung rasch starten.</translation> </message> </context> <context> @@ -57,7 +180,7 @@ <translation>Das hier ist eine Platzhalter Szene!</translation> </message> <message> - <source>The feature you expected here are not yet implemented.</source> + <source>The features you expected here are not yet implemented.</source> <translation>Die Features die Sie erwartet hatten, sind noch nicht implementiert worden.</translation> </message> </context> @@ -97,7 +220,7 @@ </message> <message> <source>Copied PIN into clipboard!</source> - <translation>Zugangspin wurde in die Zwischenablage kopiert!</translation> + <translation>Zugangs-PIN wurde in die Zwischenablage kopiert!</translation> </message> <message> <source>Copy the pin into the clipboard</source> @@ -107,6 +230,14 @@ <source>Start remote support session</source> <translation>Starte Fernwartungssitzung</translation> </message> + <message> + <source>Stop remote support session</source> + <translation>Stoppe Fernwartungssitzung</translation> + </message> + <message> + <source>Unknown state of session service.</source> + <translation>Unbekannter Status der Sitzungverwaltung.</translation> + </message> </context> <context> <name>Scene_remote_view</name> @@ -158,18 +289,10 @@ <context> <name>Scene_step_1</name> <message> - <source>Successfully added server address.</source> - <translation>Erfolgreich Server hinzugefügt.</translation> - </message> - <message> <source>Next Step</source> <translation>Nächster Schritt</translation> </message> <message> - <source>Step 1</source> - <translation>Schritt 1</translation> - </message> - <message> <source>Please input the address for the remote web app server which you want to connect to. If you don't know what this means, ask your local administrator about it please. Before you can start any remote sessions you will have to be approved for remote support.</source> @@ -182,30 +305,20 @@ Bevor Sie eine Fernwartungssitzung starten können, müssen Sie zuerst für das <translation></translation> </message> <message> - <source>RWA-server address</source> - <translation>RWA-Server Adresse</translation> - </message> -</context> -<context> - <name>Scene_step_2</name> - <message> - <source>Step 2</source> - <translation>Schritt 2</translation> + <source>Successfully added remote web app host.</source> + <translation>Erfolgreich Fernwartungsserver hinzugefügt.</translation> </message> <message> - <source>Next Step</source> - <translation>Nächster Schritt</translation> + <source>RWA host address</source> + <translation>RWA-Server Adressse</translation> </message> -</context> -<context> - <name>Scene_step_3</name> <message> - <source>Step 3</source> - <translation type="vanished">Schritt 3</translation> + <source>My example host</source> + <translation>Mein Beispiel Server</translation> </message> <message> - <source><- First step</source> - <translation type="vanished"><- Erster Schritt</translation> + <source>RWA host alias</source> + <translation>RWA-Server Alias</translation> </message> </context> <context> @@ -215,95 +328,32 @@ Bevor Sie eine Fernwartungssitzung starten können, müssen Sie zuerst für das <translation>Noch nicht verfügbar</translation> </message> <message> - <source>Unknown state of service</source> - <translation>Unbekannter Status des Dienstes</translation> - </message> - <message> - <source>Remote Support Session is up and running</source> - <translation type="vanished">Fernwartungssitzung läuft</translation> - </message> - <message> - <source>Remote Support Session was stopped ungracefully</source> - <translation type="vanished">Fernwartungssitzung wurde unvorhergesehen beendet</translation> - </message> - <message> - <source>Remote Support Session was stopped</source> - <translation type="vanished">Fernwartungssitzung wurde beendet</translation> - </message> - <message> - <source>Remote Support for your Desktop</source> - <translation type="vanished">Fernwartung für den Desktop</translation> - </message> - <message> - <source>D-Bus response was invalid. -Function call: '%1' -Maybe service isn't listening?</source> - <translation type="vanished">D-Bus Antwort auf Funktionsaufruf '%1' war nicht auswertbar -Ist der Dienst vielleicht nicht an?</translation> - </message> - <message> - <source>Remote Support Session is ready to be connected to</source> - <translation type="vanished">Fernwartungssitzung ist bereit um verbunden zu werden</translation> - </message> - <message> - <source>Your partner is connected to the Remote Support Session</source> - <translation type="vanished">Fernwartungspartner ist verbunden</translation> - </message> - <message> - <source>Remote Support session is ready to be connected to</source> - <translation>Bereit um eine Fernwartungssitzung aufzubauen</translation> - </message> - <message> - <source>Remote Support session was stopped ungracefully</source> - <translation>Fernwartungssitzung wurde unerwartet beendet</translation> - </message> - <message> - <source>Remote Support session was stopped</source> - <translation>Fernwartungssitzung wurde beendet</translation> + <source>An error occured while creating a new session!</source> + <translation>Ein Fehler trat auf bei dem Versuch eine Sitzung zu erstellen!</translation> </message> <message> - <source>Your partner is connected to the Remote Support session</source> - <translation>Fernwartungspartner ist mit dieser Sitzung verbunden</translation> + <source>The session service is configured to not support multiple sessions and there is already a session running.</source> + <translation>Die Sitzungsverwaltung ist so konfiguriert, dass nur eine Sitzung erlaubt ist und es ist bereits eine Sitzung am laufen.</translation> </message> <message> - <source>Trying to reach session service...</source> - <translation>Versuche Sitzungsdienst zu kontaktieren...</translation> + <source>Couldn't connect to host '%0'.</source> + <translation>Konnte nicht zu RWA-Server '%0' verbinden.</translation> </message> <message> - <source>Remote Support session couldn't be started!</source> - <translation>Fernwartungssitzung konnte nicht gestartet werden!</translation> - </message> - <message> - <source>An error occured while trying to start a session!</source> - <translation>Ein Fehler trat auf bei dem Versuch eine Sitzung zu starten!</translation> + <source>The RWA host '%0' couldn't be found.</source> + <translation>Konnte RWA-Server '%0' nicht finden.</translation> </message> <message> - <source>Remote Support session successfully started!</source> - <translation>Fernwartungssitzung erfolgreich gestartet!</translation> + <source>The RWA host '%0' doesn't grant access.</source> + <translation>Der RWA-Server '%0' gewährt keinen Zugang.</translation> </message> <message> - <source>Session was started successfully</source> - <translation>Sitzung wurde erfolgreich gestartet</translation> + <source>The RWA host '%0' is not supported.</source> + <translation>Der RWA-Server '%0' wird nicht unterstützt.</translation> </message> <message> - <source>Session status could not be refreshed!</source> - <translation>Sitzungstatus konnte nicht erneuert werden!</translation> - </message> - <message> - <source>Session status could not be refreshed, remote support partner could still be connected!</source> - <translation type="vanished">Sitzungstatus konnte nicht erneuert werden! Es ist möglich, dass der Fernwartungspartner noch verbunden ist!</translation> - </message> - <message> - <source>Session could not be stopped!</source> - <translation>Sitzung konnte nicht gestoppt werden!</translation> - </message> - <message> - <source>Session could not be stopped, remote support partner could still be connected!</source> - <translation type="vanished">Sitzung konnte nicht gestoppt werden! Es ist möglich, dass der Fernwartungspartner noch verbunden ist!</translation> - </message> - <message> - <source>remote support partner could still be connected!</source> - <translation>Es ist möglich, dass Ihr Partner noch verbunden ist!</translation> + <source>Can't connect to underlying session service! Is the session service started?</source> + <translation>Es kann keine Verbindung zur Sitzungsverwaltung hergestellt werden! Ist die Sitzungsverwaltung eingeschaltet?</translation> </message> </context> <context> @@ -313,30 +363,10 @@ Ist der Dienst vielleicht nicht an?</translation> <translation>Fernwartung für den Desktop</translation> </message> <message> - <source>Allow remote desktop support</source> - <translation type="vanished">Fernwartungssitzung starten</translation> - </message> - <message> - <source>Unknown state of service</source> - <translation type="vanished">Unbekannter Status des Dienstes</translation> - </message> - <message> - <source>Remote Support URL</source> - <translation type="vanished">Fernwartungs-URL</translation> - </message> - <message> - <source>Access pin to this computer</source> - <translation type="vanished">Zugangspin zu diesem Computer</translation> - </message> - <message> <source>Allow Remote Control</source> <translation>Fernwartungssitzung erlauben</translation> </message> <message> - <source>Please tell your partner your PIN and the Support URL to connect to this computer</source> - <translation type="vanished">Bitte gebe deinem Fernwartungspartner die Zugangspin und die Fernwartungs-URL um sich mit diesem Computer verbinden zu können</translation> - </message> - <message> <source>Remote Control</source> <translation>Fernwartung</translation> </message> @@ -354,74 +384,10 @@ This is a bug.</source> <translation>Diese Nachricht sollte nie gesehen werden. Das ist ein Bug.</translation> </message> <message> - <source>Start remote support session</source> - <translation type="vanished">Starte Fernwartungssitzung</translation> - </message> - <message> - <source>Copy the URL into the clipboard</source> - <translation type="vanished">Kopiere URL in die Zwischenablage</translation> - </message> - <message> - <source>Copy the pin into the clipboard</source> - <translation type="vanished">Kopiere Zugangspin in die Zwischenablage</translation> - </message> - <message> - <source>Copied URL into clipboard!</source> - <translation type="vanished">URL in die Zwischenablage kopiert!</translation> - </message> - <message> - <source>Copied PIN into clipboard!</source> - <translation type="vanished">Zugangspin wurde in die Zwischenablage kopiert!</translation> - </message> - <message> - <source>Please tell your remote support partner your personal access address and your access-PIN to let your partner connect to this computer.</source> - <translation type="vanished">Bitte teile deinem Fernwartungspartner deine persönliche Zugangsadresse und deine Zugangs-PIN mit, um sich mit diesem Computer verbinden zu können.</translation> - </message> - <message> - <source>Remote Support Address</source> - <translation type="vanished">Zugangsadresse für Fernwartung</translation> - </message> - <message> - <source>Personal Access-PIN</source> - <translation type="vanished">Persönliche Zugangs-PIN</translation> - </message> - <message> - <source>Title %1</source> - <translation type="vanished">Titel %1</translation> - </message> - <message> <source>Dark theme</source> <translation>Dunkelmodus</translation> </message> <message> - <source>Access-PIN</source> - <translation type="vanished">Zugangs-PIN</translation> - </message> - <message> - <source>Please tell your remote support partner your access address and your access-PIN to let your partner connect to this computer.</source> - <translation type="vanished">Bitte teile deinem Fernwartungspartner deine Zugangsadresse und deine Zugangs-PIN mit, um sich mit diesem Computer verbinden zu können.</translation> - </message> - <message> - <source>Copied access address into clipboard!</source> - <translation type="vanished">Zugangsadresse wurde in die Zwischenablage kopiert!</translation> - </message> - <message> - <source>Copy the access address into the clipboard</source> - <translation type="vanished">Kopiere Zugangsadresse in die Zwischenablage</translation> - </message> - <message> - <source>Session-ID</source> - <translation type="vanished">Sitzungs-ID</translation> - </message> - <message> - <source>Copied session-ID into clipboard!</source> - <translation type="vanished">Sitzungs-ID wurde in die Zwischenablage kopiert!</translation> - </message> - <message> - <source>Copy the session-ID into the clipboard</source> - <translation type="vanished">Kopiere Sitzungs-ID in die Zwischenablage</translation> - </message> - <message> <source>Allow remote control</source> <translation>Fernwartungssitzung erlauben</translation> </message> diff --git a/locales/es_ES.qm b/locales/es_ES.qm Binary files differnew file mode 100644 index 0000000..2d4045c --- /dev/null +++ b/locales/es_ES.qm diff --git a/locales/es_ES.ts b/locales/es_ES.ts index f7022de..f69359e 100644 --- a/locales/es_ES.ts +++ b/locales/es_ES.ts @@ -2,7 +2,7 @@ <!DOCTYPE TS> <TS version="2.1" language="es_ES"> <context> - <name>Add_Server_wizard</name> + <name>AddRWAHostWizard</name> <message> <source>Couldn't connect to the specified host!</source> <translation type="unfinished"></translation> @@ -16,57 +16,176 @@ <translation type="unfinished"></translation> </message> <message> - <source>This field can't be empty!</source> + <source>The features you expected here are not yet implemented.</source> <translation type="unfinished"></translation> </message> <message> - <source>The feature you expected here are not yet implemented.</source> + <source>An error occured while adding a new host!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Both textfields can't be empty!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The specified host address does not grant access!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The specified host address is not supported!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Can't connect to underlying session service!</source> <translation type="unfinished"></translation> </message> </context> <context> <name>MainQMLAdaptor</name> <message> - <source>Stop remote support session</source> - <translation></translation> + <source>A host object in the response of D-Bus service lacks a necessary value. (host_url or host_uuid)</source> + <translation type="unfinished"></translation> </message> <message> - <source>Start remote support session</source> + <source>An error occured while adding a new host:</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Successfully added new RWAHost '%0'</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The error is not clear. The session service responded with status type '%0'</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The error is not clear. The session service responded with no status type!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>No RWA host available!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Can't connect to underlying session service!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Removed RWAHost '%0'</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>QObject</name> + <name>RemoteControlManager</name> + <message> + <source>Stop remote support session</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Start remote support session</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Not available yet</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Unknown state of service</source> + <translation>Estado de servicio desconocido</translation> + </message> + <message> + <source>Remote Support session was stopped ungracefully</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Remote Support session was stopped</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Your partner is connected to the Remote Support session</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Remote Support session couldn't be started!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Session was started on '%0' successfully</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>remote support partner could still be connected!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Session could not be stopped!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Session status could not be refreshed! Your remote support partner could still be connected!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Couldn't remove current session out of '_sessions' list.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>currentSessionStartSucceeded(): Current Session is nullptr!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Remote support session was stopped.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Remote Support session successfully started! Waiting for your remote support partner to connect.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Session stopped successfully.</source> + <translation type="unfinished"></translation> + </message> <message> - <source>You already have this app running.</source> + <source>Creating a new session object.</source> <translation type="unfinished"></translation> </message> <message> - <source>Only one instance is allowed.</source> + <source>Can't start a remote support session. There is no RWA host is selected!</source> <translation type="unfinished"></translation> </message> <message> - <source>Closing application now with an error.</source> + <source>RemoteControlManager::handleConnectButtonClick(): Current Session is nullptr!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Starting a remote support session on host '%0' using the new session object.</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>Scene_placeholder</name> + <name>Scene_no_server_available</name> <message> - <source>This is the placeholder scene!</source> + <source>Welcome!</source> <translation type="unfinished"></translation> </message> <message> - <source>The feature you expected here are not yet implemented.</source> + <source>You need to add and select the remote web app server to which you want to connect. You can see a button to the left in the menu which says 'Add RWA-Server'. Follow the steps listed there and you can start your remote support session afterwards using the buttons above 'Add RWA-Server'.</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>Scene_remote_control</name> + <name>Scene_placeholder</name> <message> - <source>Please tell your remote support partner your access address and your access-PIN to let your partner connect to this computer.</source> + <source>This is the placeholder scene!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The features you expected here are not yet implemented.</source> <translation type="unfinished"></translation> </message> +</context> +<context> + <name>Scene_remote_control</name> <message> <source>Remote Support Address</source> <translation type="unfinished"></translation> @@ -107,6 +226,18 @@ <source>Start remote support session</source> <translation type="unfinished"></translation> </message> + <message> + <source>Stop remote support session</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Please tell your remote support partner your access address and your access-PIN to let your partner connect to this computer.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Unknown state of session service.</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>Scene_remote_view</name> @@ -158,18 +289,10 @@ <context> <name>Scene_step_1</name> <message> - <source>Successfully added server address.</source> - <translation type="unfinished"></translation> - </message> - <message> <source>Next Step</source> <translation type="unfinished"></translation> </message> <message> - <source>Step 1</source> - <translation type="unfinished"></translation> - </message> - <message> <source>Please input the address for the remote web app server which you want to connect to. If you don't know what this means, ask your local administrator about it please. Before you can start any remote sessions you will have to be approved for remote support.</source> @@ -180,81 +303,54 @@ Before you can start any remote sessions you will have to be approved for remote <translation></translation> </message> <message> - <source>RWA-server address</source> + <source>Successfully added remote web app host.</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>Scene_step_2</name> <message> - <source>Step 2</source> + <source>RWA host address</source> <translation type="unfinished"></translation> </message> <message> - <source>Next Step</source> + <source>My example host</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>RWA host alias</source> <translation type="unfinished"></translation> </message> </context> <context> <name>Session</name> <message> - <source>Unknown state of service</source> - <translation>Estado de servicio desconocido</translation> - </message> - <message> - <source>Remote Support for your Desktop</source> - <translation type="obsolete">Soporte remoto para su escritorio</translation> - </message> - <message> <source>Not available yet</source> <translation type="unfinished"></translation> </message> <message> - <source>Remote Support session is ready to be connected to</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Remote Support session was stopped ungracefully</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Remote Support session was stopped</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Your partner is connected to the Remote Support session</source> + <source>An error occured while creating a new session!</source> <translation type="unfinished"></translation> </message> <message> - <source>Trying to reach session service...</source> + <source>The session service is configured to not support multiple sessions and there is already a session running.</source> <translation type="unfinished"></translation> </message> <message> - <source>Remote Support session couldn't be started!</source> + <source>Couldn't connect to host '%0'.</source> <translation type="unfinished"></translation> </message> <message> - <source>An error occured while trying to start a session!</source> + <source>The RWA host '%0' couldn't be found.</source> <translation type="unfinished"></translation> </message> <message> - <source>Remote Support session successfully started!</source> + <source>The RWA host '%0' doesn't grant access.</source> <translation type="unfinished"></translation> </message> <message> - <source>Session was started successfully</source> + <source>The RWA host '%0' is not supported.</source> <translation type="unfinished"></translation> </message> <message> - <source>Session status could not be refreshed!</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>remote support partner could still be connected!</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Session could not be stopped!</source> + <source>Can't connect to underlying session service! Is the session service started?</source> <translation type="unfinished"></translation> </message> </context> @@ -265,18 +361,6 @@ Before you can start any remote sessions you will have to be approved for remote <translation>Soporte remoto para su escritorio</translation> </message> <message> - <source>Unknown state of service</source> - <translation type="vanished">Estado de servicio desconocido</translation> - </message> - <message> - <source>Allow remote desktop support</source> - <translation type="vanished">Permitir soporte de escritorio remoto</translation> - </message> - <message> - <source>Access pin to this computer</source> - <translation type="vanished">Pin de acceso a esta computadora</translation> - </message> - <message> <source>You are not supposed to see this message. This is a bug.</source> <translation type="unfinished"></translation> diff --git a/locales/main_en.qm b/locales/main_en.qm Binary files differnew file mode 100644 index 0000000..a0de2c8 --- /dev/null +++ b/locales/main_en.qm diff --git a/locales/main_en.ts b/locales/main_en.ts index a8cbc2a..7e033d1 100644 --- a/locales/main_en.ts +++ b/locales/main_en.ts @@ -2,7 +2,7 @@ <!DOCTYPE TS> <TS version="2.1" language="en_US"> <context> - <name>Add_Server_wizard</name> + <name>AddRWAHostWizard</name> <message> <source>Couldn't connect to the specified host!</source> <translation type="unfinished"></translation> @@ -16,57 +16,176 @@ <translation type="unfinished"></translation> </message> <message> - <source>This field can't be empty!</source> + <source>The features you expected here are not yet implemented.</source> <translation type="unfinished"></translation> </message> <message> - <source>The feature you expected here are not yet implemented.</source> + <source>An error occured while adding a new host!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Both textfields can't be empty!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The specified host address does not grant access!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The specified host address is not supported!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Can't connect to underlying session service!</source> <translation type="unfinished"></translation> </message> </context> <context> <name>MainQMLAdaptor</name> <message> - <source>Stop remote support session</source> + <source>A host object in the response of D-Bus service lacks a necessary value. (host_url or host_uuid)</source> <translation></translation> </message> <message> - <source>Start remote support session</source> + <source>An error occured while adding a new host:</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Successfully added new RWAHost '%0'</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The error is not clear. The session service responded with status type '%0'</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The error is not clear. The session service responded with no status type!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>No RWA host available!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Can't connect to underlying session service!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Removed RWAHost '%0'</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>QObject</name> + <name>RemoteControlManager</name> + <message> + <source>Stop remote support session</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Start remote support session</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Not available yet</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Unknown state of service</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Remote Support session was stopped ungracefully</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Remote Support session was stopped</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Your partner is connected to the Remote Support session</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Remote Support session couldn't be started!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Session was started on '%0' successfully</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>remote support partner could still be connected!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Session could not be stopped!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Session status could not be refreshed! Your remote support partner could still be connected!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Couldn't remove current session out of '_sessions' list.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>currentSessionStartSucceeded(): Current Session is nullptr!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Remote support session was stopped.</source> + <translation></translation> + </message> + <message> + <source>Remote Support session successfully started! Waiting for your remote support partner to connect.</source> + <translation></translation> + </message> + <message> + <source>Session stopped successfully.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Creating a new session object.</source> + <translation type="unfinished"></translation> + </message> <message> - <source>You already have this app running.</source> + <source>Can't start a remote support session. There is no RWA host is selected!</source> <translation type="unfinished"></translation> </message> <message> - <source>Only one instance is allowed.</source> + <source>RemoteControlManager::handleConnectButtonClick(): Current Session is nullptr!</source> <translation type="unfinished"></translation> </message> <message> - <source>Closing application now with an error.</source> + <source>Starting a remote support session on host '%0' using the new session object.</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>Scene_placeholder</name> + <name>Scene_no_server_available</name> <message> - <source>This is the placeholder scene!</source> + <source>Welcome!</source> <translation type="unfinished"></translation> </message> <message> - <source>The feature you expected here are not yet implemented.</source> + <source>You need to add and select the remote web app server to which you want to connect. You can see a button to the left in the menu which says 'Add RWA-Server'. Follow the steps listed there and you can start your remote support session afterwards using the buttons above 'Add RWA-Server'.</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>Scene_remote_control</name> + <name>Scene_placeholder</name> <message> - <source>Please tell your remote support partner your access address and your access-PIN to let your partner connect to this computer.</source> + <source>This is the placeholder scene!</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>The features you expected here are not yet implemented.</source> <translation type="unfinished"></translation> </message> +</context> +<context> + <name>Scene_remote_control</name> <message> <source>Remote Support Address</source> <translation type="unfinished"></translation> @@ -107,6 +226,18 @@ <source>Start remote support session</source> <translation type="unfinished"></translation> </message> + <message> + <source>Stop remote support session</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Please tell your remote support partner your access address and your access-PIN to let your partner connect to this computer.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Unknown state of session service.</source> + <translation></translation> + </message> </context> <context> <name>Scene_remote_view</name> @@ -158,18 +289,10 @@ <context> <name>Scene_step_1</name> <message> - <source>Successfully added server address.</source> - <translation type="unfinished"></translation> - </message> - <message> <source>Next Step</source> <translation type="unfinished"></translation> </message> <message> - <source>Step 1</source> - <translation></translation> - </message> - <message> <source>Please input the address for the remote web app server which you want to connect to. If you don't know what this means, ask your local administrator about it please. Before you can start any remote sessions you will have to be approved for remote support.</source> @@ -180,18 +303,19 @@ Before you can start any remote sessions you will have to be approved for remote <translation></translation> </message> <message> - <source>RWA-server address</source> - <translation type="unfinished"></translation> + <source>Successfully added remote web app host.</source> + <translation></translation> </message> -</context> -<context> - <name>Scene_step_2</name> <message> - <source>Step 2</source> + <source>RWA host address</source> + <translation></translation> + </message> + <message> + <source>My example host</source> <translation type="unfinished"></translation> </message> <message> - <source>Next Step</source> + <source>RWA host alias</source> <translation type="unfinished"></translation> </message> </context> @@ -202,55 +326,31 @@ Before you can start any remote sessions you will have to be approved for remote <translation type="unfinished"></translation> </message> <message> - <source>Unknown state of service</source> + <source>An error occured while creating a new session!</source> <translation type="unfinished"></translation> </message> <message> - <source>Remote Support session is ready to be connected to</source> + <source>The session service is configured to not support multiple sessions and there is already a session running.</source> <translation type="unfinished"></translation> </message> <message> - <source>Remote Support session was stopped ungracefully</source> + <source>Couldn't connect to host '%0'.</source> <translation type="unfinished"></translation> </message> <message> - <source>Remote Support session was stopped</source> + <source>The RWA host '%0' couldn't be found.</source> <translation type="unfinished"></translation> </message> <message> - <source>Your partner is connected to the Remote Support session</source> + <source>The RWA host '%0' doesn't grant access.</source> <translation type="unfinished"></translation> </message> <message> - <source>Trying to reach session service...</source> + <source>The RWA host '%0' is not supported.</source> <translation type="unfinished"></translation> </message> <message> - <source>Remote Support session couldn't be started!</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>An error occured while trying to start a session!</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Remote Support session successfully started!</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Session was started successfully</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Session status could not be refreshed!</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>remote support partner could still be connected!</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Session could not be stopped!</source> + <source>Can't connect to underlying session service! Is the session service started?</source> <translation type="unfinished"></translation> </message> </context> @@ -1,13 +1,17 @@ <RCC> <qresource prefix="/"> + <file>qtquickcontrols2.conf</file> <file>images/into-clipboard.svg</file> - <file>locales/bin/de_DE.qm</file> - <file>locales/bin/main_en.qm</file> <file>locales/de_DE.ts</file> + <file>locales/de_DE.qm</file> <file>locales/main_en.ts</file> - <file>locales/bin/es_ES.qm</file> + <file>locales/main_en.qm</file> <file>locales/es_ES.ts</file> + <file>locales/es_ES.qm</file> <file>src/main.qml</file> + <file>src/ListItem.qml</file> + <file>src/ToastManager.qml</file> + <file>src/Toast.qml</file> <file>src/main.cpp</file> <file>src/main_qmladaptor.cpp</file> <file>src/main_qmladaptor.h</file> @@ -15,17 +19,21 @@ <file>src/RWADBusAdaptor.h</file> <file>src/session.cpp</file> <file>src/session.h</file> - <file>src/ListItem.qml</file> - <file>src/ToastManager.qml</file> - <file>src/Toast.qml</file> - <file>qtquickcontrols2.conf</file> - <file>src/scenes/Scene_remote_control.qml</file> + <file>src/RWAHost.h</file> + <file>src/RWAHost.cpp</file> + <file>src/RWAHostModel.h</file> + <file>src/RWAHostModel.cpp</file> + <file>src/DBusAPI.h</file> + <file>src/DBusAPI.cpp</file> <file>src/scenes/Scene_remote_view.qml</file> <file>src/scenes/Scene_settings.qml</file> <file>src/scenes/Scene_placeholder.qml</file> - <file>src/scenes/add_server_wizard/Scene_step_1.qml</file> - <file>src/scenes/add_server_wizard/Scene_step_2.qml</file> - <file>src/scenes/add_server_wizard/add_server_wizard.cpp</file> - <file>src/scenes/add_server_wizard/add_server_wizard.h</file> + <file>src/scenes/Scene_no_server_available.qml</file> + <file>src/scenes/add_rwahost_wizard/add_rwahost_wizard.cpp</file> + <file>src/scenes/add_rwahost_wizard/add_rwahost_wizard.h</file> + <file>src/scenes/add_rwahost_wizard/Scene_step_1.qml</file> + <file>src/scenes/remote_control/remote_control_manager.cpp</file> + <file>src/scenes/remote_control/remote_control_manager.h</file> + <file>src/scenes/remote_control/Scene_remote_control.qml</file> </qresource> </RCC> diff --git a/qtquickcontrols2.conf b/qtquickcontrols2.conf index e646650..8bcc85b 100644 --- a/qtquickcontrols2.conf +++ b/qtquickcontrols2.conf @@ -1,7 +1,7 @@ # This file is part of Remote Support Desktop # https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp -# Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> -# Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> +# Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> +# Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> # SPDX-License-Identifier: GPL-2.0-or-later # # This program is free software; you can redistribute it and/or modify diff --git a/rwa-support-desktopapp.pro b/rwa-support-desktopapp.pro index 095db2d..96ecec3 100644 --- a/rwa-support-desktopapp.pro +++ b/rwa-support-desktopapp.pro @@ -41,19 +41,33 @@ DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 +# disables all the APIs deprecated before Qt 6.0.0 SOURCES += src/main.cpp \ src/main_qmladaptor.cpp \ src/RWADBusAdaptor.cpp \ src/session.cpp \ - src/scenes/add_server_wizard/add_server_wizard.cpp + src/RWAHostModel.cpp \ + src/RWAHost.cpp \ + src/DBusAPI.cpp \ + src/scenes/remote_control/remote_control_manager.cpp \ + src/scenes/add_rwahost_wizard/add_rwahost_wizard.cpp HEADERS += src/RWADBusAdaptor.h \ src/main_qmladaptor.h \ src/RWADBusAdaptor.h \ + src/scenes/remote_control/remote_control_manager.h \ src/session.h \ - src/scenes/add_server_wizard/add_server_wizard.h + src/RWAHostModel.h \ + src/RWAHost.h \ + src/DBusAPI.h \ + src/scenes/remote_control/remote_control_manager.h \ + src/scenes/add_rwahost_wizard/add_rwahost_wizard.h + +TRANSLATIONS += locales/main_en.ts \ + locales/de_DE.ts \ + locales/es_ES.ts RESOURCES += qml.qrc @@ -68,4 +82,6 @@ qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target -DISTFILES += +#DISTFILES += + +CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT diff --git a/src/DBusAPI.cpp b/src/DBusAPI.cpp new file mode 100644 index 0000000..9a0f8de --- /dev/null +++ b/src/DBusAPI.cpp @@ -0,0 +1,423 @@ +/* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "DBusAPI.h" + +/*! + * \class DBusAPI + * \brief The DBusAPI class provides all necessary D-Bus methods and distributes the response via signals. + */ +DBusAPI::DBusAPI() { + _initDBus(); +} + +/*! + * \brief Initializes private _dbus_rwa object. + */ +void DBusAPI::_initDBus() { + if (!QDBusConnection::sessionBus().isConnected()) { + qCritical() << "Cannot connect to the D-Bus session bus."; + } + + // Create DBus object + _dbus_rwa = new OrgArcticaProjectRWASupportSessionServiceInterface("org.ArcticaProject.RWASupportSessionService", + "/RWASupportSessionService", + QDBusConnection::sessionBus(), + this->parent()); + + qDebug("Initialized DBus object!"); +} + +/*! + * \brief Ask the session service to start a session. + * + * \a host RWAHost object which includes all necessary information about a RWA host. + */ +void DBusAPI::start_request(RWAHost *host) { + Q_ASSERT(host != nullptr); + + qDebug() << "Requesting D-Bus service to start a new session on host:" << host->alias(); + + // Make an asynchronous 'start' call (Response will be sent to 'start_reply') + QDBusPendingCall async = _dbus_rwa->asyncCall("start", host->uuid()); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(start_reply(QDBusPendingCallWatcher*))); +} + +/*! + * \brief Method gets called when a D-Bus response is ready. + * + * \a call contains the D-Bus response (session service or for example maybe just a error message). + * + * TODO: Example of json input + */ +void DBusAPI::start_reply(QDBusPendingCallWatcher *call) { + QString result = ""; + QDBusPendingReply<QString> reply = *call; + if (reply.isError()) { + qCritical() << "D-Bus 'start' request failed, this was the reply:"; + qCritical() << reply.error(); + + if (QDBusError::ServiceUnknown == reply.error().type()) { + qCritical() << "The session service was probably just not started!"; + } + + emit serviceStartResponse(nullptr); + + return; + } else { + result = reply.argumentAt<0>(); + } + call->deleteLater(); + + qDebug() << "Raw JSON from starting session is:" << result.toUtf8().replace('"', ""); + QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); + + emit serviceStartResponse(&doc); +} + +/*! + * \brief Ask the session service to stop session #<session_id>. + * + * \a host RWAHost object which includes all necessary information about a RWA host. + * + * \a session_id Unique identifier for a session in a specific host. + */ +void DBusAPI::stop_request(RWAHost *host, QString session_id) { + Q_ASSERT(host != nullptr); + Q_ASSERT(session_id != ""); + + bool ok; + long long session_id_number = session_id.toLongLong(&ok); + + // Sanity Check + if(ok == false){ + qWarning().noquote() << QString("Unable to parse session_id '%0' as long long!").arg(session_id); + return; + } + + qDebug().noquote() << QString("Requesting D-Bus service to stop " + "session #'%0' on host '%1'") + .arg(session_id) + .arg(host->alias()); + + // Make an asynchronous 'stop' call (Response will be sent to 'stop_reply') + QDBusPendingCall async = _dbus_rwa->asyncCall("stop", host->uuid(), session_id_number); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(stop_reply(QDBusPendingCallWatcher*))); +} + +/*! + * \brief Method gets called when a D-Bus response is ready. + * + * \a call contains the D-Bus response (session service or for example maybe just a error message). + * + * TODO: Example of json input + */ +void DBusAPI::stop_reply(QDBusPendingCallWatcher *call) { + QString result = ""; + + QDBusPendingReply<QString> reply = *call; + if (reply.isError()) { + qCritical() << "D-Bus 'stop' request failed, this was the reply:"; + qCritical() << reply.error(); + + if (QDBusError::ServiceUnknown == reply.error().type()) { + qCritical() << "The session service was probably just not started!"; + } + + emit serviceStopResponse(nullptr); + + return; + } else { + result = reply.argumentAt<0>(); + } + call->deleteLater(); + + qDebug() << "Raw JSON from stopping a session is:" << result.toUtf8().replace('"', ""); + QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); + + emit serviceStopResponse(&doc); +} + +/*! + * \brief Ask the session service for status of <session_id>. + * + * \a host RWAHost object which includes all necessary information about a RWA host. + * + * \a session_id Unique identifier for a session in a specific host. + */ +void DBusAPI::status_request(RWAHost *host, QString session_id) { + Q_ASSERT(host != nullptr); + Q_ASSERT(session_id != ""); + + bool ok; + long long session_id_number = session_id.toLongLong(&ok); + + // Sanity Check + if(ok == false){ + qWarning().noquote() << QString("Unable to parse session_id '%0' as long long!").arg(session_id); + return; + } + + qDebug().noquote() << QString("Requesting D-Bus service for status of session " + "#'%0' on host '%1'").arg(session_id).arg(host->alias()); + + // Make an asynchronous 'start' call (Response will be sent to 'status_reply') + QDBusPendingCall async = _dbus_rwa->asyncCall("status", host->uuid(), session_id_number); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(status_reply(QDBusPendingCallWatcher*))); +} + +/*! + * \brief Forces the session service to refresh it's status of <session_id> and tell us about it then. + * + * \a host RWAHost object which includes all necessary information about a RWA host. + * + * \a session_id Unique identifier for a session in a specific host. + */ +void DBusAPI::refresh_status_request(RWAHost *host, QString session_id) { + Q_ASSERT(host != nullptr); + Q_ASSERT(session_id != ""); + + bool ok; + long long session_id_number = session_id.toLongLong(&ok); + + // Sanity Check + if(ok == false){ + qWarning().noquote() << QString("Unable to parse session_id '%0' as long long!").arg(session_id); + return; + } + + qDebug().noquote() << QString("Requesting D-Bus service for refresh_status of session " + "#'%0' on host '%1'").arg(session_id).arg(host->alias()); + + // Make an asynchronous 'start' call (Response will be sent to 'status_reply') + QDBusPendingCall async = _dbus_rwa->asyncCall("refresh_status", host->uuid(), session_id_number); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(status_reply(QDBusPendingCallWatcher*))); +} + +/*! + * \brief Method gets called when a D-Bus response is ready. + * + * \a call contains the D-Bus response (session service or for example maybe just a error message). + * + * TODO: Example of json input + */ +void DBusAPI::status_reply(QDBusPendingCallWatcher *call){ + QString result = ""; + + QDBusPendingReply<QString> reply = *call; + if (reply.isError()) { + qCritical() << "D-Bus '(refresh_)status' request failed, this was the reply:"; + qCritical() << reply.error(); + + if (QDBusError::ServiceUnknown == reply.error().type()) { + qCritical() << "The session service was probably just not started!"; + } + + emit serviceStatusResponse(nullptr); + + return; + } else { + result = reply.argumentAt<0>(); + } + call->deleteLater(); + + // Get the QJsonObject + qDebug() << "Raw JSON from a status request for a session is:" << result.toUtf8().replace('"', ""); + QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); + + emit serviceStatusResponse(&doc); +} + +/*! + * \brief This method requests the session service to list all RWAHosts. + */ +void DBusAPI::get_web_app_hosts_request() { + qDebug().noquote() << QString("Requesting D-Bus service to list " + "all remote web app hosts"); + + // Make an asynchronous 'get_web_app_hosts' call + // Response will be sent to 'get_web_app_hosts_reply' + QDBusPendingCall async = _dbus_rwa->asyncCall("get_web_app_hosts"); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(get_web_app_hosts_reply(QDBusPendingCallWatcher*))); +} + +/*! + * \brief This method requests the session service to add a RWAHost to its configuration files. + * + * \a host_url is the remote web app adress which will be used by the session service to coordinate + * sessions, connections, settings and such. + */ +void DBusAPI::add_web_app_host_request(QString host_url, QString host_alias) { + Q_ASSERT(host_url != ""); + Q_ASSERT(host_alias != ""); + + qDebug().noquote() << QString("Requesting D-Bus service to register new " + "remote web app host '%0' with url '%1'").arg(host_alias).arg(host_url); + + // Make an asynchronous 'add_web_app_host' call + // Response will be sent to 'add_web_app_host_reply' + QDBusPendingCall async = _dbus_rwa->asyncCall("add_web_app_host", host_url, host_alias); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(add_web_app_host_reply(QDBusPendingCallWatcher*))); +} + +/*! + * \brief This method requests the session service to remove a RWAHost from its configuration files. + * + * \a host_uuid Unique identifier which all hosts have. + */ +void DBusAPI::remove_web_app_host_request(QString host_uuid) { + Q_ASSERT(host_uuid != ""); + + qDebug().noquote() << QString("Requesting D-Bus service to list " + "all remote web app hosts"); + + // Make an asynchronous 'remove_web_app_host' call + // Response will be sent to 'remove_web_app_host_reply' + QDBusPendingCall async = _dbus_rwa->asyncCall("remove_web_app_host", host_uuid); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(remove_web_app_host_reply(QDBusPendingCallWatcher*))); +} + +/*! + * \brief Method gets called when a D-Bus response is ready. + * + * \a call contains the D-Bus response (session service or for example maybe just a error message). + * + * TODO: Example of json input + */ +void DBusAPI::get_web_app_hosts_reply(QDBusPendingCallWatcher *call){ + QString result = ""; + + QDBusPendingReply<QString> reply = *call; + if (reply.isError()) { + qCritical() << "D-Bus 'get_web_app_hosts' request failed, this was the reply:"; + qCritical() << reply.error(); + + if (QDBusError::ServiceUnknown == reply.error().type()) { + qCritical() << "The session service was probably just not started!"; + } + + emit serviceGetWebAppHostsResponse(nullptr); + + return; + } else { + result = reply.argumentAt<0>(); + } + call->deleteLater(); + + // Get the QJsonObject + qDebug() << "Raw JSON from the remote web app host listing request:" << result.toUtf8().replace('"', ""); + QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); + + emit serviceGetWebAppHostsResponse(&doc); +} + +/*! + * \brief Method gets called when a D-Bus response is ready. + * + * \a call contains the D-Bus response (session service or for example maybe just a error message). + * + * TODO: Example of json input + */ +void DBusAPI::add_web_app_host_reply(QDBusPendingCallWatcher *call){ + QString result = ""; + + QDBusPendingReply<QString> reply = *call; + if (reply.isError()) { + qCritical() << "D-Bus 'add_web_app_host' request failed, this was the reply:"; + qCritical() << reply.error(); + + if (QDBusError::ServiceUnknown == reply.error().type()) { + qCritical() << "The session service was probably just not started!"; + } + + emit serviceAddWebAppHostResponse(nullptr); + + return; + } else { + result = reply.argumentAt<0>(); + } + call->deleteLater(); + + // Get the QJsonObject + qDebug() << "Raw JSON from the remote web app host register request:" << result.toUtf8().replace('"', ""); + QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); + + emit serviceAddWebAppHostResponse(&doc); +} + +/*! + * \brief Method gets called when a D-Bus response is ready. + * + * \a call contains the D-Bus response (session service or for example maybe just a error message). + * + * TODO: Example of json input + */ +void DBusAPI::remove_web_app_host_reply(QDBusPendingCallWatcher *call){ + QString result = ""; + + QDBusPendingReply<QString> reply = *call; + if (reply.isError()) { + qCritical() << "D-Bus 'remove_web_app_host' request failed, this was the reply:"; + qCritical() << reply.error(); + + if (QDBusError::ServiceUnknown == reply.error().type()) { + qCritical() << "The session service was probably just not started!"; + } + + emit serviceRemoveWebAppHostResponse(nullptr); + + return; + } else { + result = reply.argumentAt<0>(); + } + call->deleteLater(); + + // Get the QJsonObject + qDebug() << "Raw JSON from the remote web app host deletion request:" << result.toUtf8().replace('"', ""); + QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); + + emit serviceRemoveWebAppHostResponse(&doc); +} diff --git a/src/DBusAPI.h b/src/DBusAPI.h new file mode 100644 index 0000000..63dbe68 --- /dev/null +++ b/src/DBusAPI.h @@ -0,0 +1,87 @@ +/* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef DBUSAPI_H +#define DBUSAPI_H + +#include <QObject> +#include <QtDBus/QtDBus> + +#include "RWAHost.h" +#include "RWADBusAdaptor.h" + +class DBusAPI : public QObject { + Q_OBJECT +public: + explicit DBusAPI(); + +signals: + void serviceStartResponse(QJsonDocument*); + void serviceStopResponse(QJsonDocument*); + void serviceStatusResponse(QJsonDocument*); + + void serviceGetWebAppHostsResponse(QJsonDocument*); + void serviceAddWebAppHostResponse(QJsonDocument*); + void serviceRemoveWebAppHostResponse(QJsonDocument*); + +public slots: + void start_reply(QDBusPendingCallWatcher *call); + void stop_reply(QDBusPendingCallWatcher *call); + void status_reply(QDBusPendingCallWatcher *call); + + void get_web_app_hosts_reply(QDBusPendingCallWatcher *call); + void add_web_app_host_reply(QDBusPendingCallWatcher *call); + void remove_web_app_host_reply(QDBusPendingCallWatcher *call); + + + // Starts a remote web app session + void start_request(RWAHost *host); + + // Stop a remote web app session + void stop_request(RWAHost *host, QString session_id); + + // Refreshes a remote web app session's status + void refresh_status_request(RWAHost *host, QString session_id); + + // Gets a remote web app session's status + void status_request(RWAHost *host, QString session_id); + + // Gets all remote web app hosts deposited in the session server + void get_web_app_hosts_request(); + + // Add a specific remote web app host using a url to make sure its contactable + void add_web_app_host_request(QString host_url, QString host_alias); + + // Removes a specific remote web app host using the uuid of a host + void remove_web_app_host_request(QString host_uuid); + +private: + OrgArcticaProjectRWASupportSessionServiceInterface* _dbus_rwa; + + void _initDBus(); + +}; + +#endif // DBUSAPI_H diff --git a/src/ListItem.qml b/src/ListItem.qml index c69af4b..fa83f4b 100644 --- a/src/ListItem.qml +++ b/src/ListItem.qml @@ -1,3 +1,28 @@ +/* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + import QtQuick 2.0 import QtQuick.Controls.Material 2.3 import QtQuick.Controls 2.2 diff --git a/src/RWADBusAdaptor.cpp b/src/RWADBusAdaptor.cpp index 381faca..6c1504e 100644 --- a/src/RWADBusAdaptor.cpp +++ b/src/RWADBusAdaptor.cpp @@ -1,4 +1,29 @@ /* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +/* * This file was generated by qdbusxml2cpp version 0.8 * Command line was: qdbusxml2cpp -i src/RWADBusAdaptor.h -p :src/RWADBusAdaptor.cpp rwa.xml * rwa.xml is the xml output of the Introspect D-Bus method diff --git a/src/RWADBusAdaptor.h b/src/RWADBusAdaptor.h index 741687f..77f4b6c 100644 --- a/src/RWADBusAdaptor.h +++ b/src/RWADBusAdaptor.h @@ -1,4 +1,29 @@ /* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +/* * This file was generated by qdbusxml2cpp version 0.8 * Command line was: qdbusxml2cpp -p src/RWADBusAdaptor.h: rwa.xml * rwa.xml is the xml output of the Introspect D-Bus method diff --git a/src/RWAHost.cpp b/src/RWAHost.cpp new file mode 100644 index 0000000..f31dc60 --- /dev/null +++ b/src/RWAHost.cpp @@ -0,0 +1,68 @@ +/* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "RWAHost.h" + +RWAHost::RWAHost(QString uuid, QString alias, QString url) { + assert(uuid != ""); + assert(alias != ""); + assert(url != ""); + + /*qDebug() << "Created new RWAHost object.\n\t" + << QString("uuid: '%0'").arg(uuid) << "\n\t" + << QString("alias: '%0'").arg(alias) << "\n\t" + << QString("url: '%0'").arg(url);*/ + + _url = url; + _alias = alias; + _uuid = uuid; +} + +QString RWAHost::uuid() const { + return _uuid; +} + +QString RWAHost::alias() const { + return _alias; +} + +QString RWAHost::url() const { + return _url; +} + +void RWAHost::setUuid(const QString &uuid) { + _uuid = uuid; + emit uuidChanged(uuid); +} + +void RWAHost::setAlias(const QString &alias) { + _alias = alias; + emit aliasChanged(alias); +} + +void RWAHost::setUrl(const QString &url) { + _url = url; + emit urlChanged(url); +} diff --git a/src/RWAHost.h b/src/RWAHost.h new file mode 100644 index 0000000..cd3b1ce --- /dev/null +++ b/src/RWAHost.h @@ -0,0 +1,63 @@ +/* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef RWAHOST_H +#define RWAHOST_H + +#include <QObject> +#include <QQuickItem> + +class RWAHost : public QObject { + Q_OBJECT + Q_PROPERTY(QString uuid READ uuid WRITE setUuid NOTIFY uuidChanged) + Q_PROPERTY(QString alias READ alias WRITE setAlias NOTIFY aliasChanged) + Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) + +public: + RWAHost(QString uuid = "", QString alias = "", QString url = ""); + RWAHost(const RWAHost&); + +private: + QString _uuid; + QString _alias; + QString _url; + +signals: + void uuidChanged(QString uuid); + void aliasChanged(QString alias); + void urlChanged(QString url); + +public slots: + QString uuid() const; + QString alias() const; + QString url() const; + + void setUuid(const QString &uuid); + void setAlias(const QString &alias); + void setUrl(const QString &url); + +}; + +#endif // RWAHOST_H diff --git a/src/RWAHostModel.cpp b/src/RWAHostModel.cpp new file mode 100644 index 0000000..5f62adc --- /dev/null +++ b/src/RWAHostModel.cpp @@ -0,0 +1,37 @@ +#include "RWAHostModel.h" + +RWAHostModel::RWAHostModel(QObject *parent) { + Q_UNUSED(parent) +} + +int RWAHostModel::rowCount(const QModelIndex& parent) const { + Q_UNUSED(parent); + return mDatas.size(); +} + +int RWAHostModel::columnCount(const QModelIndex& parent) const { + Q_UNUSED(parent); + return 1; +} + +QVariant RWAHostModel::data(const QModelIndex &index, int role) const + { + if (!index.isValid()) + return QVariant(); + if ( role == Qt::DisplayRole) { + return mDatas[index.row()]; + } + return QVariant(); +} + +void RWAHostModel::populate() { + beginResetModel(); + mDatas.clear(); + RWAHost *host1 = new RWAHost("uuid-1", "Erster Server", "url1"); + RWAHost *host2 = new RWAHost("uuid-2", "Zweiter Server", "url2"); + RWAHost *host3 = new RWAHost("uuid-3", "Dritter Server", "url3"); + mDatas.append(host1->alias()); + mDatas.append(host2->alias()); + mDatas.append(host3->alias()); + endResetModel(); +} diff --git a/src/RWAHostModel.h b/src/RWAHostModel.h new file mode 100644 index 0000000..8697df2 --- /dev/null +++ b/src/RWAHostModel.h @@ -0,0 +1,23 @@ +#ifndef RWAHOSTMODEL_H +#define RWAHOSTMODEL_H + +#include <QObject> +#include <QAbstractListModel> + +#include "RWAHost.h" + +class RWAHostModel : public QAbstractListModel { + Q_OBJECT + +public: + explicit RWAHostModel(QObject * parent = nullptr); + int rowCount(const QModelIndex& parent = QModelIndex()) const; + int columnCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + void populate(); + +private: + QStringList mDatas; +}; + +#endif // RWAHOSTMODEL_H diff --git a/src/Toast.qml b/src/Toast.qml index eb65b20..7a18dc8 100644 --- a/src/Toast.qml +++ b/src/Toast.qml @@ -1,8 +1,8 @@ /* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/remote-support-desktop - * Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> - * Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ import QtQuick 2.0 import QtQuick.Controls 2.0 import QtQuick.Controls.Material 2.3 +import rwa.toast.type 1.0 /** * adapted from StackOverflow: @@ -36,7 +37,8 @@ import QtQuick.Controls.Material 2.3 */ /** - * @brief An Android-like timed message text in a box that self-destroys when finished if desired + * @brief An Android-like timed message text in + * a box that self-destroys when finished if desired */ Control { @@ -49,20 +51,41 @@ Control { * * @param {string} text Text to show * @param {real} duration Duration to show in milliseconds, defaults to 3000 + * @param {enum} type Type of toast. Available is: + * ToastType.Standard, ToastType.Info, ToastType.Warning + * ToastType.Success, ToastType.Error */ - function show(text, duration) { + function show(text, duration, type) { message.text = text; - if (typeof duration !== "undefined") { // checks if parameter was passed + + // checks if parameter was passed + if (typeof duration !== "undefined") { time = Math.max(duration, 2 * fadeTime); - } - else { + } else { time = defaultTime; } - console.log("Showing a new toast with display time: " + time); + + if (typeof type !== "undefined" ) { + if (type === ToastType.ToastStandard) { + selectedColor = "#dcdedc"; + } else if (type === ToastType.ToastInfo) { + selectedColor = "#0d5eaf"; + } else if (type === ToastType.ToastSuccess) { + selectedColor = "#0daf36"; + } else if (type === ToastType.ToastWarning) { + selectedColor = "#efef2a"; + } else if (type === ToastType.ToastError) { + selectedColor = "#ed1212"; + } + } else { + selectedColor = "#dcdedc"; + } + animation.start(); } - property bool selfDestroying: false // whether this Toast will self-destroy when it is finished + // whether this Toast will self-destroy when it is finished + property bool selfDestroying: false /** * Private @@ -70,6 +93,7 @@ Control { id: root + property color selectedColor: "#dcdedc" readonly property real defaultTime: 3000 property real time: defaultTime readonly property real fadeTime: 300 @@ -86,6 +110,8 @@ Control { background: Rectangle { color: (Material.theme == Material.Dark) ? "#212121" : "#dcdedc" + border.color: selectedColor + border.width: 1.5 radius: margin } diff --git a/src/ToastManager.qml b/src/ToastManager.qml index b448905..2f1600f 100644 --- a/src/ToastManager.qml +++ b/src/ToastManager.qml @@ -1,8 +1,8 @@ /* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/remote-support-desktop - * Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> - * Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify @@ -42,9 +42,12 @@ ListView { * * @param {string} text Text to show * @param {real} duration Duration to show in milliseconds, defaults to 3000 + * @param {enum} type Type of toast. Available is: + * ToastType.Standard, ToastType.Info, ToastType.Warning + * ToastType.Success, ToastType.Error */ - function show(text, duration) { - model.insert(0, {text: text, duration: duration}); + function show(text, duration, type) { + model.insert(0, {text: text, duration: duration, type: type}); } /** @@ -71,11 +74,13 @@ ListView { delegate: Toast { Component.onCompleted: { - if (typeof duration === "undefined") { - show(text); - } - else { - show(text, duration); + if (typeof duration === "undefined" && typeof type === "undefined") { + show(text, ToastType.ToastStandard); + } else if (typeof duration === "undefined" && + typeof type !== "undefined") { + show(text, type); + } else { + show(text, duration, type); } } } diff --git a/src/main.cpp b/src/main.cpp index f6408b3..e29c8db 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ /* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp - * Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> - * Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify @@ -36,14 +36,16 @@ #include <QQuickStyle> #include <signal.h> +#include "DBusAPI.h" #include "RWADBusAdaptor.cpp" #include "session.h" -#include "scenes/add_server_wizard/add_server_wizard.h" - -#define BUILD_TIME __DATE__ " " __TIME__ +#include "scenes/add_rwahost_wizard/add_rwahost_wizard.h" +#include "scenes/remote_control/remote_control_manager.h" +#include "RWAHostModel.h" +#include "RWAHost.h" int main(int argc, char *argv[]) { - qDebug() << "This app was built on: " << BUILD_TIME; + qDebug() << "This app was built on: " << __DATE__ << __TIME__; // We don't want users to have multiple instances of this app running QString tmpDirPath = QDir::tempPath() + "/rwa.support.desktopapp"; @@ -54,12 +56,12 @@ int main(int argc, char *argv[]) { tmpDir.mkpath("."); } QLockFile lockFile(tmpFilePath); - qDebug() << "Checking for a lockfile at: " + tmpFilePath; + qDebug().noquote() << QString("Checking for a lockfile at: '%0'").arg(tmpFilePath); if(!lockFile.tryLock(100)){ - qDebug() << QObject::tr("You already have this app running."); - qDebug() << QObject::tr("Only one instance is allowed."); - qDebug() << QObject::tr("Closing application now with an error."); + qCritical().noquote() << "You already have this app running.\n" + << "Only one instance is allowed.\n" + << "Closing application now with an error."; return 1; } @@ -70,27 +72,37 @@ int main(int argc, char *argv[]) { QQuickStyle::setStyle("Material"); QTranslator translator; - qDebug() << "Loading locale: qrc:/locales/bin/" + QLocale::system().name(); - if(translator.load(":/locales/bin/" + QLocale::system().name())) { + qDebug().noquote() << QString("Locales: Loading locale: qrc:/locales/%0") + .arg(QLocale::system().name()); + if(translator.load(":/locales/" + QLocale::system().name())) { app.installTranslator(&translator); - qDebug() << "Loaded: " + QLocale::system().name() + " locale!"; + qDebug().noquote() << "Locales: Loaded: " + QLocale::system().name() + " locale!"; } else { - qDebug() << "Unable to load translation"; + qWarning() << "Locales: Unable to load translation!"; } QQmlApplicationEngine engine(&app); - QScopedPointer<MainQMLAdaptor> main_gui (new MainQMLAdaptor(&app, &engine)); + QScopedPointer<DBusAPI> dbus_api (new DBusAPI()); + + // Make 'mainqmladaptor' available to QML + QScopedPointer<MainQMLAdaptor> main_gui ( + new MainQMLAdaptor(&app, &engine, dbus_api.data()) + ); - // Make mainqmladaptor available to QML engine.rootContext()->setContextProperty("mainqmladaptor", main_gui.data()); + QObject::connect(dbus_api.data(), + SIGNAL(serviceGetWebAppHostsResponse(QJsonDocument*)), + main_gui.data(), + SLOT(get_web_app_hosts_response(QJsonDocument*))); + dbus_api.data()->get_web_app_hosts_request(); + + engine.load(QUrl(QStringLiteral("qrc:/src/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; - QScopedPointer<Session> session (new Session(&app, main_gui.data())); - QObject::connect(main_gui.data(), SIGNAL(minimizeWindow()), engine.rootObjects().takeFirst(), @@ -101,10 +113,29 @@ int main(int argc, char *argv[]) { engine.rootObjects().takeFirst(), SLOT(showWindow())); - QScopedPointer<Add_Server_wizard> wizard (new Add_Server_wizard(&app)); - - // Make add_server_wizard available to QML - engine.rootContext()->setContextProperty("add_server_wizard", wizard.data()); + QObject::connect(engine.rootObjects().takeFirst()-> + findChild<QObject*>("sidebar_drawer"), + SIGNAL(rwaHostSelected(QString)), + main_gui.data(), + SLOT(onRwaHostSelected(QString))); + + // Make 'AddRWAHostWizard' available to QML + QScopedPointer<AddRWAHostWizard> wizard ( + new AddRWAHostWizard(&app, + main_gui.data(), + dbus_api.data()) + ); + engine.rootContext()-> + setContextProperty("add_rwahost_wizard", wizard.data()); + + // Make 'remote_control_manager' available to QML + QScopedPointer<RemoteControlManager> remote_mngr ( + new RemoteControlManager(&engine, + main_gui.data(), + dbus_api.data()) + ); + engine.rootContext()-> + setContextProperty("remote_control_manager", remote_mngr.data()); return app.exec(); } diff --git a/src/main.qml b/src/main.qml index 1981f4f..a622c18 100644 --- a/src/main.qml +++ b/src/main.qml @@ -1,8 +1,8 @@ /* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/remote-support-desktop - * Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> - * Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify @@ -32,7 +32,6 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 import QtQuick.Controls.Material 2.3 import "scenes" as Scenes -//import "ListItem.qml" /*! The main.qml file contains the window, with its header, sidebar, toast and main_content. @@ -59,7 +58,7 @@ ApplicationWindow { function minimizeWindow() { showMinimized(); - console.log("Miniming window now..."); + console.log("Minimizing window now..."); } function showWindow() { @@ -67,25 +66,25 @@ ApplicationWindow { console.log("Opening window now..."); } - function main_content_pop(item, operation) { + function main_content_pop(item) { if(item) { if(item.search(main_content.currentItem.objectName) >= 0) return } - return main_content.pop(item, operation) + return main_content.pop(item) } - function main_content_push(item, operation) { + function main_content_push(item) { if(item) { if(item.search(main_content.currentItem.objectName) >= 0) return } - return main_content.push(item, operation) + return main_content.push(item) } - function main_content_replace(item, operation) { + function main_content_replace(item) { if(item) { if(item.search(main_content.currentItem.objectName) >= 0) return } - return main_content.replace(item, operation) + return main_content.replace(item) } MessageDialog { @@ -108,14 +107,14 @@ ApplicationWindow { Connections { target: mainqmladaptor - onShowToastSignal: { - toast.show(text, durationMs) + function onShowToastSignal(text, durationMs, type) { + toast.show(text, durationMs, type) } } Connections { target: mainqmladaptor - onShowMessageDialogChanged: { + function onShowMessageDialogChanged(show) { message_dialog.visible = show } } @@ -154,21 +153,21 @@ ApplicationWindow { Connections { target: mainqmladaptor - onMessageDialogTextChanged: { + function onMessageDialogTextChanged(text) { message_dialog.text = text } } Connections { target: mainqmladaptor - onMessageDialogTitleChanged: { + function onMessageDialogTitleChanged(title) { message_dialog.title = title } } Connections { target: mainqmladaptor - onMessageDialogIconChanged: { + function onMessageDialogIconChanged(iconindex) { message_dialog.icon = iconindex } } @@ -183,9 +182,12 @@ ApplicationWindow { */ Drawer { id: sidebar_drawer - y: top_menu_bar_frame.height + objectName: "sidebar_drawer" - width: !inPortrait ? Math.min(300, Math.max(200, window.width * 0.333)) : (window.width * 0.5) + y: top_menu_bar_frame.height + width: !inPortrait ? + Math.min(300, Math.max(200, window.width * 0.333)) : + (window.width * 0.5) height: window.height - top_menu_bar_frame.height modal: inPortrait @@ -195,6 +197,9 @@ ApplicationWindow { margins: -2 visible: !inPortrait + signal rwaHostSelected(string host_uuid) + property bool rwaHostIsSelected: false + ListView { id: sidebar_listview boundsBehavior: Flickable.StopAtBounds @@ -203,17 +208,48 @@ ApplicationWindow { anchors.fill: parent model: mainModel + header: Rectangle { + height: 50 + width: parent.width + color: Material.background + + ComboBox { + id: server_chooser + objectName: "server_chooser" + + padding: 0 + width: parent.width + height: 56 - y + y: -6 + + model: mainqmladaptor.rwaHostModel + textRole: "alias" + + onCurrentIndexChanged: { + var rwa_host = mainqmladaptor.rwaHostModel + if (rwa_host[currentIndex] !== undefined) { + sidebar_drawer.rwaHostSelected(rwa_host[currentIndex].uuid) + displayText = rwa_host[currentIndex].alias + } + } + + + } + } + footer: ItemDelegate { id: footer text: " " + qsTr("Settings") width: parent.width + enabled: false onClicked: { var scene_url = "scenes/Scene_placeholder.qml" header_text.text = qsTr("Settings") - if(inPortrait) sidebar_drawer.close() + if (inPortrait) sidebar_drawer.close() - main_content_replace(scene_url, StackView.Transition) + if (scene_url.search(main_content.currentItem.objectName) >= 0) return + main_content.replace(scene_url, StackView.Transition) } MenuSeparator { @@ -225,34 +261,47 @@ ApplicationWindow { VisualItemModel { id: mainModel + ListItem { text: " " + qsTr("Remote Control") - scene_url: "scenes/Scene_remote_control.qml" + scene_url: "scenes/remote_control/Scene_remote_control.qml" onListItemClick: { header_text.text = qsTr("Allow remote control") - if(inPortrait) sidebar_drawer.close() + if (inPortrait) sidebar_drawer.close() - main_content_replace(scene_url, StackView.Transition) + if (scene_url.search(main_content.currentItem.objectName) >= 0) return + main_content.replace(scene_url, StackView.Transition) } + + // Disabled till a RWAHost object is selected. + enabled: sidebar_drawer.rwaHostIsSelected } ListItem { text: " " + qsTr("Remote View") scene_url: "scenes/Scene_remote_view.qml" onListItemClick: { header_text.text = qsTr("Allow remote view") - if(inPortrait) sidebar_drawer.close() + if (inPortrait) sidebar_drawer.close() - main_content_replace(scene_url, StackView.Transition) + if (scene_url.search(main_content.currentItem.objectName) >= 0) return + main_content.replace(scene_url, StackView.Transition) } + + // Disabled till a RWAHost object is selected. + //enabled: sidebar_drawer.rwaHostIsSelected + + // But remote view is not implemented yet + enabled: false } ListItem { text: " " + qsTr("Add RWA-Server") - scene_url: "scenes/add_server_wizard/Scene_step_1.qml" + scene_url: "scenes/add_rwahost_wizard/Scene_step_1.qml" onListItemClick: { header_text.text = qsTr("Server addition wizard") - if(inPortrait) sidebar_drawer.close() + if (inPortrait) sidebar_drawer.close() - main_content_push(scene_url, StackView.ReplaceTransition) + if (scene_url.search(main_content.currentItem.objectName) >= 0) return + main_content.push(scene_url, StackView.ReplaceTransition) } } } @@ -362,3 +411,8 @@ ApplicationWindow { } } } + +/*##^## Designer { + D{i:14;anchors_width:650} +} + ##^##*/ diff --git a/src/main_qmladaptor.cpp b/src/main_qmladaptor.cpp index 8a54eb3..688b993 100644 --- a/src/main_qmladaptor.cpp +++ b/src/main_qmladaptor.cpp @@ -1,8 +1,8 @@ /* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp - * Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> - * Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify @@ -26,53 +26,280 @@ #include "main_qmladaptor.h" -MainQMLAdaptor::MainQMLAdaptor(QObject *parent, QQmlApplicationEngine* engine) : QObject(parent) -{ +MainQMLAdaptor::MainQMLAdaptor(QObject *parent, QQmlApplicationEngine* engine, + DBusAPI *dbus_api) : QObject(parent) { + Q_ASSERT(engine != nullptr); + Q_ASSERT(dbus_api != nullptr); + _engine = engine; + _dbus_api = dbus_api; + _selected_rwa_host = nullptr; + _rwaHostModel = new QList<QObject*>; + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, _dbus_api, + QOverload<>::of(&DBusAPI::get_web_app_hosts_request)); + timer->start(10000); + + qmlRegisterUncreatableMetaObject( + Toast::staticMetaObject, // meta object created by Q_NAMESPACE macro + "rwa.toast.type", // import statement (can be any string) + 1, 0, // major and minor version of the import + "ToastType", // name in QML (does not have to match C++ name) + "Error: only enums" // error if someone tries to create a ToastType object + ); + } -bool MainQMLAdaptor::setConnectButtonEnabled(bool enabled) { - // Find item via 'objectName' - QQuickItem *item = _engine->rootObjects().at(0)->findChild<QQuickItem*>("start_support_button"); - if (item) { - item->setProperty("enabled", enabled); - if (item->property("checked").toBool()) { - item->setProperty("text", tr("Stop remote support session")); - } else { - item->setProperty("text", tr("Start remote support session")); +void MainQMLAdaptor::onRwaHostSelected(QString host_uuid) { + Q_ASSERT(host_uuid != ""); + + RWAHost *_host = nullptr; + for (int i = 0; i < getRWAHostModel().size(); i++) { + QObject *obj = _rwaHostModel->value(i); + RWAHost *host = qobject_cast<RWAHost *>(obj); + Q_ASSERT(host != nullptr); + + if (host->uuid() == host_uuid) { + _host = host; } - } else { - qWarning() << "Unable to find 'start_support_button' Item!"; - return false; } + Q_ASSERT(_host != nullptr); - return true; + _selected_rwa_host = _host; + qDebug() << "RWAHost was selected!" << + _selected_rwa_host->uuid() << + "aka" << + _selected_rwa_host->alias(); + + setRWAHostSelected(true); +} + +RWAHost* MainQMLAdaptor::getSelectedRWAHost() { + return _selected_rwa_host; +} + +void MainQMLAdaptor::setRWAHostModel(QList<QObject*> *rwa_hosts) { + _rwaHostModel = rwa_hosts; + emit rwaHostModelChanged(*rwa_hosts); +} + +void MainQMLAdaptor::addRWAHost(RWAHost *rwa_host) { + _rwaHostModel->append(rwa_host); + emit rwaHostModelChanged(*_rwaHostModel); +} + +void MainQMLAdaptor::removeRWAHost(RWAHost *rwa_host) { + _rwaHostModel->removeOne(rwa_host); + emit rwaHostModelChanged(*_rwaHostModel); +} + +QList<QObject*> MainQMLAdaptor::getRWAHostModel() { + return *_rwaHostModel; } -bool MainQMLAdaptor::setConnectButtonChecked(bool checked) { +void MainQMLAdaptor::setRWAHostSelected(bool value) { // Find item via 'objectName' - QQuickItem *item = _engine->rootObjects().at(0)->findChild<QQuickItem*>("start_support_button"); - if (item) { - item->setProperty("checked", checked); + QObject *sidebar_drawer = _engine->rootObjects().takeFirst()-> + findChild<QObject*>("sidebar_drawer"); + if (sidebar_drawer) { + sidebar_drawer->setProperty("rwaHostIsSelected", value); } else { - qWarning() << "Unable to find 'start_support_button' Item!"; - return false; + qWarning() << "Unable to find 'sidebar_drawer' Item!"; } - return true; + QObject *server_chooser = _engine->rootObjects().takeFirst()-> + findChild<QObject*>("server_chooser"); + if (server_chooser) { + server_chooser->setProperty("displayText", value ? + server_chooser->property("currentText") : + tr("No RWA host available!")); + server_chooser->setProperty("enabled", value); + } else { + qWarning() << "Unable to find 'server_chooser' Item!"; + } } -bool MainQMLAdaptor::setStatus(QString status) { - // Find item via 'objectName' - QQuickItem *item = _engine->rootObjects().at(0)->findChild<QQuickItem*>("dbus_api_status_text"); - if (item) { - item->setProperty("text", status); +void MainQMLAdaptor::get_web_app_hosts_response(QJsonDocument *doc) { + // Q_ASSERT lets the program crash immediatly at startup, + // when the session service is not started. + // Don't use Q_ASSERT(doc != nullptr); instead use: + if (doc == nullptr) { + setRWAHostSelected(false); + + showToast(tr("Can't connect to underlying session service!"), + 9800, + Toast::ToastError); + + // Go to the first page on the stack. + main_content_pop(nullptr); + + return; + } + + + bool atLeastOneHostAvailable = false; + + // Get the QJsonObject + QJsonObject jObject = doc->object(); + QVariantMap mainMap = jObject.toVariantMap(); + + // Status of request + QString request_status = mainMap["status"].toString(); + if (request_status == "success") { + // Building host_objects + QJsonArray host_objects = jObject.value("hosts").toArray(); + + QList<RWAHost*> *all_rwa_hosts = new QList<RWAHost*>; + foreach (const QJsonValue &host_object, host_objects) { + QString host_uuid = host_object["uuid"].toString(); + QString host_alias = host_object["alias"].toString(); + QString host_url = host_object["url"].toString(); + + if (host_url == "" || host_uuid == "") { + // This two values are required and can't be omitted. + QString reason = tr("A host object in the response of D-Bus service lacks" + " a necessary value. (host_url or host_uuid)"); + qCritical().noquote() << tr("An error occured while adding a new host:") + << reason; + + return; + } + + if (host_alias == "") { + qDebug().noquote() << QString("An alias for the host wasn't delivered " + "so just use '%0' as alias.").arg(host_url); + host_alias = host_url; + } + + + // Now built RWAHost object. + RWAHost *rwa_host = new RWAHost(host_uuid, host_alias, host_url); + all_rwa_hosts->append(rwa_host); + + bool found = false; + for (int i = 0; i < this->_rwaHostModel->size(); i++) { + RWAHost* old_host = qobject_cast<RWAHost*>(_rwaHostModel->value(i)); + Q_ASSERT(old_host != nullptr); + + if (rwa_host->uuid() == old_host->uuid()) { + found = true; + break; + } + } + + atLeastOneHostAvailable = true; + + if (!found) { + qInfo().noquote() << QString(tr("Successfully added new RWAHost '%0'")) + .arg(rwa_host->alias()); + addRWAHost(rwa_host); + } + } + + for (int i = 0; i < this->_rwaHostModel->size(); i++) { + RWAHost* old_host = qobject_cast<RWAHost*>(_rwaHostModel->value(i)); + Q_ASSERT(old_host != nullptr); + + bool found = false; + for (RWAHost *host : *all_rwa_hosts) { + if (host->uuid() == old_host->uuid()) { + found = true; + break; + } + } + + if (!found) { + removeRWAHost(old_host); + qInfo().noquote() << QString(tr("Removed RWAHost '%0'")).arg(old_host->alias()); + } + } } else { - qWarning() << "Unable to find 'dbus_api_status_text' Item!"; - return false; + QString reason = tr("An error occured while adding a new host:"); + qCritical().noquote() << reason; + + QString type = mainMap["type"].toString(); + if (type != "") { + reason = QString(tr("The error is not clear. The session service " + "responded with status type '%0'")).arg(type); + qCritical().noquote() << reason; + } else { + reason = QString(tr("The error is not clear. The session service " + "responded with no status type!")); + qCritical().noquote() << reason; + } + + return; } - return true; + if (!atLeastOneHostAvailable) { + main_content_replace("scenes/Scene_no_server_available.qml"); + } + + setRWAHostSelected(atLeastOneHostAvailable); +} + +void MainQMLAdaptor::main_content_push(QString scene) { + // Find item via 'objectName' + QObject *window = _engine->rootObjects().takeFirst(); + Q_ASSERT(window != nullptr); + + QObject *main_content = _engine->rootObjects().takeFirst()->findChild<QObject*>("main_content"); + Q_ASSERT(main_content != nullptr); + + QVariant to_cast = main_content->property("currentItem"); + QObject *obj = qvariant_cast<QObject *>(to_cast); + if (obj) { + if (!(scene.contains(obj->objectName()))) { + QVariant arg = QVariant::fromValue(scene); + if(!QMetaObject::invokeMethod(window, "main_content_push", Q_ARG(QVariant, arg))) + qDebug() << "Failed to invoke push"; + } + } +} + +void MainQMLAdaptor::main_content_pop(QString scene) { + // Find item via 'objectName' + QObject *window = _engine->rootObjects().takeFirst(); + Q_ASSERT(window != nullptr); + + QObject *main_content = _engine->rootObjects().takeFirst()-> + findChild<QObject*>("main_content"); + Q_ASSERT(main_content != nullptr); + + QVariant to_cast = main_content->property("currentItem"); + QObject *obj = qvariant_cast<QObject *>(to_cast); + if (obj) { + if (!(scene.contains(obj->objectName()))) { + QVariant arg = QVariant::fromValue(scene); + if(!QMetaObject::invokeMethod(window, "main_content_pop", Q_ARG(QVariant, arg))) + qDebug() << "Failed to invoke pop"; + } + } +} + +void MainQMLAdaptor::main_content_replace(QString scene) { + // Find item via 'objectName' + QObject *window = _engine->rootObjects().takeFirst(); + Q_ASSERT(window != nullptr); + + QObject *main_content = _engine->rootObjects().takeFirst()-> + findChild<QObject*>("main_content"); + Q_ASSERT(main_content != nullptr); + + QVariant to_cast = main_content->property("currentItem"); + QObject *obj = qvariant_cast<QObject *>(to_cast); + if (obj) { + QString scene_add_rwahost_wizard = "Scene_step_1"; + if (!(scene.contains(obj->objectName()) || + scene_add_rwahost_wizard.contains(obj->objectName()))) { + QVariant arg = QVariant::fromValue(scene); + if (!QMetaObject::invokeMethod(window, "main_content_replace", + Q_ARG(QVariant, arg))) { + qDebug() << "Failed to invoke replace"; + } + } + } } bool MainQMLAdaptor::openMessageDialog(QString title, QString text, QMessageBox::Icon icon) { @@ -106,65 +333,12 @@ bool MainQMLAdaptor::getShowMessageDialog() { return _showMessageDialog; } -bool MainQMLAdaptor::setStatusIndicator(bool active, QColor color) { - // Find item via 'objectName' - QQuickItem *item = _engine->rootObjects().at(0)->findChild<QQuickItem*>("dbus_api_status_indicator"); - if (item) { - item->setProperty("active", active); - item->setProperty("color", color); - } else { - qWarning() << "Unable to find 'dbus_api_status_indicator' Item!"; - return false; - } - - return true; -} - -void MainQMLAdaptor::handleCopyToClipboardButtonClick(QString copy_data) { - QClipboard *clipboard = QApplication::clipboard(); - QString originalText = clipboard->text(); - clipboard->setText(copy_data); - qDebug() << "Copied into clipboard:" << copy_data; -} - -void MainQMLAdaptor::handleConnectButtonClick(bool checked) { - emit onConnectButtonClick(checked); -} - -void MainQMLAdaptor::setPin(QString pin) { - _pin = pin; - emit pinChanged(pin); -} -void MainQMLAdaptor::setURL(QString URL) { - _url = URL; - emit urlChanged(URL); -} -void MainQMLAdaptor::setSessionID(QString session_id) { - _session_id = session_id; - emit sessionIDChanged(session_id); -} -QString MainQMLAdaptor::getURL() { - return _url; -} -QString MainQMLAdaptor::getPin() { - return _pin; -} -QString MainQMLAdaptor::getSessionID() { - return _session_id; -} - void MainQMLAdaptor::onCloseHandler() { // Do cleanup things here... emit onCloseSignal(); } -void MainQMLAdaptor::showToast(QString text, uint durationMs) { - emit showToastSignal(text, QString::number(durationMs)); +void MainQMLAdaptor::showToast(QString text, uint durationMs, uint type) { + // type is actually Toast::ToastType + emit showToastSignal(text, QString::number(durationMs), type); } - -//void MainQMLAdaptor::onCloseHandler() { -// qDebug() << "Inside MainQMLAdaptor::onCloseHandler()"; - -// // Sending onClose signal to main and there to Session::onCloseHandler() -// emit MainQMLAdaptor::onCloseSignal(); -//} diff --git a/src/main_qmladaptor.h b/src/main_qmladaptor.h index 4619968..70c075b 100644 --- a/src/main_qmladaptor.h +++ b/src/main_qmladaptor.h @@ -1,8 +1,8 @@ /* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp - * Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> - * Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify @@ -26,22 +26,47 @@ #pragma once -#include <QObject> #include <QQmlApplicationEngine> #include <QQuickItem> #include <QMessageBox> #include <QApplication> #include <QClipboard> - -class MainQMLAdaptor : public QObject +#include <QJsonObject> +#include <QJsonDocument> +#include <QJsonArray> +#include <QTimer> + +#include "RWAHost.h" +#include "DBusAPI.h" + +namespace Toast { + Q_NAMESPACE + enum ToastType { + ToastStandard, + ToastInfo, + ToastSuccess, + ToastWarning, + ToastError + }; + Q_ENUM_NS(ToastType) +} + +/*#include <QObject> +namespace MyNamespace { + Q_NAMESPACE // required for meta object creation + enum EnStyle { + STYLE_RADIAL, + STYLE_ENVELOPE, + STYLE_FILLED + }; + Q_ENUM_NS(EnStyle) // register the enum in meta object data +}*/ + +class MainQMLAdaptor : public QObject { Q_OBJECT - // this makes url available as a QML property - Q_PROPERTY(QString url READ getURL WRITE setURL NOTIFY urlChanged) - // this makes pin available as a QML property - Q_PROPERTY(QString pin READ getPin WRITE setPin NOTIFY pinChanged) - // this makes session_id available as a QML property - Q_PROPERTY(QString session_id READ getSessionID WRITE setSessionID NOTIFY sessionIDChanged) + // this makes rwaHostModel available as a QML property + Q_PROPERTY(QList<QObject*> rwaHostModel READ getRWAHostModel NOTIFY rwaHostModelChanged) // this makes showMessageDialog available as a QML property Q_PROPERTY(bool showMessageDialog READ getShowMessageDialog NOTIFY showMessageDialogChanged) // this makes showMessageDialogTitle available as a QML property @@ -51,22 +76,23 @@ class MainQMLAdaptor : public QObject // this makes showMessageDialogIcon available as a QML property Q_PROPERTY(QMessageBox::Icon _messageDialogIcon READ getMessageDialogIcon NOTIFY messageDialogIconChanged) -public: - explicit MainQMLAdaptor(QObject *parent, QQmlApplicationEngine *engine = nullptr); -// void setSession(Session session); +public: + explicit MainQMLAdaptor(QObject *parent, QQmlApplicationEngine *engine = nullptr, + DBusAPI *dbus_api = nullptr); - bool setConnectButtonEnabled(bool enabled); - bool setConnectButtonChecked(bool checked); + void setRWAHostModel(QList<QObject*> *rwa_hosts); - bool setStatusIndicator(bool active, QColor color = QColor(255,255,255)); - bool setStatus(QString status); + void main_content_push(QString); + void main_content_pop(QString); + void main_content_replace(QString); bool openMessageDialog(QString title, QString text, QMessageBox::Icon); QString getMessageDialogTitle(); QString getMessageDialogText(); QMessageBox::Icon getMessageDialogIcon(); bool getShowMessageDialog(); + signals: void showMessageDialogChanged(bool show); void messageDialogTextChanged(QString text); @@ -76,22 +102,19 @@ signals: void minimizeWindow(); void showWindow(); - void onConnectButtonClick(bool checked); - - void pinChanged(QString pin); - void urlChanged(QString URL); - void sessionIDChanged(QString session_id); + void rwaHostModelChanged(QList<QObject*>); void onCloseSignal(); - void showToastSignal(QString text, QString durationMs); + void showToastSignal(QString text, QString durationMs, int type); protected: - QString _url; - QString _pin; - QString _session_id; + DBusAPI *_dbus_api; + QList<QObject*> *_rwaHostModel; + private: - QQmlApplicationEngine* _engine; + QQmlApplicationEngine *_engine; + RWAHost *_selected_rwa_host; bool _showMessageDialog; QString _messageDialogTitle; @@ -99,17 +122,23 @@ private: QMessageBox::Icon _messageDialogIcon; public slots: - void handleCopyToClipboardButtonClick(QString copy_data); - void handleConnectButtonClick(bool checked); + void get_web_app_hosts_response(QJsonDocument *doc); + + void addRWAHost(RWAHost *rwa_host); + void removeRWAHost(RWAHost *rwa_host); + void setRWAHostSelected(bool value); + + // No pointer because QML doesn't + // like this type much with pointer + QList<QObject*> getRWAHostModel(); - void setPin(QString pin); - void setURL(QString URL); - void setSessionID(QString session_id); - QString getURL(); - QString getPin(); - QString getSessionID(); + RWAHost *getSelectedRWAHost(); + void onRwaHostSelected(QString host_uuid); void onCloseHandler(); - void showToast(QString text, uint durationMs = 3000); + // arg type is actually Toast::ToastType + void showToast(QString text, + uint durationMs = 3000, + uint type = 0); }; diff --git a/src/scenes/Scene_no_server_available.qml b/src/scenes/Scene_no_server_available.qml new file mode 100644 index 0000000..7b99b55 --- /dev/null +++ b/src/scenes/Scene_no_server_available.qml @@ -0,0 +1,71 @@ +import QtQuick 2.9 +import QtQuick.Window 2.2 +import QtQuick.Extras 1.4 +import QtQuick.Controls 2.2 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls.Material 2.3 + +/*! + * This .qml file is a Scene which can be loaded through for + * example a StackView (main_content in main.qml). + */ + +Item { + id: scene_no_server_available + objectName: "Scene_no_server_available" + + Rectangle { + id: rectangle + anchors.fill: parent + color: Material.background + + Text { + color: Material.foreground + id: title + + text: qsTr("Welcome!") + font.pointSize: 20 + font.bold: true + wrapMode: Text.WordWrap + + horizontalAlignment: Text.AlignLeft + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 15 + } + + Text { + color: Material.foreground + anchors.top: title.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 15 + + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + /*: 'Add RWA-Server' has to be replaced with the correct translation in file main.qml .*/ + font.pointSize: 13 + text: qsTr("You need to add and select the remote \ +web app server to which you want to connect. \ +You can see a button to the left in the menu \ +which says 'Add RWA-Server'. Follow the steps \ +listed there and you can start your remote \ +support session afterwards using the buttons \ +above 'Add RWA-Server'.") + } + } +} + + + + + + + +/*##^## Designer { + D{i:0;autoSize:true;height:480;width:640} +} + ##^##*/ diff --git a/src/scenes/Scene_placeholder.qml b/src/scenes/Scene_placeholder.qml index a5ceac9..f492e00 100644 --- a/src/scenes/Scene_placeholder.qml +++ b/src/scenes/Scene_placeholder.qml @@ -6,7 +6,8 @@ import QtQuick.Dialogs 1.2 import QtQuick.Controls.Material 2.3 /*! - This .qml file is a Scene which can be loaded through for example a StackView (main_content in main.qml). + * This .qml file is a Scene which can be loaded through for + * example a StackView (main_content in main.qml). */ Item { @@ -43,7 +44,7 @@ Item { anchors.right: parent.right wrapMode: Text.WordWrap - text: qsTr("The feature you expected here are not yet implemented.") + text: qsTr("The features you expected here are not yet implemented.") horizontalAlignment: Text.AlignHCenter } } diff --git a/src/scenes/Scene_remote_view.qml b/src/scenes/Scene_remote_view.qml index cea5ccf..436d8aa 100644 --- a/src/scenes/Scene_remote_view.qml +++ b/src/scenes/Scene_remote_view.qml @@ -5,6 +5,11 @@ import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Controls.Material 2.3 +/*! + * This .qml file is a Scene which can be loaded through for + * example a StackView (main_content in main.qml). + */ + Item { id: scene_remote_view objectName: "Scene_remote_view" diff --git a/src/scenes/Scene_settings.qml b/src/scenes/Scene_settings.qml index 5a29071..e2f89da 100644 --- a/src/scenes/Scene_settings.qml +++ b/src/scenes/Scene_settings.qml @@ -5,6 +5,11 @@ import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Controls.Material 2.3 +/*! + * This .qml file is a Scene which can be loaded through for + * example a StackView (main_content in main.qml). + */ + Item { id: scene_settings objectName: "Scene_settings" diff --git a/src/scenes/add_server_wizard/Scene_step_1.qml b/src/scenes/add_rwahost_wizard/Scene_step_1.qml index e76462a..873e0fd 100644 --- a/src/scenes/add_server_wizard/Scene_step_1.qml +++ b/src/scenes/add_rwahost_wizard/Scene_step_1.qml @@ -3,9 +3,11 @@ import QtQuick.Window 2.2 import QtQuick.Extras 1.4 import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.3 +import rwa.toast.type 1.0 /*! - This .qml file is a Scene which can be loaded through for example a StackView (main_content in main.qml). + * This .qml file is a Scene which can be loaded through for + * example a StackView (main_content in main.qml). */ Item { @@ -13,18 +15,22 @@ Item { objectName: "Scene_step_1" Connections { - target: add_server_wizard - onStep1Success: { - //main_content_push("scenes/add_server_wizard/Scene_step_2.qml", StackView.Transition) + target: add_rwahost_wizard + function onStep1Success() { + // Go onto the first page of the stack. main_content_pop(null) - mainqmladaptor.showToast(qsTr("Successfully added server address."), 5000); + mainqmladaptor.showToast(qsTr("Successfully added remote web app host."), + 5000, + ToastType.ToastSuccess); } } Connections { - target: add_server_wizard - onStep1Failed: { - mainqmladaptor.showToast(reason, 3000); + target: add_rwahost_wizard + function onStep1Failed(reason, toast_type) { + mainqmladaptor.showToast(reason, + 5000, + toast_type) } } @@ -42,11 +48,11 @@ Item { anchors.rightMargin: 15 onClicked: { - add_server_wizard.processStep1(host_url.text) + add_rwahost_wizard.processStep1(host_url.text, host_alias.text) } } - Text { + /*Text { color: Material.foreground id: step_indicator @@ -61,7 +67,7 @@ Item { horizontalAlignment: Text.AlignHCenter anchors.left: parent.left anchors.margins: 5 - } + }*/ Text { color: Material.foreground @@ -74,12 +80,12 @@ Item { "administrator about it please.\nBefore you can "+ "start any remote sessions you will have to "+ "be approved for remote support.") - font.pointSize: 13 + font.pointSize: 12 anchors.right: parent.right anchors.rightMargin: 15 anchors.leftMargin: 15 - anchors.top: step_indicator.bottom - anchors.topMargin: 30 + anchors.top: parent.top //step_indicator.bottom + anchors.topMargin: 15 wrapMode: Text.WordWrap anchors.left: parent.left @@ -160,7 +166,7 @@ Item { Text { color: Material.foreground - text: qsTr("RWA-server address") + text: qsTr("RWA host address") anchors.left: parent.left anchors.leftMargin: 15 anchors.verticalCenterOffset: - host_url.height / 2 @@ -176,6 +182,97 @@ Item { } } } + + TextField { + id: host_alias + selectByMouse: true + placeholderText: qsTr("My example host") + + font.pixelSize: 16 + color: Material.foreground + + padding: 15 + topPadding: 15 + + anchors.top: host_url.bottom + anchors.topMargin: 30 + anchors.left: parent.left + anchors.leftMargin: 15 + anchors.right: parent.right + anchors.margins: 30 + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onHoveredChanged: { + if (containsMouse) { + host_alias_background.state = "hovered" + } else if (!host_alias.focus) { + host_alias_background.state = "unhovered" + } + } + onClicked: { + host_alias.forceActiveFocus(); + } + } + + onFocusChanged: { + host_alias_background.state = host_alias.focus ? "hovered" : "unhovered" + } + + background: Rectangle { + id: host_alias_background + color: Material.background + border.color: Material.foreground + border.width: 1 + radius: 4 + + states: [ + State { + name: "hovered" + PropertyChanges { + target: host_alias_background + border.color: "#0178EF" + } + }, + State { + name: "unhovered" + PropertyChanges { + target: host_alias_background + border.color: Material.foreground + } + } + ] + transitions: [ + Transition { + from: "*" + to: "*" + PropertyAnimation { + property: "border.color" + duration: 100 + easing.type: Easing.Linear + } + } + ] + + Text { + color: Material.foreground + text: qsTr("RWA host alias") + anchors.left: parent.left + anchors.leftMargin: 15 + anchors.verticalCenterOffset: - host_alias.height / 2 + anchors.verticalCenter: parent.verticalCenter + leftPadding: 5 + + Rectangle { + color: Material.background + width: parent.width + 5 + 3 + height: parent.height + z: -1 + } + } + } + } } } diff --git a/src/scenes/add_rwahost_wizard/add_rwahost_wizard.cpp b/src/scenes/add_rwahost_wizard/add_rwahost_wizard.cpp new file mode 100644 index 0000000..7adbdf9 --- /dev/null +++ b/src/scenes/add_rwahost_wizard/add_rwahost_wizard.cpp @@ -0,0 +1,124 @@ +/* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "add_rwahost_wizard.h" +#include "../../RWADBusAdaptor.h" +#include "../../RWAHost.h" + +AddRWAHostWizard::AddRWAHostWizard(QObject *parent, + MainQMLAdaptor *main_gui, DBusAPI *dbus_api) : QObject(parent) { + Q_ASSERT(main_gui != nullptr); + Q_ASSERT(dbus_api != nullptr); + + _dbus_api = dbus_api; + _main_gui = main_gui; + + // _dbus_api --serviceAddWebAppHostResponse-> this.add_web_app_host_response() + QObject::connect(_dbus_api, + SIGNAL(serviceAddWebAppHostResponse(QJsonDocument*)), + this, + SLOT(add_web_app_host_response(QJsonDocument*))); +} + +void AddRWAHostWizard::processStep1(QString host_url, QString host_alias) { + qDebug() << "Processing Step 1 with args: " << host_url << host_alias; + + if(host_alias == "" || host_url == "") { + QString reason = tr("Both textfields can't be empty!"); + emit step1Failed(reason, Toast::ToastType::ToastWarning); + qDebug().noquote() << reason; + return; + } + + return add_server(host_url, host_alias); +} + +void AddRWAHostWizard::processStep2() { + qDebug() << "Processing Step 2 with args: No Args."; + emit step2Failed(tr("The features you expected here are not yet implemented."), Toast::ToastType::ToastWarning); + // Just show placeholder scene now. + emit step2Success(); +} + +void AddRWAHostWizard::add_server(QString host_url, QString host_alias) { + _dbus_api->add_web_app_host_request(host_url, host_alias); +} + +void AddRWAHostWizard::add_web_app_host_response(QJsonDocument *doc) { + // Q_ASSERT lets the program crash immediatly at startup, + // when the session service is not started. + // Don't use Q_ASSERT(doc != nullptr); instead use: + if (doc == nullptr) { + _main_gui->setRWAHostSelected(false); + _main_gui->showToast(tr("Can't connect to underlying session service!"), + 9800, + Toast::ToastType::ToastError); + return; + } + + // Get the QJsonObject + QJsonObject jObject = doc->object(); + QVariantMap mainMap = jObject.toVariantMap(); + + // Status of request + QString request_status = mainMap["status"].toString(); + if (request_status == "success") { + _dbus_api->get_web_app_hosts_request(); + + qInfo() << "Successfully added a new RWAHost."; + emit step1Success(); + } else { + qCritical().noquote() << tr("An error occured while adding a new host!"); + + uint toast_type = Toast::ToastType::ToastStandard; + QString reason = tr("An error occured while adding a new host!"); + QString type = mainMap["type"].toString(); + + if(type == "connection"){ + reason = tr("Couldn't connect to the specified host!"); + toast_type = Toast::ToastType::ToastError; + qCritical().noquote() << reason; + } else if (type == "duplicate") { + reason = tr("The specified host was already added!"); + toast_type = Toast::ToastType::ToastWarning; + qCritical().noquote() << reason; + } else if (type == "invalid_url") { + reason = tr("The specified host address is not valid!"); + toast_type = Toast::ToastType::ToastWarning; + qCritical().noquote() << reason; + } else if (type == "permission_denied") { + reason = tr("The specified host address does not grant access!"); + toast_type = Toast::ToastType::ToastError; + qCritical().noquote() << reason; + } else if (type == "unsupported_server") { + reason = tr("The specified host address is not supported!"); + toast_type = Toast::ToastType::ToastError; + qCritical().noquote() << reason; + } + emit step1Failed(reason, toast_type); + + return; + } +} diff --git a/src/scenes/add_rwahost_wizard/add_rwahost_wizard.h b/src/scenes/add_rwahost_wizard/add_rwahost_wizard.h new file mode 100644 index 0000000..f019be1 --- /dev/null +++ b/src/scenes/add_rwahost_wizard/add_rwahost_wizard.h @@ -0,0 +1,61 @@ +/* + * This file is part of Remote Support Desktop + * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp + * Copyright 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef ADD_RWAHOST_WIZARD_H +#define ADD_RWAHOST_WIZARD_H + +#include <QObject> + +#include "../../RWADBusAdaptor.h" +#include "../../DBusAPI.h" +#include "../../main_qmladaptor.h" + +class AddRWAHostWizard : public QObject +{ + Q_OBJECT +public: + explicit AddRWAHostWizard(QObject *parent = nullptr, + MainQMLAdaptor *main_gui = nullptr, + DBusAPI *dbus_api = nullptr); + void add_server(QString host_url, QString host_alias); + +private: + DBusAPI *_dbus_api; + MainQMLAdaptor *_main_gui; + +signals: + void step1Success(); + void step1Failed(QString reason, uint toast_type); + void step2Success(); + void step2Failed(QString reason, uint toast_type); + +public slots: + void processStep1(QString host_url, QString host_alias); + void processStep2(); + + void add_web_app_host_response(QJsonDocument *doc); +}; + +#endif // ADD_SERVER_WIZARD_H diff --git a/src/scenes/add_server_wizard/Scene_step_2.qml b/src/scenes/add_server_wizard/Scene_step_2.qml deleted file mode 100644 index 5cfbe46..0000000 --- a/src/scenes/add_server_wizard/Scene_step_2.qml +++ /dev/null @@ -1,70 +0,0 @@ -import QtQuick 2.9 -import QtQuick.Window 2.2 -import QtQuick.Extras 1.4 -import QtQuick.Controls 2.2 -import QtQuick.Dialogs 1.2 -import QtQuick.Controls.Material 2.3 - -/*! - This .qml file is a Scene which can be loaded through for example a StackView (main_content in main.qml). - */ - -Item { - id: scene_server_wizard_step_2 - objectName: "Scene_step_2" - - Connections { - target: add_server_wizard - onStep2Success: { - main_content_pop(null, StackView.Transition) - //main_content_replace("scenes/Scene_placeholder.qml", StackView.Transition) - } - } - - Connections { - target: add_server_wizard - onStep2Failed: { - mainqmladaptor.showToast(reason, 3000); - } - } - - Rectangle { - id: rectangle - anchors.fill: parent - color: Material.background - - Text { - color: Material.foreground - id: title - - text: qsTr("Step 2") - font.pointSize: 18 - wrapMode: Text.WordWrap - - font.bold: true - horizontalAlignment: Text.AlignHCenter - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: 5 - } - - Button { - id: next_step2_button - text: qsTr("Next Step") - anchors.bottom: parent.bottom - anchors.bottomMargin: 10 - anchors.right: parent.right - anchors.rightMargin: 10 - - onClicked: { - add_server_wizard.processStep2() - } - } - } -} - -/*##^## Designer { - D{i:0;autoSize:true;height:480;width:640} -} - ##^##*/ diff --git a/src/scenes/add_server_wizard/add_server_wizard.cpp b/src/scenes/add_server_wizard/add_server_wizard.cpp deleted file mode 100644 index f8d4b7b..0000000 --- a/src/scenes/add_server_wizard/add_server_wizard.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "add_server_wizard.h" -#include "../../RWADBusAdaptor.h" -#include <QDebug> -#include <QtDBus> -#include <QDBusPendingCallWatcher> - -Add_Server_wizard::Add_Server_wizard(QObject *parent) : QObject(parent) { - _initDBus(); - - -} - -bool Add_Server_wizard::processStep1(QString host_url) { - qDebug() << "Processing Step 1 with args: " << host_url; - - if(host_url == "") { - emit step1Failed(tr("This field can't be empty!")); - return false; - } - - return _add_server(host_url); -} - -bool Add_Server_wizard::processStep2() { - qDebug() << "Processing Step 2 with args: No Args."; - emit step2Failed(tr("The feature you expected here are not yet implemented.")); - // Just show placeholder scene now. - emit step2Success(); - return false; -} - -bool Add_Server_wizard::_add_server(QString host_url) { - return _add_web_app_host(host_url); -} - -void Add_Server_wizard::_initDBus() { - if (!QDBusConnection::sessionBus().isConnected()) { - qCritical() << "Cannot connect to the D-Bus session bus."; - } - - // Create DBus object - _dbus_rwa = new OrgArcticaProjectRWASupportSessionServiceInterface("org.ArcticaProject.RWASupportSessionService", "/RWASupportSessionService", - QDBusConnection::sessionBus(), this->parent()); - - qDebug("Initialized DBus object!"); -} - -bool Add_Server_wizard::_add_web_app_host(QString host_url) { - qDebug() << "Requesting D-Bus session service to add a new host: " << host_url; - - // Make an asynchrous 'add_web_app_host' call (Response will be sent to '_add_web_app_host_dbus_replied') - QDBusPendingCall async = _dbus_rwa->asyncCall("add_web_app_host", host_url); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); - - QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(_add_web_app_host_dbus_replied(QDBusPendingCallWatcher*))); - - return true; -} - -void Add_Server_wizard::_add_web_app_host_dbus_replied(QDBusPendingCallWatcher *call) { - QString result = ""; - - QDBusPendingReply<QString> reply = *call; - if (reply.isError()) { - qDebug() << "D-Bus 'add_web_app_host' request failed, this was the reply:"; - qDebug() << reply.error(); - return; - } else { - result = reply.argumentAt<0>(); - } - call->deleteLater(); - - qDebug() << "Raw JSON from starting session is:" << result.toUtf8().replace('"', ""); - QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); - - // Get the QJsonObject - QJsonObject jObject = doc.object(); - QVariantMap mainMap = jObject.toVariantMap(); - - // Status of request - QString request_status = mainMap["status"].toString(); - if (request_status == "success") { - qDebug() << "Successfully started a Session."; - emit step1Success(); - } else { - qCritical() << "An error occured while adding a new host!"; - - QString reason = "An error occured while adding a new host!"; - QString type = mainMap["type"].toString(); - if(type == "connection"){ - reason = tr("Couldn't connect to the specified host!"); - } else if (type == "duplicate") { - reason = tr("The specified host was already added!"); - } else if (type == "invalid_url") { - reason = tr("The specified host address is not valid!"); - } - emit step1Failed(reason); - } -} diff --git a/src/scenes/add_server_wizard/add_server_wizard.h b/src/scenes/add_server_wizard/add_server_wizard.h deleted file mode 100644 index 1feec9b..0000000 --- a/src/scenes/add_server_wizard/add_server_wizard.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef ADD_SERVER_WIZARD_H -#define ADD_SERVER_WIZARD_H - -#include "../../RWADBusAdaptor.h" -#include <QObject> - -class Add_Server_wizard : public QObject -{ - Q_OBJECT -public: - explicit Add_Server_wizard(QObject *parent = nullptr); - -private: - bool _add_server(QString host_url); - bool _add_web_app_host(QString host_url); - - OrgArcticaProjectRWASupportSessionServiceInterface *_dbus_rwa; - void _initDBus(); - -signals: - void step1Success(); - void step1Failed(QString reason); - void step2Success(); - void step2Failed(QString reason); - -public slots: - bool processStep1(QString host_url); - bool processStep2(); - - void _add_web_app_host_dbus_replied(QDBusPendingCallWatcher *call); - //void _get_web_app_hosts_dbus_replied(QDBusPendingCallWatcher *call); - //void _remove_web_app_host_dbus_replied(QDBusPendingCallWatcher *call); -}; - -#endif // ADD_SERVER_WIZARD_H diff --git a/src/scenes/Scene_remote_control.qml b/src/scenes/remote_control/Scene_remote_control.qml index ea59ea7..dc152ef 100644 --- a/src/scenes/Scene_remote_control.qml +++ b/src/scenes/remote_control/Scene_remote_control.qml @@ -5,27 +5,34 @@ import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Controls.Material 2.3 +/*! + * This .qml file is a Scene which can be loaded through for + * example a StackView (main_content in main.qml). + */ + Item { id: scene_remote_control objectName: "Scene_remote_control" Label { id: dbus_api_status_text - text: "Unknown state of Service" + text: qsTr("Unknown state of session service.") anchors.leftMargin: 10 + 5 + dbus_api_status_indicator.width anchors.bottom: parent.bottom anchors.bottomMargin: 10 - wrapMode: Text.WrapAtWordBoundaryOrAnywhere + wrapMode: Text.WordWrap + anchors.rightMargin: 10 verticalAlignment: Text.AlignVCenter font.pointSize: 11 fontSizeMode: Text.Fit objectName: "dbus_api_status_text" anchors.left: parent.left + anchors.right: parent.right StatusIndicator { id: dbus_api_status_indicator width: height - height: parent.height + height: 20 objectName: "dbus_api_status_indicator" color: "#73d216" anchors.verticalCenter: parent.verticalCenter @@ -37,25 +44,25 @@ Item { Label { id: explain_function_label - text: qsTr("Please tell your remote support partner your access address and your access-PIN to let your partner connect to this computer.") - font.pixelSize: 18 - fontSizeMode: Text.VerticalFit + text: qsTr("Please tell your remote support \ +partner your access address and \ +your access-PIN to let your partner \ +connect to this computer.") wrapMode: Text.WordWrap - anchors.left: parent.left + font.pixelSize: 18 + anchors.topMargin: 10 anchors.leftMargin: 10 + anchors.rightMargin: 10 anchors.top: parent.top - anchors.topMargin: 10 + anchors.left: parent.left anchors.right: parent.right - anchors.rightMargin: 10 horizontalAlignment: Text.AlignLeft - enabled: false color: Material.theme == Material.Light ? "#000000" : "#FFFFFF" } Rectangle { id: dbus_api_status_line - y: 379 height: 1 radius: 1 anchors.right: parent.right @@ -113,7 +120,7 @@ Item { TextEdit { id: url_text height: parent.height/2 - text: mainqmladaptor.url + text: remote_control_manager.url anchors.rightMargin: 10 + copy_url_to_clipboard_button.width anchors.right: parent.right wrapMode: Text.WrapAtWordBoundaryOrAnywhere @@ -155,19 +162,21 @@ Item { y: -26 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - source: "../../images/into-clipboard.svg" + source: "../../../images/into-clipboard.svg" opacity: 0.65 } onClicked: { - mainqmladaptor.handleCopyToClipboardButtonClick(url_text.text); - toast.show(qsTr("Copied access address into clipboard!"), "1000"); + remote_control_manager.handleCopyToClipboardButtonClick(url_text.text); + toast.show(qsTr("Copied access address into clipboard!"), + "5000", + mainqmladaptor.ToastInfo); } ToolTip.text: qsTr("Copy the access address into the clipboard") hoverEnabled: true - ToolTip.delay: 1000 + ToolTip.delay: 600 ToolTip.timeout: 5000 ToolTip.visible: hovered } @@ -200,7 +209,7 @@ Item { objectName: "session_id_text" id: session_id_text height: parent.height/2 - text: mainqmladaptor.session_id + text: remote_control_manager.session_id font.letterSpacing: 10 anchors.rightMargin: 10 + copy_session_id_to_clipboard_button.width anchors.right: parent.right @@ -214,9 +223,9 @@ Item { Rectangle { radius: 5 color: Material.theme == Material.Light ? "#F0F0F0" : "#383838" - height: url_text.height + height: session_id_text.height // whole line + copy-into-clipboard button + some margin - width: url_text.width + copy_url_to_clipboard_button.width + 5 + 5 + width: session_id_text.width + copy_session_id_to_clipboard_button.width + 5 + 5 x: 0; y: 0 z: -1 } @@ -241,19 +250,21 @@ Item { anchors.horizontalCenter: parent.horizontalCenter opacity: 0.65 anchors.verticalCenter: parent.verticalCenter - source: "../../images/into-clipboard.svg" + source: "../../../images/into-clipboard.svg" fillMode: Image.PreserveAspectFit } onClicked: { - mainqmladaptor.handleCopyToClipboardButtonClick(pin_text.text); - toast.show(qsTr("Copied session-ID into clipboard!"), "1000"); + remote_control_manager.handleCopyToClipboardButtonClick(session_id_text.text); + toast.show(qsTr("Copied session-ID into clipboard!"), + "5000", + mainqmladaptor.ToastInfo); } ToolTip.text: qsTr("Copy the session-ID into the clipboard") hoverEnabled: true - ToolTip.delay: 1000 + ToolTip.delay: 600 ToolTip.timeout: 5000 ToolTip.visible: hovered } @@ -285,7 +296,7 @@ Item { objectName: "pin_text" id: pin_text height: parent.height/2 - text: mainqmladaptor.pin + text: remote_control_manager.pin anchors.rightMargin: 10 + copy_pin_to_clipboard_button.width anchors.right: parent.right font.pointSize: 15 @@ -326,19 +337,21 @@ Item { anchors.verticalCenter: parent.verticalCenter opacity: 0.65 anchors.horizontalCenter: parent.horizontalCenter - source: "../../images/into-clipboard.svg" + source: "../../../images/into-clipboard.svg" fillMode: Image.PreserveAspectFit } onClicked: { - mainqmladaptor.handleCopyToClipboardButtonClick(pin_text.text); - toast.show(qsTr("Copied PIN into clipboard!"), "1000"); + remote_control_manager.handleCopyToClipboardButtonClick(pin_text.text); + toast.show(qsTr("Copied PIN into clipboard!"), + "5000", + mainqmladaptor.ToastInfo); } ToolTip.text: qsTr("Copy the pin into the clipboard") hoverEnabled: true - ToolTip.delay: 1000 + ToolTip.delay: 600 ToolTip.timeout: 5000 ToolTip.visible: hovered } @@ -351,18 +364,19 @@ Item { id: start_support_button height: Math.min(50) objectName: "start_support_button" - text: qsTr("Start remote support session") + text: checked ? qsTr("Stop remote support session") : qsTr("Start remote support session") anchors.rightMargin: column.anchors.leftMargin anchors.bottom: dbus_api_status_line.top anchors.bottomMargin: 10 anchors.right: parent.right checkable: true - onClicked: mainqmladaptor.handleConnectButtonClick(checked); + onClicked: remote_control_manager.handleConnectButtonClick(checked); } } -/*##^## Designer { +/*##^## +Designer { D{i:0;autoSize:true;height:480;width:640} } - ##^##*/ +##^##*/ diff --git a/src/scenes/remote_control/remote_control_manager.cpp b/src/scenes/remote_control/remote_control_manager.cpp new file mode 100644 index 0000000..b76ea2a --- /dev/null +++ b/src/scenes/remote_control/remote_control_manager.cpp @@ -0,0 +1,511 @@ +#include "remote_control_manager.h" + +#ifndef QT_NO_DEBUG +#define CHECK_TRUE(instruction) Q_ASSERT(instruction) +#else +#define CHECK_TRUE(instruction) (instruction) +#endif + +RemoteControlManager::RemoteControlManager(QQmlApplicationEngine *engine, + MainQMLAdaptor *main_gui, + DBusAPI *dbus_api) : QObject() { + Q_ASSERT(dbus_api != nullptr); + Q_ASSERT(main_gui != nullptr); + Q_ASSERT(engine != nullptr); + + _current_session = nullptr; + _sessions = new QSet<Session*>; + _dbus_api = dbus_api; + _main_gui = main_gui; + _engine = engine; +} + +bool RemoteControlManager::setConnectButtonEnabled(bool enabled) { + // Find item via 'objectName' + QQuickItem *scene_remote_control = _engine->rootObjects().takeFirst()-> + findChild<QQuickItem*>("Scene_remote_control"); + QQuickItem *item = scene_remote_control-> + findChild<QQuickItem*>("start_support_button"); + if (item) { + item->setProperty("enabled", enabled); + if (item->property("checked").toBool()) { + item->setProperty("text", tr("Stop remote support session")); + } else { + item->setProperty("text", tr("Start remote support session")); + } + } else { + qWarning() << "Unable to find 'start_support_button' Item!"; + return false; + } + + return true; +} + +bool RemoteControlManager::setConnectButtonChecked(bool checked) { + // Find item via 'objectName' + QQuickItem *scene_remote_control = _engine->rootObjects().takeFirst()-> + findChild<QQuickItem*>("Scene_remote_control"); + QQuickItem *item = scene_remote_control-> + findChild<QQuickItem*>("start_support_button"); + if (item) { + item->setProperty("checked", checked); + } else { + qWarning() << "Unable to find 'start_support_button' Item!"; + return false; + } + + return true; +} + +bool RemoteControlManager::setStatusIndicatorText(QString status_text) { + // Find item via 'objectName' + QQuickItem *scene_remote_control = _engine->rootObjects().takeFirst()-> + findChild<QQuickItem*>("Scene_remote_control"); + QQuickItem *item = scene_remote_control-> + findChild<QQuickItem*>("dbus_api_status_text"); + if (item) { + item->setProperty("text", status_text); + } else { + qWarning() << "Unable to find 'dbus_api_status_text' Item!"; + return false; + } + + return true; +} + +bool RemoteControlManager::setStatusIndicatorColor(bool active, QColor color) { + // Find item via 'objectName' + QQuickItem *scene_remote_control = _engine->rootObjects().takeFirst()-> + findChild<QQuickItem*>("Scene_remote_control"); + QQuickItem *item = scene_remote_control-> + findChild<QQuickItem*>("dbus_api_status_indicator"); + if (item) { + item->setProperty("active", active); + item->setProperty("color", color); + } else { + qWarning() << "Unable to find 'dbus_api_status_indicator' Item!"; + return false; + } + + return true; +} + +void RemoteControlManager::handleCopyToClipboardButtonClick(QString copy_data) { + QClipboard *clipboard = QApplication::clipboard(); + QString originalText = clipboard->text(); + clipboard->setText(copy_data); + qDebug() << "Copied into clipboard:" << copy_data; +} + +QString RemoteControlManager::getURL() { + if (getCurrentSession() == nullptr) { + return tr("Not available yet"); + } + return getCurrentSession()->getURL(); +} + +QString RemoteControlManager::getPin() { + if (getCurrentSession() == nullptr) { + return "-----"; + } + return getCurrentSession()->getPin(); +} + +QString RemoteControlManager::getSessionID() { + if (getCurrentSession() == nullptr) { + return "-----"; + } + return getCurrentSession()->getSessionID(); +} + +Session* RemoteControlManager::getCurrentSession() { + return _current_session; +} + +void RemoteControlManager::addSession(Session *session) { + _sessions->insert(session); +} + +bool RemoteControlManager::removeSession(Session *session) { + if (getCurrentSession() == session) { + setCurrentSession(nullptr); + } + + bool ok = _sessions->remove(session); + + if (session != nullptr) { + session->disconnect(); + session->deleteLater(); + } + + setConnectButtonChecked(false); + setConnectButtonEnabled(true); + + currentSessionUrlChanged(tr("Not available yet")); + currentSessionPinChanged("-----"); + currentSessionSessionIDChanged("-----"); + + return ok; +} + +void RemoteControlManager::setCurrentSession(Session *session) { + if (session == nullptr) { + qDebug() << "Deselecting currentSession."; + _current_session = nullptr; + + return; + } + + if (_sessions->contains(session)) { + qDebug() << "Set currentSession to new session."; + _current_session = session; + + connectSession(_current_session); + } else { + qDebug() << "Given session was not in _sessions!"; + } +} + +void RemoteControlManager::connectSession(Session *session) { + // session --statusChanged-> this.currentSessionStatusChanged() + CHECK_TRUE(QObject::connect(session, + &Session::statusChanged, + this, + &RemoteControlManager::currentSessionStatusChanged)); + + // session --pinChanged-> this.currentSessionPinChanged() + CHECK_TRUE(QObject::connect(session, + &Session::pinChanged, + this, + &RemoteControlManager::currentSessionPinChanged)); + + // session --urlChanged-> this.currentSessionUrlChanged() + CHECK_TRUE(QObject::connect(session, + &Session::urlChanged, + this, + &RemoteControlManager::currentSessionUrlChanged)); + + // session --sessionIDChanged-> this.currentSessionSessionIDChanged() + CHECK_TRUE(QObject::connect(session, + &Session::sessionIDChanged, + this, + &RemoteControlManager::currentSessionSessionIDChanged)); + + + // session --startSucceeded -> this.currentSessionStartSucceeded() + CHECK_TRUE(QObject::connect(session, + &Session::startSucceeded, + this, + &RemoteControlManager::currentSessionStartSucceeded)); + + // session --startFailed-> this.currentSessionStartFailed() + CHECK_TRUE(QObject::connect(session, + &Session::startFailed, + this, + &RemoteControlManager::currentSessionStartFailed)); + + + // session --stopSucceeded-> this.currentSessionStopSucceeded() + CHECK_TRUE(QObject::connect(session, + &Session::stopSucceeded, + this, + &RemoteControlManager::currentSessionStopSucceeded)); + + // session --stopFailed-> this.currentSessionStopFailed() + CHECK_TRUE(QObject::connect(session, + &Session::stopFailed, + this, + &RemoteControlManager::currentSessionStopFailed)); + + + // session --statusSucceeded-> this.currentSessionStatusSucceeded() + CHECK_TRUE(QObject::connect(session, + &Session::statusSucceeded, + this, + &RemoteControlManager::currentSessionStatusSucceeded)); + + // session --statusFailed-> this.currentSessionStatusFailed() + CHECK_TRUE(QObject::connect(session, + &Session::statusFailed, + this, + &RemoteControlManager::currentSessionStatusFailed)); +} + +void RemoteControlManager::currentSessionStartFailed(QString error_message) { + _main_gui->showToast(error_message, 6000, Toast::ToastType::ToastError); + + // Start failed. No need to stop session, so remove it directly. + removeSession(getCurrentSession()); + + setConnectButtonChecked(false); + setConnectButtonEnabled(true); + + emit _main_gui->showWindow(); +} + +void RemoteControlManager::currentSessionStopFailed(QString error_message) { + _main_gui->showToast(error_message, 6000, Toast::ToastType::ToastError); + + // Stop failed, so don't do anything! The user should try again. + + // Set status indicator to the corresponding error message. + QString translated = translateStatusIndicatorText("stop_session_error"); + setStatusIndicatorText(translated); + + setConnectButtonChecked(true); + setConnectButtonEnabled(true); + + emit _main_gui->showWindow(); +} + +void RemoteControlManager::currentSessionStatusFailed(QString error_message) { + _main_gui->showToast(error_message, 6000, Toast::ToastType::ToastError); + + // Set status indicator to the corresponding error message. + QString translated = translateStatusIndicatorText("status_session_error"); + setStatusIndicatorText(translated); + + setConnectButtonChecked(false); + setConnectButtonEnabled(true); + + currentSessionUrlChanged(tr("Not available yet")); + currentSessionPinChanged("-----"); + currentSessionSessionIDChanged("-----"); + + emit _main_gui->showWindow(); +} + +void RemoteControlManager::currentSessionStartSucceeded() { + Session *current_session = getCurrentSession(); + if (current_session != nullptr) { + _main_gui->showToast( + tr("Session was started on '%0' successfully") + .arg(current_session->getHost()->alias()), + 6000, + Toast::ToastType::ToastSuccess + ); + } else { + qCritical().noquote() << tr("currentSessionStartSucceeded(): " + "Current Session is nullptr!"); + _engine->exit(1); + } + + // Set status indicator to the corresponding error message. + QString translated = translateStatusIndicatorText("start_session_success"); + setStatusIndicatorText(translated); + + setConnectButtonChecked(true); + setConnectButtonEnabled(true); +} + +void RemoteControlManager::currentSessionStopSucceeded() { + qDebug() << "Session stop succeeded: Delete current session object now."; + bool ok = removeSession(getCurrentSession()); + if (!ok) { + qWarning() << tr("Couldn't remove current session out of '_sessions' list."); + } + + _main_gui->showToast(tr("Remote support session was stopped."), + 6000, + Toast::ToastType::ToastSuccess); + + // Set status indicator to the corresponding error message. + QString translated = translateStatusIndicatorText("stop_session_success"); + setStatusIndicatorText(translated); + + setConnectButtonChecked(false); + setConnectButtonEnabled(true); +} + +void RemoteControlManager::currentSessionUnexpectedStop(QString error_message) { + _main_gui->showToast(error_message, 6000, Toast::ToastType::ToastError); + + setConnectButtonChecked(false); + setConnectButtonEnabled(true); + + qDebug() << "Delete current session object now."; + bool ok = removeSession(getCurrentSession()); + if (!ok) { + qWarning() << tr("Couldn't remove current session out of '_sessions' list."); + } +} + +void RemoteControlManager::currentSessionStatusSucceeded() { + // Nothing to do. +} + +void RemoteControlManager::currentSessionStatusChanged(QString new_status_code) { + Session *current_session = getCurrentSession(); + if (current_session == nullptr || !current_session->started) { + qDebug() << "RemoteControlManager::currentSessionStatusChanged(QString): " + "got called even though the session isn't even started."; + return; + } + QString translated = translateStatusIndicatorText(new_status_code); + setStatusIndicatorText(translated); +} + +QString RemoteControlManager::translateStatusIndicatorText(QString status_code) { + QString guiString = tr("Unknown state of service"); + setStatusIndicatorColor(false); + + if (status_code == "dead") { + + /* Session died */ + guiString = tr("Remote Support session was stopped ungracefully"); + + // Red color + setStatusIndicatorColor(true, QColor(255, 0, 0, 127)); + + currentSessionUnexpectedStop(guiString); + + } else if (status_code == "stopped") { + + /* Session was stopped normally somehow other than the users request. + * Remote support partner could have clicked exit for example */ + guiString = tr("Remote Support session was stopped"); + + // Green color + setStatusIndicatorColor(true, QColor(0, 255, 0, 127)); + + currentSessionUnexpectedStop(guiString); + + } else if (status_code == "active") { + + /* Partner is connected */ + guiString = tr("Your partner is connected to the Remote Support session"); + + // Green color + setStatusIndicatorColor(true, QColor(0, 255, 0, 127)); + + } else if (status_code == "start_session_success" || status_code == "running") { + + /* Session successfully started */ + guiString = tr("Remote Support session successfully started! " + "Waiting for your remote support partner to connect."); + + // yellow color (will be green when partner is connected) + setStatusIndicatorColor(true, QColor(255, 255, 0, 127)); + + } else if (status_code == "start_session_error") { + + /* Session couldn't be started */ + guiString = tr("Remote Support session couldn't be started!"); + + // Red color + setStatusIndicatorColor(true, QColor(255, 0, 0, 127)); + + } else if (status_code == "stop_session_success") { + + /* Session was successfully stopped by the users request */ + guiString = tr("Session stopped successfully."); + + // Green color + setStatusIndicatorColor(true, QColor(0, 255, 0, 127)); + + } else if (status_code == "stop_session_error") { + + /* Session couldn't be stopped */ + guiString = tr("Session could not be stopped!") + "\n" + + tr("remote support partner could still be connected!"); + + // Red color + setStatusIndicatorColor(true, QColor(255, 0, 0, 127)); + + } else if (status_code == "status_session_error") { + + /* Session's status couldn't be refreshed */ + guiString = tr("Session status could not be refreshed! " + "Your remote support partner could still be connected!"); + + // Red color + setStatusIndicatorColor(true, QColor(255, 0, 0, 127)); + + } + + qDebug().noquote() << QString("Translating status code '%0' to '%1'") + .arg(status_code) + .arg(guiString); + return guiString; +} + +void RemoteControlManager::currentSessionPinChanged(QString pin) { + emit pinChanged(pin); +} + +void RemoteControlManager::currentSessionUrlChanged(QString url) { + emit urlChanged(url); +} + +void RemoteControlManager::currentSessionSessionIDChanged(QString session_id) { + emit sessionIDChanged(session_id); +} + +void RemoteControlManager::connectToDBusAPI(Session *session) { + // _dbus_api --sessionStartResponse-> this.start_response() + CHECK_TRUE(QObject::connect(_dbus_api, + &DBusAPI::serviceStartResponse, + session, + &Session::start_response)); + + // _dbus_api --serviceStopResponse-> this.stop_response() + CHECK_TRUE(QObject::connect(_dbus_api, + &DBusAPI::serviceStopResponse, + session, + &Session::stop_response, + Qt::DirectConnection)); + + // _dbus_api --sessionStatusResponse-> this.status_response() + CHECK_TRUE(QObject::connect(_dbus_api, + &DBusAPI::serviceStatusResponse, + session, + &Session::status_response)); +} + +void RemoteControlManager::handleConnectButtonClick(bool checked) { + if (checked) { + // Create a Session object and start it. + qDebug() << "Creating and starting a new session object now."; + RWAHost *selected_host = _main_gui->getSelectedRWAHost(); + if (selected_host) { + qInfo() << tr("Creating a new session object."); + Session *new_session = new Session(_dbus_api, selected_host); + + connectToDBusAPI(new_session); + + qDebug() << "Adding session to QSet"; + addSession(new_session); + + qDebug() << "Setting session as current session."; + setCurrentSession(new_session); + + qInfo().noquote() << tr("Starting a remote support session on host '%0' " + "using the new session object.") + .arg(selected_host->uuid()); + new_session->start(); + } else { + qCritical().noquote() << tr("Can't start a remote support session. " + "There is no RWA host is selected!"); + } + + setConnectButtonChecked(false); + setConnectButtonEnabled(false); + } else { + Session *current_session = getCurrentSession(); + if (current_session != nullptr) { + emit current_session->stop(); + } else { + qCritical().noquote() << tr("RemoteControlManager::" + "handleConnectButtonClick(): Current Session " + "is nullptr!"); + setConnectButtonChecked(false); + setConnectButtonEnabled(true); + } + } +} + +/*void RemoteControlManager::onCloseHandler() { + // To cleanup things here + // check current session nullptr + getCurrentSession()->stop(); +}*/ diff --git a/src/scenes/remote_control/remote_control_manager.h b/src/scenes/remote_control/remote_control_manager.h new file mode 100644 index 0000000..4554724 --- /dev/null +++ b/src/scenes/remote_control/remote_control_manager.h @@ -0,0 +1,78 @@ +#ifndef REMOTE_CONTROL_MANAGER_H +#define REMOTE_CONTROL_MANAGER_H + +#include <QObject> + +#include "../../main_qmladaptor.h" +#include "../../DBusAPI.h" +#include "../../session.h" + +class RemoteControlManager : public QObject { + + Q_OBJECT + // this makes url available as a QML property + Q_PROPERTY(QString url READ getURL NOTIFY urlChanged) + // this makes pin available as a QML property + Q_PROPERTY(QString pin READ getPin NOTIFY pinChanged) + // this makes session_id available as a QML property + Q_PROPERTY(QString session_id READ getSessionID NOTIFY sessionIDChanged) + +public: + explicit RemoteControlManager(QQmlApplicationEngine *engine = nullptr, + MainQMLAdaptor *main_gui = nullptr, + DBusAPI *dbus_api = nullptr); + + bool setConnectButtonEnabled(bool enabled); + bool setConnectButtonChecked(bool checked); + bool setStatusIndicatorColor(bool active, QColor color = QColor(255,255,255)); + + QString translateStatusIndicatorText(QString status_code); + bool setStatusIndicatorText(QString status_text); + +private: + DBusAPI *_dbus_api; + MainQMLAdaptor *_main_gui; + QQmlApplicationEngine *_engine; + Session *_current_session; + QSet<Session*> *_sessions; + + void connectSession(Session *session); + void connectToDBusAPI(Session *session); + + bool refreshTimerIsRunning; + +signals: + void onConnectButtonClick(bool checked); + void pinChanged(QString pin); + void urlChanged(QString URL); + void sessionIDChanged(QString session_id); + +public slots: + void handleCopyToClipboardButtonClick(QString copy_data); + void handleConnectButtonClick(bool checked); + + void setCurrentSession(Session *session); + void addSession(Session *session); + bool removeSession(Session *session); + + void currentSessionStatusChanged(QString); + void currentSessionPinChanged(QString); + void currentSessionSessionIDChanged(QString); + void currentSessionUrlChanged(QString); + + QString getURL(); + QString getPin(); + QString getSessionID(); + Session* getCurrentSession(); + + void currentSessionStartFailed(QString error_message); + void currentSessionStopFailed(QString error_message); + void currentSessionStatusFailed(QString error_message); + void currentSessionUnexpectedStop(QString error_message); + + void currentSessionStartSucceeded(); + void currentSessionStopSucceeded(); + void currentSessionStatusSucceeded(); +}; + +#endif // REMOTECONTROLMANAGER_H diff --git a/src/session.cpp b/src/session.cpp index f3aa168..25a8512 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -1,8 +1,8 @@ /* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp - * Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> - * Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify @@ -26,174 +26,60 @@ #include "session.h" -Session::Session(QObject *parent, MainQMLAdaptor* main_gui) : QObject(parent) { - _initDBus(); - - _main_gui = main_gui; - - // QML -> MainQMLAdaptor::handleConnectButtonClick --onConnectButtonClick--> this::handleConnectButtonClick - QObject::connect(_main_gui, - SIGNAL(onConnectButtonClick(bool)), - this, - SLOT(handleConnectButtonClick(bool))); - - // session::setPin --pinChanged--> MainQMLAdaptor::setPin --pinChanged--> QML - QObject::connect(this, - SIGNAL(pinChanged(QString)), - main_gui, - SLOT(setPin(QString))); - // session::setURL --urlChanged--> MainQMLAdaptor::setURL --urlChanged--> QML - QObject::connect(this, - SIGNAL(urlChanged(QString)), - main_gui, - SLOT(setURL(QString))); - // session::setSessionID --sessionIDChanged--> MainQMLAdaptor::setSessionID --sessionIDChanged--> QML - QObject::connect(this, - SIGNAL(sessionIDChanged(QString)), - main_gui, - SLOT(setSessionID(QString))); - - // QML -> MainQMLAdaptor::onCloseHandler --onCloseSignal--> session::onCloseHandler - QObject::connect(main_gui, - SIGNAL(onCloseSignal()), - this, - SLOT(onCloseHandler())); - - this->init_vars(); -} +Session::Session(DBusAPI *dbus_api, RWAHost *host) : QObject() { + Q_ASSERT(host != nullptr); + Q_ASSERT(dbus_api != nullptr); -void Session::statusTimerEvent() { - qDebug() << "Status timer event triggered"; - this->refresh_status_request_dbus(this->getHostID(), this->getID()); -} + _dbus_api = dbus_api; + _host = host; -void Session::init_vars() { setPin("-----"); setSessionID("-----"); - setID("-----"); setURL(tr("Not available yet")); setStatus("unknown"); - - _main_gui->setConnectButtonChecked(false); - _main_gui->setConnectButtonEnabled(true); - _main_gui->setStatusIndicator(false); - - _minimizedBefore = false; -} - -QString Session::getStatus() { - return _status; -} - -QString Session::getURL() { - return _url; + started = false; } -QString Session::getID() { - return QString(_id); +Session::~Session() { + qDebug().noquote() << QString("Session #'%0' on host '%1' will be deconstructed.") + .arg(getSessionID()) + .arg(getHost()->alias()); + stop(); + disconnect(); } -QString Session::getSessionID() { - return QString(_session_id); -} - -QString Session::getHostID() { - return QString(_host_id); -} +void Session::statusTimerEvent() { + qDebug() << "Status timer event triggered"; -QString Session::getPin() { - return _pin; + refresh_status(); } -bool Session::isSessionAliveOrRunning(QString status) { - if (status == "running" || status == "active") { +bool Session::isSessionAliveOrRunning() { + if (getStatus() == "running" || getStatus() == "active") { return true; } else { return false; } } -void Session::minimizeWindow() { - if (!_minimizedBefore) { - qDebug() << "Minimizing window now..."; - emit _main_gui->minimizeWindow(); - _minimizedBefore = true; - } +RWAHost* Session::getHost() { + return _host; } -void Session::setStatus(QString status) { - _status = status; +QString Session::getStatus() { + return _status; +} - QString guiString = tr("Unknown state of service"); - _main_gui->setStatusIndicator(false); - - qDebug() << "setStatus(): Setting status to " << status; - - if (status == "running") { - /* Session is running but no one is connected yet */ - guiString = tr("Remote Support session is ready to be connected to"); - _main_gui->setStatusIndicator(true, QColor(255, 255, 0, 127)); - } else if (status == "dead") { - /* Session died */ - guiString = tr("Remote Support session was stopped ungracefully"); - - // Clear current variables - this->init_vars(); - _main_gui->setStatusIndicator(true, QColor(255, 0, 0, 127)); - - emit _main_gui->showWindow(); - - _main_gui->showToast(tr("Remote Support session was stopped ungracefully"), 5000); - } else if (status == "stopped") { - /* Session is stopped */ - guiString = tr("Remote Support session was stopped"); - - // Clear current variables - this->init_vars(); - - emit _main_gui->showWindow(); - - _main_gui->showToast(tr("Remote Support session was stopped"), 5000); - } else if (status == "active") { - /* Partner is connected */ - QTimer::singleShot(1000, this, &Session::minimizeWindow); - guiString = tr("Your partner is connected to the Remote Support session"); - _main_gui->setStatusIndicator(true, QColor(0, 255, 0, 127)); - - } else if (status == "waiting_start_request_answer") { - /* When pressing on start button display following message while waiting */ - guiString = tr("Trying to reach session service..."); - } else if (status == "start_session_error") { - /* Session couldn't be started */ - guiString = tr("Remote Support session couldn't be started!"); - _main_gui->setStatusIndicator(true, QColor(255, 0, 0, 127)); - - _main_gui->showToast(tr("An error occured while trying to start a session!"), 5000); - } else if (status == "start_session_success") { - /* Session successfully started */ - guiString = tr("Remote Support session successfully started!"); - _main_gui->setStatusIndicator(true, QColor(255, 255, 0, 127)); - - _main_gui->showToast(tr("Session was started successfully")); - } else if (status == "status_session_error") { - /* Session's status couldn't be refreshed */ - _main_gui->showToast(tr("Session status could not be refreshed!"), 5000); - guiString = tr("Session status could not be refreshed!") + "\n" + tr("remote support partner could still be connected!"); - _main_gui->setStatusIndicator(true, QColor(255, 0, 0, 127)); - - emit _main_gui->showWindow(); - } else if (status == "stop_session_error") { - /* Session couldn't be stopped */ - _main_gui->showToast(tr("Session could not be stopped!"), 5000); - guiString = tr("Session could not be stopped!") + "\n" + tr("remote support partner could still be connected!"); - _main_gui->setStatusIndicator(true, QColor(255, 0, 0, 127)); - - emit _main_gui->showWindow(); - } +QString Session::getURL() { + return _url; +} - _main_gui->setStatus(guiString); +QString Session::getSessionID() { + return _session_id; +} - emit statusChanged(_status); +QString Session::getPin() { + return _pin; } void Session::setURL(QString url) { @@ -201,11 +87,6 @@ void Session::setURL(QString url) { emit urlChanged(url); } -void Session::setID(QString id) { - _id = id; - emit idChanged(id); -} - void Session::setSessionID(QString session_id) { _session_id = session_id; emit sessionIDChanged(session_id); @@ -216,108 +97,66 @@ void Session::setPin(QString pin) { emit pinChanged(pin); } -void Session::handleConnectButtonClick(bool checked) { - qDebug() << "-----Connect button handler-----" << - "\nCurrent service-session #" << this->getID() << - "\nCurrent support-session #" << this->getSessionID(); - - // Stopping even if nothing is running - this->stop_request_dbus(this->getID()); - - if (checked) { - // Start the Session again - this->start_request_dbus(getHostID()); - } - qDebug() << "-----\\Connect button handler-----"; +void Session::setHost(RWAHost *host) { + _host = host; + emit hostChanged(host); } -void Session::_initDBus() { - if (!QDBusConnection::sessionBus().isConnected()) { - qCritical() << "Cannot connect to the D-Bus session bus."; - } - - // Create DBus object - _dbus_rwa = new OrgArcticaProjectRWASupportSessionServiceInterface("org.ArcticaProject.RWASupportSessionService", "/RWASupportSessionService", - QDBusConnection::sessionBus(), this->parent()); - - qDebug("Initialized DBus object!"); +void Session::setStatus(QString status) { + _status = status; + emit statusChanged(status); } -void Session::start_request_dbus(QString host_id) { - qDebug() << "Requesting D-Bus session service to start a new session on host: " << host_id; - bool ok; - host_id.toLongLong(&ok); - if(ok == false){ - qErrnoWarning("Unable to convert <QString> id to <long long>"); - return; - } - - // Make an asynchrous 'start' call (Response will be sent to 'start_dbus_replied') - QDBusPendingCall async = _dbus_rwa->asyncCall("start", host_id); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); - - QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(start_dbus_replied(QDBusPendingCallWatcher*))); - - // Disable connect button to reduce spam. - // And don't enable it as long there is no status update. - // (Or something fails) - _main_gui->setConnectButtonEnabled(false); - this->setStatus("waiting_start_request_answer"); +void Session::start() { + _dbus_api->start_request(_host); } -void Session::start_dbus_replied(QDBusPendingCallWatcher *call) { - QString result = ""; - - QDBusPendingReply<QString> reply = *call; - if (reply.isError()) { - qDebug() << "D-Bus 'start' request failed, this was the reply:"; - qDebug() << reply.error(); - - // The user should have the oportunity to try again. - this->init_vars(); - - qCritical() << "An error occured while creating a new session!"; - this->setStatus("start_session_error"); - +void Session::start_response(QJsonDocument *doc) { + // Q_ASSERT lets the program crash immediatly after method call + // when the session service is not started. + // Don't use Q_ASSERT(doc != nullptr); instead use: + if (doc == nullptr) { + emit startFailed(tr("Can't connect to underlying session service! " + "Is the session service started?")); return; - } else { - result = reply.argumentAt<0>(); } - call->deleteLater(); - - qDebug() << "Raw JSON from starting session is:" << result; - QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); // Get the QJsonObject - QJsonObject jObject = doc.object(); + QJsonObject jObject = doc->object(); QVariantMap mainMap = jObject.toVariantMap(); // Status of request - QString request_status = mainMap["status"].toString(); - if (request_status == "success") { - qDebug() << "Successfully started a Session."; - this->setStatus("start_session_success"); - - // Immediately ask for the status of the session - QTimer::singleShot(0, this, &Session::statusTimerEvent); - } else { - qCritical() << "An error occured while creating a new session!"; - this->init_vars(); - this->setStatus("start_session_error"); + QString status = mainMap["status"].toString(); + if (status != "success") { + QString type = mainMap["type"].toString(); + QString error_message = tr("An error occured while creating a new session!"); + + if (type == "multiple") { + // TODO: Ask to stop other session via a message dialog. + error_message = tr("The session service is configured " + "to not support multiple sessions " + "and there is already a session running."); + } else if (type == "connection") { + error_message = tr("Couldn't connect to host '%0'.") + .arg(getHost()->alias()); + } else if (type == "host_not_found") { + error_message = tr("The RWA host '%0' couldn't be found.") + .arg(getHost()->alias()); + } else if (type == "permission_denid") { + error_message = tr("The RWA host '%0' doesn't grant access.") + .arg(getHost()->alias()); + } else if (type == "unsupported_server") { + error_message = tr("The RWA host '%0' is not supported.") + .arg(getHost()->alias()); + } + + setStatus("start_session_error"); + qCritical().noquote() << error_message; + emit startFailed(error_message); return; } - // Service ID == PID bool ok; - long long service_id = mainMap["id"].toLongLong(&ok); - this->setID(QString::number(service_id)); - // Sanity Check - if(ok == false){ - qErrnoWarning("Unable to parse <service_id> out of dbus answer!"); - init_vars(); - return; - } // URL of remote web app frontend QString url = mainMap["url"].toString(); @@ -325,178 +164,108 @@ void Session::start_dbus_replied(QDBusPendingCallWatcher *call) { // PIN long long pin = mainMap["pin"].toLongLong(&ok); - this->setPin(QString::number(pin)); // Sanity Check if(ok == false){ - qErrnoWarning("Unable to parse <pin> out of dbus answer!"); - init_vars(); + QString error_message = "Unable to parse <pin> into longo long out of dbus answer!"; + qCritical().noquote() << error_message; + emit startFailed(error_message); return; } + this->setPin(QString::number(pin)); // session_id = remote support id from the rwa-server long long session_id = mainMap["session_id"].toLongLong(); - this->setSessionID(QString::number(session_id)); // Sanity Check if(ok == false){ - qErrnoWarning("Unable to parse <pin> out of dbus answer!"); - init_vars(); + QString error_message = "Unable to parse <session_id> into long long out of dbus answer!"; + qCritical().noquote() << error_message; + emit startFailed(error_message); return; } + this->setSessionID(QString::number(session_id)); - qDebug() << "Got service_id:" << service_id << - "\nGot session_id:" << session_id << + qDebug() << "Got session_id:" << session_id << "\nGot url:" << url << "\nGot pin:" << pin; emit pinChanged(QString::number(pin)); emit urlChanged(url); - emit idChanged(QString::number(service_id)); emit sessionIDChanged(QString::number(session_id)); -} -void Session::stop_request_dbus(QString id) { - bool ok; - if (id.toLongLong(&ok) == 0 ){ - qDebug() << "Won't send a request to D-Bus service to stop a session when session ID == 0"; - return; - } - if(ok == false){ - qErrnoWarning("Unable to convert <QString> id to <long long>"); - return; - } - - // Stopping now. - qDebug() << "Requesting D-Bus session service to stop session #" << id; + started = true; - // Make an asynchrous 'stop' call (Response will be sent to 'stop_dbus_replied') - QDBusPendingCall async = _dbus_rwa->asyncCall("stop", id); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); + // Ask status every 1000 millisecond - QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(stop_dbus_replied(QDBusPendingCallWatcher*))); + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, + QOverload<>::of(&Session::statusTimerEvent)); + timer->start(1000); - // Clear current variables - this->init_vars(); + qDebug() << "Successfully started a session."; + this->setStatus("start_session_success"); - // Disable connect button to reduce spam. - // And don't enable it as long there is no status update. - // (Or something fails) - _main_gui->setConnectButtonEnabled(false); + emit startSucceeded(); } -void Session::stop_dbus_replied(QDBusPendingCallWatcher *call) { - QString result = ""; - - QDBusPendingReply<QString> reply = *call; - if (reply.isError()) { - qDebug() << "D-Bus 'stop' request failed, this was the reply:"; - qDebug() << reply.error(); - - this->init_vars(); - this->setStatus("stop_session_error"); +void Session::stop() { + if (started) + _dbus_api->stop_request(getHost(), getSessionID()); +} +void Session::stop_response(QJsonDocument *doc) { + // Q_ASSERT lets the program crash immediatly after method call + // when the session service is not started. + // Don't use Q_ASSERT(doc != nullptr); instead use: + if (doc == nullptr) { + emit stopFailed(tr("Can't connect to underlying session service! " + "Is the session service started?")); return; - } else { - result = reply.argumentAt<0>(); } - call->deleteLater(); - // Get the QJsonObject - QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); - QJsonObject jObject = doc.object(); + QJsonObject jObject = doc->object(); QVariantMap mainMap = jObject.toVariantMap(); QString new_status = mainMap["status"].toString(); - qDebug() << "stop_dbus_replied(): Refreshed status:" << new_status; + qDebug() << "stop_response() retrieved json. Status now:" << new_status; - // Clear current variables - this->init_vars(); + started = false; - // But the status indicator should display that the Session has stopped this->setStatus(new_status); -} -void Session::status_request_dbus(QString id) { - bool ok; - if (id.toLongLong(&ok) == 0 ){ - qDebug() << "Won't send a request to D-Bus service of a session's status when session ID == 0"; - return; - } - if(ok == false){ - qErrnoWarning("Unable to convert <QString> id to <long long>"); - return; - } - - // Requesting status now. - qDebug() << "Requesting status for session #" << id; - - // Make an asynchrous 'status' call (Response will be sent to 'status_dbus_replied') - QDBusPendingCall async = _dbus_rwa->asyncCall("status", id); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); - - QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(stop_dbus_replied(QDBusPendingCallWatcher*))); + emit stopSucceeded(); } -void Session::refresh_status_request_dbus(QString host_id, QString id) { - bool ok; - if (id.toLongLong(&ok) == 0){ - qDebug() << "Won't send a request to D-Bus service to refresh the status of a session when session ID == 0"; - return; - } - if(ok == false){ - qErrnoWarning("Unable to convert <QString> id to <long long>"); - return; - } - - // Refreshing status - qDebug() << "Requesting status refresh for session #" << id; - - // Make an asynchrous 'refresh_status' call (Response will be sent to 'status_dbus_replied') - QDBusPendingCall async = _dbus_rwa->asyncCall("refresh_status", host_id, id); - QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); - - QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(status_dbus_replied(QDBusPendingCallWatcher*))); +void Session::status() { + _dbus_api->status_request(getHost(), this->getSessionID()); } -void Session::status_dbus_replied(QDBusPendingCallWatcher *call) { - QString result = ""; - - QDBusPendingReply<QString> reply = *call; - if (reply.isError()) { - qDebug() << "D-Bus '(refresh_)status' request failed, this was the reply:"; - qDebug() << reply.error(); +void Session::refresh_status() { + _dbus_api->refresh_status_request(getHost(), this->getSessionID()); +} - this->init_vars(); +void Session::status_response(QJsonDocument *doc) { + // Q_ASSERT lets the program crash immediatly after method call, + // when the session service is not started. + // Don't use Q_ASSERT(doc != nullptr); instead use: + if (doc == nullptr) { + if (!_emitted_status_error_already) { + emit statusFailed(tr("Can't connect to underlying session service! " + "Is the session service started?")); - this->setStatus("status_session_error"); + _emitted_status_error_already = true; + } return; - } else { - result = reply.argumentAt<0>(); } - call->deleteLater(); - // Get the QJsonObject - QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); - QJsonObject jObject = doc.object(); + _emitted_status_error_already = false; + + QJsonObject jObject = doc->object(); QVariantMap mainMap = jObject.toVariantMap(); QString new_status = mainMap["status"].toString(); - qDebug() << "status_dbus_replied(): Refreshed status:" << new_status; - - // Enable (dis)connect button - _main_gui->setConnectButtonEnabled(true); - - this->setStatus(new_status); + qDebug() << "status_response() retrieved json. Status:" << new_status; - if (this->isSessionAliveOrRunning(new_status)) { - // Ask status every 1000 millisecond - QTimer::singleShot(1000, this, &Session::statusTimerEvent); - } -} + setStatus(new_status); -void Session::onCloseHandler() { - // To cleanup things here - this->stop_request_dbus(this->getID()); + emit statusSucceeded(); } diff --git a/src/session.h b/src/session.h index 1b8b2d6..e1c924f 100644 --- a/src/session.h +++ b/src/session.h @@ -1,8 +1,8 @@ /* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp - * Copyright 2020-2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> - * Copyright 2020-2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> + * Copyright 2020, 2021 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> + * Copyright 2020, 2021 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify @@ -24,94 +24,81 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#pragma once +#ifndef SESSION_H +#define SESSION_H #include <QObject> #include <QQmlApplicationEngine> #include <QQuickItem> #include <QTimerEvent> #include <QTranslator> -#include <QtDBus/QtDBus> -#include "RWADBusAdaptor.h" -#include "main_qmladaptor.h" +#include "RWAHost.h" +#include "DBusAPI.h" -class Session : public QObject -{ +class Session : public QObject { Q_OBJECT - Q_PROPERTY(QString host_id READ getHostID NOTIFY hostIDChanged) - // this makes status available as a QML property - Q_PROPERTY(QString status READ getStatus NOTIFY statusChanged) - // this makes service id available as a QML property - Q_PROPERTY(QString id READ getID NOTIFY idChanged) - // this makes session_id available as a QML property - Q_PROPERTY(QString session_id READ getSessionID NOTIFY sessionIDChanged) - // this makes url available as a QML property - Q_PROPERTY(QString url READ getURL NOTIFY urlChanged) - // this makes pin available as a QML property - Q_PROPERTY(QString pin READ getPin NOTIFY pinChanged) public: - explicit Session(QObject *parent, MainQMLAdaptor *main_gui = nullptr); + explicit Session(DBusAPI *dbus_api = nullptr, + RWAHost *host = nullptr); + ~Session(); QString getStatus(); QString getURL(); - QString getID(); - QString getHostID(); QString getSessionID(); QString getPin(); + RWAHost* getHost(); void setStatus(QString status); void setURL(QString url); - void setID(QString id); - void setHostID(QString host_id); void setSessionID(QString session_id); void setPin(QString pin); + void setHost(RWAHost *host); - // Starts a VNC Session - void start_request_dbus(QString host_id); - // Refreshes a VNC Session's status - void refresh_status_request_dbus(QString host_id, QString id); - // Stop the Session - void stop_request_dbus(QString id); - // Gets a VNC Session's status - void status_request_dbus(QString id); - // Returns true if Session is somewhat usable (Running, Alive, etc..) - bool isSessionAliveOrRunning(QString status); - -protected: - QString _status; - void statusTimerEvent(); - void init_vars(); + void start(); + void stop(); + void status(); + void refresh_status(); + + bool started; private: - MainQMLAdaptor* _main_gui; - QString _id; + void statusTimerEvent(); + + bool _emitted_status_error_already; + QString _status; + RWAHost *_host; + DBusAPI *_dbus_api; + QString _session_id; - QString _host_id; QString _url; QString _pin; - OrgArcticaProjectRWASupportSessionServiceInterface* _dbus_rwa; - void _initDBus(); bool _minimizedBefore = false; - void minimizeWindow(); signals: - void finished(); void statusChanged(QString); - void idChanged(QString); void sessionIDChanged(QString); void urlChanged(QString); void pinChanged(QString); - void hostIDChanged(QString); + void hostChanged(RWAHost*); -public slots: - void handleConnectButtonClick(bool checked); + void startFailed(QString error_message); + void stopFailed(QString error_message); + void statusFailed(QString error_message); - void start_dbus_replied(QDBusPendingCallWatcher *call); - void stop_dbus_replied(QDBusPendingCallWatcher *call); - void status_dbus_replied(QDBusPendingCallWatcher *call); + void startSucceeded(); + void stopSucceeded(); + void statusSucceeded(); - void onCloseHandler(); +public slots: + // Returns true if a session is somewhat usable (Running, Alive, etc..) + bool isSessionAliveOrRunning(); + + void start_response(QJsonDocument*); + void stop_response(QJsonDocument*); + void status_response(QJsonDocument*); }; + +#endif // SESSION_H diff --git a/update_locales.sh b/update_locales.sh index 4884cb6..8c05ece 100755 --- a/update_locales.sh +++ b/update_locales.sh @@ -24,7 +24,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -lupdate . -ts locales/*.ts +lupdate rwa-support-desktopapp.pro -no-obsolete linguist locales/*.ts -lrelease locales/de_DE.ts -qm locales/bin/de_DE.qm -lrelease locales/es_ES.ts -qm locales/bin/es_ES.qm +lrelease rwa-support-desktopapp.pro -compress -removeidentical |