diff --git a/v0.001/source-android/friendiqa.pro.user b/v0.001/source-android/friendiqa.pro.user
deleted file mode 100644
index 46a0323..0000000
--- a/v0.001/source-android/friendiqa.pro.user
+++ /dev/null
@@ -1,387 +0,0 @@
-
-
-
-
-
- EnvironmentId
- {feb2a9e8-6b42-4908-9ecd-b9e4d47e7412}
-
-
- ProjectExplorer.Project.ActiveTarget
- 0
-
-
- ProjectExplorer.Project.EditorSettings
-
- true
- false
- true
-
- Cpp
-
- CppGlobal
-
-
-
- QmlJS
-
- QmlJSGlobal
-
-
- 2
- UTF-8
- false
- 4
- false
- 80
- true
- true
- 1
- true
- false
- 0
- true
- true
- 0
- 8
- true
- 1
- true
- true
- true
- false
-
-
-
- ProjectExplorer.Project.PluginSettings
-
-
-
- ProjectExplorer.Project.Target.0
-
- Android für armeabi-v7a (GCC 4.9, Qt 5.5.1)
- Android für armeabi-v7a (GCC 4.9, Qt 5.5.1)
- {10172d6f-97b2-4745-a95e-fc1020afb172}
- 1
- 0
- 0
-
- /home/pankraz/bin
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- false
-
-
-
-
- true
- Anwendungsdaten kopieren
-
- Qt4ProjectManager.AndroidPackageInstallationStep
-
-
- android-18
-
- true
- Android-APK erstellen
-
- QmakeProjectManager.AndroidBuildApkStep
- 2
- false
- false
-
- 4
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- true
- clean
-
-
- 1
- Bereinigen
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Debug
-
- Qt4ProjectManager.Qt4BuildConfiguration
- 2
- true
-
-
- /home/pankraz/ownCloud/clientsync/Friendiqa/v0.001/bin
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- false
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- false
-
-
-
-
- true
- Anwendungsdaten kopieren
-
- Qt4ProjectManager.AndroidPackageInstallationStep
-
-
- android-18
- /home/pankraz/ownCloud/clientsync/android_release.keystore
- true
- Android-APK erstellen
-
- QmakeProjectManager.AndroidBuildApkStep
- 2
- false
- false
-
- 4
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- true
- clean
-
-
- 1
- Bereinigen
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Release
-
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
-
- /home/pankraz/ownCloud/clientsync/Friendiqa/v0.001/build-friendiqa-Android_f_r_armeabi_v7a_GCC_4_9_Qt_5_5_1-Profile
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- true
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- false
-
-
-
-
- true
- Anwendungsdaten kopieren
-
- Qt4ProjectManager.AndroidPackageInstallationStep
-
-
- android-23
-
- true
- Android-APK erstellen
-
- QmakeProjectManager.AndroidBuildApkStep
- 2
- false
- false
-
- 4
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- true
- clean
-
-
- 1
- Bereinigen
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Profile
-
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
- 3
-
-
-
- true
- Deployment auf Android-Gerät
-
- Qt4ProjectManager.AndroidDeployQtStep
- true
-
- 1
- Deployment
-
- ProjectExplorer.BuildSteps.Deploy
-
- 1
- Deployment auf Android-Gerät
- Deployment auf Android-Gerät
- Qt4ProjectManager.AndroidDeployConfiguration2
-
- 1
-
-
- false
- false
- 1000
-
- true
-
- false
- false
- false
- false
- true
- 0.01
- 10
- true
- 1
- 25
-
- 1
- true
- false
- true
- valgrind
-
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
- friendiqa
-
- Qt4ProjectManager.AndroidRunConfiguration:/home/pankraz/ownCloud/clientsync/Friendiqa/v0.001/source-android/friendiqa.pro
- friendiqa.pro
- 3768
- false
- true
- false
- false
- true
-
- 1
-
-
-
- ProjectExplorer.Project.TargetCount
- 1
-
-
- ProjectExplorer.Project.Updater.FileVersion
- 18
-
-
- Version
- 18
-
-
diff --git a/v0.002/Develop/QtApp-debug.apk b/v0.002/Develop/QtApp-debug.apk
new file mode 100644
index 0000000..5be19f5
Binary files /dev/null and b/v0.002/Develop/QtApp-debug.apk differ
diff --git a/v0.002/Develop/source-android/android/AndroidManifest.xml b/v0.002/Develop/source-android/android/AndroidManifest.xml
new file mode 100644
index 0000000..acb0d5d
--- /dev/null
+++ b/v0.002/Develop/source-android/android/AndroidManifest.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v0.002/Develop/source-android/android/build.gradle b/v0.002/Develop/source-android/android/build.gradle
new file mode 100644
index 0000000..ef416b0
--- /dev/null
+++ b/v0.002/Develop/source-android/android/build.gradle
@@ -0,0 +1,57 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.1.0'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.application'
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
+
+android {
+ /*******************************************************
+ * The following variables:
+ * - androidBuildToolsVersion,
+ * - androidCompileSdkVersion
+ * - qt5AndroidDir - holds the path to qt android files
+ * needed to build any Qt application
+ * on Android.
+ *
+ * are defined in gradle.properties file. This file is
+ * updated by QtCreator and androiddeployqt tools.
+ * Changing them manually might break the compilation!
+ *******************************************************/
+
+ compileSdkVersion androidCompileSdkVersion.toInteger()
+
+ buildToolsVersion androidBuildToolsVersion
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
+ aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
+ res.srcDirs = [qt5AndroidDir + '/res', 'res']
+ resources.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ assets.srcDirs = ['assets']
+ jniLibs.srcDirs = ['libs']
+ }
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+}
diff --git a/v0.002/Develop/source-android/android/gradle.properties b/v0.002/Develop/source-android/android/gradle.properties
new file mode 100644
index 0000000..a8fd204
--- /dev/null
+++ b/v0.002/Develop/source-android/android/gradle.properties
@@ -0,0 +1,9 @@
+## This file is automatically generated by QtCreator.
+#
+# This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+
+androidBuildToolsVersion=23.0.2
+androidCompileSdkVersion=18
+buildDir=.build
+qt5AndroidDir=/home/pankraz/bin/Qt5.5.1/5.5/android_armv7/src/android/java
diff --git a/v0.002/Develop/source-android/android/gradle/wrapper/gradle-wrapper.jar b/v0.002/Develop/source-android/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/v0.002/Develop/source-android/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/v0.002/Develop/source-android/android/gradle/wrapper/gradle-wrapper.properties b/v0.002/Develop/source-android/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1e61d1f
--- /dev/null
+++ b/v0.002/Develop/source-android/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
diff --git a/v0.002/Develop/source-android/android/gradlew b/v0.002/Develop/source-android/android/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/v0.002/Develop/source-android/android/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/v0.002/Develop/source-android/android/gradlew.bat b/v0.002/Develop/source-android/android/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/v0.002/Develop/source-android/android/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/v0.002/Develop/source-android/android/local.properties b/v0.002/Develop/source-android/android/local.properties
new file mode 100644
index 0000000..f73784e
--- /dev/null
+++ b/v0.002/Develop/source-android/android/local.properties
@@ -0,0 +1,6 @@
+## This file is automatically generated by QtCreator.
+#
+# This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+
+sdk.dir=/opt/android-sdk
diff --git a/v0.002/Develop/source-android/android/res/drawable-ldpi/icon.png b/v0.002/Develop/source-android/android/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..d77cd59
Binary files /dev/null and b/v0.002/Develop/source-android/android/res/drawable-ldpi/icon.png differ
diff --git a/v0.002/Develop/source-android/android/res/values/libs.xml b/v0.002/Develop/source-android/android/res/values/libs.xml
new file mode 100644
index 0000000..4d68673
--- /dev/null
+++ b/v0.002/Develop/source-android/android/res/values/libs.xml
@@ -0,0 +1,25 @@
+
+
+
+ - https://download.qt-project.org/ministro/android/qt5/qt-5.4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v0.002/Develop/source-android/application.qrc b/v0.002/Develop/source-android/application.qrc
new file mode 100644
index 0000000..94f9b85
--- /dev/null
+++ b/v0.002/Develop/source-android/application.qrc
@@ -0,0 +1,24 @@
+
+
+ js/layout.js
+ js/photoworker.js
+ js/service.js
+ qml/FriendComponent.qml
+ qml/MessageSend.qml
+ qml/Newsitem.qml
+ qml/PhotoComponent.qml
+ qml/PhotogroupComponent.qml
+ qml/PhotoPlaceholder.qml
+ qml/friendiqa.qml
+ qml/PhotoTab.qml
+ qml/ConfigTab.qml
+ qml/FriendsTab.qml
+ qml/NewsTab.qml
+ js/news.js
+ js/newsworker.js
+ js/helper.js
+ images/defaultcontact.jpg
+ qml/InfoBox.qml
+ qml/GroupComponent.qml
+
+
diff --git a/v0.002/Develop/source-android/common/filesystem.cpp b/v0.002/Develop/source-android/common/filesystem.cpp
new file mode 100644
index 0000000..b828c8d
--- /dev/null
+++ b/v0.002/Develop/source-android/common/filesystem.cpp
@@ -0,0 +1,49 @@
+#include "filesystem.h"
+
+FILESYSTEM *FILESYSTEM::instance()
+{
+ static FILESYSTEM filesystem;
+ return &filesystem;
+}
+
+FILESYSTEM::FILESYSTEM(QObject *parent) : QObject(parent){}
+
+void FILESYSTEM::setDirectory(QString Directory)
+{
+ if (Directory!=m_Directory) {
+ m_Directory = Directory;
+ emit directoryChanged();
+ }
+}
+
+QString FILESYSTEM::Directory() const
+{
+ return m_Directory;
+}
+
+void FILESYSTEM::makeDir(QString name)
+{
+ QDir dir(m_Directory);
+ if (dir.mkdir(name)){
+ emit success(name);
+ }
+ else {emit error(name,1);}
+}
+
+void FILESYSTEM::rmDir(QString name)
+{
+ QDir dir(m_Directory);
+ if (dir.rmdir(name)){
+ emit success(name);
+ }
+ else {emit error(name,1);}
+}
+
+void FILESYSTEM::rmFile(QString name)
+{
+ QDir dir(m_Directory);
+ if(dir.remove(name)){
+ emit success(name);
+ }
+ else {emit error(name,1);}
+}
diff --git a/v0.002/Develop/source-android/common/filesystem.h b/v0.002/Develop/source-android/common/filesystem.h
new file mode 100644
index 0000000..f0aebb3
--- /dev/null
+++ b/v0.002/Develop/source-android/common/filesystem.h
@@ -0,0 +1,49 @@
+#ifndef FILESYSTEM_H
+#define FILESYSTEM_H
+
+#include
+#include
+
+class FILESYSTEM : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString Directory READ Directory WRITE setDirectory NOTIFY directoryChanged)
+
+public:
+ static FILESYSTEM *instance();
+ explicit FILESYSTEM(QObject *parent = 0);
+ void setDirectory(QString Directory);
+ QString Directory() const;
+
+signals:
+ void directoryChanged();
+ void success(QString data);
+ void error(QString data, int code);
+
+public slots:
+ //void setDirectory(QString Directory);
+ void makeDir(QString name);
+ void rmDir(QString name);
+ void rmFile(QString name);
+
+private slots:
+ //void onReplyError(QNetworkReply::NetworkError code);
+ // void onReplySuccess();
+ //void onReadyRead();
+ // void onSSLError(const QList &errors);
+
+private:
+ //QByteArray buffer;
+ QString m_Directory;
+ //QString m_login;
+ //QHash params;
+ //QHash files;
+
+ //QNetworkAccessManager manager;
+ //QNetworkRequest request;
+ //QNetworkReply *reply;
+
+ //QString bufferToString();
+};
+
+#endif // FILSYSTEM_H
diff --git a/v0.002/Develop/source-android/common/friendiqa.cpp b/v0.002/Develop/source-android/common/friendiqa.cpp
new file mode 100644
index 0000000..670f21a
--- /dev/null
+++ b/v0.002/Develop/source-android/common/friendiqa.cpp
@@ -0,0 +1,22 @@
+#include
+#include
+#include
+#include "xhr.h"
+#include "filesystem.h"
+
+int main(int argc, char *argv[]) {
+ QApplication app(argc, argv);
+ QQuickView view;
+
+
+ XHR* xhr = XHR::instance();
+ view.rootContext()->setContextProperty("xhr", xhr);
+ FILESYSTEM* filesystem = FILESYSTEM::instance();
+ view.rootContext()->setContextProperty("filesystem", filesystem);
+ view.setSource(QUrl("qrc:/qml/friendiqa.qml"));
+ view.show();
+ view.connect(view.rootContext()->engine(), SIGNAL(quit()), &app, SLOT(quit()));
+ return app.exec();
+
+}
+
diff --git a/v0.002/Develop/source-android/common/uploadableimage.cpp b/v0.002/Develop/source-android/common/uploadableimage.cpp
new file mode 100644
index 0000000..81bea1a
--- /dev/null
+++ b/v0.002/Develop/source-android/common/uploadableimage.cpp
@@ -0,0 +1,90 @@
+#include "uploadableimage.h"
+
+
+#include
+#include
+#include
+#include
+
+void UploadableImage::setSource(const QString &a) {
+ if (a != m_source) {
+ m_source = a;
+ //m_base64 = "";
+ m_mimetype = "";
+ m_filename = "";
+
+ qDebug() << "UploadableImage::setSource : " << m_source;
+
+ if (m_source=="") {
+ emit sourceChanged();
+ //emit base64Changed();
+ emit mimetypeChanged();
+ emit filenameChanged();
+ return;
+ }
+
+ QImage fullimage = QImage(QUrl(m_source).toLocalFile());
+ if (fullimage.width() > 800 || fullimage.height() > 800) {
+ if (fullimage.width() > fullimage.height()) {
+ m_image = fullimage.scaledToWidth(800);
+ } else {
+ m_image = fullimage.scaledToHeight(800);
+ }
+ } else {
+ m_image = fullimage;
+ }
+ qDebug() << "UploadableImage::setSource : " << m_image.width() << "x" << m_image.height();
+ emit sourceChanged();
+
+ QFileInfo fi(m_source);
+
+ m_filename = fi.fileName();
+ emit filenameChanged();
+
+ QString filetype = fi.suffix().toUpper();
+ if (filetype!="PNG" && filetype!="JPG") {
+ filetype = "JPG";
+ }
+ qDebug() << "UploadableImage::setSource : " << "Saving as " << filetype;
+
+ m_mimetype = "image/"+filetype.toLower();
+ emit mimetypeChanged();
+
+ /*
+ QByteArray byteArray;
+ QBuffer buffer(&byteArray);
+ m_image.save(&buffer, filetype.toLatin1().constData());
+ QString b64 = QString::fromLatin1(byteArray.toBase64().data());
+
+ for(int k=0; k
+#include
+#include
+
+class UploadableImage : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
+ //Q_PROPERTY(QString base64 READ base64 NOTIFY base64Changed)
+ Q_PROPERTY(QString filename READ filename NOTIFY filenameChanged)
+ Q_PROPERTY(QString mimetype READ mimetype NOTIFY mimetypeChanged)
+ Q_PROPERTY(QByteArray bytes READ bytes)
+
+public:
+ void setSource(const QString &a);
+ QString source() const;
+
+ //QString base64() const;
+ QString filename() const;
+ QString mimetype() const;
+
+ QByteArray bytes();
+signals:
+ void sourceChanged();
+ //void base64Changed();
+ void filenameChanged();
+ void mimetypeChanged();
+
+private:
+ QString m_source;
+ QImage m_image;
+ //QString m_base64;
+ QString m_filename;
+ QString m_mimetype;
+};
+
+#endif // UPLOADABLEIMAGE_H
diff --git a/v0.002/Develop/source-android/common/xhr.cpp b/v0.002/Develop/source-android/common/xhr.cpp
new file mode 100644
index 0000000..6877135
--- /dev/null
+++ b/v0.002/Develop/source-android/common/xhr.cpp
@@ -0,0 +1,168 @@
+#include "xhr.h"
+
+#include
+#include
+#include
+
+#include "uploadableimage.h"
+
+XHR *XHR::instance()
+{
+ static XHR xhr;
+ return &xhr;
+}
+
+XHR::XHR(QObject *parent) : QObject(parent)
+{
+ request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
+}
+
+void XHR::setUrl(QString url)
+{
+ if (url!=m_url) {
+ m_url = url;
+ emit urlChanged();
+ }
+}
+
+void XHR::setLogin(QString login)
+{
+ if (login!=m_login) {
+ m_login = login;
+ emit loginChanged();
+ }
+}
+QString XHR::url() const
+{
+ return m_url;
+}
+
+QString XHR::login() const
+{
+ return m_login;
+}
+
+void XHR::setParam(QString name, QString value)
+{
+ params.insert(name, value);
+}
+
+void XHR::setImageFileParam(QString name, QString url)
+{
+ files.insert(name, url);
+}
+
+void XHR::clearParams()
+{
+ files.clear();
+ params.clear();
+}
+
+void XHR::get()
+{
+ QUrlQuery query;
+
+ QHashIterator i(params);
+ while(i.hasNext()) {
+ i.next();
+ query.addQueryItem(i.key(), i.value());
+ }
+
+ QUrl requrl(m_url);
+ requrl.setQuery(query);
+
+ QByteArray loginData = m_login.toLocal8Bit().toBase64();
+ QString headerData = "Basic " + loginData;
+ request.setRawHeader("Authorization", headerData.toLocal8Bit());
+
+
+ request.setUrl(requrl);
+ reply = manager.get(request);
+
+ connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess);
+ connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError)));
+ connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead);
+
+}
+
+void XHR::post()
+{
+ qDebug() << "start post to " << m_url;
+ QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+
+ QHashIterator iparams(params);
+ while(iparams.hasNext()) {
+ iparams.next();
+ qDebug() << "\t add param " << iparams.key() << " : " << iparams.value();
+ QHttpPart textPart;
+ textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + iparams.key() + "\""));
+
+
+ textPart.setBody(iparams.value().toUtf8());
+ multiPart->append(textPart);
+ }
+
+ UploadableImage uimg;
+ QHashIterator ifiles(files);
+ while(ifiles.hasNext()) {
+ ifiles.next();
+
+ uimg.setSource(ifiles.value());
+ qDebug() << "\t image: " << uimg.mimetype() << ", " << ifiles.key();
+
+ QHttpPart imagePart;
+ imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(uimg.mimetype()));
+ imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + ifiles.key() + "\"; filename=\""+uimg.filename()+"\""));
+ imagePart.setBody(uimg.bytes());
+ multiPart->append(imagePart);
+ }
+
+ QByteArray loginData = m_login.toLocal8Bit().toBase64();
+ QString headerData = "Basic " + loginData;
+ request.setRawHeader(QByteArray("Authorization"), headerData.toLocal8Bit());
+
+ request.setUrl(m_url);
+ reply = manager.post(request, multiPart);
+ qDebug() << "\t request sent";
+ connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess);
+ connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError)));
+ connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead);
+ connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError);
+ qDebug() << "\t reply signals connected";
+}
+
+void XHR::onReplyError(QNetworkReply::NetworkError code)
+{
+ qDebug() << code;
+ emit this->error( bufferToString(), (int) code);
+ reply->deleteLater();
+}
+
+void XHR::onReplySuccess()
+{
+ qDebug() << "!";
+ emit this->success( bufferToString() );
+ reply->deleteLater();
+}
+
+void XHR::onReadyRead()
+{
+ qDebug() << ".";
+ buffer += reply->readAll();
+}
+
+void XHR::onSSLError(const QList &errors)
+{
+ qDebug() << "XHR::onSSLError :" ;
+ QListIterator ierrs(errors);
+ while(ierrs.hasNext()) {
+ qDebug() << "\t" << ierrs.next().errorString();
+ }
+}
+
+QString XHR::bufferToString()
+{
+ return QTextCodec::codecForName("utf-8")->toUnicode(buffer);
+}
+
+
diff --git a/v0.002/Develop/source-android/common/xhr.h b/v0.002/Develop/source-android/common/xhr.h
new file mode 100644
index 0000000..3d61f25
--- /dev/null
+++ b/v0.002/Develop/source-android/common/xhr.h
@@ -0,0 +1,60 @@
+#ifndef XHR_H
+#define XHR_H
+
+#include
+#include
+#include
+#include
+
+class XHR : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
+ Q_PROPERTY(QString login READ login WRITE setLogin NOTIFY loginChanged)
+
+public:
+ static XHR *instance();
+
+ explicit XHR(QObject *parent = 0);
+
+ void setUrl(QString url);
+ // void setLogin(QString login);
+
+ QString url() const;
+ QString login() const;
+
+signals:
+ void urlChanged();
+ void loginChanged();
+ void success(QString data);
+ void error(QString data, int code);
+
+public slots:
+ void setLogin(QString login);
+ void setParam(QString name, QString value);
+ void setImageFileParam(QString name, QString url);
+ void clearParams();
+ void post();
+ void get();
+
+private slots:
+ void onReplyError(QNetworkReply::NetworkError code);
+ void onReplySuccess();
+ void onReadyRead();
+ void onSSLError(const QList &errors);
+
+private:
+ QByteArray buffer;
+ QString m_url;
+ QString m_login;
+ QHash params;
+ QHash files;
+
+ QNetworkAccessManager manager;
+ QNetworkRequest request;
+ QNetworkReply *reply;
+
+ QString bufferToString();
+};
+
+#endif // XHR_H
diff --git a/v0.002/Develop/source-android/friendiqa.pro b/v0.002/Develop/source-android/friendiqa.pro
new file mode 100644
index 0000000..09f9a77
--- /dev/null
+++ b/v0.002/Develop/source-android/friendiqa.pro
@@ -0,0 +1,40 @@
+# NOTICE:
+#
+# Application name defined in TARGET has a corresponding QML filename.
+# If name defined in TARGET is changed, the following needs to be done
+# to match new name:
+# - corresponding QML filename must be changed
+# - desktop icon filename must be changed
+# - desktop filename must be changed
+# - icon definition filename in desktop file must be changed
+# - translation filenames have to be changed
+
+# The name of your application
+TARGET = friendiqa
+CONFIG += debug
+QT += qml quick gui widgets
+
+SOURCES += common/friendiqa.cpp \
+ common/uploadableimage.cpp \
+ common/xhr.cpp \
+ common/filesystem.cpp
+
+RESOURCES = application.qrc
+
+OTHER_FILES += qml/friendiqa.qml \
+ translations/*.ts \
+ qml/*.qml
+ js/*.js
+
+# German translation is enabled as an example. If you aren't
+# planning to localize your app, remember to comment out the
+# following TRANSLATIONS line. And also do not forget to
+# modify the localized app name in the the .desktop file.
+TRANSLATIONS += translations/friendiqa-de.ts
+
+HEADERS += \
+ common/uploadableimage.h \
+ common/xhr.h \
+ common/filesystem.h
+
+
diff --git a/v0.002/Develop/source-android/images/defaultcontact.jpg b/v0.002/Develop/source-android/images/defaultcontact.jpg
new file mode 100644
index 0000000..bb7bce2
Binary files /dev/null and b/v0.002/Develop/source-android/images/defaultcontact.jpg differ
diff --git a/v0.002/Develop/source-android/js/friendworker.js b/v0.002/Develop/source-android/js/friendworker.js
new file mode 100644
index 0000000..6b6c5be
--- /dev/null
+++ b/v0.002/Develop/source-android/js/friendworker.js
@@ -0,0 +1,12 @@
+WorkerScript.onMessage = function(msg) {
+ msg.model.clear();
+ for (var j=0;j 0 ) {
+ for (var i in list) { if (list[i][prop] === val) {
+ return i;
+ }
+ }
+ } return -1;
+}
diff --git a/v0.002/Develop/source-android/js/layout.js b/v0.002/Develop/source-android/js/layout.js
new file mode 100644
index 0000000..51ccfb4
--- /dev/null
+++ b/v0.002/Develop/source-android/js/layout.js
@@ -0,0 +1,44 @@
+function showFriends(db) {
+ Service.readActiveConfig(db,function(login){
+ Service.requestFriends(login.url,login.user,login.password,displayFriends);
+ });
+}
+function displayFriends(obj){
+ for (var i=0; i= c.x)
+ f.contentX = c.x;
+ else if (f.contentX+f.width <= c.x+c.width)
+ f.contentX = c.x+c.width-f.width;
+ if (f.contentY >= c.y)
+ f.contentY = c.y;
+ else if (f.contentY+f.height <= c.y+c.height)
+ f.contentY = c.y+c.height-f.height;
+}
+
+function createObject(objectQml,qmlParameters,parentitem,callback) {
+ var component = Qt.createComponent(objectQml);
+ if (component.status === Component.Ready || component.status === Component.Error)
+ finishCreation(component,qmlParameters,parentitem,callback);
+ else
+ component.statusChanged.connect(finishCreation(qmlParameters));
+}
+
+function finishCreation(component,qmlParameters,parentitem,callback) {
+ if (component.status === Component.Ready) {
+ var createdObject = component.createObject(parentitem, qmlParameters);
+ if (createdObject === null)
+ print("Error creating image"); }
+ else if (component.status === Component.Error)
+ print("Error loading component:"+component.errorString());
+ else {print("created")}
+ callback(createdObject);
+}
+
diff --git a/v0.002/Develop/source-android/js/news.js b/v0.002/Develop/source-android/js/news.js
new file mode 100644
index 0000000..a7ed464
--- /dev/null
+++ b/v0.002/Develop/source-android/js/news.js
@@ -0,0 +1,311 @@
+.pragma library
+.import QtQuick.LocalStorage 2.0 as Sql
+.import "qrc:/js/helper.js" as Helperjs
+
+function requestFriends(login,database,rootwindow,callback){
+// return array of friends
+ Helperjs.friendicaRequest(login,"/api/statuses/friends", rootwindow,function (obj){
+ var friends=JSON.parse(obj);
+ for (var i=0;i0){
+ print("Like Contact"+JSON.stringify(news[i].friendica_activities.like));
+ for (var j=0;j0){
+ print("DisLike Contact"+JSON.stringify(news[i].friendica_activities.dislike));
+ for (var k=0;j 0 ) {
+ for (var i in list) { if (list[i][prop] === val) {
+ return true;
+ }
+ }
+ } return false;
+}
+
+function cleanDate(date){
+var cleanedDate= date.slice(0,3)+", "+date.slice(8,11)+date.slice(4,7)+date.slice(25,30)+date.slice(10,25);
+return cleanedDate
+}
diff --git a/v0.002/Develop/source-android/js/newsworker.js b/v0.002/Develop/source-android/js/newsworker.js
new file mode 100644
index 0000000..8d5cc46
--- /dev/null
+++ b/v0.002/Develop/source-android/js/newsworker.js
@@ -0,0 +1,48 @@
+WorkerScript.onMessage = function(msg) {
+ if(msg.appendnews!==true){ msg.model.clear()};
+ for (var j=0;j0){
+ if (msg.news[j].like.length==1){likeText= Qt.atob(msg.news[j].like[0].name)+" "+ qsTr("likes this.")}
+ else {likeText= msg.news[j].like.length+" "+ qsTr("like this.")}
+ }
+ if (msg.news[j].dislike.length>0){
+ if (msg.news[j].dislike.length==1){dislikeText= QT.atob(msg.news[j].dislike[0].name)+" "+ qsTr("doesn't like this.")}
+ else {dislikeText= msg.news[j].dislike.length+" "+ qsTr("don't like this.")}
+ }
+ if (msg.news[j].attendyes.length>0){
+ if (msg.news[j].attendyes.length==1){attendyesText= Qt.atob(msg.news[j].attendyes[0].name)+" "+ qsTr("will attend.")}
+ else {attendyesText= msg.news[j].attendyes.length+" "+ qsTr("persons will attend.")}
+ }
+ if (msg.news[j].attendno.length>0){
+ if (msg.news[j].attendno.length==1){attendnoText= Qt.atob(msg.news[j].attendno[0].name)+" "+ qsTr("will not attend.")}
+ else {attendnoText= msg.news[j].attendno.length+" "+ qsTr("persons will not attend.")}
+ }
+ if (msg.news[j].attendmaybe.length>0){
+ if (msg.news[j].attendmaybe.length==1){attendmaybeText= Qt.atob(msg.news[j].attendmaybe[0].name)+" "+ qsTr("may attend.")}
+ else {attendmaybeText= msg.news[j].attendmaybe.length+" "+ qsTr("persons may attend.")}
+ }
+ }
+ var friendica_activities={likeText:likeText,dislikeText:dislikeText,attendyesText:attendyesText,attendnoText:attendnoText,attendmaybeText:attendmaybeText}
+ var seconds=(msg.currentTime-newsitemobject.created_at)/1000;
+ var timestring="";
+ if (seconds<60) {timestring=seconds+" "+qsTr("seconds") +" "+qsTr("ago");}
+ else if (seconds<90){timestring=Math.round(seconds/60)+" "+qsTr("minute") +" "+qsTr("ago");}
+ else if (seconds<3600){timestring=Math.round(seconds/60)+" "+qsTr("minutes") +" "+qsTr("ago");}
+ else if (seconds<5400){timestring=Math.round(seconds/3600)+" "+qsTr("hour") +" "+qsTr("ago");}
+ else if (seconds<86400){timestring=Math.round(seconds/3600)+" "+qsTr("hours") +" "+qsTr("ago");}
+ else if (seconds<129600){timestring=Math.round(seconds/86400)+" "+qsTr("day") +" "+qsTr("ago");}
+ else if (seconds<3888000){timestring=Math.round(seconds/86400)+" "+qsTr("days") +" "+qsTr("ago");}
+ else if (seconds<5832000){timestring=Math.round(seconds/3888000)+" "+qsTr("month") +" "+qsTr("ago");}
+ else if (seconds<69984000){timestring=Math.round(seconds/3888000)+" "+qsTr("months") +" "+qsTr("ago");}
+ else {timestring=Math.round(seconds/69984000)+" "+qsTr("years") +" "+qsTr("ago");}
+ var data=({"newsitemobject": newsitemobject,"dateDiff":timestring,"friendica_activities":friendica_activities})}
+ // print("News:"+j+msg.news.length+JSON.stringify(data));
+ msg.model.append(data);}
+ if (j==msg.news.length){
+ msg.model.sync()
+ };
+}
diff --git a/v0.002/Develop/source-android/js/photoworker.js b/v0.002/Develop/source-android/js/photoworker.js
new file mode 100644
index 0000000..36646c7
--- /dev/null
+++ b/v0.002/Develop/source-android/js/photoworker.js
@@ -0,0 +1,15 @@
+WorkerScript.onMessage = function(msg) {
+ if (msg.firstalbum==0){msg.model.clear();}
+ var limit=0; if (msg.albums.length-msg.firstalbum<20){limit=msg.albums.length} else{limit=msg.firstalbum+20}
+ for (var j=msg.firstalbum;j0){
+ for(var i=0;i< AllStoredImages.length;i++){
+ var position=Helperjs.inArray(obj,"resourceID",AllStoredImages[i]);
+ if (position>-1){obj.splice(position,1)}
+ //obj.splice(obj.indexOf(AllStoredImages[i]),1);// list of objects instead of list!!!
+ }
+ }}
+ );
+ callback(obj);
+ })}
+
+function dataRequest(login,photoID,database,rootwindow) {
+ // check if image exist and call download function
+ Helperjs.friendicaRequest(login,"/api/friendica/photo?photo_id="+photoID, rootwindow, function (obj){
+ try{ if(obj==""){currentImageNo=currentImageNo+1}else{
+ var image = JSON.parse(obj);
+ print('storeData() for ' + JSON.stringify(obj));
+ try{sprite.destroy();}catch(e){}
+ if (obj["link"]["0"]){var source=obj["link"]["0"]} else {var source=obj["link"]["4"]}//source for profile picture or original size
+ print("Source"+obj["link"]["0"]+source)
+ obj["source"]=source;
+ var filename=obj.filename;
+ if (filename==""){// check if file as any filename
+ if (obj.type=="image/jpeg") {obj["filename"]=obj["id"]+".jpg"}
+ else if (obj.type=="image/png") {obj["filename"]=obj["id"]+".png"}
+ }
+ // check if text name has valid image format ending, otherwise append appropriate ending
+ if(["jpg","png"].indexOf(obj["filename"].substring(obj["filename"].lastIndexOf(".")+1,obj["filename"].length))==-1){
+ print("falscheEndung: "+obj["filename"].substring(obj["filename"].lastIndexOf(".")+1,obj["filename"].length));
+ { if (obj.type=="image/jpeg") {obj["filename"]=obj["filename"]+".jpg"}
+ else if (obj.type=="image/png") {obj["filename"]=obj["filename"]+".png"}
+ }
+ print("obj.Filename: "+obj["filename"]+" filename: "+filename)
+ }
+ saveImage(obj,login.imagestore,function(obj,sprite){
+ //sprite.destroy(500);
+ var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
+ db.transaction( function(tx) {
+ print('... check if a object exists: '+obj["id"])
+ var result = tx.executeSql('SELECT * from imageData where id = "'+obj["id"]+'"');
+ if(result.rows.length === 1) {// use update
+ print(obj["id"] +' exists, update it')
+ result = tx.executeSql('UPDATE imageData SET username ="' +login.username+ '",id="'+obj.id+'", created="'+obj.created+'", edited="'+obj.edited+'", profile="'+obj.profile+'", link="'+obj.source+'", filename="'+obj.filename+'",title="'+obj.title+'", desc="'+obj.desc+'", type="'+obj.type+'", width="'+obj.width+'", height="'+obj.height+'", album="'+obj.album+'", location="file://'+login.imagestore+'" where id="'+obj["id"]+'"');
+ } else {// use insert print('... does not exists, create it')
+ result = tx.executeSql('INSERT INTO imageData VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,obj.id,obj.created,obj.edited, obj.title, obj.desc, obj.album, obj.filename, obj.type, obj.height, obj.width,obj. profile,obj.source,'file://'+login.imagestore]);
+ print("Inserted");}
+ })})}}
+ catch (e){print("Data retrieval failure! "+ e+obj);}
+})}
+
+function saveImage(obj,storagedirectory,callback) {
+ // create image component from base64 code and save it
+ print("Storing "+storagedirectory+obj.filename+obj.width+"x"+obj.height);
+ var maxSize=Math.max(fotostab.width,fotostab.height);
+ var helpwidth=(obj.widthobj.height){ //landscape
+ helpheight=helpwidth*obj.height/obj.width
+ } else { //portrait
+ helpwidth=helpheight*obj.width/obj.height
+ }
+ var component=Qt.createComponent("qrc:/qml/PhotoPlaceholder.qml");
+ var sprite = component.createObject(fotostab, {"x":0,"y":0,"imageName":storagedirectory+obj.filename ,"width":helpwidth,"height":helpheight,"source": obj.source,"downloadtype":"picture"});
+ callback(obj,sprite)
+}
+
+function deleteImageData(database,user,field,selection,callback) { // does nothing useful at the moment
+ var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
+ print(' delete Image Data() for ' + field +"="+selection)
+ db.transaction( function(tx) {
+ result = tx.executeSql('UPDATE imageData SET data="" where '+ field +'="'+selection+'"');
+ callback(result)})
+}
+
+function requestFriendsAlbumPictures(friend,rootwindow,callback){
+// screenscraping of albums page of contact without user and password
+ Helperjs.friendicaWebRequest(friend.url.replace("profile","photos"),rootwindow,function(photohtml){
+ var photoarray=[];
+ var arr = photohtml.split("sidebar-photos-albums-li");
+ for (var i=2;i')-1);
+ var album={'link':albumlink,'name':albumname}
+ photoarray.push(album);
+ }
+//print("Album"+JSON.stringify(photoarray));
+callback(photoarray)
+ })
+}
+
+function requestFriendsPictures(link,rootwindow,callback){
+// screenscraping of pictures page for given album
+ Helperjs.friendicaWebRequest(link,rootwindow,function(photohtml){
+ var photoarray=[];
+ var basehtml=photohtml.substring(photohtml.indexOf('',photohtml.indexOf('-1){ //theme 1
+ var arr = photohtml.split("photo-album-image-wrapper-end");}
+
+// other themes
+ if (photohtml.indexOf("photo-album-wrapper")>-1){ //theme 2
+ var photoarea=photohtml.substring(photohtml.indexOf("photo-album-wrapper"),photohtml.indexOf("photo-album-end"))
+ var arr = photoarea.split("");}
+
+ for (var i=0;i0){
+ for(var i = 0; i < rs.rows.length; i++) {
+ rsArray.push(rs.rows.item(i))
+ }
+ var rsObject={server:rsArray[0].server,username:rsArray[0].username, password:rsArray[0].password,imagestore:rsArray[0].imagestore,maxnews:rsArray[0].maxnews,isActive:rsArray[0].isActive};
+ } else {
+ var rsObject=""
+ }
+ callback(rsObject);}}
+ );
+}
+
+function readActiveConfig(database){
+ var obj;
+ readConfig(database,function(config){
+obj=config},"isActive", 0);
+ return obj;
+}
+
+function deleteConfig(database,userobj,callback) { // delete user data from DB
+ print('deleteConfig()')
+if (userobj){var where = " WHERE username='"+ userobj.username+"' and server='"+userobj.server+"'";} else { return "no user selected!";}
+ var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
+ if(!db) { return; }
+ db.transaction( function(tx) {
+ print('... read from database '+where)
+ var rs = tx.executeSql('delete * from config'+where);
+ print(rs.toString);
+ callback(rs);
+ });
+}
diff --git a/v0.002/Develop/source-android/qml/AlbumComboBox.qml b/v0.002/Develop/source-android/qml/AlbumComboBox.qml
new file mode 100644
index 0000000..1683d4a
--- /dev/null
+++ b/v0.002/Develop/source-android/qml/AlbumComboBox.qml
@@ -0,0 +1,29 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.2
+
+Item {
+ ComboBox{
+ id: selectionTypeCombo
+ width: 150
+ model: ["album", "type","filename"]
+ onCurrentIndexChanged:{
+ var login=Service.readActiveConfig(db);
+ if (currentText!==""){
+ photogroupModel.clear();
+ Service.readData(db, "imageData",function(filter){
+ for (var j=0;j 1) {
+ stackView.pop(); event.accepted = true;
+ }
+ initialItem: Flickable{
+ width:root.width-5*mm
+ height:root.height-12*mm
+ contentHeight: configBackground.height
+ boundsBehavior: Flickable.StopAtBounds
+ Rectangle{
+ id:configBackground
+ color: "grey"
+ width:parent.width
+ height:Math.max(80*mm,root.height-12*mm)
+ focus: true
+
+ ComboBox{
+ y:mm
+ width: root.width/2
+ model: ListModel{
+ id: usermodel
+ }
+onCurrentIndexChanged:{
+ try {Service.readConfig(db,function(obj){
+ servername.text=obj.server;
+ username.text= obj.username;
+ password.text=Qt.atob(obj.password);
+ imagestore.text=obj.imagestore;
+ maxNewsText.text=obj.maxnews;
+ if( obj.isActive==0){isActiveField.text=qsTr("yes")} else {isActiveField.text=qsTr("no")}
+ },"username",currentText)}
+ catch (e){print(e)}
+ }
+}
+
+
+ Text {
+ text: "Server"
+ x: 4*mm; y: 10*mm
+ }
+ Text {
+ text: "User"
+ x: 4*mm; y: 20*mm
+ }
+
+ Text {
+ text: "Password"
+ x: 4*mm; y: 30*mm
+ }
+ Text {
+ text: "Image dir."
+ x: 4*mm; y: 40*mm
+ }
+
+ Text {
+ text: "Max. News"
+ x: 4*mm; y: 50*mm
+ }
+ Text {
+ text: "is Active"
+ x: 4*mm; y: 60*mm
+ }
+
+ Rectangle{color: "white"; x: 25*mm; y: 10*mm; width: root.width/2; height: 5*mm;}
+ Flickable {
+ id: servernameFlickable
+ x: 25*mm; y: 10*mm; width: root.width/2; height: 5*mm;
+ contentWidth: servername.paintedWidth
+ contentHeight: servername.paintedHeight
+ clip: true
+ TextEdit {
+ id: servername
+ width: servernameFlickable.width
+ height: servernameFlickable.height
+ focus: true
+ text:"https://..."
+ //wrapMode: TextEdit.NoWrap
+ //validator: RegExpValidator { regExp: /^(((http|https|ftp):\/\/)?([[a-zA-Z0-9]\-\.])+(\.)([[a-zA-Z0-9]]){2,4}([[a-zA-Z0-9]\/+=%&_\.~?\-]*))*$/}
+ // onEditingFinished:{}
+ onCursorRectangleChanged: Layoutjs.ensureVisibility(cursorRectangle,servernameFlickable)
+ }
+ }
+
+ Rectangle{
+ color: "white"
+ x: 25*mm; y: 20*mm; width: root.width/2; height: 5*mm;
+ TextInput {
+ id: username
+ anchors.fill: parent
+ selectByMouse: true
+ }
+ }
+ Rectangle{
+ color: "white"
+ x: 25*mm; y: 30*mm; width: root.width/2; height: 5*mm;
+ TextInput {
+ id: password
+ anchors.fill: parent
+ selectByMouse: true
+ echoMode: TextInput.PasswordEchoOnEdit
+ }
+ }
+
+ Rectangle{color: "white"; x: 25*mm; y: 40*mm; width: root.width/2-9*mm; height: 5*mm;}
+ Flickable {
+ id: imagestoreFlickable
+ x: 25*mm; y: 40*mm; width: root.width/2-9*mm; height: 5*mm;
+ clip: true
+ TextInput {
+ id: imagestore
+ width: imagestoreFlickable.width
+ height: imagestoreFlickable.height
+ wrapMode: TextEdit.NoWrap
+ onCursorRectangleChanged: Layoutjs.ensureVisibility(cursorRectangle,imagestoreFlickable)
+ }
+ }
+ Slider{ id: maxNews
+ x:37*mm; y: 50*mm;width: root.width/3;height:5*mm
+ minimumValue: 0;maximumValue:100000; stepSize: 1000
+ }
+ Rectangle{color: "white"; x: 25*mm; y: 50*mm; width: 9*mm; height: 5*mm;
+ TextEdit{id:maxNewsText;
+ anchors.fill: parent
+ verticalAlignment:TextEdit.AlignRight
+ text:maxNews.value
+ focus: true
+ selectByMouse: true
+ }
+ }
+
+
+ Rectangle{
+ x: 25*mm; y: 60*mm; width: root.width/2; height: 5*mm;
+ Text{
+ id: isActiveField
+ anchors.fill: parent
+ }
+ }
+
+ FileDialog {
+ id: imagestoreDialog
+ title: "Please choose a directory"
+ folder: shortcuts.pictures
+ selectFolder: true
+ onAccepted: {
+ var imagestoreString=imagestoreDialog.folder.toString(); imagestoreString=imagestoreString.replace(/^(file:\/{2})/,"")+"/"
+ imagestore.text=imagestoreString;
+ console.log("You chose: " + imagestoreDialog.folder)
+ }
+ onRejected: {
+ console.log("Canceled")
+ }
+ }
+
+ Button {
+ x: root.width/2+18*mm; y: 40*mm; width: 7*mm; height: 5*mm;
+ text: "..."
+ onClicked:
+ {imagestoreDialog.open()}
+ }
+
+
+ Button {
+ x: 25*mm; y: 70*mm; width: implicitWidth; height: implicitHeight;
+ text: "Update"
+ onClicked:{
+ var userconfig={server: servername.text, username: username.text, password:Qt.btoa(password.text), imagestore:imagestore.text,maxnews:maxNewsText.text};
+ var errormessage="";
+ if (servername.text==""){errormessage=qsTr("No server given! ")}
+ //if (!servername.acceptableInput){errormessage+=qsTr("Server name not valid! ")}
+ else if (username.text==""){errormessage+=qsTr("No username given! ")}
+ else if (password.text=="") {errormessage+=qsTr("No password given! ")}
+ else if (imagestore.text=="") {errormessage+=qsTr("No image directory given!")}
+ else if (maxNewsText.text=="") {errormessage+=qsTr("No maximum news number given!")}
+ else {errormessage=""}
+ if (errormessage=="") {
+ filesystem.Directory=userconfig.imagestore;
+ filesystem.makeDir("contacts");
+ Service.storeConfig(db,userconfig);
+ Service.readConfig(db,function(userconfig){Service.getServerConfig(userconfig,configBackground, function(obj){
+ var serverString=obj;
+ var serverconfigObject=Qt.createQmlObject(serverString,configBackground,"serverconfigOutput");
+ usermodel.append({text:username.text});
+ //reset values
+ root.login=userconfig;
+ root.contactlist=[];
+ root.news=[]
+ root.newContacts=[]
+ root.currentContact= 0
+ root.contactLoadType= ""
+ root.currentIndex=0;
+ newstab.active=true;
+ })},"isActive",0);
+ }
+ else {Helperjs.show("Error", errormessage,root)}
+}}
+
+ Button {
+ x: root.width/2+2*mm; y: mm; width: 5*mm; height: 5*mm;
+ text: "-"
+ onClicked:{
+ var userconfig={server: servername.text, username: username.text, password: Qt.btoa(password.text)};
+ Service.deleteConfig(db,userconfig);
+ }}
+
+ Button {
+ x: root.width/2+8*mm; y: mm; width: 5*mm; height: 5*mm;
+ text: "+"
+ onClicked:{
+ servername.text="https://..."
+ username.text=""
+ password.text=""
+ imagestore.text=""
+ maxNews.value=1000
+ isActiveField.text=""
+ }
+ }
+
+ Button {
+ x: root.width/2+14*mm; y: mm; width: 5*mm; height: 5*mm;
+ text: "?"
+ onClicked:{
+ configStack.push({item:"qrc:/qml/InfoBox.qml"});
+ }
+ }
+
+ Component.onCompleted: {
+ try{Helperjs.readData(db,"config",root.login.username,function(users){
+ users.sort(function(obj1, obj2) {
+ return obj1.isActive - obj2.isActive;
+ });
+ for (var i=0; i"+qsTr("Description")+": "+Qt.atob(friend.description)+"
"+qsTr("Server Type")+": "+friend.location+"
"+qsTr("Posts")+": "+friend.statuses_count+
+ "
"+qsTr("URL")+": "+friend.url+"
"+
+ qsTr("Created at")+": "+createdAtDate.toLocaleString(Qt.locale())
+ onLinkActivated: {
+ Qt.openUrlExternally(link)}
+ }
+ }
+
+ Row{
+ anchors.top: namelabelflickable.bottom
+ anchors.topMargin: 2*mm
+ spacing:4
+
+ Button{
+ id:photobutton
+ text:"Photos"
+ visible:friend.location=="Friendica"? 1:0
+ onClicked:{root.currentIndex=2;
+ fotostab.active=true;
+ root.fotoSignal(friend) ;
+ }
+ }
+
+ Button{
+ id:messagebutton
+ text:"Messages"
+ onClicked:{root.currentIndex=0;
+ newstab.active=true;
+ root.messageSignal(friend.id) ;
+ }
+ }
+
+ Button{
+ id:dmbutton
+ visible: friend.following=="true"?true:false
+ text: "DM"
+ onClicked:{root.currentIndex=0;
+ newstab.active=true;
+ root.directmessageSignal(friend.screen_name);
+ }
+ }
+
+ Button{
+ id: closeButton
+ text: "close"
+ onClicked:{friendComponent.state=""}
+ }
+ }
+ }
+}
+states: [
+ State {
+ name: "large"
+ PropertyChanges { target: namelabel; font.pixelSize: 4*mm; width:friendsTabView.width-4*mm; text:Qt.atob(friend.name)+" (@"+friend.screen_name+")"}
+ PropertyChanges { target: friendComponent; z: 2 }
+ PropertyChanges { target: wrapper; width:friendsTabView.width-3*mm;height:friendsTabView.height-14*mm}
+ PropertyChanges { target: photoImage; width:15*mm;height:15*mm }
+ PropertyChanges { target:friendComponent.GridView.view;contentY:friendComponent.y;contentX:friendComponent.x;interactive:false}
+ PropertyChanges { target: detailsrectangle; opacity:1 }
+ }
+]
+}
diff --git a/v0.002/Develop/source-android/qml/FriendsTab.qml b/v0.002/Develop/source-android/qml/FriendsTab.qml
new file mode 100644
index 0000000..71b4b0b
--- /dev/null
+++ b/v0.002/Develop/source-android/qml/FriendsTab.qml
@@ -0,0 +1,174 @@
+import QtQuick 2.0
+import QtQuick.Dialogs 1.2
+import QtQuick.Controls 1.2
+import "qrc:/js/service.js" as Service
+import "qrc:/js/helper.js" as Helperjs
+import "qrc:/js/news.js" as Newsjs
+import "qrc:/qml"
+
+Rectangle {
+ y:1
+ color: "white"
+ TabView{
+ id:friendsTabView
+// property var contacts: []
+// property var groups: []
+
+ tabPosition: Qt.TopEdge
+ x:mm
+ y:mm
+ width: root.width-5*mm
+ height: root.height-10*mm
+ currentIndex: 0
+ signal friendsSignal(var username)
+ signal contactsSignal(var username)
+ signal groupsSignal(var username)
+ onCurrentIndexChanged:{
+ if (currentIndex==0){friendsSignal(root.login.username)}
+ else if (currentIndex==1){contactsSignal(root.login.username)}
+ else if (currentIndex==2){groupsSignal(root.login.username)}
+ }
+
+ Tab{
+ title: qsTr("Friends")
+ Rectangle{
+ id: friendsGridTab
+ function showFriends(username){
+ try {friendsModel.clear()} catch(e){print(e)};
+ Helperjs.readData(db,"contacts",username,function(friends){
+ for (var i=0;iFriendiqa v0.001
Licensed under GPL 3
"+
+ "Sourcecode: https://github.com/LubuWest/Friendica
"+
+ "C++ code by Fabio
"+
+ "QML and Javascript code by Marco"
+ onLinkActivated:{
+ Qt.openUrlExternally(link)}
+ }
+ Button{
+ text:qsTr("Close")
+ onClicked:{configStack.pop()}
+ anchors.top:infoBoxText.bottom
+ }
+ }
diff --git a/v0.002/Develop/source-android/qml/MessageSend.qml b/v0.002/Develop/source-android/qml/MessageSend.qml
new file mode 100644
index 0000000..bdc7875
--- /dev/null
+++ b/v0.002/Develop/source-android/qml/MessageSend.qml
@@ -0,0 +1,132 @@
+// message.qml
+// message with buttons
+import QtQuick 2.0
+import QtQml 2.2
+import QtQuick.Controls 1.3
+import QtQuick.Dialogs 1.2
+import QtQuick.LocalStorage 2.0
+//import "../qml"
+import "qrc:/js/service.js" as Service
+
+Item{
+ id:messageSend
+ property var login
+ property string parentId: ""
+ property string reply_to_user:""
+ property string attachImageURL: "";
+ property int directmessage: 0;
+ property var contacts: []
+ // title: parentId !== "" ? qsTr("Reply to "+reply_to_user) : qsTr("New post")
+
+ function statusUpdate(title,status,in_reply_to_status_id,attachImageURL) {
+ xhr.url= login.server + "/api/statuses/update.xml";
+ xhr.setLogin(login.username+":"+Qt.atob(login.password));
+ print("login: "+login.username+":"+Qt.atob(login.password));
+ xhr.clearParams();
+ xhr.setParam("source", "Friendiqa");
+ xhr.setParam("status", status);
+ if (parentId!="") {xhr.setParam("in_reply_to_status_id", parentid)};
+ if (title!=="") {xhr.setParam("title", title)};
+ if (attachImageURL!=="") {xhr.setImageFileParam("media", attachImageURL )};
+ xhr.post();
+ }
+
+ function dmUpdate(title,text,replyto,screen_name,attachImageURL) {
+ xhr.url= login.server + "/api/direct_messages/new.xml";
+ xhr.setLogin(login.username+":"+Qt.atob(login.password));
+ xhr.clearParams();
+ xhr.setParam("text", text);
+ xhr.setParam("screen_name", screen_name);
+ if (parentId!="") {xhr.setParam("replyto", replyto)};
+ if (title!=="") {xhr.setParam("title", title)};
+ if (attachImageURL!=="") {xhr.setImageFileParam("media", attachImageURL )};
+ xhr.post();
+ }
+
+ Column {
+ id:messageColumn
+ spacing: 2
+
+ TextField {
+ id: titleField
+ width: parent.width
+ placeholderText: qsTr("Title (optional)")
+ visible: messageSend.parentId === ""
+ }
+
+ TextArea {
+ id: bodyField
+ width: parent.width
+ height: 30*mm
+ wrapMode: TextEdit.Wrap
+ }
+CheckBox{
+ id:dmCheckbox
+ text:"DM"
+ enabled: false
+ checked: (directmessage==1)?true:false
+ onClicked:{
+ if(dmCheckbox.checkedState==Qt.Checked){directmessage=1}
+ else if(dmCheckbox.checkedState==Qt.Unchecked){directmessage=0}
+ }
+ }
+ Row{
+ spacing:2
+ Button {
+ id: cancelButton
+ text: qsTr("Cancel")
+ onClicked: {newsStack.pop()}
+ }
+
+ Button {
+ id: attachButton
+ text: qsTr("Attach")
+ onClicked: {imageAttachmentDialog.open()}
+ }
+ Button{
+ id:contactButton
+ text:qsTr("cc")
+ visible:(directmessage==0)
+ onClicked:{
+ var contactitems="";
+ for (var i=0;imaxnews){var lastvalidtimers= tx.executeSql('select created_at from news ORDER BY created_at DESC LIMIT ' +(newscount-maxnews));
+ var lastvalidtime=lastvalidtimers.rows.item(-1);
+ var deleters = tx.executeSql('DELETE from news WHERE created_at<'+lastvalidtime)}
+ });
+ Qt.quit()
+ }
+
+ StackView{
+ id: newsStack
+ anchors.fill:parent
+ focus: true
+ Keys.onReleased: if (event.key === Qt.Key_Back && stackView.depth > 1) {
+ stackView.pop(); event.accepted = true;
+ }
+ initialItem:Rectangle {
+ y:1
+ color: "white"
+ width:root.width-2*mm
+ height:root.height-8*mm
+
+ Button {
+ id: newMessageButton
+ text: qsTr("+")
+ anchors.top: parent.top
+ anchors.right: parent.right
+ onClicked: {
+ Helperjs.readField("screen_name",root.db,"contacts",root.login.username,function(friends){
+ newsStack.push({item:"qrc:/qml/MessageSend.qml",properties:{"contacts": friends,"login":root.login}})
+ },"isFriend",1);
+ }
+ }
+ Button {
+ id: quitButton
+ text: qsTr("Quit")
+ anchors.top: parent.top
+ anchors.right: newMessageButton.left
+ onClicked: {cleanNews(root.db)}
+ }
+
+ Component { id:footerComponent
+ Rectangle{
+ border.color: "#EEEEEE"
+ border.width: 1
+ width:parent.width
+ height:6*mm
+ Text{
+ font.pixelSize: 1.5*mm
+ anchors.centerIn: parent
+ text:qsTr("More")
+ }
+ MouseArea{anchors.fill:parent
+ onClicked:{
+ var currentTime= new Date();
+ if(newstabStatus=="news"){
+ var lastnews_id=newsModel.get(newsModel.count-1).newsitemobject.created_at;
+ Newsjs.newsfromdb(root.db,root.login.username, function(news){
+ var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true};
+ newsWorker.sendMessage(msg);
+ },false,lastnews_id)}
+ else if(newstabStatus=="friendmessage"){
+ Newsjs.newsfromdb(root.db,root.login.username, function(news){
+ var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true};
+ newsWorker.sendMessage(msg);
+ },newsModel.get(newsModel.count-1).newsitemobject.uid,newsModel.get(newsModel.count-1).newsitemobject.created_at)}
+ }}
+ }
+ }
+ ListView {
+ id: newsView
+ anchors.fill: parent
+ anchors.topMargin: 8*root.mm
+ anchors.leftMargin: 3*root.mm; anchors.rightMargin: root.mm
+ anchors.bottomMargin: 1*root.mm
+ clip: true
+ spacing: 0
+ footer: footerComponent
+ model: newsModel
+ delegate: Newsitem{}
+ }
+
+ ListModel{id: newsModel}
+
+ WorkerScript {
+ id: newsWorker
+ source: "qrc:/js/newsworker.js"
+ }
+
+ Button {
+ id: update
+ anchors.top: parent.top
+ anchors.right: quitButton.left
+ text: "Update"
+ onClicked: { //try{newsModel.clear()} catch(e){}
+ newsBusy.running=true;
+ root.contactLoadType="news";
+ Newsjs.getFriendsTimeline(login,db,contactlist,newstab,function(ns,nc){
+ root.news=ns;root.newContacts=nc;root.currentContact=0;
+ if (ns.length==0){
+ Newsjs.getDirectMessage(root.login,root.db,root,function(dbnews){showNews(dbnews)});
+ newsBusy.running=false}
+ })}
+ }
+ BusyIndicator{
+ id: newsBusy
+ anchors.centerIn:update
+ //anchors.right: update.left
+ //anchors.top:parent.top
+ width:7*mm
+ height: 7*mm
+ }
+ Component.onCompleted: {
+ root.messageSignal.connect(onFriendsMessages);
+ root.directmessageSignal.connect(onDirectMessage);
+ root.newsSignal.connect(showNews);
+ try{newsModel.clear()} catch(e){}
+ Newsjs.newsfromdb(root.db,root.login.username, function(dbnews){
+ showNews(dbnews)
+ })
+ }
+}
+}
+}
diff --git a/v0.002/Develop/source-android/qml/Newsitem.qml b/v0.002/Develop/source-android/qml/Newsitem.qml
new file mode 100644
index 0000000..726d8ba
--- /dev/null
+++ b/v0.002/Develop/source-android/qml/Newsitem.qml
@@ -0,0 +1,286 @@
+import QtQuick 2.0
+import QtQuick.LocalStorage 2.0
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "qrc:/js/news.js" as Newsjs
+import "qrc:/js/layout.js" as Layoutjs
+
+
+Item {
+ id: newsitem
+ width: newsView.width
+ height:Math.max((itemMessage.height+createdAtLabel.height+friendicaActivities.height+4*mm),profileImage.height+user_name.height+mm)
+
+ // property var friendica_activities
+ property string conversation_id: ""
+ property string attending: ""
+ onAttendingChanged: {attendLabel.visible=true;
+ attendLabel.text= qsTr("attending: ")+ qsTr(attending)}
+ signal replyto(string parent_id)
+
+ Rectangle{width:newsitem.width; height: 1; anchors.bottom: newsitem.bottom; color:"light grey"}
+
+ Rectangle{
+ width:newsitem.width
+ height:newsitem.height-1
+ color: (newsitemobject.directmessage)?"#ffe6e6" : "white"
+
+ Column {
+ id: authorcolumn
+ width: 8*mm
+
+ Image {
+ id:profileImage
+ source: "file://"+newsitemobject.user.profile_image
+ x:1
+ width: 7*mm
+ height: 7*mm
+ MouseArea{
+ anchors.fill: parent
+ onPressAndHold: { newsmenu.popup()}
+ }
+ onStatusChanged: if (profileImage.status == Image.Error) {source="qrc:/images/defaultcontact.jpg"}
+ }
+ Label {
+ id:user_name
+ color: "grey"
+ //height:3.5*mm
+ width:parent.width
+ font.pixelSize: 1.5*mm
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ text: Qt.atob(newsitemobject.user.name)
+ }
+ }
+ Column {
+ id:newscolumn
+ anchors.left: authorcolumn.right
+
+ Row{
+ spacing: 5*mm
+ Label {
+ color: "grey"
+ text: if (newsitemobject.messagetype==0){qsTr("Source: ")+newsitemobject.source
+ } else if (newsitemobject.messagetype==1){ qsTr("Direct Message")} else {" Notification"}
+ font.pixelSize: 1.5*mm
+ }
+ Label {
+ id:createdAtLabel
+ color: "grey"
+ height:3.5*mm
+ font.pixelSize: 1.5*mm
+ horizontalAlignment: Label.AlignRight
+ text: dateDiff
+ }
+ CheckBox {
+ id:favoritedCheckbox
+ style: CheckBoxStyle {
+ background: Rectangle {
+ implicitWidth: 6*mm
+ implicitHeight:2*mm
+ color:"white"
+ }
+ indicator:
+ Rectangle{x:3*mm
+ width: 3*mm
+ implicitHeight:2*mm
+ Text{
+ anchors.centerIn: parent
+ color:control.checked?"black":"grey"
+ text:"\u2605"
+ }}
+ }
+ checked:(newsitemobject.favorited>0)
+ Text{
+ anchors.left: parent.right
+ color: "grey"
+ font.pixelSize: 1.5*mm
+ text: (newsitemobject.favorited>0)? newsitemobject.favorited+qsTr(" Favorites"):""
+ }
+ onClicked:{
+ if(favoritedCheckbox.checkedState==Qt.Checked)
+ {Newsjs.favorite(login,true,newsitemobject.status_id,root)}
+ else
+ if(favoritedCheckbox.checkedState==Qt.Unchecked)
+ {Newsjs.favorite(login,false,newsitemobject.status_id,root)}
+ }
+ }
+
+ }
+
+ Text {
+ color: "#404040"
+ linkColor: "light green"
+ id: itemMessage
+ textFormat: Text.RichText
+ text: Qt.atob(newsitemobject.statusnet_html)
+ width: newsitem.width-8*mm-2
+ height: implicitHeight
+ wrapMode: Text.Wrap
+ onLinkActivated:{ print("link "+link);
+ Qt.openUrlExternally(link)}
+ }
+ Row{id: friendicaActivities
+ spacing:mm
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.likeText
+ }
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.dislikeText
+ }
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.attendyesText
+ }
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.attendnoText
+ }
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.attendmaybeText
+ }
+ }
+Row {
+ CheckBox{id:likeCheckbox
+ height:3*mm
+ width:8*mm
+ checked:(newsitemobject.liked==1)?true:false
+ style: CheckBoxStyle {
+ background: Rectangle {
+ implicitWidth: 7*mm
+ implicitHeight: 3*mm
+ color:"white"
+ }
+ indicator:
+ Rectangle{
+ implicitWidth: 3*mm
+ implicitHeight:3*mm
+ color:control.checked?"yellow":"white"
+ x: 5*mm
+ Text{
+ font.pixelSize: 1.5*mm
+ color:"grey"
+ text:":-)"
+ }}
+ }
+ onClicked: {
+ if(likeCheckbox.checked==true){Newsjs.like(root.login,root.db,1,"like",newsitemobject.status_id,root);dislikeCheckbox.checked=false}
+ else{Newsjs.like(root.login,root.db,0,"like",newsitemobject.status_id,root)}}
+ }
+ CheckBox{id: dislikeCheckbox
+ height:3*mm
+ width:8*mm
+ checked: (newsitemobject.disliked==1)?true:false
+ style: CheckBoxStyle {
+ background: Rectangle {
+ implicitWidth: 7*mm
+ implicitHeight:3*mm
+ color:"white"
+ }
+ indicator:
+ Rectangle{
+ implicitWidth: 3*mm
+ implicitHeight:3*mm
+ color:control.checked?"yellow":"white"
+ x:5*mm
+ Text{
+ font.pixelSize: 1.5*mm
+ color:"grey"
+ text:":-("
+ }}
+ }
+ onClicked: {
+ if (dislikeCheckbox.checked==true){Newsjs.like(root.login,root.db,1,"dislike",newsitemobject.status_id);likeCheckbox.checked=false}
+ else {Newsjs.like(root.login,root.db,0,"dislike",newsitemobject.status_id,root)}}
+ }
+ Label {
+ id:replytoLabel
+ color: "grey"
+ height:3.5*mm
+ font.pixelSize: 1.5*mm
+ horizontalAlignment: Label.AlignRight
+ text: try {qsTr("In reply to ")+newsitemobject.reply_user.screen_name
+ }catch(e){" "}
+ }
+ Label {
+ id:attendLabel
+ visible: false
+ color: "grey"
+ height:3.5*mm
+ font.pixelSize: 1.5*mm
+ horizontalAlignment: Label.AlignRight
+ text: qsTr("attending: ")+ qsTr(attending)
+ }
+ }
+ }
+
+ Menu {
+ id:newsmenu
+ MenuItem {
+ text: qsTr("Reply")
+ onTriggered: {
+ newsStack.push({item:"qrc:/qml/MessageSend.qml",properties:{"reply_to_user": newsitemobject.user.screen_name,"parentId":newsitemobject.status_id}});
+ }
+ }
+MenuItem {
+ text: qsTr("DM")
+ onTriggered: {
+ root.directmessageSignal(newsitemobject.user.screen_name);
+ }
+ }
+ MenuItem {
+ text: qsTr("Repost")
+ onTriggered: {
+ Newsjs.retweetNews(root.login,db,newsitemobject.status_id,root,function(reply){
+ print(reply);
+ })
+ }
+ }
+ MenuItem {
+ text: qsTr("Conversation")
+ onTriggered: {
+ Newsjs.requestConversation(root.login,db,newsitemobject.status_id,root,function(){
+ var currentTime= new Date();
+ Newsjs.conversationfromdb(db,root.login.username,newsitemobject.statusnet_conversation_id, function(newsarray){
+ newsModel.clear();
+ var msg = {'currentTime': currentTime, 'model': newsModel,'news':newsarray,'latestmessage':0};
+ newsWorker.sendMessage(msg);
+ });
+ }
+ )}
+ }
+
+Menu{
+ title: qsTr("Attending")
+
+ MenuItem{text:qsTr("yes")
+ onTriggered: {Newsjs.attend(root.login,db,"yes",newsitemobject.status_id,root,function(){
+ newsitem.attending="yes";
+ attendLabel.visible=true})}
+ }
+
+ MenuItem{text:qsTr("maybe")
+ onTriggered: {Newsjs.attend(root.login,db,"maybe",newsitemobject.status_id,root,function(){
+ newsitem.attending="maybe"})}
+ }
+
+ MenuItem{text:qsTr("no")
+ onTriggered: {Newsjs.attend(root.login,db,"no",newsitemobject.status_id,root,function(){
+ newsitem.attending="no"})}
+ }
+ }
+
+ MenuItem {
+ text: qsTr("Delete")
+ onTriggered: {
+ Newsjs.deleteNews(root.login,db,newsitemobject.status_id,root,function(reply){
+ print(JSON.stringify(reply));
+ newsModel.remove(index);
+ })
+ }
+ }
+}
+}}
+
diff --git a/v0.002/Develop/source-android/qml/PhotoComponent.qml b/v0.002/Develop/source-android/qml/PhotoComponent.qml
new file mode 100644
index 0000000..7da5f8b
--- /dev/null
+++ b/v0.002/Develop/source-android/qml/PhotoComponent.qml
@@ -0,0 +1,100 @@
+import QtQuick 2.0
+import QtQuick.LocalStorage 2.0
+import QtQuick.Controls 1.2
+
+Package {
+ Item { id: stackItem; Package.name: 'stack'; z: stackItem.PathView.z;width:16.5*mm;height:16.5*mm}
+ Item { id: listItem; Package.name: 'list'; width: root.width-1*mm; height: root.height-8*mm; }
+ Item { id: gridItem; Package.name: 'grid';}
+
+ Item {
+ id: photoWrapper
+ width: 16.5*mm; height: 16.5*mm
+ z: stackItem.PathView.z
+ property string hqphotolink: photoLink
+
+ Rectangle {
+ id: placeHolder
+ color: 'lightblue'; antialiasing: true
+ anchors.fill:parent
+ //width: parent.width; height: parent.height;
+ }
+
+ BusyIndicator { anchors.centerIn: parent; running: realImage.status != Image.Ready }
+ Image {
+ id: realImage;
+ // property string hqphotolink: photoLink
+ width: photoWrapper.width; height: photoWrapper.height
+ antialiasing: true;
+ asynchronous: true
+ cache: false
+ fillMode: Image.PreserveAspectFit;
+ source: imageLocation
+ // onStatusChanged: if (realImage.status == Image.Ready) print(realImage.paintedHeight+"x"+realImage.paintedWidth)
+ }
+ Rectangle{
+ id:phototextRectangle
+ color:"black"
+ z:3
+ opacity: 0.5
+ width:phototext.contentWidth
+ height: phototext.contentHeight
+ anchors.bottom: photoWrapper.bottom
+ }
+ Text {
+ id:phototext
+ z:4
+ text: photoDescription.trim()
+ width:15*mm
+ anchors.bottom: photoWrapper.bottom
+ color: "white"
+ font.pixelSize: 2*mm
+ wrapMode:Text.Wrap
+ }
+ MouseArea {
+ width: realImage.paintedWidth; height: realImage.paintedHeight; anchors.centerIn: realImage
+ onClicked: {
+ if (albumWrapper.state == 'inGrid') {
+ gridItem.GridView.view.currentIndex = index;
+ //print("photoLink"+realImage.photoLink)
+ albumWrapper.state = 'fullscreen'
+ } else {
+ gridItem.GridView.view.currentIndex = index;
+ albumWrapper.state = 'inGrid'
+ }
+ }
+ }
+
+ states: [
+ State {
+ name: 'stacked'; when: albumWrapper.state == ''
+ ParentChange { target: photoWrapper; parent: stackItem; }//x: 1*mm; y: 1*mm }
+ PropertyChanges { target: photoWrapper; opacity: stackItem.PathView.onPath ? 1.0 : 0.0 }
+ PropertyChanges { target: phototext; opacity: 0.0 }
+ PropertyChanges { target: phototextRectangle; opacity: 0.0 }
+ },
+ State {
+ name: 'inGrid'; when: albumWrapper.state == 'inGrid'
+ ParentChange { target: photoWrapper; parent: gridItem; x: 1*mm; y: 1*mm;}
+ PropertyChanges { target: phototext; opacity: 1.0 }
+ PropertyChanges { target: phototextRectangle; opacity: 0.5 }
+ PropertyChanges { target: placeHolder; opacity: 1.0 }
+ },
+ State {
+ name: 'fullscreen'; when: albumWrapper.state == 'fullscreen'
+ ParentChange {
+ target: photoWrapper; parent: listItem; x: 1; y: 1;
+ width: root.width-mm; height: root.heigh-8*mm
+ }
+ PropertyChanges { target: placeHolder; opacity: 0.0 }
+ PropertyChanges { target: realImage; source: photoWrapper.hqphotolink}
+ PropertyChanges { target: phototext; anchors.bottom: realImage.bottom}
+ PropertyChanges { target: phototext; width:realImage.width }
+ PropertyChanges { target: phototextRectangle; anchors.bottom: realImage.bottom }
+ PropertyChanges { target: realImage; width: Math.min(listItem.width,sourceSize.width);height: Math.min(listItem.height,sourceSize.height) }
+ }
+ ]
+
+ }
+}
+
diff --git a/v0.002/Develop/source-android/qml/PhotoPlaceholder.qml b/v0.002/Develop/source-android/qml/PhotoPlaceholder.qml
new file mode 100644
index 0000000..0719059
--- /dev/null
+++ b/v0.002/Develop/source-android/qml/PhotoPlaceholder.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+import "qrc:/js/helper.js" as Helperjs
+
+Image {
+id:photoPlaceholder
+property string imageName:"x.jpg"
+property string downloadtype:""
+fillMode: Image.PreserveAspectFit
+onStatusChanged: {
+ if (photoPlaceholder.status == Image.Ready) {
+ print("Source: "+source+"Photo width"+width+" height"+height+" Ratio "+ fillMode);
+ var saveprocess=photoPlaceholder.grabToImage(function(result){
+ var saveresult=result.saveToFile(imageName.toString());
+ if (saveresult){
+ if ((downloadtype=="picture")&&(newImages.length>0)){
+ photoPlaceholder.destroy();
+ currentImageNo=currentImageNo+1
+ }
+ else if ((downloadtype=="contact")&&(root.newContacts.length>0))
+ {
+ photoPlaceholder.destroy(100);
+ root.currentContact=root.currentContact+1
+ }
+ }});
+}}}
+
diff --git a/v0.002/Develop/source-android/qml/PhotoTab.qml b/v0.002/Develop/source-android/qml/PhotoTab.qml
new file mode 100644
index 0000000..e535c42
--- /dev/null
+++ b/v0.002/Develop/source-android/qml/PhotoTab.qml
@@ -0,0 +1,140 @@
+import QtQuick 2.0
+import QtQuick.Dialogs 1.2
+import QtQuick.Controls 1.4
+import QtQml.Models 2.1
+import "qrc:/js/service.js" as Service
+import "qrc:/js/helper.js" as Helperjs
+import "qrc:/qml"
+
+Rectangle {
+ id:fotorectangle
+ y:1
+ width:root.width-mm
+ height:root.height-5*mm
+ color: '#fff'
+ property var newImages:[]
+ property int currentImageNo: 0
+//onLoginChanged:{var msg = {'model': photogroupModel,'albums':[],'firstalbum':0,'foreignPicture':false};
+// photoWorker.sendMessage(msg);
+//}
+ onNewImagesChanged:{
+ if(newImages.length>0){
+ Service.dataRequest(root.login,newImages[currentImageNo].id,root.db,fotorectangle);
+ newImagesProgress.visible=true //download first image
+ }}
+ onCurrentImageNoChanged:{
+ if(currentImageNo0){// download first contact image and update db
+ print("newcontact"+JSON.stringify(newContacts[0]));
+ updateContactInDB(login,db,newContacts[currentContact].isFriend,newContacts[currentContact])}
+ }
+ onCurrentContactChanged:{// download next contact image after photoplaceholder is finished saving and update db
+ print("Current contact"+JSON.stringify(newContacts[currentContact]));
+ if(currentContact
+
+ js/layout.js
+ js/photoworker.js
+ js/service.js
+ qml/FriendComponent.qml
+ qml/MessageSend.qml
+ qml/Newsitem.qml
+ qml/PhotoComponent.qml
+ qml/PhotogroupComponent.qml
+ qml/PhotoPlaceholder.qml
+ qml/friendiqa.qml
+ qml/PhotoTab.qml
+ qml/ConfigTab.qml
+ qml/FriendsTab.qml
+ qml/NewsTab.qml
+ js/news.js
+ js/newsworker.js
+ js/helper.js
+ images/defaultcontact.jpg
+ qml/InfoBox.qml
+ qml/GroupComponent.qml
+
+
diff --git a/v0.002/Develop/source-linux/common/filesystem.cpp b/v0.002/Develop/source-linux/common/filesystem.cpp
new file mode 100644
index 0000000..b828c8d
--- /dev/null
+++ b/v0.002/Develop/source-linux/common/filesystem.cpp
@@ -0,0 +1,49 @@
+#include "filesystem.h"
+
+FILESYSTEM *FILESYSTEM::instance()
+{
+ static FILESYSTEM filesystem;
+ return &filesystem;
+}
+
+FILESYSTEM::FILESYSTEM(QObject *parent) : QObject(parent){}
+
+void FILESYSTEM::setDirectory(QString Directory)
+{
+ if (Directory!=m_Directory) {
+ m_Directory = Directory;
+ emit directoryChanged();
+ }
+}
+
+QString FILESYSTEM::Directory() const
+{
+ return m_Directory;
+}
+
+void FILESYSTEM::makeDir(QString name)
+{
+ QDir dir(m_Directory);
+ if (dir.mkdir(name)){
+ emit success(name);
+ }
+ else {emit error(name,1);}
+}
+
+void FILESYSTEM::rmDir(QString name)
+{
+ QDir dir(m_Directory);
+ if (dir.rmdir(name)){
+ emit success(name);
+ }
+ else {emit error(name,1);}
+}
+
+void FILESYSTEM::rmFile(QString name)
+{
+ QDir dir(m_Directory);
+ if(dir.remove(name)){
+ emit success(name);
+ }
+ else {emit error(name,1);}
+}
diff --git a/v0.002/Develop/source-linux/common/filesystem.h b/v0.002/Develop/source-linux/common/filesystem.h
new file mode 100644
index 0000000..f0aebb3
--- /dev/null
+++ b/v0.002/Develop/source-linux/common/filesystem.h
@@ -0,0 +1,49 @@
+#ifndef FILESYSTEM_H
+#define FILESYSTEM_H
+
+#include
+#include
+
+class FILESYSTEM : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString Directory READ Directory WRITE setDirectory NOTIFY directoryChanged)
+
+public:
+ static FILESYSTEM *instance();
+ explicit FILESYSTEM(QObject *parent = 0);
+ void setDirectory(QString Directory);
+ QString Directory() const;
+
+signals:
+ void directoryChanged();
+ void success(QString data);
+ void error(QString data, int code);
+
+public slots:
+ //void setDirectory(QString Directory);
+ void makeDir(QString name);
+ void rmDir(QString name);
+ void rmFile(QString name);
+
+private slots:
+ //void onReplyError(QNetworkReply::NetworkError code);
+ // void onReplySuccess();
+ //void onReadyRead();
+ // void onSSLError(const QList &errors);
+
+private:
+ //QByteArray buffer;
+ QString m_Directory;
+ //QString m_login;
+ //QHash params;
+ //QHash files;
+
+ //QNetworkAccessManager manager;
+ //QNetworkRequest request;
+ //QNetworkReply *reply;
+
+ //QString bufferToString();
+};
+
+#endif // FILSYSTEM_H
diff --git a/v0.002/Develop/source-linux/common/friendiqa.cpp b/v0.002/Develop/source-linux/common/friendiqa.cpp
new file mode 100644
index 0000000..670f21a
--- /dev/null
+++ b/v0.002/Develop/source-linux/common/friendiqa.cpp
@@ -0,0 +1,22 @@
+#include
+#include
+#include
+#include "xhr.h"
+#include "filesystem.h"
+
+int main(int argc, char *argv[]) {
+ QApplication app(argc, argv);
+ QQuickView view;
+
+
+ XHR* xhr = XHR::instance();
+ view.rootContext()->setContextProperty("xhr", xhr);
+ FILESYSTEM* filesystem = FILESYSTEM::instance();
+ view.rootContext()->setContextProperty("filesystem", filesystem);
+ view.setSource(QUrl("qrc:/qml/friendiqa.qml"));
+ view.show();
+ view.connect(view.rootContext()->engine(), SIGNAL(quit()), &app, SLOT(quit()));
+ return app.exec();
+
+}
+
diff --git a/v0.002/Develop/source-linux/common/uploadableimage.cpp b/v0.002/Develop/source-linux/common/uploadableimage.cpp
new file mode 100644
index 0000000..81bea1a
--- /dev/null
+++ b/v0.002/Develop/source-linux/common/uploadableimage.cpp
@@ -0,0 +1,90 @@
+#include "uploadableimage.h"
+
+
+#include
+#include
+#include
+#include
+
+void UploadableImage::setSource(const QString &a) {
+ if (a != m_source) {
+ m_source = a;
+ //m_base64 = "";
+ m_mimetype = "";
+ m_filename = "";
+
+ qDebug() << "UploadableImage::setSource : " << m_source;
+
+ if (m_source=="") {
+ emit sourceChanged();
+ //emit base64Changed();
+ emit mimetypeChanged();
+ emit filenameChanged();
+ return;
+ }
+
+ QImage fullimage = QImage(QUrl(m_source).toLocalFile());
+ if (fullimage.width() > 800 || fullimage.height() > 800) {
+ if (fullimage.width() > fullimage.height()) {
+ m_image = fullimage.scaledToWidth(800);
+ } else {
+ m_image = fullimage.scaledToHeight(800);
+ }
+ } else {
+ m_image = fullimage;
+ }
+ qDebug() << "UploadableImage::setSource : " << m_image.width() << "x" << m_image.height();
+ emit sourceChanged();
+
+ QFileInfo fi(m_source);
+
+ m_filename = fi.fileName();
+ emit filenameChanged();
+
+ QString filetype = fi.suffix().toUpper();
+ if (filetype!="PNG" && filetype!="JPG") {
+ filetype = "JPG";
+ }
+ qDebug() << "UploadableImage::setSource : " << "Saving as " << filetype;
+
+ m_mimetype = "image/"+filetype.toLower();
+ emit mimetypeChanged();
+
+ /*
+ QByteArray byteArray;
+ QBuffer buffer(&byteArray);
+ m_image.save(&buffer, filetype.toLatin1().constData());
+ QString b64 = QString::fromLatin1(byteArray.toBase64().data());
+
+ for(int k=0; k
+#include
+#include
+
+class UploadableImage : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
+ //Q_PROPERTY(QString base64 READ base64 NOTIFY base64Changed)
+ Q_PROPERTY(QString filename READ filename NOTIFY filenameChanged)
+ Q_PROPERTY(QString mimetype READ mimetype NOTIFY mimetypeChanged)
+ Q_PROPERTY(QByteArray bytes READ bytes)
+
+public:
+ void setSource(const QString &a);
+ QString source() const;
+
+ //QString base64() const;
+ QString filename() const;
+ QString mimetype() const;
+
+ QByteArray bytes();
+signals:
+ void sourceChanged();
+ //void base64Changed();
+ void filenameChanged();
+ void mimetypeChanged();
+
+private:
+ QString m_source;
+ QImage m_image;
+ //QString m_base64;
+ QString m_filename;
+ QString m_mimetype;
+};
+
+#endif // UPLOADABLEIMAGE_H
diff --git a/v0.002/Develop/source-linux/common/xhr.cpp b/v0.002/Develop/source-linux/common/xhr.cpp
new file mode 100644
index 0000000..6877135
--- /dev/null
+++ b/v0.002/Develop/source-linux/common/xhr.cpp
@@ -0,0 +1,168 @@
+#include "xhr.h"
+
+#include
+#include
+#include
+
+#include "uploadableimage.h"
+
+XHR *XHR::instance()
+{
+ static XHR xhr;
+ return &xhr;
+}
+
+XHR::XHR(QObject *parent) : QObject(parent)
+{
+ request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
+}
+
+void XHR::setUrl(QString url)
+{
+ if (url!=m_url) {
+ m_url = url;
+ emit urlChanged();
+ }
+}
+
+void XHR::setLogin(QString login)
+{
+ if (login!=m_login) {
+ m_login = login;
+ emit loginChanged();
+ }
+}
+QString XHR::url() const
+{
+ return m_url;
+}
+
+QString XHR::login() const
+{
+ return m_login;
+}
+
+void XHR::setParam(QString name, QString value)
+{
+ params.insert(name, value);
+}
+
+void XHR::setImageFileParam(QString name, QString url)
+{
+ files.insert(name, url);
+}
+
+void XHR::clearParams()
+{
+ files.clear();
+ params.clear();
+}
+
+void XHR::get()
+{
+ QUrlQuery query;
+
+ QHashIterator i(params);
+ while(i.hasNext()) {
+ i.next();
+ query.addQueryItem(i.key(), i.value());
+ }
+
+ QUrl requrl(m_url);
+ requrl.setQuery(query);
+
+ QByteArray loginData = m_login.toLocal8Bit().toBase64();
+ QString headerData = "Basic " + loginData;
+ request.setRawHeader("Authorization", headerData.toLocal8Bit());
+
+
+ request.setUrl(requrl);
+ reply = manager.get(request);
+
+ connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess);
+ connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError)));
+ connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead);
+
+}
+
+void XHR::post()
+{
+ qDebug() << "start post to " << m_url;
+ QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+
+ QHashIterator iparams(params);
+ while(iparams.hasNext()) {
+ iparams.next();
+ qDebug() << "\t add param " << iparams.key() << " : " << iparams.value();
+ QHttpPart textPart;
+ textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + iparams.key() + "\""));
+
+
+ textPart.setBody(iparams.value().toUtf8());
+ multiPart->append(textPart);
+ }
+
+ UploadableImage uimg;
+ QHashIterator ifiles(files);
+ while(ifiles.hasNext()) {
+ ifiles.next();
+
+ uimg.setSource(ifiles.value());
+ qDebug() << "\t image: " << uimg.mimetype() << ", " << ifiles.key();
+
+ QHttpPart imagePart;
+ imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(uimg.mimetype()));
+ imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + ifiles.key() + "\"; filename=\""+uimg.filename()+"\""));
+ imagePart.setBody(uimg.bytes());
+ multiPart->append(imagePart);
+ }
+
+ QByteArray loginData = m_login.toLocal8Bit().toBase64();
+ QString headerData = "Basic " + loginData;
+ request.setRawHeader(QByteArray("Authorization"), headerData.toLocal8Bit());
+
+ request.setUrl(m_url);
+ reply = manager.post(request, multiPart);
+ qDebug() << "\t request sent";
+ connect(reply, &QNetworkReply::finished, this, &XHR::onReplySuccess);
+ connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onReplyError(QNetworkReply::NetworkError)));
+ connect(reply, &QNetworkReply::readyRead, this, &XHR::onReadyRead);
+ connect(reply, &QNetworkReply::sslErrors, this, &XHR::onSSLError);
+ qDebug() << "\t reply signals connected";
+}
+
+void XHR::onReplyError(QNetworkReply::NetworkError code)
+{
+ qDebug() << code;
+ emit this->error( bufferToString(), (int) code);
+ reply->deleteLater();
+}
+
+void XHR::onReplySuccess()
+{
+ qDebug() << "!";
+ emit this->success( bufferToString() );
+ reply->deleteLater();
+}
+
+void XHR::onReadyRead()
+{
+ qDebug() << ".";
+ buffer += reply->readAll();
+}
+
+void XHR::onSSLError(const QList &errors)
+{
+ qDebug() << "XHR::onSSLError :" ;
+ QListIterator ierrs(errors);
+ while(ierrs.hasNext()) {
+ qDebug() << "\t" << ierrs.next().errorString();
+ }
+}
+
+QString XHR::bufferToString()
+{
+ return QTextCodec::codecForName("utf-8")->toUnicode(buffer);
+}
+
+
diff --git a/v0.002/Develop/source-linux/common/xhr.h b/v0.002/Develop/source-linux/common/xhr.h
new file mode 100644
index 0000000..3d61f25
--- /dev/null
+++ b/v0.002/Develop/source-linux/common/xhr.h
@@ -0,0 +1,60 @@
+#ifndef XHR_H
+#define XHR_H
+
+#include
+#include
+#include
+#include
+
+class XHR : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
+ Q_PROPERTY(QString login READ login WRITE setLogin NOTIFY loginChanged)
+
+public:
+ static XHR *instance();
+
+ explicit XHR(QObject *parent = 0);
+
+ void setUrl(QString url);
+ // void setLogin(QString login);
+
+ QString url() const;
+ QString login() const;
+
+signals:
+ void urlChanged();
+ void loginChanged();
+ void success(QString data);
+ void error(QString data, int code);
+
+public slots:
+ void setLogin(QString login);
+ void setParam(QString name, QString value);
+ void setImageFileParam(QString name, QString url);
+ void clearParams();
+ void post();
+ void get();
+
+private slots:
+ void onReplyError(QNetworkReply::NetworkError code);
+ void onReplySuccess();
+ void onReadyRead();
+ void onSSLError(const QList &errors);
+
+private:
+ QByteArray buffer;
+ QString m_url;
+ QString m_login;
+ QHash params;
+ QHash files;
+
+ QNetworkAccessManager manager;
+ QNetworkRequest request;
+ QNetworkReply *reply;
+
+ QString bufferToString();
+};
+
+#endif // XHR_H
diff --git a/v0.002/Develop/source-linux/friendiqa.pro b/v0.002/Develop/source-linux/friendiqa.pro
new file mode 100644
index 0000000..09f9a77
--- /dev/null
+++ b/v0.002/Develop/source-linux/friendiqa.pro
@@ -0,0 +1,40 @@
+# NOTICE:
+#
+# Application name defined in TARGET has a corresponding QML filename.
+# If name defined in TARGET is changed, the following needs to be done
+# to match new name:
+# - corresponding QML filename must be changed
+# - desktop icon filename must be changed
+# - desktop filename must be changed
+# - icon definition filename in desktop file must be changed
+# - translation filenames have to be changed
+
+# The name of your application
+TARGET = friendiqa
+CONFIG += debug
+QT += qml quick gui widgets
+
+SOURCES += common/friendiqa.cpp \
+ common/uploadableimage.cpp \
+ common/xhr.cpp \
+ common/filesystem.cpp
+
+RESOURCES = application.qrc
+
+OTHER_FILES += qml/friendiqa.qml \
+ translations/*.ts \
+ qml/*.qml
+ js/*.js
+
+# German translation is enabled as an example. If you aren't
+# planning to localize your app, remember to comment out the
+# following TRANSLATIONS line. And also do not forget to
+# modify the localized app name in the the .desktop file.
+TRANSLATIONS += translations/friendiqa-de.ts
+
+HEADERS += \
+ common/uploadableimage.h \
+ common/xhr.h \
+ common/filesystem.h
+
+
diff --git a/v0.002/Develop/source-linux/images/defaultcontact.jpg b/v0.002/Develop/source-linux/images/defaultcontact.jpg
new file mode 100644
index 0000000..bb7bce2
Binary files /dev/null and b/v0.002/Develop/source-linux/images/defaultcontact.jpg differ
diff --git a/v0.002/Develop/source-linux/js/friendworker.js b/v0.002/Develop/source-linux/js/friendworker.js
new file mode 100644
index 0000000..6b6c5be
--- /dev/null
+++ b/v0.002/Develop/source-linux/js/friendworker.js
@@ -0,0 +1,12 @@
+WorkerScript.onMessage = function(msg) {
+ msg.model.clear();
+ for (var j=0;j 0 ) {
+ for (var i in list) { if (list[i][prop] === val) {
+ return i;
+ }
+ }
+ } return -1;
+}
diff --git a/v0.002/Develop/source-linux/js/layout.js b/v0.002/Develop/source-linux/js/layout.js
new file mode 100644
index 0000000..51ccfb4
--- /dev/null
+++ b/v0.002/Develop/source-linux/js/layout.js
@@ -0,0 +1,44 @@
+function showFriends(db) {
+ Service.readActiveConfig(db,function(login){
+ Service.requestFriends(login.url,login.user,login.password,displayFriends);
+ });
+}
+function displayFriends(obj){
+ for (var i=0; i= c.x)
+ f.contentX = c.x;
+ else if (f.contentX+f.width <= c.x+c.width)
+ f.contentX = c.x+c.width-f.width;
+ if (f.contentY >= c.y)
+ f.contentY = c.y;
+ else if (f.contentY+f.height <= c.y+c.height)
+ f.contentY = c.y+c.height-f.height;
+}
+
+function createObject(objectQml,qmlParameters,parentitem,callback) {
+ var component = Qt.createComponent(objectQml);
+ if (component.status === Component.Ready || component.status === Component.Error)
+ finishCreation(component,qmlParameters,parentitem,callback);
+ else
+ component.statusChanged.connect(finishCreation(qmlParameters));
+}
+
+function finishCreation(component,qmlParameters,parentitem,callback) {
+ if (component.status === Component.Ready) {
+ var createdObject = component.createObject(parentitem, qmlParameters);
+ if (createdObject === null)
+ print("Error creating image"); }
+ else if (component.status === Component.Error)
+ print("Error loading component:"+component.errorString());
+ else {print("created")}
+ callback(createdObject);
+}
+
diff --git a/v0.002/Develop/source-linux/js/news.js b/v0.002/Develop/source-linux/js/news.js
new file mode 100644
index 0000000..a7ed464
--- /dev/null
+++ b/v0.002/Develop/source-linux/js/news.js
@@ -0,0 +1,311 @@
+.pragma library
+.import QtQuick.LocalStorage 2.0 as Sql
+.import "qrc:/js/helper.js" as Helperjs
+
+function requestFriends(login,database,rootwindow,callback){
+// return array of friends
+ Helperjs.friendicaRequest(login,"/api/statuses/friends", rootwindow,function (obj){
+ var friends=JSON.parse(obj);
+ for (var i=0;i0){
+ print("Like Contact"+JSON.stringify(news[i].friendica_activities.like));
+ for (var j=0;j0){
+ print("DisLike Contact"+JSON.stringify(news[i].friendica_activities.dislike));
+ for (var k=0;j 0 ) {
+ for (var i in list) { if (list[i][prop] === val) {
+ return true;
+ }
+ }
+ } return false;
+}
+
+function cleanDate(date){
+var cleanedDate= date.slice(0,3)+", "+date.slice(8,11)+date.slice(4,7)+date.slice(25,30)+date.slice(10,25);
+return cleanedDate
+}
diff --git a/v0.002/Develop/source-linux/js/newsworker.js b/v0.002/Develop/source-linux/js/newsworker.js
new file mode 100644
index 0000000..8d5cc46
--- /dev/null
+++ b/v0.002/Develop/source-linux/js/newsworker.js
@@ -0,0 +1,48 @@
+WorkerScript.onMessage = function(msg) {
+ if(msg.appendnews!==true){ msg.model.clear()};
+ for (var j=0;j0){
+ if (msg.news[j].like.length==1){likeText= Qt.atob(msg.news[j].like[0].name)+" "+ qsTr("likes this.")}
+ else {likeText= msg.news[j].like.length+" "+ qsTr("like this.")}
+ }
+ if (msg.news[j].dislike.length>0){
+ if (msg.news[j].dislike.length==1){dislikeText= QT.atob(msg.news[j].dislike[0].name)+" "+ qsTr("doesn't like this.")}
+ else {dislikeText= msg.news[j].dislike.length+" "+ qsTr("don't like this.")}
+ }
+ if (msg.news[j].attendyes.length>0){
+ if (msg.news[j].attendyes.length==1){attendyesText= Qt.atob(msg.news[j].attendyes[0].name)+" "+ qsTr("will attend.")}
+ else {attendyesText= msg.news[j].attendyes.length+" "+ qsTr("persons will attend.")}
+ }
+ if (msg.news[j].attendno.length>0){
+ if (msg.news[j].attendno.length==1){attendnoText= Qt.atob(msg.news[j].attendno[0].name)+" "+ qsTr("will not attend.")}
+ else {attendnoText= msg.news[j].attendno.length+" "+ qsTr("persons will not attend.")}
+ }
+ if (msg.news[j].attendmaybe.length>0){
+ if (msg.news[j].attendmaybe.length==1){attendmaybeText= Qt.atob(msg.news[j].attendmaybe[0].name)+" "+ qsTr("may attend.")}
+ else {attendmaybeText= msg.news[j].attendmaybe.length+" "+ qsTr("persons may attend.")}
+ }
+ }
+ var friendica_activities={likeText:likeText,dislikeText:dislikeText,attendyesText:attendyesText,attendnoText:attendnoText,attendmaybeText:attendmaybeText}
+ var seconds=(msg.currentTime-newsitemobject.created_at)/1000;
+ var timestring="";
+ if (seconds<60) {timestring=seconds+" "+qsTr("seconds") +" "+qsTr("ago");}
+ else if (seconds<90){timestring=Math.round(seconds/60)+" "+qsTr("minute") +" "+qsTr("ago");}
+ else if (seconds<3600){timestring=Math.round(seconds/60)+" "+qsTr("minutes") +" "+qsTr("ago");}
+ else if (seconds<5400){timestring=Math.round(seconds/3600)+" "+qsTr("hour") +" "+qsTr("ago");}
+ else if (seconds<86400){timestring=Math.round(seconds/3600)+" "+qsTr("hours") +" "+qsTr("ago");}
+ else if (seconds<129600){timestring=Math.round(seconds/86400)+" "+qsTr("day") +" "+qsTr("ago");}
+ else if (seconds<3888000){timestring=Math.round(seconds/86400)+" "+qsTr("days") +" "+qsTr("ago");}
+ else if (seconds<5832000){timestring=Math.round(seconds/3888000)+" "+qsTr("month") +" "+qsTr("ago");}
+ else if (seconds<69984000){timestring=Math.round(seconds/3888000)+" "+qsTr("months") +" "+qsTr("ago");}
+ else {timestring=Math.round(seconds/69984000)+" "+qsTr("years") +" "+qsTr("ago");}
+ var data=({"newsitemobject": newsitemobject,"dateDiff":timestring,"friendica_activities":friendica_activities})}
+ // print("News:"+j+msg.news.length+JSON.stringify(data));
+ msg.model.append(data);}
+ if (j==msg.news.length){
+ msg.model.sync()
+ };
+}
diff --git a/v0.002/Develop/source-linux/js/photoworker.js b/v0.002/Develop/source-linux/js/photoworker.js
new file mode 100644
index 0000000..36646c7
--- /dev/null
+++ b/v0.002/Develop/source-linux/js/photoworker.js
@@ -0,0 +1,15 @@
+WorkerScript.onMessage = function(msg) {
+ if (msg.firstalbum==0){msg.model.clear();}
+ var limit=0; if (msg.albums.length-msg.firstalbum<20){limit=msg.albums.length} else{limit=msg.firstalbum+20}
+ for (var j=msg.firstalbum;j0){
+ for(var i=0;i< AllStoredImages.length;i++){
+ var position=Helperjs.inArray(obj,"resourceID",AllStoredImages[i]);
+ if (position>-1){obj.splice(position,1)}
+ //obj.splice(obj.indexOf(AllStoredImages[i]),1);// list of objects instead of list!!!
+ }
+ }}
+ );
+ callback(obj);
+ })}
+
+function dataRequest(login,photoID,database,rootwindow) {
+ // check if image exist and call download function
+ Helperjs.friendicaRequest(login,"/api/friendica/photo?photo_id="+photoID, rootwindow, function (obj){
+ try{ if(obj==""){currentImageNo=currentImageNo+1}else{
+ var image = JSON.parse(obj);
+ print('storeData() for ' + JSON.stringify(obj));
+ try{sprite.destroy();}catch(e){}
+ if (obj["link"]["0"]){var source=obj["link"]["0"]} else {var source=obj["link"]["4"]}//source for profile picture or original size
+ print("Source"+obj["link"]["0"]+source)
+ obj["source"]=source;
+ var filename=obj.filename;
+ if (filename==""){// check if file as any filename
+ if (obj.type=="image/jpeg") {obj["filename"]=obj["id"]+".jpg"}
+ else if (obj.type=="image/png") {obj["filename"]=obj["id"]+".png"}
+ }
+ // check if text name has valid image format ending, otherwise append appropriate ending
+ if(["jpg","png"].indexOf(obj["filename"].substring(obj["filename"].lastIndexOf(".")+1,obj["filename"].length))==-1){
+ print("falscheEndung: "+obj["filename"].substring(obj["filename"].lastIndexOf(".")+1,obj["filename"].length));
+ { if (obj.type=="image/jpeg") {obj["filename"]=obj["filename"]+".jpg"}
+ else if (obj.type=="image/png") {obj["filename"]=obj["filename"]+".png"}
+ }
+ print("obj.Filename: "+obj["filename"]+" filename: "+filename)
+ }
+ saveImage(obj,login.imagestore,function(obj,sprite){
+ //sprite.destroy(500);
+ var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
+ db.transaction( function(tx) {
+ print('... check if a object exists: '+obj["id"])
+ var result = tx.executeSql('SELECT * from imageData where id = "'+obj["id"]+'"');
+ if(result.rows.length === 1) {// use update
+ print(obj["id"] +' exists, update it')
+ result = tx.executeSql('UPDATE imageData SET username ="' +login.username+ '",id="'+obj.id+'", created="'+obj.created+'", edited="'+obj.edited+'", profile="'+obj.profile+'", link="'+obj.source+'", filename="'+obj.filename+'",title="'+obj.title+'", desc="'+obj.desc+'", type="'+obj.type+'", width="'+obj.width+'", height="'+obj.height+'", album="'+obj.album+'", location="file://'+login.imagestore+'" where id="'+obj["id"]+'"');
+ } else {// use insert print('... does not exists, create it')
+ result = tx.executeSql('INSERT INTO imageData VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [login.username,obj.id,obj.created,obj.edited, obj.title, obj.desc, obj.album, obj.filename, obj.type, obj.height, obj.width,obj. profile,obj.source,'file://'+login.imagestore]);
+ print("Inserted");}
+ })})}}
+ catch (e){print("Data retrieval failure! "+ e+obj);}
+})}
+
+function saveImage(obj,storagedirectory,callback) {
+ // create image component from base64 code and save it
+ print("Storing "+storagedirectory+obj.filename+obj.width+"x"+obj.height);
+ var maxSize=Math.max(fotostab.width,fotostab.height);
+ var helpwidth=(obj.widthobj.height){ //landscape
+ helpheight=helpwidth*obj.height/obj.width
+ } else { //portrait
+ helpwidth=helpheight*obj.width/obj.height
+ }
+ var component=Qt.createComponent("qrc:/qml/PhotoPlaceholder.qml");
+ var sprite = component.createObject(fotostab, {"x":0,"y":0,"imageName":storagedirectory+obj.filename ,"width":helpwidth,"height":helpheight,"source": obj.source,"downloadtype":"picture"});
+ callback(obj,sprite)
+}
+
+function deleteImageData(database,user,field,selection,callback) { // does nothing useful at the moment
+ var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
+ print(' delete Image Data() for ' + field +"="+selection)
+ db.transaction( function(tx) {
+ result = tx.executeSql('UPDATE imageData SET data="" where '+ field +'="'+selection+'"');
+ callback(result)})
+}
+
+function requestFriendsAlbumPictures(friend,rootwindow,callback){
+// screenscraping of albums page of contact without user and password
+ Helperjs.friendicaWebRequest(friend.url.replace("profile","photos"),rootwindow,function(photohtml){
+ var photoarray=[];
+ var arr = photohtml.split("sidebar-photos-albums-li");
+ for (var i=2;i')-1);
+ var album={'link':albumlink,'name':albumname}
+ photoarray.push(album);
+ }
+//print("Album"+JSON.stringify(photoarray));
+callback(photoarray)
+ })
+}
+
+function requestFriendsPictures(link,rootwindow,callback){
+// screenscraping of pictures page for given album
+ Helperjs.friendicaWebRequest(link,rootwindow,function(photohtml){
+ var photoarray=[];
+ var basehtml=photohtml.substring(photohtml.indexOf('',photohtml.indexOf('-1){ //theme 1
+ var arr = photohtml.split("photo-album-image-wrapper-end");}
+
+// other themes
+ if (photohtml.indexOf("photo-album-wrapper")>-1){ //theme 2
+ var photoarea=photohtml.substring(photohtml.indexOf("photo-album-wrapper"),photohtml.indexOf("photo-album-end"))
+ var arr = photoarea.split("");}
+
+ for (var i=0;i0){
+ for(var i = 0; i < rs.rows.length; i++) {
+ rsArray.push(rs.rows.item(i))
+ }
+ var rsObject={server:rsArray[0].server,username:rsArray[0].username, password:rsArray[0].password,imagestore:rsArray[0].imagestore,maxnews:rsArray[0].maxnews,isActive:rsArray[0].isActive};
+ } else {
+ var rsObject=""
+ }
+ callback(rsObject);}}
+ );
+}
+
+function readActiveConfig(database){
+ var obj;
+ readConfig(database,function(config){
+obj=config},"isActive", 0);
+ return obj;
+}
+
+function deleteConfig(database,userobj,callback) { // delete user data from DB
+ print('deleteConfig()')
+if (userobj){var where = " WHERE username='"+ userobj.username+"' and server='"+userobj.server+"'";} else { return "no user selected!";}
+ var db=Sql.LocalStorage.openDatabaseSync(database[0],database[1],database[2],database[3]);
+ if(!db) { return; }
+ db.transaction( function(tx) {
+ print('... read from database '+where)
+ var rs = tx.executeSql('delete * from config'+where);
+ print(rs.toString);
+ callback(rs);
+ });
+}
diff --git a/v0.002/Develop/source-linux/qml/CameraComponent.qml b/v0.002/Develop/source-linux/qml/CameraComponent.qml
new file mode 100644
index 0000000..189b94d
--- /dev/null
+++ b/v0.002/Develop/source-linux/qml/CameraComponent.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.0
+import QtMultimedia 5.0
+import QtQuick.Controls 1.2
+
+Item {
+ Rectangle {
+ VideoOutput {
+ anchors.fill: parent
+ source: localCamera
+ }
+ Camera {
+ id: localCamera
+ }
+ Button {
+ id: shotButton
+ width: 200; height: 75
+ text: "Take Photo"
+ onClicked: {
+ localCamera.imageCapture.capture();
+ } }
+ Connections {
+ target: localCamera.imageCapture
+ onImageSaved: {
+ photofile= imagePaths.append({"path": path})
+ listView.positionViewAtEnd(); }
+ }
+
+Image {
+ id: photoFromCamera
+ anchors.fill: parent
+ fillMode: Image.PreserveAspectFit
+ source: photo
+ }
+
+ Button {
+ text: "Upload"
+ onClicked:{
+ var login=Service.readActiveConfig(db);
+ img.src=file;
+ Service.Upload(login,file)
+ }
+ }
+}
+}
diff --git a/v0.002/Develop/source-linux/qml/ConfigTab.qml b/v0.002/Develop/source-linux/qml/ConfigTab.qml
new file mode 100644
index 0000000..6cc9e8c
--- /dev/null
+++ b/v0.002/Develop/source-linux/qml/ConfigTab.qml
@@ -0,0 +1,250 @@
+import QtQuick 2.0
+import QtQuick.Dialogs 1.2
+import QtQuick.Controls 1.2
+import "qrc:/js/service.js" as Service
+import "qrc:/js/layout.js" as Layoutjs
+import "qrc:/js/helper.js" as Helperjs
+import "qrc:/qml"
+
+StackView{
+ id: configStack
+ anchors.fill:parent
+ focus: true
+ Keys.onReleased: if (event.key === Qt.Key_Back && stackView.depth > 1) {
+ stackView.pop(); event.accepted = true;
+ }
+ initialItem: Flickable{
+ width:root.width-5*mm
+ height:root.height-12*mm
+ contentHeight: configBackground.height
+ boundsBehavior: Flickable.StopAtBounds
+ Rectangle{
+ id:configBackground
+ color: "grey"
+ width:parent.width
+ height:Math.max(80*mm,root.height-12*mm)
+ focus: true
+
+ ComboBox{
+ y:mm
+ width: root.width/2
+ model: ListModel{
+ id: usermodel
+ }
+onCurrentIndexChanged:{
+ try {Service.readConfig(db,function(obj){
+ servername.text=obj.server;
+ username.text= obj.username;
+ password.text=Qt.atob(obj.password);
+ imagestore.text=obj.imagestore;
+ maxNewsText.text=obj.maxnews;
+ if( obj.isActive==0){isActiveField.text=qsTr("yes")} else {isActiveField.text=qsTr("no")}
+ },"username",currentText)}
+ catch (e){print(e)}
+ }
+}
+
+
+ Text {
+ text: "Server"
+ x: 4*mm; y: 10*mm
+ }
+ Text {
+ text: "User"
+ x: 4*mm; y: 20*mm
+ }
+
+ Text {
+ text: "Password"
+ x: 4*mm; y: 30*mm
+ }
+ Text {
+ text: "Image dir."
+ x: 4*mm; y: 40*mm
+ }
+
+ Text {
+ text: "Max. News"
+ x: 4*mm; y: 50*mm
+ }
+ Text {
+ text: "is Active"
+ x: 4*mm; y: 60*mm
+ }
+
+ Rectangle{color: "white"; x: 25*mm; y: 10*mm; width: root.width/2; height: 5*mm;}
+ Flickable {
+ id: servernameFlickable
+ x: 25*mm; y: 10*mm; width: root.width/2; height: 5*mm;
+ contentWidth: servername.paintedWidth
+ contentHeight: servername.paintedHeight
+ clip: true
+ TextEdit {
+ id: servername
+ width: servernameFlickable.width
+ height: servernameFlickable.height
+ focus: true
+ text:"https://..."
+ //wrapMode: TextEdit.NoWrap
+ //validator: RegExpValidator { regExp: /^(((http|https|ftp):\/\/)?([[a-zA-Z0-9]\-\.])+(\.)([[a-zA-Z0-9]]){2,4}([[a-zA-Z0-9]\/+=%&_\.~?\-]*))*$/}
+ // onEditingFinished:{}
+ onCursorRectangleChanged: Layoutjs.ensureVisibility(cursorRectangle,servernameFlickable)
+ }
+ }
+
+ Rectangle{
+ color: "white"
+ x: 25*mm; y: 20*mm; width: root.width/2; height: 5*mm;
+ TextInput {
+ id: username
+ anchors.fill: parent
+ selectByMouse: true
+ }
+ }
+ Rectangle{
+ color: "white"
+ x: 25*mm; y: 30*mm; width: root.width/2; height: 5*mm;
+ TextInput {
+ id: password
+ anchors.fill: parent
+ selectByMouse: true
+ echoMode: TextInput.PasswordEchoOnEdit
+ }
+ }
+
+ Rectangle{color: "white"; x: 25*mm; y: 40*mm; width: root.width/2-9*mm; height: 5*mm;}
+ Flickable {
+ id: imagestoreFlickable
+ x: 25*mm; y: 40*mm; width: root.width/2-9*mm; height: 5*mm;
+ clip: true
+ TextInput {
+ id: imagestore
+ width: imagestoreFlickable.width
+ height: imagestoreFlickable.height
+ wrapMode: TextEdit.NoWrap
+ onCursorRectangleChanged: Layoutjs.ensureVisibility(cursorRectangle,imagestoreFlickable)
+ }
+ }
+ Slider{ id: maxNews
+ x:37*mm; y: 50*mm;width: root.width/3;height:5*mm
+ minimumValue: 0;maximumValue:100000; stepSize: 1000
+ }
+ Rectangle{color: "white"; x: 25*mm; y: 50*mm; width: 9*mm; height: 5*mm;
+ TextEdit{id:maxNewsText;
+ anchors.fill: parent
+ verticalAlignment:TextEdit.AlignRight
+ text:maxNews.value
+ focus: true
+ selectByMouse: true
+ }
+ }
+
+
+ Rectangle{
+ x: 25*mm; y: 60*mm; width: root.width/2; height: 5*mm;
+ Text{
+ id: isActiveField
+ anchors.fill: parent
+ }
+ }
+
+ FileDialog {
+ id: imagestoreDialog
+ title: "Please choose a directory"
+ folder: shortcuts.pictures
+ selectFolder: true
+ onAccepted: {
+ var imagestoreString=imagestoreDialog.folder.toString(); imagestoreString=imagestoreString.replace(/^(file:\/{2})/,"")+"/"
+ imagestore.text=imagestoreString;
+ console.log("You chose: " + imagestoreDialog.folder)
+ }
+ onRejected: {
+ console.log("Canceled")
+ }
+ }
+
+ Button {
+ x: root.width/2+18*mm; y: 40*mm; width: 7*mm; height: 5*mm;
+ text: "..."
+ onClicked:
+ {imagestoreDialog.open()}
+ }
+
+
+ Button {
+ x: 25*mm; y: 70*mm; width: implicitWidth; height: implicitHeight;
+ text: "Update"
+ onClicked:{
+ var userconfig={server: servername.text, username: username.text, password:Qt.btoa(password.text), imagestore:imagestore.text,maxnews:maxNewsText.text};
+ var errormessage="";
+ if (servername.text==""){errormessage=qsTr("No server given! ")}
+ //if (!servername.acceptableInput){errormessage+=qsTr("Server name not valid! ")}
+ else if (username.text==""){errormessage+=qsTr("No username given! ")}
+ else if (password.text=="") {errormessage+=qsTr("No password given! ")}
+ else if (imagestore.text=="") {errormessage+=qsTr("No image directory given!")}
+ else if (maxNewsText.text=="") {errormessage+=qsTr("No maximum news number given!")}
+ else {errormessage=""}
+ if (errormessage=="") {
+ filesystem.Directory=userconfig.imagestore;
+ filesystem.makeDir("contacts");
+ Service.storeConfig(db,userconfig);
+ Service.readConfig(db,function(userconfig){Service.getServerConfig(userconfig,configBackground, function(obj){
+ var serverString=obj;
+ var serverconfigObject=Qt.createQmlObject(serverString,configBackground,"serverconfigOutput");
+ usermodel.append({text:username.text});
+ //reset values
+ root.login=userconfig;
+ root.contactlist=[];
+ root.news=[]
+ root.newContacts=[]
+ root.currentContact= 0
+ root.contactLoadType= ""
+ root.currentIndex=0;
+ newstab.active=true;
+ })},"isActive",0);
+ }
+ else {Helperjs.show("Error", errormessage,root)}
+}}
+
+ Button {
+ x: root.width/2+2*mm; y: mm; width: 5*mm; height: 5*mm;
+ text: "-"
+ onClicked:{
+ var userconfig={server: servername.text, username: username.text, password: Qt.btoa(password.text)};
+ Service.deleteConfig(db,userconfig);
+ }}
+
+ Button {
+ x: root.width/2+8*mm; y: mm; width: 5*mm; height: 5*mm;
+ text: "+"
+ onClicked:{
+ servername.text="https://..."
+ username.text=""
+ password.text=""
+ imagestore.text=""
+ maxNews.value=1000
+ isActiveField.text=""
+ }
+ }
+
+ Button {
+ x: root.width/2+14*mm; y: mm; width: 5*mm; height: 5*mm;
+ text: "?"
+ onClicked:{
+ configStack.push({item:"qrc:/qml/InfoBox.qml"});
+ }
+ }
+
+ Component.onCompleted: {
+ try{Helperjs.readData(db,"config",root.login.username,function(users){
+ users.sort(function(obj1, obj2) {
+ return obj1.isActive - obj2.isActive;
+ });
+ for (var i=0; i"+qsTr("Description")+": "+Qt.atob(friend.description)+"
"+qsTr("Server Type")+": "+friend.location+"
"+qsTr("Posts")+": "+friend.statuses_count+
+ "
"+qsTr("URL")+": "+friend.url+"
"+
+ qsTr("Created at")+": "+createdAtDate.toLocaleString(Qt.locale())
+ onLinkActivated: {
+ Qt.openUrlExternally(link)}
+ }
+ }
+
+ Row{
+ anchors.top: namelabelflickable.bottom
+ anchors.topMargin: 2*mm
+ spacing:4
+
+ Button{
+ id:photobutton
+ text:"Photos"
+ visible:friend.location=="Friendica"? 1:0
+ onClicked:{root.currentIndex=2;
+ fotostab.active=true;
+ root.fotoSignal(friend) ;
+ }
+ }
+
+ Button{
+ id:messagebutton
+ text:"Messages"
+ onClicked:{root.currentIndex=0;
+ newstab.active=true;
+ root.messageSignal(friend.id) ;
+ }
+ }
+
+ Button{
+ id:dmbutton
+ visible: friend.following=="true"?true:false
+ text: "DM"
+ onClicked:{root.currentIndex=0;
+ newstab.active=true;
+ root.directmessageSignal(friend.screen_name);
+ }
+ }
+
+ Button{
+ id: closeButton
+ text: "close"
+ onClicked:{friendComponent.state=""}
+ }
+ }
+ }
+}
+states: [
+ State {
+ name: "large"
+ PropertyChanges { target: namelabel; font.pixelSize: 4*mm; width:friendsTabView.width-4*mm; text:Qt.atob(friend.name)+" (@"+friend.screen_name+")"}
+ PropertyChanges { target: friendComponent; z: 2 }
+ PropertyChanges { target: wrapper; width:friendsTabView.width-3*mm;height:friendsTabView.height-14*mm}
+ PropertyChanges { target: photoImage; width:15*mm;height:15*mm }
+ PropertyChanges { target:friendComponent.GridView.view;contentY:friendComponent.y;contentX:friendComponent.x;interactive:false}
+ PropertyChanges { target: detailsrectangle; opacity:1 }
+ }
+]
+}
diff --git a/v0.002/Develop/source-linux/qml/FriendsTab.qml b/v0.002/Develop/source-linux/qml/FriendsTab.qml
new file mode 100644
index 0000000..71b4b0b
--- /dev/null
+++ b/v0.002/Develop/source-linux/qml/FriendsTab.qml
@@ -0,0 +1,174 @@
+import QtQuick 2.0
+import QtQuick.Dialogs 1.2
+import QtQuick.Controls 1.2
+import "qrc:/js/service.js" as Service
+import "qrc:/js/helper.js" as Helperjs
+import "qrc:/js/news.js" as Newsjs
+import "qrc:/qml"
+
+Rectangle {
+ y:1
+ color: "white"
+ TabView{
+ id:friendsTabView
+// property var contacts: []
+// property var groups: []
+
+ tabPosition: Qt.TopEdge
+ x:mm
+ y:mm
+ width: root.width-5*mm
+ height: root.height-10*mm
+ currentIndex: 0
+ signal friendsSignal(var username)
+ signal contactsSignal(var username)
+ signal groupsSignal(var username)
+ onCurrentIndexChanged:{
+ if (currentIndex==0){friendsSignal(root.login.username)}
+ else if (currentIndex==1){contactsSignal(root.login.username)}
+ else if (currentIndex==2){groupsSignal(root.login.username)}
+ }
+
+ Tab{
+ title: qsTr("Friends")
+ Rectangle{
+ id: friendsGridTab
+ function showFriends(username){
+ try {friendsModel.clear()} catch(e){print(e)};
+ Helperjs.readData(db,"contacts",username,function(friends){
+ for (var i=0;iFriendiqa v0.001
Licensed under GPL 3
"+
+ "Sourcecode: https://github.com/LubuWest/Friendica
"+
+ "C++ code by Fabio
"+
+ "QML and Javascript code by Marco"
+ onLinkActivated:{
+ Qt.openUrlExternally(link)}
+ }
+ Button{
+ text:qsTr("Close")
+ onClicked:{configStack.pop()}
+ anchors.top:infoBoxText.bottom
+ }
+ }
diff --git a/v0.002/Develop/source-linux/qml/MessageSend.qml b/v0.002/Develop/source-linux/qml/MessageSend.qml
new file mode 100644
index 0000000..bdc7875
--- /dev/null
+++ b/v0.002/Develop/source-linux/qml/MessageSend.qml
@@ -0,0 +1,132 @@
+// message.qml
+// message with buttons
+import QtQuick 2.0
+import QtQml 2.2
+import QtQuick.Controls 1.3
+import QtQuick.Dialogs 1.2
+import QtQuick.LocalStorage 2.0
+//import "../qml"
+import "qrc:/js/service.js" as Service
+
+Item{
+ id:messageSend
+ property var login
+ property string parentId: ""
+ property string reply_to_user:""
+ property string attachImageURL: "";
+ property int directmessage: 0;
+ property var contacts: []
+ // title: parentId !== "" ? qsTr("Reply to "+reply_to_user) : qsTr("New post")
+
+ function statusUpdate(title,status,in_reply_to_status_id,attachImageURL) {
+ xhr.url= login.server + "/api/statuses/update.xml";
+ xhr.setLogin(login.username+":"+Qt.atob(login.password));
+ print("login: "+login.username+":"+Qt.atob(login.password));
+ xhr.clearParams();
+ xhr.setParam("source", "Friendiqa");
+ xhr.setParam("status", status);
+ if (parentId!="") {xhr.setParam("in_reply_to_status_id", parentid)};
+ if (title!=="") {xhr.setParam("title", title)};
+ if (attachImageURL!=="") {xhr.setImageFileParam("media", attachImageURL )};
+ xhr.post();
+ }
+
+ function dmUpdate(title,text,replyto,screen_name,attachImageURL) {
+ xhr.url= login.server + "/api/direct_messages/new.xml";
+ xhr.setLogin(login.username+":"+Qt.atob(login.password));
+ xhr.clearParams();
+ xhr.setParam("text", text);
+ xhr.setParam("screen_name", screen_name);
+ if (parentId!="") {xhr.setParam("replyto", replyto)};
+ if (title!=="") {xhr.setParam("title", title)};
+ if (attachImageURL!=="") {xhr.setImageFileParam("media", attachImageURL )};
+ xhr.post();
+ }
+
+ Column {
+ id:messageColumn
+ spacing: 2
+
+ TextField {
+ id: titleField
+ width: parent.width
+ placeholderText: qsTr("Title (optional)")
+ visible: messageSend.parentId === ""
+ }
+
+ TextArea {
+ id: bodyField
+ width: parent.width
+ height: 30*mm
+ wrapMode: TextEdit.Wrap
+ }
+CheckBox{
+ id:dmCheckbox
+ text:"DM"
+ enabled: false
+ checked: (directmessage==1)?true:false
+ onClicked:{
+ if(dmCheckbox.checkedState==Qt.Checked){directmessage=1}
+ else if(dmCheckbox.checkedState==Qt.Unchecked){directmessage=0}
+ }
+ }
+ Row{
+ spacing:2
+ Button {
+ id: cancelButton
+ text: qsTr("Cancel")
+ onClicked: {newsStack.pop()}
+ }
+
+ Button {
+ id: attachButton
+ text: qsTr("Attach")
+ onClicked: {imageAttachmentDialog.open()}
+ }
+ Button{
+ id:contactButton
+ text:qsTr("cc")
+ visible:(directmessage==0)
+ onClicked:{
+ var contactitems="";
+ for (var i=0;imaxnews){var lastvalidtimers= tx.executeSql('select created_at from news ORDER BY created_at DESC LIMIT ' +(newscount-maxnews));
+ var lastvalidtime=lastvalidtimers.rows.item(-1);
+ var deleters = tx.executeSql('DELETE from news WHERE created_at<'+lastvalidtime)}
+ });
+ Qt.quit()
+ }
+
+ StackView{
+ id: newsStack
+ anchors.fill:parent
+ focus: true
+ Keys.onReleased: if (event.key === Qt.Key_Back && stackView.depth > 1) {
+ stackView.pop(); event.accepted = true;
+ }
+ initialItem:Rectangle {
+ y:1
+ color: "white"
+ width:root.width-2*mm
+ height:root.height-8*mm
+
+ Button {
+ id: newMessageButton
+ text: qsTr("+")
+ anchors.top: parent.top
+ anchors.right: parent.right
+ onClicked: {
+ Helperjs.readField("screen_name",root.db,"contacts",root.login.username,function(friends){
+ newsStack.push({item:"qrc:/qml/MessageSend.qml",properties:{"contacts": friends,"login":root.login}})
+ },"isFriend",1);
+ }
+ }
+ Button {
+ id: quitButton
+ text: qsTr("Quit")
+ anchors.top: parent.top
+ anchors.right: newMessageButton.left
+ onClicked: {cleanNews(root.db)}
+ }
+
+ Component { id:footerComponent
+ Rectangle{
+ border.color: "#EEEEEE"
+ border.width: 1
+ width:parent.width
+ height:6*mm
+ Text{
+ font.pixelSize: 1.5*mm
+ anchors.centerIn: parent
+ text:qsTr("More")
+ }
+ MouseArea{anchors.fill:parent
+ onClicked:{
+ var currentTime= new Date();
+ if(newstabStatus=="news"){
+ var lastnews_id=newsModel.get(newsModel.count-1).newsitemobject.created_at;
+ Newsjs.newsfromdb(root.db,root.login.username, function(news){
+ var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true};
+ newsWorker.sendMessage(msg);
+ },false,lastnews_id)}
+ else if(newstabStatus=="friendmessage"){
+ Newsjs.newsfromdb(root.db,root.login.username, function(news){
+ var msg = {'currentTime': currentTime, 'model': newsModel,'news':news,'appendnews':true};
+ newsWorker.sendMessage(msg);
+ },newsModel.get(newsModel.count-1).newsitemobject.uid,newsModel.get(newsModel.count-1).newsitemobject.created_at)}
+ }}
+ }
+ }
+ ListView {
+ id: newsView
+ anchors.fill: parent
+ anchors.topMargin: 8*root.mm
+ anchors.leftMargin: 3*root.mm; anchors.rightMargin: root.mm
+ anchors.bottomMargin: 1*root.mm
+ clip: true
+ spacing: 0
+ footer: footerComponent
+ model: newsModel
+ delegate: Newsitem{}
+ }
+
+ ListModel{id: newsModel}
+
+ WorkerScript {
+ id: newsWorker
+ source: "qrc:/js/newsworker.js"
+ }
+
+ Button {
+ id: update
+ anchors.top: parent.top
+ anchors.right: quitButton.left
+ text: "Update"
+ onClicked: { //try{newsModel.clear()} catch(e){}
+ newsBusy.running=true;
+ root.contactLoadType="news";
+ Newsjs.getFriendsTimeline(login,db,contactlist,newstab,function(ns,nc){
+ root.news=ns;root.newContacts=nc;root.currentContact=0;
+ if (ns.length==0){
+ Newsjs.getDirectMessage(root.login,root.db,root,function(dbnews){showNews(dbnews)});
+ newsBusy.running=false}
+ })}
+ }
+ BusyIndicator{
+ id: newsBusy
+ anchors.centerIn:update
+ //anchors.right: update.left
+ //anchors.top:parent.top
+ width:7*mm
+ height: 7*mm
+ }
+ Component.onCompleted: {
+ root.messageSignal.connect(onFriendsMessages);
+ root.directmessageSignal.connect(onDirectMessage);
+ root.newsSignal.connect(showNews);
+ try{newsModel.clear()} catch(e){}
+ Newsjs.newsfromdb(root.db,root.login.username, function(dbnews){
+ showNews(dbnews)
+ })
+ }
+}
+}
+}
diff --git a/v0.002/Develop/source-linux/qml/Newsitem.qml b/v0.002/Develop/source-linux/qml/Newsitem.qml
new file mode 100644
index 0000000..726d8ba
--- /dev/null
+++ b/v0.002/Develop/source-linux/qml/Newsitem.qml
@@ -0,0 +1,286 @@
+import QtQuick 2.0
+import QtQuick.LocalStorage 2.0
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "qrc:/js/news.js" as Newsjs
+import "qrc:/js/layout.js" as Layoutjs
+
+
+Item {
+ id: newsitem
+ width: newsView.width
+ height:Math.max((itemMessage.height+createdAtLabel.height+friendicaActivities.height+4*mm),profileImage.height+user_name.height+mm)
+
+ // property var friendica_activities
+ property string conversation_id: ""
+ property string attending: ""
+ onAttendingChanged: {attendLabel.visible=true;
+ attendLabel.text= qsTr("attending: ")+ qsTr(attending)}
+ signal replyto(string parent_id)
+
+ Rectangle{width:newsitem.width; height: 1; anchors.bottom: newsitem.bottom; color:"light grey"}
+
+ Rectangle{
+ width:newsitem.width
+ height:newsitem.height-1
+ color: (newsitemobject.directmessage)?"#ffe6e6" : "white"
+
+ Column {
+ id: authorcolumn
+ width: 8*mm
+
+ Image {
+ id:profileImage
+ source: "file://"+newsitemobject.user.profile_image
+ x:1
+ width: 7*mm
+ height: 7*mm
+ MouseArea{
+ anchors.fill: parent
+ onPressAndHold: { newsmenu.popup()}
+ }
+ onStatusChanged: if (profileImage.status == Image.Error) {source="qrc:/images/defaultcontact.jpg"}
+ }
+ Label {
+ id:user_name
+ color: "grey"
+ //height:3.5*mm
+ width:parent.width
+ font.pixelSize: 1.5*mm
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ text: Qt.atob(newsitemobject.user.name)
+ }
+ }
+ Column {
+ id:newscolumn
+ anchors.left: authorcolumn.right
+
+ Row{
+ spacing: 5*mm
+ Label {
+ color: "grey"
+ text: if (newsitemobject.messagetype==0){qsTr("Source: ")+newsitemobject.source
+ } else if (newsitemobject.messagetype==1){ qsTr("Direct Message")} else {" Notification"}
+ font.pixelSize: 1.5*mm
+ }
+ Label {
+ id:createdAtLabel
+ color: "grey"
+ height:3.5*mm
+ font.pixelSize: 1.5*mm
+ horizontalAlignment: Label.AlignRight
+ text: dateDiff
+ }
+ CheckBox {
+ id:favoritedCheckbox
+ style: CheckBoxStyle {
+ background: Rectangle {
+ implicitWidth: 6*mm
+ implicitHeight:2*mm
+ color:"white"
+ }
+ indicator:
+ Rectangle{x:3*mm
+ width: 3*mm
+ implicitHeight:2*mm
+ Text{
+ anchors.centerIn: parent
+ color:control.checked?"black":"grey"
+ text:"\u2605"
+ }}
+ }
+ checked:(newsitemobject.favorited>0)
+ Text{
+ anchors.left: parent.right
+ color: "grey"
+ font.pixelSize: 1.5*mm
+ text: (newsitemobject.favorited>0)? newsitemobject.favorited+qsTr(" Favorites"):""
+ }
+ onClicked:{
+ if(favoritedCheckbox.checkedState==Qt.Checked)
+ {Newsjs.favorite(login,true,newsitemobject.status_id,root)}
+ else
+ if(favoritedCheckbox.checkedState==Qt.Unchecked)
+ {Newsjs.favorite(login,false,newsitemobject.status_id,root)}
+ }
+ }
+
+ }
+
+ Text {
+ color: "#404040"
+ linkColor: "light green"
+ id: itemMessage
+ textFormat: Text.RichText
+ text: Qt.atob(newsitemobject.statusnet_html)
+ width: newsitem.width-8*mm-2
+ height: implicitHeight
+ wrapMode: Text.Wrap
+ onLinkActivated:{ print("link "+link);
+ Qt.openUrlExternally(link)}
+ }
+ Row{id: friendicaActivities
+ spacing:mm
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.likeText
+ }
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.dislikeText
+ }
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.attendyesText
+ }
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.attendnoText
+ }
+ Label{color: "grey"
+ font.pixelSize: 1.5*mm
+ text: friendica_activities.attendmaybeText
+ }
+ }
+Row {
+ CheckBox{id:likeCheckbox
+ height:3*mm
+ width:8*mm
+ checked:(newsitemobject.liked==1)?true:false
+ style: CheckBoxStyle {
+ background: Rectangle {
+ implicitWidth: 7*mm
+ implicitHeight: 3*mm
+ color:"white"
+ }
+ indicator:
+ Rectangle{
+ implicitWidth: 3*mm
+ implicitHeight:3*mm
+ color:control.checked?"yellow":"white"
+ x: 5*mm
+ Text{
+ font.pixelSize: 1.5*mm
+ color:"grey"
+ text:":-)"
+ }}
+ }
+ onClicked: {
+ if(likeCheckbox.checked==true){Newsjs.like(root.login,root.db,1,"like",newsitemobject.status_id,root);dislikeCheckbox.checked=false}
+ else{Newsjs.like(root.login,root.db,0,"like",newsitemobject.status_id,root)}}
+ }
+ CheckBox{id: dislikeCheckbox
+ height:3*mm
+ width:8*mm
+ checked: (newsitemobject.disliked==1)?true:false
+ style: CheckBoxStyle {
+ background: Rectangle {
+ implicitWidth: 7*mm
+ implicitHeight:3*mm
+ color:"white"
+ }
+ indicator:
+ Rectangle{
+ implicitWidth: 3*mm
+ implicitHeight:3*mm
+ color:control.checked?"yellow":"white"
+ x:5*mm
+ Text{
+ font.pixelSize: 1.5*mm
+ color:"grey"
+ text:":-("
+ }}
+ }
+ onClicked: {
+ if (dislikeCheckbox.checked==true){Newsjs.like(root.login,root.db,1,"dislike",newsitemobject.status_id);likeCheckbox.checked=false}
+ else {Newsjs.like(root.login,root.db,0,"dislike",newsitemobject.status_id,root)}}
+ }
+ Label {
+ id:replytoLabel
+ color: "grey"
+ height:3.5*mm
+ font.pixelSize: 1.5*mm
+ horizontalAlignment: Label.AlignRight
+ text: try {qsTr("In reply to ")+newsitemobject.reply_user.screen_name
+ }catch(e){" "}
+ }
+ Label {
+ id:attendLabel
+ visible: false
+ color: "grey"
+ height:3.5*mm
+ font.pixelSize: 1.5*mm
+ horizontalAlignment: Label.AlignRight
+ text: qsTr("attending: ")+ qsTr(attending)
+ }
+ }
+ }
+
+ Menu {
+ id:newsmenu
+ MenuItem {
+ text: qsTr("Reply")
+ onTriggered: {
+ newsStack.push({item:"qrc:/qml/MessageSend.qml",properties:{"reply_to_user": newsitemobject.user.screen_name,"parentId":newsitemobject.status_id}});
+ }
+ }
+MenuItem {
+ text: qsTr("DM")
+ onTriggered: {
+ root.directmessageSignal(newsitemobject.user.screen_name);
+ }
+ }
+ MenuItem {
+ text: qsTr("Repost")
+ onTriggered: {
+ Newsjs.retweetNews(root.login,db,newsitemobject.status_id,root,function(reply){
+ print(reply);
+ })
+ }
+ }
+ MenuItem {
+ text: qsTr("Conversation")
+ onTriggered: {
+ Newsjs.requestConversation(root.login,db,newsitemobject.status_id,root,function(){
+ var currentTime= new Date();
+ Newsjs.conversationfromdb(db,root.login.username,newsitemobject.statusnet_conversation_id, function(newsarray){
+ newsModel.clear();
+ var msg = {'currentTime': currentTime, 'model': newsModel,'news':newsarray,'latestmessage':0};
+ newsWorker.sendMessage(msg);
+ });
+ }
+ )}
+ }
+
+Menu{
+ title: qsTr("Attending")
+
+ MenuItem{text:qsTr("yes")
+ onTriggered: {Newsjs.attend(root.login,db,"yes",newsitemobject.status_id,root,function(){
+ newsitem.attending="yes";
+ attendLabel.visible=true})}
+ }
+
+ MenuItem{text:qsTr("maybe")
+ onTriggered: {Newsjs.attend(root.login,db,"maybe",newsitemobject.status_id,root,function(){
+ newsitem.attending="maybe"})}
+ }
+
+ MenuItem{text:qsTr("no")
+ onTriggered: {Newsjs.attend(root.login,db,"no",newsitemobject.status_id,root,function(){
+ newsitem.attending="no"})}
+ }
+ }
+
+ MenuItem {
+ text: qsTr("Delete")
+ onTriggered: {
+ Newsjs.deleteNews(root.login,db,newsitemobject.status_id,root,function(reply){
+ print(JSON.stringify(reply));
+ newsModel.remove(index);
+ })
+ }
+ }
+}
+}}
+
diff --git a/v0.002/Develop/source-linux/qml/PhotoComponent.qml b/v0.002/Develop/source-linux/qml/PhotoComponent.qml
new file mode 100644
index 0000000..7da5f8b
--- /dev/null
+++ b/v0.002/Develop/source-linux/qml/PhotoComponent.qml
@@ -0,0 +1,100 @@
+import QtQuick 2.0
+import QtQuick.LocalStorage 2.0
+import QtQuick.Controls 1.2
+
+Package {
+ Item { id: stackItem; Package.name: 'stack'; z: stackItem.PathView.z;width:16.5*mm;height:16.5*mm}
+ Item { id: listItem; Package.name: 'list'; width: root.width-1*mm; height: root.height-8*mm; }
+ Item { id: gridItem; Package.name: 'grid';}
+
+ Item {
+ id: photoWrapper
+ width: 16.5*mm; height: 16.5*mm
+ z: stackItem.PathView.z
+ property string hqphotolink: photoLink
+
+ Rectangle {
+ id: placeHolder
+ color: 'lightblue'; antialiasing: true
+ anchors.fill:parent
+ //width: parent.width; height: parent.height;
+ }
+
+ BusyIndicator { anchors.centerIn: parent; running: realImage.status != Image.Ready }
+ Image {
+ id: realImage;
+ // property string hqphotolink: photoLink
+ width: photoWrapper.width; height: photoWrapper.height
+ antialiasing: true;
+ asynchronous: true
+ cache: false
+ fillMode: Image.PreserveAspectFit;
+ source: imageLocation
+ // onStatusChanged: if (realImage.status == Image.Ready) print(realImage.paintedHeight+"x"+realImage.paintedWidth)
+ }
+ Rectangle{
+ id:phototextRectangle
+ color:"black"
+ z:3
+ opacity: 0.5
+ width:phototext.contentWidth
+ height: phototext.contentHeight
+ anchors.bottom: photoWrapper.bottom
+ }
+ Text {
+ id:phototext
+ z:4
+ text: photoDescription.trim()
+ width:15*mm
+ anchors.bottom: photoWrapper.bottom
+ color: "white"
+ font.pixelSize: 2*mm
+ wrapMode:Text.Wrap
+ }
+ MouseArea {
+ width: realImage.paintedWidth; height: realImage.paintedHeight; anchors.centerIn: realImage
+ onClicked: {
+ if (albumWrapper.state == 'inGrid') {
+ gridItem.GridView.view.currentIndex = index;
+ //print("photoLink"+realImage.photoLink)
+ albumWrapper.state = 'fullscreen'
+ } else {
+ gridItem.GridView.view.currentIndex = index;
+ albumWrapper.state = 'inGrid'
+ }
+ }
+ }
+
+ states: [
+ State {
+ name: 'stacked'; when: albumWrapper.state == ''
+ ParentChange { target: photoWrapper; parent: stackItem; }//x: 1*mm; y: 1*mm }
+ PropertyChanges { target: photoWrapper; opacity: stackItem.PathView.onPath ? 1.0 : 0.0 }
+ PropertyChanges { target: phototext; opacity: 0.0 }
+ PropertyChanges { target: phototextRectangle; opacity: 0.0 }
+ },
+ State {
+ name: 'inGrid'; when: albumWrapper.state == 'inGrid'
+ ParentChange { target: photoWrapper; parent: gridItem; x: 1*mm; y: 1*mm;}
+ PropertyChanges { target: phototext; opacity: 1.0 }
+ PropertyChanges { target: phototextRectangle; opacity: 0.5 }
+ PropertyChanges { target: placeHolder; opacity: 1.0 }
+ },
+ State {
+ name: 'fullscreen'; when: albumWrapper.state == 'fullscreen'
+ ParentChange {
+ target: photoWrapper; parent: listItem; x: 1; y: 1;
+ width: root.width-mm; height: root.heigh-8*mm
+ }
+ PropertyChanges { target: placeHolder; opacity: 0.0 }
+ PropertyChanges { target: realImage; source: photoWrapper.hqphotolink}
+ PropertyChanges { target: phototext; anchors.bottom: realImage.bottom}
+ PropertyChanges { target: phototext; width:realImage.width }
+ PropertyChanges { target: phototextRectangle; anchors.bottom: realImage.bottom }
+ PropertyChanges { target: realImage; width: Math.min(listItem.width,sourceSize.width);height: Math.min(listItem.height,sourceSize.height) }
+ }
+ ]
+
+ }
+}
+
diff --git a/v0.002/Develop/source-linux/qml/PhotoPlaceholder.qml b/v0.002/Develop/source-linux/qml/PhotoPlaceholder.qml
new file mode 100644
index 0000000..0719059
--- /dev/null
+++ b/v0.002/Develop/source-linux/qml/PhotoPlaceholder.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+import "qrc:/js/helper.js" as Helperjs
+
+Image {
+id:photoPlaceholder
+property string imageName:"x.jpg"
+property string downloadtype:""
+fillMode: Image.PreserveAspectFit
+onStatusChanged: {
+ if (photoPlaceholder.status == Image.Ready) {
+ print("Source: "+source+"Photo width"+width+" height"+height+" Ratio "+ fillMode);
+ var saveprocess=photoPlaceholder.grabToImage(function(result){
+ var saveresult=result.saveToFile(imageName.toString());
+ if (saveresult){
+ if ((downloadtype=="picture")&&(newImages.length>0)){
+ photoPlaceholder.destroy();
+ currentImageNo=currentImageNo+1
+ }
+ else if ((downloadtype=="contact")&&(root.newContacts.length>0))
+ {
+ photoPlaceholder.destroy(100);
+ root.currentContact=root.currentContact+1
+ }
+ }});
+}}}
+
diff --git a/v0.002/Develop/source-linux/qml/PhotoTab.qml b/v0.002/Develop/source-linux/qml/PhotoTab.qml
new file mode 100644
index 0000000..e535c42
--- /dev/null
+++ b/v0.002/Develop/source-linux/qml/PhotoTab.qml
@@ -0,0 +1,140 @@
+import QtQuick 2.0
+import QtQuick.Dialogs 1.2
+import QtQuick.Controls 1.4
+import QtQml.Models 2.1
+import "qrc:/js/service.js" as Service
+import "qrc:/js/helper.js" as Helperjs
+import "qrc:/qml"
+
+Rectangle {
+ id:fotorectangle
+ y:1
+ width:root.width-mm
+ height:root.height-5*mm
+ color: '#fff'
+ property var newImages:[]
+ property int currentImageNo: 0
+//onLoginChanged:{var msg = {'model': photogroupModel,'albums':[],'firstalbum':0,'foreignPicture':false};
+// photoWorker.sendMessage(msg);
+//}
+ onNewImagesChanged:{
+ if(newImages.length>0){
+ Service.dataRequest(root.login,newImages[currentImageNo].id,root.db,fotorectangle);
+ newImagesProgress.visible=true //download first image
+ }}
+ onCurrentImageNoChanged:{
+ if(currentImageNo0){// download first contact image and update db
+ print("newcontact"+JSON.stringify(newContacts[0]));
+ updateContactInDB(login,db,newContacts[currentContact].isFriend,newContacts[currentContact])}
+ }
+ onCurrentContactChanged:{// download next contact image after photoplaceholder is finished saving and update db
+ print("Current contact"+JSON.stringify(newContacts[currentContact]));
+ if(currentContact