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