add settings to example 'wear-os-gps'

This commit is contained in:
pls.153 2022-07-05 17:59:16 +02:00
parent de50d90245
commit f1a7d0093c
13 changed files with 174 additions and 36 deletions

View file

@ -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 So, you only need to **manually** care about the usual ASDF project files in
`app.asd`. `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 Run desktop
----------- -----------

View file

@ -4,14 +4,15 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QVariant QT::keepScreenOn() { QVariant QT::keepScreenOn(const QVariant& on) {
QtAndroid::runOnAndroidThread([] { QtAndroid::runOnAndroidThread([&] {
QAndroidJniObject activity = QtAndroid::androidActivity(); QAndroidJniObject activity = QtAndroid::androidActivity();
if (activity.isValid()) { if (activity.isValid()) {
QAndroidJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); QAndroidJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
if (window.isValid()) { if (window.isValid()) {
const int FLAG_KEEP_SCREEN_ON = 128; const int FLAG_KEEP_SCREEN_ON = 128;
window.callMethod<void>("addFlags", "(I)V", FLAG_KEEP_SCREEN_ON); const char* method = on.toBool() ? "addFlags" : "clearFlags";
window.callMethod<void>(method, "(I)V", FLAG_KEEP_SCREEN_ON);
} }
} }
QAndroidJniEnvironment env; QAndroidJniEnvironment env;

View file

@ -11,7 +11,7 @@ class QT : public QObject {
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE QVariant keepScreenOn(); Q_INVOKABLE QVariant keepScreenOn(const QVariant& = true);
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View file

@ -8,8 +8,9 @@
(qt:ini) (qt:ini)
#+android #+android
(progn (progn
(qt:keep-screen-on qt:*cpp*) (qlater (lambda () (qt:keep-screen-on qt:*cpp*))) ; delay until UI is loaded (see 'Settings.qml')
(ensure-permissions :access-fine-location))) (ensure-permissions :access-fine-location))
(q> |ready| ui:*position-source* t))
(defun closing () (defun closing ()
(close *log-stream*) (close *log-stream*)
@ -64,4 +65,12 @@
(round* (speed*) 1) (round* (speed*) 1)
(round* (distance)))))) (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) (qlater 'run)

View file

@ -1,14 +1,16 @@
(in-package :gps) (in-package :gps)
(defvar *distance-samples* 5)
(let ((speed 0.0) (let ((speed 0.0)
samples) samples)
(defun update-speed () (defun update-speed ()
"After 10 distance samples, calculate average speed and update on every new "After *DISTANCE-SAMPLES* samples, calculate average speed and update on
distance sample." every new distance sample."
(push (cons (distance) (push (cons (distance)
(get-internal-real-time)) (get-internal-real-time))
samples) samples)
(when (> (length samples) 10) (when (> (length samples) *distance-samples*)
(setf samples (butlast samples)) (setf samples (butlast samples))
(let ((b (first samples)) (let ((b (first samples))
(a (first (last samples)))) (a (first (last samples))))

View file

@ -3,10 +3,14 @@
(:export (:export
#:*accuracy* #:*accuracy*
#:*distance* #:*distance*
#:*max-speed*
#:*position-source*
#:*speed*)) #:*speed*))
(in-package :ui) (in-package :ui)
(defparameter *accuracy* "accuracy") (defparameter *accuracy* "accuracy")
(defparameter *distance* "distance") (defparameter *distance* "distance")
(defparameter *speed* "speed") (defparameter *max-speed* "max_speed")
(defparameter *position-source* "position_source")
(defparameter *speed* "speed")

View file

@ -8,11 +8,11 @@ Rectangle {
color: "black" color: "black"
CircularGauge { CircularGauge {
id: gauge id: speed
objectName: "speed" objectName: "speed"
anchors.fill: parent anchors.fill: parent
maximumValue: 10 maximumValue: 10
stepSize: 0.1 stepSize: 0.05
Behavior on value { Behavior on value {
NumberAnimation { NumberAnimation {
@ -23,8 +23,8 @@ Rectangle {
style: CircularGaugeStyle { style: CircularGaugeStyle {
id: style id: style
labelStepSize: 1 labelStepSize: parent.maximumValue / 10
tickmarkStepSize: 1 tickmarkStepSize: labelStepSize
minorTickmarkCount: 5 minorTickmarkCount: 5
minimumValueAngle: -90 minimumValueAngle: -90
maximumValueAngle: 90 maximumValueAngle: 90
@ -43,7 +43,7 @@ Rectangle {
ctx.strokeStyle = limitColor ctx.strokeStyle = limitColor
ctx.lineWidth = outerRadius * 0.02 ctx.lineWidth = outerRadius * 0.02
ctx.arc(outerRadius, outerRadius, outerRadius - ctx.lineWidth / 2, 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() ctx.stroke()
} }
} }

View file

@ -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 }
}
}

View file

@ -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)
}
}
}

View file

@ -14,27 +14,51 @@ Rectangle {
updateInterval: 1000 updateInterval: 1000
active: true active: true
property bool ready: false
onPositionChanged: { onPositionChanged: {
Lisp.call("gps:position-changed", if (ready) {
position.latitudeValid ? position.coordinate.latitude : null, Lisp.call("gps:position-changed",
position.longitudeValid ? position.coordinate.longitude : null, position.latitudeValid ? position.coordinate.latitude : null,
position.horizontalAccuracyValid ? position.horizontalAccuracy : null, position.longitudeValid ? position.coordinate.longitude : null,
position.speedValid ? position.speed : null, position.horizontalAccuracyValid ? position.horizontalAccuracy : null,
position.directionValid ? position.direction : null, position.speedValid ? position.speed : null,
position.timestamp.toLocaleString(Qt.locale(), "yyyy-MM-dd hh:mm:ss")) 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.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom height: 14
anchors.bottomMargin: 5 count: view.count
spacing: -5 currentIndex: view.currentIndex
Ext.LogText { objectName: "distance"; font.pixelSize: 48 } delegate: Rectangle {
Ext.LogText { objectName: "accuracy"; font.pixelSize: 24 } implicitWidth: 6
implicitHeight: 6
radius: width / 2
color: "white"
opacity: index === indicator.currentIndex ? 1 : 0.35
}
} }
// quit // quit

View file

@ -7,6 +7,18 @@ requires WearOS 2 or later.
It is assumed that you already enabled the developer settings on your watch. 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 Build 32bit android APK
@ -27,7 +39,7 @@ Now you can build the app:
``` ```
$ cd build-android $ cd build-android
$ qmake-android "CONFIG+=32bit" .. $ qmake-android .. # "CONFIG+=32bit" can be omitted here: already defined in app.pro
$ make apk $ make apk
$ ./install-run.sh $ ./install-run.sh

View file

@ -14,7 +14,12 @@ Info
This is a practical example of displaying both the speed and the whole distance 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 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. The data is automatically logged, and can be accessed with e.g.
**Device File Explorer** from Android Studio (see Help / Find Action...). **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. 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 The UI uses a `CircularGauge` for displaying the speed (by default, an average
latest 10 seconds). Additionally it shows the whole distance and the GPS of the latest 5 seconds). Additionally it shows the whole distance and the GPS
accuracy in meters. accuracy in meters.

View file

@ -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. 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 Build 32bit android APK
@ -30,7 +42,7 @@ Now you can build the app:
``` ```
$ cd build-android $ cd build-android
$ qmake-android "CONFIG+=32bit" .. $ qmake-android .. # "CONFIG+=32bit" can be omitted here: already defined in app.pro
$ make apk $ make apk
$ ./install-run.sh $ ./install-run.sh