mirror of
https://gitlab.com/eql/lqml.git
synced 2025-12-06 02:30:38 -08:00
new example: 'wear-os-gps' (meant for use on e.g. a canoe)
This commit is contained in:
parent
33d7ed3cce
commit
ab22dd809f
50 changed files with 920 additions and 3 deletions
10
examples/wear-os-gps/app.asd
Normal file
10
examples/wear-os-gps/app.asd
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
(defsystem :app
|
||||||
|
:serial t
|
||||||
|
:depends-on ()
|
||||||
|
:components ((:file "lisp/package")
|
||||||
|
(:file "lisp/ui-vars")
|
||||||
|
(:file "lisp/kalman")
|
||||||
|
(:file "lisp/distance")
|
||||||
|
(:file "lisp/speed")
|
||||||
|
(:file "lisp/qt")
|
||||||
|
(:file "lisp/main")))
|
||||||
84
examples/wear-os-gps/app.pro
Normal file
84
examples/wear-os-gps/app.pro
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
CONFIG += 32bit
|
||||||
|
|
||||||
|
LISP_FILES = $$files(lisp/*) app.asd make.lisp
|
||||||
|
|
||||||
|
android {
|
||||||
|
32bit {
|
||||||
|
ECL = $$(ECL_ANDROID_32)
|
||||||
|
} else {
|
||||||
|
ECL = $$(ECL_ANDROID)
|
||||||
|
}
|
||||||
|
lisp.commands = $$ECL/../ecl-android-host/bin/ecl \
|
||||||
|
-norc -shell $$PWD/make.lisp
|
||||||
|
} else:unix {
|
||||||
|
lisp.commands = /usr/local/bin/ecl -shell $$PWD/make.lisp
|
||||||
|
} else:win32 {
|
||||||
|
lisp.commands = ecl.exe -shell $$PWD/make.lisp
|
||||||
|
}
|
||||||
|
|
||||||
|
lisp.input = LISP_FILES
|
||||||
|
|
||||||
|
win32: lisp.output = tmp/app.lib
|
||||||
|
!win32: lisp.output = tmp/libapp.a
|
||||||
|
|
||||||
|
QMAKE_EXTRA_COMPILERS += lisp
|
||||||
|
|
||||||
|
win32: PRE_TARGETDEPS = tmp/app.lib
|
||||||
|
!win32: PRE_TARGETDEPS = tmp/libapp.a
|
||||||
|
|
||||||
|
QT += quick qml sensors positioning
|
||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += c++17 no_keywords release
|
||||||
|
DEFINES = DESKTOP_APP INI_LISP #INI_ECL_CONTRIB
|
||||||
|
INCLUDEPATH = /usr/local/include
|
||||||
|
LIBS = -L/usr/local/lib -lecl
|
||||||
|
DESTDIR = .
|
||||||
|
TARGET = app
|
||||||
|
OBJECTS_DIR = tmp
|
||||||
|
MOC_DIR = tmp
|
||||||
|
|
||||||
|
linux: LIBS += -L../../../platforms/linux/lib
|
||||||
|
macx: LIBS += -L../../../platforms/macos/lib
|
||||||
|
win32: LIBS += -L../../../platforms/windows/lib
|
||||||
|
|
||||||
|
win32 {
|
||||||
|
include(../../src/windows.pri)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
QT += androidextras
|
||||||
|
#DEFINES += INI_ASDF
|
||||||
|
DEFINES -= DESKTOP_APP
|
||||||
|
DEFINES += QT_EXTENSION
|
||||||
|
INCLUDEPATH = $$ECL/include
|
||||||
|
LIBS = -L$$ECL/lib -lecl
|
||||||
|
LIBS += -L../../../platforms/android/lib
|
||||||
|
HEADERS += cpp/qt.h
|
||||||
|
SOURCES += cpp/qt.cpp
|
||||||
|
|
||||||
|
ANDROID_EXTRA_LIBS += $$ECL/lib/libecl.so
|
||||||
|
ANDROID_PACKAGE_SOURCE_DIR = ../platforms/android
|
||||||
|
|
||||||
|
32bit {
|
||||||
|
ANDROID_ABIS = "armeabi-v7a"
|
||||||
|
} else {
|
||||||
|
ANDROID_ABIS = "arm64-v8a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
32bit {
|
||||||
|
LIBS += -llqml32 -llisp32
|
||||||
|
} else {
|
||||||
|
LIBS += -llqml -llisp
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBS += -Ltmp -lapp
|
||||||
|
HEADERS += ../../src/cpp/main.h
|
||||||
|
SOURCES += ../../src/cpp/main.cpp
|
||||||
|
|
||||||
|
RESOURCES += $$files(qml/*)
|
||||||
|
RESOURCES += $$files(i18n/*.qm)
|
||||||
|
|
||||||
|
lupdate_only {
|
||||||
|
SOURCES += i18n/tr.h
|
||||||
|
}
|
||||||
3
examples/wear-os-gps/build-android/install-run.sh
Executable file
3
examples/wear-os-gps/build-android/install-run.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# install/update (keeps app data)
|
||||||
|
adb install -r android-build/*.apk
|
||||||
|
adb shell am start -n org.qtproject.example.gps/org.qtproject.qt5.android.bindings.QtActivity # Qt5
|
||||||
25
examples/wear-os-gps/cpp/qt.cpp
Normal file
25
examples/wear-os-gps/cpp/qt.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "qt.h"
|
||||||
|
#include <QtAndroid>
|
||||||
|
#include <QAndroidJniEnvironment>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
QVariant QT::keepScreenOn() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QAndroidJniEnvironment env;
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
17
examples/wear-os-gps/cpp/qt.h
Normal file
17
examples/wear-os-gps/cpp/qt.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
extern "C" { QObject* ini(); }
|
||||||
|
|
||||||
|
class QT : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE QVariant keepScreenOn();
|
||||||
|
};
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
89
examples/wear-os-gps/kml/kml.lisp
Normal file
89
examples/wear-os-gps/kml/kml.lisp
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
;;; usage: ecl -shell kml.lisp
|
||||||
|
|
||||||
|
(in-package :cl-user)
|
||||||
|
|
||||||
|
(defun file-contents (file)
|
||||||
|
(with-open-file (s file)
|
||||||
|
(let ((str (make-string (file-length s))))
|
||||||
|
(read-sequence str s)
|
||||||
|
str)))
|
||||||
|
|
||||||
|
(defvar *kml-1* (file-contents "template-1.kml"))
|
||||||
|
(defvar *kml-2* (file-contents "template-2.kml"))
|
||||||
|
(defvar *kml-3* (file-contents "template-3.kml"))
|
||||||
|
(defvar *kml-4* (file-contents "template-4.kml"))
|
||||||
|
(defvar *kml-5* (file-contents "template-5.kml"))
|
||||||
|
|
||||||
|
(defun cc (&rest strings)
|
||||||
|
(apply 'concatenate 'string strings))
|
||||||
|
|
||||||
|
(defun split (str &optional (sep #\,))
|
||||||
|
(unless (zerop (length str))
|
||||||
|
(let (list)
|
||||||
|
(do ((e (position sep str) (position sep str :start (1+ e)))
|
||||||
|
(b 0 (1+ e)))
|
||||||
|
((not e) (push (subseq str b) list))
|
||||||
|
(push (subseq str b e) list))
|
||||||
|
(nreverse list))))
|
||||||
|
|
||||||
|
(defun generate-kml (file)
|
||||||
|
(let* ((name (cc "logs/" file "."))
|
||||||
|
(csv (cc name "csv"))
|
||||||
|
(kml (cc name "kml"))
|
||||||
|
(kmz (cc name "kmz")))
|
||||||
|
(unless (probe-file kmz)
|
||||||
|
(format t "~&generating ~S~%" kmz)
|
||||||
|
(let* ((point "point.png")
|
||||||
|
(header "<h2>~A</h2>~%")
|
||||||
|
(row "<tr><td class=s~D>~A</td><td>~A</td></tr>~%")
|
||||||
|
(lines (with-open-file (s csv)
|
||||||
|
(loop :for line = (split (read-line s nil nil))
|
||||||
|
:while line :collect line)))
|
||||||
|
(i-timestamp 0)
|
||||||
|
(i-accuracy 3)
|
||||||
|
(i-lat 4)
|
||||||
|
(i-lon 5)
|
||||||
|
(i-direction 7)
|
||||||
|
(i-speed 8)
|
||||||
|
(i-distance 9)
|
||||||
|
(*print-pretty* nil))
|
||||||
|
(dolist (file (list kml kmz))
|
||||||
|
(when (probe-file file)
|
||||||
|
(delete-file file)))
|
||||||
|
(with-open-file (s kml :direction :output :if-exists :supersede)
|
||||||
|
(write-string *kml-1* s)
|
||||||
|
(dolist (line lines)
|
||||||
|
(format s "~A,~A,0 "
|
||||||
|
(nth i-lon line)
|
||||||
|
(nth i-lat line)))
|
||||||
|
(write-string *kml-2* s)
|
||||||
|
(let ((c 0))
|
||||||
|
(dolist (line lines)
|
||||||
|
(when (zerop (mod (incf c) 60)) ; a marker every minute
|
||||||
|
(write-string *kml-3* s)
|
||||||
|
(format s header (nth i-timestamp line))
|
||||||
|
(write-line "<table>" s)
|
||||||
|
;; table of log values
|
||||||
|
(format s row 2
|
||||||
|
(nth i-distance line)
|
||||||
|
"m")
|
||||||
|
(format s row 2
|
||||||
|
(nth i-speed line)
|
||||||
|
"km/h")
|
||||||
|
(format s row 1
|
||||||
|
(nth i-accuracy line)
|
||||||
|
"acc")
|
||||||
|
(format s row 1
|
||||||
|
(nth i-direction line)
|
||||||
|
"°")
|
||||||
|
;; position
|
||||||
|
(format s *kml-4*
|
||||||
|
(nth i-lon line)
|
||||||
|
(nth i-lat line)))))
|
||||||
|
(write-string *kml-5* s))
|
||||||
|
(ext:run-program "zip" (list kmz kml point))
|
||||||
|
(delete-file kml)))))
|
||||||
|
|
||||||
|
(dolist (file (directory "logs/*.csv"))
|
||||||
|
(generate-kml (pathname-name file)))
|
||||||
|
|
||||||
BIN
examples/wear-os-gps/kml/point.png
Normal file
BIN
examples/wear-os-gps/kml/point.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
47
examples/wear-os-gps/kml/template-1.kml
Normal file
47
examples/wear-os-gps/kml/template-1.kml
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<Document>
|
||||||
|
<name>LQML Example GPS.kml</name>
|
||||||
|
<Style id="point">
|
||||||
|
<IconStyle>
|
||||||
|
<scale>1.0</scale>
|
||||||
|
<Icon>
|
||||||
|
<href>point.png</href>
|
||||||
|
</Icon>
|
||||||
|
<hotSpot x="100" y="75" xunits="pixels" yunits="pixels"/>
|
||||||
|
</IconStyle>
|
||||||
|
<LineStyle>
|
||||||
|
<color>ff0000ff</color>
|
||||||
|
<width>6</width>
|
||||||
|
</LineStyle>
|
||||||
|
</Style>
|
||||||
|
<Style id="point_hl">
|
||||||
|
<IconStyle>
|
||||||
|
<scale>1.8</scale>
|
||||||
|
<Icon>
|
||||||
|
<href>point.png</href>
|
||||||
|
</Icon>
|
||||||
|
<hotSpot x="100" y="75" xunits="pixels" yunits="pixels"/>
|
||||||
|
</IconStyle>
|
||||||
|
<LineStyle>
|
||||||
|
<color>ff0000ff</color>
|
||||||
|
<width>6</width>
|
||||||
|
</LineStyle>
|
||||||
|
</Style>
|
||||||
|
<StyleMap id="marker">
|
||||||
|
<Pair>
|
||||||
|
<key>normal</key>
|
||||||
|
<styleUrl>#point</styleUrl>
|
||||||
|
</Pair>
|
||||||
|
<Pair>
|
||||||
|
<key>highlight</key>
|
||||||
|
<styleUrl>#point_hl</styleUrl>
|
||||||
|
</Pair>
|
||||||
|
</StyleMap>
|
||||||
|
<Placemark>
|
||||||
|
<name></name>
|
||||||
|
<styleUrl>#marker</styleUrl>
|
||||||
|
<LineString>
|
||||||
|
<tessellate>0</tessellate>
|
||||||
|
<altitudemode>relativeToGround</altitudemode>
|
||||||
|
<coordinates>
|
||||||
4
examples/wear-os-gps/kml/template-2.kml
Normal file
4
examples/wear-os-gps/kml/template-2.kml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
</coordinates>
|
||||||
|
</LineString>
|
||||||
|
</Placemark>
|
||||||
11
examples/wear-os-gps/kml/template-3.kml
Normal file
11
examples/wear-os-gps/kml/template-3.kml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<Placemark>
|
||||||
|
<styleUrl>#marker</styleUrl>
|
||||||
|
<description><![CDATA[
|
||||||
|
<style>
|
||||||
|
.s1 { font-size: 16px; text-align: right; font-weight: bold; }
|
||||||
|
.s2 { font-size: 22px; text-align: right; font-weight: bold; }
|
||||||
|
td { font-size: 10px; }
|
||||||
|
table { border-collapse: collapse; margin: auto; width: 90%; }
|
||||||
|
table, th, td { border: 1px solid #d0d0d0; }
|
||||||
|
h2 { text-align: center; color: blue; }
|
||||||
|
</style>
|
||||||
6
examples/wear-os-gps/kml/template-4.kml
Normal file
6
examples/wear-os-gps/kml/template-4.kml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
</table>
|
||||||
|
]]></description>
|
||||||
|
<Point>
|
||||||
|
<coordinates>~A,~A,0</coordinates>
|
||||||
|
</Point>
|
||||||
|
</Placemark>
|
||||||
2
examples/wear-os-gps/kml/template-5.kml
Normal file
2
examples/wear-os-gps/kml/template-5.kml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
</Document>
|
||||||
|
</kml>
|
||||||
69
examples/wear-os-gps/lisp/distance.lisp
Normal file
69
examples/wear-os-gps/lisp/distance.lisp
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
(in-package :gps)
|
||||||
|
|
||||||
|
(defconstant +earth-mean-radius+ 6371.0072d0)
|
||||||
|
|
||||||
|
(defun to-rad (deg)
|
||||||
|
(/ (* deg pi) 180))
|
||||||
|
|
||||||
|
(defun to-deg (rad)
|
||||||
|
(/ (* 180 rad) pi))
|
||||||
|
|
||||||
|
(defun point-distance (from to)
|
||||||
|
"Coordiante distance according to Haversine formula."
|
||||||
|
(let ((from-x (car from))
|
||||||
|
(from-y (cdr from))
|
||||||
|
(to-x (car to))
|
||||||
|
(to-y (cdr to)))
|
||||||
|
(if (and (numberp from-x)
|
||||||
|
(numberp to-x))
|
||||||
|
(let* ((dlat (to-rad (- to-x from-x)))
|
||||||
|
(dlon (to-rad (- to-y from-y)))
|
||||||
|
(h-dlat (sin (/ dlat 2)))
|
||||||
|
(h-dlon (sin (/ dlon 2))))
|
||||||
|
(setf h-dlat (expt h-dlat 2)
|
||||||
|
h-dlon (expt h-dlon 2))
|
||||||
|
(let* ((y (+ h-dlat (* (cos (to-rad from-x))
|
||||||
|
(cos (to-rad to-x))
|
||||||
|
h-dlon)))
|
||||||
|
(x (* 2 (asin (sqrt y)))))
|
||||||
|
(* x +earth-mean-radius+ 1000)))
|
||||||
|
0)))
|
||||||
|
|
||||||
|
(let ((positions-distance 0)
|
||||||
|
(distance 0)
|
||||||
|
position-1
|
||||||
|
position-2
|
||||||
|
constant-direction)
|
||||||
|
(defun update-distance ()
|
||||||
|
"Calculate distance of farest points with same direction
|
||||||
|
(fallback: re-calculate after a certain distance)."
|
||||||
|
(when (and *direction*
|
||||||
|
(not constant-direction))
|
||||||
|
(setf constant-direction *direction*))
|
||||||
|
(let ((pos (cons kal:*lat* kal:*lon*))
|
||||||
|
(reset (if constant-direction
|
||||||
|
(or (>= (abs (- *direction* constant-direction))
|
||||||
|
15) ; not too small (not accurate with low speed)
|
||||||
|
(>= positions-distance 75)) ; 75 m
|
||||||
|
(>= positions-distance 25)))) ; 25 m
|
||||||
|
(if reset
|
||||||
|
(progn
|
||||||
|
(setf constant-direction *direction*)
|
||||||
|
(if position-1
|
||||||
|
(progn
|
||||||
|
(setf position-2 pos)
|
||||||
|
(incf distance (positions-distance))
|
||||||
|
(setf positions-distance 0)
|
||||||
|
(shiftf position-1 position-2 nil))
|
||||||
|
(setf position-1 pos)))
|
||||||
|
(progn
|
||||||
|
(if position-1
|
||||||
|
(setf position-2 pos)
|
||||||
|
(setf position-1 pos))
|
||||||
|
(setf positions-distance (positions-distance))))))
|
||||||
|
(defun positions-distance ()
|
||||||
|
(if position-2
|
||||||
|
(point-distance position-1 position-2)
|
||||||
|
0))
|
||||||
|
(defun distance ()
|
||||||
|
(+ distance positions-distance)))
|
||||||
37
examples/wear-os-gps/lisp/kalman.lisp
Normal file
37
examples/wear-os-gps/lisp/kalman.lisp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
(defpackage :kalman
|
||||||
|
(:use :cl)
|
||||||
|
(:nicknames :kal)
|
||||||
|
(:export
|
||||||
|
#:*lat*
|
||||||
|
#:*lon*
|
||||||
|
#:*timestamp*
|
||||||
|
#:*variance*
|
||||||
|
#:filter
|
||||||
|
#:reset))
|
||||||
|
|
||||||
|
(in-package :kal)
|
||||||
|
|
||||||
|
(defvar *lat* nil)
|
||||||
|
(defvar *lon* nil)
|
||||||
|
(defvar *timestamp* nil)
|
||||||
|
(defvar *variance* nil)
|
||||||
|
|
||||||
|
(defun reset ()
|
||||||
|
(setf *variance* nil))
|
||||||
|
|
||||||
|
(defun filter (lat lon accuracy speed)
|
||||||
|
(when (and lat lon accuracy speed)
|
||||||
|
(setf accuracy (max 1 accuracy))
|
||||||
|
(let ((timestamp (get-internal-real-time)))
|
||||||
|
(if *variance*
|
||||||
|
(let ((time-inc (- timestamp *timestamp*)))
|
||||||
|
(incf *variance* (/ (* time-inc (expt speed 2)) 1000))
|
||||||
|
(setf *timestamp* timestamp)
|
||||||
|
(let ((k (/ *variance* (+ *variance* (expt accuracy 2)))))
|
||||||
|
(incf *lat* (* k (- lat *lat*)))
|
||||||
|
(incf *lon* (* k (- lon *lon*)))
|
||||||
|
(setf *variance* (* (- 1 k) *variance*))))
|
||||||
|
(setf *lat* lat
|
||||||
|
*lon* lon
|
||||||
|
*timestamp* timestamp
|
||||||
|
*variance* (expt accuracy 2))))))
|
||||||
59
examples/wear-os-gps/lisp/main.lisp
Normal file
59
examples/wear-os-gps/lisp/main.lisp
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
(in-package :gps)
|
||||||
|
|
||||||
|
(defvar *start-time* nil)
|
||||||
|
(defvar *direction* nil)
|
||||||
|
(defvar *log-stream* nil)
|
||||||
|
|
||||||
|
(defun run ()
|
||||||
|
(qt:ini)
|
||||||
|
#+android
|
||||||
|
(progn
|
||||||
|
(qt:keep-screen-on qt:*cpp*)
|
||||||
|
(ensure-permissions :access-fine-location)))
|
||||||
|
|
||||||
|
(defun closing ()
|
||||||
|
(close *log-stream*)
|
||||||
|
(qquit))
|
||||||
|
|
||||||
|
(defun round* (x &optional (digits 0))
|
||||||
|
(when (numberp x)
|
||||||
|
(let* ((f (expt 10 digits))
|
||||||
|
(r (/ (truncate (+ 0.5 (* f x)))
|
||||||
|
f)))
|
||||||
|
(if (zerop digits)
|
||||||
|
r
|
||||||
|
(float r)))))
|
||||||
|
|
||||||
|
(defun str (x)
|
||||||
|
(cond ((stringp x)
|
||||||
|
x)
|
||||||
|
((null x)
|
||||||
|
"")
|
||||||
|
(t
|
||||||
|
(princ-to-string x))))
|
||||||
|
|
||||||
|
(defun position-changed (lat lon accuracy speed direction timestamp)
|
||||||
|
(unless *start-time*
|
||||||
|
(setf *start-time* timestamp)
|
||||||
|
(setf *log-stream* (open (format nil "LOG ~A.csv" (substitute #\. #\: timestamp))
|
||||||
|
:direction :output :if-does-not-exist :create)))
|
||||||
|
(when (and lat lon accuracy)
|
||||||
|
(when direction
|
||||||
|
(setf *direction* direction))
|
||||||
|
(kal:filter lat lon accuracy speed)
|
||||||
|
(update-distance)
|
||||||
|
(update-speed)
|
||||||
|
(q> |value| ui:*speed* (speed*))
|
||||||
|
(q> |text| ui:*distance* (str (round* (distance))))
|
||||||
|
(q> |text| ui:*accuracy* (str (round* accuracy 1)))
|
||||||
|
(when kal:*lat*
|
||||||
|
(format *log-stream* "~A,~F,~F,~F,~F,~F,~A,~A,~A,~A~%"
|
||||||
|
timestamp
|
||||||
|
lat lon (round* accuracy 1)
|
||||||
|
kal:*lat* kal:*lon*
|
||||||
|
(str (round* speed 1))
|
||||||
|
(str (round* direction))
|
||||||
|
(round* (speed*) 1)
|
||||||
|
(round* (distance))))))
|
||||||
|
|
||||||
|
(qlater 'run)
|
||||||
3
examples/wear-os-gps/lisp/package.lisp
Normal file
3
examples/wear-os-gps/lisp/package.lisp
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
(defpackage :gps
|
||||||
|
(:use :cl :qml)
|
||||||
|
(:export))
|
||||||
17
examples/wear-os-gps/lisp/qt.lisp
Normal file
17
examples/wear-os-gps/lisp/qt.lisp
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
(defpackage :qt
|
||||||
|
(:use :cl :qml)
|
||||||
|
(:export
|
||||||
|
#:*cpp*
|
||||||
|
#:ini
|
||||||
|
#:keep-screen-on))
|
||||||
|
|
||||||
|
(in-package :qt)
|
||||||
|
|
||||||
|
(defvar *cpp* nil)
|
||||||
|
|
||||||
|
(defun ini ()
|
||||||
|
#+android
|
||||||
|
(progn
|
||||||
|
(setf *cpp* (qfind-child nil "QT"))
|
||||||
|
(let ((*package* (find-package :qt)))
|
||||||
|
(define-qt-wrappers *cpp*))))
|
||||||
20
examples/wear-os-gps/lisp/speed.lisp
Normal file
20
examples/wear-os-gps/lisp/speed.lisp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
(in-package :gps)
|
||||||
|
|
||||||
|
(let ((speed 0.0)
|
||||||
|
samples)
|
||||||
|
(defun update-speed ()
|
||||||
|
"After 10 distance samples, calculate average speed and update on every new
|
||||||
|
distance sample."
|
||||||
|
(push (cons (distance)
|
||||||
|
(get-internal-real-time))
|
||||||
|
samples)
|
||||||
|
(when (> (length samples) 10)
|
||||||
|
(setf samples (butlast samples))
|
||||||
|
(let ((b (first samples))
|
||||||
|
(a (first (last samples))))
|
||||||
|
(setf speed (* 3.6 ; km/h
|
||||||
|
(/ (- (car b) (car a)) ; m
|
||||||
|
(/ (- (cdr b) (cdr a)) ; s
|
||||||
|
1000.0)))))))
|
||||||
|
(defun speed* ()
|
||||||
|
speed))
|
||||||
12
examples/wear-os-gps/lisp/ui-vars.lisp
Normal file
12
examples/wear-os-gps/lisp/ui-vars.lisp
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
(defpackage ui
|
||||||
|
(:use :cl :qml)
|
||||||
|
(:export
|
||||||
|
#:*accuracy*
|
||||||
|
#:*distance*
|
||||||
|
#:*speed*))
|
||||||
|
|
||||||
|
(in-package :ui)
|
||||||
|
|
||||||
|
(defparameter *accuracy* "accuracy")
|
||||||
|
(defparameter *distance* "distance")
|
||||||
|
(defparameter *speed* "speed")
|
||||||
60
examples/wear-os-gps/make.lisp
Normal file
60
examples/wear-os-gps/make.lisp
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
;; check target
|
||||||
|
(let ((arg (first (ext:command-args))))
|
||||||
|
(mapc (lambda (name feature)
|
||||||
|
(when (search name arg)
|
||||||
|
(pushnew feature *features*)))
|
||||||
|
(list "/ecl-android/" "/ecl-ios/")
|
||||||
|
(list :android :ios)))
|
||||||
|
|
||||||
|
#+(or android ios)
|
||||||
|
(pushnew :mobile *features*)
|
||||||
|
|
||||||
|
(require :asdf)
|
||||||
|
|
||||||
|
(push (merge-pathnames "../")
|
||||||
|
asdf:*central-registry*)
|
||||||
|
|
||||||
|
(setf *default-pathname-defaults*
|
||||||
|
(truename (merge-pathnames "../../../"))) ; LQML root
|
||||||
|
|
||||||
|
(defvar *current*
|
||||||
|
(let ((name (namestring *load-truename*)))
|
||||||
|
(subseq name
|
||||||
|
(length (namestring *default-pathname-defaults*))
|
||||||
|
(1+ (position #\/ name :from-end t)))))
|
||||||
|
|
||||||
|
(dolist (file (list "package" "x" "ecl-ext" "ini" "qml")) ; load LQML symbols
|
||||||
|
(load (merge-pathnames file "src/lisp/")))
|
||||||
|
|
||||||
|
(defun cc (&rest args)
|
||||||
|
(apply 'concatenate 'string args))
|
||||||
|
|
||||||
|
#-mobile
|
||||||
|
(asdf:make-build "app"
|
||||||
|
:monolithic t
|
||||||
|
:type :static-library
|
||||||
|
:move-here (cc *current* "build/tmp/")
|
||||||
|
:init-name "ini_app")
|
||||||
|
|
||||||
|
#+mobile
|
||||||
|
(progn
|
||||||
|
(pushnew :interpreter *features*)
|
||||||
|
(defvar *asdf-system* "app")
|
||||||
|
(defvar *ql-libs* (cc *current* "ql-libs.lisp"))
|
||||||
|
(defvar *init-name* "ini_app")
|
||||||
|
(defvar *library-path* (format nil "~Abuild-~A/tmp/"
|
||||||
|
*current*
|
||||||
|
#+android "android"
|
||||||
|
#+ios "ios"))
|
||||||
|
(defvar *require* (list :ecl-curl))
|
||||||
|
(load "platforms/shared/make"))
|
||||||
|
|
||||||
|
;; rename lib
|
||||||
|
(let* ((from #-mobile (cc *current* "build/tmp/app--all-systems.a")
|
||||||
|
#+mobile (cc *library-path* "app--all-systems.a"))
|
||||||
|
(to "libapp.a")
|
||||||
|
(to* #-mobile (cc *current* "build/tmp/" to)
|
||||||
|
#+mobile (cc *library-path* to)))
|
||||||
|
(when (probe-file to*)
|
||||||
|
(delete-file to*))
|
||||||
|
(rename-file from to))
|
||||||
78
examples/wear-os-gps/platforms/android/AndroidManifest.xml
Normal file
78
examples/wear-os-gps/platforms/android/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<manifest package="org.qtproject.example.gps" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
|
||||||
|
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||||
|
Remove the comment if you do not require these default permissions. -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
|
||||||
|
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||||
|
Remove the comment if you do not require these default features. -->
|
||||||
|
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
|
||||||
|
|
||||||
|
<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="GPS" android:extractNativeLibs="true">
|
||||||
|
<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="GPS" android:screenOrientation="unspecified" android:launchMode="singleTop" android:theme="@android:style/Theme.DeviceDefault">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
<!-- Application arguments -->
|
||||||
|
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
||||||
|
<!-- Application arguments -->
|
||||||
|
<meta-data android:name="android.app.lib_name" android:value="app"/>
|
||||||
|
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||||
|
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||||
|
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||||
|
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||||
|
<!-- Deploy Qt libs as part of package -->
|
||||||
|
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
|
||||||
|
<!-- Run with local libs -->
|
||||||
|
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
|
||||||
|
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||||
|
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
||||||
|
<meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidExtras.jar:jar/QtAndroidBearer.jar:jar/QtPositioning.jar"/>
|
||||||
|
<meta-data android:name="android.app.static_init_classes" android:value="org.qtproject.qt5.android.positioning.QtPositioning"/>
|
||||||
|
<!-- Used to specify custom system library path to run with local system libs -->
|
||||||
|
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
||||||
|
<!-- Messages maps -->
|
||||||
|
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||||
|
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||||
|
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||||
|
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
||||||
|
<!-- Messages maps -->
|
||||||
|
<!-- Splash screen -->
|
||||||
|
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
||||||
|
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
|
||||||
|
are done populating your window with content. -->
|
||||||
|
<!-- 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" android:resource="@drawable/logo"/ -->
|
||||||
|
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
||||||
|
<!-- Splash screen -->
|
||||||
|
<!-- Background running -->
|
||||||
|
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||||
|
application still try to draw after
|
||||||
|
"applicationStateChanged(Qt::ApplicationSuspended)"
|
||||||
|
signal is sent! -->
|
||||||
|
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||||
|
<!-- Background running -->
|
||||||
|
<!-- auto screen scale factor -->
|
||||||
|
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
||||||
|
<!-- auto screen scale factor -->
|
||||||
|
<!-- extract android style -->
|
||||||
|
<!-- available android:values :
|
||||||
|
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
||||||
|
* full - useful QWidget & Quick Controls 1 apps
|
||||||
|
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
|
||||||
|
* none - useful for apps that don't use any of the above Qt modules
|
||||||
|
-->
|
||||||
|
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
||||||
|
<!-- extract android style -->
|
||||||
|
</activity>
|
||||||
|
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
92
examples/wear-os-gps/qml/ext/Gauge.qml
Normal file
92
examples/wear-os-gps/qml/ext/Gauge.qml
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import QtQuick.Extras 1.4
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
CircularGauge {
|
||||||
|
id: gauge
|
||||||
|
objectName: "speed"
|
||||||
|
anchors.fill: parent
|
||||||
|
maximumValue: 10
|
||||||
|
stepSize: 0.1
|
||||||
|
|
||||||
|
Behavior on value {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
style: CircularGaugeStyle {
|
||||||
|
id: style
|
||||||
|
labelStepSize: 1
|
||||||
|
tickmarkStepSize: 1
|
||||||
|
minorTickmarkCount: 5
|
||||||
|
minimumValueAngle: -90
|
||||||
|
maximumValueAngle: 90
|
||||||
|
|
||||||
|
property int limit: parent.maximumValue * 8/10
|
||||||
|
property string baseColor: "white"
|
||||||
|
property string limitColor: "orange"
|
||||||
|
|
||||||
|
function toRad(deg) { return deg * (Math.PI / 180) }
|
||||||
|
|
||||||
|
background: Canvas {
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d")
|
||||||
|
ctx.reset()
|
||||||
|
ctx.beginPath()
|
||||||
|
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))
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tickmark: Rectangle {
|
||||||
|
implicitWidth: outerRadius * 0.02
|
||||||
|
antialiasing: true
|
||||||
|
implicitHeight: outerRadius * 0.06
|
||||||
|
color: styleData.value >= limit ? limitColor : baseColor
|
||||||
|
}
|
||||||
|
|
||||||
|
minorTickmark: Rectangle {
|
||||||
|
visible: styleData.value < limit
|
||||||
|
implicitWidth: outerRadius * 0.01
|
||||||
|
antialiasing: true
|
||||||
|
implicitHeight: outerRadius * 0.03
|
||||||
|
color: baseColor
|
||||||
|
}
|
||||||
|
|
||||||
|
tickmarkLabel: Text {
|
||||||
|
font.pixelSize: Math.max(6, outerRadius * 0.15)
|
||||||
|
font.bold: true
|
||||||
|
text: styleData.value
|
||||||
|
color: styleData.value >= limit ? limitColor : baseColor
|
||||||
|
antialiasing: true
|
||||||
|
}
|
||||||
|
|
||||||
|
needle: Rectangle {
|
||||||
|
y: outerRadius * 0.15
|
||||||
|
implicitWidth: outerRadius * 0.07
|
||||||
|
implicitHeight: outerRadius * 0.85
|
||||||
|
radius: implicitWidth / 2
|
||||||
|
antialiasing: true
|
||||||
|
color: baseColor
|
||||||
|
}
|
||||||
|
|
||||||
|
foreground: Rectangle {
|
||||||
|
width: outerRadius * 0.15
|
||||||
|
height: width
|
||||||
|
radius: width / 2
|
||||||
|
color: baseColor
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
examples/wear-os-gps/qml/ext/LogText.qml
Normal file
7
examples/wear-os-gps/qml/ext/LogText.qml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: "white"
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
48
examples/wear-os-gps/qml/main.qml
Normal file
48
examples/wear-os-gps/qml/main.qml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtSensors 5.15
|
||||||
|
import QtPositioning 5.15
|
||||||
|
import "ext/" as Ext
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 210 // for desktop
|
||||||
|
height: 210
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
PositionSource {
|
||||||
|
objectName: "position_source"
|
||||||
|
updateInterval: 1000
|
||||||
|
active: true
|
||||||
|
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.Gauge {}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 5
|
||||||
|
spacing: -5
|
||||||
|
|
||||||
|
Ext.LogText { objectName: "distance"; font.pixelSize: 48 }
|
||||||
|
Ext.LogText { objectName: "accuracy"; font.pixelSize: 24 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// quit
|
||||||
|
|
||||||
|
Keys.onPressed: {
|
||||||
|
if (event.key === Qt.Key_Back) {
|
||||||
|
event.accepted = true
|
||||||
|
Lisp.call("gps:closing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,7 +42,6 @@ Log note: for showing only your own messages, see `log.sh`.
|
||||||
Important note (Qt bug)
|
Important note (Qt bug)
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
|
||||||
After editing [AndroidManifest.xml](platforms/android/AndroidManifest.xml)
|
After editing [AndroidManifest.xml](platforms/android/AndroidManifest.xml)
|
||||||
in **Qt Creator** you need to add this inside `activity`:
|
in **Qt Creator** you need to add this inside `activity`:
|
||||||
```
|
```
|
||||||
59
examples/wear-os-gps/readme.md
Normal file
59
examples/wear-os-gps/readme.md
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
Prepare
|
||||||
|
-------
|
||||||
|
|
||||||
|
Please copy the app template files first:
|
||||||
|
```
|
||||||
|
$ cd ..
|
||||||
|
$ ./copy.sh wear-os-gps
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The data is automatically logged, and can be accessed with e.g.
|
||||||
|
**Device File Explorer** from Android Studio (see Help / Find Action...).
|
||||||
|
|
||||||
|
The path of the log files is:
|
||||||
|
```
|
||||||
|
/data/data/org.qtproject.example.gps/files/
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then put those log files in directory [kml/logs/](kml/logs/) and run
|
||||||
|
```
|
||||||
|
cd kml
|
||||||
|
|
||||||
|
ecl -shell kml.lisp
|
||||||
|
```
|
||||||
|
This will generate **kmz** files of all log files, which can then be viewed
|
||||||
|
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
|
||||||
|
accuracy in meters.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Run (desktop demo)
|
||||||
|
------------------
|
||||||
|
```
|
||||||
|
lqml run.lisp
|
||||||
|
```
|
||||||
|
Optionally pass `-slime` to start a Swank server, and connect from Emacs with
|
||||||
|
`M-x slime-connect`.
|
||||||
|
|
||||||
|
During development you can pass `-auto`, which will releoad all QML files after
|
||||||
|
you made a change to any of them and saved it. For re-initialization after
|
||||||
|
reloading, file `lisp/qml-reload/on-reloaded` will be loaded.
|
||||||
|
|
||||||
|
Closing the window quits the app. If you try to kill it with `ctrl-c`, you need
|
||||||
|
an additional `ctrl-d` to exit from ECL. To quit from Slime, do `(qq)` which is
|
||||||
|
short for `(qquit)`.
|
||||||
|
|
||||||
2
examples/wear-os-heart/.gitignore
vendored
Normal file
2
examples/wear-os-heart/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
5
examples/wear-os-heart/build-android/connect.sh
Executable file
5
examples/wear-os-heart/build-android/connect.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# IP of your watch / run and confirm on the watch
|
||||||
|
|
||||||
|
adb kill-server
|
||||||
|
adb connect 192.168.1.?:5555
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<manifest package="org.qtproject.example.app" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
|
<manifest package="org.qtproject.example.heart" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="30"/>
|
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="30"/>
|
||||||
<uses-feature android:name="android.hardware.type.watch"/>
|
<uses-feature android:name="android.hardware.type.watch"/>
|
||||||
<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"/>
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
52
examples/wear-os-heart/readme-build.md
Normal file
52
examples/wear-os-heart/readme-build.md
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
|
|
||||||
|
This will only work with Qt 5.15 (customized AndroidManifest.xml file) and
|
||||||
|
requires WearOS 2 or later.
|
||||||
|
|
||||||
|
You'll also need an easy to apply hack for the **heart rate sensor**, see
|
||||||
|
[qt-sensor-hack](qt-sensor-hack/).
|
||||||
|
|
||||||
|
It is assumed that you already enabled the developer settings on your watch.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Build 32bit android APK
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
First you need to connect your device through WiFi. Please set your watch IP
|
||||||
|
address in [build-android/connect.sh](build-android/connect.sh) and run:
|
||||||
|
```
|
||||||
|
$ ./build-android/connect.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
You probably want to build a **32bit** version, because most watches are 32bit.
|
||||||
|
|
||||||
|
For this you first need to build a 32bit version of the LQML library, see
|
||||||
|
[readme-prepare-android.md](../../readme-prepare-android.md).
|
||||||
|
|
||||||
|
Now you can build the app:
|
||||||
|
```
|
||||||
|
$ cd build-android
|
||||||
|
|
||||||
|
$ qmake-android "CONFIG+=32bit" ..
|
||||||
|
$ make apk
|
||||||
|
|
||||||
|
$ ./install-run.sh
|
||||||
|
```
|
||||||
|
Log note: for showing only your own messages, see `log.sh`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Important note (Qt bug)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
After editing [AndroidManifest.xml](platforms/android/AndroidManifest.xml)
|
||||||
|
in **Qt Creator** you need to add this inside `activity`:
|
||||||
|
```
|
||||||
|
android:theme="@android:style/Theme.DeviceDefault"
|
||||||
|
```
|
||||||
|
If you omit the above setting, the app will not start and just show an error
|
||||||
|
message instead.
|
||||||
|
|
||||||
|
|
@ -5,7 +5,7 @@ Prepare
|
||||||
Please copy the app template files first:
|
Please copy the app template files first:
|
||||||
```
|
```
|
||||||
$ cd ..
|
$ cd ..
|
||||||
$ ./copy.sh wear-os
|
$ ./copy.sh wear-os-heart
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
BIN
screenshots/wear-os-gps.png
Normal file
BIN
screenshots/wear-os-gps.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Loading…
Add table
Add a link
Reference in a new issue