diff --git a/SpeedClimbingReactionTrainer.pro b/SpeedClimbingReactionTrainer.pro
new file mode 100644
index 0000000..acca1b6
--- /dev/null
+++ b/SpeedClimbingReactionTrainer.pro
@@ -0,0 +1,49 @@
+QT += quick qml quickcontrols2
+
+android {
+ QT += androidextras
+}
+
+CONFIG += c++11
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Refer to the documentation for the
+# deprecated API to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ sources/main.cpp \
+ sources/appsettings.cpp \
+ sources/apptheme.cpp
+
+HEADERS += \
+ headers/appsettings.h \
+ headers/apptheme.h
+
+RESOURCES += qml/qml.qrc \
+ shared/shared.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+
+# Additional import path used to resolve QML modules just for Qt Quick Designer
+QML_DESIGNER_IMPORT_PATH =
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+android {
+ ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android-sources
+}
+
+DISTFILES += \
+ android-sources/AndroidManifest.xml
+
diff --git a/SpeedClimbingReactionTrainer.pro.user b/SpeedClimbingReactionTrainer.pro.user
new file mode 100644
index 0000000..ee61a9c
--- /dev/null
+++ b/SpeedClimbingReactionTrainer.pro.user
@@ -0,0 +1,635 @@
+
+
+
+
+
+ EnvironmentId
+ {73fd4d96-a4f7-426d-9695-506509aacee9}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ UTF-8
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ 1
+ true
+ true
+ true
+ false
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Android for armeabi-v7a (Clang Qt 5.11.3 for Android ARMv7)
+ Android for armeabi-v7a (Clang Qt 5.11.3 for Android ARMv7)
+ {0c845c7b-f333-4cf9-be32-0bca6d080fce}
+ 1
+ 0
+ 0
+
+ /home/dorian/Documents/git/builds/build-SpeedClimbingReactionTrainer-Android_for_armeabi_v7a_Clang_Qt_5_11_3_for_Android_ARMv7-Debug
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ true
+
+ false
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+ false
+
+
+ true
+ Copy application data
+
+ Qt4ProjectManager.AndroidPackageInstallationStep
+
+
+ android-28
+
+ true
+ Build Android APK
+
+ QmakeProjectManager.AndroidBuildApkStep
+ false
+ false
+
+ 4
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+ false
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Debug
+ Debug
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+ true
+
+
+ /home/dorian/Documents/git/builds/build-SpeedClimbingReactionTrainer-Android_for_armeabi_v7a_Clang_Qt_5_11_3_for_Android_ARMv7-Release
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ false
+
+ false
+ false
+ true
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+ false
+
+
+ true
+ Copy application data
+
+ Qt4ProjectManager.AndroidPackageInstallationStep
+
+
+ android-28
+ /home/dorian/Documents/git/bluemessage/android_release.keystore
+ true
+ Build Android APK
+
+ QmakeProjectManager.AndroidBuildApkStep
+ false
+ false
+
+ 4
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+ false
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Release
+ Release
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+
+ /home/dorian/Documents/git/builds/build-SpeedClimbingReactionTrainer-Android_for_armeabi_v7a_Clang_Qt_5_11_3_for_Android_ARMv7-Profile
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ true
+
+ false
+ true
+ true
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+ false
+
+
+ true
+ Copy application data
+
+ Qt4ProjectManager.AndroidPackageInstallationStep
+
+
+ android-28
+
+ true
+ Build Android APK
+
+ QmakeProjectManager.AndroidBuildApkStep
+ false
+ false
+
+ 4
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+ false
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Profile
+ Profile
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+ 3
+
+
+
+ true
+ Deploy to Android device
+
+ Qt4ProjectManager.AndroidDeployQtStep
+ false
+
+ 1
+ Deploy
+
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+ Deploy to Android device
+
+ Qt4ProjectManager.AndroidDeployConfiguration2
+
+ 1
+
+ MWS0216A15001488
+ 24
+
+
+ false
+ false
+ 1000
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+
+
+
+ 0
+
+ SpeedClimbingReactionTrainer
+ SpeedClimbingReactionTrainer
+ Qt4ProjectManager.AndroidRunConfiguration:/home/dorian/Documents/git/SpeedClimbingReactionTrainer/SpeedClimbingReactionTrainer.pro
+
+ 3768
+ false
+ true
+ false
+ false
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.Target.1
+
+ Desktop Qt 5.11.3 GCC 64bit
+ Desktop Qt 5.11.3 GCC 64bit
+ qt.qt5.5113.gcc_64_kit
+ 0
+ 0
+ 0
+
+ /home/dorian/Documents/git/builds/build-SpeedClimbingReactionTrainer-Desktop_Qt_5_11_3_GCC_64bit-Debug
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ true
+
+ false
+ false
+ false
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+ false
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+ false
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Debug
+ Debug
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 2
+ true
+
+
+ /home/dorian/Documents/git/builds/build-SpeedClimbingReactionTrainer-Desktop_Qt_5_11_3_GCC_64bit-Release
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ false
+
+ false
+ false
+ true
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+ false
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+ false
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Release
+ Release
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+
+ /home/dorian/Documents/git/builds/build-SpeedClimbingReactionTrainer-Desktop_Qt_5_11_3_GCC_64bit-Profile
+
+
+ true
+ qmake
+
+ QtProjectManager.QMakeBuildStep
+ true
+
+ false
+ true
+ true
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ false
+
+
+ false
+
+ 2
+ Build
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+ true
+ Make
+
+ Qt4ProjectManager.MakeStep
+
+ true
+ clean
+
+ false
+
+ 1
+ Clean
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ Profile
+ Profile
+ Qt4ProjectManager.Qt4BuildConfiguration
+ 0
+ true
+
+ 3
+
+
+ 0
+ Deploy
+
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+ Deploy Configuration
+
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+ false
+ false
+ 1000
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+ 2
+
+ SpeedClimbingReactionTrainer
+
+ Qt4ProjectManager.Qt4RunConfiguration:/home/dorian/Documents/git/SpeedClimbingReactionTrainer/SpeedClimbingReactionTrainer.pro
+ SpeedClimbingReactionTrainer.pro
+
+ 3768
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 2
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 20
+
+
+ Version
+ 20
+
+
diff --git a/android-sources/AndroidManifest.xml b/android-sources/AndroidManifest.xml
new file mode 100644
index 0000000..0413764
--- /dev/null
+++ b/android-sources/AndroidManifest.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android-sources/res/drawable-hdpi/icon.png b/android-sources/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..c98ba7e
Binary files /dev/null and b/android-sources/res/drawable-hdpi/icon.png differ
diff --git a/android-sources/res/drawable-ldpi/icon.png b/android-sources/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..c98ba7e
Binary files /dev/null and b/android-sources/res/drawable-ldpi/icon.png differ
diff --git a/android-sources/res/drawable-mdpi/icon.png b/android-sources/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..c98ba7e
Binary files /dev/null and b/android-sources/res/drawable-mdpi/icon.png differ
diff --git a/headers/appsettings.h b/headers/appsettings.h
new file mode 100644
index 0000000..7101f29
--- /dev/null
+++ b/headers/appsettings.h
@@ -0,0 +1,28 @@
+#ifndef APPSETTINGS_H
+#define APPSETTINGS_H
+
+#include
+#include
+#include
+#include
+class AppSettings : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AppSettings(QObject *parent = nullptr);
+ ~AppSettings();
+
+ Q_INVOKABLE QString read(const QString &key);
+ Q_INVOKABLE void write(const QString &key, const QVariant &variant);
+ Q_INVOKABLE void setDefaultSetting(const QString &key, const QVariant &defaultVariant);
+
+ QSettings *settingsManager;
+
+signals:
+
+public slots:
+
+};
+extern AppSettings * pGlobalAppSettings;
+
+#endif // APPSETTINGS_H
diff --git a/headers/apptheme.h b/headers/apptheme.h
new file mode 100644
index 0000000..60afe2f
--- /dev/null
+++ b/headers/apptheme.h
@@ -0,0 +1,30 @@
+#ifndef APPTHEME_H
+#define APPTHEME_H
+
+#include
+#include
+#include "appsettings.h"
+
+class AppTheme : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant style READ getStyle NOTIFY styleChanged)
+public:
+ explicit AppTheme(QObject *parent = nullptr);
+
+private:
+
+ QList themes;
+
+ QVariant * currentTheme;
+
+
+signals:
+ void styleChanged();
+
+public slots:
+ QVariant getStyle();
+ Q_INVOKABLE void refreshTheme();
+};
+
+#endif // APPTHEME_H
diff --git a/qml/Components/FadeAnimation.qml b/qml/Components/FadeAnimation.qml
new file mode 100644
index 0000000..bb05356
--- /dev/null
+++ b/qml/Components/FadeAnimation.qml
@@ -0,0 +1,49 @@
+/*
+ Speed Climbing Stopwatch - Simple Stopwatch for Climbers
+ Copyright (C) 2018 Itsblue Development - Dorian Zeder
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, version 3 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+import QtQuick 2.0
+
+SequentialAnimation {
+ id: root
+ property QtObject target
+ property string fadeProperty: "scale"
+ property int fadeDuration: 150
+ property int fadeDuration_in: fadeDuration
+ property int fadeDuration_out: fadeDuration
+ property alias outValue: outAnimation.to
+ property alias inValue: inAnimation.to
+ property alias outEasingType: outAnimation.easing.type
+ property alias inEasingType: inAnimation.easing.type
+ property string easingType: "Quad"
+ NumberAnimation { // in the default case, fade scale to 0
+ id: outAnimation
+ target: root.target
+ property: root.fadeProperty
+ duration: root.fadeDuration_in
+ to: 0
+ easing.type: Easing["In"+root.easingType]
+ }
+ PropertyAction { } // actually change the property targeted by the Behavior between the 2 other animations
+ NumberAnimation { // in the default case, fade scale back to 1
+ id: inAnimation
+ target: root.target
+ property: root.fadeProperty
+ duration: root.fadeDuration_out
+ to: 1
+ easing.type: Easing["Out"+root.easingType]
+ }
+}
diff --git a/qml/Components/FancyButton.qml b/qml/Components/FancyButton.qml
new file mode 100644
index 0000000..2ec7623
--- /dev/null
+++ b/qml/Components/FancyButton.qml
@@ -0,0 +1,88 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.4
+import QtGraphicalEffects 1.0
+
+Button {
+ id: control
+
+ property string image
+ property color backgroundColor: "white"
+ property color textColor: "black"
+ property real imageScale: 1
+ property double glowRadius: 0.001
+ property double glowSpread: 0.2
+ property bool glowVisible: true
+ property double glowScale: 0.75
+ property double glowOpacity: 1
+
+ scale: control.pressed ? 0.8:1
+
+ Behavior on scale {
+ PropertyAnimation {
+ duration: 100
+ }
+ }
+
+ Behavior on backgroundColor {
+ ColorAnimation {
+ duration: 200
+ }
+ }
+
+ contentItem: Text {
+ visible: false
+ }
+
+ Text {
+ id: conetntText
+ text: qsTr(control.text)
+ anchors.centerIn: parent
+ font: parent.font
+ color: control.textColor
+ opacity: control.enabled ? 1:0.4
+ }
+
+ background: Item {
+ id: controlBackgroundContainer
+
+ RectangularGlow {
+ id: effect
+ glowRadius: control.glowRadius
+ spread: control.glowSpread
+ color: "black"
+
+ visible: control.glowVisible
+
+ cornerRadius: controlBackground.radius
+ anchors.fill: controlBackground
+ scale: control.glowScale
+ opacity: control.glowOpacity
+ }
+
+ Rectangle {
+ id: controlBackground
+
+ anchors.fill: parent
+
+ radius: height * 0.5
+
+ color: control.backgroundColor
+
+ Image {
+ id: buttonIcon
+ source: control.image
+
+ anchors.centerIn: parent
+ height: parent.height * 0.5
+ width: height
+
+ mipmap: true
+
+ fillMode: Image.PreserveAspectFit
+
+ scale: control.imageScale
+ }
+ }
+ }
+
+}
diff --git a/qml/main.qml b/qml/main.qml
new file mode 100644
index 0000000..a4fc2fc
--- /dev/null
+++ b/qml/main.qml
@@ -0,0 +1,367 @@
+import QtQuick 2.9
+import QtQuick.Window 2.2
+import QtQuick.Controls 2.4
+import QtMultimedia 5.0
+import QtGraphicalEffects 1.0
+import QtQuick.Controls.Styles 1.4
+
+import com.itsblue.SpeedClimbingStartTrainer 2.0
+
+import "./Components"
+
+Window {
+ visible: true
+ width: 640
+ height: 480
+ title: qsTr("Hello World")
+
+ Page {
+ id: app
+
+ state: "IDLE"
+
+ anchors.fill: parent
+
+ property double startTime: -1
+ property double stopTime: -1
+
+ property double reactionTime: 0
+
+ property int highscore: settings.read("highscore")
+
+ AppSettings {
+ id: settings
+ }
+
+ AppTheme {
+ id: theme
+ }
+
+ Item {
+ id: soundsItm
+
+ SoundEffect {
+ id: readySe
+
+ source: "qrc:/sounds/ready_1.wav"
+
+ muted: app.state === "WAITING"
+
+ onPlayingChanged: {
+ if(!playing && app.state === "RUNNING"){
+ startSe.play()
+ }
+ }
+ }
+
+ SoundEffect {
+ id: startSe
+
+ source: "qrc:/sounds/OFFICAL_IFSC_STARTIGNAL.wav"
+
+ muted: app.state === "WAITING"
+
+ onPlayingChanged: {
+ if(!playing){
+ app.startTime = new Date().getTime()
+ console.log("offset: ", new Date().getTime() - app.startTime)
+
+ if(app.state === "WAITING"){
+ app.reactionTime = app.stopTime - app.startTime
+ app.state = "STOPPED"
+ }
+ }
+ }
+ }
+
+ SoundEffect {
+ id: failedSe
+ source: "qrc:/sounds/false.wav"
+ }
+
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: theme.style.backgroundColor
+
+ Behavior on color {
+
+ ColorAnimation {
+ duration: 200
+ }
+ }
+ }
+
+ Item {
+ id: topContainerItm
+
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: app.landscape() ? startBt.left:parent.right
+ bottom: app.landscape() ? parent.bottom:startBt.top
+ bottomMargin: app.landscape() ? undefined:parent.height * 0.1
+ rightMargin: app.landscape() ? parent.width * 0.05:0
+ }
+
+ Text {
+ id: topLa
+
+ anchors.centerIn: parent
+
+ width: parent.width * 0.8
+
+ text: ""
+ color: theme.style.textColor
+
+ fontSizeMode: Text.Fit
+
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+
+ font.pixelSize: app.landscape() ? parent.width * 0.1 : parent.height * 0.4
+
+ minimumPixelSize: 0
+
+ Behavior on text {
+ FadeAnimation{
+ target: topLa
+ }
+ }
+ }
+
+ Text {
+ id: highscoreLa
+
+ anchors {
+ top: topLa.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
+
+ width: topLa.width * 0.5
+ height: topLa.height
+
+ visible: text !== "highscore: -1"
+
+ text: ""
+
+ color: theme.style.textColor
+
+ fontSizeMode: Text.Fit
+
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+
+ font.pixelSize: app.landscape() ? parent.width * 0.1 : parent.height * 0.4
+
+ minimumPixelSize: 0
+
+ Behavior on text {
+ FadeAnimation{
+ target: highscoreLa
+ }
+ }
+ }
+
+ Switch {
+ anchors {
+ top: topLa.bottom
+ horizontalCenter: parent.horizontalCenter
+ horizontalCenterOffset: width * 0.1
+ }
+
+ checked: settings.read("theme") === "1"
+
+ scale: 0.8
+
+ opacity: app.state === "IDLE" ? 1:0
+
+ onCheckedChanged: {
+ if(checked){
+ settings.write("theme", 1)
+ }
+ else {
+ settings.write("theme", 0)
+ }
+
+ theme.refreshTheme()
+ }
+
+ indicator: Rectangle {
+
+ property bool checked: parent.checked
+ property bool down: parent.down
+
+ height: parent.height
+ width: height * 2
+
+ radius: height * 0.5
+ color: parent.checked ? "#17a81a" : "transparent"
+ border.color: parent.checked ? "#17a81a" : "#cccccc"
+
+ Behavior on color{
+ ColorAnimation{
+ duration: 200
+ }
+ }
+
+ Rectangle {
+ x: parent.checked ? parent.width - width : 0
+ width: parent.height
+ height: parent.height
+ radius: height * 0.5
+ color: parent.down ? theme.style.buttonPressedColor:theme.style.backgroundColor //"#cccccc" : "#ffffff"
+ border.color: parent.checked ? (parent.down ? "#17a81a" : "#21be2b") : "#999999"
+ Behavior on x{
+ NumberAnimation {
+ property: "x"
+ duration: 200
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ }
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: 200
+ }
+ }
+ }
+ }
+
+ FancyButton {
+ id: startBt
+
+ text: "start"
+ property int size: app.landscape() ? parent.width * 0.5:parent.height * 0.5
+
+ backgroundColor: theme.style.buttonColor
+ textColor: theme.style.textColor
+
+ anchors {
+ bottom: parent.bottom
+ right: parent.right
+ bottomMargin: app.landscape() ? parent.height * 0.5 - startBt.height * 0.5:parent.height * 0.1
+ rightMargin: app.landscape() ? parent.width * 0.05:parent.width * 0.5 - startBt.width * 0.5
+ }
+
+ font.pixelSize: height * 0.15
+
+ height: app.landscape() ? (size > parent.height * 0.9 ? parent.height * 0.9:size) : (size > parent.width * 0.9 ? parent.width * 0.9:size)
+ width: height
+
+ onPressed: {
+ app.start()
+ }
+
+ onReleased: {
+ app.stop()
+ }
+ }
+
+ function landscape() {
+ return app.height < app.width
+ }
+
+ function start() {
+ if(app.state !== "IDLE"){
+ app.reset()
+ return
+ }
+
+ app.state = "RUNNING"
+ readySe.play()
+ }
+
+ function stop() {
+
+ app.stopTime = new Date().getTime()
+
+ if(app.state !== "RUNNING"){
+ return
+ }
+
+ if(startSe.playing){
+ app.state = "WAITING"
+
+ failedSe.play()
+ }
+ else if(readySe.playing){
+ app.state = "IDLE"
+ }
+
+ else {
+ app.state = "STOPPED"
+
+ app.reactionTime = app.stopTime - app.startTime
+
+ app.refreshHighscore()
+ }
+ }
+
+ function refreshHighscore(){
+ if(app.reactionTime > 0 && ( app.reactionTime < parseInt(settings.read("highscore")) || settings.read("highscore") === "-1" )){
+ settings.write("highscore", app.reactionTime)
+ app.highscore = app.reactionTime
+ }
+ }
+
+ function reset() {
+ app.state = "IDLE"
+ }
+
+ states: [
+ State {
+ name: "IDLE"
+ PropertyChanges {
+ target: topLa
+ text: "Hold down start to start"
+ }
+ },
+
+ State {
+ name: "RUNNING"
+ PropertyChanges {
+ target: topLa
+ text: "release to stop"
+ }
+ },
+
+ State {
+ name: "STOPPED"
+ PropertyChanges {
+ target: topLa
+ text: "Your reaction time: " + app.reactionTime
+ }
+
+ PropertyChanges {
+ target: startBt
+ text: "reset"
+ }
+
+ PropertyChanges {
+ target: highscoreLa
+ text: "highscore: " + app.highscore
+ }
+ },
+
+ State {
+ name: "WAITING"
+ PropertyChanges {
+ target: topLa
+ text: "please wait..."
+ }
+
+ PropertyChanges {
+ target: startBt
+ enabled: false
+ text: "waiting..."
+ }
+ }
+ ]
+
+ }
+
+}
diff --git a/qml/qml.qrc b/qml/qml.qrc
new file mode 100644
index 0000000..0e60d44
--- /dev/null
+++ b/qml/qml.qrc
@@ -0,0 +1,7 @@
+
+
+ main.qml
+ Components/FadeAnimation.qml
+ Components/FancyButton.qml
+
+
diff --git a/shared/graphics/Banner.png b/shared/graphics/Banner.png
new file mode 100644
index 0000000..2d8337d
Binary files /dev/null and b/shared/graphics/Banner.png differ
diff --git a/shared/graphics/Banner.xcf b/shared/graphics/Banner.xcf
new file mode 100644
index 0000000..d05d956
Binary files /dev/null and b/shared/graphics/Banner.xcf differ
diff --git a/shared/graphics/SpeedClimbingStartTrainer.xcf b/shared/graphics/SpeedClimbingStartTrainer.xcf
new file mode 100644
index 0000000..f3f3f65
Binary files /dev/null and b/shared/graphics/SpeedClimbingStartTrainer.xcf differ
diff --git a/shared/graphics/favicon.png b/shared/graphics/favicon.png
new file mode 100644
index 0000000..c98ba7e
Binary files /dev/null and b/shared/graphics/favicon.png differ
diff --git a/shared/graphics/faviconForGooglePlay.png b/shared/graphics/faviconForGooglePlay.png
new file mode 100644
index 0000000..2d44424
Binary files /dev/null and b/shared/graphics/faviconForGooglePlay.png differ
diff --git a/shared/shared.qrc b/shared/shared.qrc
new file mode 100644
index 0000000..96d201d
--- /dev/null
+++ b/shared/shared.qrc
@@ -0,0 +1,8 @@
+
+
+ sounds/at_marks_1.wav
+ sounds/OFFICAL_IFSC_STARTIGNAL.wav
+ sounds/ready_1.wav
+ sounds/false.wav
+
+
diff --git a/shared/sounds/OFFICAL_IFSC_STARTIGNAL.wav b/shared/sounds/OFFICAL_IFSC_STARTIGNAL.wav
new file mode 100644
index 0000000..b3aa454
Binary files /dev/null and b/shared/sounds/OFFICAL_IFSC_STARTIGNAL.wav differ
diff --git a/shared/sounds/at_marks_1.wav b/shared/sounds/at_marks_1.wav
new file mode 100644
index 0000000..f73c703
Binary files /dev/null and b/shared/sounds/at_marks_1.wav differ
diff --git a/shared/sounds/at_marks_2.wav b/shared/sounds/at_marks_2.wav
new file mode 100644
index 0000000..54494c0
Binary files /dev/null and b/shared/sounds/at_marks_2.wav differ
diff --git a/shared/sounds/false.wav b/shared/sounds/false.wav
new file mode 100644
index 0000000..ae4daa7
Binary files /dev/null and b/shared/sounds/false.wav differ
diff --git a/shared/sounds/ready_1.wav b/shared/sounds/ready_1.wav
new file mode 100644
index 0000000..42f0867
Binary files /dev/null and b/shared/sounds/ready_1.wav differ
diff --git a/shared/sounds/ready_2.wav b/shared/sounds/ready_2.wav
new file mode 100644
index 0000000..abe5032
Binary files /dev/null and b/shared/sounds/ready_2.wav differ
diff --git a/shared/sounds/sounds.aup b/shared/sounds/sounds.aup
new file mode 100644
index 0000000..65a9068
--- /dev/null
+++ b/shared/sounds/sounds.aup
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shared/sounds/sounds_data/e00/d00/e0000260.au b/shared/sounds/sounds_data/e00/d00/e0000260.au
new file mode 100644
index 0000000..2a99728
Binary files /dev/null and b/shared/sounds/sounds_data/e00/d00/e0000260.au differ
diff --git a/shared/sounds/sounds_data/e00/d00/e0000290.au b/shared/sounds/sounds_data/e00/d00/e0000290.au
new file mode 100644
index 0000000..ecea0e6
Binary files /dev/null and b/shared/sounds/sounds_data/e00/d00/e0000290.au differ
diff --git a/shared/sounds/sounds_data/e00/d00/e0000457.au b/shared/sounds/sounds_data/e00/d00/e0000457.au
new file mode 100644
index 0000000..2a99728
Binary files /dev/null and b/shared/sounds/sounds_data/e00/d00/e0000457.au differ
diff --git a/shared/sounds/sounds_data/e00/d00/e0000542.au b/shared/sounds/sounds_data/e00/d00/e0000542.au
new file mode 100644
index 0000000..fbc4417
Binary files /dev/null and b/shared/sounds/sounds_data/e00/d00/e0000542.au differ
diff --git a/shared/sounds/sounds_data/e00/d00/e0000714.au b/shared/sounds/sounds_data/e00/d00/e0000714.au
new file mode 100644
index 0000000..9ed3dfc
Binary files /dev/null and b/shared/sounds/sounds_data/e00/d00/e0000714.au differ
diff --git a/shared/sounds/sounds_data/e00/d00/e0000996.au b/shared/sounds/sounds_data/e00/d00/e0000996.au
new file mode 100644
index 0000000..ecea0e6
Binary files /dev/null and b/shared/sounds/sounds_data/e00/d00/e0000996.au differ
diff --git a/shared/sounds/sounds_data/e00/d00/e00009d0.au b/shared/sounds/sounds_data/e00/d00/e00009d0.au
new file mode 100644
index 0000000..9ed3dfc
Binary files /dev/null and b/shared/sounds/sounds_data/e00/d00/e00009d0.au differ
diff --git a/shared/sounds/sounds_data/e00/d00/e0000ce4.au b/shared/sounds/sounds_data/e00/d00/e0000ce4.au
new file mode 100644
index 0000000..fbc4417
Binary files /dev/null and b/shared/sounds/sounds_data/e00/d00/e0000ce4.au differ
diff --git a/sources/appsettings.cpp b/sources/appsettings.cpp
new file mode 100644
index 0000000..61e636d
--- /dev/null
+++ b/sources/appsettings.cpp
@@ -0,0 +1,63 @@
+/*
+ Speed Climbing Stopwatch - Simple Stopwatch for Climbers
+ Copyright (C) 2018 Itsblue Development - Dorian Zeder
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, version 3 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+#include "headers/appsettings.h"
+
+AppSettings * pGlobalAppSettings = nullptr;
+
+AppSettings::AppSettings(QObject* parent)
+ :QObject(parent)
+{
+ QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+
+ this->settingsManager = new QSettings(path+"/settings.ini", QSettings::IniFormat);
+
+ this->setDefaultSetting("highscore", "-1");
+ this->setDefaultSetting("theme", "0");
+
+ pGlobalAppSettings = this;
+}
+
+QString AppSettings::read(const QString &key)
+{
+ this->settingsManager->beginGroup("AppSettings");
+ QString value = this->settingsManager->value(key , false).toString();
+ this->settingsManager->endGroup();
+ return(value);
+}
+
+void AppSettings::write(const QString &key, const QVariant &variant)
+{
+ this->settingsManager->beginGroup("AppSettings");
+ this->settingsManager->setValue(key , variant);
+ this->settingsManager->endGroup();
+}
+
+void AppSettings::setDefaultSetting(const QString &key, const QVariant &defaultVariant)
+{
+ QString value = this->read(key);
+ if(value == "false"){
+ this->write(key, defaultVariant);
+ }
+
+}
+
+AppSettings::~AppSettings()
+{
+ delete settingsManager;
+}
+
diff --git a/sources/apptheme.cpp b/sources/apptheme.cpp
new file mode 100644
index 0000000..22e36f6
--- /dev/null
+++ b/sources/apptheme.cpp
@@ -0,0 +1,95 @@
+#include "headers/apptheme.h"
+
+AppTheme::AppTheme(QObject *parent) : QObject(parent)
+{
+
+ QVariantMap tmpLightTheme = {
+ {"backgroundColor", "white"},
+
+ {"buttonColor", "white"},
+ {"buttonPressedColor", "lightgrey"},
+ {"buttonBorderColor", "grey"},
+ {"disabledButtonColor", "#d5d5d5"},
+
+ {"viewColor", "white"},
+ {"menuColor", "#f8f8f8"},
+
+ {"delegate1Color", "#202227"},
+ {"delegate2Color", "#202227"},
+ {"delegateBackgroundColor", "white"},
+ {"delegatePressedColor", "#dddedf"},
+
+ {"textColor", "black"},
+ {"textDarkColor", "#232323"},
+ {"disabledTextColor", "grey"},
+
+ {"sliderColor", "#6ccaf2"},
+
+ {"errorColor", "#ba3f62"},
+ {"infoColor", "#3fba62"},
+
+ {"lineColor", "grey"},
+
+ {"backIcon", "qrc:/graphics/icons/back_black.png"},
+ {"settIcon", "qrc:/graphics/icons/settings_black.png"},
+ {"buzzerIcon", "qrc:/graphics/icons/buzzer_black.png"},
+ {"startpadIcon", "qrc:/graphics/icons/startpad_black.png"},
+ {"baseStationIcon", "qrc:/graphics/icons/BaseStation_black.png"}
+
+ };
+ this->themes.append(tmpLightTheme);
+
+ QVariantMap tmpDarkTheme = {
+ {"backgroundColor", "#2d3037"},
+
+ {"buttonColor", "#202227"},
+ {"buttonPressedColor", "#6ccaf2"},
+ {"buttonBorderColor", "grey"},
+ {"disabledButtonColor", "#555555"},
+
+ {"viewColor", "#202227"},
+ {"menuColor", "#292b32"},
+
+ {"delegate1Color", "#202227"},
+ {"delegate2Color", "#202227"},
+ {"delegateBackgroundColor", "#202227"},
+ {"delegatePressedColor", "#41454f"},
+
+ {"textColor", "#ffffff"},
+ {"textDarkColor", "#232323"},
+ {"disabledTextColor", "#777777"},
+
+ {"sliderColor", "#6ccaf2"},
+
+ {"errorColor", "#ba3f62"},
+ {"infoColor", "#3fba62"},
+
+ {"lineColor", "grey"},
+
+ {"backIcon", "qrc:/graphics/icons/back.png"},
+ {"settIcon", "qrc:/graphics/icons/settings.png"},
+ {"buzzerIcon", "qrc:/graphics/icons/buzzer.png"},
+ {"startpadIcon", "qrc:/graphics/icons/startpad.png"},
+ {"baseStationIcon", "qrc:/graphics/icons/BaseStation.png"}
+
+ };
+ this->themes.append(tmpDarkTheme);
+
+ QString currentThemeString = pGlobalAppSettings->read("theme");
+
+ this->currentTheme = &this->themes[currentThemeString.toInt()];
+
+ qDebug() << currentThemeString << currentThemeString.toInt();
+}
+
+QVariant AppTheme::getStyle() {
+ return *this->currentTheme;
+}
+
+void AppTheme::refreshTheme() {
+ QString currentThemeString = pGlobalAppSettings->read("theme");
+
+ this->currentTheme = &this->themes[currentThemeString.toInt()];
+
+ emit this->styleChanged();
+}
diff --git a/sources/main.cpp b/sources/main.cpp
new file mode 100644
index 0000000..c6f1c9d
--- /dev/null
+++ b/sources/main.cpp
@@ -0,0 +1,45 @@
+#include
+#include
+
+#include "headers/apptheme.h"
+#include "headers/appsettings.h"
+
+#ifdef Q_OS_ANDROID
+#include
+#endif
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+
+ QGuiApplication app(argc, argv);
+
+#ifdef Q_OS_ANDROID
+ //set the standard volume to media
+ QAndroidJniObject jactivity=QtAndroid::androidActivity();
+ if(jactivity.isValid())
+ jactivity.callMethod("setVolumeControlStream","(I)V",3);
+
+ //set statusbar color
+ QtAndroid::runOnAndroidThread([=]()
+ {
+ QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;");
+ window.callMethod("addFlags", "(I)V", 0x80000000);
+ window.callMethod("clearFlags", "(I)V", 0x04000000);
+ //window.callMethod("setStatusBarColor", "(I)V", 0x202227); // Desired statusbar color
+ //QAndroidJniObject decorView = window.callObjectMethod("getDecorView", "()Landroid/view/View;");
+ //decorView.callMethod("setSystemUiVisibility", "(I)V", 0x00002000);
+ });
+#endif
+
+ qmlRegisterType("com.itsblue.SpeedClimbingStartTrainer", 2, 0, "AppTheme");
+ qmlRegisterType("com.itsblue.SpeedClimbingStartTrainer", 2, 0, "AppSettings");
+
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}