From 9699a0ce6f00dfdce59bd5ad8a320f3f89d89a3d Mon Sep 17 00:00:00 2001 From: "pls.153" Date: Sat, 15 Jul 2023 10:48:34 +0200 Subject: [PATCH] example 'meshtastic': send phone GPS position to radio; revisions --- examples/meshtastic/app.asd | 1 + examples/meshtastic/app.pro | 2 +- examples/meshtastic/cpp/ble.cpp | 4 +- examples/meshtastic/cpp/ble.h | 1 + examples/meshtastic/cpp/qt.cpp | 53 + examples/meshtastic/cpp/qt.h | 4 + examples/meshtastic/cpp/qt.pro | 2 +- examples/meshtastic/lisp/group.lisp | 2 +- examples/meshtastic/lisp/location.lisp | 41 + examples/meshtastic/lisp/lora.lisp | 128 +- examples/meshtastic/lisp/main.lisp | 9 +- examples/meshtastic/lisp/messages.lisp | 6 +- examples/meshtastic/lisp/package.lisp | 20 +- examples/meshtastic/lisp/qt.lisp | 2 + examples/meshtastic/lisp/radios.lisp | 20 +- examples/meshtastic/lisp/ui-vars.lisp | 2 + examples/meshtastic/platforms/ios/Info.plist | 4 + examples/meshtastic/qml/ext/Group.qml | 1 + examples/meshtastic/qml/ext/Messages.qml | 8 +- examples/meshtastic/qml/ext/Radios.qml | 5 +- examples/meshtastic/qml/img/radio.png | Bin 13045 -> 23038 bytes examples/meshtastic/qml/main.qml | 26 + .../qt-location-hack/QtActivity.java | 1181 +++++++++++++++++ .../meshtastic/qt-location-hack/diff.java | 59 + .../meshtastic/qt-location-hack/readme.md | 25 + examples/meshtastic/readme-build.md | 2 + examples/meshtastic/readme-usage.md | 11 + examples/meshtastic/readme.md | 26 +- 28 files changed, 1566 insertions(+), 79 deletions(-) create mode 100644 examples/meshtastic/lisp/location.lisp create mode 100644 examples/meshtastic/qt-location-hack/QtActivity.java create mode 100644 examples/meshtastic/qt-location-hack/diff.java create mode 100644 examples/meshtastic/qt-location-hack/readme.md diff --git a/examples/meshtastic/app.asd b/examples/meshtastic/app.asd index a81f545..aa29c09 100644 --- a/examples/meshtastic/app.asd +++ b/examples/meshtastic/app.asd @@ -12,5 +12,6 @@ (:file "lisp/messages") (:file "lisp/radios") (:file "lisp/lora") + (:file "lisp/location") (:file "lisp/main"))) diff --git a/examples/meshtastic/app.pro b/examples/meshtastic/app.pro index 4c89e21..d38752c 100644 --- a/examples/meshtastic/app.pro +++ b/examples/meshtastic/app.pro @@ -27,7 +27,7 @@ QMAKE_EXTRA_COMPILERS += lisp win32: PRE_TARGETDEPS = tmp/app.lib !win32: PRE_TARGETDEPS = tmp/libapp.a -QT += quick qml bluetooth sql +QT += quick qml bluetooth sql positioning TEMPLATE = app CONFIG += c++17 no_keywords release DEFINES += DESKTOP_APP INI_ECL_CONTRIB QT_EXTENSION BACKGROUND_INI_LISP diff --git a/examples/meshtastic/cpp/ble.cpp b/examples/meshtastic/cpp/ble.cpp index 8050f4f..f9d57fb 100644 --- a/examples/meshtastic/cpp/ble.cpp +++ b/examples/meshtastic/cpp/ble.cpp @@ -31,7 +31,9 @@ void BLE::startDeviceDiscovery() { void BLE::addDevice(const QBluetoothDeviceInfo& device) { if (deviceFilter(device)) { - qDebug() << "device added:" << device.name(); + QString name(device.name()); + qDebug() << "device added:" << name; + Q_EMIT deviceDiscovered(name); } } diff --git a/examples/meshtastic/cpp/ble.h b/examples/meshtastic/cpp/ble.h index d62b427..8b4215e 100644 --- a/examples/meshtastic/cpp/ble.h +++ b/examples/meshtastic/cpp/ble.h @@ -33,6 +33,7 @@ public: Q_SIGNALS: // notify + void deviceDiscovered(const QString&); void mainServiceReady(); void deviceDisconnecting(); diff --git a/examples/meshtastic/cpp/qt.cpp b/examples/meshtastic/cpp/qt.cpp index d968a7d..3b28d96 100644 --- a/examples/meshtastic/cpp/qt.cpp +++ b/examples/meshtastic/cpp/qt.cpp @@ -1,9 +1,15 @@ #include "qt.h" #include "ble_meshtastic.h" +#include #include #include #include +#ifdef Q_OS_ANDROID + #include + #include +#endif + QT_BEGIN_NAMESPACE QObject* ini() { @@ -15,7 +21,12 @@ QObject* ini() { } QT::QT() : QObject() { + // BLE ble = new BLE_ME; + ble->connect(ble, &BLE::deviceDiscovered, + [](const QString& fullName) { + ecl_fun("radios:device-discovered", fullName.right(4)); + }); } // BLE_ME @@ -47,6 +58,48 @@ QVariant QT::write2(const QVariant& bytes) { return QVariant(); } +// GPS + +#ifdef Q_OS_ANDROID +static void clearEventualExceptions() { + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) { + env->ExceptionClear(); + } +} + +static qlonglong getLongField(const char* name) { + QAndroidJniObject activity = QtAndroid::androidActivity(); + return static_cast(activity.getField(name)); +} + +static double getDoubleField(const char* name) { + QAndroidJniObject activity = QtAndroid::androidActivity(); + return static_cast(activity.getField(name)); +} +#endif + +QVariant QT::iniPositioning() { +#ifdef Q_OS_ANDROID + QtAndroid::runOnAndroidThread([] { + QAndroidJniObject activity = QtAndroid::androidActivity(); + activity.callMethod("iniLocation", "()V"); + clearEventualExceptions(); + }); +#endif + return QVariant(); +} + +QVariant QT::lastPosition() { + QVariantList pos; +#ifdef Q_OS_ANDROID + pos << getDoubleField("_position_lat_") + << getDoubleField("_position_lon_") + << QString::number(getLongField("_position_time_")); // 'QString': see QML 'lastPosition()' +#endif + return pos; +} + // SQLite QVariant QT::iniDb(const QVariant& name) { diff --git a/examples/meshtastic/cpp/qt.h b/examples/meshtastic/cpp/qt.h index 8a4a4b5..0e2d1ee 100644 --- a/examples/meshtastic/cpp/qt.h +++ b/examples/meshtastic/cpp/qt.h @@ -25,6 +25,10 @@ public: Q_INVOKABLE QVariant read2(); Q_INVOKABLE QVariant write2(const QVariant&); + // GPS + Q_INVOKABLE QVariant iniPositioning(); + Q_INVOKABLE QVariant lastPosition(); + // SQLite Q_INVOKABLE QVariant iniDb(const QVariant&); Q_INVOKABLE QVariant sqlQuery(const QVariant&, const QVariant&); diff --git a/examples/meshtastic/cpp/qt.pro b/examples/meshtastic/cpp/qt.pro index 83a1fc7..4127a2d 100644 --- a/examples/meshtastic/cpp/qt.pro +++ b/examples/meshtastic/cpp/qt.pro @@ -1,4 +1,4 @@ -QT += bluetooth sql +QT += bluetooth sql positioning TEMPLATE = lib CONFIG += c++17 plugin release no_keywords DEFINES += PLUGIN diff --git a/examples/meshtastic/lisp/group.lisp b/examples/meshtastic/lisp/group.lisp index 80de5a6..a465e25 100644 --- a/examples/meshtastic/lisp/group.lisp +++ b/examples/meshtastic/lisp/group.lisp @@ -13,7 +13,7 @@ (defun radio-names () (qjs |radioNames| ui:*group*)) -(defun name-edited (radio name) ; called from QML +(defun name-edited (radio name) ; see QML (app:change-setting radio name :sub-key :custom-name) (values)) diff --git a/examples/meshtastic/lisp/location.lisp b/examples/meshtastic/lisp/location.lisp new file mode 100644 index 0000000..bf6b71b --- /dev/null +++ b/examples/meshtastic/lisp/location.lisp @@ -0,0 +1,41 @@ +(in-package :loc) + +(defvar *positions* nil) +(defvar *my-position* nil) + +(defun ini () + #+android + (qt:ini-positioning qt:*cpp*) + #+mobile + (progn + (q> |active| ui:*position-source* t) + (update-my-position))) + +(defun update-my-position () + "Mobile only: update position from GPS of mobile device." + (unless (getf *positions* (lora:my-num)) + (destructuring-bind (lat lon time) + #+android + (qt:last-position qt:*cpp*) + #-android + (qjs |lastPosition| ui:*position-source*) + (if (zerop lat) + (qsingle-shot 1000 'update-my-position) + (let ((pos (list :lat lat + :lon lon + :time (if (zerop (length time)) 0 (parse-integer time))))) + (setf *my-position* pos) + (qlog "position-updated: ~A" pos) + (set-position (lora:my-num) pos) + (send-to-radio)))))) ; just once on startup (for now) + +(defun send-to-radio () + (if lora:*config-complete* + (lora:send-position *my-position*) + (qsingle-shot 1000 'send-to-radio))) + +(defun set-position (node pos) + (let ((lat (getf pos :lat))) + (when (and node lat (not (zerop lat))) + (setf (getf *positions* node) pos)))) + diff --git a/examples/meshtastic/lisp/lora.lisp b/examples/meshtastic/lisp/lora.lisp index 2a3166c..4764603 100644 --- a/examples/meshtastic/lisp/lora.lisp +++ b/examples/meshtastic/lisp/lora.lisp @@ -24,12 +24,13 @@ ;;; ini/send/receive -(defvar *config-id* 0) -(defvar *notify-id* nil) -(defvar *ready* nil) -(defvar *reading* nil) -(defvar *received* nil) -(defvar *schedule-clear* nil) +(defvar *config-id* 0) +(defvar *config-complete* nil) +(defvar *notify-id* nil) +(defvar *ready* nil) +(defvar *reading* nil) +(defvar *received* nil) +(defvar *schedule-clear* t) (defun to-bytes (list) (make-array (length list) @@ -45,14 +46,15 @@ (defun start-config () (when *ready* (setf *schedule-clear* t) - (setf *channels* nil - *node-infos* nil) + (setf *config-complete* nil + *channels* nil + *node-infos* nil) (incf *config-id*) (send-to-radio (me:make-to-radio :want-config-id *config-id*)) (q> |playing| ui:*busy* t))) -(defun set-ready (name &optional (ready t)) ; called from Qt +(defun set-ready (name &optional (ready t)) ; see Qt (setf *ready* ready) (when ready (app:toast (x:cc (tr "radio") ": " name) 2) @@ -62,30 +64,40 @@ (defun add-line-breaks (text) (x:string-substitute "
" (string #\Newline) text)) +(defun my-name () + (when *config-complete* + (me:short-name (me:user *my-node-info*)))) + +(defun my-num () + (when *config-complete* + (me:num *my-node-info*))) + (defun send-message (text) "Sends TEXT to radio and adds it to QML item model." - (incf msg:*message-id*) - (when (stringp *receiver*) - (setf *receiver* (name-to-node *receiver*))) - (send-to-radio - (me:make-to-radio - :packet (me:make-mesh-packet - :from (me:num *my-node-info*) - :to *receiver* - :id msg:*message-id* - :want-ack t - :decoded (me:make-data - :portnum :text-message-app - :payload (qto-utf8 text))))) - (msg:add-message - (list :receiver (node-to-name *receiver*) - :sender (my-name) - :timestamp (princ-to-string (get-universal-time)) ; STRING for JS - :hour (timestamp-to-hour) - :text (add-line-breaks text) - :mid (princ-to-string msg:*message-id*) ; STRING for JS - :ack-state (position :sending msg:*states*) - :me t))) + (msg:check-utf8-length (q< |text| ui:*edit*)) + (unless (q< |tooLong| ui:*edit*) + (incf msg:*message-id*) + (when (stringp *receiver*) + (setf *receiver* (name-to-node *receiver*))) + (send-to-radio + (me:make-to-radio + :packet (me:make-mesh-packet + :from (my-num) + :to *receiver* + :id msg:*message-id* + :want-ack t + :decoded (me:make-data + :portnum :text-message-app + :payload (qto-utf8 text))))) + (msg:add-message + (list :receiver (node-to-name *receiver*) + :sender (my-name) + :timestamp (princ-to-string (get-universal-time)) ; STRING for JS + :hour (timestamp-to-hour) + :text (add-line-breaks text) + :mid (princ-to-string msg:*message-id*) ; STRING for JS + :ack-state (position :sending msg:*states*) + :me t)))) (defun read-radio () "Triggers a read on the radio. Will call RECEIVED-FROM-RADIO on success." @@ -99,7 +111,7 @@ (qt:write* qt:*cpp* (header (length bytes))) (qt:write* qt:*cpp* bytes)))) -(defun received-from-radio (bytes &optional notified) ; called from Qt +(defun received-from-radio (bytes &optional notified) ; see Qt (if notified (progn (setf *notify-id* bytes) @@ -110,7 +122,7 @@ (push from-radio *received*))) (values)) -(defun receiving-done () ; called from Qt +(defun receiving-done () ; see Qt (setf *reading* nil) (process-received) (values)) @@ -125,14 +137,20 @@ (when (string= name (me:short-name (me:user info))) (return (me:num info))))) -(defun my-name () - (me:short-name (me:user *my-node-info*))) - (defun timestamp-to-hour (&optional (secs (get-universal-time))) (multiple-value-bind (_ m h) (decode-universal-time secs) (format nil "~D:~2,'0D" h m))) +(defun set-gps-position (node pos) + (flet ((to-float (i) + (float (/ i (expt 10 7))))) + (when (me:latitude-i pos) + (loc:set-position node (list :lat (to-float (me:latitude-i pos)) + :lon (to-float (me:longitude-i pos)) + :alt (me:altitude pos) + :time (me:time pos)))))) + (defun process-received () "Walks *RECEIVED* FROM-RADIOs and saves relevant data." (setf *received* (nreverse *received*)) @@ -167,7 +185,12 @@ (t (qlog "message state changed: ~A" state) :not-received)) - (princ-to-string (me:request-id decoded)))))))))) ; STRING for JS + (princ-to-string (me:request-id decoded))))) ; STRING for JS + ;; GPS location + (:position-app + (unless (zerop (length payload)) + (set-gps-position (me:from packet) + (pr:deserialize-from-bytes 'me:position payload))))))))) ;; my-info ((me:from-radio.has-my-info struct) (setf *my-node-info* (me:my-node-num (me:my-info struct)))) @@ -178,6 +201,8 @@ (setf *my-node-info* info) (setf *node-infos* (nconc *node-infos* (list info)))) + (x:when-it (me:position info) + (set-gps-position (me:num info) x:it)) (when *schedule-clear* (radios:clear) (group:clear)) @@ -192,6 +217,7 @@ :node-num (princ-to-string (me:num info)) ; STRING for JS :current (equal name (app:setting :latest-receiver))))) (when (find name *ble-names* :test 'string=) + (setf radios:*found* t) (radios:add-radio (list :name name :hw-model (symbol-name (me:hw-model (me:user info))) @@ -213,6 +239,7 @@ ;; config-complete-id ((me:from-radio.has-config-complete-id struct) (when (= *config-id* (me:config-complete-id struct)) + (setf *config-complete* t) (q> |playing| ui:*busy* nil) (qlog "config-complete id: ~A" *config-id*) (unless (find (my-name) (app:setting :configured) :test 'string=) @@ -256,12 +283,12 @@ (qsleep 20) (start-device-discovery (app:setting :device))) -(defun change-region (region) ; called from QML +(defun change-region (region) ; see QML (app:change-setting :region (app:kw region)) (qlater 'change-lora-config) (values)) -(defun change-modem-preset (modem-preset) ; called from QML +(defun change-modem-preset (modem-preset) ; see QML (app:change-setting :modem-preset (app:kw modem-preset)) (qlater 'change-lora-config) (values)) @@ -277,6 +304,27 @@ :role :primary)) (change-lora-config)) +(defun send-position (pos &optional (to (my-num))) + "Send GPS position to radio." + (flet ((to-int (f) + (floor (* f (expt 10 7))))) + (send-to-radio + (me:make-to-radio + :packet (me:make-mesh-packet + :from (my-num) + :to to + :id (incf msg:*message-id*) + :hop-limit 3 + :priority :background + :decoded (me:make-data + :portnum :position-app + :payload (pr:serialize-to-bytes + (me:make-position + :latitude-i (to-int (getf pos :lat)) + :longitude-i (to-int (getf pos :lon)) + :time (getf pos :time))) + :want-response t)))))) + (defun channel-to-url (&optional channel) (let ((base64 (base64:usb8-array-to-base64-string (pr:serialize-to-bytes (or channel *my-channel*))))) @@ -295,7 +343,7 @@ (set-channel channel) channel)))) -(defun change-receiver (receiver) ; called from QML +(defun change-receiver (receiver) ; see QML (setf *receiver* (parse-integer receiver)) ; STRING for JS (app:change-setting :latest-receiver (node-to-name *receiver*)) (msg:receiver-changed) diff --git a/examples/meshtastic/lisp/main.lisp b/examples/meshtastic/lisp/main.lisp index 89f222e..0530c5b 100644 --- a/examples/meshtastic/lisp/main.lisp +++ b/examples/meshtastic/lisp/main.lisp @@ -5,6 +5,7 @@ (load-settings) (lora:ini) (db:ini) + (loc:ini) (setf msg:*message-id* (1+ (db:max-message-id))) (if (setting :latest-receiver) (msg:show-messages) @@ -12,17 +13,19 @@ (q> |playing| ui:*loading* nil) (q> |interactive| ui:*main-view* t) #+android - (ensure-permissions :bluetooth-scan :bluetooth-connect) ; android >= 12 + (progn + (ensure-permissions :access-fine-location) ; for sharing location + (ensure-permissions :bluetooth-scan :bluetooth-connect)) ; android >= 12 (lora:start-device-discovery (or (setting :device) ""))) -(defun view-index-changed (index) ; called from QML +(defun view-index-changed (index) ; see QML (when (and (= 1 index) (not (app:setting :latest-receiver))) (q> |currentIndex| ui:*main-view* 0)) (q> |visible| ui:*find* (= 1 index)) (values)) -(defun icon-press-and-hold (name) ; called from QML +(defun icon-press-and-hold (name) ; see QML (cond ((string= ui:*radio-icon* name) ;; force update devices (lora:start-device-discovery (or (setting :device) ""))) diff --git a/examples/meshtastic/lisp/messages.lisp b/examples/meshtastic/lisp/messages.lisp index 3c37838..1ea5c1a 100644 --- a/examples/meshtastic/lisp/messages.lisp +++ b/examples/meshtastic/lisp/messages.lisp @@ -52,7 +52,7 @@ (q> |currentIndex| ui:*main-view* 1) ; 'Messages' (show-messages)) -(defun check-utf8-length (text) ; called from QML +(defun check-utf8-length (&optional (text (q< |text| ui:*edit*))) ; see QML "Checks the actual number of bytes to send (e.g. an emoji is 4 utf8 bytes), because we can't exceed 234 bytes, which will give 312 bytes encoded protobuf payload." @@ -66,7 +66,7 @@ (q> |tooLong| ui:*edit* nil)))) (values)) -(defun message-press-and-hold (text) ; called from QML +(defun message-press-and-hold (text) ; see QML (set-clipboard-text text) (app:toast (tr "message copied") 2) (values)) @@ -89,7 +89,7 @@ (qjs |clearFind| ui:*messages*) (qlater (lambda () (qjs |positionViewAtEnd| ui:*message-view*)))) -(defun highlight-term (text term) ; called from QML +(defun highlight-term (text term) ; see QML "Highlights TERM in red, returns NIL if TERM is not found." (let ((len (length term)) found) diff --git a/examples/meshtastic/lisp/package.lisp b/examples/meshtastic/lisp/package.lisp index c3f9e1a..ab2628d 100644 --- a/examples/meshtastic/lisp/package.lisp +++ b/examples/meshtastic/lisp/package.lisp @@ -18,6 +18,7 @@ (:export #:*channel* #:*channels* + #:*config-complete* #:*config-lora* #:*my-node-info* #:*node-infos* @@ -34,6 +35,9 @@ #:change-modem-preset #:channel-to-url #:ini + #:my-name + #:my-num + #:send-position #:start-config #:start-device-discovery #:read-radio @@ -80,7 +84,21 @@ (defpackage :radios (:use :cl :qml) (:export + #:*found* #:add-radio #:change-radio - #:clear)) + #:clear + #:device-discovered + #:reset-configured + #:reset-default-radio)) + +(defpackage :location + (:nicknames :loc) + (:use :cl :qml) + (:export + #:*my-position* + #:*positions* + #:ini + #:set-position + #:update-my-position)) diff --git a/examples/meshtastic/lisp/qt.lisp b/examples/meshtastic/lisp/qt.lisp index a46f6ea..e3e0d66 100644 --- a/examples/meshtastic/lisp/qt.lisp +++ b/examples/meshtastic/lisp/qt.lisp @@ -4,6 +4,8 @@ #:*cpp* #:ini #:ini-db + #:ini-positioning + #:last-position #:start-device-discovery #:read* #:short-names diff --git a/examples/meshtastic/lisp/radios.lisp b/examples/meshtastic/lisp/radios.lisp index 07c4bfa..f23609b 100644 --- a/examples/meshtastic/lisp/radios.lisp +++ b/examples/meshtastic/lisp/radios.lisp @@ -1,5 +1,16 @@ (in-package :radios) +(defvar *found* nil) + +(defun device-discovered (name) + "Show discovered (cached) device, which may not be reachable / turned on." + (unless *found* + (add-radio + (list :name name + :hw-model "Meshtastic" ; we don't know yet + :current (equal name (app:setting :device)) + :ini t)))) + (defun add-radio (radio) "Adds passed RADIO (a PLIST) to QML item model. The model keys are: @@ -10,10 +21,17 @@ (setf lora:*schedule-clear* nil) (q! |clear| ui:*radios*)) -(defun change-radio (name) ; called from QML +(defun change-radio (name) ; see QML + (app:change-setting :device name) + (unless *found* + (clear)) (qlater (lambda () (lora:start-device-discovery name))) (values)) +(defun reset-default-radio () + ;; TODO: add in UI settings + (app:change-setting :device nil)) + (defun reset-configured () ;; TODO: add in UI settings (app:change-setting :configured nil)) diff --git a/examples/meshtastic/lisp/ui-vars.lisp b/examples/meshtastic/lisp/ui-vars.lisp index 5a2ff7d..1e16c37 100644 --- a/examples/meshtastic/lisp/ui-vars.lisp +++ b/examples/meshtastic/lisp/ui-vars.lisp @@ -14,6 +14,7 @@ #:*messages* #:*message-view* #:*modem* + #:*position-source* #:*radio-icon* #:*radios* #:*region* @@ -33,6 +34,7 @@ (defparameter *messages* "messages") (defparameter *message-view* "message_view") (defparameter *modem* "modem") +(defparameter *position-source* "position_source") (defparameter *radio-icon* "radio_icon") (defparameter *radios* "radios") (defparameter *region* "region") diff --git a/examples/meshtastic/platforms/ios/Info.plist b/examples/meshtastic/platforms/ios/Info.plist index 12b6697..db68fd2 100644 --- a/examples/meshtastic/platforms/ios/Info.plist +++ b/examples/meshtastic/platforms/ios/Info.plist @@ -28,6 +28,10 @@ This file was generated by Qt/QMake. NSBluetoothAlwaysUsageDescription For connecting to meshtastic radio devices. + NSLocationAlwaysUsageDescription + To share location. + NSLocationWhenInUseUsageDescription + To share location. UILaunchStoryboardName LaunchScreen UISupportedInterfaceOrientations diff --git a/examples/meshtastic/qml/ext/Group.qml b/examples/meshtastic/qml/ext/Group.qml index 4bd83f3..c6cff43 100644 --- a/examples/meshtastic/qml/ext/Group.qml +++ b/examples/meshtastic/qml/ext/Group.qml @@ -183,3 +183,4 @@ Rectangle { } } } + diff --git a/examples/meshtastic/qml/ext/Messages.qml b/examples/meshtastic/qml/ext/Messages.qml index 6366636..4feb39c 100644 --- a/examples/meshtastic/qml/ext/Messages.qml +++ b/examples/meshtastic/qml/ext/Messages.qml @@ -72,7 +72,7 @@ Rectangle { Item { id: delegate - width: Math.max(text.paintedWidth, rowSender.width + 4 * text.padding) + 2 * text.padding + width: Math.max(text.paintedWidth, rowSender.width + 4 * text.padding) + 2 * text.padding + 4 height: visible ? (text.contentHeight + 2 * text.padding + sender.contentHeight + 8) : 0 Rectangle { @@ -95,7 +95,7 @@ Rectangle { AnimatedImage { id: semaphore playing: false - y: 3 + y: 4 width: 8 height: width source: "../img/semaphore.gif" @@ -105,7 +105,7 @@ Rectangle { Text { id: sender - font.pixelSize: 11 + font.pixelSize: 12 font.family: fontText.name color: "#8B0000" text: model.senderName ? model.senderName : model.sender @@ -116,7 +116,7 @@ Rectangle { id: timestamp x: delegate.width - contentWidth - text.padding y: text.padding - font.pixelSize: 11 + font.pixelSize: 12 font.family: fontText.name color: "#505050" text: model.hour diff --git a/examples/meshtastic/qml/ext/Radios.qml b/examples/meshtastic/qml/ext/Radios.qml index cb1e70d..0faee3d 100644 --- a/examples/meshtastic/qml/ext/Radios.qml +++ b/examples/meshtastic/qml/ext/Radios.qml @@ -46,7 +46,7 @@ Rectangle { // hack to define all model key _types_ ListElement { - name: ""; hwModel: ""; batteryLevel: 0; current: false + name: ""; ini: false; hwModel: ""; batteryLevel: 0; current: false } function addRadio(radio) { @@ -66,7 +66,7 @@ Rectangle { id: delegate width: Math.min(265, view.width) height: 35 - color: (index === view.currentIndex) ? "firebrick" : "steelblue" + color: (index === view.currentIndex) ? "firebrick" : (model.ini ? "#808080" : "steelblue") radius: height / 2 Rectangle { @@ -101,6 +101,7 @@ Rectangle { anchors.right: parent.right anchors.rightMargin: 14 level: model.batteryLevel + visible: !model.ini } MouseArea { diff --git a/examples/meshtastic/qml/img/radio.png b/examples/meshtastic/qml/img/radio.png index 7b57015e98ac1abcb5a0d48c26708fcb49b96fc7..dcd5f50c1b7a5e31e788d09a8bf462ffdea9f54d 100644 GIT binary patch literal 23038 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 E067Njg#Z8m literal 13045 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 + Contact: http://www.qt.io/licensing/ + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and The Qt Company. For licensing terms + and conditions see http://www.qt.io/terms-conditions. For further + information use the contact form at http://www.qt.io/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.qtproject.qt5.android.bindings; + +import android.app.Activity; +import android.app.Dialog; +import android.app.Fragment; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources.Theme; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.os.Build; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.ActionMode; +import android.view.ActionMode.Callback; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager.LayoutParams; +import android.view.accessibility.AccessibilityEvent; + +import org.qtproject.qt5.android.QtNative; + +// for hack +import android.location.LocationManager; +import android.location.LocationListener; +import android.location.LocationRequest; +import android.util.Log; + +public class QtActivity extends Activity +{ + // hack + public double _position_lat_ = 0.0; + public double _position_lon_ = 0.0; + public double _position_alt_ = 0.0; + public long _position_time_ = 0; + private static final String LQML = "[LQML]"; + + // hack + public void iniLocation() + { + LocationListener mLocationListenerGPS = new LocationListener() { + @Override + public void onLocationChanged(android.location.Location location) { + _position_lat_ = location.getLatitude(); + _position_lon_ = location.getLongitude(); + _position_alt_ = location.getAltitude(); + _position_time_ = location.getTime(); + //String msg = "lat: " + latitude + " lon: " + longitude; + //Log.d(LQML, msg); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + //Log.d(LQML, "GPS: onStatusChanged"); + } + + @Override + public void onProviderEnabled(String provider) { + //Log.d(LQML, "GPS: onProviderEnabled"); + } + + @Override + public void onProviderDisabled(String provider) { + //Log.d(LQML, "GPS: onProviderDisabled"); + } + }; + + try { + //Log.d(LQML, "ini GPS location..."); + LocationManager mLocationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); + mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, + 2000, // 2 secs + 100, // HIGH_ACCURACY + mLocationListenerGPS); + //Log.d(LQML, "ini GPS location OK"); + } + catch (Exception e) { + Log.e(LQML, Log.getStackTraceString(e)); + } + } + + public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application, + // the parameters must not contain any white spaces + // and must be separated with "\t" + // e.g "-param1\t-param2=value2\t-param3\tvalue3" + + public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1"; + // use this variable to add any environment variables to your application. + // the env vars must be separated with "\t" + // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" + // Currently the following vars are used by the android plugin: + // * QT_USE_ANDROID_NATIVE_DIALOGS - 1 to use the android native dialogs. + + public String[] QT_ANDROID_THEMES = null; // A list with all themes that your application want to use. + // The name of the theme must be the same with any theme from + // http://developer.android.com/reference/android/R.style.html + // The most used themes are: + // * "Theme" - (fallback) check http://developer.android.com/reference/android/R.style.html#Theme + // * "Theme_Black" - check http://developer.android.com/reference/android/R.style.html#Theme_Black + // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light + // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo + // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light + // * "Theme_DeviceDefault" - check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault + // * "Theme_DeviceDefault_Light" - (default for API 14+) check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault_Light + + public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme. + + private QtActivityLoader m_loader; + public QtActivity() + { + m_loader = new QtActivityLoader(this); + if (Build.VERSION.SDK_INT >= 21) { + QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"}; + QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light"; + } else { + QT_ANDROID_THEMES = new String[] {"Theme_DeviceDefault_Light"}; + QT_ANDROID_DEFAULT_THEME = "Theme_DeviceDefault_Light"; + } + } + + + /////////////////////////// forward all notifications //////////////////////////// + /////////////////////////// Super class calls //////////////////////////////////// + /////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE ////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + + @Override + public boolean dispatchKeyEvent(KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyEvent, event); + else + return super.dispatchKeyEvent(event); + } + public boolean super_dispatchKeyEvent(KeyEvent event) + { + return super.dispatchKeyEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchPopulateAccessibilityEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchPopulateAccessibilityEvent, event); + else + return super.dispatchPopulateAccessibilityEvent(event); + } + public boolean super_dispatchPopulateAccessibilityEvent(AccessibilityEvent event) + { + return super.dispatchPopulateAccessibilityEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchTouchEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTouchEvent, ev); + else + return super.dispatchTouchEvent(ev); + } + public boolean super_dispatchTouchEvent(MotionEvent event) + { + return super.dispatchTouchEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchTrackballEvent(MotionEvent ev) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchTrackballEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTrackballEvent, ev); + else + return super.dispatchTrackballEvent(ev); + } + public boolean super_dispatchTrackballEvent(MotionEvent event) + { + return super.dispatchTrackballEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + + if (QtApplication.m_delegateObject != null && QtApplication.onActivityResult != null) { + QtApplication.invokeDelegateMethod(QtApplication.onActivityResult, requestCode, resultCode, data); + return; + } + if (requestCode == QtLoader.MINISTRO_INSTALL_REQUEST_CODE) + m_loader.startApp(false); + super.onActivityResult(requestCode, resultCode, data); + } + public void super_onActivityResult(int requestCode, int resultCode, Intent data) + { + super.onActivityResult(requestCode, resultCode, data); + } + //--------------------------------------------------------------------------- + + @Override + protected void onApplyThemeResource(Theme theme, int resid, boolean first) + { + if (!QtApplication.invokeDelegate(theme, resid, first).invoked) + super.onApplyThemeResource(theme, resid, first); + } + public void super_onApplyThemeResource(Theme theme, int resid, boolean first) + { + super.onApplyThemeResource(theme, resid, first); + } + //--------------------------------------------------------------------------- + + + @Override + protected void onChildTitleChanged(Activity childActivity, CharSequence title) + { + if (!QtApplication.invokeDelegate(childActivity, title).invoked) + super.onChildTitleChanged(childActivity, title); + } + public void super_onChildTitleChanged(Activity childActivity, CharSequence title) + { + super.onChildTitleChanged(childActivity, title); + } + //--------------------------------------------------------------------------- + + @Override + public void onConfigurationChanged(Configuration newConfig) + { + if (!QtApplication.invokeDelegate(newConfig).invoked) + super.onConfigurationChanged(newConfig); + } + public void super_onConfigurationChanged(Configuration newConfig) + { + super.onConfigurationChanged(newConfig); + } + //--------------------------------------------------------------------------- + + @Override + public void onContentChanged() + { + if (!QtApplication.invokeDelegate().invoked) + super.onContentChanged(); + } + public void super_onContentChanged() + { + super.onContentChanged(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onContextItemSelected(MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onContextItemSelected(item); + } + public boolean super_onContextItemSelected(MenuItem item) + { + return super.onContextItemSelected(item); + } + //--------------------------------------------------------------------------- + + @Override + public void onContextMenuClosed(Menu menu) + { + if (!QtApplication.invokeDelegate(menu).invoked) + super.onContextMenuClosed(menu); + } + public void super_onContextMenuClosed(Menu menu) + { + super.onContextMenuClosed(menu); + } + //--------------------------------------------------------------------------- + + protected void onCreateHook(Bundle savedInstanceState) { + m_loader.APPLICATION_PARAMETERS = APPLICATION_PARAMETERS; + m_loader.ENVIRONMENT_VARIABLES = ENVIRONMENT_VARIABLES; + m_loader.QT_ANDROID_THEMES = QT_ANDROID_THEMES; + m_loader.QT_ANDROID_DEFAULT_THEME = QT_ANDROID_DEFAULT_THEME; + m_loader.onCreate(savedInstanceState); + } + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + onCreateHook(savedInstanceState); + } + //--------------------------------------------------------------------------- + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + if (!QtApplication.invokeDelegate(menu, v, menuInfo).invoked) + super.onCreateContextMenu(menu, v, menuInfo); + } + public void super_onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + super.onCreateContextMenu(menu, v, menuInfo); + } + //--------------------------------------------------------------------------- + + @Override + public CharSequence onCreateDescription() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return (CharSequence)res.methodReturns; + else + return super.onCreateDescription(); + } + public CharSequence super_onCreateDescription() + { + return super.onCreateDescription(); + } + //--------------------------------------------------------------------------- + + @Override + protected Dialog onCreateDialog(int id) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(id); + if (res.invoked) + return (Dialog)res.methodReturns; + else + return super.onCreateDialog(id); + } + public Dialog super_onCreateDialog(int id) + { + return super.onCreateDialog(id); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreateOptionsMenu(menu); + } + public boolean super_onCreateOptionsMenu(Menu menu) + { + return super.onCreateOptionsMenu(menu); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreatePanelMenu(int featureId, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreatePanelMenu(featureId, menu); + } + public boolean super_onCreatePanelMenu(int featureId, Menu menu) + { + return super.onCreatePanelMenu(featureId, menu); + } + //--------------------------------------------------------------------------- + + + @Override + public View onCreatePanelView(int featureId) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreatePanelView(featureId); + } + public View super_onCreatePanelView(int featureId) + { + return super.onCreatePanelView(featureId); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(outBitmap, canvas); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreateThumbnail(outBitmap, canvas); + } + public boolean super_onCreateThumbnail(Bitmap outBitmap, Canvas canvas) + { + return super.onCreateThumbnail(outBitmap, canvas); + } + //--------------------------------------------------------------------------- + + @Override + public View onCreateView(String name, Context context, AttributeSet attrs) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(name, context, attrs); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreateView(name, context, attrs); + } + public View super_onCreateView(String name, Context context, AttributeSet attrs) + { + return super.onCreateView(name, context, attrs); + } + //--------------------------------------------------------------------------- + + @Override + protected void onDestroy() + { + super.onDestroy(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyDown, keyCode, event); + else + return super.onKeyDown(keyCode, event); + } + public boolean super_onKeyDown(int keyCode, KeyEvent event) + { + return super.onKeyDown(keyCode, event); + } + //--------------------------------------------------------------------------- + + + @Override + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyMultiple != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyMultiple, keyCode, repeatCount, event); + else + return super.onKeyMultiple(keyCode, repeatCount, event); + } + public boolean super_onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) + { + return super.onKeyMultiple(keyCode, repeatCount, event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyUp != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyUp, keyCode, event); + else + return super.onKeyUp(keyCode, event); + } + public boolean super_onKeyUp(int keyCode, KeyEvent event) + { + return super.onKeyUp(keyCode, event); + } + //--------------------------------------------------------------------------- + + @Override + public void onLowMemory() + { + if (!QtApplication.invokeDelegate().invoked) + super.onLowMemory(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onMenuItemSelected(featureId, item); + } + public boolean super_onMenuItemSelected(int featureId, MenuItem item) + { + return super.onMenuItemSelected(featureId, item); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onMenuOpened(int featureId, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onMenuOpened(featureId, menu); + } + public boolean super_onMenuOpened(int featureId, Menu menu) + { + return super.onMenuOpened(featureId, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onNewIntent(Intent intent) + { + if (!QtApplication.invokeDelegate(intent).invoked) + super.onNewIntent(intent); + } + public void super_onNewIntent(Intent intent) + { + super.onNewIntent(intent); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onOptionsItemSelected(item); + } + public boolean super_onOptionsItemSelected(MenuItem item) + { + return super.onOptionsItemSelected(item); + } + //--------------------------------------------------------------------------- + + @Override + public void onOptionsMenuClosed(Menu menu) + { + if (!QtApplication.invokeDelegate(menu).invoked) + super.onOptionsMenuClosed(menu); + } + public void super_onOptionsMenuClosed(Menu menu) + { + super.onOptionsMenuClosed(menu); + } + //--------------------------------------------------------------------------- + + @Override + public void onPanelClosed(int featureId, Menu menu) + { + if (!QtApplication.invokeDelegate(featureId, menu).invoked) + super.onPanelClosed(featureId, menu); + } + public void super_onPanelClosed(int featureId, Menu menu) + { + super.onPanelClosed(featureId, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPause() + { + super.onPause(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPostCreate(Bundle savedInstanceState) + { + super.onPostCreate(savedInstanceState); + QtApplication.invokeDelegate(savedInstanceState); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPostResume() + { + super.onPostResume(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPrepareDialog(int id, Dialog dialog) + { + if (!QtApplication.invokeDelegate(id, dialog).invoked) + super.onPrepareDialog(id, dialog); + } + public void super_onPrepareDialog(int id, Dialog dialog) + { + super.onPrepareDialog(id, dialog); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onPrepareOptionsMenu(Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onPrepareOptionsMenu(menu); + } + public boolean super_onPrepareOptionsMenu(Menu menu) + { + return super.onPrepareOptionsMenu(menu); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onPreparePanel(int featureId, View view, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, view, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onPreparePanel(featureId, view, menu); + } + public boolean super_onPreparePanel(int featureId, View view, Menu menu) + { + return super.onPreparePanel(featureId, view, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onRestart() + { + super.onRestart(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) + { + if (!QtApplication.invokeDelegate(savedInstanceState).invoked) + super.onRestoreInstanceState(savedInstanceState); + } + public void super_onRestoreInstanceState(Bundle savedInstanceState) + { + super.onRestoreInstanceState(savedInstanceState); + } + //--------------------------------------------------------------------------- + + @Override + protected void onResume() + { + super.onResume(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + public Object onRetainNonConfigurationInstance() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return res.methodReturns; + else + return super.onRetainNonConfigurationInstance(); + } + public Object super_onRetainNonConfigurationInstance() + { + return super.onRetainNonConfigurationInstance(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onSaveInstanceState(Bundle outState) + { + if (!QtApplication.invokeDelegate(outState).invoked) + super.onSaveInstanceState(outState); + } + public void super_onSaveInstanceState(Bundle outState) + { + super.onSaveInstanceState(outState); + + } + //--------------------------------------------------------------------------- + + @Override + public boolean onSearchRequested() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onSearchRequested(); + } + public boolean super_onSearchRequested() + { + return super.onSearchRequested(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onStart() + { + super.onStart(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onStop() + { + super.onStop(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onTitleChanged(CharSequence title, int color) + { + if (!QtApplication.invokeDelegate(title, color).invoked) + super.onTitleChanged(title, color); + } + public void super_onTitleChanged(CharSequence title, int color) + { + super.onTitleChanged(title, color); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onTouchEvent(MotionEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onTouchEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTouchEvent, event); + else + return super.onTouchEvent(event); + } + public boolean super_onTouchEvent(MotionEvent event) + { + return super.onTouchEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onTrackballEvent(MotionEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onTrackballEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTrackballEvent, event); + else + return super.onTrackballEvent(event); + } + public boolean super_onTrackballEvent(MotionEvent event) + { + return super.onTrackballEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public void onUserInteraction() + { + if (!QtApplication.invokeDelegate().invoked) + super.onUserInteraction(); + } + public void super_onUserInteraction() + { + super.onUserInteraction(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onUserLeaveHint() + { + if (!QtApplication.invokeDelegate().invoked) + super.onUserLeaveHint(); + } + public void super_onUserLeaveHint() + { + super.onUserLeaveHint(); + } + //--------------------------------------------------------------------------- + + @Override + public void onWindowAttributesChanged(LayoutParams params) + { + if (!QtApplication.invokeDelegate(params).invoked) + super.onWindowAttributesChanged(params); + } + public void super_onWindowAttributesChanged(LayoutParams params) + { + super.onWindowAttributesChanged(params); + } + //--------------------------------------------------------------------------- + + @Override + public void onWindowFocusChanged(boolean hasFocus) + { + if (!QtApplication.invokeDelegate(hasFocus).invoked) + super.onWindowFocusChanged(hasFocus); + } + public void super_onWindowFocusChanged(boolean hasFocus) + { + super.onWindowFocusChanged(hasFocus); + } + //--------------------------------------------------------------------------- + + //////////////// Activity API 5 ///////////// +//@ANDROID-5 + @Override + public void onAttachedToWindow() + { + if (!QtApplication.invokeDelegate().invoked) + super.onAttachedToWindow(); + } + public void super_onAttachedToWindow() + { + super.onAttachedToWindow(); + } + //--------------------------------------------------------------------------- + + @Override + public void onBackPressed() + { + if (!QtApplication.invokeDelegate().invoked) + super.onBackPressed(); + } + public void super_onBackPressed() + { + super.onBackPressed(); + } + //--------------------------------------------------------------------------- + + @Override + public void onDetachedFromWindow() + { + if (!QtApplication.invokeDelegate().invoked) + super.onDetachedFromWindow(); + } + public void super_onDetachedFromWindow() + { + super.onDetachedFromWindow(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyLongPress != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyLongPress, keyCode, event); + else + return super.onKeyLongPress(keyCode, event); + } + public boolean super_onKeyLongPress(int keyCode, KeyEvent event) + { + return super.onKeyLongPress(keyCode, event); + } + //--------------------------------------------------------------------------- +//@ANDROID-5 + +//////////////// Activity API 8 ///////////// +//@ANDROID-8 +@Override + protected Dialog onCreateDialog(int id, Bundle args) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(id, args); + if (res.invoked) + return (Dialog)res.methodReturns; + else + return super.onCreateDialog(id, args); + } + public Dialog super_onCreateDialog(int id, Bundle args) + { + return super.onCreateDialog(id, args); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPrepareDialog(int id, Dialog dialog, Bundle args) + { + if (!QtApplication.invokeDelegate(id, dialog, args).invoked) + super.onPrepareDialog(id, dialog, args); + } + public void super_onPrepareDialog(int id, Dialog dialog, Bundle args) + { + super.onPrepareDialog(id, dialog, args); + } + //--------------------------------------------------------------------------- +//@ANDROID-8 + //////////////// Activity API 11 ///////////// + +//@ANDROID-11 + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyShortcutEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyShortcutEvent, event); + else + return super.dispatchKeyShortcutEvent(event); + } + public boolean super_dispatchKeyShortcutEvent(KeyEvent event) + { + return super.dispatchKeyShortcutEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public void onActionModeFinished(ActionMode mode) + { + if (!QtApplication.invokeDelegate(mode).invoked) + super.onActionModeFinished(mode); + } + public void super_onActionModeFinished(ActionMode mode) + { + super.onActionModeFinished(mode); + } + //--------------------------------------------------------------------------- + + @Override + public void onActionModeStarted(ActionMode mode) + { + if (!QtApplication.invokeDelegate(mode).invoked) + super.onActionModeStarted(mode); + } + public void super_onActionModeStarted(ActionMode mode) + { + super.onActionModeStarted(mode); + } + //--------------------------------------------------------------------------- + + @Override + public void onAttachFragment(Fragment fragment) + { + if (!QtApplication.invokeDelegate(fragment).invoked) + super.onAttachFragment(fragment); + } + public void super_onAttachFragment(Fragment fragment) + { + super.onAttachFragment(fragment); + } + //--------------------------------------------------------------------------- + + @Override + public View onCreateView(View parent, String name, Context context, AttributeSet attrs) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(parent, name, context, attrs); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreateView(parent, name, context, attrs); + } + public View super_onCreateView(View parent, String name, Context context, + AttributeSet attrs) { + return super.onCreateView(parent, name, context, attrs); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyShortcut(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyShortcut != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyShortcut, keyCode,event); + else + return super.onKeyShortcut(keyCode, event); + } + public boolean super_onKeyShortcut(int keyCode, KeyEvent event) + { + return super.onKeyShortcut(keyCode, event); + } + //--------------------------------------------------------------------------- + + @Override + public ActionMode onWindowStartingActionMode(Callback callback) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(callback); + if (res.invoked) + return (ActionMode)res.methodReturns; + else + return super.onWindowStartingActionMode(callback); + } + public ActionMode super_onWindowStartingActionMode(Callback callback) + { + return super.onWindowStartingActionMode(callback); + } + //--------------------------------------------------------------------------- +//@ANDROID-11 + //////////////// Activity API 12 ///////////// + +//@ANDROID-12 + @Override + public boolean dispatchGenericMotionEvent(MotionEvent ev) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchGenericMotionEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchGenericMotionEvent, ev); + else + return super.dispatchGenericMotionEvent(ev); + } + public boolean super_dispatchGenericMotionEvent(MotionEvent event) + { + return super.dispatchGenericMotionEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onGenericMotionEvent(MotionEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onGenericMotionEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onGenericMotionEvent, event); + else + return super.onGenericMotionEvent(event); + } + public boolean super_onGenericMotionEvent(MotionEvent event) + { + return super.onGenericMotionEvent(event); + } + //--------------------------------------------------------------------------- +//@ANDROID-12 + + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + if (QtApplication.m_delegateObject != null && QtApplication.onRequestPermissionsResult != null) { + QtApplication.invokeDelegateMethod(QtApplication.onRequestPermissionsResult, requestCode , permissions, grantResults); + return; + } + } + + public void bringChildToBack(int id) + { + QtNative.activityDelegate().bringChildToBack(id); + } + + public void bringChildToFront(int id) + { + QtNative.activityDelegate().bringChildToFront(id); + } + + public void closeContextMenu() + { + QtNative.activityDelegate().closeContextMenu(); + } + + public void createSurface(int id, boolean onTop, int x, int y, int w, int h, int imageDepth) + { + QtNative.activityDelegate().createSurface(id, onTop, x, y, w, h, imageDepth); + } + + public void destroySurface(int id) + { + QtNative.activityDelegate().destroySurface(id); + } + + public int getSurfaceCount() + { + return QtNative.activityDelegate().getSurfaceCount(); + } + + public void hideSoftwareKeyboard() + { + QtNative.activityDelegate().hideSoftwareKeyboard(); + } + + public void hideSplashScreen() + { + QtNative.activityDelegate().hideSplashScreen(); + } + + public void hideSplashScreen(final int duration) + { + QtNative.activityDelegate().hideSplashScreen(duration); + } + + public void initializeAccessibility() + { + QtNative.activityDelegate().initializeAccessibility(); + } + + public void insertNativeView(int id, View view, int x, int y, int w, int h) + { + QtNative.activityDelegate().insertNativeView(id, view, x, y, w, h); + } + + public boolean loadApplication(Activity activity, ClassLoader classLoader, Bundle loaderParams) + { + return QtNative.activityDelegate().loadApplication(activity, classLoader, loaderParams); + } + + public void onCreatePopupMenu(Menu menu) + { + QtNative.activityDelegate().onCreatePopupMenu(menu); + } + + public void onTerminate() + { + QtNative.activityDelegate().onTerminate(); + } + + public void openContextMenu(final int x, final int y, final int w, final int h) + { + QtNative.activityDelegate().openContextMenu(x, y, w, h); + } + + public void resetOptionsMenu() + { + QtNative.activityDelegate().resetOptionsMenu(); + } + + public void resetSoftwareKeyboard() + { + QtNative.activityDelegate().resetSoftwareKeyboard(); + } + + public boolean setKeyboardVisibility(boolean visibility, long timeStamp) + { + return QtNative.activityDelegate().setKeyboardVisibility(visibility, timeStamp); + } + + public void setSurfaceGeometry(int id, int x, int y, int w, int h) + { + QtNative.activityDelegate().setSurfaceGeometry(id, x, y, w, h); + } + + public void showSoftwareKeyboard(final int x, final int y, final int width, + final int height, final int inputHints, + final int enterKeyType) + { + QtNative.activityDelegate().showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType); + } + + public boolean startApplication() + { + return QtNative.activityDelegate().startApplication(); + } + + public void updateFullScreen() + { + QtNative.activityDelegate().updateFullScreen(); + } + + public void updateHandles(int mode, int editX, int editY, int editButtons, + int x1, int y1, int x2, int y2, boolean rtl) + { + QtNative.activityDelegate().updateHandles(mode, editX, editY, editButtons, x1, y1, x2, y2, rtl); + } + + public void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd) + { + QtNative.activityDelegate().updateSelection(selStart, selEnd, candidatesStart, candidatesEnd); + } + + public void setFullScreen(boolean enterFullScreen) + { + QtNative.activityDelegate().setFullScreen(enterFullScreen); + } +} diff --git a/examples/meshtastic/qt-location-hack/diff.java b/examples/meshtastic/qt-location-hack/diff.java new file mode 100644 index 0000000..abba2cf --- /dev/null +++ b/examples/meshtastic/qt-location-hack/diff.java @@ -0,0 +1,59 @@ +64a65,70 +> // for hack +> import android.location.LocationManager; +> import android.location.LocationListener; +> import android.location.LocationRequest; +> import android.util.Log; +> +66a73,123 +> // hack +> public double _position_lat_ = 0.0; +> public double _position_lon_ = 0.0; +> public double _position_alt_ = 0.0; +> public long _position_time_ = 0; +> private static final String LQML = "[LQML]"; +> +> // hack +> public void iniLocation() +> { +> LocationListener mLocationListenerGPS = new LocationListener() { +> @Override +> public void onLocationChanged(android.location.Location location) { +> _position_lat_ = location.getLatitude(); +> _position_lon_ = location.getLongitude(); +> _position_alt_ = location.getAltitude(); +> _position_time_ = location.getTime(); +> //String msg = "lat: " + latitude + " lon: " + longitude; +> //Log.d(LQML, msg); +> } +> +> @Override +> public void onStatusChanged(String provider, int status, Bundle extras) { +> //Log.d(LQML, "GPS: onStatusChanged"); +> } +> +> @Override +> public void onProviderEnabled(String provider) { +> //Log.d(LQML, "GPS: onProviderEnabled"); +> } +> +> @Override +> public void onProviderDisabled(String provider) { +> //Log.d(LQML, "GPS: onProviderDisabled"); +> } +> }; +> +> try { +> //Log.d(LQML, "ini GPS location..."); +> LocationManager mLocationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE); +> mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, +> 2000, // 2 secs +> 100, // HIGH_ACCURACY +> mLocationListenerGPS); +> //Log.d(LQML, "ini GPS location OK"); +> } +> catch (Exception e) { +> Log.e(LQML, Log.getStackTraceString(e)); +> } +> } +> diff --git a/examples/meshtastic/qt-location-hack/readme.md b/examples/meshtastic/qt-location-hack/readme.md new file mode 100644 index 0000000..1c30eb2 --- /dev/null +++ b/examples/meshtastic/qt-location-hack/readme.md @@ -0,0 +1,25 @@ + +**android only** + + +Why +--- + +This is needed on android because a mobile device which doesn't move only +receives one (or maybe two) GPS updates before stopping the updates. + +Since we have a long startup time with this app, we would completely lose +the first GPS updates using the convenient QML wrapper `PositionSource`, +hence not being able to communicate our position to the radio device. + +The only workaround seems to be to directly access the Java `Location` +functions. + + +HowTo +----- + +Just copy `QtActivity.java` to path in `path.txt` (for Qt 5.15); +alternatively apply patch `git.diff`. + +See also [cpp/qt.cpp](../cpp/qt.cpp). diff --git a/examples/meshtastic/readme-build.md b/examples/meshtastic/readme-build.md index 55c7a3e..8d13aea 100644 --- a/examples/meshtastic/readme-build.md +++ b/examples/meshtastic/readme-build.md @@ -10,6 +10,8 @@ See also notes in [my-cl-protobufs.asd](my-cl-protobufs.asd). You will also need **uiop** installed under e.g. `~/quicklisp/local-projects/` (see ASDF sources). +For android please see [qt-location-hack](qt-location-hack/). + Prepare diff --git a/examples/meshtastic/readme-usage.md b/examples/meshtastic/readme-usage.md index 2b513d5..39d3f08 100644 --- a/examples/meshtastic/readme-usage.md +++ b/examples/meshtastic/readme-usage.md @@ -64,6 +64,10 @@ If you use more than 1 radio, just switch here to the radio you want to use. Changing a radio will take several seconds, because the initial configuration needs to be repeated. +Initially, the list will contain gray items, so you can choose another radio +if you experience that the connection didn't work, in case the selected radio +is currently not available. + A press-and-hold on the radio icon will restart bluetooth device discovery. This may be useful if you forgot to enable bluetooth before starting the app, or if your radio is not being discovered the first time. @@ -79,6 +83,13 @@ Switching to **Group**, a red circle with the number of unread messages is shown on the right of every person. +GPS position +------------ + +On mobile, and if the radio doesn't have a GPS module, the location of the +phone is sent once (at startup) to the radio. + + Tips ---- diff --git a/examples/meshtastic/readme.md b/examples/meshtastic/readme.md index a13895f..d264655 100644 --- a/examples/meshtastic/readme.md +++ b/examples/meshtastic/readme.md @@ -5,8 +5,11 @@ Info Please note: this is **WIP!** Currently it can be used to send direct messages between any number of radios. -Eventually it will (hopefully) catch up with the official app versions. - + +It's basically meant to be used in an emergency situation, where internet is +not available, in order to communicate with simple text messages. This kind of +mesh network is limited to about 70 nodes/radios/users to remain reliable. + Technical notes @@ -82,25 +85,6 @@ See also [readme-usage](readme-usage.md). -TODO / ideas ------------- - -(1) Since this uses Lisp, there's the possibility to integrate a programmable -interface, where users can define their own functions and extend the UI. - -Think of simple scripts, which send protobufs to the radio and process the -received data, while having access to all the variables and functions of -the app itself. - -Those extensions could also be shared among users. - -(2) Regarding encryption: since I don't like QR codes for sharing a channel, -there's the possibility to just share a made up phrase which can easily be -remembered, and use the letters of the phrase as encryption key, so people can -share their channel in a simple way. - - - Run --- ```