diff --git a/.gitignore b/.gitignore
index aa3808c..c9e89eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,82 +1,2 @@
-# This file is used to ignore files which are generated
-# ----------------------------------------------------------------------------
-
-*~
-*.autosave
-*.a
-*.core
-*.moc
-*.o
-*.obj
-*.orig
-*.rej
-*.so
-*.so.*
-*_pch.h.cpp
-*_resource.rc
-*.qm
-.#*
-*.*#
-core
-!core/
-tags
-.DS_Store
-.directory
-*.debug
-Makefile*
-*.prl
-*.app
-moc_*.cpp
-ui_*.h
-qrc_*.cpp
-Thumbs.db
-*.res
-*.rc
-/.qmake.cache
-/.qmake.stash
-
-# qtcreator generated files
-*.pro.user*
-*.qbs.user*
-CMakeLists.txt.user*
-
-# xemacs temporary files
-*.flc
-
-# Vim temporary files
-.*.swp
-
-# Visual Studio generated files
-*.ib_pdb_index
-*.idb
-*.ilk
-*.pdb
-*.sln
-*.suo
-*.vcproj
-*vcproj.*.*.user
-*.ncb
-*.sdf
-*.opensdf
-*.vcxproj
-*vcxproj.*
-
-# MinGW generated files
-*.Debug
-*.Release
-
-# Python byte code
-*.pyc
-
-# Binaries
-# --------
-*.dll
-*.exe
-
-# Directories with generated files
-.moc/
-.obj/
-.pch/
-.rcc/
-.uic/
-/build*/
+build/*
+.qtcreator/*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2b80b0d..957c9c2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,111 +1,69 @@
cmake_minimum_required(VERSION 3.16)
-project(waterLevelSensor VERSION 0.1 LANGUAGES CXX)
+project(WaterLevelRewrite VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTORCC ON)
-find_package(Qt6 REQUIRED COMPONENTS Quick Network CorePrivate)
+find_package(Qt6 REQUIRED COMPONENTS Gui Core Quick Multimedia)
-qt_standard_project_setup(REQUIRES 6.11)
-
-qt_add_executable(appwaterLevelSensor
- main.cpp
- images.qrc
-)
-
-
-if(ANDROID)
-
-include(FetchContent)
-FetchContent_Declare(
- android_openssl
- DOWNLOAD_EXTRACT_TIMESTAMP true
- URL https://github.com/KDAB/android_openssl/archive/refs/heads/master.zip
-)
-FetchContent_MakeAvailable(android_openssl)
-include(${android_openssl_SOURCE_DIR}/android_openssl.cmake)
-
-qt_add_library(service SHARED
- servicemain.cpp
- service.h service.cpp
-)
-
-
-set_property(TARGET appwaterLevelSensor APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
- ${CMAKE_CURRENT_SOURCE_DIR}/android)
-set_property(TARGET service APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
- ${CMAKE_CURRENT_SOURCE_DIR}/android)
-
-
-target_link_libraries(service
- PRIVATE
- Qt::Core
- Qt::Network
- Qt::CorePrivate
- Qt::Quick
-)
-qt_add_qml_module(appwaterLevelSensor
- URI waterLevelSensor
- QML_FILES
- Main.qml
- RESOURCES
- RESOURCES images.qrc
- SOURCES moving_container.h
- SOURCES configinterface.h configinterface.cpp
- SOURCES udplistener.h udplistener.cpp
- SOURCES service.h
- RESOURCES README.md
-)
-add_android_openssl_libraries(service appwaterLevelSensor)
-target_link_libraries(appwaterLevelSensor
- PRIVATE Qt6::Quick Qt6::Network service
-)
-
-qt_finalize_executable(appwaterLevelSensor)
-add_dependencies(appwaterLevelSensor service)
-
-qt_generate_deploy_app_script(
- TARGET appwaterLevelSensor
- OUTPUT_SCRIPT deploy_script
- NO_UNSUPPORTED_PLATFORM_ERROR
-)
-install(SCRIPT ${deploy_script})
+qt_standard_project_setup(REQUIRES 6.10)
+if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/importedcontent/CMakeLists.txt")
+ add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/importedcontent)
endif()
-if(NOT ANDROID)
-qt_add_qml_module(appwaterLevelSensor
- URI waterLevelSensor
+
+qt_add_executable(appWaterLevelRewrite
+ main.cpp
+)
+
+qt_add_qml_module(appWaterLevelRewrite
+ URI WaterLevelRewrite
QML_FILES
Main.qml
- RESOURCES
- RESOURCES images.qrc
- SOURCES udplistener.h udplistener.cpp
- SOURCES moving_container.h
- SOURCES configinterface.h configinterface.cpp
+ WaterTank.qml
+ SettingsBarIcon.qml
+ SettingsPopup.qml
+ ConnectButton.qml
+ SOURCES
+ configinterface.h
+ configinterface.cpp
+ requestworker.h
+ requestworker.cpp
+ wifihelper.h
+ wifihelper.cpp
+ RESOURCES
+ media.qrc
+ QML_FILES CustomCheckBox.qml
+ QML_FILES RequestResponsePopup.qml
+ QML_FILES ColorInput.qml
)
-# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
-# If you are developing for iOS or macOS you should consider setting an
-# explicit, fixed bundle identifier manu ally though.
-set_target_properties(appwaterLevelSensor PROPERTIES
-# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appwaterLevelSensor
- MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
- MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
- MACOSX_BUNDLE TRUE
- WIN32_EXECUTABLE TRUE
+if(APPLE)
+ set_target_properties(appWaterLevelRewrite PROPERTIES
+ # MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appWaterLevelRewrite
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ )
+endif()
+
+if(WIN32)
+ set_target_properties(appWaterLevelRewrite PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ )
+endif()
+
+set_target_properties(appWaterLevelRewrite PROPERTIES
+ QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
)
-target_link_libraries(appwaterLevelSensor
- PRIVATE Qt6::Quick Qt6::Network
+target_link_libraries(appWaterLevelRewrite
+ PRIVATE Qt6::Quick Qt6::Multimedia Qt6::Gui Qt6::Core
)
-qt_finalize_executable(appwaterLevelSensor)
-
-include(GNUInstallDirs)
-install(TARGETS appwaterLevelSensor
+install(TARGETS appWaterLevelRewrite
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
-endif()
diff --git a/ColorInput.qml b/ColorInput.qml
new file mode 100644
index 0000000..a4cebe7
--- /dev/null
+++ b/ColorInput.qml
@@ -0,0 +1,21 @@
+import QtQuick
+import QtQuick.Effects
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+TextField {
+ id: color_text
+ text: "F05FAF5F"
+ property alias color_text: base.color
+ Rectangle {
+ id: base
+ anchors {
+ right: parent.right
+ rightMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ width: 10
+ height: parent.height / 2
+ color: "#" + color_text.text
+ }
+}
diff --git a/ConnectButton.qml b/ConnectButton.qml
new file mode 100644
index 0000000..dd058bc
--- /dev/null
+++ b/ConnectButton.qml
@@ -0,0 +1,63 @@
+import QtQuick
+import QtQuick.Effects
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Rectangle {
+ id: root
+ property color accent_color: "#00000000"
+ property color font_color: "#00000000"
+ property int font_enlarged_text_size: 20
+ property int font_normal_text_size: 16
+ property string text: "Connect"
+ property string text2: "Stop"
+ property int mode: 0
+ signal connect;
+ layer.enabled: true
+ layer.effect: MultiEffect {
+ shadowEnabled: true
+ }
+
+ Text {
+ anchors.centerIn: parent
+ font.pointSize: root.font_enlarged_text_size
+ color: root.font_color
+ text: mode ? root.text2 : root.text
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ radius: 5
+ border {
+ color: root.accent_color
+ width: 0.5
+ }
+ MouseArea {
+ id: settings_button_area
+ anchors.fill: parent
+ hoverEnabled: true
+ property color settings_rect_pressed_prev: "#00000000"
+ property color settings_rect_entered_prev: "#00000000"
+ onEntered: {
+ settings_rect_entered_prev = root.color
+ root.color = "#5A1F1F1F"
+ }
+ onExited: {
+ root.color = settings_rect_entered_prev
+ }
+ onPressed: {
+ settings_rect_pressed_prev = root.color
+ root.color = "#5A1F4F1F"
+ root.connect();
+ switchMode();
+ }
+ onReleased: {
+ root.color = settings_rect_pressed_prev
+ }
+ }
+ function switchMode() {
+ if(root.mode === 0)
+ root.mode = 1
+ else
+ root.mode = 0;
+ }
+}
diff --git a/CustomCheckBox.qml b/CustomCheckBox.qml
new file mode 100644
index 0000000..0cdbb38
--- /dev/null
+++ b/CustomCheckBox.qml
@@ -0,0 +1,17 @@
+import QtQuick
+import QtQuick.Controls.Basic
+
+CheckBox {
+ id: control
+ property color font_color: "#0000FF00"
+ property int font_point_size: 22
+ font.pointSize: font_point_size
+ checked: true
+ contentItem: Text {
+ text: control.text
+ font: control.font
+ color: control.font_color
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: control.indicator.width + control.spacing
+ }
+}
diff --git a/Main.qml b/Main.qml
index e7844b9..28693c5 100644
--- a/Main.qml
+++ b/Main.qml
@@ -1,71 +1,59 @@
import QtQuick
-import waterLevelSensor
+import WaterLevelRewrite
import QtQuick.Effects
import QtQuick.Controls
import QtQuick.Controls.Material
Window {
- width: 640
- height: 1080
+ width: 960
+ height: 1400
visible: true
- title: qsTr("Hello World")
+ title: qsTr("Water Level Rewrite")
id: root
property color accent_color: "#F05FAF5F"
property color dark_accent_color: "#FF5F7F5F"
property color danger_color: "#F0AF2F1F"
property color water_color: "#F00F7FAF"
+ property color water_color_above_alert: "#F00FAF0F"
+ property color background_color: "#1F1F1F"
Material.accent: root.accent_color
property int font_enlarged_text_size: 20
property int font_normal_text_size: 16
- property double distance: 0
- property int distance_count: 0
property int distance_count_thres: 3
property string mode: "http"
- // visibility: Window.FullScreen
- Component.onCompleted: {
- loading_popup.open();
- }
-
-
- Popup {
- id: loading_popup
- background: Rectangle {
- color: "#B0121212"
- radius: 5
- border.color: root.accent_color
- border.width: 1
- }
- height: parent.height / 10
- width: parent.width / 1.1
- anchors.centerIn: Overlay.overlay
- closePolicy: Popup.NoAutoClose
- BusyIndicator {
- id: loading_indicator
- running: true
- anchors {
- left: parent.left
- verticalCenter: parent.verticalCenter
- }
- }
- Text {
- id: loading_text
- text: "Loading..."
- anchors {
- leftMargin: 20
- left: loading_indicator.right
- verticalCenter: parent.verticalCenter
- }
- verticalAlignment: Text.AlignVCenter
- color: root.accent_color
- font.pointSize: root.font_enlarged_text_size
- }
-
+ property string status: "Initializing"
+ property string connection_text_string: "Reading WiFi Status..."
+ property string alarm_status_text: ""
+ property double water_tank_height_factor: 1.3
+ property double water_tank_width_factor: 2
+ property var root_config: ({})
+ property var base_config: {
+ "max": 0,
+ "min": 0,
+ "alert": 0,
+ "interval": 0,
+ "url": "none",
+ "water_tank_width": 2,
+ "water_tank_height": 1.3,
+ "rate_of_fill_enable": true,
+ "current_reading_enable": true,
+ "percentage_enable": true,
+ "filling_status_enable": true,
+ "expected_to_fill_enable": true,
+ "accent_color": "F05FAF5F",
+ "dark_accent_color": "FF5F7F5F",
+ "danger_color": "F0AF2F1F",
+ "water_color": "F00F7FAF",
+ "water_color_above_alert": "F00FAF0F",
+ "background_color": "1F1F1F",
+ "font_normal_text_size": 16,
+ "font_enlarged_text_size": 20
}
Rectangle {
anchors.fill: parent
- id: background
- color: "#DF0F0F0F"
+ color: root.background_color
+ id: root_rect
Rectangle {
id: top_bar
height: Math.min(root.height / 12, 100)
@@ -76,729 +64,321 @@ Window {
topMargin: 25
}
color: "#0A0F0F0F"
-
- Rectangle {
- id: settings_rect
+ SettingsBarIcon {
+ id: settings_icon
height: top_bar.height - 20
- width: top_bar.width / 12
+ width: height
anchors {
top: parent.top
- left: parent.left
+ right: parent.right
bottomMargin: 5
topMargin: 5
leftMargin: 5
}
- Behavior on color {
- ColorAnimation {
- duration: 200
- }
- }
-
color: "#0A0F0F0F"
- border.color: "#AF121212"
- border.width: 0.5
- radius: 2
- Text {
- id: settings_text
- text: "⚙"
- anchors.centerIn: parent
- color: root.accent_color
- font.pointSize: root.font_enlarged_text_size
- }
-
- MouseArea {
- id: settings_button_area
- anchors.fill: parent
- hoverEnabled: true
- property color settings_rect_pressed_prev: "#00000000"
- property color settings_rect_entered_prev: "#00000000"
- onEntered: {
- settings_rect_entered_prev = settings_rect.color
- settings_rect.color = "#5A1F1F1F"
- }
- onExited: {
- settings_rect.color = settings_rect_entered_prev
- }
- onPressed: {
- settings_rect_pressed_prev = settings_rect.color
- settings_rect.color = "#5A1F4F1F"
- settings_popup.open()
- }
- onReleased: {
- settings_rect.color = settings_rect_pressed_prev
- }
+ radius: 100
+ accent_color: root.accent_color
+ onOpen: {
+ settings_popup.open()
}
}
-
-
- Rectangle {
- property color old_color: "#0F0F0F0F"
- id: status_indicator
- width: 20
- height: 20
- radius: 20
+ SettingsBarIcon {
+ height: top_bar.height - 20
+ width: height
anchors {
top: parent.top
- right: parent.right
- topMargin: 20
+ left: parent.left
bottomMargin: 5
+ topMargin: 10
leftMargin: 5
- rightMargin: 20
}
- color: "#F0AF2F1F"
- layer.enabled: true
- layer.effect: MultiEffect {
- shadowEnabled: true
+ text: "⇅"
+ color: "#0A0F0F0F"
+ radius: 100
+ accent_color: root.accent_color
+ onOpen: {
+ request_response_popup.open()
}
}
- Text {
- id: status_label
- anchors {
- top: parent.top
- topMargin: 20
- right: status_indicator.left
- rightMargin: 10
- }
- horizontalAlignment: Text.AlignRight
- width: parent.width - status_indicator.width
- height: parent.height
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- text: "Disconnected"
+ }
+
+ RequestResponsePopup {
+ id: request_response_popup
+ accent_color: root.accent_color
+ font_enlarged_text_size: 20
+
+ }
+
+ SettingsPopup {
+ id: settings_popup
+ accent_color: root.accent_color
+ background_color: root.background_color
+ font_enlarged_text_size: root.font_enlarged_text_size
+ font_normal_text_size: root.font_normal_text_size
+ function setConfig(new_config: var) {
+ settings_popup.config.max = new_config.max
+ settings_popup.config.min = new_config.min
+ settings_popup.config.alert = new_config.alert
+ settings_popup.config.url = new_config.url
}
+ onSaveConfig: (config) => {
+ console.log("Saving config: ", config)
+ config_interface.saveConfig(config);
+ }
+ onDeleteConfig: () => {
+ config_interface.deleteConfig();
+ }
+ }
+
+ Text {
+ id: connection_text
+ anchors {
+ top: top_bar.bottom
+ topMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ text: root.connection_text_string
+ color: root.accent_color
+ font.pointSize: root.font_normal_text_size
+ }
+
+ WaterTank {
+ id: tank
+ anchors {
+ top: connection_text.bottom
+ horizontalCenter: parent.horizontalCenter
+ topMargin: 40
+ }
+ height: (root_rect.height) / root.water_tank_height_factor
+ width: root_rect.width / root.water_tank_width_factor
+ base_color: root.background_color
+ distance: 0
+ tank_lid_color: "#F0121212"
+ tank_base_color: "#0FFFFFFF"
+ water_level_color: root.water_color
+ tank_base_border_color: "#F0121212"
+ water_color_above_alert: root.water_color_above_alert
+ min_thres_input: 0
+ max_thres_input: 0
+ text_color: root.accent_color
+ font_normal_text_size: root.font_normal_text_size
+ font_enlarged_text_size: root.font_enlarged_text_size
+ }
+
+
+ Text {
+ id: alarm_status
+ text: root.alarm_status_text
+ visible: (root.alarm_status_text.length > 0 ) ? true : false
+ anchors {
+ bottom: status.top
+ bottomMargin: 10
+ horizontalCenter: parent.horizontalCenter
+ }
+ horizontalAlignment: Text.AlignHCenter
+ color: root.accent_color
+ font.pointSize: root.font_normal_text_size
+ }
+
+ Text {
+ id: status
+ text: root.status
+ anchors {
+ bottom: button_base.top
+ bottomMargin: 20
+ horizontalCenter: parent.horizontalCenter
+ }
+ horizontalAlignment: Text.AlignHCenter
+ color: root.accent_color
+ font.pointSize: root.font_normal_text_size
}
Rectangle {
- id: base
- color: "#0A0F0F0F"
- width: parent.width
- height: parent.height - top_bar.height
+ id: button_base
+ color: "#00000000"
+ width: parent.width / 1.5
+ height: parent.height / 20
anchors {
- top: top_bar.bottom
- bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
+ bottom: parent.bottom
+ bottomMargin: 30
}
-
- Popup {
- id: settings_popup
- background: Rectangle {
- color: "#F0121212"
- radius: 5
- border.color: root.accent_color
- border.width: 1
- }
- height: Math.max(parent.height / 1.2, contentHeight)
- width: parent.width / 1.1
- anchors.centerIn: Overlay.overlay
-
-
- Rectangle {
- id: settings_base
- anchors.fill: parent
- color: "#F0121212"
-
- Text {
- id: settings_label
- text: "Settings"
- width: parent.width
- anchors {
- top: parent.top
- horizontalCenter: parent.horizontalCenter
- }
- horizontalAlignment: Text.AlignHCenter
- color: root.accent_color
- font.pointSize: root.font_enlarged_text_size
- }
-
-
- Text {
- id: max_thres_label
- text: "Max Threshold: "
- anchors {
- topMargin: 20
- top: settings_label.bottom
- left: parent.left
- verticalCenter: max_thres_input.verticalCenter
- }
- height: max_thres_input.height
- verticalAlignment: Text.AlignVCenter
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- }
-
- TextField {
- id: max_thres_input
- text: config.max
- anchors {
- right: parent.right
- rightMargin: 5
- top: settings_label.bottom
- topMargin: 20
- }
- width: parent.width / 2
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- }
-
-
- Text {
- id: min_thres_label
- text: "Min Threshold: "
- anchors {
- topMargin: 20
- top: max_thres_label.bottom
- left: parent.left
- verticalCenter: min_thres_input.verticalCenter
- }
- height: min_thres_input.height
- verticalAlignment: Text.AlignVCenter
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- }
-
- TextField {
- id: min_thres_input
- text: config.min
- anchors {
- right: parent.right
- rightMargin: 5
- top: max_thres_label.bottom
- topMargin: 20
- }
- width: parent.width / 2
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- }
-
-
- Text {
- id: alert_thres_label
- text: "Alert Percentage: "
- anchors {
- topMargin: 20
- top: min_thres_label.bottom
- left: parent.left
- verticalCenter: alert_thres_input.verticalCenter
- }
- height: alert_thres_input.height
- verticalAlignment: Text.AlignVCenter
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- }
-
- TextField {
- id: alert_thres_input
- text: config.alert
- anchors {
- right: parent.right
- rightMargin: 5
- top: min_thres_label.bottom
- topMargin: 20
- }
- width: parent.width / 2
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- }
- CheckBox {
- id: flip_bits_box
- text: "Flip Bits"
- checked: config.flipBits
- anchors {
- topMargin: 20
- top: alert_thres_label.bottom
- horizontalCenter: parent.horizontalCenter
- }
- contentItem: Text {
- text: flip_bits_box.text
- font.pointSize: root.font_normal_text_size
- color: root.accent_color
- leftPadding: flip_bits_box.indicator.width + flip_bits_box.spacing
- }
- }
-
- Text {
- id: settings_mode_text
- anchors {
- topMargin: 20
- top: flip_bits_box.bottom
- left: parent.left
- }
- font.pointSize: root.font_normal_text_size
- color: root.accent_color
- text: "Mode: "
- }
- Row {
- id: mode_row
- anchors {
- left: settings_mode_text.right
- right: parent.right
- verticalCenter: settings_mode_text.verticalCenter
- leftMargin: 10
- }
- RadioButton {
- id: udp_radio_button
- checked: config.mode === "udp" ? true : false
- text: "UDP"
- font.pointSize: root.font_normal_text_size
- contentItem: Text {
- text: udp_radio_button.text
- font: udp_radio_button.font
- opacity: enabled ? 1.0 : 0.3
- color: udp_radio_button.down ? root.dark_accent_color : root.accent_color
- verticalAlignment: Text.AlignVCenter
- leftPadding: udp_radio_button.indicator.width + udp_radio_button.spacing
- }
- onCheckedChanged: {
- if(checked) {
- root.mode = "udp";
- }
- }
- }
- RadioButton {
- id: http_radio_button
- checked: config.mode === "http" ? true : false
- text: "HTTP"
- font.pointSize: root.font_normal_text_size
- contentItem: Text {
- text: http_radio_button.text
- font: http_radio_button.font
- opacity: enabled ? 1.0 : 0.3
- color: http_radio_button.down ? root.dark_accent_color : root.accent_color
- verticalAlignment: Text.AlignVCenter
- leftPadding: http_radio_button.indicator.width + http_radio_button.spacing
- }
- onCheckedChanged: {
- if(checked) {
- root.mode = "http";
- }
- }
- }
- }
- Text {
- id: http_url_label
- text: "Url: "
- anchors {
- topMargin: 20
- top: settings_mode_text.bottom
- left: parent.left
- verticalCenter: http_url_input.verticalCenter
- }
- height: http_url_input.height
- verticalAlignment: Text.AlignVCenter
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- }
-
- TextField {
- id: http_url_input
- text: config.url
- anchors {
- right: parent.right
- rightMargin: 20
- leftMargin: 20
- top: mode_row.bottom
- topMargin: 20
- }
- enabled: root.mode === "http" ? true : false
- width: Math.max(parent.width / 2, parent.width - http_url_label.width - anchors.leftMargin)
- color: root.accent_color
- font.pointSize: root.font_normal_text_size
- cursorPosition: 0
- }
-
- Text {
- id: settings_error_label
- property string settings_error_text: ""
- text: "Error: " + settings_error_text
- visible: settings_error_text === "" ? false : true
- anchors {
- topMargin: 20
- top: http_url_label.bottom
- bottom: reset_averages.top
- bottomMargin: 10
- left: parent.left
- horizontalCenter: parent.horizontalCenter
- }
- horizontalAlignment: Text.AlignHCenter
- color: root.danger_color
- font.pixelSize: root.font_normal_text_size
- }
-
- Rectangle {
- id: reset_averages
- anchors {
- topMargin: 10
- bottom: save_config.top
- bottomMargin: 10
- horizontalCenter: parent.horizontalCenter
- }
- color: "#F0121212"
- width: parent.width / 1.3
- height: 60
- border {
- width: 1
- color: root.accent_color
- }
- radius: 3
-
- Text {
- anchors.centerIn: parent
- text: "Reset Averages"
- color: root.accent_color
- font.pointSize: root.font_enlarged_text_size
- }
- MouseArea {
- anchors.fill: parent
- hoverEnabled: true
- property color settings_rect_pressed_prev: "#00000000"
- property color settings_rect_entered_prev: "#00000000"
- onEntered: {
- settings_rect_entered_prev = reset_averages.color
- reset_averages.color = "#5A1F1F1F"
- }
- onExited: {
- reset_averages.color = settings_rect_entered_prev
- }
- onPressed: {
- settings_rect_pressed_prev = reset_averages.color
- reset_averages.color = "#5A1F4F1F"
- listener.resetAverages();
- }
- onReleased: {
- reset_averages.color = settings_rect_pressed_prev
- }
- }
-
- }
-
- Rectangle {
- id: save_config
- anchors {
- topMargin: 10
- bottom: parent.bottom
- horizontalCenter: parent.horizontalCenter
- }
- color: "#F0121212"
- width: parent.width / 1.3
- height: 60
- border {
- width: 1
- color: root.accent_color
- }
- radius: 3
-
- Text {
- anchors.centerIn: parent
- text: "Save"
- color: root.accent_color
- font.pointSize: root.font_enlarged_text_size
- }
- MouseArea {
- anchors.fill: parent
- hoverEnabled: true
- property color settings_rect_pressed_prev: "#00000000"
- property color settings_rect_entered_prev: "#00000000"
- onEntered: {
- settings_rect_entered_prev = save_config.color
- save_config.color = "#5A1F1F1F"
- }
- onExited: {
- save_config.color = settings_rect_entered_prev
- }
- onPressed: {
- settings_rect_pressed_prev = save_config.color
- save_config.color = "#5A1F4F1F"
- let max = parseFloat(min_thres_input.text);
- let min = parseFloat(min_thres_input.text);
- let alert = parseFloat(alert_thres_input.text);
- let flipBits = flip_bits_box.checked;
- let mode = root.mode;
- let url = http_url_input.text;
- console.log("Parsed values:", max, min, alert, flip_bits_box.checked);
- if(max < min) {
- settings_error_label.settings_error_text = "Max cannot be less than min";
- return;
- }
- if (alert > 100 || alert < 0) {
- settings_error_label.settings_error_text = "Alert must be between 0 to 100";
- return;
- }
- let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text)));
- let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
- let alert_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading;
- console.log("Alert effective reading is: " + alert_effective_reading + " for value: " + alert);
- listener.setConfig(alert_effective_reading, flipBits, mode, url);
- config.saveConfig(max_thres_input.text, min_thres_input.text, alert_thres_input.text, flip_bits_box.checked, mode, url);
- settings_popup.close();
- }
- onReleased: {
- save_config.color = settings_rect_pressed_prev
- }
- }
-
- }
- }
- }
-
- Rectangle {
- id: tank_lid
- color: "#F0121212"
+ ConnectButton {
+ id: connect_button
+ width: parent.width / 2.1
+ height: parent.height
+ font_color: root.accent_color
+ font_enlarged_text_size: root.font_enlarged_text_size
+ font_normal_text_size: root.font_normal_text_size
+ color: root.background_color
+ accent_color: root.accent_color
anchors {
- bottom: tank_base.top
- horizontalCenter: tank_base.horizontalCenter
- bottomMargin: -5
- }
- radius: 10
- width: parent.width / 4
- height: parent.height / 15
- }
-
- Rectangle {
- id: tank_base
- color: "#0FFFFFFF"
- anchors.centerIn: parent
- width: parent.width / 2
- height: parent.height / 2
- border.color: "#F0121212"
- border.width: 5
- radius: 4
- }
-
- Rectangle {
- id: water_level
- color: root.water_color
- property double height_max: tank_base.height - 10
- property double current_level: 0
- property double percentage_filled: 0
- height: water_level.current_level
- width: tank_base.width - 10
- anchors {
- bottom: tank_base.bottom
- horizontalCenter: tank_base.horizontalCenter
+ left: parent.left
+ leftMargin: 5
+ bottom: parent.bottom
bottomMargin: 5
}
- Behavior on color {
- ColorAnimation {
- duration: 200
+ onConnect: {
+ if(connect_button.mode === 0)
+ {
+ requester.start();
+ root.status = "Waiting to Send.";
}
- }
- Behavior on height {
- NumberAnimation {
- duration: 200
+ else {
+ root.status = "Stopped.";
+ requester.stop();
}
}
}
- Text {
- id: info_text
- text: "Current Reading: " + root.distance.toFixed(2) + " cm"
- anchors {
- top: tank_base.bottom
- topMargin: 20
- horizontalCenter: parent.horizontalCenter
- }
- horizontalAlignment: Text.AlignHCenter
- color: root.accent_color
- font.pixelSize: root.font_normal_text_size
- }
- Text {
- id: info_percent_text
- text: "Percentage filled: " + water_level.percentage_filled.toFixed(2) + " %"
+ ConnectButton {
+ id: start_alarm
+ width: parent.width / 2.1
+ height: parent.height
+ font_color: root.accent_color
+ font_enlarged_text_size: root.font_enlarged_text_size
+ font_normal_text_size: root.font_normal_text_size
+ color: root.background_color
+ accent_color: root.accent_color
+ text: "Start Alarm"
+ text2: "Stop Alarm"
anchors {
- top: info_text.bottom
- topMargin: 10
- horizontalCenter: parent.horizontalCenter
+ left: connect_button.right
+ leftMargin: 10
+ bottom: parent.bottom
+ bottomMargin: 5
}
- horizontalAlignment: Text.AlignHCenter
- color: root.accent_color
- font.pixelSize: root.font_normal_text_size
- }
- Text {
- id: last_updated_text
- property string last_updated: ""
- text: "Last Updated On Server On: " + last_updated
- anchors {
- top: info_percent_text.bottom
- topMargin: 10
- horizontalCenter: parent.horizontalCenter
+ onConnect: {
+ if(start_alarm.mode === 0)
+ {
+ requester.startAlarm();
+ root.alarm_status_text = "Started Alarm; Waiting for Trigger: " + root.root_config.alert
+ }
+ else {
+ requester.stopAlarm();
+ root.alarm_status_text = "";
+ }
}
- horizontalAlignment: Text.AlignHCenter
- color: root.accent_color
- font.pixelSize: root.font_normal_text_size
- visible: config.mode === "http" ? true : false
}
- Text {
- id: error_text
- property string error_string: ""
- text: "Error: " + error_string
- visible: error_string === "" ? false : true;
- anchors {
- top: last_updated_text.bottom
- topMargin: 10
- horizontalCenter: parent.horizontalCenter
- }
- horizontalAlignment: Text.AlignHCenter
- color: root.danger_color
- font.pixelSize: root.font_normal_text_size
- }
-
-
}
+
+
}
+ RequestWorker {
+ id: requester
+ onStatusChanged: () => {
+ root.status = requester.status;
+ }
+ onNetworkReply: (water_level, fill_rate, expected, data) => {
+ tank.setDistance(water_level);
+ tank.rate_of_fill = fill_rate;
+ tank.expected_to_fill = expected;
+ status.color = root.accent_color;
+ request_response_popup.request_response_string = data;
+ }
+ onRequestFailed: (data) => {
+ status.color = root.danger_color;
+ request_response_popup.request_response_string = data;
+ }
- Timer {
- id: status_indicator_timer
- running: false
- repeat: false
- triggeredOnStart: false
- interval: 300
- onTriggered: {
- status_indicator.color = status_indicator.old_color;
+ onConnectionText: (text, error) => {
+ root.connection_text_string = text
+ if(!error) {
+ connection_text.color = root.accent_color
+ }
+ else {
+ connection_text.color = root.danger_color
+ }
}
}
ConfigInterface {
- id: config
- onConfigData: (max, min, alert, mode, url) => {
- console.log("Got config data as: ", max, min, alert, mode, url);
- }
- onModeChanged: {
- root.mode = mode;
- }
- }
- Timer {
- property int count: 0
- id: init_timer
- running: true
- repeat: true
- interval: 1000
- onTriggered: {
- if(count === 0) {
- listener.startService();
- count++;
- return;
- }
- else {
- console.log("Timer triggered saving config, hope everything is loaded by now");
- let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text)));
- let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
- let alert = parseFloat(alert_thres_input.text);
- let alert_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading;
- console.log("Setting initial configs: ", alert_effective_reading, config.flipBits , config.mode, config.url);
- listener.setConfig(alert_effective_reading, config.flipBits , config.mode, config.url);
- http_url_input.cursorPosition = 0;
- init_timer.repeat = false;
- init_timer.stop();
+ id: config_interface
+ fileName: "config.json"
+ onConfig: (config) => {
+ settings_popup.config = config;
+ console.log("Loading initial config");
+ setConfig(config);
+ if(root.status === "Initializing") {
+ root.status = "Press Connect.";
}
}
-
- }
-
- UDPListener {
- id: listener
-
- onShowError: (err) => {
- error_text.error_string = err;
- }
-
- onLastUpdatedChanged: (update, warn) => {
- console.log("QML Last Updated: ", update);
- if(!warn)
- {
- last_updated_text.last_updated = update;
- last_updated_text.color = root.accent_color;
- }
- else
- {
- last_updated_text.last_updated = update;
- last_updated_text.color = root.danger_color;
- }
- }
-
- onDistanceChanged: {
- console.log("Distance changed");
- root.distance = listener.distance;
- error_text.error_string = "";
- console.log("Distance count: ", root.distance_count);
- if(root.distance_count >= root.distance_count_thres) {
- if(loading_popup.opened) {
- loading_popup.close();
- }
- status_label.text = "Recieving values";
- let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text)));
- let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
- let current_reading = Math.max(0, root.distance - current_min_reading);
- let height_max_possible = water_level.height_max;
- let percentage_filled = Math.max(0, ((effective_max_reading - current_reading) / effective_max_reading * 100))
- let normalized_height = percentage_filled * height_max_possible / 100;
- let alert = parseFloat(alert_thres_input.text);
- if(percentage_filled <= alert) {
- console.log("Water Level is lower than alert");
- water_level.color = root.danger_color;
- }
- else {
- water_level.color = root.water_color;
- }
- if(root.distance_count > 1000) {
- root.distance_count = root.distance_count_thres;
- }
- console.log("Current reading is: " + current_reading)
- console.log("Normalized height is: " + normalized_height)
- water_level.current_level = normalized_height;
- water_level.percentage_filled = percentage_filled;
- }
- }
- onConnected: {
- error_text.error_string = "";
- console.log("Connected");
- status_indicator.color = "#F05F5F0F"
- status_label.text = "Waiting for more values";
-
- }
- onDisconnected: {
- error_text.error_string = "Disconnected";
- console.log("Disconnected");
- status_indicator.color = "#F05F5F0F"
- status_label.text = "Disconnected";
- }
- onDistanceReceived: (distance, time) => {
- if(root.distance_count >= 1)
- dataBlink();
- if(root.distance_count == 0) {
- let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text)));
- let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
- let alert = parseFloat(alert_thres_input.text);
- let alert_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading;
- console.log("Alert effective reading is: " + alert_effective_reading + " for value: " + alert);
- listener.setConfig(alert_effective_reading, flip_bits_box.checked, root.mode, http_url_input.text);
- }
-
- distance_count += 1;
- }
- }
-
- onClosing: (close)=> {
- if(Qt.platform.os === "android"){
- // close.accepted = false
- // listener.moveAppToBackground();
- // console.log("Moving to backgound");
- close.accepted = true;
- } else {
- close.accepted = true
+ onConfigDeleted: () => {
+ let config = base_config;
+ settings_popup.config = config;
+ console.log("Loading initial config");
+ setConfig(config);
+ if(root.status === "Initializing") {
+ root.status = "Press Connect.";
}
- }
-
- function dataBlink() {
- if(status_indicator_timer.running) {
- status_indicator_timer.stop();
}
- let old_color = "#F05F5F0F";
- let new_color = "#F04FAF5F";
- status_indicator.color = new_color;
- status_indicator.old_color = old_color;
- status_indicator_timer.start();
+
+ function setConfig(config) {
+ tank.setConfig(config);
+ requester.setConfig(config);
+ root.root_config = config;
+ if(config.water_tank_height === undefined) {
+ config.water_tank_height = 2
+ }
+ if(config.water_tank_width === undefined) {
+ config.water_tank_width = 2.2
+ }
+ if(config.rate_of_fill_enable === undefined) {
+ config.rate_of_fill_enable = true
+ }
+ if(config.current_reading_enable === undefined) {
+ config.current_reading_enable = true
+ }
+ if(config.percentage_enable === undefined) {
+ config.percentage_enable = true
+ }
+ if(config.filling_status_enable === undefined) {
+ config.filling_status_enable = true
+ }
+ if(config.expected_to_fill_enable === undefined) {
+ config.expected_to_fill_enable = true
+ }
+ if(config.font_enlarged_text_size === undefined) {
+ config.font_enlarged_text_size = 20
+ }
+ if(config.font_normal_text_size === undefined) {
+ config.font_normal_text_size = 16
+ }
+ if(config.accent_color === undefined) {
+ config.accent_color = "F05FAF5F"
+ }
+ if(config.danger_color === undefined) {
+ config.danger_color = "F0AF2F1F"
+ }
+ if(config.dark_accent_color === undefined) {
+ config.dark_accent_color = "FF5F7F5F"
+ }
+ if(config.water_color === undefined) {
+ config.water_color = "F00F7FAF"
+ }
+ if(config.water_color_above_alert === undefined) {
+ config.water_color_above_alert = "F00FAF0F"
+ }
+ if(config.background_color === undefined) {
+ config.background_color = "1F1F1F"
+ }
+ root.water_tank_height_factor = config.water_tank_height
+ root.water_tank_width_factor = config.water_tank_width
+ root.accent_color = "#" + config.accent_color
+ root.danger_color = "#" + config.danger_color
+ root.dark_accent_color = "#" + config.dark_accent_color
+ root.water_color = "#" + config.water_color
+ root.water_color_above_alert = "#" + config.water_color_above_alert
+ root.background_color = "#" + config.background_color
+ root.font_enlarged_text_size = config.font_enlarged_text_size
+ root.font_normal_text_size = config.font_normal_text_size
+
+ }
+ }
+ onClosing: {
+ requester.stopAlarm();
}
}
diff --git a/NotificationManager.java b/NotificationManager.java
deleted file mode 100644
index 5656db5..0000000
--- a/NotificationManager.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.qtproject.example.androidnotifier;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.BitmapFactory;
-import android.app.NotificationChannel;
-
-
-public class NotificationManager {
-
-}
diff --git a/README.md b/README.md
deleted file mode 100644
index 7198d51..0000000
--- a/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Water Level Sensor
-
-Builds using QtCreator, use Qt Version 6.9.
-Builds for android and desktop.
-On Android Creates a service from service.cpp and android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java.
-Changes in AndroidManifest.xml are important.
-On Desktop just waits for values from a UDP Broadcast on local network with port 8176 and shows those values.
-On Android the service does that, and creates an alert notification if the value in broadcast is less than alert threshold.
-
-
-
-Create a new socket in Worker that will instantly start listening.
-In the service launch the threads and immediately send client hello to the worker socket.
-On client hello send command data to the android service.
-On quit send server bye to android service.
-On server bye deinit the sockets that send data to cpp server.
-On next launch, the command and client threads can be reinitialized from the StartService java class.
\ No newline at end of file
diff --git a/RequestResponsePopup.qml b/RequestResponsePopup.qml
new file mode 100644
index 0000000..468bcda
--- /dev/null
+++ b/RequestResponsePopup.qml
@@ -0,0 +1,30 @@
+import QtQuick
+import QtQuick.Effects
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Popup {
+ id: request_response_popup
+ property color accent_color: "#00000000"
+ property int font_enlarged_text_size: 20
+ property int font_normal_text_size: 16
+ property string request_response_string: ""
+ background: Rectangle {
+ color: "#F0121212"
+ radius: 5
+ border.color: request_response_popup.accent_color
+ border.width: 1
+ }
+ height: Math.max(parent.height / 1.6, contentHeight)
+ width: parent.width / 1.2
+ anchors.centerIn: Overlay.overlay
+ ScrollView {
+ id: view
+ anchors.fill: parent
+ TextArea {
+ text: request_response_string
+ font.pointSize: font_normal_text_size
+ color: request_response_popup.accent_color
+ }
+ }
+}
diff --git a/SettingsBarIcon.qml b/SettingsBarIcon.qml
new file mode 100644
index 0000000..35072b7
--- /dev/null
+++ b/SettingsBarIcon.qml
@@ -0,0 +1,49 @@
+import QtQuick
+import QtQuick.Effects
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Rectangle {
+ id: settings_rect
+ signal open;
+ property color accent_color: "#00000000"
+ property int font_enlarged_text_size: 20
+ property string text: "⚙"
+ Behavior on color {
+ ColorAnimation {
+ duration: 200
+ }
+ }
+ Text {
+ id: settings_text
+ text: settings_rect.text
+ anchors.centerIn: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: settings_rect.accent_color
+ font.pointSize: settings_rect.font_enlarged_text_size
+ }
+
+ MouseArea {
+ id: settings_button_area
+ anchors.fill: parent
+ hoverEnabled: true
+ property color settings_rect_pressed_prev: "#00000000"
+ property color settings_rect_entered_prev: "#00000000"
+ onEntered: {
+ settings_rect_entered_prev = settings_rect.color
+ settings_rect.color = "#5A1F1F1F"
+ }
+ onExited: {
+ settings_rect.color = settings_rect_entered_prev
+ }
+ onPressed: {
+ settings_rect_pressed_prev = settings_rect.color
+ settings_rect.color = "#5A1F4F1F"
+ settings_rect.open();
+ }
+ onReleased: {
+ settings_rect.color = settings_rect_pressed_prev
+ }
+ }
+}
diff --git a/SettingsPopup.qml b/SettingsPopup.qml
new file mode 100644
index 0000000..6a97c2b
--- /dev/null
+++ b/SettingsPopup.qml
@@ -0,0 +1,594 @@
+import QtQuick
+import QtQuick.Effects
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Popup {
+ id: settings_popup
+ signal saveConfig(config: var)
+ signal deleteConfig()
+ property color accent_color: "#00000000"
+ property int font_enlarged_text_size: 20
+ property int font_normal_text_size: 16
+ property color background_color: "#00000000"
+ property var config: {
+ "max": 0,
+ "min": 0,
+ "alert": 0,
+ "interval": 0,
+ "url": "none",
+ "water_tank_width": 2,
+ "water_tank_height": 1.3,
+ "rate_of_fill_enable": true,
+ "current_reading_enable": true,
+ "percentage_enable": true,
+ "filling_status_enable": true,
+ "expected_to_fill_enable": true,
+ "accent_color": "F05FAF5F",
+ "dark_accent_color": "FF5F7F5F",
+ "danger_color": "F0AF2F1F",
+ "water_color": "F00F7FAF",
+ "water_color_above_alert": "F00FAF0F",
+ "background_color": "1F1F1F",
+ "font_normal_text_size": 16,
+ "font_enlarged_text_size": 20
+ }
+
+ background: Rectangle {
+ color: "#F0121212"
+ radius: 5
+ border.color: settings_popup.accent_color
+ border.width: 1
+ }
+ height: Math.max(parent.height / 1.6, contentHeight)
+ width: parent.width / 1.2
+ anchors.centerIn: Overlay.overlay
+ Rectangle {
+ id: settings_base
+ anchors.fill: parent
+ color: "#F0121212"
+
+ ScrollView {
+ id: rect_base_scroll_view
+ anchors {
+ top: parent.top
+ horizontalCenter: parent.horizontalCenter
+ bottom: save_config.top
+ bottomMargin: 10
+ }
+ width: parent.width
+ clip: true
+
+ // Use contentWidth and contentHeight to define the scrollable area
+ contentWidth: availableWidth
+ contentHeight: settings_column.height
+
+ Column {
+ id: settings_column
+ width: rect_base_scroll_view.width
+ spacing: 20
+
+ Text {
+ id: settings_label
+ text: "Settings"
+ width: parent.width
+ horizontalAlignment: Text.AlignHCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_enlarged_text_size
+ }
+
+ // Max Threshold
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Max Threshold: "
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: max_thres_input.verticalCenter
+ }
+
+ }
+ TextField {
+ id: max_thres_input
+ text: config.max
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Min Threshold
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Min Threshold: "
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: min_thres_input.verticalCenter
+ }
+
+ }
+ TextField {
+ id: min_thres_input
+ text: config.min
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Alert Threshold
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Alert Threshold: "
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: alert_thres_input.verticalCenter
+ }
+
+ }
+ TextField {
+ id: alert_thres_input
+ text: config.alert
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Update Interval
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Update Interval: "
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: interval_input.verticalCenter
+ }
+ }
+ TextField {
+ id: interval_input
+ text: config.interval
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // URL
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Url: "
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: http_url_input.verticalCenter
+ }
+
+ }
+ TextField {
+ id: http_url_input
+ text: config.url
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ cursorPosition: 0
+ }
+ }
+
+ // Tank Image Width
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Tank Image Width"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: water_tank_width_input.verticalCenter
+ }
+
+ }
+ TextField {
+ id: water_tank_width_input
+ text: String(config.water_tank_width)
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ cursorPosition: 0
+ }
+ }
+
+ // Tank Image Height
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Tank Image Height"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: water_tank_height_input.verticalCenter
+ }
+ }
+ TextField {
+ id: water_tank_height_input
+ text: String(config.water_tank_height)
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ cursorPosition: 0
+ }
+ }
+
+ // Accent Color
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Accent Color"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: accent_color_input.verticalCenter
+ }
+ }
+ ColorInput {
+ id: accent_color_input
+ text: config.accent_color
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Dark Accent Color
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Dark Accent Color"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: dark_accent_color_input.verticalCenter
+ }
+ }
+ ColorInput {
+ id: dark_accent_color_input
+ text: config.dark_accent_color
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Danger Color
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Danger Text Color"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: danger_color_input.verticalCenter
+ }
+ }
+ ColorInput {
+ id: danger_color_input
+ text: config.danger_color
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Water Color
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Water Color"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: water_color_input.verticalCenter
+ }
+ }
+ ColorInput {
+ id: water_color_input
+ text: config.water_color
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Alert Water Color
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Alert Water Color"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: water_color_above_alert_input.verticalCenter
+ }
+ }
+ ColorInput {
+ id: water_color_above_alert_input
+ text: config.water_color_above_alert
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Background Color
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Backgound Color"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: background_color_input.verticalCenter
+ }
+ }
+ ColorInput {
+ id: background_color_input
+ text: config.background_color
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ }
+ }
+
+ // Normal Font Size
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Font Size"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: font_normal_text_size_input.verticalCenter
+ }
+ }
+ TextField {
+ id: font_normal_text_size_input
+ text: String(config.font_normal_text_size)
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ cursorPosition: 0
+ }
+ }
+
+ // Header Font Size
+ Row {
+ width: parent.width
+ spacing: 10
+ Text {
+ text: "Header Font Size"
+ width: parent.width * 0.4
+ verticalAlignment: Text.AlignVCenter
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ anchors {
+ verticalCenter: font_enlarged_text_size_input.verticalCenter
+ }
+ }
+ TextField {
+ id: font_enlarged_text_size_input
+ text: String(config.font_enlarged_text_size)
+ width: parent.width * 0.55
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_normal_text_size
+ cursorPosition: 0
+ }
+ }
+
+ // CheckBox
+ CustomCheckBox {
+ id: current_reading_enable
+ checked: config.current_reading_enable
+ font_color: settings_popup.accent_color
+ font_point_size: settings_popup.font_normal_text_size
+ text: "Enable Current Reading"
+ leftPadding: 0
+ }
+
+ // CheckBox
+ CustomCheckBox {
+ id: percentage_enable
+ checked: config.percentage_enable
+ font_color: settings_popup.accent_color
+ font_point_size: settings_popup.font_normal_text_size
+ text: "Enable Percentage Reading"
+ leftPadding: 0
+ }
+
+ // CheckBox
+ CustomCheckBox {
+ id: filling_status_enable
+ checked: config.filling_status_enable
+ font_color: settings_popup.accent_color
+ font_point_size: settings_popup.font_normal_text_size
+ text: "Enable Filling Status"
+ leftPadding: 0
+ }
+
+ // CheckBox
+ CustomCheckBox {
+ id: rate_of_fill_enable
+ checked: config.rate_of_fill_enable
+ font_color: settings_popup.accent_color
+ font_point_size: settings_popup.font_normal_text_size
+ text: "Enable Text Rate of Fill"
+ leftPadding: 0
+ }
+
+ // CheckBox
+ CustomCheckBox {
+ id: expected_to_fill_enable
+ checked: config.expected_to_fill_enable
+ font_color: settings_popup.accent_color
+ font_point_size: settings_popup.font_normal_text_size
+ text: "Enable Expected to fill"
+ leftPadding: 0
+ }
+
+ ConnectButton {
+ id: delete_config_button
+ width: parent.width / 1.5
+ height: 80
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ }
+
+ font_color: settings_popup.accent_color
+ font_enlarged_text_size: settings_popup.font_enlarged_text_size
+ font_normal_text_size: settings_popup.font_normal_text_size
+ color: settings_popup.background_color
+ accent_color: settings_popup.accent_color
+ text: "Delete/Reset Config"
+ text2: text
+ onConnect: {
+ settings_popup.deleteConfig();
+ }
+ }
+ }
+ }
+ Rectangle {
+ id: save_config
+ anchors {
+ topMargin: 10
+ bottom: parent.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
+ color: "#F0121212"
+ width: parent.width / 1.3
+ height: 60
+ border {
+ width: 1
+ color: settings_popup.accent_color
+ }
+ radius: 3
+
+ Text {
+ anchors.centerIn: parent
+ text: "Save"
+ color: settings_popup.accent_color
+ font.pointSize: settings_popup.font_enlarged_text_size
+ }
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ property color settings_rect_pressed_prev: "#00000000"
+ property color settings_rect_entered_prev: "#00000000"
+ onEntered: {
+ settings_rect_entered_prev = save_config.color
+ save_config.color = "#5A1F1F1F"
+ }
+ onExited: {
+ save_config.color = settings_rect_entered_prev
+ }
+ onPressed: {
+ settings_rect_pressed_prev = save_config.color
+ save_config.color = "#5A1F4F1F"
+ let max = parseFloat(max_thres_input.text);
+ let min = parseFloat(min_thres_input.text);
+ let alert = parseFloat(alert_thres_input.text);
+ let url = http_url_input.text;
+ let interval = parseInt(interval_input.text);
+ let image_width = parseFloat(water_tank_width_input.text);
+ let image_height = parseFloat(water_tank_height_input.text);
+
+ let current_reading = current_reading_enable.checked;
+ let percentage_reading = percentage_enable.checked
+ let filling_status = filling_status_enable.checked;
+ let rate_of_fill = rate_of_fill_enable.checked;
+ let expected_to_fill = expected_to_fill_enable.checked;
+
+ let normal_font_size = parseInt(font_normal_text_size_input.text);
+ let enlarged_font_size = parseInt(font_enlarged_text_size_input.text);
+
+ let accent_color = accent_color_input.text;
+ let dark_accent_color = dark_accent_color_input.text;
+ let danger_color = danger_color_input.text;
+ let water_color = water_color_input.text;
+ let water_color_above_alert = water_color_above_alert_input.text;
+ let background_color = background_color_input.text;
+
+ var new_config = {"max": max, "min": min, "alert": alert, "url": url,
+ "interval": interval,
+ "water_tank_height": image_height,
+ "water_tank_width": image_width,
+ "current_reading_enable": current_reading,
+ "percentage_enable": percentage_reading,
+ "filling_status_enable": filling_status,
+ "rate_of_fill_enable": rate_of_fill,
+ "expected_to_fill_enable": expected_to_fill,
+ "font_normal_text_size": normal_font_size,
+ "font_enlarged_text_size": enlarged_font_size,
+ "accent_color": accent_color,
+ "dark_accent_color": dark_accent_color,
+ "danger_color": danger_color,
+ "water_color": water_color,
+ "water_color_above_alert": water_color_above_alert,
+ "background_color": background_color
+ }
+ settings_popup.saveConfig(new_config)
+ settings_popup.close();
+ }
+ onReleased: {
+ save_config.color = settings_rect_pressed_prev
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WaterTank.qml b/WaterTank.qml
new file mode 100644
index 0000000..633fac8
--- /dev/null
+++ b/WaterTank.qml
@@ -0,0 +1,204 @@
+import QtQuick
+import QtQuick.Effects
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+Item {
+ id: root
+ property color base_color: "#000F0F0F"
+ property color tank_lid_color: "#F0121212"
+ property color tank_base_color: "#00FFFFFF"
+ property color water_level_color: "#F00F7FAF"
+ property color water_color_above_alert: "#F00FAF0F"
+ property color tank_base_border_color: "#F0121212"
+ property color text_color: "#F05FAF5F"
+ property double height_max: tank_base.height - 10
+ property int font_normal_text_size: 0
+ property int font_enlarged_text_size: 0
+ property double current_level: 0
+ property double percentage_filled: 0
+ property int distance: 0
+ property double min_thres_input: 0
+ property double max_thres_input: 0
+ property double rate_of_fill: 0
+ property double expected_to_fill: 0
+ property double alert_threshold: 0
+ property bool enable_current_reading: true
+ property bool enable_percentage_reading: true
+ property bool enable_filling_status: true
+ property bool enable_rate_of_fill: true
+ property bool enable_expected_to_fill: true
+
+ Rectangle {
+ id: base
+ color: root.base_color
+ anchors.fill: parent
+ Rectangle {
+ id: tank_lid
+ color: root.tank_lid_color
+ anchors {
+ top: parent.top
+ horizontalCenter: tank_base.horizontalCenter
+ bottomMargin: -5
+ }
+ radius: 10
+ width: tank_base.width / 3
+ height: tank_base.height / 15
+ }
+
+ Rectangle {
+ id: tank_base
+ color: root.tank_base_color
+ anchors {
+ top: tank_lid.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
+ width: parent.width
+ height: parent.height * 0.55
+ border.color: root.tank_base_border_color
+ border.width: 5
+ radius: 4
+ }
+
+ Rectangle {
+ id: water_level
+ color: root.water_level_color
+ height: root.current_level
+ width: tank_base.width - 10
+ anchors {
+ bottom: tank_base.bottom
+ horizontalCenter: tank_base.horizontalCenter
+ bottomMargin: 5
+ }
+ Behavior on color {
+ ColorAnimation {
+ duration: 200
+ }
+ }
+ Behavior on height {
+ NumberAnimation {
+ duration: 200
+ }
+ }
+ }
+
+ ScrollView {
+ id: rect_base_scroll_view
+ anchors {
+ margins: 5
+ top: tank_base.bottom
+ bottom: parent.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
+ width: parent.width
+
+ contentWidth: parent.width
+ contentHeight: settings_column.implicitHeight
+ Column {
+ id: settings_column
+ spacing: 10
+ width: rect_base_scroll_view.width
+ Text {
+ id: water_level_reading
+ text: "Current Reading: " + (root.distance / 10)+ " cm"
+ font.pointSize: root.font_normal_text_size
+ color: root.text_color
+ visible: enable_current_reading
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ }
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Text {
+ id: water_level_percentage
+ text: "Percentage: " + root.percentage_filled.toFixed(2) + " %"
+ font.pointSize: root.font_normal_text_size
+ visible: enable_percentage_reading
+ color: root.text_color
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ }
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Text {
+ id: fill_status
+ text: "Filling status: " + (( root.rate_of_fill > 0)? "Filling" : "Discharging")
+ font.pointSize: root.font_normal_text_size
+ color: root.text_color
+ visible: enable_filling_status
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ }
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Text {
+ id: rate_of_fill_label
+ text: "Rate of fill: " + root.rate_of_fill + " cm/s"
+ font.pointSize: root.font_normal_text_size
+ color: root.text_color
+ visible: enable_rate_of_fill
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ }
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Text {
+ id: expected_to_fill_label
+ text: "Expected to fill in: " + root.expected_to_fill + " s"
+ font.pointSize: root.font_normal_text_size
+ color: root.text_color
+ visible: (root.rate_of_fill > 0 && enable_expected_to_fill === true) ? true : false
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ }
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+ }
+
+ }
+ function setConfig(config: var) {
+ min_thres_input = config.min;
+ max_thres_input = config.max;
+ alert_threshold = config.alert;
+ enable_current_reading = config.current_reading_enable;
+ enable_percentage_reading = config.percentage_enable;
+ enable_filling_status = config.filling_status_enable;
+ enable_rate_of_fill = config.rate_of_fill_enable;
+ enable_expected_to_fill = config.expected_to_fill_enable;
+
+ }
+
+ function setDistance(distance) {
+ root.distance = distance;
+ let distance_f = distance / 10;
+ console.log("Distance changed to: ", distance_f);
+ let current_min_reading = Math.max(0, min_thres_input);
+ let effective_max_reading = Math.max(0, max_thres_input) - current_min_reading;
+ let current_reading = Math.max(0, distance_f - current_min_reading);
+ console.log("Current reading is: " + current_reading)
+ console.log("Current min reading is: " + current_min_reading)
+ console.log("Effective max reading is: " + effective_max_reading)
+
+ let height_max_possible = root.height_max;
+ let percentage_filled = Math.max(0, ((effective_max_reading - current_reading) / effective_max_reading * 100))
+ let normalized_height = percentage_filled * height_max_possible / 100;
+ console.log("Current reading is: " + current_reading)
+ console.log("Normalized height is: " + normalized_height)
+ console.log("Percentage filled is: " + percentage_filled)
+ console.log("Alert threshold is: " + root.alert_threshold)
+ console.log("Is level above alert threshold: " + (distance_f <= root.alert_threshold))
+ root.current_level = normalized_height;
+ root.percentage_filled = percentage_filled;
+
+ if(distance_f <= root.alert_threshold) {
+ water_level.color = water_color_above_alert
+ }
+ else {
+ water_level.color = water_level_color
+ }
+ }
+}
+
diff --git a/WaterTank1.png b/WaterTank1.png
deleted file mode 100644
index 24ef3c1..0000000
Binary files a/WaterTank1.png and /dev/null differ
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index ead6548..2598885 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -1,18 +1,18 @@
-
-
-
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
@@ -49,9 +38,11 @@
+
+ android:value="appWaterLevelRewrite" />
+
diff --git a/android/build.gradle b/android/build.gradle
deleted file mode 100644
index ea6c193..0000000
--- a/android/build.gradle
+++ /dev/null
@@ -1,83 +0,0 @@
-buildscript {
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- //noinspection AndroidGradlePluginVersion
- classpath 'com.android.tools.build:gradle:8.8.0'
- }
-}
-
-repositories {
- google()
- mavenCentral()
-}
-
-apply plugin: qtGradlePluginType
-
-dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
- //noinspection GradleDependency
- implementation 'androidx.core:core:1.16.0'
-}
-
-android {
- /*******************************************************
- * The following variables:
- * - androidBuildToolsVersion,
- * - androidCompileSdkVersion
- * - qtAndroidDir - holds the path to qt android files
- * needed to build any Qt application
- * on Android.
- * - qtGradlePluginType - whether to build an app or a library
- *
- * are defined in gradle.properties file. This file is
- * updated by QtCreator and androiddeployqt tools.
- * Changing them manually might break the compilation!
- *******************************************************/
-
- namespace = androidPackageName
- compileSdkVersion androidCompileSdkVersion
- buildToolsVersion androidBuildToolsVersion
- ndkVersion = androidNdkVersion
-
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
- aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
- res.srcDirs = [qtAndroidDir + '/res', 'res']
- resources.srcDirs = ['resources']
- renderscript.srcDirs = ['src']
- assets.srcDirs = ['assets']
- jniLibs.srcDirs = ['libs']
- }
- }
-
- tasks.withType(JavaCompile) {
- options.incremental = true
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
- lintOptions {
- abortOnError = false
- }
-
- aaptOptions {
- // Do not compress Qt binary resources file
- noCompress 'rcc'
- }
-
- defaultConfig {
- resConfig "en"
- minSdkVersion qtMinSdkVersion
- targetSdkVersion qtTargetSdkVersion
- ndk.abiFilters = qtTargetAbiList.split(",")
- }
-}
diff --git a/android/res/drawable-hdpi/ic_launcher.png b/android/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 7d0f630..0000000
Binary files a/android/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/android/res/drawable-mdpi/ic_launcher.png b/android/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 9260e4e..0000000
Binary files a/android/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/android/res/drawable-xhdpi/ic_launcher.png b/android/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index c3e5ba9..0000000
Binary files a/android/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/android/res/drawable-xxhdpi/ic_launcher.png b/android/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 1257bb6..0000000
Binary files a/android/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/android/res/drawable-xxxhdpi/ic_launcher.png b/android/res/drawable-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 4ed8a75..0000000
Binary files a/android/res/drawable-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/android/src/org/qtproject/example/appwaterLevelSensor/.WaterLevelService.java.kate-swp b/android/src/org/qtproject/example/appwaterLevelSensor/.WaterLevelService.java.kate-swp
deleted file mode 100644
index aac66dc..0000000
Binary files a/android/src/org/qtproject/example/appwaterLevelSensor/.WaterLevelService.java.kate-swp and /dev/null differ
diff --git a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver.java b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver.java
deleted file mode 100644
index 60b004c..0000000
--- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.qtproject.example.appwaterLevelSensor;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class WaterLevelReceiver extends BroadcastReceiver {
- public native static void updateWaterLevel(byte[] data);
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if ("com.example.WATER_LEVEL_UPDATE".equals(intent.getAction())) {
- byte[] data = intent.getByteArrayExtra("level_data");
- updateWaterLevel(data);
- }
- }
-}
diff --git a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java
deleted file mode 100644
index cc765d0..0000000
--- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java
+++ /dev/null
@@ -1,536 +0,0 @@
-package org.qtproject.example.appwaterLevelSensor;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.BitmapFactory;
-import android.app.NotificationChannel;
-import org.qtproject.qt.android.bindings.QtService;
-import android.util.Log;
-import android.content.Intent;
-import java.lang.Runnable;
-import java.io.IOException;
-import java.net.*;
-import java.nio.charset.StandardCharsets;
-import java.nio.*;
-import org.qtproject.qt.android.bindings.QtActivity;
-import android.app.Service;
-import java.time.Instant;
-import java.time.Duration;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONArray;
-import android.widget.Toast;
-import android.os.IBinder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-
-public class WaterLevelService extends Service {
-
- private static float alert_thres_distance = 1000000;
- private static boolean bits_flipped = false;
- private static String mode = "";
- private static String url = "";
- private static Thread udpThread;
- private static Thread cmdThread;
- private static boolean resetBinds = false;
- private final IBinder binder = new LocalBinder();
- final Messenger mMessenger = new Messenger(new IncomingHandler());
- public static final int MSG_RESTART_THREADS = 1;
-
-
- class IncomingHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_RESTART_THREADS:
- Log.d("UDP Service MainThread", "Received IPC request to restart threads");
- restartThreads();
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
-
-
- public synchronized void restartThreads() {
- Log.d("UDP Service MainThread", "In synchronized thread stopper.");
- udpThread.interrupt();
- cmdThread.interrupt();
- try {
- udpThread.join();
- cmdThread.join();
- } catch (InterruptedException e) {
- Log.d("UDP Service MainThread", "Thread joining was interrupted because of: " + e);
- }
-
- new Thread(new
- Runnable() {
- @Override
- public void run() {
- try {
- // Move your socket and thread starting logic here
- if (udpThread == null || !udpThread.isAlive()) {
- udpThread = new Thread(new UDPClient());
- udpThread.start();
- }
- if (cmdThread == null || !cmdThread.isAlive()) {
- cmdThread = new Thread(new UDPCommandClient());
- cmdThread.start();
- }
- } catch (Exception e) {
- Log.e("UDP Service MainThread", "Restarting the threads", e);
- }
- }
- }).start();
- Log.d("UDP Service MainThread", "Actually restarting the thread");
- }
-
- public static synchronized boolean checkBound() {
- return resetBinds;
- }
-
- public static synchronized void setBound(boolean value) {
- resetBinds = value;
- }
-
- public class LocalBinder extends android.os.Binder {
- WaterLevelService getService() {
- return WaterLevelService.this;
- }
- }
-
- public class UDPCommandClient implements Runnable {
- private DatagramSocket socket_cmd_recv;
- private DatagramSocket socket_cmd_send;
- private boolean received_cmd_first = false;
-
- @Override
- public void run() {
- Log.d("UDP Service CommandThread", "Command thread initialising");
- try {
- socket_cmd_recv = new DatagramSocket(null);
- socket_cmd_send = new DatagramSocket(null);
- socket_cmd_recv.setSoTimeout(4000);
- socket_cmd_recv.setReuseAddress(true);
- socket_cmd_send.setReuseAddress(true);
- socket_cmd_recv.bind(new InetSocketAddress(8178));
- socket_cmd_send.bind(new InetSocketAddress(8187));
- boolean not_send_set = true;
- Log.d("UDP Service CommandThread", "Created command thread sockets");
- while (!Thread.currentThread().isInterrupted()) {
- if(socket_cmd_recv.isClosed()) {
- socket_cmd_recv.bind(new InetSocketAddress(8178));
- }
- if(socket_cmd_send.isClosed()) {
- socket_cmd_send.bind(new InetSocketAddress(8187));
- }
-
- if(not_send_set) {
- String s_data = new String("");
- byte[] buff_s = s_data.getBytes(StandardCharsets.UTF_8);
- DatagramPacket packet = new DatagramPacket(buff_s, buff_s.length, InetAddress.getByName("127.0.0.1"), 8186);
- try {
- socket_cmd_send.connect(InetAddress.getByName("127.0.0.1"), 8186);
- socket_cmd_send.send(packet);
- } catch(Exception e) {
- Log.d("UDP Service CommandThread", "Failed to send data to client due to: " + e);
- }
- Log.d("UDP Service CommandThread", "Sent set message to client");
- }
-
- byte[] buffer = new byte[1024];
- DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
- try {
- socket_cmd_recv.receive(packet);
- } catch(java.net.SocketTimeoutException e) {
- continue;
- }
- String str = new String(buffer, StandardCharsets.UTF_8);
- Log.d("UDP Service CommandThread", "Received command packet " + str);
- not_send_set = false;
- if(str.startsWith("", "");
- alert_thres_distance = Float.parseFloat(str);
- {
- if(!checkBound())
- {
- setBound(true);
- Log.d("UDP Service CommandThread", "Set Bound to True");
- }
- else {
- Log.d("UDP Service CommandThread", "Set Bound is False");
- }
-
- }
- }
- else if(str.startsWith("", "");
- Log.d("UDP Service CommandThread", "Flipping " + str);
- if(str.toLowerCase() == "false") {
- bits_flipped = false;
- }
- else if(str.toLowerCase() == "true") {
- bits_flipped = true;
- }
- }
- else if(str.startsWith("", "");
- Log.d("UDP Service CommandThread", "Mode " + str);
- mode = str.toLowerCase();
- }
- else if(str.startsWith("", "");
- Log.d("UDP Service CommandThread", "Url " + str);
- url = str.toLowerCase();
- }
- }
- }
- catch (IOException e) {
- // showToast("IOException: " + e);
- Log.e("UDP Service CommandThread", "IOException Error" + e);
- }
- }
-
- }
-
- public class UDPClient implements Runnable {
- private DatagramSocket socket_recv;
- private DatagramSocket socket_send;
- private Instant prevAlertTimestamp;
- private InetAddress address;
- private boolean started = false;
-
- @Override
- public void run() {
- Log.d("UDP Service ClientThread", "Client Thread initialising");
- android.net.wifi.WifiManager wifi = (android.net.wifi.WifiManager) getSystemService(Context.WIFI_SERVICE);
- android.net.wifi.WifiManager.MulticastLock multicastLock = wifi.createMulticastLock("water_level_lock");
- android.net.wifi.WifiManager.WifiLock wifiLock = wifi.createWifiLock(android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF, "WaterLevel:WifiLock");
- android.os.PowerManager pm = (android.os.PowerManager) getSystemService(Context.POWER_SERVICE);
- android.os.PowerManager.WakeLock wakeLock = pm.newWakeLock(android.os.PowerManager.PARTIAL_WAKE_LOCK, "WaterLevel:WakeLock");
-
-
- NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- String alertChannelId = "water_alert_channel";
- NotificationChannel channel = new NotificationChannel(
- alertChannelId,
- "Water Level Alerts",
- NotificationManager.IMPORTANCE_HIGH
- );
- manager.createNotificationChannel(channel);
-
- try {
- multicastLock.acquire();
- wakeLock.acquire();
- wifiLock.acquire();
- socket_recv = new DatagramSocket(null);
- socket_send = new DatagramSocket(null);
- socket_recv.setBroadcast(true);
- socket_recv.setSoTimeout(4000);
- socket_recv.setReuseAddress(true);
- socket_send.setReuseAddress(true);
- socket_send.bind(new InetSocketAddress(8177));
- socket_recv.bind(new InetSocketAddress(8176));
- Log.d("UDP Service ClientThread", "Created Sockets");
- int count = 0;
- while (!Thread.currentThread().isInterrupted()) {
- count++;
- if(count == 3) {
- Log.i("UDP Service ClientThread", "Thread is running with mode: " + mode);
- count = 0;
- }
- if(checkBound()) {
- Log.i("UDP Service ClientThread", "Reconnecting sockets");
- this.address = InetAddress.getByName("127.0.0.1");
- socket_recv.disconnect();
- socket_send.disconnect();
- socket_recv.close();
- socket_send.close();
- socket_recv = null;
- socket_send = null;
- socket_recv = new DatagramSocket(null);
- socket_send = new DatagramSocket(null);
- socket_recv.setBroadcast(true);
- socket_recv.setSoTimeout(4000);
- socket_recv.setReuseAddress(true);
- socket_send.setReuseAddress(true);
- socket_send.bind(new InetSocketAddress(8177));
- socket_recv.bind(new InetSocketAddress(8176));
- setBound(false);
- Log.i("UDP Service ClientThread", "Reconnected sockets");
- }
- // if(checkBound()) {
- // Log.i("UDP Service", "Reset socket binds: " + mode);
- // socket_recv.close();
- // socket_send.close();
- // socket_send.bind(new InetSocketAddress(8177));
- // socket_recv.bind(new InetSocketAddress(8176));
- // setBound(false);
- // if(socket_send.isBound()) {
- // Log.i("UDP Service", "Rebound socket: " + 8177);
- // }
- // else {
- // Log.i("UDP Service", "Failed to rebound socket: " + 8177);
- // }
- // if(socket_recv.isBound()) {
- // Log.i("UDP Service", "Rebound socket: " + 8176);
- // }
- // else {
- // Log.i("UDP Service", "Failed to rebound socket: " + 8176);
- // }
-
- // }
- // if(checkBound()) {
- // Log.i("UDP Service", "Reset socket binds: " + mode);
- // }
- // else {
- // Log.i("UDP Service", "Reset socket binds: " + mode);
- // }
- byte[] buffer = null;
- String updated = "";
- if(mode.equals("udp")) {
- try {
- if(socket_recv.isClosed()) {
- socket_recv.bind(new InetSocketAddress(8176));
- }
- buffer = new byte[4];
- DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
- socket_recv.receive(packet);
- } catch(java.net.SocketTimeoutException e) {
- // showToast("Socket Timeout: " + e);
- continue;
- }
- }
- else if(mode.equals("http")) {
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- Log.d("UDP Service ClientThread", "Thread exiting due to interrupt.");
- return;
- }
- Log.d("UDP Service ClientThread", "Http String Url: " + url);
- URL t_url = new URL(url);
- HttpURLConnection con = (HttpURLConnection) t_url.openConnection();
- con.setRequestMethod("GET");
- con.setConnectTimeout(5000);
- con.setReadTimeout(5000);
- con.setInstanceFollowRedirects(true);
- int status = con.getResponseCode();
- Log.d("UDP Service ClientThread", "Got status from http: " + status);
- if(status == 200) {
- BufferedReader streamReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
- String data = streamReader.readLine();
- try {
- JSONObject obj = new JSONObject(data);
- JSONArray arr = obj.getJSONArray("water_level");
- float f = (float ) arr.getDouble(0);
- updated = obj.getString("updated_on");
- buffer = ByteBuffer.allocate(4).putFloat(f).array();
- Log.d("UDP Service ClientThread", "Got value from http: " + f);
- Log.d("UDP Service ClientThread", "Last updated on: " + updated);
- } catch (JSONException e) {
- Log.e("UDP Service ClientThread", "Invalid Json");
- // showToast("Invalid Json" + e);
- continue;
- }
- }
- else {
- // showToast("Bad Response From Server: " + status);
- InputStreamReader streamReader = new InputStreamReader(con.getErrorStream());
- continue;
- }
- }
- else {
- Log.d("UDP Service ClientThread", "Invalid mode: " + mode);
- // showToast("Invalid mode: " + mode);
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- Log.d("UDP Service ClientThread", "Thread exiting due to interrupt.");
- return;
- }
- continue;
- }
- float f;
- if(bits_flipped) {
- f = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getFloat();
- }
- else {
- f = ByteBuffer.wrap(buffer).order(ByteOrder.BIG_ENDIAN).getFloat();
- }
- Log.d("UDP Service ClientThread", "Received packet: " + f);
- Log.d("UDP Service ClientThread", "Threshold is: " + alert_thres_distance);
- boolean duration_passed = false;
- if(!started) {
- duration_passed = true;
- prevAlertTimestamp = Instant.now();
- }
- else {
- Instant currentTimestamp = Instant.now();
- Duration duration = Duration.between(prevAlertTimestamp, currentTimestamp);
- if(duration.toMinutes() > 60) {
- duration_passed = true;
- }
- prevAlertTimestamp = currentTimestamp;
- }
- if(f > alert_thres_distance && alert_thres_distance < 999999 && duration_passed) {
- Notification.Builder builder = new Notification.Builder(WaterLevelService.this, alertChannelId)
- .setSmallIcon(android.R.drawable.ic_dialog_alert)
- .setContentTitle("Water Level Sensor")
- .setContentText("Water Level is below alert threshold")
- .setPriority(Notification.PRIORITY_LOW);
- manager.notify(2, builder.build());
- Log.d("UDP Service ClientThread", "Notification sent");
- if(!started) {
- started = true;
- }
- }
- if(socket_send.isClosed()) {
- socket_send.bind(new InetSocketAddress(8177));
- }
- DatagramPacket send_packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("127.0.0.1"), 8180);
- socket_send.send(send_packet);
- Log.d("UDP Service ClientThread", "Sent packet");
- if(mode.equals("http")) {
- byte[] buff2 = updated.getBytes(StandardCharsets.UTF_8);
- DatagramPacket send_packet_2 = new DatagramPacket(buff2, buff2.length, InetAddress.getByName("127.0.0.1"), 8185);
- socket_send.send(send_packet_2);
- Log.d("UDP Service ClientThread", "Sent last updated packet");
- }
- }
- }
- catch (IOException e) {
- // showToast("IOException: " + e);
- Log.e("UDP Service ClientThread", "IOException Error" + e);
- } finally {
- if (wifiLock.isHeld())
- wifiLock.release();
- if (multicastLock.isHeld())
- multicastLock.release();
- if (wakeLock.isHeld())
- wakeLock.release();
- if (socket_recv != null)
- socket_recv.close();
- }
- }
- }
-
- public void startWaterLevelService() {
- Log.d("UDP Service MainThread", "Starting the service from Activity");
-
- Intent intent = new Intent(this, WaterLevelService.class);
-
- startForegroundService(intent);
- }
- public WaterLevelService() {
- super();
- Log.i("UDP Service MainThread", "Class is created");
- }
-
- @Override
- public android.os.IBinder onBind(android.content.Intent intent) { return mMessenger.getBinder(); }
-
- @Override
- public void onCreate() {
- super.onCreate();
- Notification notification = createNotification();
- startForeground(1, notification);
- Log.i("UDP Service MainThread", "Creating Service");
-
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.i("UDP Service MainThread", "Destroying Service");
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Notification notification = createNotification();
- startForeground(1, notification);
-
-
- if (intent == null) {
- Log.w("UDP Service MainThread", "Service restarted with null intent. Stopping to avoid crash.");
- stopSelf();
- return START_NOT_STICKY;
- }
- new Thread(new
- Runnable() {
- @Override
- public void run() {
- try {
- // Move your socket and thread starting logic here
- if (udpThread == null || !udpThread.isAlive()) {
- udpThread = new Thread(new UDPClient());
- udpThread.start();
- }
- if (cmdThread == null || !cmdThread.isAlive()) {
- cmdThread = new Thread(new UDPCommandClient());
- cmdThread.start();
- }
- } catch (Exception e) {
- Log.e("UDP Service MainThread", "Error starting threads", e);
- }
- }
- }).start();
-
- Log.d("UDP Service MainThread", "Actually starting the thread");
-
- int ret = super.onStartCommand(intent, flags, startId);
-
- // (new Thread(new UDPClient())).start();
- // (new Thread(new UDPCommandClient())).start();
-
- return START_NOT_STICKY;
- }
-
- public void showToast(String str) {
- int duration = Toast.LENGTH_SHORT;
- Toast toast = Toast.makeText(this, str, duration);
- toast.show();
- }
-
- private Notification createNotification() {
- String channelId = "water_level_channel";
-
- NotificationManager manager =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-
- NotificationChannel channel = new NotificationChannel(
- channelId,
- "Water Level Service",
- NotificationManager.IMPORTANCE_LOW
- );
- manager.createNotificationChannel(channel);
-
- return new Notification.Builder(this, channelId)
- .setContentTitle("Water Level Sensor Running")
- .setContentText("Monitoring water level...")
- .setSmallIcon(android.R.drawable.ic_menu_info_details)
- .build();
- }
-
- public static void startQtAndroidService(QtActivity activity) {
- Log.d("UDP Service MainThread", "Starting the service");
- Intent intent = new Intent(activity, WaterLevelService.class);
- activity.startForegroundService(intent);
-
- }
-}
diff --git a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java
deleted file mode 100644
index 9dccce9..0000000
--- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.qtproject.example.appwaterLevelSensor;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.util.Log;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningServiceInfo;
-import org.qtproject.qt.android.bindings.QtActivity;
-import android.os.Handler;
-import android.os.Looper;
-import android.content.ServiceConnection;
-import android.content.ComponentName;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-
-public class WaterLevelServiceStarter {
- private static Messenger mServiceMessenger = null;
- private static boolean mBound = false;
- private static boolean restart_bind = false;
-
- private static ServiceConnection connection = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- mServiceMessenger = new Messenger(service);
- mBound = true;
- Log.d("UDP Service StarterThread", "Connected to Service object reference");
- if(restart_bind)
- {
- sendRestartMessage();
- Log.d("UDP Service StarterThread", "Restart Threads in Service.");
- }
- restart_bind = false;
- }
-
- @Override
- public void onServiceDisconnected(ComponentName arg0) {
- mServiceMessenger = null;
- mBound = false;
- }
- };
-
- public static void startAndBindService(Context context) {
- Intent intent = new Intent(context, WaterLevelService.class);
- context.startForegroundService(intent);
- context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
- }
-
- public static void bindService(Context context) {
- restart_bind = true;
- Intent intent = new Intent(context, WaterLevelService.class);
- context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
- }
-
- private static boolean isMyServiceRunning(Class> serviceClass, Context context) {
- ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
- if (serviceClass.getName().equals(service.service.getClassName())) {
- return true;
- }
- }
- return false;
- }
-
-
- private static int getServicePid(Class> serviceClass, Context context) {
- ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
- if (serviceClass.getName().equals(service.service.getClassName())) {
- return service.pid;
- }
- }
- return 0;
- }
-
-
- public static void sendRestartMessage() {
- if (!mBound || mServiceMessenger == null) {
- Log.d("UDP Service StarterThread", "Service is not running.");
- }
- try {
- Message msg = Message.obtain(null, WaterLevelService.MSG_RESTART_THREADS);
- mServiceMessenger.send(msg);
- Log.d("UDP Service StarterThread", "Sent restart signal to remote process");
- } catch (RemoteException e) {
- Log.e("UDP Service StarterThread", "Failed to send message", e);
- }
- }
-
- public static void startService(Context context) {
- // Log.d("UDP Service", "Starting service via QtActivity instance");
-
- // if(!isMyServiceRunning(WaterLevelService.class, context)) {
- // Intent intent = new Intent(context, WaterLevelService.class);
- // context.startForegroundService(intent);
- // }
-
- new Handler(Looper.getMainLooper()).post(new Runnable() {
- @Override
- public void run() {
-
- if (!isMyServiceRunning(WaterLevelService.class, context)) {
- Log.d("UDP Service StarterThread", "Starting service safely on Main Looper");
- startAndBindService(context);
- }
- else {
- Log.d("UDP Service StarterThread", "Service already running, so restarting the threads.");
- bindService(context);
- }
- Log.d("UDP Service StarterThread", "Background service running with pid: " + getServicePid(WaterLevelService.class, context));
- }
- });
- }
-}
diff --git a/configinterface.cpp b/configinterface.cpp
index 73ae20a..8f40d45 100644
--- a/configinterface.cpp
+++ b/configinterface.cpp
@@ -1,168 +1,60 @@
#include "configinterface.h"
+#include
+#include
+#include
+
ConfigInterface::ConfigInterface(QObject *parent)
: QObject{parent}
{
- auto paths = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
- qDebug() << "Got writable path as: " << paths;
- std::filesystem::path dir = paths.toStdString();
- if(!QDir(dir).exists()) {
- QDir().mkpath(paths);
+}
+
+QString ConfigInterface::fileName() const
+{
+ return m_fileName;
+}
+
+void ConfigInterface::setFileName(const QString &newFileName)
+{
+ if (m_fileName == newFileName)
+ return;
+ m_fileName = newFileName;
+ std::fstream input(newFileName.toStdString(), std::fstream::in);
+ if(!input.is_open()) {
+ return;
}
- std::filesystem::path path = dir / "config.json";
- QFile config(path);
- if(config.exists()) {
- qDebug() << "Loading config file....";
- bool ok = config.open(QFile::ReadOnly);
- if(!ok) {
- qDebug() << config.errorString();
- return;
- }
- auto data = config.readAll();
- config.close();
- QJsonObject top = QJsonDocument::fromJson(data).object();
- QString max, min, alert, mode, url;
- bool flipbits;
- max = top["max_threshold"].toString("110");
- min = top["min_threshold"].toString("10");
- alert = top["alert_threshold"].toString("30");
- flipbits = top["flip_bits"].toBool(true);
- mode = top["mode"].toString("http");
- url = top["url"].toString("https://unrealdragon.in/server/waterlevel");
- setMax(max);
- setMin(min);
- setAlert(alert);
- setFlipBits(flipbits);
- setMode(mode);
- setUrl(url);
+ std::string data;
+ std::string temp;
+ while(std::getline(input, temp)) {
+ data += temp;
}
- else {
- qDebug() << "File does not exist creating one...";
- QJsonObject top;
- top["max_threshold"] = "110";
- top["min_threshold"] = "10";
- top["alert_threshold"] = "30";
- top["flip_bits"] = true;
- top["mode"] = "http";
- top["url"] = "https://unrealdragon.in/server/waterlevel";
- QByteArray data = QJsonDocument(top).toJson();
- bool ok = config.open(QFile::WriteOnly);
- if(!ok) {
- qDebug() << config.errorString();
- return;
- }
- config.write(data);
- config.flush();
- config.close();
- setMax("110");
- setMin("10");
- setAlert("30");
- setFlipBits(true);
- setMode("http");
- setUrl("https://unrealdragon.in/server/waterlevel");
- qDebug() << "Config set initial values completed";
+ input.flush();
+ input.close();
+ QByteArray arr_data(data);
+ QJsonDocument doc = QJsonDocument::fromJson(arr_data);
+ // The following works even the qml part is not initailzed/connected
+ // do not change back to just emit config()
+ QMetaObject::invokeMethod(this, &ConfigInterface::config, Qt::QueuedConnection, doc.object());
+ emit fileNameChanged();
+}
+
+void ConfigInterface::deleteConfig()
+{
+ std::filesystem::remove(std::filesystem::path(m_fileName.toStdString()));
+ emit configDeleted();
+}
+
+void ConfigInterface::saveConfig(QJsonObject config)
+{
+ QJsonDocument doc;
+ doc.setObject(config);
+ std::string data = doc.toJson().toStdString();
+ std::fstream output(m_fileName.toStdString(), std::fstream::out);
+ if(!output.is_open()) {
+ return;
}
-}
-
-void ConfigInterface::saveConfig(QString max, QString min, QString alert, bool flipbits, QString mode, QString url)
-{
- auto paths = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
- qDebug() << "Got writable path as: " << paths;
- std::filesystem::path path = paths.toStdString();
- QFile config(path / "config.json");
- qDebug() << "Writing to config file..." << max << min << alert;
- QJsonObject top;
- top["max_threshold"] = max;
- top["min_threshold"] = min;
- top["alert_threshold"] = alert;
- top["flip_bits"] = flipbits;
- top["mode"] = mode;
- top["url"] = url;
- setMax(max);
- setMin(min);
- setAlert(alert);
- setMode(mode);
- QByteArray data = QJsonDocument(top).toJson();
- config.open(QIODevice::WriteOnly);
- config.write(data);
- config.flush();
- config.close();
-}
-
-QString ConfigInterface::alert() const
-{
- return m_alert;
-}
-
-void ConfigInterface::setAlert(const QString &newAlert)
-{
- if (m_alert == newAlert)
- return;
- m_alert = newAlert;
- emit alertChanged();
-}
-
-QString ConfigInterface::max() const
-{
- return m_max;
-}
-
-void ConfigInterface::setMax(const QString &newMax)
-{
- if (m_max == newMax)
- return;
- m_max = newMax;
- emit maxChanged();
-}
-
-QString ConfigInterface::min() const
-{
- return m_min;
-}
-
-void ConfigInterface::setMin(const QString &newMin)
-{
- if (m_min == newMin)
- return;
- m_min = newMin;
- emit minChanged();
-}
-
-bool ConfigInterface::flipBits() const
-{
- return m_flipBits;
-}
-
-void ConfigInterface::setFlipBits(bool newFlipBits)
-{
- if (m_flipBits == newFlipBits)
- return;
- m_flipBits = newFlipBits;
- emit flipBitsChanged();
-}
-
-QString ConfigInterface::mode() const
-{
- return m_mode;
-}
-
-void ConfigInterface::setMode(const QString &newMode)
-{
- if (m_mode == newMode)
- return;
- m_mode = newMode;
- emit modeChanged();
-}
-
-QString ConfigInterface::url() const
-{
- return m_url;
-}
-
-void ConfigInterface::setUrl(const QString &newUrl)
-{
- if (m_url == newUrl)
- return;
- m_url = newUrl;
- emit urlChanged();
+ output << data;
+ output.flush();
+ output.close();
+ QMetaObject::invokeMethod(this, &ConfigInterface::config, Qt::QueuedConnection, doc.object());
}
diff --git a/configinterface.h b/configinterface.h
index 840cc2c..e3cb75e 100644
--- a/configinterface.h
+++ b/configinterface.h
@@ -3,61 +3,28 @@
#include
#include
-#include
-#include
-#include
-#include
-#include
#include
+#include
+#include
class ConfigInterface : public QObject
{
Q_OBJECT
QML_ELEMENT
- Q_PROPERTY(QString min READ min WRITE setMin NOTIFY minChanged FINAL)
- Q_PROPERTY(QString max READ max WRITE setMax NOTIFY maxChanged FINAL)
- Q_PROPERTY(QString alert READ alert WRITE setAlert NOTIFY alertChanged FINAL)
- Q_PROPERTY(QString mode READ mode WRITE setMode NOTIFY modeChanged FINAL)
- Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged FINAL)
- Q_PROPERTY(bool flipBits READ flipBits WRITE setFlipBits NOTIFY flipBitsChanged FINAL)
+ Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged FINAL)
public:
explicit ConfigInterface(QObject *parent = nullptr);
- QString alert() const;
- void setAlert(const QString &newAlert);
-
- QString max() const;
- void setMax(const QString &newMax);
-
- QString min() const;
- void setMin(const QString &newMin);
-
- bool flipBits() const;
- void setFlipBits(bool newFlipBits);
-
- QString mode() const;
- void setMode(const QString &newMode);
-
- QString url() const;
- void setUrl(const QString &newUrl);
-
+ QString fileName() const;
+ void setFileName(const QString &newFileName);
signals:
- void configData(QString max, QString min, QString alert, bool flipbits, QString mode, QString url);
- void alertChanged();
- void maxChanged();
- void minChanged();
- void flipBitsChanged();
- void modeChanged();
- void urlChanged();
-
+ void config(QJsonObject config);
+ void fileNameChanged();
+ void configDeleted();
public slots:
- void saveConfig(QString max, QString min, QString alert, bool flipbits, QString mode, QString url);
+ void deleteConfig();
+ void saveConfig(QJsonObject config);
private:
- QString m_alert;
- QString m_max;
- QString m_min;
- bool m_flipBits;
- QString m_mode;
- QString m_url;
+ QString m_fileName;
};
#endif // CONFIGINTERFACE_H
diff --git a/echo.mp3 b/echo.mp3
new file mode 100644
index 0000000..4373d95
Binary files /dev/null and b/echo.mp3 differ
diff --git a/images.qrc b/images.qrc
deleted file mode 100644
index 2d11999..0000000
--- a/images.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- WaterTank1.png
-
-
diff --git a/main.cpp b/main.cpp
index ffff36f..ecedbf4 100644
--- a/main.cpp
+++ b/main.cpp
@@ -3,8 +3,11 @@
int main(int argc, char *argv[])
{
+ qputenv("QT_SCALE_FACTOR", "1.0");
+ qputenv("QT_SCALE_FACTOR_ROUNDING_POLICY", "Round");
QGuiApplication app(argc, argv);
+
QQmlApplicationEngine engine;
QObject::connect(
&engine,
@@ -12,7 +15,7 @@ int main(int argc, char *argv[])
&app,
[]() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
- engine.loadFromModule("waterLevelSensor", "Main");
+ engine.loadFromModule("WaterLevelRewrite", "Main");
- return app.exec();
+ return QCoreApplication::exec();
}
diff --git a/media.qrc b/media.qrc
new file mode 100644
index 0000000..2af454f
--- /dev/null
+++ b/media.qrc
@@ -0,0 +1,5 @@
+
+
+ echo.mp3
+
+
diff --git a/moving_container.h b/moving_container.h
deleted file mode 100644
index 966bce8..0000000
--- a/moving_container.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef MOVING_CONTAINER
-#define MOVING_CONTAINER
-
-#include
-using ld = long double;
-
-template class average {
- struct info {
- info *next = nullptr;
- T data;
- };
- info *head = nullptr, *tail = nullptr, *curr = nullptr;
- ld avg = 0.0;
- unsigned int count = 0;
- unsigned int max_size = 10;
-
-public:
- average(int max_size = 10) {
- this->max_size = max_size;
- }
-
- ~average() {
- curr = head;
- if(!curr)
- return;
- while(curr != nullptr) {
- info *now = curr;
- curr = curr->next;
- delete now;
- }
- curr = nullptr;
- head = nullptr;
- tail = nullptr;
- }
-
- unsigned int size() {
- return this->count;
- }
-
- double get_average() {
- return this->avg;
- }
-
- void clear() {
- curr = head;
- if(!curr)
- return;
- while(curr != nullptr) {
- info *now = curr;
- curr = curr->next;
- delete now;
- }
- curr = nullptr;
- head = nullptr;
- tail = nullptr;
- count = 0;
- }
-
- void recalculate_average() {
- curr = head;
- this->avg = 0;
- this->count = 0;
- while(curr != nullptr) {
- info *now = curr;
- auto t = now->data;
- curr = curr->next;
- count++;
- avg = ((avg) * (ld) (count - 1) / (ld )(count)) + ((ld)(t) / (ld) count);
- }
- }
-
- void push_back(T t) {
- if(head == nullptr) {
- auto *i = new info();
- i->next = nullptr;
- i->data = t;
- head = i;
- tail = i;
- }
- else {
- info *i = new info();
- if(this->count == max_size) {
- T old_value;
- if(head->next) {
- auto *temp = head;
- head = head->next;
- old_value = temp->data;
- delete temp;
- }
- recalculate_average();
- tail->next = i;
- i->next = nullptr;
- i->data = t;
- tail = i;
- }
- tail->next = i;
- i->next = nullptr;
- i->data = t;
- tail = i;
- }
- count++;
- avg = ((avg) * (ld) (count - 1) / (ld )(count)) + ((ld)(t) / (ld) count);
- }
-};
-
-
-
-
-#endif
diff --git a/requestworker.cpp b/requestworker.cpp
new file mode 100644
index 0000000..f0657ef
--- /dev/null
+++ b/requestworker.cpp
@@ -0,0 +1,302 @@
+#include "requestworker.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+RequestWorker::RequestWorker(QObject *parent)
+ : QObject{parent}
+{
+#ifdef Q_OS_ANDROID
+ wifi = new WiFiHelper(this);
+ connect(wifi, &WiFiHelper::ssidRetrieved, this, &RequestWorker::gotWiFiSSID);
+ poll_wifi = new QTimer(this);
+ poll_wifi->setInterval(3000);
+ poll_wifi->setSingleShot(false);
+ connect(poll_wifi, &QTimer::timeout, this, &RequestWorker::pollWiFi);
+ wifi->requestPermissionsAndFetch();
+ poll_wifi->start();
+#endif
+}
+
+void RequestWorker::setConfig(QJsonObject config)
+{
+ this->url = config["url"].toString();
+ this->interval = config["interval"].toInt();
+ this->max_height = config["max"].toInt();
+ this->min_height = config["min"].toInt();
+ this->alarm_thres = config["alert"].toInt();
+ qDebug() << "In cpp config values are: ";
+ qDebug() << "Url: " << url;
+ qDebug() << "Interval: " << interval;
+ qDebug() << "Max Height: " << max_height;
+ qDebug() << "Min Height: " << min_height;
+ qDebug() << "Alert: " << alarm_thres;
+}
+
+void RequestWorker::start()
+{
+ if(manager) {
+ manager->deleteLater();
+ manager = nullptr;
+ }
+ manager = new QNetworkAccessManager();
+ if(timer) {
+ timer->deleteLater();
+ timer = nullptr;
+ }
+ timer = new QTimer(this);
+ timer->setInterval(interval);
+ timer->setSingleShot(true);
+ connect(timer, &QTimer::timeout, this, &RequestWorker::makeRequest);
+ timer->start();
+ stop_timer = false;
+ setStatus("Waiting to send.");
+}
+
+void RequestWorker::stop()
+{
+ if(timer->isActive())
+ timer->stop();
+ stop_timer = true;
+ qDebug() << "Stopping" << timer->isActive();
+}
+
+QString RequestWorker::getRequestResponseString(QNetworkReply *rep, QString rep_data)
+{
+ if(!rep) {
+ return "No Reply";
+ }
+ QNetworkRequest req = rep->request();
+ QString errorString = rep->errorString();
+ QString resp = "GET " + req.url().toString() + "\n";
+ for(auto &key: req.rawHeaderList()) {
+ QByteArray value = req.rawHeader(key);
+ resp += key + ": " + value + "\n";
+ }
+ qDebug() << "Raw header list size: " << req.rawHeaderList().size();
+ QString ret;
+ ret = "Request:\n";
+ ret += resp;
+ ret += "Response:\n";
+ if(rep_data == "")
+ ret += errorString;
+ else
+ ret += rep_data;
+ return ret;
+}
+
+void RequestWorker::pollWiFi()
+{
+#ifdef Q_OS_ANDROID
+ if(wifi) {
+ wifi->requestPermissionsAndFetch();
+ }
+#endif
+}
+
+void RequestWorker::gotWiFiSSID(QString ssid)
+{
+ if(ssid.length() > 0 && ssid != "Permission Denied" && ssid != "") {
+ emit connectionText("WiFi connected: " + ssid);
+ }
+ else if(ssid.length() == 0) {
+ emit connectionText("WiFi might not be connected", true);
+ }
+ else if(ssid != "") {
+ emit connectionText("Failed to query wifi info", true);
+ }
+ else {
+ emit connectionText("WiFi disconnected", true);
+ }
+}
+
+void RequestWorker::playAlarm()
+{
+ if (player_started)
+ return;
+
+ // Step 1: Copy the qrc resource to a local cache file so Android can read it
+#ifdef Q_OS_ANDROID
+ QString qrcPath = ":/sounds/echo.mp3";
+ QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/echo.mp3";
+
+ if (QFile::exists(cachePath)) {
+ QFile::remove(cachePath);
+ }
+
+ if (QFile::copy(qrcPath, cachePath)) {
+ qDebug() << "Successfully copied resource to cache:" << cachePath;
+ } else {
+ qDebug() << "Failed to copy resource to cache path!";
+ return;
+ }
+
+ // Step 2: Use Android JNI to initialize and play via android.media.MediaPlayer
+ QJniEnvironment env;
+
+ // Create new android.media.MediaPlayer()
+ QJniObject mediaPlayer("android/media/MediaPlayer");
+ if (!mediaPlayer.isValid()) {
+ qDebug() << "Failed to create native Android MediaPlayer!";
+ return;
+ }
+
+ // Set data source using the local cache file path
+ QJniObject jPath = QJniObject::fromString(cachePath);
+ mediaPlayer.callMethod("setDataSource", "(Ljava/lang/String;)V", jPath.object());
+
+ // Enable looping
+ mediaPlayer.callMethod("setLooping", "(Z)V", true);
+
+ // Prepare and start playback
+ mediaPlayer.callMethod("prepare");
+ mediaPlayer.callMethod("start");
+
+ // Store a global reference so it doesn't get garbage collected immediately
+ m_androidMediaPlayer = new QJniObject(mediaPlayer.object());
+
+ player_started = true;
+ qDebug() << "Native Android alarm started successfully.";
+ return;
+#endif
+ if(player) {
+ player->deleteLater();
+ player = nullptr;
+ }
+ if(audioOutput) {
+ audioOutput->deleteLater();
+ audioOutput->deleteLater();
+ }
+ qDebug() << "Loading player";
+ player = new QMediaPlayer(this);
+ audioOutput = new QAudioOutput(this);
+ player->setLoops(QMediaPlayer::Loops::Infinite);
+ player->setAudioOutput(audioOutput);
+ if (QFile::exists(":/sounds/echo.mp3")) {
+ qDebug() << "Resource found successfully!";
+ } else {
+ qDebug() << "Resource not found. Check CMakeLists.txt or .pro file!";
+ }
+ player->setSource(QUrl("qrc:/sounds/echo.mp3"));
+ audioOutput->setVolume(0.5);
+ connect(this, &RequestWorker::playMedia, player, &QMediaPlayer::play);
+ player_started = true;
+ emit playMedia();
+}
+
+void RequestWorker::makeRequest()
+{
+ if(stop_timer)
+ return;
+ QNetworkRequest req;
+ req.setUrl(this->url);
+ disconnect(manager, nullptr, nullptr, nullptr);
+ connect(manager, &QNetworkAccessManager::finished, this, &RequestWorker::requestFinished);
+ manager->setTransferTimeout(15000);
+ manager->get(req);
+ setStatus("Request sent; Waiting for Reply.");
+}
+
+void RequestWorker::requestFinished(QNetworkReply *rep)
+{
+ QByteArray data = rep->readAll();
+ auto now = std::chrono::high_resolution_clock::now();
+ auto time_diff = std::chrono::duration_cast(now - prev_time);
+ if(data.size() == 0) {
+ prev_time = now;
+ timer->start();
+ QString request_response_string = getRequestResponseString(rep);
+ emit requestFailed(request_response_string);
+ emit setStatus("Invalid Response; Resending Request");
+ rep->deleteLater();
+ rep = nullptr;
+ return;
+ }
+ unsigned int time_in_microseconds = time_diff.count();
+ QJsonObject jdata = QJsonDocument::fromJson(data).object();
+ auto data_water_level = jdata["water_level"].toInt();
+ float water_level = (float) ((float) (data_water_level) / 10.0);
+ float rate_of_fill = ( float )(prev_distance - water_level) /(float) time_in_microseconds;
+ rate_of_fill = rate_of_fill * 1'000'000;
+ int expected_to_fill = (water_level - min_height) / rate_of_fill;
+ int percentage_filled = (float (max_height - water_level) / float (max_height)) * 100;
+ if(enable_alarm) {
+ qDebug() << "Alarm enabled";
+ if(water_level <= alarm_thres) {
+ playAlarm();
+ }
+ }
+ if(reply_number == 0) {
+ rate_of_fill = 0;
+ expected_to_fill = 0;
+ }
+ reply_number = 1;
+ qDebug() << "Got water level: " << water_level;
+ qDebug() << "Got rate of fill: " << rate_of_fill;
+ qDebug() << "Got expected to fill: " << expected_to_fill;
+ qDebug() << "Percentage filled: " << percentage_filled;
+ qDebug() << "Time difference: " << time_in_microseconds;
+ prev_distance = water_level;
+ QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
+ QString request_response_string = getRequestResponseString(rep, data);
+ request_response_string += "Timestamp:\n" + timestamp;
+ rep->deleteLater();
+ rep = nullptr;
+ emit networkReply(data_water_level, rate_of_fill, expected_to_fill, request_response_string);
+ if(stop_timer) {
+ setStatus("Timer stopped.");
+ return;
+ }
+ prev_time = now;
+ timer->start();
+ QString status = "Got Reply on: " + timestamp + "; Waiting to Send.";
+ setStatus(status);
+}
+
+QString RequestWorker::status() const
+{
+ return m_status;
+}
+
+void RequestWorker::setStatus(const QString &newStatus)
+{
+ if (m_status == newStatus)
+ return;
+ m_status = newStatus;
+ emit statusChanged();
+}
+
+void RequestWorker::startAlarm()
+{
+ enable_alarm = true;
+}
+
+void RequestWorker::stopAlarm()
+{
+ if (!player_started)
+ return;
+
+ enable_alarm = false;
+ player_started = false;
+ if(player) {
+ player->stop();
+ player->deleteLater();
+ player = nullptr;
+ }
+ if(audioOutput) {
+ audioOutput->deleteLater();
+ audioOutput = nullptr;
+ }
+#ifdef Q_OS_ANDROID
+ if (m_androidMediaPlayer && m_androidMediaPlayer->isValid()) {
+ m_androidMediaPlayer->callMethod("stop");
+ m_androidMediaPlayer->callMethod("release");
+
+ delete m_androidMediaPlayer;
+ m_androidMediaPlayer = nullptr;
+ }
+#endif
+}
diff --git a/requestworker.h b/requestworker.h
new file mode 100644
index 0000000..c759e44
--- /dev/null
+++ b/requestworker.h
@@ -0,0 +1,74 @@
+#ifndef REQUESTWORKER_H
+#define REQUESTWORKER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef Q_OS_ANDROID
+#include "wifihelper.h"
+#include
+#endif
+
+class RequestWorker : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QString status READ status WRITE setStatus NOTIFY statusChanged FINAL)
+ QNetworkAccessManager *manager = nullptr;
+ QTimer *timer = nullptr;
+ QString url;
+ int interval;
+ float prev_distance = 0;
+ int max_height = 0;
+ int min_height = 0;
+ bool stop_timer = false;
+ int reply_number = 0;
+ int alarm_thres = 0;
+ bool enable_alarm = false;
+ bool player_started = false;
+ QMediaPlayer *player = nullptr;
+ QAudioOutput *audioOutput = nullptr;
+#ifdef Q_OS_ANDROID
+ WiFiHelper *wifi = nullptr;
+ QTimer *poll_wifi = nullptr;
+#endif
+ std::chrono::time_point prev_time;
+public:
+ explicit RequestWorker(QObject *parent = nullptr);
+ QString status() const;
+ void setStatus(const QString &newStatus);
+
+signals:
+ void requestFailed(QString request_response_string);
+ void networkReply(int distance, float rate_of_fill, int expected_to_fill, QString data);
+ void statusChanged();
+ void playMedia();
+ void connectionText(QString text, bool errored = false);
+public slots:
+ void startAlarm();
+ void stopAlarm();
+ void setConfig(QJsonObject config);
+ void start();
+ void stop();
+private slots:
+ QString getRequestResponseString(QNetworkReply *rep, QString rep_data = "");
+ void pollWiFi();
+ void gotWiFiSSID(QString ssid);
+ void playAlarm();
+ void makeRequest();
+ void requestFinished(QNetworkReply *rep);
+private:
+ QString m_status;
+#ifdef Q_OS_ANDROID
+ QJniObject *m_androidMediaPlayer = nullptr;
+#endif
+};
+
+#endif // REQUESTWORKER_H
diff --git a/service.cpp b/service.cpp
deleted file mode 100644
index f48e455..0000000
--- a/service.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-#include "service.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-// Q_DECLARE_JNI_CLASS(Intent, "android/content/Intent")
-Q_DECLARE_JNI_CLASS(ComponentName, "android/content/ComponentName")
-Q_DECLARE_JNI_CLASS(WaterLevelService, "org/qtproject/example/appwaterLevelSensor/WaterLevelService")
-
-static volatile float level = -1;
-
-static void handleWaterLevelUpdate(JNIEnv *env, jobject /*obj*/, jbyteArray data) {
- jbyte *bytes = env->GetByteArrayElements(data, nullptr);
- jsize len = env->GetArrayLength(data);
-
- // Process your data here
- qDebug() << "Received data from Java broadcast! Length:" << len;
-
-
- level = *((float *) (bytes));
-
- qDebug() << "Level received as: " << level;
-
- env->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
-}
-
-extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
- JNIEnv* env;
- if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) {
- return JNI_ERR;
- }
-
- // Register your native method ONCE when the library loads
- jclass javaClass = env->FindClass("org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver");
- if (javaClass) {
- static JNINativeMethod methods[] = {
- {"updateWaterLevel", "([B)V", (void *)handleWaterLevelUpdate}
- };
- env->RegisterNatives(javaClass, methods, 1);
- }
-
- return JNI_VERSION_1_6;
-}
-
-
-
-service::service(QObject *parent)
- : QObject{parent}
-{
- if(QSysInfo::productType() == "android") {
- qDebug() << "Running android service";
- // QJniEnvironment env;
- // // if (env->ExceptionCheck()) env->ExceptionClear();
- // // static bool nativesRegistered = false;
-
- // // if(!nativesRegistered) {
- // // // jclass javaClass = env.findClass("org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver");
- // // // if (javaClass) {
- // // // JNINativeMethod methods[] = {{"updateWaterLevel", "([B)V", (void *)handleWaterLevelUpdate}};
- // // // if (env->RegisterNatives(javaClass, methods, 1) < 0) {
- // // // qDebug() << "RegisterNatives failed!";
- // // // } else {
- // // // nativesRegistered = true;
- // // // }
- // // // } else {
- // // // qDebug() << "Could not find WaterLevelReceiver class";
- // // // }
- // // // if (env->ExceptionCheck()) env->ExceptionClear();
- // // }
- // using namespace QtJniTypes;
- // using namespace QNativeInterface;
-
-
-
- // auto *androidApp = qGuiApp->nativeInterface();
- // // Context context = androidApp->context();
- // QJniObject context = QNativeInterface::QAndroidApplication::context();
- // auto serviceClass = env.findClass(Traits::className());
- // if (!serviceClass) {
- // qDebug() << "FAILED to find WaterLevelService class!";
- // env->ExceptionDescribe(); // VERY important
- // env->ExceptionClear();
- // return;
- // }
- // if (context.isValid()) {
- // QJniObject::callStaticMethod<
- // void>(
- // "org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter",
- // "startService",
- // "(Landroid/content/Context;)V",
- // context.object());
- // }
- }
-}
-
-void service::moveAppToBackground()
-{
- QJniObject activity = QNativeInterface::QAndroidApplication::context();
- if (activity.isValid()) {
- qDebug() << "Moved app to background";
- activity.callMethod("moveTaskToBack", "(Z)Z", true);
- }
- qDebug() << "Closing";
-}
-
-void service::begin() {
-
-
- // Only call this once the UI is visible
- QJniObject context = QNativeInterface::QAndroidApplication::context();
- QJniObject::callStaticMethod(
- "org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter",
- "startService",
- "(Landroid/content/Context;)V",
- context.object()
- );
-}
-
diff --git a/service.h b/service.h
deleted file mode 100644
index 1ba18a1..0000000
--- a/service.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef SERVICE_H
-#define SERVICE_H
-
-#include
-
-class service : public QObject
-{
- Q_OBJECT
-public:
- explicit service(QObject *parent = nullptr);
- void moveAppToBackground();
- void begin();
-
-signals:
-};
-
-#endif // SERVICE_H
diff --git a/servicemain.cpp b/servicemain.cpp
deleted file mode 100644
index e0a684e..0000000
--- a/servicemain.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include
-#include
-#include "service.h"
-
-int main(int argc, char *argv[])
-{
- qWarning() << "Service starting from a separate .so file";
- QAndroidService app(argc, argv);
- service service;
-
- return app.exec();
-}
\ No newline at end of file
diff --git a/udplistener.cpp b/udplistener.cpp
deleted file mode 100644
index 155df4e..0000000
--- a/udplistener.cpp
+++ /dev/null
@@ -1,475 +0,0 @@
-#include "udplistener.h"
-#include
-
-
-UDPListener::UDPListener(QObject *parent)
- : QObject{parent}
-{
- worker = new Worker();
- thread = new QThread();
- worker->moveToThread(thread);
- connect(worker, &Worker::lastUpdateddatagramReceived, this, &UDPListener::lastUpdateddatagramReceived);
- connect(worker, &Worker::datagramReceived, this, &UDPListener::datagramRecieved);
- connect(worker, &Worker::socketError, this, &UDPListener::socketError);
- connect(worker, &Worker::sendConfig, this, &UDPListener::sendConfigOnSet);
- connect(this, &UDPListener::startWorker, worker, &Worker::run);
- connect(this, &UDPListener::changeWorkerMode, worker, &Worker::changeMode);
- thread->start();
- emit startWorker();
- timer = new QTimer(this);
- timer->setInterval(10000);
- timer->setSingleShot(false);
- connect(timer, &QTimer::timeout, this, &UDPListener::connectionTimerTimedOut);
- connect(qApp, &QCoreApplication::aboutToQuit, this, &UDPListener::destroy);
-}
-
-double UDPListener::distance() const
-{
- return m_distance;
-}
-
-void UDPListener::setDistance(double newDistance)
-{
- m_distance = newDistance;
- emit distanceChanged();
-}
-
-void UDPListener::sendConfigOnSet()
-{
- bool flip_bits = m_flip_bits;
- QString url = m_url;
- QString mode = m_mode;
- float distance = m_distance;
- if(!command_socket) {
- command_socket = new QUdpSocket(this);
- bool bind = command_socket->bind(QHostAddress::Any, 0);
- if(bind) {
- QNetworkDatagram data1, data2, data3, data4;
- data1.setDestination(QHostAddress::LocalHost, 8178);
- data2.setDestination(QHostAddress::LocalHost, 8178);
- data3.setDestination(QHostAddress::LocalHost, 8178);
- data4.setDestination(QHostAddress::LocalHost, 8178);
-
- QString s_data = "";
- while(s_data.toUtf8().size() != 1024) {
- s_data += "=";
- }
- data1.setData(s_data.toUtf8());
- command_socket->writeDatagram(data1);
- command_socket->flush();
-
- QString b_data = "";
- while(b_data.toUtf8().size() != 1024) {
- b_data += "=";
- }
- data2.setData(b_data.toUtf8());
- command_socket->writeDatagram(data2);
- command_socket->flush();
-
- QString m_data = "";
- while(m_data.toUtf8().size() != 1024) {
- m_data += "=";
- }
- data3.setData(m_data.toUtf8());
- command_socket->writeDatagram(data3);
- command_socket->flush();
-
- QString u_data = "";
- while(u_data.toUtf8().size() != 1024) {
- u_data += "=";
- }
- data4.setData(u_data.toUtf8());
- command_socket->writeDatagram(data4);
- command_socket->flush();
-
- command_socket->close();
- command_socket->deleteLater();
- command_socket = nullptr;
-
- qDebug() << "Sent all Command Statements.";
- emit changeWorkerMode(mode, url);
- }
- else {
- showError("Command Socket Bind Failed");
- }
- }
-}
-
-void UDPListener::moveAppToBackground()
-{
-#ifdef Q_OS_ANDROID
- service->moveAppToBackground();
-#endif
-}
-
-void UDPListener::lastUpdateddatagramReceived(QByteArray data)
-{
- QString s_data = QString::fromUtf8(data);
- qDebug() << "Main Class last updated on: " << s_data;
- QDateTime time = QDateTime::fromString(s_data, "dd/MM/yy hh:mm:ss").addYears(100);
- QDateTime thres_time = QDateTime::currentDateTime().addSecs(-1800);
- if(time.isValid() && thres_time < time)
- {
- emit lastUpdatedChanged(s_data, false);
- }
- else if(thres_time > time && time.isValid()) {
- emit lastUpdatedChanged(s_data, true);
- }
- else if(!time.isValid()) {
- emit lastUpdatedChanged("Invalid Date", true);
- }
-}
-
-void UDPListener::continueDestruction()
-{
- destroyed = true;
-}
-
-void UDPListener::resetAverages()
-{
- averages.clear();
-}
-
-void UDPListener::setFlipBits(bool flip_bits)
-{
- m_flip_bits = flip_bits;
-}
-
-void UDPListener::setConfig(float distance, bool flip_bits, QString mode, QString url)
-{
- setFlipBits(flip_bits);
- setDistance(distance);
- setMode(mode);
- setUrl(url);
- emit changeWorkerMode(mode, url);
- qDebug() << "Setting config from UI completed." << flip_bits << distance << mode << url;
- averages.clear();
-}
-
-void UDPListener::socketError(QString err)
-{
- qDebug() << "Socket error received: " << err;
- if(err == "The bound address is already in use") {
- err = "Port already in use.";
- }
- emit showError(err);
-}
-
-void UDPListener::startService()
-{
-#ifdef Q_OS_ANDROID
- qDebug() << "Running on Android";
- service = new class service();
- qDebug() << "Ran service";
- service->begin();
-#endif
-
-}
-
-void UDPListener::datagramRecieved(QByteArray data1, QByteArray data2)
-{
- std::string correct_order = "";
-#ifdef Q_OS_ANDROID
- if(!m_flip_bits)
- correct_order = "0x" + data2.toHex().toStdString();
- else
- correct_order = "0x" + data1.toHex().toStdString();
-#else
- if(!m_flip_bits)
- correct_order = "0x" + data1.toHex().toStdString();
- else
- correct_order = "0x" + data2.toHex().toStdString();
-#endif
- float data = convertToFloat(correct_order);
- qDebug() << "Data received in main class: " << data;
- averages.push_back(data);
- prev_time_point = std::chrono::high_resolution_clock::now();
- emit distanceReceived(data, QDateTime::currentDateTime().toString("dd/mm/yyyy hh:mm:ss.z ap"));
- if(averages.size() >= 5) {
- this->m_distance = averages.get_average();
- emit distanceChanged();
- }
- if(averages.size() == 1)
- emit connected();
-
-}
-
-void UDPListener::destroy()
-{
- qDebug() << "Destroying UDP Listener";
-
-#ifdef Q_OS_ANDROID
- delete service;
-#endif
- disconnect(timer, nullptr, nullptr, nullptr);
- timer->stop();
- QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
- connect(this, &UDPListener::stopWorker, worker, &Worker::stop);
- connect(worker, &Worker::stopped, this, &UDPListener::continueDestruction);
- emit stopWorker();
- while(!destroyed) {
- QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
- }
- thread->exit(0);
- QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
- worker->deleteLater();
- QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
- while(thread->isRunning()) {
- qDebug() << "The thread is still running.";
- QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
- }
- thread->deleteLater();
- QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
- timer->deleteLater();
- QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
- qDebug() << "Deinitialized UDP Listener";
-}
-
-void UDPListener::connectionTimerTimedOut()
-{
- using namespace std::chrono_literals;
- auto current_time_point = std::chrono::high_resolution_clock::now();
- if((current_time_point - prev_time_point) > 10s) {
- qDebug() << "Disconnected::" << current_time_point.time_since_epoch() << " " << prev_time_point.time_since_epoch();
- }
- else {
- qDebug() << "All fine";
- }
-}
-
-float UDPListener::convertToFloat(std::string hex)
-{
- uint32_t num;
- sscanf(hex.c_str(), "%x", &num);
- float dist = *((float*)&num);
- return dist;
-}
-
-Worker::Worker(QObject *parent)
-{
-
-}
-
-void Worker::gotHttpReply(QNetworkReply *rep)
-{
- QByteArray m_data = rep->readAll();
- rep->deleteLater();
- qDebug() << "Got reply from http server";
- QJsonObject top = QJsonDocument::fromJson(m_data).object();
- float distance = (float ) top["water_level"].toArray()[0].toDouble();
- QByteArray data, data2;
- qDebug() << "Updated on: " << top["updated_on"].toString();
- data = QByteArray::fromRawData((char *)&distance, sizeof(float));
- char *buff = new char[4];
- char *reader = (char *) &distance;
- for(int i=0;i< sizeof(float) ;i++) {
- qDebug() << i << "vs" << sizeof(float) - 1 - i;
- buff[i] = reader[sizeof(float) - 1 - i];
- }
- delete[] buff;
- data2 = QByteArray::fromRawData(buff, sizeof(float));
- emit datagramReceived(data, data2);
- emit lastUpdateddatagramReceived(top["updated_on"].toString().toUtf8());
-}
-
-void Worker::httpTimerTimeout()
-{
- qDebug() << "Worker httpTimer Timeout";
- QNetworkRequest req;
- req.setUrl(QUrl("https://unrealdragon.in/server/waterlevel"));
- disconnect(manager, nullptr, nullptr, nullptr);
- connect(manager, &QNetworkAccessManager::finished, this, &Worker::gotHttpReply);
- manager->get(req);
-}
-
-void Worker::stop()
-{
- if(socket_2) {
- disconnect(socket_2, nullptr, nullptr, nullptr);
- socket_2->close();
- delete socket_2;
- }
- if(socket) {
- disconnect(socket, nullptr, nullptr, nullptr);
- socket->close();
- delete socket;
- }
- if(socket_3) {
- disconnect(socket_3, nullptr, nullptr, nullptr);
- socket_3->close();
- delete socket_3;
- }
- if(httpTimer) {
- disconnect(httpTimer, nullptr, nullptr, nullptr);
- delete httpTimer;
- }
- if(manager) {
- disconnect(manager, nullptr, nullptr, nullptr);
- delete manager;
- }
- emit stopped();
-}
-
-void Worker::run()
-{
- using namespace std::chrono_literals;
- socket = new QUdpSocket(this);
- socket_2 = new QUdpSocket(this);
- socket_3 = new QUdpSocket(this);
- httpTimer = new QTimer(this);
- manager = new QNetworkAccessManager(this);
-#ifdef Q_OS_ANDROID
- // If it is android use it as a command socket;
- bool bind = socket->bind(QHostAddress::LocalHost, 8180, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress);
- bool bind2 = socket_2->bind(QHostAddress::LocalHost, 8185, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress);
- bool bind3 = socket_3->bind(QHostAddress::LocalHost, 8186, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress);
-#else
- // Else use it as a receiver;
- bool bind = socket->bind(QHostAddress("192.168.1.255"), 8176);
- bool bind2 = true;
-#endif
- if(!bind || !bind2 || !bind3)
- {
- qDebug() << "UI Worker socket bind failed" << bind << socket->errorString();
- qDebug() << "UI Worker socket bind failed" << bind2 << socket_2->errorString();
- qDebug() << "UI Worker socket bind failed" << bind3 << socket_3->errorString();
- emit socketError(socket->errorString());
- emit socketError(socket_2->errorString());
- emit socketError(socket_3->errorString());
- qApp->exit(-1);
- }
- connect(httpTimer, &QTimer::timeout, this, &Worker::httpTimerTimeout);
- connect(socket, &QUdpSocket::readyRead, this, &Worker::socketDataReceived);
- connect(socket_2, &QUdpSocket::readyRead, this, &Worker::socket2DataReceived);
- connect(socket_3, &QUdpSocket::readyRead, this, &Worker::socket3DataReceived);
-#ifdef Q_OS_ANDROID
- qDebug() << "Waiting for message from service.";
-#endif
-
-}
-
-void Worker::changeMode(QString mode, QString url)
-{
- qDebug() << "Worker changing mode: " << mode << url;
- this->mode = mode;
- this->url = url;
- if(mode == "http" && QSysInfo::productType() != "android") {
- httpTimer->setInterval(2000);
- httpTimer->setSingleShot(false);
- httpTimer->start();
- }
- else if(mode == "udp") {
- httpTimer->stop();
- socketDataReceived();
- }
- if(socket) {
- if(socket->hasPendingDatagrams()) {
- socketDataReceived();
- }
- }
- if(socket_2) {
- if(socket_2->hasPendingDatagrams()) {
- socket2DataReceived();
- }
- }
- if(socket_3) {
- if(socket_3->hasPendingDatagrams()) {
- socket3DataReceived();
- }
- }
-
-}
-
-void Worker::socket3DataReceived()
-{
- if(socket_3->hasPendingDatagrams()) {
- while(this->mode == "" && this->url == "") {
- QCoreApplication::processEvents();
- // qDebug() << "Worker is waiting for mode to be set.";
- }
- size_t data_size = socket_3->pendingDatagramSize();
- qDebug() << "Socket 3 has pending data: " << data_size;
- if(data_size < 100000) {
- char buff[data_size];
- socket_3->readDatagram(buff, data_size);
- QByteArray data = QByteArray::fromRawData(buff, data_size);
- if(QString::fromUtf8(data) == "") {
- qDebug() << "Data Received from Service Threads.";
- emit sendConfig();
- }
- }
- }
-}
-
-void Worker::socket2DataReceived()
-{
- if(socket_2->hasPendingDatagrams()) {
- if(mode == "http") {
- size_t data_size = socket_2->pendingDatagramSize();
- qDebug() << "Socket 2 has pending data: " << data_size;
- char buff[data_size];
- socket_2->readDatagram(buff, data_size);
- QByteArray data = QByteArray::fromRawData(buff, data_size);
- emit lastUpdateddatagramReceived(data);
- }
- else if(mode == "udp") {
- qDebug() << "Last Updated on Socket data received in udp mode ignoring it.";
- }
- else {
- qDebug() << "Mode is not set in socket 2";
- }
- }
-
-}
-
-void Worker::socketDataReceived()
-{
- if(socket->hasPendingDatagrams()) {
- if(mode == "udp" || (mode == "http" && QSysInfo::productType() == "android")) {
- qDebug() << "Socket has pending data: " << socket->pendingDatagramSize();
- char buff[4] = {0x00, 0x00, 0x00, 0x00};
- socket->readDatagram(buff, 4);
- auto data= QByteArray(buff,4);
- char ch[4];
- for(int i=0;i<4;i++) {
- ch[i] = buff[3-i];
- }
- auto data2 = QByteArray(ch,4);
- std::string correct_order = "";
- qDebug() << "Distance received: " << correct_order;
- emit datagramReceived(data, data2);
- }
- else if(mode == "http") {
- qDebug() << "Socket data received in http mode ignoring it.";
- }
- else {
- qDebug() << "Mode is not set in socket.";
- }
- }
-}
-
-QString UDPListener::mode() const
-{
- return m_mode;
-}
-
-void UDPListener::setMode(const QString &newMode)
-{
- if (m_mode == newMode)
- return;
- m_mode = newMode;
- emit modeChanged();
-}
-
-QString UDPListener::url() const
-{
- return m_url;
-}
-
-void UDPListener::setUrl(const QString &newUrl)
-{
- if (m_url == newUrl)
- return;
- m_url = newUrl;
- emit urlChanged();
-}
diff --git a/udplistener.h b/udplistener.h
deleted file mode 100644
index 079dd35..0000000
--- a/udplistener.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#ifndef UDPLISTENER_H
-#define UDPLISTENER_H
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "moving_container.h"
-
-#ifdef Q_OS_ANDROID
-#include "service.h"
-#endif
-
-
-class Worker: public QObject {
- Q_OBJECT
- QUdpSocket *socket = nullptr;
- QUdpSocket *socket_2 = nullptr;
- QUdpSocket *socket_3 = nullptr;
- QString mode = "";
- QString url = "";
- QNetworkAccessManager *manager = nullptr;
- QTimer *httpTimer = nullptr;
-public:
- explicit Worker(QObject *parent = nullptr);
-signals:
- void sendConfig();
- void stopped();
- void socketError(QString error);
- void lastUpdateddatagramReceived(QByteArray data);
- void datagramReceived(QByteArray data, QByteArray data2);
-public slots:
- void gotHttpReply(QNetworkReply *rep);
- void httpTimerTimeout();
- void stop();
- void run();
- void changeMode(QString mode, QString url);
-private slots:
- void socket3DataReceived();
- void socket2DataReceived();
- void socketDataReceived();
-};
-
-class UDPListener : public QObject
-{
- Q_OBJECT
- QML_ELEMENT
- Q_PROPERTY(double distance READ distance WRITE setDistance NOTIFY distanceChanged FINAL)
- Q_PROPERTY(QString mode READ mode WRITE setMode NOTIFY modeChanged FINAL)
- Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged FINAL)
- bool destroyed = false;
-#ifdef Q_OS_ANDROID
- service *service = nullptr;
-#endif
-public:
- explicit UDPListener(QObject *parent = nullptr);
- double distance() const;
- void setDistance(double newDistance);
- QString mode() const;
- void setMode(const QString &newMode);
-
- QString url() const;
- void setUrl(const QString &newUrl);
-
-signals:
- void lastUpdatedChanged(QString last_updated, bool warn);
- void stopWorker();
- void showError(QString err);
- void distanceChanged();
- void startWorker();
- void connected();
- void disconnected();
- void distanceReceived(double dist, QString timestamp);
- void changeWorkerMode(QString mode, QString url);
- void modeChanged();
- void initLoadFinished();
- void urlChanged();
-
-public slots:
- void sendConfigOnSet();
- void moveAppToBackground();
- void lastUpdateddatagramReceived(QByteArray data);
- void continueDestruction();
- void resetAverages();
- void setFlipBits(bool flip_bits);
- void setConfig(float distance, bool flip_bits, QString mode, QString url);
- void socketError(QString err);
- void startService();
- void datagramRecieved(QByteArray data, QByteArray data2);
-private slots:
- void destroy();
- void connectionTimerTimedOut();
- float convertToFloat(std::string hex);
-private:
- QUdpSocket *command_socket = nullptr;
- std::chrono::time_point prev_time_point;
- average averages;
- double m_distance;
- Worker *worker = nullptr;
- QThread *thread = nullptr;
- QTimer *timer = nullptr;
- bool m_flip_bits;
- QString m_mode;
- QString m_url;
-};
-
-#endif // UDPLISTENER_H
diff --git a/wifihelper.cpp b/wifihelper.cpp
new file mode 100644
index 0000000..835cea7
--- /dev/null
+++ b/wifihelper.cpp
@@ -0,0 +1,77 @@
+#include "wifihelper.h"
+#include
+#include
+#ifdef Q_OS_ANDROID
+#include
+#endif
+#include
+
+WiFiHelper::WiFiHelper(QObject *parent)
+ : QObject{parent}
+{
+
+}
+
+QString WiFiHelper::getWifiSsid() {
+#ifdef Q_OS_ANDROID
+ // Get Android application context in Qt 6
+ QJniObject context = QNativeInterface::QAndroidApplication::context();
+ if (!context.isValid()) return QString();
+
+ // Get WifiManager service
+ QJniObject wifiService = QJniObject::fromString("wifi");
+ QJniObject wifiManager = context.callObjectMethod(
+ "getSystemService",
+ "(Ljava/lang/String;)Ljava/lang/Object;",
+ wifiService.object()
+ );
+ if (!wifiManager.isValid()) return QString();
+
+ // Fetch WifiInfo
+ QJniObject wifiInfo = wifiManager.callObjectMethod(
+ "getConnectionInfo",
+ "()Landroid/net/wifi/WifiInfo;"
+ );
+ if (!wifiInfo.isValid()) return QString();
+
+ // Get SSID string
+ QJniObject ssidJni = wifiInfo.callObjectMethod(
+ "getSSID",
+ "()Ljava/lang/String;"
+ );
+ if (!ssidJni.isValid()) return QString();
+
+ QString ssid = ssidJni.toString();
+
+ // Android returns SSIDs wrapped in double quotes (e.g., "\"MyWiFi\"")
+ if (ssid.startsWith('"') && ssid.endsWith('"') && ssid.length() > 1) {
+ ssid = ssid.mid(1, ssid.length() - 2);
+ }
+
+ return ssid;
+#endif
+ return "";
+}
+
+void WiFiHelper::requestPermissionsAndFetch() {
+#ifdef Q_OS_ANDROID
+ QLocationPermission locationPermission;
+ locationPermission.setAccuracy(QLocationPermission::Accuracy::Precise);
+ Qt::PermissionStatus status = qApp->checkPermission(locationPermission);
+
+ if (status == Qt::PermissionStatus::Granted) {
+ emit ssidRetrieved(getWifiSsid());
+ } else {
+ qDebug() << "Requesting permission.";
+ qApp->requestPermission(locationPermission, this, [this](const QPermission &permission) {
+ if (permission.status() == Qt::PermissionStatus::Granted) {
+ qDebug() << "Requested permission and got ssid";
+ emit ssidRetrieved(getWifiSsid());
+ } else {
+ qDebug() << "Failed to get WiFi SSID due to denied permission.";
+ emit ssidRetrieved("Permission Denied");
+ }
+ });
+ }
+#endif
+}
diff --git a/wifihelper.h b/wifihelper.h
new file mode 100644
index 0000000..ab10632
--- /dev/null
+++ b/wifihelper.h
@@ -0,0 +1,17 @@
+#ifndef WIFIHELPER_H
+#define WIFIHELPER_H
+
+#include
+
+class WiFiHelper : public QObject
+{
+ Q_OBJECT
+public:
+ explicit WiFiHelper(QObject *parent = nullptr);
+ Q_INVOKABLE QString getWifiSsid();
+ Q_INVOKABLE void requestPermissionsAndFetch();
+
+signals:
+ void ssidRetrieved(const QString &ssid);
+};
+#endif // WIFIHELPER_H