mirror of
https://gitlab.com/eql/lqml.git
synced 2025-12-06 02:30:38 -08:00
add settings to example 'wear-os-gps'
This commit is contained in:
parent
de50d90245
commit
f1a7d0093c
13 changed files with 174 additions and 36 deletions
|
|
@ -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
|
||||
-----------
|
||||
|
|
|
|||
|
|
@ -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<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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))))
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
examples/wear-os-gps/qml/ext/MainView.qml
Normal file
19
examples/wear-os-gps/qml/ext/MainView.qml
Normal 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 }
|
||||
}
|
||||
}
|
||||
46
examples/wear-os-gps/qml/ext/Settings.qml
Normal file
46
examples/wear-os-gps/qml/ext/Settings.qml
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue