- release candidate 1.1.0
- added the ability to download pdf files - added warning message "Processing error" - new pull-to-refresh indicator - fixed eye-ion on login screen
12
CHANGELOG.md
|
@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.1.0] - unreleased
|
||||||
|
### Hinzugefügt
|
||||||
|
- PDF-Taste oben links in der Vertretungsplan-Ansicht, um den Vertretungsplan als PDF anzusehen
|
||||||
|
- Fehlermeldung "Verarbeitungsfehler" mit Taste zum Ansehen der PDF Datei
|
||||||
|
|
||||||
|
### Entfernt
|
||||||
|
- ständige Abfragen im Hintergrund, die sehr viel Datenvolumen verbrauchten
|
||||||
|
|
||||||
|
### Geändert
|
||||||
|
- einige Icons
|
||||||
|
- pull-to-refresh indikator
|
||||||
|
|
||||||
## [1.0.2] - 2019-03-14
|
## [1.0.2] - 2019-03-14
|
||||||
### Hinzugefügt
|
### Hinzugefügt
|
||||||
- Dunkler Modus
|
- Dunkler Modus
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<manifest package="com.itsblue.flgvertretungtest" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0.2" android:versionCode="16" android:installLocation="auto">
|
<manifest package="com.itsblue.flgvertretungbeta" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.1.0" android:versionCode="17" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="28"/>
|
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="28"/>
|
||||||
|
|
||||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||||
|
|
||||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="fannyapp" android:icon="@drawable/icon">
|
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="fannyapp" android:icon="@drawable/icon">
|
||||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="fannyapp" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="de.itsblue.fannyapp.MainActivity" android:label="fannyapp" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
@ -52,8 +52,8 @@
|
||||||
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
||||||
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
|
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
|
||||||
are done populating your window with content. -->
|
are done populating your window with content. -->
|
||||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/screen"/>
|
<!--meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/screen"/>
|
||||||
<!--meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" />
|
<meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" />
|
||||||
<meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" />
|
<meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" />
|
||||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
|
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
|
||||||
<meta-data android:name="android.app.splash_screen_sticky" android:value="true"/-->
|
<meta-data android:name="android.app.splash_screen_sticky" android:value="true"/-->
|
||||||
|
@ -83,6 +83,10 @@
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||||
|
<!-- Register the File Provider for document sharing -->
|
||||||
|
<provider android:name="android.support.v4.content.FileProvider" android:authorities="de.itsblue.fannyapp.fileprovider" android:grantUriPermissions="true" android:exported="false">
|
||||||
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/>
|
||||||
|
</provider>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
58
android-sources/build.gradle
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.2.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||||
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
BIN
android-sources/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
5
android-sources/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
172
android-sources/gradlew
vendored
Executable file
|
@ -0,0 +1,172 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# 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\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# 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
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
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" -a "$nonstop" = "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"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
84
android-sources/gradlew.bat
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
@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
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@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=
|
||||||
|
|
||||||
|
@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 Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_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=%*
|
||||||
|
|
||||||
|
: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
|
BIN
android-sources/libs/arm64-v8a/lib/libcrypto.a
Normal file
BIN
android-sources/libs/arm64-v8a/lib/libcrypto.so.1.1
Executable file
BIN
android-sources/libs/arm64-v8a/lib/libssl.a
Normal file
BIN
android-sources/libs/arm64-v8a/lib/libssl.so.1.1
Executable file
3
android-sources/libs/arm64-v8a/lib/symlink-windows.bat
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
del libcrypto.so libssl.so
|
||||||
|
mklink /H libcrypto.so libcrypto.so.1.1
|
||||||
|
mklink /H libssl.so libssl.so.1.1
|
BIN
android-sources/libs/armeabi/lib/libcrypto.a
Normal file
BIN
android-sources/libs/armeabi/lib/libcrypto.so.1.1
Executable file
BIN
android-sources/libs/armeabi/lib/libssl.a
Normal file
BIN
android-sources/libs/armeabi/lib/libssl.so.1.1
Executable file
3
android-sources/libs/armeabi/lib/symlink-windows.bat
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
del libcrypto.so libssl.so
|
||||||
|
mklink /H libcrypto.so libcrypto.so.1.1
|
||||||
|
mklink /H libssl.so libssl.so.1.1
|
BIN
android-sources/libs/x86/lib/libcrypto.a
Normal file
BIN
android-sources/libs/x86/lib/libcrypto.so.1.1
Executable file
BIN
android-sources/libs/x86/lib/libssl.a
Normal file
BIN
android-sources/libs/x86/lib/libssl.so.1.1
Executable file
3
android-sources/libs/x86/lib/symlink-windows.bat
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
del libcrypto.so libssl.so
|
||||||
|
mklink /H libcrypto.so libcrypto.so.1.1
|
||||||
|
mklink /H libssl.so libssl.so.1.1
|
BIN
android-sources/libs/x86_64/lib/libcrypto.a
Normal file
BIN
android-sources/libs/x86_64/lib/libcrypto.so.1.1
Executable file
BIN
android-sources/libs/x86_64/lib/libssl.a
Normal file
BIN
android-sources/libs/x86_64/lib/libssl.so.1.1
Executable file
3
android-sources/libs/x86_64/lib/symlink-windows.bat
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
del libcrypto.so libssl.so
|
||||||
|
mklink /H libcrypto.so libcrypto.so.1.1
|
||||||
|
mklink /H libssl.so libssl.so.1.1
|
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 5.5 KiB |
25
android-sources/res/values/libs.xml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<resources>
|
||||||
|
<array name="qt_sources">
|
||||||
|
<item>https://download.qt.io/ministro/android/qt5/qt-5.9</item>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<!-- The following is handled automatically by the deployment tool. It should
|
||||||
|
not be edited manually. -->
|
||||||
|
|
||||||
|
<array name="bundled_libs">
|
||||||
|
<!-- %%INSERT_EXTRA_LIBS%% -->
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<array name="qt_libs">
|
||||||
|
<!-- %%INSERT_QT_LIBS%% -->
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<array name="bundled_in_lib">
|
||||||
|
<!-- %%INSERT_BUNDLED_IN_LIB%% -->
|
||||||
|
</array>
|
||||||
|
<array name="bundled_in_assets">
|
||||||
|
<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->
|
||||||
|
</array>
|
||||||
|
|
||||||
|
</resources>
|
3
android-sources/res/xml/filepaths.xml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<files-path name="tmp_pdf_files" path="tmp_pdf_files/" />
|
||||||
|
</paths>
|
39
android-sources/src/de/itsblue/fannyapp/MainActivity.java
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package de.itsblue.fannyapp;
|
||||||
|
|
||||||
|
import org.qtproject.qt5.android.QtNative;
|
||||||
|
|
||||||
|
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||||
|
import android.os.*;
|
||||||
|
import android.content.*;
|
||||||
|
import android.app.*;
|
||||||
|
|
||||||
|
import java.lang.String;
|
||||||
|
import android.content.Intent;
|
||||||
|
import java.io.File;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import org.ekkescorner.utils.*;
|
||||||
|
|
||||||
|
public class MainActivity extends QtActivity
|
||||||
|
{
|
||||||
|
// native - must be implemented in Cpp via JNI
|
||||||
|
//
|
||||||
|
public static native void fireActivityResult(int requestCode, int resultCode);
|
||||||
|
|
||||||
|
// we start Activity with result code
|
||||||
|
// to test JNI with QAndroidActivityResultReceiver you must comment or rename
|
||||||
|
// this method here - otherwise you'll get wrong request or result codes
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
// hint: result comes back too fast for Action SEND
|
||||||
|
// if you want to delete/move the File add a Timer w 500ms delay
|
||||||
|
// see Example App main.qml - delayDeleteTimer
|
||||||
|
// if you want to revoke permissions for older OS
|
||||||
|
// it makes sense also do this after the delay
|
||||||
|
fireActivityResult(requestCode, resultCode);
|
||||||
|
}
|
||||||
|
} // class QShareActivity
|
||||||
|
|
199
android-sources/src/org/ekkescorner/utils/QShareUtils.java
Executable file
|
@ -0,0 +1,199 @@
|
||||||
|
// (c) 2017 Ekkehard Gentz (ekke)
|
||||||
|
// this project is based on ideas from
|
||||||
|
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
|
||||||
|
// see github project https://github.com/lasconic/ShareUtils-QML
|
||||||
|
// also inspired by:
|
||||||
|
// https://www.androidcode.ninja/android-share-intent-example/
|
||||||
|
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
|
||||||
|
// https://stackoverflow.com/questions/7156932/open-file-in-another-app
|
||||||
|
// http://www.qtcentre.org/threads/58668-How-to-use-QAndroidJniObject-for-intent-setData
|
||||||
|
// https://stackoverflow.com/questions/5734678/custom-filtering-of-intent-chooser-based-on-installed-android-package-name
|
||||||
|
// see also /COPYRIGHT and /LICENSE
|
||||||
|
|
||||||
|
package org.ekkescorner.utils;
|
||||||
|
|
||||||
|
import org.qtproject.qt5.android.QtNative;
|
||||||
|
|
||||||
|
import java.lang.String;
|
||||||
|
import android.content.Intent;
|
||||||
|
import java.io.File;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Collections;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import android.support.v4.content.FileProvider;
|
||||||
|
import android.support.v4.app.ShareCompat;
|
||||||
|
|
||||||
|
public class QShareUtils
|
||||||
|
{
|
||||||
|
// reference Authority as defined in AndroidManifest.xml
|
||||||
|
private static String AUTHORITY="de.itsblue.fannyapp.fileprovider";
|
||||||
|
|
||||||
|
protected QShareUtils()
|
||||||
|
{
|
||||||
|
//Log.d("ekkescorner", "QShareUtils()");
|
||||||
|
}
|
||||||
|
|
||||||
|
// thx @oxied and @pooks for the idea: https://stackoverflow.com/a/18835895/135559
|
||||||
|
// theIntent is already configured with all needed properties and flags
|
||||||
|
// so we only have to add the packageName of targeted app
|
||||||
|
public static boolean createCustomChooserAndStartActivity(Intent theIntent, String title, int requestId, Uri uri) {
|
||||||
|
final Context context = QtNative.activity();
|
||||||
|
final PackageManager packageManager = context.getPackageManager();
|
||||||
|
final boolean isLowerOrEqualsKitKat = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;
|
||||||
|
|
||||||
|
// MATCH_DEFAULT_ONLY: Resolution and querying flag. if set, only filters that support the CATEGORY_DEFAULT will be considered for matching.
|
||||||
|
// Check if there is a default app for this type of content.
|
||||||
|
ResolveInfo defaultAppInfo = packageManager.resolveActivity(theIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||||
|
if(defaultAppInfo == null) {
|
||||||
|
Log.d("ekkescorner", title+" PackageManager cannot resolve Activity");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// had to remove this check - there can be more Activity names, per ex
|
||||||
|
// com.google.android.apps.docs.editors.kix.quickword.QuickWordDocumentOpenerActivityAlias
|
||||||
|
// if (!defaultAppInfo.activityInfo.name.endsWith("ResolverActivity") && !defaultAppInfo.activityInfo.name.endsWith("EditActivity")) {
|
||||||
|
// Log.d("ekkescorner", title+" defaultAppInfo not Resolver or EditActivity: "+defaultAppInfo.activityInfo.name);
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Retrieve all apps for our intent. Check if there are any apps returned
|
||||||
|
List<ResolveInfo> appInfoList = packageManager.queryIntentActivities(theIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||||
|
if (appInfoList.isEmpty()) {
|
||||||
|
Log.d("ekkescorner", title+" appInfoList.isEmpty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Log.d("ekkescorner", title+" appInfoList: "+appInfoList.size());
|
||||||
|
|
||||||
|
// Sort in alphabetical order
|
||||||
|
Collections.sort(appInfoList, new Comparator<ResolveInfo>() {
|
||||||
|
@Override
|
||||||
|
public int compare(ResolveInfo first, ResolveInfo second) {
|
||||||
|
String firstName = first.loadLabel(packageManager).toString();
|
||||||
|
String secondName = second.loadLabel(packageManager).toString();
|
||||||
|
return firstName.compareToIgnoreCase(secondName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
List<Intent> targetedIntents = new ArrayList<Intent>();
|
||||||
|
// Filter itself and create intent with the rest of the apps.
|
||||||
|
for (ResolveInfo appInfo : appInfoList) {
|
||||||
|
// get the target PackageName
|
||||||
|
String targetPackageName = appInfo.activityInfo.packageName;
|
||||||
|
// we don't want to share with our own app
|
||||||
|
// in fact sharing with own app with resultCode will crash because doesn't work well with launch mode 'singleInstance'
|
||||||
|
if (targetPackageName.equals(context.getPackageName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// if you have a blacklist of apps please exclude them here
|
||||||
|
|
||||||
|
// we create the targeted Intent based on our already configured Intent
|
||||||
|
Intent targetedIntent = new Intent(theIntent);
|
||||||
|
// now add the target packageName so this Intent will only find the one specific App
|
||||||
|
targetedIntent.setPackage(targetPackageName);
|
||||||
|
// collect all these targetedIntents
|
||||||
|
targetedIntents.add(targetedIntent);
|
||||||
|
|
||||||
|
// legacy support and Workaround for Android bug
|
||||||
|
// grantUriPermission needed for KITKAT or older
|
||||||
|
// see https://code.google.com/p/android/issues/detail?id=76683
|
||||||
|
// also: https://stackoverflow.com/questions/18249007/how-to-use-support-fileprovider-for-sharing-content-to-other-apps
|
||||||
|
if(isLowerOrEqualsKitKat) {
|
||||||
|
Log.d("ekkescorner", "legacy support grantUriPermission");
|
||||||
|
context.grantUriPermission(targetPackageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
// attention: you must revoke the permission later, so this only makes sense with getting back a result to know that Intent was done
|
||||||
|
// I always move or delete the file, so I don't revoke permission
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there are apps found for our Intent to avoid that there was only our own removed app before
|
||||||
|
if (targetedIntents.isEmpty()) {
|
||||||
|
Log.d("ekkescorner", title+" targetedIntents.isEmpty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we can create our Intent with custom Chooser
|
||||||
|
// we need all collected targetedIntents as EXTRA_INITIAL_INTENTS
|
||||||
|
// we're using the last targetedIntent as initializing Intent, because
|
||||||
|
// chooser adds its initializing intent to the end of EXTRA_INITIAL_INTENTS :)
|
||||||
|
Intent chooserIntent = Intent.createChooser(targetedIntents.remove(targetedIntents.size() - 1), title);
|
||||||
|
if (targetedIntents.isEmpty()) {
|
||||||
|
Log.d("ekkescorner", title+" only one Intent left for Chooser");
|
||||||
|
} else {
|
||||||
|
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[] {}));
|
||||||
|
}
|
||||||
|
// Verify that the intent will resolve to an activity
|
||||||
|
if (chooserIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
|
||||||
|
if(requestId > 0) {
|
||||||
|
QtNative.activity().startActivityForResult(chooserIntent, requestId);
|
||||||
|
} else {
|
||||||
|
QtNative.activity().startActivity(chooserIntent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log.d("ekkescorner", title+" Chooser Intent not resolved. Should never happen");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean viewFile(String filePath, String title, String mimeType, int requestId) {
|
||||||
|
if (QtNative.activity() == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// using v4 support library create the Intent from ShareCompat
|
||||||
|
// Intent viewIntent = new Intent();
|
||||||
|
Intent viewIntent = ShareCompat.IntentBuilder.from(QtNative.activity()).getIntent();
|
||||||
|
viewIntent.setAction(Intent.ACTION_VIEW);
|
||||||
|
|
||||||
|
File imageFileToShare = new File(filePath);
|
||||||
|
|
||||||
|
// Using FileProvider you must get the URI from FileProvider using your AUTHORITY
|
||||||
|
// Uri uri = Uri.fromFile(imageFileToShare);
|
||||||
|
Uri uri;
|
||||||
|
try {
|
||||||
|
uri = FileProvider.getUriForFile(QtNative.activity(), AUTHORITY, imageFileToShare);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.d("ekkescorner viewFile - cannot be shared: ", filePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// now we got a content URI per ex
|
||||||
|
// content://org.ekkescorner.examples.sharex.fileprovider/my_shared_files/qt-logo.png
|
||||||
|
// from a fileUrl:
|
||||||
|
// /data/user/0/org.ekkescorner.examples.sharex/files/share_example_x_files/qt-logo.png
|
||||||
|
Log.d("ekkescorner viewFile from file path: ", filePath);
|
||||||
|
Log.d("ekkescorner viewFile to content URI: ", uri.toString());
|
||||||
|
|
||||||
|
if(mimeType == null || mimeType.isEmpty()) {
|
||||||
|
// fallback if mimeType not set
|
||||||
|
mimeType = QtNative.activity().getContentResolver().getType(uri);
|
||||||
|
Log.d("ekkescorner viewFile guessed mimeType:", mimeType);
|
||||||
|
} else {
|
||||||
|
Log.d("ekkescorner viewFile w mimeType:", mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewIntent.setDataAndType(uri, mimeType);
|
||||||
|
|
||||||
|
viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
viewIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
|
||||||
|
return createCustomChooserAndStartActivity(viewIntent, title, requestId, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ TARGET = fannyapp
|
||||||
ICON = shared/graphics/favicon.icns
|
ICON = shared/graphics/favicon.icns
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
sources/filehelper.cpp \
|
||||||
sources/serverconn.cpp \
|
sources/serverconn.cpp \
|
||||||
sources/main.cpp \
|
sources/main.cpp \
|
||||||
sources/appsettings.cpp \
|
sources/appsettings.cpp \
|
||||||
|
@ -29,6 +30,7 @@ SOURCES += \
|
||||||
sources/appstyle.cpp
|
sources/appstyle.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
headers/filehelper.h \
|
||||||
headers/serverconn.h \
|
headers/serverconn.h \
|
||||||
headers/appsettings.h \
|
headers/appsettings.h \
|
||||||
headers/foodplanmodel.h \
|
headers/foodplanmodel.h \
|
||||||
|
@ -68,5 +70,8 @@ ios {
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
android-sources/AndroidManifest.xml \
|
android-sources/AndroidManifest.xml \
|
||||||
CHANGELOG.md
|
android-sources/build.gradle \
|
||||||
|
CHANGELOG.md \
|
||||||
|
android-sources/src/de/itsblue/fannyapp/MainActivity.java \
|
||||||
|
android-sources/src/org/ekkescorner/utils/QShareUtils.java
|
||||||
|
|
||||||
|
|
50
headers/filehelper.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef FILEHELPER_H
|
||||||
|
#define FILEHELPER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS)
|
||||||
|
mPlatformShareUtils = new IosShareUtils(this);
|
||||||
|
#elif defined(Q_OS_ANDROID)
|
||||||
|
#include <QtAndroid>
|
||||||
|
#include <QAndroidActivityResultReceiver>
|
||||||
|
#else
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QUrl>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class FileHelper : public QObject
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
, public QAndroidActivityResultReceiver
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit FileHelper(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data);
|
||||||
|
void onActivityResult(int requestCode, int resultCode);
|
||||||
|
static FileHelper* getInstance();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if defined(Q_OS_IOS)
|
||||||
|
#elif defined(Q_OS_ANDROID)
|
||||||
|
void processActivityResult(int requestCode, int resultCode);
|
||||||
|
static FileHelper* mInstance;
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void shareEditDone(int requestCode);
|
||||||
|
void shareFinished(int requestCode);
|
||||||
|
void shareNoAppAvailable(int requestCode);
|
||||||
|
void shareError(int requestCode, QString message);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILEHELPER_H
|
|
@ -29,21 +29,17 @@
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
|
||||||
#include "headers/appsettings.h"
|
#include "headers/appsettings.h"
|
||||||
|
#include "headers/filehelper.h"
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include <QtAndroidExtras>
|
#include <QtAndroidExtras>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef struct strReturnData{
|
|
||||||
int status_code;
|
|
||||||
QString text;
|
|
||||||
}ReturnData_t;
|
|
||||||
|
|
||||||
class ServerConn : public QObject
|
class ServerConn : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString state READ getState NOTIFY stateChanged)
|
Q_PROPERTY(QString state READ getState NOTIFY stateChanged)
|
||||||
|
Q_PROPERTY(double downloadProgress READ getDownloadProgress NOTIFY downloadProgressChanged)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString state;
|
QString state;
|
||||||
|
@ -51,12 +47,18 @@ private:
|
||||||
QString username;
|
QString username;
|
||||||
QString password;
|
QString password;
|
||||||
|
|
||||||
ReturnData_t senddata(QUrl serviceUrl, QUrlQuery postData);
|
QVariantMap senddata(QUrl serviceUrl, QUrlQuery postData, bool raw = false);
|
||||||
|
|
||||||
QList<int> apiVersion = {0,2,1};
|
QList<int> apiVersion = {0,2,1};
|
||||||
|
|
||||||
|
FileHelper * fileHelper;
|
||||||
|
QString mDocumentsWorkPath;
|
||||||
|
|
||||||
|
double downloadProgress;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void setState(QString state);
|
void setState(QString state);
|
||||||
|
void updateDownloadProgress(qint64 read, qint64 total);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ServerConn(QObject *parent = nullptr);
|
explicit ServerConn(QObject *parent = nullptr);
|
||||||
|
@ -66,12 +68,15 @@ public slots:
|
||||||
Q_INVOKABLE int login(QString username, QString password, bool permanent);
|
Q_INVOKABLE int login(QString username, QString password, bool permanent);
|
||||||
Q_INVOKABLE int logout();
|
Q_INVOKABLE int logout();
|
||||||
Q_INVOKABLE int getFoodPlan();
|
Q_INVOKABLE int getFoodPlan();
|
||||||
|
Q_INVOKABLE int openEventPdf(QString day);
|
||||||
Q_INVOKABLE int getEvents(QString day);
|
Q_INVOKABLE int getEvents(QString day);
|
||||||
|
|
||||||
|
Q_INVOKABLE double getDownloadProgress();
|
||||||
Q_INVOKABLE QString getState();
|
Q_INVOKABLE QString getState();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void stateChanged(QString newState);
|
void stateChanged(QString newState);
|
||||||
|
void downloadProgressChanged();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QList<QStringList> m_weekplan;
|
QList<QStringList> m_weekplan;
|
||||||
|
|
|
@ -29,7 +29,6 @@ FannyDataListView {
|
||||||
id: foodPlanModel
|
id: foodPlanModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
delegate: Button {
|
delegate: Button {
|
||||||
id: delegate
|
id: delegate
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ ListView {
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
property int status: -1
|
property int status: -1
|
||||||
|
property var optionButtonFunction: undefined
|
||||||
|
|
||||||
signal refresh()
|
signal refresh()
|
||||||
|
|
||||||
|
@ -56,15 +57,23 @@ ListView {
|
||||||
InfoArea {
|
InfoArea {
|
||||||
id: infoArea
|
id: infoArea
|
||||||
|
|
||||||
anchors {
|
z: 0
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
anchors.fill: parent
|
||||||
top: parent.top
|
|
||||||
margins: app.landscape() ? parent.width * 0.4:parent.width * 0.3
|
|
||||||
topMargin: parent.height*( status === 901 ? 0.6:0.5) - height * 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
excludedCodes: [200, 902]
|
excludedCodes: [200, 902]
|
||||||
errorCode: control.status
|
errorCode: control.status
|
||||||
|
optionButtonFunction: control.optionButtonFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
PullRefresher{
|
||||||
|
target: control
|
||||||
|
|
||||||
|
backgroundColor: app.style.style.buttonColor
|
||||||
|
pullIndicatorColor: app.style.style.textColor
|
||||||
|
|
||||||
|
preRefreshDelay: 300
|
||||||
|
|
||||||
|
refreshPosition: height * 1.3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,44 +29,48 @@ Item {
|
||||||
property int errorCode: -1
|
property int errorCode: -1
|
||||||
property var excludedCodes: []
|
property var excludedCodes: []
|
||||||
|
|
||||||
|
property var optionButtonFunction: undefined
|
||||||
|
|
||||||
visible: !(excludedCodes.indexOf(errorCode) >= 0)
|
visible: !(excludedCodes.indexOf(errorCode) >= 0)
|
||||||
|
|
||||||
height: childrenRect.height
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
Rectangle {
|
width: parent.width * 0.8
|
||||||
|
|
||||||
radius: height * 0.5
|
|
||||||
width: parent.width
|
|
||||||
height: width
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
border.width: 5
|
|
||||||
border.color: infoArea.alertLevel > 0 ? infoArea.alertLevel > 1 ? "red":"grey" : "green"
|
|
||||||
|
|
||||||
opacity: infoArea.errorCode !== 200 && infoArea.errorCode !== (-1) ? 1:0
|
opacity: infoArea.errorCode !== 200 && infoArea.errorCode !== (-1) ? 1:0
|
||||||
|
|
||||||
Behavior on opacity {
|
spacing: 20
|
||||||
NumberAnimation {
|
|
||||||
duration: 500
|
Rectangle {
|
||||||
}
|
anchors {
|
||||||
}
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
radius: height * 0.5
|
||||||
|
width: app.landscape() ? infoArea.height * 0.4 : parent.width * 0.5
|
||||||
|
height: width
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
border.width: 5
|
||||||
|
border.color: ["green", "grey", "orange", "red"][infoArea.alertLevel]
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.pixelSize: parent.height * 0.8
|
||||||
|
text: infoArea.alertLevel > 1 ? "!":"i"
|
||||||
|
color: parent.border.color
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
font.pixelSize: parent.height * 0.8
|
|
||||||
text: infoArea.alertLevel > 1 ? "!":"i"
|
|
||||||
color: infoArea.alertLevel > 0 ? infoArea.alertLevel > 1 ? "red":"grey" : "green"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: errorShortDescription
|
id: errorShortDescription
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
top: parent.bottom
|
|
||||||
margins: parent.height * 0.1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
width: app.width * 0.8
|
width: parent.width
|
||||||
|
|
||||||
wrapMode: Label.Wrap
|
wrapMode: Label.Wrap
|
||||||
|
|
||||||
|
@ -80,11 +84,9 @@ Item {
|
||||||
id: errorLongDescription
|
id: errorLongDescription
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
top: errorShortDescription.bottom
|
|
||||||
margins: parent.height * 0.1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
width: app.width * 0.8
|
width: parent.width
|
||||||
|
|
||||||
wrapMode: Label.Wrap
|
wrapMode: Label.Wrap
|
||||||
|
|
||||||
|
@ -92,6 +94,28 @@ Item {
|
||||||
|
|
||||||
text: app.getErrorInfo(infoArea.errorCode)[2]
|
text: app.getErrorInfo(infoArea.errorCode)[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: optionButton
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: text !== "" && infoArea.optionButtonFunction !== undefined
|
||||||
|
|
||||||
|
text: app.getErrorInfo(infoArea.errorCode)[3]
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
infoArea.optionButtonFunction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 500
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
90
qml/Components/ProgressCircle.qml
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQml 2.2
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
width: size
|
||||||
|
height: size
|
||||||
|
|
||||||
|
property int size: 200 // The size of the circle in pixel
|
||||||
|
property real arcBegin: 0 // start arc angle in degree
|
||||||
|
property real arcEnd: 270 // end arc angle in degree
|
||||||
|
property real arcOffset: 0 // rotation
|
||||||
|
property bool isPie: false // paint a pie instead of an arc
|
||||||
|
property bool showBackground: false // a full circle as a background of the arc
|
||||||
|
property real lineWidth: 20 // width of the line
|
||||||
|
property string colorCircle: "#CC3333"
|
||||||
|
property string colorBackground: "#779933"
|
||||||
|
|
||||||
|
property alias beginAnimation: animationArcBegin.enabled
|
||||||
|
property alias endAnimation: animationArcEnd.enabled
|
||||||
|
|
||||||
|
property int animationDuration: 20
|
||||||
|
|
||||||
|
onArcBeginChanged: canvas.requestPaint()
|
||||||
|
onArcEndChanged: canvas.requestPaint()
|
||||||
|
|
||||||
|
Behavior on arcBegin {
|
||||||
|
id: animationArcBegin
|
||||||
|
enabled: true
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animationDuration
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on arcEnd {
|
||||||
|
id: animationArcEnd
|
||||||
|
enabled: true
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animationDuration
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: canvas
|
||||||
|
anchors.fill: parent
|
||||||
|
rotation: -90 + parent.arcOffset
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d")
|
||||||
|
var x = width / 2
|
||||||
|
var y = height / 2
|
||||||
|
var start = Math.PI * (parent.arcBegin / 180)
|
||||||
|
var end = Math.PI * (parent.arcEnd / 180)
|
||||||
|
ctx.reset()
|
||||||
|
|
||||||
|
if (root.isPie) {
|
||||||
|
if (root.showBackground) {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.fillStyle = root.colorBackground
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
ctx.arc(x, y, width / 2, 0, Math.PI * 2, false)
|
||||||
|
ctx.lineTo(x, y)
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.fillStyle = root.colorCircle
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
ctx.arc(x, y, width / 2, start, end, false)
|
||||||
|
ctx.lineTo(x, y)
|
||||||
|
ctx.fill()
|
||||||
|
} else {
|
||||||
|
if (root.showBackground) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, (width / 2) - parent.lineWidth / 2, 0, Math.PI * 2, false)
|
||||||
|
ctx.lineWidth = root.lineWidth
|
||||||
|
ctx.strokeStyle = root.colorBackground
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, (width / 2) - parent.lineWidth / 2, start, end, false)
|
||||||
|
ctx.lineWidth = root.lineWidth
|
||||||
|
ctx.strokeStyle = root.colorCircle
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
327
qml/Components/PullRefresher.qml
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
blueROCK - for digital rock
|
||||||
|
Copyright (C) 2019 Dorian Zedler
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.4
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
state: "idle"
|
||||||
|
|
||||||
|
property var target // targeted ListView
|
||||||
|
|
||||||
|
property bool autoConfigureTarget: true // should the target be automaticaly be configured?
|
||||||
|
|
||||||
|
property int postRefreshDelay: 1000 // delay after reload funcion has finished
|
||||||
|
property int preRefreshDelay: 1000 // delay before reload funcion is called
|
||||||
|
|
||||||
|
property int refreshPosition: height * 1.2 // position of the item when refreshing
|
||||||
|
property int dragOutPosition: height * 1.8 // maximum drag out
|
||||||
|
|
||||||
|
property double dragRefreshPositionMultiplier: 0.5 // position of the item when starting to refresh
|
||||||
|
|
||||||
|
property color backgroundColor: "white" // color for the pre-defined background
|
||||||
|
property color pullIndicatorColor: "black" // color for the pre-defined pull indicator
|
||||||
|
//property color busyIndicatorColor: "pink" // color for the pre-defined busy indicator
|
||||||
|
|
||||||
|
readonly property double dragProgress: Math.min( userPosition / dragOutPosition, 1)
|
||||||
|
|
||||||
|
property Component background: Item {
|
||||||
|
RectangularGlow {
|
||||||
|
anchors.fill: backgroundRe
|
||||||
|
|
||||||
|
scale: 0.8 * backgroundRe.scale
|
||||||
|
cornerRadius: backgroundRe.radius
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
glowRadius: 0.001
|
||||||
|
spread: 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: backgroundRe
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
radius: width * 0.5
|
||||||
|
color: control.backgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
property Component busyIndicator: BusyIndicator { running: true }
|
||||||
|
property Component pullIndicator: Canvas {
|
||||||
|
|
||||||
|
property double drawProgress: control.dragProgress
|
||||||
|
|
||||||
|
rotation: drawProgress > control.dragRefreshPositionMultiplier ? 180:0
|
||||||
|
|
||||||
|
onDrawProgressChanged: {
|
||||||
|
requestPaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d");
|
||||||
|
|
||||||
|
var topMargin = height * 0.1
|
||||||
|
var bottomMargin = topMargin
|
||||||
|
var rightMargin = 0
|
||||||
|
var leftMargin = 0
|
||||||
|
|
||||||
|
var arrowHeight = height - topMargin - bottomMargin
|
||||||
|
|
||||||
|
var peakHeight = arrowHeight * 0.35
|
||||||
|
var peakWidth = peakHeight
|
||||||
|
|
||||||
|
var lineWidth = 2
|
||||||
|
|
||||||
|
var progress = drawProgress * 1 / control.dragRefreshPositionMultiplier > 1 ? 1 : drawProgress * 1 / control.dragRefreshPositionMultiplier
|
||||||
|
// modify all values to math the progress
|
||||||
|
|
||||||
|
arrowHeight = arrowHeight * progress
|
||||||
|
if(progress > 0.3){
|
||||||
|
peakHeight = peakHeight * (progress - 0.3) * 1/0.7
|
||||||
|
peakWidth = peakWidth * (progress - 0.3) * 1/0.7
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
peakHeight = 0
|
||||||
|
peakWidth = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear canvas
|
||||||
|
ctx.reset()
|
||||||
|
|
||||||
|
ctx.lineWidth = lineWidth;
|
||||||
|
ctx.strokeStyle = control.pullIndicatorColor;
|
||||||
|
|
||||||
|
// middle line
|
||||||
|
ctx.moveTo(width/2, topMargin);
|
||||||
|
ctx.lineTo(width/2, arrowHeight + topMargin);
|
||||||
|
|
||||||
|
// right line
|
||||||
|
ctx.moveTo(width/2 - lineWidth * 0.3, arrowHeight + topMargin);
|
||||||
|
ctx.lineTo(width/2 + peakWidth,arrowHeight + topMargin - peakHeight);
|
||||||
|
// left line
|
||||||
|
ctx.moveTo(width/2 + lineWidth * 0.3, arrowHeight + topMargin);
|
||||||
|
ctx.lineTo(width/2 - peakWidth,arrowHeight + topMargin - peakHeight);
|
||||||
|
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on rotation {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
signal refreshRequested
|
||||||
|
|
||||||
|
// internal properties
|
||||||
|
property int minimumPosition: 0
|
||||||
|
property int maximumPosition: 0
|
||||||
|
property int userPosition: 0
|
||||||
|
property int position: Math.max( minimumPosition, Math.min(maximumPosition, userPosition))
|
||||||
|
|
||||||
|
height: 50
|
||||||
|
width: height
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if(control.autoConfigureTarget){
|
||||||
|
target.boundsBehavior = Flickable.DragOverBounds
|
||||||
|
target.boundsMovement = Flickable.StopAtBounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
control.refreshRequested()
|
||||||
|
postRefreshTimer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: control.target.top
|
||||||
|
horizontalCenter: control.target.horizontalCenter
|
||||||
|
topMargin: control.position - height
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: control.target
|
||||||
|
onDragEnded: {
|
||||||
|
if(userPosition >= control.dragOutPosition * control.dragRefreshPositionMultiplier){
|
||||||
|
control.state = "refreshing"
|
||||||
|
preRefreshTimer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: backgroundLd
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
sourceComponent: control.background
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: pullIndicatorLd
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
height: parent.height * 0.6
|
||||||
|
width: height
|
||||||
|
|
||||||
|
rotation: 180
|
||||||
|
|
||||||
|
sourceComponent: control.pullIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: busyIndicatorLd
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
height: parent.height * 0.7
|
||||||
|
width: height
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
sourceComponent: control.busyIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: preRefreshTimer
|
||||||
|
interval: control.preRefreshDelay <= 0 ? 1:control.preRefreshDelay
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
control.refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: postRefreshTimer
|
||||||
|
interval: control.postRefreshDelay <= 0 ? 1:control.postRefreshDelay
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
control.state = "hidden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on minimumPosition {
|
||||||
|
enabled: !control.target.dragging && state !== "idle"
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "idle"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
minimumPosition: userPosition > maximumPosition ? maximumPosition:userPosition
|
||||||
|
userPosition: -1 / (Math.abs( (target.verticalOvershoot > 0 ? 0:target.verticalOvershoot) * 0.001 + 0.003 ) + 1 / control.dragOutPosition * 0.001) + control.dragOutPosition // Math.abs( target.verticalOvershoot )
|
||||||
|
maximumPosition: control.dragOutPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: pullIndicatorLd
|
||||||
|
rotation: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "refreshing"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
minimumPosition: control.refreshPosition
|
||||||
|
userPosition: 0
|
||||||
|
maximumPosition: control.refreshPosition
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: pullIndicatorLd
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: busyIndicatorLd
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "hidden"
|
||||||
|
PropertyChanges {
|
||||||
|
target: control
|
||||||
|
minimumPosition: control.refreshPosition
|
||||||
|
userPosition: 0
|
||||||
|
maximumPosition: control.refreshPosition
|
||||||
|
scale: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: pullIndicatorLd
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: busyIndicatorLd
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 100
|
||||||
|
properties: "rotation, opacity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Transition {
|
||||||
|
from: "refreshing"
|
||||||
|
to: "hidden"
|
||||||
|
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 200
|
||||||
|
properties: "scale"
|
||||||
|
}
|
||||||
|
|
||||||
|
onRunningChanged: {
|
||||||
|
if(control.state === "hidden" && !running){
|
||||||
|
control.state = "idle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Transition {
|
||||||
|
from: "hidden"
|
||||||
|
to: "idle"
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
|
@ -17,8 +17,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
|
import Backend 1.0
|
||||||
import QtQuick.Controls 2.2
|
import QtQuick.Controls 2.2
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
import QtQuick.Controls.Material 2.0
|
||||||
import "../Components"
|
import "../Components"
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
|
@ -35,56 +37,169 @@ Page {
|
||||||
|
|
||||||
onOpened: {}
|
onOpened: {}
|
||||||
|
|
||||||
|
function pdfAction() {
|
||||||
|
busyDialog.open()
|
||||||
|
serverConn.openEventPdf(day)
|
||||||
|
busyDialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: pageLoader
|
id: pageLoader
|
||||||
|
|
||||||
property string newSource: ""
|
property var newSourceComponent
|
||||||
|
|
||||||
onNewSourceChanged: {
|
anchors.fill: parent
|
||||||
|
sourceComponent: loadingFormComp
|
||||||
|
|
||||||
|
onNewSourceComponentChanged: {
|
||||||
oldItemAnimation.start()
|
oldItemAnimation.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
onSourceComponentChanged: {
|
||||||
source: "./LoadingForm.qml"
|
if(pageLoader.item !== null) {
|
||||||
|
pageLoader.item.status = root.status
|
||||||
onSourceChanged: {
|
newItemAnimation.start()
|
||||||
pageLoader.item.status = root.status
|
}
|
||||||
newItemAnimation.start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
|
||||||
|
|
||||||
|
ParallelAnimation {
|
||||||
id: newItemAnimation
|
id: newItemAnimation
|
||||||
target: pageLoader.item
|
|
||||||
property: "opacity"
|
NumberAnimation {
|
||||||
from: 0
|
target: pageLoader.item
|
||||||
to: 1
|
property: "opacity"
|
||||||
duration: 200
|
from: 0
|
||||||
easing.type: Easing.InExpo
|
to: 1
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InExpo
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
target: pageLoader.item
|
||||||
|
property: "scale"
|
||||||
|
from: 0.98
|
||||||
|
to: 1
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InExpo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
ParallelAnimation {
|
||||||
id: oldItemAnimation
|
id: oldItemAnimation
|
||||||
target: pageLoader.item
|
|
||||||
property: "opacity"
|
NumberAnimation {
|
||||||
from: 1
|
target: pageLoader.item
|
||||||
to: 0
|
property: "opacity"
|
||||||
duration: 200
|
from: 1
|
||||||
easing.type: Easing.InExpo
|
to: 0
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.InExpo
|
||||||
|
}
|
||||||
|
|
||||||
onRunningChanged: {
|
onRunningChanged: {
|
||||||
if(!running){
|
if(!running){
|
||||||
pageLoader.source = pageLoader.newSource
|
pageLoader.sourceComponent = pageLoader.newSourceComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Component {
|
||||||
target: pageLoader.item
|
id: eventListComp
|
||||||
onRefresh: {
|
|
||||||
pageLoader.newSource = "./LoadingForm.qml"
|
FannyDataListView {
|
||||||
loadTimer.start()
|
id: eventList
|
||||||
|
|
||||||
|
status: 900
|
||||||
|
|
||||||
|
optionButtonFunction: function() {
|
||||||
|
busyDialog.open()
|
||||||
|
serverConn.openEventPdf(day)
|
||||||
|
busyDialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
model: EventModel {
|
||||||
|
id: foodPlanModel
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Button {
|
||||||
|
id: delegate
|
||||||
|
|
||||||
|
width: eventList.width
|
||||||
|
height: contentCol.height + 10
|
||||||
|
|
||||||
|
z: 100
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: contentCol
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
height: childrenRect.height + 10
|
||||||
|
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: gradeLa
|
||||||
|
// label for the grade
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: hourReplaceSubjectRoomLa.font.pixelSize * 1.5
|
||||||
|
|
||||||
|
width: parent.width - 10
|
||||||
|
wrapMode: Label.Wrap
|
||||||
|
|
||||||
|
text: grade
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: hourReplaceSubjectRoomLa
|
||||||
|
// label for the hour, replacement, subject and room
|
||||||
|
|
||||||
|
width: parent.width - 10
|
||||||
|
wrapMode: Label.Wrap
|
||||||
|
|
||||||
|
text: hour + ( replace === "" ? "": ( " | "
|
||||||
|
+ replace + ( subject === "" ? "": ( " | "
|
||||||
|
+ subject + ( room === "" ? "": ( " | "
|
||||||
|
+ room ) ) ) ) ) )
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: toTextLa
|
||||||
|
// label for the new room (to) and the additional text (text)
|
||||||
|
|
||||||
|
width: parent.width - 10
|
||||||
|
wrapMode: Label.Wrap
|
||||||
|
|
||||||
|
font.pixelSize: gradeLa.font.pixelSize
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
visible: text !== ""
|
||||||
|
|
||||||
|
text: to !== "" && model.text !== "" ? to + " | " + model.text:model.to + model.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: loadingFormComp
|
||||||
|
LoadingForm {}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
|
@ -94,7 +209,47 @@ Page {
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.status = serverConn.getEvents(day)
|
root.status = serverConn.getEvents(day)
|
||||||
pageLoader.newSource = "../Components/EventView.qml"
|
pageLoader.newSourceComponent = eventListComp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: busyDialog
|
||||||
|
|
||||||
|
parent: overlay
|
||||||
|
|
||||||
|
x: Math.round((parent.width - width) / 2)
|
||||||
|
y: Math.round((parent.height - height) / 2)
|
||||||
|
|
||||||
|
height: contentHeight * 1.5
|
||||||
|
width: contentWidth * 1.5
|
||||||
|
contentHeight: progressCircle.height
|
||||||
|
contentWidth: progressCircle.width
|
||||||
|
|
||||||
|
Material.theme: root.Material.theme
|
||||||
|
|
||||||
|
modal: true
|
||||||
|
closePolicy: "NoAutoClose"
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
ProgressCircle {
|
||||||
|
id: progressCircle
|
||||||
|
size: 50
|
||||||
|
lineWidth: 5
|
||||||
|
anchors.centerIn: parent
|
||||||
|
colorCircle: Material.theme === Material.Dark ? "#F48FB1":"#E91E63"
|
||||||
|
colorBackground: "transparent"
|
||||||
|
showBackground: true
|
||||||
|
arcBegin: 0
|
||||||
|
arcEnd: 360 * serverConn.downloadProgress
|
||||||
|
animationDuration: 0
|
||||||
|
Label {
|
||||||
|
id: progress
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: Math.round( serverConn.downloadProgress * 100 ) + "%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,14 +75,6 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: pageLoader.item
|
|
||||||
onRefresh: {
|
|
||||||
pageLoader.newSource = "./LoadingForm.qml"
|
|
||||||
loadTimer.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
|
|
|
@ -140,14 +140,17 @@ Page {
|
||||||
rightMargin: root.width * 0.05
|
rightMargin: root.width * 0.05
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
ToolButton{
|
||||||
id: passwordHideShow
|
id: passwordHideShow
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
right: parent.right
|
right: parent.right
|
||||||
}
|
}
|
||||||
width: visibleIcon.width
|
|
||||||
|
icon.height: parent.height * 0.5
|
||||||
|
icon.width: parent.height * 0.5
|
||||||
|
icon.color: app.style.style.textColor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if(state === "visible"){
|
if(state === "visible"){
|
||||||
|
@ -164,13 +167,10 @@ Page {
|
||||||
State {
|
State {
|
||||||
name: "invisible"
|
name: "invisible"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: visibleIcon
|
target: passwordHideShow
|
||||||
scale: 0
|
icon.name: "hide"
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: invisibleIcon
|
|
||||||
scale: 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: tipasswd
|
target: tipasswd
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
|
@ -179,12 +179,8 @@ Page {
|
||||||
State {
|
State {
|
||||||
name: "visible"
|
name: "visible"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: visibleIcon
|
target: passwordHideShow
|
||||||
scale: 1
|
icon.name: "view"
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: invisibleIcon
|
|
||||||
scale: 0
|
|
||||||
}
|
}
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: tipasswd
|
target: tipasswd
|
||||||
|
@ -192,39 +188,8 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
Image {
|
|
||||||
id: visibleIcon
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
right: parent.right
|
|
||||||
|
|
||||||
bottomMargin: parent.height * 0.25
|
|
||||||
topMargin: anchors.bottomMargin
|
|
||||||
}
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
smooth: true
|
|
||||||
source: "qrc:/graphics/icons/view.png"
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: invisibleIcon
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
right: parent.right
|
|
||||||
|
|
||||||
bottomMargin: parent.height * 0.25
|
|
||||||
topMargin: anchors.bottomMargin
|
|
||||||
}
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
smooth: true
|
|
||||||
source: "qrc:/graphics/icons/hide.png"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckDelegate {
|
CheckDelegate {
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.1
|
import QtQuick.Controls 2.2
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ Page {
|
||||||
id: root
|
id: root
|
||||||
objectName: "MainPage"
|
objectName: "MainPage"
|
||||||
|
|
||||||
/*Shortcut {
|
Shortcut {
|
||||||
sequences: ["Esc", "Back"]
|
sequences: ["Esc", "Back"]
|
||||||
enabled: formStack.depth > 1
|
enabled: formStack.depth > 1
|
||||||
onActivated: {
|
onActivated: {
|
||||||
|
@ -36,7 +36,7 @@ Page {
|
||||||
formStack.pop()
|
formStack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -149,6 +149,10 @@ Page {
|
||||||
ToolButton {
|
ToolButton {
|
||||||
id: toolButton
|
id: toolButton
|
||||||
|
|
||||||
|
enabled: !formStack.currentItem.locked
|
||||||
|
|
||||||
|
opacity: enabled ? 1:0.5
|
||||||
|
|
||||||
icon.name: "back"
|
icon.name: "back"
|
||||||
icon.color: app.style.style.textColor
|
icon.color: app.style.style.textColor
|
||||||
|
|
||||||
|
@ -182,6 +186,24 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
id: pdfToolButton
|
||||||
|
|
||||||
|
enabled: !formStack.currentItem.locked
|
||||||
|
|
||||||
|
opacity: enabled ? 1:0.5
|
||||||
|
|
||||||
|
visible: formStack.currentItem.title === "Vertretungsplan"
|
||||||
|
|
||||||
|
icon.name: "pdf"
|
||||||
|
icon.color: app.style.style.textColor
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if(formStack.currentItem.pdfAction !== undefined) {
|
||||||
|
formStack.currentItem.pdfAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on anchors.topMargin {
|
Behavior on anchors.topMargin {
|
||||||
|
@ -211,3 +233,5 @@ Page {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
25
qml/main.qml
|
@ -115,31 +115,34 @@ ApplicationWindow {
|
||||||
var infoLevel
|
var infoLevel
|
||||||
// 0 - ok
|
// 0 - ok
|
||||||
// 1 - info
|
// 1 - info
|
||||||
// 2 - error
|
// 2 - warn
|
||||||
|
// 3 - error
|
||||||
|
|
||||||
var errorString
|
var errorString
|
||||||
var errorDescription
|
var errorDescription
|
||||||
|
var errorButtonOption = ""
|
||||||
|
|
||||||
switch(errorCode) {
|
switch(errorCode) {
|
||||||
case 0:
|
case 0:
|
||||||
infoLevel = 2
|
infoLevel = 3
|
||||||
errorString = "Keine Verbindung zum Server"
|
errorString = "Keine Verbindung zum Server"
|
||||||
errorDescription = "Bitte überprüfe deine Internetverbindung und versuche es erneut."
|
errorDescription = "Bitte überprüfe deine Internetverbindung und versuche es erneut."
|
||||||
break
|
break
|
||||||
case 401:
|
case 401:
|
||||||
infoLevel = 2
|
infoLevel = 3
|
||||||
errorString = "Ungültige Zugangsdaten"
|
errorString = "Ungültige Zugangsdaten"
|
||||||
errorDescription = "Der Server hat den Zugang verweigert, bitte überprüfe deine Zugangsdaten und versuche es erneut"
|
errorDescription = "Der Server hat den Zugang verweigert, bitte überprüfe deine Zugangsdaten und versuche es erneut"
|
||||||
break
|
break
|
||||||
case 500:
|
case 500:
|
||||||
infoLevel = 2
|
infoLevel = 3
|
||||||
errorString = "Interner Server Fehler"
|
errorString = "Interner Server Fehler"
|
||||||
errorDescription = "Scheinbar kann der Server die Anfrage im Moment nicht verarbeiten, bitte versuche es später erneut."
|
errorDescription = "Scheinbar kann der Server die Anfrage im Moment nicht verarbeiten, bitte versuche es später erneut."
|
||||||
break
|
break
|
||||||
case 900:
|
case 900:
|
||||||
infoLevel = 2
|
infoLevel = 2
|
||||||
errorString = "Interner Verarbeitungsfehler"
|
errorString = "Verarbeitungsfehler"
|
||||||
errorDescription = "Die Daten, die vom Server übertragen wurden, konnten nicht richtig verarbeitet werden, bitte versuche es später erneut."
|
errorDescription = "Die Daten, die vom Server übertragen wurden, konnten nicht richtig verarbeitet werden, bitte versuche es später erneut."
|
||||||
|
errorButtonOption = "Als Pdf ansehen"
|
||||||
break
|
break
|
||||||
case 901:
|
case 901:
|
||||||
infoLevel = 1
|
infoLevel = 1
|
||||||
|
@ -157,17 +160,23 @@ ApplicationWindow {
|
||||||
errorDescription = "Die aufgerufene Funktion ist momentan nicht verfügbar, bitte versuche es später erneut."
|
errorDescription = "Die aufgerufene Funktion ist momentan nicht verfügbar, bitte versuche es später erneut."
|
||||||
break
|
break
|
||||||
case 904:
|
case 904:
|
||||||
infoLevel = 2
|
infoLevel = 3
|
||||||
errorString = "Inkompatible API"
|
errorString = "Inkompatible API"
|
||||||
errorDescription = "Die Version der API auf dem Server ist zu neu und kann daher nicht verarbeitet werden. Bitte aktualisiere die App auf die aktuellste Version."
|
errorDescription = "Die Version der API auf dem Server ist zu neu und kann daher nicht verarbeitet werden. Bitte aktualisiere die App auf die aktuellste Version."
|
||||||
|
errorButtonOption = "Als Pdf ansehen"
|
||||||
|
break
|
||||||
|
case 905:
|
||||||
|
infoLevel = 3
|
||||||
|
errorString = "Interner Speicherfehler"
|
||||||
|
errorDescription = "Die Pdf-Datei konnte nicht gespeichert werden."
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
infoLevel = 2
|
infoLevel = 3
|
||||||
errorString = "Unerwarteter Fehler ("+errorCode+")"
|
errorString = "Unerwarteter Fehler ("+errorCode+")"
|
||||||
errorDescription = "Unbekannter Fehler bei der Verbindung mit dem Server."
|
errorDescription = "Unbekannter Fehler bei der Verbindung mit dem Server."
|
||||||
}
|
}
|
||||||
|
|
||||||
return([infoLevel, errorString, errorDescription])
|
return([infoLevel, errorString, errorDescription, errorButtonOption])
|
||||||
}
|
}
|
||||||
|
|
||||||
function landscape(){
|
function landscape(){
|
||||||
|
|
|
@ -16,5 +16,7 @@
|
||||||
<file>Forms/FilterForm.qml</file>
|
<file>Forms/FilterForm.qml</file>
|
||||||
<file>Components/SettingsDelegate.qml</file>
|
<file>Components/SettingsDelegate.qml</file>
|
||||||
<file>Components/FannyDataListView.qml</file>
|
<file>Components/FannyDataListView.qml</file>
|
||||||
|
<file>Components/ProgressCircle.qml</file>
|
||||||
|
<file>Components/PullRefresher.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
Before Width: | Height: | Size: 776 B |
BIN
shared/icons/ibmaterial/20x20/hide.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
shared/icons/ibmaterial/20x20/pdf.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
shared/icons/ibmaterial/20x20/view.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
shared/icons/ibmaterial/20x20@2/hide.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
shared/icons/ibmaterial/20x20@2/pdf.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
shared/icons/ibmaterial/20x20@2/view.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
shared/icons/ibmaterial/20x20@3/hide.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
shared/icons/ibmaterial/20x20@3/pdf.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
shared/icons/ibmaterial/20x20@3/view.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
shared/icons/ibmaterial/20x20@4/hide.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
shared/icons/ibmaterial/20x20@4/pdf.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
shared/icons/ibmaterial/20x20@4/view.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
|
@ -72,7 +72,6 @@
|
||||||
<file>icons/ibmaterial/index.theme</file>
|
<file>icons/ibmaterial/index.theme</file>
|
||||||
<file>icons/back.png</file>
|
<file>icons/back.png</file>
|
||||||
<file>icons/backDark.png</file>
|
<file>icons/backDark.png</file>
|
||||||
<file>icons/delete.png</file>
|
|
||||||
<file>icons/hide.png</file>
|
<file>icons/hide.png</file>
|
||||||
<file>icons/logoutBlack.png</file>
|
<file>icons/logoutBlack.png</file>
|
||||||
<file>icons/logoutRed.png</file>
|
<file>icons/logoutRed.png</file>
|
||||||
|
@ -86,5 +85,18 @@
|
||||||
<file>icons/ibmaterial/20x20@2/delete.png</file>
|
<file>icons/ibmaterial/20x20@2/delete.png</file>
|
||||||
<file>icons/ibmaterial/20x20@3/delete.png</file>
|
<file>icons/ibmaterial/20x20@3/delete.png</file>
|
||||||
<file>icons/ibmaterial/20x20@4/delete.png</file>
|
<file>icons/ibmaterial/20x20@4/delete.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20/pdf.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@2/pdf.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@3/pdf.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@4/pdf.png</file>
|
||||||
|
<file>graphics/Splashscreen.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20/hide.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20/view.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@2/hide.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@2/view.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@3/hide.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@3/view.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@4/hide.png</file>
|
||||||
|
<file>icons/ibmaterial/20x20@4/view.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
110
sources/filehelper.cpp
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#include "../headers/filehelper.h"
|
||||||
|
|
||||||
|
#if defined(Q_OS_IOS)
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include <QtAndroidExtras/QAndroidJniObject>
|
||||||
|
#include <jni.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FileHelper::FileHelper(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_IOS)
|
||||||
|
#elif defined(Q_OS_ANDROID)
|
||||||
|
mInstance = this;
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileHelper::viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_IOS)
|
||||||
|
#elif defined(Q_OS_ANDROID)
|
||||||
|
QAndroidJniObject jsPath = QAndroidJniObject::fromString(filePath);
|
||||||
|
QAndroidJniObject jsTitle = QAndroidJniObject::fromString(title);
|
||||||
|
QAndroidJniObject jsMimeType = QAndroidJniObject::fromString(mimeType);
|
||||||
|
jboolean ok = QAndroidJniObject::callStaticMethod<jboolean>("org/ekkescorner/utils/QShareUtils",
|
||||||
|
"viewFile",
|
||||||
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Z",
|
||||||
|
jsPath.object<jstring>(), jsTitle.object<jstring>(), jsMimeType.object<jstring>(), requestId);
|
||||||
|
if(!ok) {
|
||||||
|
qWarning() << "Unable to resolve activity from Java";
|
||||||
|
emit shareNoAppAvailable(requestId);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Q_UNUSED(title)
|
||||||
|
Q_UNUSED(mimeType)
|
||||||
|
Q_UNUSED(requestId)
|
||||||
|
|
||||||
|
QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(Q_OS_ANDROID)
|
||||||
|
const static int RESULT_OK = -1;
|
||||||
|
const static int RESULT_CANCELED = 0;
|
||||||
|
|
||||||
|
FileHelper* FileHelper::mInstance = nullptr;
|
||||||
|
|
||||||
|
FileHelper* FileHelper::getInstance()
|
||||||
|
{
|
||||||
|
if (!mInstance) {
|
||||||
|
mInstance = new FileHelper;
|
||||||
|
qWarning() << "AndroidShareUtils should be instantiated !";
|
||||||
|
}
|
||||||
|
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// used from QAndroidActivityResultReceiver
|
||||||
|
void FileHelper::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(data)
|
||||||
|
qDebug() << "From JNI QAndroidActivityResultReceiver: " << receiverRequestCode << "ResultCode:" << resultCode;
|
||||||
|
processActivityResult(receiverRequestCode, resultCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// used from Activity.java onActivityResult()
|
||||||
|
void FileHelper::onActivityResult(int requestCode, int resultCode)
|
||||||
|
{
|
||||||
|
qDebug() << "From Java Activity onActivityResult: " << requestCode << "ResultCode:" << resultCode;
|
||||||
|
processActivityResult(requestCode, resultCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileHelper::processActivityResult(int requestCode, int resultCode)
|
||||||
|
{
|
||||||
|
// we're getting RESULT_OK only if edit is done
|
||||||
|
if(resultCode == RESULT_OK) {
|
||||||
|
emit shareEditDone(requestCode);
|
||||||
|
} else if(resultCode == RESULT_CANCELED) {
|
||||||
|
emit shareFinished(requestCode);
|
||||||
|
} else {
|
||||||
|
qDebug() << "wrong result code: " << resultCode << " from request: " << requestCode;
|
||||||
|
emit shareError(requestCode, tr("Share: an Error occured"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_de_itsblue_fannyapp_MainActivity_fireActivityResult(JNIEnv *env,
|
||||||
|
jobject obj,
|
||||||
|
jint requestCode,
|
||||||
|
jint resultCode)
|
||||||
|
{
|
||||||
|
Q_UNUSED (obj)
|
||||||
|
Q_UNUSED (env)
|
||||||
|
FileHelper::getInstance()->onActivityResult(requestCode, resultCode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
#endif //defined(Q_OS_ANDROID)
|
|
@ -26,6 +26,27 @@ ServerConn::ServerConn(QObject *parent) : QObject(parent)
|
||||||
qDebug("+----- ServerConn constructor -----+");
|
qDebug("+----- ServerConn constructor -----+");
|
||||||
pGlobalServConn = this;
|
pGlobalServConn = this;
|
||||||
|
|
||||||
|
this->fileHelper = new FileHelper();
|
||||||
|
connect(this->fileHelper, &FileHelper::shareError, [=](){qWarning() << "share error";});
|
||||||
|
connect(this->fileHelper, &FileHelper::shareFinished, [=](){qWarning() << "share finished";});
|
||||||
|
connect(this->fileHelper, &FileHelper::shareNoAppAvailable, [=](){qWarning() << "share no app available";});
|
||||||
|
|
||||||
|
// get local work path
|
||||||
|
#if defined (Q_OS_IOS)
|
||||||
|
QString docLocationRoot = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0);
|
||||||
|
qDebug() << "iOS: QStandardPaths::DocumentsLocation: " << docLocationRoot;
|
||||||
|
#elif defined(Q_OS_ANDROID)
|
||||||
|
QString docLocationRoot = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).value(0);
|
||||||
|
#else
|
||||||
|
QString docLocationRoot = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).value(0);
|
||||||
|
#endif
|
||||||
|
mDocumentsWorkPath = docLocationRoot.append("/tmp_pdf_files");
|
||||||
|
if (!QDir(mDocumentsWorkPath).exists()) {
|
||||||
|
if (!QDir("").mkpath(mDocumentsWorkPath)) {
|
||||||
|
pGlobalAppSettings->writeSetting("localDocPathError", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check login state
|
// check login state
|
||||||
int perm = pGlobalAppSettings->loadSetting("permanent").toInt();
|
int perm = pGlobalAppSettings->loadSetting("permanent").toInt();
|
||||||
qDebug() << "+----- login state: " << perm << " -----+";
|
qDebug() << "+----- login state: " << perm << " -----+";
|
||||||
|
@ -51,9 +72,9 @@ int ServerConn::login(QString username, QString password, bool permanent)
|
||||||
pdata.addQueryItem("password", password);
|
pdata.addQueryItem("password", password);
|
||||||
|
|
||||||
// send the request
|
// send the request
|
||||||
ReturnData_t ret = this->senddata(QUrl("http://www.fanny-leicht.de/j34/templates/g5_helium/intern/events.php"), pdata);
|
QVariantMap ret = this->senddata(QUrl("http://www.fanny-leicht.de/j34/templates/g5_helium/intern/events.php"), pdata);
|
||||||
|
|
||||||
if(ret.status_code == 200){
|
if(ret["status"].toInt() == 200){
|
||||||
// if not 200 was returned -> user data was correct
|
// if not 200 was returned -> user data was correct
|
||||||
// store username and password in the class variables
|
// store username and password in the class variables
|
||||||
this->username = username;
|
this->username = username;
|
||||||
|
@ -81,7 +102,7 @@ int ServerConn::login(QString username, QString password, bool permanent)
|
||||||
pGlobalAppSettings->writeSetting("permanent", "0");
|
pGlobalAppSettings->writeSetting("permanent", "0");
|
||||||
pGlobalAppSettings->writeSetting("username", "");
|
pGlobalAppSettings->writeSetting("username", "");
|
||||||
pGlobalAppSettings->writeSetting("password", "");
|
pGlobalAppSettings->writeSetting("password", "");
|
||||||
return(ret.status_code);
|
return(ret["status"].toInt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,19 +140,19 @@ int ServerConn::getEvents(QString day)
|
||||||
pdata.addQueryItem("day", day);
|
pdata.addQueryItem("day", day);
|
||||||
|
|
||||||
// send the request
|
// send the request
|
||||||
ReturnData_t ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/templates/g5_helium/intern/events.php"), pdata);
|
QVariantMap ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/templates/g5_helium/intern/events.php"), pdata);
|
||||||
|
|
||||||
if(ret.status_code != 200){
|
if(ret["status"].toInt() != 200){
|
||||||
// if the request didn't result in a success, clear the old events, as they are probaply incorrect and return the error code
|
// if the request didn't result in a success, clear the old events, as they are probaply incorrect and return the error code
|
||||||
this->m_events.clear();
|
this->m_events.clear();
|
||||||
|
|
||||||
if(ret.status_code == 401){
|
if(ret["status"].toInt() == 401){
|
||||||
// if the stats code is 401 -> userdata is incorrect
|
// if the stats code is 401 -> userdata is incorrect
|
||||||
qDebug() << "+----- checkconn: user data is incorrect -----+";
|
qDebug() << "+----- checkconn: user data is incorrect -----+";
|
||||||
logout();
|
logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret.status_code;
|
return ret["status"].toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -152,7 +173,7 @@ int ServerConn::getEvents(QString day)
|
||||||
QStringList tmpEventHeader;
|
QStringList tmpEventHeader;
|
||||||
|
|
||||||
//qDebug() << jsonString;
|
//qDebug() << jsonString;
|
||||||
QJsonDocument jsonFilters = QJsonDocument::fromJson(ret.text.toUtf8());
|
QJsonDocument jsonFilters = QJsonDocument::fromJson(ret["text"].toString().toUtf8());
|
||||||
|
|
||||||
// array with tghe whole response in it
|
// array with tghe whole response in it
|
||||||
QJsonObject JsonArray = jsonFilters.object();
|
QJsonObject JsonArray = jsonFilters.object();
|
||||||
|
@ -230,15 +251,68 @@ int ServerConn::getEvents(QString day)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if there is any valid data
|
// check if there is any valid data
|
||||||
if(tmpEvents.length() < 3){
|
if(tmpEvents.length() == 0 || tmpEvents.length() == 1) {
|
||||||
|
// no data was delivered at all -> the server encountered a parse error
|
||||||
|
tmpEvents.clear();
|
||||||
|
ret["status"].setValue(900);
|
||||||
|
}
|
||||||
|
else if(tmpEvents.length() == 2) {
|
||||||
// remove the last (in this case the second) element, as it is unnecessary (it is the legend -> not needed when there is no data)
|
// remove the last (in this case the second) element, as it is unnecessary (it is the legend -> not needed when there is no data)
|
||||||
tmpEvents.takeLast();
|
tmpEvents.takeLast();
|
||||||
// set return code to 'no data' (901)
|
// set return code to 'no data' (901)
|
||||||
ret.status_code = 901;
|
ret["status"].setValue(901);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_events = tmpEvents;
|
this->m_events = tmpEvents;
|
||||||
return(ret.status_code);
|
return(ret["status"].toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
int ServerConn::openEventPdf(QString day) {
|
||||||
|
// day: 0-today; 1-tomorrow
|
||||||
|
if(this->state != "loggedIn"){
|
||||||
|
return 401;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pGlobalAppSettings->loadSetting("localDocPathError") == "true") {
|
||||||
|
// we have no local document path to work with -> this is not going to work!
|
||||||
|
return 905;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the data to the request
|
||||||
|
QUrlQuery pdata;
|
||||||
|
pdata.addQueryItem("username", this->username);
|
||||||
|
pdata.addQueryItem("password", this->password);
|
||||||
|
pdata.addQueryItem("mode", pGlobalAppSettings->loadSetting("teacherMode") == "true" ? "1":"0");
|
||||||
|
pdata.addQueryItem("day", day);
|
||||||
|
pdata.addQueryItem("asPdf", "true");
|
||||||
|
|
||||||
|
// send the request
|
||||||
|
QVariantMap ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/templates/g5_helium/intern/events.php"), pdata, true);
|
||||||
|
|
||||||
|
if(ret["status"].toInt() != 200){
|
||||||
|
// if the request didn't result in a success, clear the old events, as they are probaply incorrect and return the error code
|
||||||
|
this->m_events.clear();
|
||||||
|
|
||||||
|
if(ret["status"].toInt() == 401){
|
||||||
|
// if the stats code is 401 -> userdata is incorrect
|
||||||
|
qDebug() << "+----- checkconn: user data is incorrect -----+";
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret["status"].toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||||
|
QString filname = (QString(pGlobalAppSettings->loadSetting("teacherMode") == "true" ? "l":"s") + QString(day == "0" ? "heute":"morgen" ));
|
||||||
|
QFile file(mDocumentsWorkPath + "/" + filname + ".pdf");
|
||||||
|
file.remove();
|
||||||
|
file.open(QIODevice::ReadWrite);
|
||||||
|
file.write(ret["data"].toByteArray());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
this->fileHelper->viewFile(mDocumentsWorkPath + "/" + filname + ".pdf", "SMorgen", "application/pdf", 1);
|
||||||
|
|
||||||
|
return 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ServerConn::getFoodPlan()
|
int ServerConn::getFoodPlan()
|
||||||
|
@ -255,18 +329,18 @@ int ServerConn::getFoodPlan()
|
||||||
|
|
||||||
QUrlQuery pdata;
|
QUrlQuery pdata;
|
||||||
// send the request to the server
|
// send the request to the server
|
||||||
ReturnData_t ret = this->senddata(QUrl(url), pdata);
|
QVariantMap ret = this->senddata(QUrl(url), pdata);
|
||||||
|
|
||||||
if(ret.status_code != 200){
|
if(ret["status"].toInt() != 200){
|
||||||
// if the request didn't result in a success, return the error code
|
// if the request didn't result in a success, return the error code
|
||||||
|
|
||||||
// if the request failed but there is still old data available
|
// if the request failed but there is still old data available
|
||||||
if(!this->m_weekplan.isEmpty()){
|
if(!this->m_weekplan.isEmpty()){
|
||||||
// set the status code to 902 (old data)
|
// set the status code to 902 (old data)
|
||||||
ret.status_code = 902;
|
ret["status"].setValue(902);
|
||||||
}
|
}
|
||||||
|
|
||||||
return(ret.status_code);
|
return(ret["status"].toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
// list to be returned
|
// list to be returned
|
||||||
|
@ -274,8 +348,8 @@ int ServerConn::getFoodPlan()
|
||||||
QList<QStringList> tmpWeekplan;
|
QList<QStringList> tmpWeekplan;
|
||||||
|
|
||||||
//qDebug() << jsonString;
|
//qDebug() << jsonString;
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(ret.text.toUtf8());
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(ret["text"].toString().toUtf8());
|
||||||
//qDebug() << ret.text;
|
//qDebug() << ret["text"].toString();
|
||||||
// array with the whole response in it
|
// array with the whole response in it
|
||||||
QJsonArray foodplanDays = jsonDoc.array();
|
QJsonArray foodplanDays = jsonDoc.array();
|
||||||
|
|
||||||
|
@ -329,12 +403,12 @@ int ServerConn::getFoodPlan()
|
||||||
return(200);
|
return(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnData_t ServerConn::senddata(QUrl serviceUrl, QUrlQuery pdata)
|
QVariantMap ServerConn::senddata(QUrl serviceUrl, QUrlQuery pdata, bool raw)
|
||||||
{
|
{
|
||||||
// create network manager
|
// create network manager
|
||||||
QNetworkAccessManager * networkManager = new QNetworkAccessManager();
|
QNetworkAccessManager * networkManager = new QNetworkAccessManager();
|
||||||
|
|
||||||
ReturnData_t ret; //this is a custom type to store the return-data
|
QVariantMap ret; //this is a custom type to store the return-data
|
||||||
|
|
||||||
// Create network request
|
// Create network request
|
||||||
QNetworkRequest request(serviceUrl);
|
QNetworkRequest request(serviceUrl);
|
||||||
|
@ -362,22 +436,33 @@ ReturnData_t ServerConn::senddata(QUrl serviceUrl, QUrlQuery pdata)
|
||||||
// start the timer
|
// start the timer
|
||||||
timer.start(10000);
|
timer.start(10000);
|
||||||
|
|
||||||
qDebug() << "+--- starting request now...";
|
this->updateDownloadProgress(0, 1);
|
||||||
reply = networkManager->post(request, pdata.toString(QUrl::FullyEncoded).toUtf8());
|
reply = networkManager->post(request, pdata.toString(QUrl::FullyEncoded).toUtf8());
|
||||||
connect(reply, &QNetworkReply::sslErrors, this, [=](){ reply->ignoreSslErrors(); });
|
connect(reply, &QNetworkReply::sslErrors, this, [=](){ reply->ignoreSslErrors(); });
|
||||||
|
|
||||||
|
connect(reply, SIGNAL(downloadProgress(qint64, qint64)),
|
||||||
|
this, SLOT(updateDownloadProgress(qint64, qint64)));
|
||||||
|
|
||||||
// start the loop
|
// start the loop
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
qDebug() << "+--- request finished";
|
if(!timer.isActive()) {
|
||||||
|
// timeout
|
||||||
|
return {{"status", 0}};
|
||||||
|
}
|
||||||
|
|
||||||
//get the status code
|
//get the status code
|
||||||
QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||||
|
|
||||||
ret.status_code = status_code.toInt();
|
ret.insert("status", status_code.toInt());
|
||||||
|
|
||||||
//get the full text response
|
//get the full text response
|
||||||
ret.text = QString::fromUtf8(reply->readAll());
|
if(!raw) {
|
||||||
|
ret.insert("text", QString::fromUtf8(reply->readAll()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret.insert("data", reply->readAll());
|
||||||
|
}
|
||||||
|
|
||||||
// delete the reply object
|
// delete the reply object
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
@ -389,6 +474,22 @@ ReturnData_t ServerConn::senddata(QUrl serviceUrl, QUrlQuery pdata)
|
||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerConn::updateDownloadProgress(qint64 read, qint64 total)
|
||||||
|
{
|
||||||
|
double progress;
|
||||||
|
|
||||||
|
if(total <= 0)
|
||||||
|
progress = 0;
|
||||||
|
else
|
||||||
|
progress = (double(read) / double(total));
|
||||||
|
|
||||||
|
if(progress < 0)
|
||||||
|
progress = 0;
|
||||||
|
|
||||||
|
this->downloadProgress = progress;
|
||||||
|
emit this->downloadProgressChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QString ServerConn::getState() {
|
QString ServerConn::getState() {
|
||||||
return(this->state);
|
return(this->state);
|
||||||
}
|
}
|
||||||
|
@ -402,6 +503,10 @@ void ServerConn::setState(QString state) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double ServerConn::getDownloadProgress() {
|
||||||
|
return this->downloadProgress;
|
||||||
|
}
|
||||||
|
|
||||||
ServerConn::~ServerConn()
|
ServerConn::~ServerConn()
|
||||||
{
|
{
|
||||||
qDebug("+----- ServerConn destruktor -----+");
|
qDebug("+----- ServerConn destruktor -----+");
|
||||||
|
|