first commit
This commit is contained in:
commit
9bfa1e11fe
11 changed files with 1751 additions and 0 deletions
68
android/AndroidManifest.xml
Normal file
68
android/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.qtproject.example.appwaterLevelSensor"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation" />
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true" />
|
||||
<application
|
||||
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||
android:hardwareAccelerated="true"
|
||||
android:label="appwaterLevelSensor"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupOnly="false">
|
||||
|
||||
<service android:name=".WaterLevelService"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:exported="true">
|
||||
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="Monitoring water levels via local network UDP packets for real-time sensor alerts." />
|
||||
<meta-data android:name="android.app.lib_name" android:value="service"/>
|
||||
<meta-data android:name="android.app.background_running" android:value="true"/>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||
android:label="appwaterLevelSensor"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="unspecified"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="appwaterLevelSensor" />
|
||||
<meta-data
|
||||
android:name="android.app.arguments"
|
||||
android:value="" />
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.qtprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/qtprovider_paths"/>
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
83
android/build.gradle
Normal file
83
android/build.gradle
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
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(",")
|
||||
}
|
||||
}
|
||||
BIN
android/res/drawable-hdpi/ic_launcher.png
Normal file
BIN
android/res/drawable-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
BIN
android/res/drawable-mdpi/ic_launcher.png
Normal file
BIN
android/res/drawable-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4 KiB |
BIN
android/res/drawable-xhdpi/ic_launcher.png
Normal file
BIN
android/res/drawable-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
android/res/drawable-xxhdpi/ic_launcher.png
Normal file
BIN
android/res/drawable-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
android/res/drawable-xxxhdpi/ic_launcher.png
Normal file
BIN
android/res/drawable-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
|
@ -0,0 +1,17 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
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;
|
||||
|
||||
public class WaterLevelService extends Service {
|
||||
|
||||
private static float alert_thres_distance = 1000000;
|
||||
|
||||
public class UDPCommandClient implements Runnable {
|
||||
private DatagramSocket socket_cmd_recv;
|
||||
private DatagramSocket socket_cmd_send;
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d("UDP Service", "Command thread initialising");
|
||||
try {
|
||||
socket_cmd_recv = new DatagramSocket(8178, InetAddress.getByName("localhost"));
|
||||
socket_cmd_send = new DatagramSocket(8179);
|
||||
|
||||
Log.d("UDP Service", "Created command thread sockets");
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
byte[] buffer = new byte[1024];
|
||||
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
||||
socket_cmd_recv.receive(packet);
|
||||
String str = new String(buffer, StandardCharsets.UTF_8);
|
||||
if(str.startsWith("<SET NAME=ALERT VALUE=")) {
|
||||
str = str.replace("<SET NAME=ALERT VALUE=", "");
|
||||
str = str.replace("=", "");
|
||||
str = str.replace(">", "");
|
||||
alert_thres_distance = Float.parseFloat(str);
|
||||
}
|
||||
Log.d("UDP Service ", "Received command packet " + str);
|
||||
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.e("UDP Service", "Socket Error", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class UDPClient implements Runnable {
|
||||
private DatagramSocket socket_recv;
|
||||
private DatagramSocket socket_send;
|
||||
private Instant prevAlertTimestamp;
|
||||
private boolean started = false;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d("UDP Service", "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.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();
|
||||
socket_recv = new DatagramSocket(8176);
|
||||
socket_send = new DatagramSocket(8177);
|
||||
socket_recv.setBroadcast(true);
|
||||
Log.d("UDP Service", "Created Sockets");
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
byte[] buffer = new byte[4];
|
||||
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
||||
socket_recv.receive(packet);
|
||||
float f = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getFloat();
|
||||
Log.d("UDP Service ", "Received packet: " + f);
|
||||
Log.d("UDP Service ", "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() > 30) {
|
||||
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_HIGH);
|
||||
manager.notify(2, builder.build());
|
||||
Log.d("UDP Service", "Notification sent");
|
||||
}
|
||||
DatagramPacket send_packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 8180);
|
||||
socket_send.send(send_packet);
|
||||
Log.d("UDP Service ", "Sent packet");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.e("UDP Service", "Socket Error", e);
|
||||
} finally {
|
||||
if (multicastLock.isHeld())
|
||||
multicastLock.release();
|
||||
if (wakeLock.isHeld())
|
||||
wakeLock.release();
|
||||
if (socket_recv != null)
|
||||
socket_recv.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startWaterLevelService() {
|
||||
Log.d("UDP Service", "Starting the service from Activity");
|
||||
|
||||
Intent intent = new Intent(this, WaterLevelService.class);
|
||||
|
||||
startForegroundService(intent);
|
||||
}
|
||||
public WaterLevelService() {
|
||||
super();
|
||||
Log.i("UDP Service", "Class is created");
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.os.IBinder onBind(android.content.Intent intent) { return null; }
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Notification notification = createNotification();
|
||||
startForeground(1, notification);
|
||||
Log.i("UDP Service", "Creating Service");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.i("UDP Service", "Destroying Service");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Notification notification = createNotification();
|
||||
startForeground(1, notification);
|
||||
|
||||
Log.d("UDP Service", "Actually starting the thread");
|
||||
|
||||
int ret = super.onStartCommand(intent, flags, startId);
|
||||
|
||||
(new Thread(new UDPClient())).start();
|
||||
(new Thread(new UDPCommandClient())).start();
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
|
||||
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", "Starting the service");
|
||||
Intent intent = new Intent(activity, WaterLevelService.class);
|
||||
activity.startForegroundService(intent);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
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 org.qtproject.qt.android.bindings.QtActivity;
|
||||
|
||||
public class WaterLevelServiceStarter {
|
||||
|
||||
public static void startService(Context context) {
|
||||
Log.d("UDP Service", "Starting service via QtActivity instance");
|
||||
|
||||
Intent intent = new Intent(context, WaterLevelService.class);
|
||||
|
||||
context.startForegroundService(intent);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue