QML activity interacts with backend.

Test QML activity changed for a simplified version of drbob's rsqml-models.
It requests JSON documents from the libresapilocalserver and shows them raw.
Updated Android documentation.
Moved Android qmake section to the end of libretroshare.pro and openpgpsdk.pro
to avoid static linking errors.
This commit is contained in:
manu 2016-09-04 15:01:44 +02:00
parent 47944b30e6
commit 50fe3dd711
29 changed files with 886 additions and 128 deletions

View file

@ -1,10 +0,0 @@
import QtQuick 2.7
Page1Form {
button1.onClicked: {
console.log("Button 1 clicked.");
}
button2.onClicked: {
console.log("Button 2 clicked.");
}
}

View file

@ -1,22 +0,0 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
Item {
property alias button1: button1
property alias button2: button2
RowLayout {
anchors.centerIn: parent
Button {
id: button1
text: qsTr("Press Me 1")
}
Button {
id: button2
text: qsTr("Press Me 2")
}
}
}

View file

@ -1,12 +1,7 @@
<?xml version="1.0"?>
<manifest package="org.retroshare.android.qml_app" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="RetroShare">
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name=".RetroShareQmlActivity"
android:label="RetroShare QML"
android:screenOrientation="unspecified"
android:launchMode="singleTop">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name=".RetroShareQmlActivity" android:label="RetroShare QML" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -74,11 +69,7 @@
</receiver>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<service
android:process=":rs"
android:name=".RetroShareAndroidService"
android:label="RetroShare Service"
android:exported="true"> <!-- Added to be able to run the service from adb shell -->
<service android:process=":rs" android:name=".RetroShareAndroidService" android:label="RetroShare Service" android:exported="true"> <!-- Added to be able to run the service from adb shell -->
<!-- android:process=":qt" is needed to force the service to run on a separate process than the Activity -->
@ -135,5 +126,5 @@
<!-- %%INSERT_FEATURES -->
<!-- Added by G10h4ck: Needed permission for autostart at boot -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
</manifest>

View file

@ -0,0 +1,15 @@
#ifndef DEBUGUTILS_H
#define DEBUGUTILS_H
#include <QDebug>
//To switch between debugging and normal mode, un-/comment next line
#define DEBUGGING
#ifdef DEBUGGING
#define myDebug(line) qDebug() << "| FILE:" << __FILE__ << " | LINE_NUMBER:"\
<< __LINE__ << " | FUNCTION:" << __FUNCTION__ << " | CONTENT:" << line
#else
#define myDebug(line)
#endif
#endif // DEBUGUTILS_H

View file

@ -0,0 +1,79 @@
#include "libresapilocalclient.h"
#include "debugutils.h"
#include <QChar>
/* Constructor de còpia per proves, no s'ha d'usar.
LibresapiLocalClient::LibresapiLocalClient(const LibresapiLocalClient & l)
{
//mLocalSocket = l.mLocalSocket;
receivedBytes = l.receivedBytes;
json = l.json;
}*/
LibresapiLocalClient::LibresapiLocalClient(const QString & socketPath) :
mLocalSocket(this)
{
myDebug(this);
mSocketPath = socketPath;
connect(& mLocalSocket, SIGNAL(error(QLocalSocket::LocalSocketError)),
this, SLOT(socketError(QLocalSocket::LocalSocketError)));
connect(& mLocalSocket, SIGNAL(readyRead()),
this, SLOT(read()));
//openConnection();
}
void LibresapiLocalClient::openConnection()
{
mLocalSocket.connectToServer(mSocketPath);
}
int LibresapiLocalClient::request(const QString & path, const QString & jsonData)
{
QByteArray data;
data.append(path); data.append('\n');
data.append(jsonData); data.append('\n');
mLocalSocket.write(data);
return 1;
}
void LibresapiLocalClient::socketError(QLocalSocket::LocalSocketError error)
{
myDebug("error!!!!\n" + mLocalSocket.errorString());//error.errorString());
}
void LibresapiLocalClient::read()
{
receivedBytes = mLocalSocket.readAll();
if(parseResponse()){ // pensar en fer un buffer per parsejar, per evitar errors.
emit goodResponseReceived(QString(receivedBytes));
return;
}
QString errMess = "The message was not understood!\n"
"It should be a JSON formatted text file\n"
"Its contents were:\n" + receivedBytes;
myDebug(errMess.replace(QChar('\n'), QChar::LineSeparator));
}
bool LibresapiLocalClient::parseResponse()
{
QJsonParseError error;
json = QJsonDocument::fromJson(receivedBytes, &error);
myDebug(QString(json.toJson()).replace(QChar('\n'), QChar::LineSeparator));
if(error.error == QJsonParseError::NoError){
return true;
}
myDebug(error.errorString());
return false;
}
const QJsonDocument & LibresapiLocalClient::getJson()
{
return json;
}

View file

@ -0,0 +1,64 @@
/*
* libresapi local socket client
* Copyright (C) 2016 Manu Pineda <manu@cooperativa.cat>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBRESAPILOCALCLIENT_H
#define LIBRESAPILOCALCLIENT_H
#include <QLocalSocket>
#include <QDir>
#include <QJsonDocument>
#include <QVector>
class LibresapiLocalClient : public QObject
{
Q_OBJECT
public:
LibresapiLocalClient() {}
// LibresapiLocalClient(const LibresapiLocalClient & l);
LibresapiLocalClient(const QString & socketPath);
// potser abstreure el següent amb QUrl urlPath (path) i amb QJson jsonData.
Q_INVOKABLE int request(const QString & path, const QString & jsonData);
const QJsonDocument & getJson();
Q_INVOKABLE void openConnection();
private:
QString mSocketPath;
QLocalSocket mLocalSocket;
QByteArray receivedBytes;
QJsonDocument json;
//QVector<QJsonDocument> responses;
bool parseResponse(); //std::string msg);
private slots:
void socketError(QLocalSocket::LocalSocketError error);
void read();
signals:
void goodResponseReceived(const QString & msg);//, int requestId);
public slots:
};
#endif // LIBRESAPILOCALCLIENT_H

View file

@ -18,30 +18,37 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QDebug>
#include <QtAndroidExtras>
#include <QFileInfo>
#include <QDateTime>
#include "libresapilocalclient.h"
#include "retroshare/rsinit.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
QQmlApplicationEngine engine;
QString sockPath = QString::fromStdString(RsAccounts::ConfigDirectory());
sockPath.append("/libresapi.sock");
QString sockPath = QString::fromStdString(RsAccounts::ConfigDirectory());
sockPath.append("/libresapi.sock");
LibresapiLocalClient llc(sockPath);
qmlRegisterType<LibresapiLocalClient>("LibresapiLocalClientQml", 1, 0, "LibresapiLocalClientComm");
QFileInfo fileInfo(sockPath);
engine.rootContext()->setContextProperty("llc", &llc);
engine.load(QUrl(QLatin1String("qrc:/qml/main.qml")));
qDebug() << "Is service.cpp running as a service?" << QtAndroid::androidService().isValid();
qDebug() << "Is service.cpp running as an activity?" << QtAndroid::androidActivity().isValid();
qDebug() << "QML APP:" << sockPath << fileInfo.exists() << fileInfo.lastModified().toString();
QFileInfo fileInfo(sockPath);
return app.exec();
qDebug() << "Is main.cpp running as a service?" << QtAndroid::androidService().isValid();
qDebug() << "Is main.cpp running as an activity?" << QtAndroid::androidActivity().isValid();
qDebug() << "QML APP:" << sockPath << fileInfo.exists() << fileInfo.lastModified().toString();
return app.exec();
}

View file

@ -1,37 +0,0 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page1 {
}
Page {
Label {
text: qsTr("Second page")
anchors.centerIn: parent
}
}
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("First")
}
TabButton {
text: qsTr("Second")
}
}
}

View file

@ -3,5 +3,21 @@
<file>main.qml</file>
<file>Page1.qml</file>
<file>Page1Form.ui.qml</file>
<file>qml/main.qml</file>
<file>qml/icons/star-2-128.png</file>
<file>qml/icons/settings-4-128.png</file>
<file>qml/icons/email-128.png</file>
<file>qml/icons/contacts-128.png</file>
<file>qml/PostedMsgDelegate.qml</file>
<file>qml/GxsService.qml</file>
<file>qml/GxsIdDelegate.qml</file>
<file>qml/GxsGroupDelegate.qml</file>
<file>qml/ForumMsgDelegate.qml</file>
<file>qml/ContactBox.qml</file>
<file>qml/ChannelMsgDelegate.qml</file>
<file>qml/ChannelGroupDelegate.qml</file>
<file>qml/ApplicationBar.qml</file>
<file>qml/AppButton.qml</file>
<file>qml/LibresapiLocalClientComm.qml</file>
</qresource>
</RCC>

View file

@ -0,0 +1,30 @@
import QtQuick 2.2
import QtQuick.Layouts 1.1
import "."
Rectangle {
id: appButton
property alias icon: appIcon.source
signal buttonClicked
width: parent.height
height: parent.height
color: "#00000000"
Image {
id: appIcon
anchors.centerIn: parent
width: 25
height: 25
}
MouseArea {
hoverEnabled: false
anchors.fill: parent
onClicked: {
appButton.buttonClicked()
}
}
}

View file

@ -0,0 +1,37 @@
import QtQuick 2.2
import QtQuick.Layouts 1.1
import "."
Rectangle {
id: status
anchors.fill: parent
color: "#336699" //"#FF7733"
height: 50
default property alias contents: placeholder.children
RowLayout {
id: placeholder
spacing: 0
width: 200
height: parent.height
anchors.top: parent.top
anchors.left: parent.left
}
ContactBox {
width: 200
height: parent.height
anchors.top: parent.top
anchors.right: parent.right
icon: "icons/contacts-128.png"
name: "Vade Retro"
status: "Away"
}
}

View file

@ -0,0 +1,33 @@
import QtQuick 2.2
import "."
Item {
id: item
width: parent.width
height: 50
Column {
Text { text: '<b>' + model.GroupName + '</b>' }
Text { text: GroupId }
}
MouseArea {
hoverEnabled: false
anchors.fill: parent
onClicked: {
item.ListView.view.currentIndex = index
channelMsgModel.updateEntries(model.GroupId)
console.log("Clicked on Channel GroupId: " + model.GroupId)
}
}
Rectangle {
width: parent.width
height: 1
color: "#AAAAAA"
anchors.left: parent.left
anchors.top: parent.bottom
}
}

View file

@ -0,0 +1,42 @@
import QtQuick 2.2
import "."
Item {
id: msgDelegate
width: parent.width
height: 150
Column {
Text { text: '<b>MsgId:</b> ' + AuthorId }
Text { text: '<b>AuthorId:</b> ' + AuthorId }
Row {
Text { text: '<b>Name:</b> ' + MsgName }
Text { text: ' <b>PublishTs:</b> ' + PublishTs }
}
Text { text: '<b>Msg:</b> ' + Msg }
Row {
Text { text: '<b>NumberFiles:</b> ' + NumberFiles }
Text { text: ' <b>TotalFileSize:</b> ' + TotalFileSize }
}
Text { text: '<b>FileNames:</b> ' + FileNames }
Text { text: '<b>FileSizes:</b> ' + FileSizes }
Text { text: '<b>FileHashes:</b> ' + FileHashes }
Row {
Text { text: '<b>HaveVoted:</b> ' + HaveVoted }
Text { text: ' <b>UpVotes:</b> ' + UpVotes }
Text { text: ' <b>DownVotes:</b> ' + DownVotes }
Text { text: ' <b>Comments:</b> ' + Comments }
}
}
MouseArea {
hoverEnabled: false
anchors.fill: parent
onClicked: {
item.ListView.view.currentIndex = index
}
}
}

View file

@ -0,0 +1,61 @@
import QtQuick 2.2
import "."
Item {
property alias icon: contactIcon.source
property alias name: contactName.text
property alias status: contactStatus.text
Rectangle {
anchors.fill: parent
color: "#00000000"
Image {
id: contactIcon
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
width: 40
height: 40
source: "icons/contacts-128.png"
}
Rectangle {
height: contactIcon.height
anchors.verticalCenter: parent.verticalCenter
anchors.left: contactIcon.right
color: parent.color
Text {
id: contactName
text: "Username"
anchors.left: parent.left
anchors.leftMargin: 10
anchors.bottom: contactStatus.top
anchors.bottomMargin: 2
horizontalAlignment: Text.AlignHCenter
font.pointSize: 14
font.bold: false
color: "#FFFFFF"
}
Text {
id: contactStatus
text: "Hello world!"
anchors.left: parent.right
anchors.leftMargin: 10
anchors.bottom: parent.bottom
anchors.bottomMargin: 1
horizontalAlignment: Text.AlignHCenter
font.pointSize: 10
font.bold: false
color: "#FFFFFF"
}
}
}
}

View file

@ -0,0 +1,41 @@
import QtQuick 2.2
import "."
Item {
id: msgDelegate
width: parent.width
height: col.height
Column {
id: col
Text { text: '<b>MsgId:</b> ' + AuthorId }
Text { text: '<b>AuthorId:</b> ' + AuthorId }
Row {
Text { text: '<b>Name:</b> ' + MsgName }
Text { text: ' <b>PublishTs:</b> ' + PublishTs }
}
Text {
wrapMode: Text.Wrap
text: '<b>Msg:</b> ' + Msg
}
}
MouseArea {
hoverEnabled: false
anchors.fill: parent
onClicked: {
item.ListView.view.currentIndex = index
}
}
Rectangle {
width: parent.width
height: 2
color: "#AAAAAA"
anchors.left: parent.left
anchors.top: parent.bottom
}
}

View file

@ -0,0 +1,26 @@
import QtQuick 2.2
import "."
Item {
id: item
property var msgModel: {}
width: parent.width
height: 50
Column {
Text { text: '<b>Name:</b> ' + model.GroupName }
Text { text: '<b>Number:</b> ' + GroupId }
}
MouseArea {
hoverEnabled: false
anchors.fill: parent
onClicked: {
item.ListView.view.currentIndex = index
item.msgModel.updateEntries(model.GroupId)
}
}
}

View file

@ -0,0 +1,33 @@
import QtQuick 2.2
import "."
Item {
id: item
width: parent.width
height: 50
Column {
Text { text: '<b>' + model.GroupName + '</b>' }
Text { text: GroupId }
}
MouseArea {
hoverEnabled: false
anchors.fill: parent
onClicked: {
item.ListView.view.currentIndex = index
//channelMsgModel.updateEntries(model.GroupId)
//console.log("Clicked on Channel GroupId: " + model.GroupId)
}
}
Rectangle {
width: parent.width
height: 1
color: "#AAAAAA"
anchors.left: parent.left
anchors.top: parent.bottom
}
}

View file

@ -0,0 +1,119 @@
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.1
import "."
Item {
id: gxsService
property alias icon: sideIcon.source
property alias title: sideTitle.text
property alias groupDelegate: sideList.delegate
property alias groupModel: sideList.model
property alias msgDelegate: mainList.delegate
property alias msgModel: mainList.model
RowLayout {
spacing: 0
anchors.fill: parent
Rectangle {
id: sideBar
width: 200
Layout.fillHeight: true
Rectangle {
id: sideHeader
width: parent.width
height: 30
Text {
id: sideTitle
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
width: 20
height: 20
text: "Service"
color: "#333333"
}
Image {
id: sideIcon
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 10
width: 20
height: 20
source: "icons/contacts-128.png"
}
}
Rectangle {
id: sideListBox
width: parent.width
anchors.top: sideHeader.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
ListView {
id: sideList
anchors.fill: parent
delegate: GxsGroupDelegate {
msgModel: mainList.model
}
// section.
section.property: "SubscribeStatus"
section.criteria: ViewSection.FullString
section.delegate: Rectangle {
width: sideListBox.width
height: childrenRect.height
color: "blue"
Text {
text: section
font.bold: true
font.pixelSize: 20
}
}
clip: true
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
focus: true
onCurrentItemChanged: {
console.log("SideBar Item Changed on " + gxsService.title)
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
ListView {
id: mainList
anchors.fill: parent
clip: true
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
focus: true
onCurrentItemChanged: {
console.log("item changed")
}
}
}
}
}

View file

@ -0,0 +1,5 @@
import LibresapiLocalClientQml 1.0
LibresapiLocalClientComm {
id: llc
}

View file

@ -0,0 +1,40 @@
import QtQuick 2.2
import "."
Item {
id: msgDelegate
width: parent.width
height: 150
Column {
Text { text: '<b>MsgId:</b> ' + AuthorId }
Text { text: '<b>AuthorId:</b> ' + AuthorId }
Row {
Text { text: '<b>Name:</b> ' + MsgName }
Text { text: ' <b>PublishTs:</b> ' + PublishTs }
}
Text { text: '<b>Link:</b> ' + Link }
Text { text: '<b>Notes:</b> ' + Notes }
Row {
Text { text: '<b>Hot:</b> ' + HotScore }
Text { text: ' <b>Top:</b> ' + HotScore }
Text { text: ' <b>New:</b> ' + HotScore }
}
Row {
Text { text: '<b>HaveVoted:</b> ' + HaveVoted }
Text { text: ' <b>UpVotes:</b> ' + UpVotes }
Text { text: ' <b>DownVotes:</b> ' + DownVotes }
Text { text: ' <b>Comments:</b> ' + Comments }
}
}
MouseArea {
hoverEnabled: false
anchors.fill: parent
onClicked: {
item.ListView.view.currentIndex = index
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,160 @@
//import QtQuick 2.7 //2.2
//import QtQuick.Layouts 1.0 //1.1
//import QtQuick.Controls 2.0 //1.1
import "."
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.1 // millor fer servir 2.0 o més
import LibresapiLocalClientQml 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("RSChat")
/*
LibresapiLocalClientComm{
id: llc
onGoodResponseReceived: gxss.title = msg
}*/
onSceneGraphInitialized: llc.openConnection()
Rectangle {
id: page
width: 600; height: 400
color: "#336699" // "#FFFFFF"
Rectangle {
id: header
width: parent.width
anchors.top: parent.top
anchors.left: parent.left
height: 50
ApplicationBar {
id: status
AppButton {
icon: "icons/contacts-128.png"
onButtonClicked : {
tabView.currentIndex = 0
}
}
AppButton {
icon: "icons/settings-4-128.png"
onButtonClicked : {
tabView.currentIndex = 1
}
}
AppButton {
icon: "icons/email-128.png"
onButtonClicked : {
tabView.currentIndex = 2
}
}
AppButton {
icon: "icons/star-2-128.png"
onButtonClicked : {
tabView.currentIndex = 3
}
}
}
}
TabView {
id: tabView
width: parent.width
anchors.top: header.bottom
anchors.left: parent.left
anchors.bottom: parent.bottom
tabsVisible: false
Tab {
id: gxsIds
//onActiveChanged: llc.request("/identity/", "")
onVisibleChanged: llc.request("/identity/", "")
GxsService {
id: gxss
title: "Friends"
// Button {
// text: "buto"
// anchors.left: gxss.right
// onClicked: {
// // gxss.title = "provaboba"
// // gxss.title = llc.request("/identity/", "")
// //llc.request("/identity/", "") // canviar per onVisibleChanged de Tab potser
// }
// }
Connections {
target: llc
onGoodResponseReceived: gxss.title = msg //console.log("Image has changed!")
}
//groupDelegate: GxsIdDelegate {}
//groupModel: gxsIdModel
}
}
Tab {
id: forum
GxsService {
id: gxssforum
title: "Forums"
onVisibleChanged: llc.request("/control/locations/", "")
Connections {
target: llc
onGoodResponseReceived: gxssforum.title = msg //console.log("Image has changed!")
}
// This one uses the default GxsGroupDelegate.
// groupModel: forumGroupModel
// msgDelegate: ForumMsgDelegate {}
// msgModel: forumMsgModel
}
}
Tab {
id: channelLinks
GxsService {
title: "Channels"
// custom GroupDelegate.
// groupDelegate: ChannelGroupDelegate {}
// groupModel: channelGroupModel
// msgDelegate: ChannelMsgDelegate {}
// msgModel: channelMsgModel
}
}
Tab {
id: postedLinks
GxsService {
title: "Posted"
// This one uses the default GxsGroupDelegate.
// groupModel: postedGroupModel
// msgDelegate: PostedMsgDelegate {}
// msgModel: postedMsgModel
}
}
}
}
}

View file

@ -4,7 +4,8 @@ QT += qml quick androidextras
CONFIG += c++11
SOURCES += main.cpp
SOURCES += main.cpp \
libresapilocalclient.cpp
RESOURCES += qml.qrc
@ -29,3 +30,7 @@ DEPENDPATH *= ../../libretroshare/src
INCLUDEPATH *= ../../libretroshare/src
PRE_TARGETDEPS *= ../../libretroshare/src/lib/libretroshare.a
LIBS *= ../../libretroshare/src/lib/libretroshare.a
HEADERS += \
libresapilocalclient.h \
debugutils.h