From f1a7d0093c9a30c5f18c661c4140c599cbc7e3b0 Mon Sep 17 00:00:00 2001 From: "pls.153" Date: Tue, 5 Jul 2022 17:59:16 +0200 Subject: [PATCH] add settings to example 'wear-os-gps' --- examples/app-template/readme-build.md | 4 ++ examples/wear-os-gps/cpp/qt.cpp | 7 +-- examples/wear-os-gps/cpp/qt.h | 2 +- examples/wear-os-gps/lisp/main.lisp | 13 +++++- examples/wear-os-gps/lisp/speed.lisp | 8 ++-- examples/wear-os-gps/lisp/ui-vars.lisp | 10 +++-- examples/wear-os-gps/qml/ext/Gauge.qml | 10 ++--- examples/wear-os-gps/qml/ext/MainView.qml | 19 +++++++++ examples/wear-os-gps/qml/ext/Settings.qml | 46 ++++++++++++++++++++ examples/wear-os-gps/qml/main.qml | 52 +++++++++++++++++------ examples/wear-os-gps/readme-build.md | 14 +++++- examples/wear-os-gps/readme.md | 11 +++-- examples/wear-os-heart/readme-build.md | 14 +++++- 13 files changed, 174 insertions(+), 36 deletions(-) create mode 100644 examples/wear-os-gps/qml/ext/MainView.qml create mode 100644 examples/wear-os-gps/qml/ext/Settings.qml diff --git a/examples/app-template/readme-build.md b/examples/app-template/readme-build.md index fdd16ce..4be3d99 100644 --- a/examples/app-template/readme-build.md +++ b/examples/app-template/readme-build.md @@ -20,6 +20,10 @@ and adding to resources included in the executable). So, you only need to **manually** care about the usual ASDF project files in `app.asd`. +But -- *of course* -- you still need to run the respective **qmake** command +every time you add new files to the project, because the automation is all +defined in `app.pro`. + Run desktop ----------- diff --git a/examples/wear-os-gps/cpp/qt.cpp b/examples/wear-os-gps/cpp/qt.cpp index 25ee936..2ab1fd7 100644 --- a/examples/wear-os-gps/cpp/qt.cpp +++ b/examples/wear-os-gps/cpp/qt.cpp @@ -4,14 +4,15 @@ QT_BEGIN_NAMESPACE -QVariant QT::keepScreenOn() { - QtAndroid::runOnAndroidThread([] { +QVariant QT::keepScreenOn(const QVariant& on) { + QtAndroid::runOnAndroidThread([&] { QAndroidJniObject activity = QtAndroid::androidActivity(); if (activity.isValid()) { QAndroidJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); if (window.isValid()) { const int FLAG_KEEP_SCREEN_ON = 128; - window.callMethod("addFlags", "(I)V", FLAG_KEEP_SCREEN_ON); + const char* method = on.toBool() ? "addFlags" : "clearFlags"; + window.callMethod(method, "(I)V", FLAG_KEEP_SCREEN_ON); } } QAndroidJniEnvironment env; diff --git a/examples/wear-os-gps/cpp/qt.h b/examples/wear-os-gps/cpp/qt.h index 1fe8dfa..65273b4 100644 --- a/examples/wear-os-gps/cpp/qt.h +++ b/examples/wear-os-gps/cpp/qt.h @@ -11,7 +11,7 @@ class QT : public QObject { Q_OBJECT public: - Q_INVOKABLE QVariant keepScreenOn(); + Q_INVOKABLE QVariant keepScreenOn(const QVariant& = true); }; QT_END_NAMESPACE diff --git a/examples/wear-os-gps/lisp/main.lisp b/examples/wear-os-gps/lisp/main.lisp index 05c701f..35fa8dd 100644 --- a/examples/wear-os-gps/lisp/main.lisp +++ b/examples/wear-os-gps/lisp/main.lisp @@ -8,8 +8,9 @@ (qt:ini) #+android (progn - (qt:keep-screen-on qt:*cpp*) - (ensure-permissions :access-fine-location))) + (qlater (lambda () (qt:keep-screen-on qt:*cpp*))) ; delay until UI is loaded (see 'Settings.qml') + (ensure-permissions :access-fine-location)) + (q> |ready| ui:*position-source* t)) (defun closing () (close *log-stream*) @@ -64,4 +65,12 @@ (round* (speed*) 1) (round* (distance)))))) +(defun always-on-changed (on) ; called from QML + #+android + (qt:keep-screen-on qt:*cpp* on)) + +(defun set-max-speed () ; called from QML + (q> |maximumValue| ui:*speed* + (q< |value| ui:*max-speed*))) + (qlater 'run) diff --git a/examples/wear-os-gps/lisp/speed.lisp b/examples/wear-os-gps/lisp/speed.lisp index 15fd408..7f49990 100644 --- a/examples/wear-os-gps/lisp/speed.lisp +++ b/examples/wear-os-gps/lisp/speed.lisp @@ -1,14 +1,16 @@ (in-package :gps) +(defvar *distance-samples* 5) + (let ((speed 0.0) samples) (defun update-speed () - "After 10 distance samples, calculate average speed and update on every new - distance sample." + "After *DISTANCE-SAMPLES* samples, calculate average speed and update on + every new distance sample." (push (cons (distance) (get-internal-real-time)) samples) - (when (> (length samples) 10) + (when (> (length samples) *distance-samples*) (setf samples (butlast samples)) (let ((b (first samples)) (a (first (last samples)))) diff --git a/examples/wear-os-gps/lisp/ui-vars.lisp b/examples/wear-os-gps/lisp/ui-vars.lisp index 8d6676e..d3c4b8f 100644 --- a/examples/wear-os-gps/lisp/ui-vars.lisp +++ b/examples/wear-os-gps/lisp/ui-vars.lisp @@ -3,10 +3,14 @@ (:export #:*accuracy* #:*distance* + #:*max-speed* + #:*position-source* #:*speed*)) (in-package :ui) -(defparameter *accuracy* "accuracy") -(defparameter *distance* "distance") -(defparameter *speed* "speed") +(defparameter *accuracy* "accuracy") +(defparameter *distance* "distance") +(defparameter *max-speed* "max_speed") +(defparameter *position-source* "position_source") +(defparameter *speed* "speed") diff --git a/examples/wear-os-gps/qml/ext/Gauge.qml b/examples/wear-os-gps/qml/ext/Gauge.qml index f1b5501..6c387f0 100644 --- a/examples/wear-os-gps/qml/ext/Gauge.qml +++ b/examples/wear-os-gps/qml/ext/Gauge.qml @@ -8,11 +8,11 @@ Rectangle { color: "black" CircularGauge { - id: gauge + id: speed objectName: "speed" anchors.fill: parent maximumValue: 10 - stepSize: 0.1 + stepSize: 0.05 Behavior on value { NumberAnimation { @@ -23,8 +23,8 @@ Rectangle { style: CircularGaugeStyle { id: style - labelStepSize: 1 - tickmarkStepSize: 1 + labelStepSize: parent.maximumValue / 10 + tickmarkStepSize: labelStepSize minorTickmarkCount: 5 minimumValueAngle: -90 maximumValueAngle: 90 @@ -43,7 +43,7 @@ Rectangle { ctx.strokeStyle = limitColor ctx.lineWidth = outerRadius * 0.02 ctx.arc(outerRadius, outerRadius, outerRadius - ctx.lineWidth / 2, - toRad(valueToAngle(limit) - 90), toRad(valueToAngle(gauge.maximumValue) - 90)) + toRad(valueToAngle(limit) - 90), toRad(valueToAngle(speed.maximumValue) - 90)) ctx.stroke() } } diff --git a/examples/wear-os-gps/qml/ext/MainView.qml b/examples/wear-os-gps/qml/ext/MainView.qml new file mode 100644 index 0000000..6f8f49c --- /dev/null +++ b/examples/wear-os-gps/qml/ext/MainView.qml @@ -0,0 +1,19 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import "." as Ext + +Rectangle { + color: "black" + + Ext.Gauge {} + + Column { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 10 + spacing: -5 + + Ext.LogText { objectName: "distance"; font.pixelSize: 48 } + Ext.LogText { objectName: "accuracy"; font.pixelSize: 24 } + } +} diff --git a/examples/wear-os-gps/qml/ext/Settings.qml b/examples/wear-os-gps/qml/ext/Settings.qml new file mode 100644 index 0000000..a27a45c --- /dev/null +++ b/examples/wear-os-gps/qml/ext/Settings.qml @@ -0,0 +1,46 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Rectangle { + Column { + anchors.centerIn: parent + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: "max speed" + font.pixelSize: 16 + font.weight: Font.DemiBold + } + + Tumbler { + id: maxSpeed + objectName: "max_speed" + anchors.horizontalCenter: parent.horizontalCenter + height: 80 + model: [10, 20, 30, 40, 50] + visibleItemCount: 3 + + property int value: model[0] + + delegate: Text { + text: modelData + font.pixelSize: 22 + font.bold: true + opacity: 0.4 + Math.max(0, 1 - Math.abs(Tumbler.displacement)) * 0.6 + Component.onCompleted: { maxSpeed.width = paintedWidth } + } + + onCurrentIndexChanged: { value = model[currentIndex] } + } + + Switch { + objectName: "always_on" + text: "always on" + font.pixelSize: 16 + font.weight: Font.DemiBold + checked: true + + onCheckedChanged: Lisp.call("gps:always-on-changed", checked) + } + } +} diff --git a/examples/wear-os-gps/qml/main.qml b/examples/wear-os-gps/qml/main.qml index d09195a..a889ae7 100644 --- a/examples/wear-os-gps/qml/main.qml +++ b/examples/wear-os-gps/qml/main.qml @@ -14,27 +14,51 @@ Rectangle { updateInterval: 1000 active: true + property bool ready: false + onPositionChanged: { - Lisp.call("gps:position-changed", - position.latitudeValid ? position.coordinate.latitude : null, - position.longitudeValid ? position.coordinate.longitude : null, - position.horizontalAccuracyValid ? position.horizontalAccuracy : null, - position.speedValid ? position.speed : null, - position.directionValid ? position.direction : null, - position.timestamp.toLocaleString(Qt.locale(), "yyyy-MM-dd hh:mm:ss")) + if (ready) { + Lisp.call("gps:position-changed", + position.latitudeValid ? position.coordinate.latitude : null, + position.longitudeValid ? position.coordinate.longitude : null, + position.horizontalAccuracyValid ? position.horizontalAccuracy : null, + position.speedValid ? position.speed : null, + position.directionValid ? position.direction : null, + position.timestamp.toLocaleString(Qt.locale(), "yyyy-MM-dd hh:mm:ss")) + } } } - Ext.Gauge {} + SwipeView { + id: view + anchors.fill: parent + orientation: Qt.Vertical - Column { + onCurrentIndexChanged: { + if (currentIndex === 0) { + Lisp.call("gps:set-max-speed") + } + } + + Ext.MainView {} + Ext.Settings {} + } + + PageIndicator { + id: indicator + anchors.bottom: view.bottom anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: 5 - spacing: -5 + height: 14 + count: view.count + currentIndex: view.currentIndex - Ext.LogText { objectName: "distance"; font.pixelSize: 48 } - Ext.LogText { objectName: "accuracy"; font.pixelSize: 24 } + delegate: Rectangle { + implicitWidth: 6 + implicitHeight: 6 + radius: width / 2 + color: "white" + opacity: index === indicator.currentIndex ? 1 : 0.35 + } } // quit diff --git a/examples/wear-os-gps/readme-build.md b/examples/wear-os-gps/readme-build.md index 1e6e136..5803b39 100644 --- a/examples/wear-os-gps/readme-build.md +++ b/examples/wear-os-gps/readme-build.md @@ -7,6 +7,18 @@ requires WearOS 2 or later. It is assumed that you already enabled the developer settings on your watch. +-- + +Every Lisp file under `lisp/` and every qml/image/font/whatever file under +`qml/` is added **automatically** to your Qt project file (both for re-compile +and adding to resources included in the executable). + +So, you only need to **manually** care about the usual ASDF project files in +`app.asd`. + +But -- *of course* -- you still need to run the respective **qmake** command +every time you add new files to the project, because the automation is all +defined in `app.pro`. Build 32bit android APK @@ -27,7 +39,7 @@ Now you can build the app: ``` $ cd build-android -$ qmake-android "CONFIG+=32bit" .. +$ qmake-android .. # "CONFIG+=32bit" can be omitted here: already defined in app.pro $ make apk $ ./install-run.sh diff --git a/examples/wear-os-gps/readme.md b/examples/wear-os-gps/readme.md index 669be56..7e15b66 100644 --- a/examples/wear-os-gps/readme.md +++ b/examples/wear-os-gps/readme.md @@ -14,7 +14,12 @@ Info This is a practical example of displaying both the speed and the whole distance of e.g. a canoe session (only meant for constant altitude values). You probably -need to adapt the maximum speed value (km/h) to your personal needs. +need to adapt the maximum speed value (km/h) to your personal needs, see +settings (swipe up). + +An important feature is keeping the display always on (implemented with the Qt +JNI interface). But this also consumes more battery, so you can switch it off +in the settings. The data is automatically logged, and can be accessed with e.g. **Device File Explorer** from Android Studio (see Help / Find Action...). @@ -35,8 +40,8 @@ using **Google Earth** (the free desktop app). A simple **Kalman** filter is used for the necessary GPS data smoothing. -The UI uses a `CircularGauge` for displaying the speed (an average of the -latest 10 seconds). Additionally it shows the whole distance and the GPS +The UI uses a `CircularGauge` for displaying the speed (by default, an average +of the latest 5 seconds). Additionally it shows the whole distance and the GPS accuracy in meters. diff --git a/examples/wear-os-heart/readme-build.md b/examples/wear-os-heart/readme-build.md index 84c9aa6..724782a 100644 --- a/examples/wear-os-heart/readme-build.md +++ b/examples/wear-os-heart/readme-build.md @@ -10,6 +10,18 @@ You'll also need an easy to apply hack for the **heart rate sensor**, see It is assumed that you already enabled the developer settings on your watch. +-- + +Every Lisp file under `lisp/` and every qml/image/font/whatever file under +`qml/` is added **automatically** to your Qt project file (both for re-compile +and adding to resources included in the executable). + +So, you only need to **manually** care about the usual ASDF project files in +`app.asd`. + +But -- *of course* -- you still need to run the respective **qmake** command +every time you add new files to the project, because the automation is all +defined in `app.pro`. Build 32bit android APK @@ -30,7 +42,7 @@ Now you can build the app: ``` $ cd build-android -$ qmake-android "CONFIG+=32bit" .. +$ qmake-android .. # "CONFIG+=32bit" can be omitted here: already defined in app.pro $ make apk $ ./install-run.sh