From 70351b316c185c8a61717be4a3bcca550ea751a4 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Tue, 28 May 2024 22:34:13 +0200 Subject: [PATCH] Tracking service: initial prototype with fused location --- CMakeLists.txt | 2 +- app/CMakeLists.txt | 2 - app/activeproject.cpp | 11 +- .../PositionTrackingService.java | 180 ++++------------ .../tracking/androidtrackingbackend.cpp | 194 +++++------------- .../tracking/androidtrackingbackend.h | 6 - .../tracking/androidtrackingbroadcast.cpp | 106 ---------- .../tracking/androidtrackingbroadcast.h | 97 --------- cmake_templates/AndroidManifest.xml.in | 3 +- 9 files changed, 99 insertions(+), 502 deletions(-) delete mode 100644 app/position/tracking/androidtrackingbroadcast.cpp delete mode 100644 app/position/tracking/androidtrackingbroadcast.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 67a15f1db..eecd7d35e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ if (DEFINED ENV{INPUT_SDK_ANDROID_BASE}) set(ANDROID_STL "c++_shared") # Target/Minimum API levels for Android, used as Input target properties - set(INPUT_ANDROID_TARGET_SDK_VERSION "33") + set(INPUT_ANDROID_TARGET_SDK_VERSION "34") set(INPUT_ANDROID_MIN_SDK_VERSION "${ANDROIDAPI}") set(INPUT_ANDROID_NDK_PATH "$ENV{ANDROID_NDK_ROOT}") if (NOT INPUT_ANDROID_NDK_PATH) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index b96c77c27..2a4d70a1d 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -255,14 +255,12 @@ if (ANDROID) set(MM_HDRS ${MM_HDRS} position/tracking/androidtrackingbackend.h - position/tracking/androidtrackingbroadcast.h position/providers/androidpositionprovider.h ) set(MM_SRCS ${MM_SRCS} position/tracking/androidtrackingbackend.cpp - position/tracking/androidtrackingbroadcast.cpp position/providers/androidpositionprovider.cpp ) endif () diff --git a/app/activeproject.cpp b/app/activeproject.cpp index acfb53093..ba83e077c 100644 --- a/app/activeproject.cpp +++ b/app/activeproject.cpp @@ -21,9 +21,6 @@ #include "activeproject.h" #include "coreutils.h" -#ifdef ANDROID -#include "position/tracking/androidtrackingbroadcast.h" -#endif const QString ActiveProject::LOADING_FLAG_FILE_PATH = QString( "%1/.input_loading_project" ).arg( QStandardPaths::standardLocations( QStandardPaths::TempLocation ).first() ); @@ -105,11 +102,7 @@ bool ActiveProject::forceLoad( const QString &filePath, bool force ) // clear autosync setAutosyncEnabled( false ); - // clear position tracking broadcast listeners -#ifdef ANDROID - disconnect( &AndroidTrackingBroadcast::getInstance() ); - AndroidTrackingBroadcast::unregisterBroadcast(); -#endif + // TODO: stop tracking here if it is running? // Just clear project if empty if ( filePath.isEmpty() ) @@ -222,6 +215,7 @@ bool ActiveProject::forceLoad( const QString &filePath, bool force ) } // in case tracking is running, we want to show the UI +/* #ifdef ANDROID if ( positionTrackingSupported() ) { @@ -244,6 +238,7 @@ bool ActiveProject::forceLoad( const QString &filePath, bool force ) AndroidTrackingBroadcast::sendAliveRequestAsync(); } #endif +*/ return res; } diff --git a/app/android/src/uk/co/lutraconsulting/PositionTrackingService.java b/app/android/src/uk/co/lutraconsulting/PositionTrackingService.java index 3d384e0a4..c6b22ae53 100644 --- a/app/android/src/uk/co/lutraconsulting/PositionTrackingService.java +++ b/app/android/src/uk/co/lutraconsulting/PositionTrackingService.java @@ -16,6 +16,8 @@ import android.content.Intent; import android.app.PendingIntent; import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.util.Log; import android.app.Notification; import android.app.NotificationChannel; @@ -24,18 +26,21 @@ import android.location.Location; import android.location.LocationManager; import android.location.LocationListener; +import android.location.GnssStatus; + +import androidx.annotation.NonNull; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Locale; -import uk.co.lutraconsulting.PositionTrackingBroadcastMiddleware; +import uk.co.lutraconsulting.MMAndroidPosition; import java.util.Timer; import java.util.TimerTask; -public class PositionTrackingService extends Service implements LocationListener { +public class PositionTrackingService extends Service { private static final String TAG = "PositionTrackingService"; @@ -43,37 +48,27 @@ public class PositionTrackingService extends Service implements LocationListener public static final String CHANNEL_ID = "PositionTrackingServiceChannel"; public static final int SERVICE_ID = 1010; + + private MMAndroidPosition mAndroidPos = null; - Location loc; - double latitude; - double longitude; - - private static long MIN_DISTANCE_CHANGE_FOR_UPDATES = 1; - private static long MIN_TIME_BW_UPDATES = 1000; //ms - - protected LocationManager locationManager; - private FileOutputStream positionUpdatesStream; - - public boolean amIRunning = false; - + private static native void servicePositionUpdated(Location location); @Override public void onCreate() { super.onCreate(); - // Create the file to store position updates - File file = new File( getFilesDir(), "tracking_updates.txt" ); + Log.i("CPP", "[java] [service] onCreate()"); - sendStatusUpdateMessage( "Tracking file path:" + file.getAbsolutePath() ); - - try { - // Open the FileOutputStream in append mode - positionUpdatesStream = new FileOutputStream(file, true); + MMAndroidPosition.Callback callback = new MMAndroidPosition.Callback() { + @Override + public void onPositionChanged(@NonNull Location location, GnssStatus gnssStatus) { + Log.i("CPP", "[java] [service] new pos " + location.getLatitude() + " " + location.getLongitude()); + // notify tracking backend (c++ code) + servicePositionUpdated(location); + } + }; - } catch ( IOException e ) { - e.printStackTrace(); - sendStatusUpdateMessage("ERROR #GENERAL: Could not open file stream: " + e.getMessage() ); - } + mAndroidPos = new MMAndroidPosition(this, callback, true); } @Override @@ -85,55 +80,32 @@ public IBinder onBind( Intent intent ) { @Override public void onDestroy() { - if (locationManager != null) { - locationManager.removeUpdates(this); - } + Log.i("CPP", "[java] [service] onDestroy()"); - // Close the FileOutputStream when the service is destroyed - try { - if (positionUpdatesStream != null) { - positionUpdatesStream.close(); - } - } catch (IOException e) { - e.printStackTrace(); - sendStatusUpdateMessage("ERROR #SILENT: Could not close file stream: " + e.getMessage() ); - } + mAndroidPos.stop(); super.onDestroy(); } + + @Override + public void onTaskRemoved(Intent rootIntent){ + Log.i("CPP", "[java] [service] onTaskRemoved()"); + + // this does not seem to be called - public void sendStatusUpdateMessage( String message ) { - Intent sendToBroadcastIntent = new Intent(); - - sendToBroadcastIntent.setAction( PositionTrackingBroadcastMiddleware.TRACKING_STATUS_MESSAGE_ACTION ); - sendToBroadcastIntent.putExtra( PositionTrackingBroadcastMiddleware.TRACKING_STATUS_MESSAGE_TAG, message ); - - sendBroadcast( sendToBroadcastIntent ); - } - - public void sendAliveStatusResponse( boolean isAlive ) { - Intent sendToBroadcastIntent = new Intent(); - - sendToBroadcastIntent.setAction( PositionTrackingBroadcastMiddleware.TRACKING_ALIVE_STATUS_ACTION ); - sendToBroadcastIntent.putExtra( PositionTrackingBroadcastMiddleware.TRACKING_ALIVE_STATUS_TAG, isAlive ); - - sendBroadcast( sendToBroadcastIntent ); + // TODO: maybe restart with AlarmManager? + // https://stackoverflow.com/questions/26842675/continue-service-even-if-application-is-cleared-from-recent-app + + super.onTaskRemoved(rootIntent); } @Override public int onStartCommand( Intent intent, int flags, int startId ) { - if ( intent.hasExtra( PositionTrackingBroadcastMiddleware.TRACKING_ALIVE_STATUS_ACTION ) ) { - // we are just checking if the service is running, without intention to start it - sendStatusUpdateMessage("Responding to alive request from service!!"); - sendAliveStatusResponse( amIRunning ); - stopSelf(); - - return START_NOT_STICKY; // do not bother recreating it - } + Log.i("CPP", "[java] [service] onStartCommand()"); if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.O ) { - sendStatusUpdateMessage( "ERROR #UNSUPPORTED: tracking is not supported on your Android version ( Android O (8.0) required )" ); + Log.e("CPP", "ERROR #UNSUPPORTED: tracking is not supported on your Android version ( Android O (8.0) required )" ); stopSelf(); return START_NOT_STICKY; // do not bother recreating it @@ -166,90 +138,12 @@ public int onStartCommand( Intent intent, int flags, int startId ) { Notification notification = notificationBuilder.build(); - startForeground( SERVICE_ID, notification ); - - sendStatusUpdateMessage( "Position tracking: Started the foreground service!" ); + // TODO: use ServiceCompat? (this variant of startForeground needs Android >= 10 (API level 29) + startForeground( SERVICE_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION ); - locationManager = ( LocationManager ) getApplication().getSystemService( LOCATION_SERVICE ); - - if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.O ) { - sendStatusUpdateMessage( "ERROR #UNSUPPORTED: tracking is not supported on your Android version ( Android O (8.0) required )" ); - stopSelf(); - - return START_NOT_STICKY; // do not bother recreating it - } - - String positionProvider; - - // FUSED_PROVIDER is available since API 31 (Android 12) - if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.S ) { - positionProvider = LocationManager.GPS_PROVIDER; - } - else { - positionProvider = LocationManager.FUSED_PROVIDER; - } - - boolean isGPSAvailable = locationManager.isProviderEnabled( positionProvider ); - if ( !isGPSAvailable ) { - sendStatusUpdateMessage( "ERROR #GPS_UNAVAILABLE: GPS is not available!" ); - stopSelf(); - stopForeground( true ); - } - else { - boolean fineLocationAccessGranted = checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED; - boolean coarseLocationAccessGranted = checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; - - if ( !fineLocationAccessGranted || !coarseLocationAccessGranted ) { - sendStatusUpdateMessage( "ERROR #PERMISSIONS: missing location permissions!" ); - stopSelf(); - stopForeground(true); - } - - long timeInterval = MIN_TIME_BW_UPDATES; - long distanceInterval = MIN_DISTANCE_CHANGE_FOR_UPDATES; - - if ( intent.hasExtra( "uk.co.lutraconsulting.tracking.timeInterval" ) ) { - timeInterval = (long) intent.getDoubleExtra( "uk.co.lutraconsulting.tracking.timeInterval", 1000 ); - } - - if ( intent.hasExtra( "uk.co.lutraconsulting.tracking.distanceInterval" ) ) { - distanceInterval = (long) intent.getDoubleExtra( "uk.co.lutraconsulting.tracking.distanceInterval", 1 ); - } - - locationManager.requestLocationUpdates( - positionProvider, - timeInterval, - distanceInterval, - this - ); - - sendStatusUpdateMessage( "Started to listen to position updates!" ); - } - - amIRunning = true; + mAndroidPos.start(); return START_STICKY; } - @Override - public void onLocationChanged( Location location ) { - - long currentTimeSeconds = System.currentTimeMillis() / 1000; // UTC time - String positionUpdate = String.format(Locale.US, "%f %f %f %d\n", location.getLongitude(), location.getLatitude(), location.getAltitude(), currentTimeSeconds); - - try { - if ( positionUpdatesStream != null ) { - positionUpdatesStream.write( positionUpdate.getBytes() ); - } - } catch ( IOException e ) { - e.printStackTrace(); - sendStatusUpdateMessage("ERROR #GENERAL: Could not write to file:" + e.getMessage() ); - } - - // notify cpp about position update - Intent sendToBroadcastIntent = new Intent(); - - sendToBroadcastIntent.setAction( PositionTrackingBroadcastMiddleware.TRACKING_POSITION_UPDATE_ACTION ); - sendBroadcast( sendToBroadcastIntent ); - } } diff --git a/app/position/tracking/androidtrackingbackend.cpp b/app/position/tracking/androidtrackingbackend.cpp index 41b025147..ff6e1c5ee 100644 --- a/app/position/tracking/androidtrackingbackend.cpp +++ b/app/position/tracking/androidtrackingbackend.cpp @@ -8,13 +8,40 @@ ***************************************************************************/ #include "androidtrackingbackend.h" -#include "androidtrackingbroadcast.h" #include "coreutils.h" #include "inpututils.h" #include "androidutils.h" +#include + #include +static AndroidTrackingBackend *sBackend = nullptr; + + +void servicePositionUpdated( JNIEnv *env, jclass clazz, jobject locationObj ) +{ + __android_log_print( ANDROID_LOG_INFO, "CPP", "[service] [c++] new position" ); + + QJniObject location( locationObj ); + if ( !location.isValid() ) + { + __android_log_print( ANDROID_LOG_ERROR, "CPP", "[service] [c++] invalid location obj" ); + return; + } + + const jdouble latitude = location.callMethod( "getLatitude" ); + const jdouble longitude = location.callMethod( "getLongitude" ); + const jlong timestamp = location.callMethod( "getTime" ); + + // TODO: add time as well? + QgsPoint pt( longitude, latitude ); + + QMetaObject::invokeMethod( sBackend, "positionChanged", + Qt::AutoConnection, Q_ARG( QgsPoint, pt ) ); +} + + AndroidTrackingBackend::AndroidTrackingBackend( AbstractTrackingBackend::UpdateFrequency frequency, QObject *parent ) @@ -25,6 +52,9 @@ AndroidTrackingBackend::AndroidTrackingBackend( parent ) { + Q_ASSERT( sBackend == nullptr ); + sBackend = this; + switch ( frequency ) { case AbstractTrackingBackend::Often: @@ -43,14 +73,28 @@ AndroidTrackingBackend::AndroidTrackingBackend( break; } + + // register the native methods + + JNINativeMethod methods[] + { + { + "servicePositionUpdated", + "(Landroid/location/Location;)V", + reinterpret_cast( servicePositionUpdated ) + } + }; + + QJniEnvironment javaenv; + javaenv.registerNativeMethods( "uk/co/lutraconsulting/PositionTrackingService", methods, 1 ); + setupForegroundUpdates(); } AndroidTrackingBackend::~AndroidTrackingBackend() { - disconnect( &AndroidTrackingBroadcast::getInstance() ); - - AndroidTrackingBroadcast::unregisterBroadcast(); + Q_ASSERT( sBackend == this ); + sBackend = nullptr; // stop the foreground service auto activity = QJniObject( QNativeInterface::QAndroidApplication::context() ); @@ -60,135 +104,17 @@ AndroidTrackingBackend::~AndroidTrackingBackend() "stopService", "(Landroid/content/Intent;)Z", serviceIntent.handle().object() ); - - if ( mTrackingFile.isOpen() ) - { - mTrackingFile.close(); - } - - if ( !mTrackingFile.remove() ) - { - qDebug() << "Tracking file could not be removed"; - } } QList AndroidTrackingBackend::getAllUpdates() { - QList allUpdates; - - if ( mTrackingFile.isOpen() ) - { - if ( !mTrackingFile.seek( 0 ) ) - { - qDebug() << "Unknown error when rewinding to the beginning of the tracking file"; - return allUpdates; - } - } - else - { - if ( !mTrackingFile.open( QFile::ReadOnly ) ) - { - CoreUtils::log( - QStringLiteral( "Android Tracking Backend" ), - QStringLiteral( "Tracking file could not be opened for reading: %1" ).arg( mTrackingFile.fileName() ) - ); - - return allUpdates; - } - } - - QString fileData = QString( mTrackingFile.readAll() ); - - return InputUtils::parsePositionUpdates( fileData ); -} - -void AndroidTrackingBackend::sourceUpdatedPosition() -{ - if ( !mTrackingFile.isOpen() ) - { - if ( !mTrackingFile.open( QFile::ReadOnly ) ) - { - CoreUtils::log( - QStringLiteral( "Android Tracking Backend" ), - QStringLiteral( "Tracking file could not be opened for reading: %1" ).arg( mTrackingFile.fileName() ) - ); - - emit errorOccured( tr( "There was an error and tracking could not start, please contact support" ) ); - emit abort(); - - return; - } - } - - QString fileData = QString( mTrackingFile.readAll() ); - QList parsedUpdates = InputUtils::parsePositionUpdates( fileData ); - - if ( parsedUpdates.size() > 1 ) - { - emit multiplePositionChanges( parsedUpdates ); - } - else if ( parsedUpdates.size() == 1 ) - { - emit positionChanged( parsedUpdates[0] ); - } -} - -void AndroidTrackingBackend::sourceUpdatedState( const QString &statusMessage ) -{ - if ( statusMessage.startsWith( QStringLiteral( "ERROR" ), Qt::CaseSensitive ) ) - { - CoreUtils::log( QStringLiteral( "Android Tracking Backend" ), statusMessage ); - - if ( statusMessage.contains( QStringLiteral( "#UNSUPPORTED" ), Qt::CaseSensitive ) ) - { - emit errorOccured( tr( "Your device does not support tracking, available from Android 8.0" ) ); - emit abort(); - } - else if ( statusMessage.contains( QStringLiteral( "#PERMISSIONS" ), Qt::CaseSensitive ) ) - { - emit errorOccured( tr( "Please enable location permission before starting tracking" ) ); - emit abort(); - } - else if ( statusMessage.contains( QStringLiteral( "#GPS_UNAVAILABLE" ), Qt::CaseSensitive ) ) - { - emit errorOccured( tr( "Please enable location services on your device before starting tracking" ) ); - emit abort(); - } - else if ( statusMessage.contains( QStringLiteral( "#GENERAL" ), Qt::CaseSensitive ) ) - { - emit errorOccured( tr( "There was an error and tracking could not start, please contact support" ) ); - emit abort(); - } - } - else - { - qDebug() << "Position Tracking:" << statusMessage; // just dev logs - } + // TODO + return QList(); } void AndroidTrackingBackend::setupForegroundUpdates() { - if ( !AndroidTrackingBroadcast::registerBroadcast() ) - { - emit errorOccured( tr( "There was an error and tracking could not start, please contact support" ) ); - emit abort(); - - return; - } - - connect( - &AndroidTrackingBroadcast::getInstance(), - &AndroidTrackingBroadcast::positionUpdated, - this, - &AndroidTrackingBackend::sourceUpdatedPosition - ); - - connect( - &AndroidTrackingBroadcast::getInstance(), - &AndroidTrackingBroadcast::statusChanged, - this, - &AndroidTrackingBackend::sourceUpdatedState - ); + __android_log_print( ANDROID_LOG_INFO, "CPP", "[c++] START SERVICE!" ); // We need to ask for a permission to show notifications, // but it is not mandatory to start the foreground service @@ -202,21 +128,13 @@ void AndroidTrackingBackend::setupForegroundUpdates() auto activity = QJniObject( QNativeInterface::QAndroidApplication::context() ); QAndroidIntent serviceIntent( activity.object(), "uk/co/lutraconsulting/PositionTrackingService" ); - serviceIntent.putExtra( QStringLiteral( "uk.co.lutraconsulting.tracking.distanceInterval" ), mDistanceFilter ); - serviceIntent.putExtra( QStringLiteral( "uk.co.lutraconsulting.tracking.timeInterval" ), mUpdateInterval ); + // TODO + //serviceIntent.putExtra( QStringLiteral( "uk.co.lutraconsulting.tracking.distanceInterval" ), mDistanceFilter ); + //serviceIntent.putExtra( QStringLiteral( "uk.co.lutraconsulting.tracking.timeInterval" ), mUpdateInterval ); + // startForegroundService() needs Android >= 8 (API level 26) QJniObject result = activity.callObjectMethod( - "startService", + "startForegroundService", "(Landroid/content/Intent;)Landroid/content/ComponentName;", serviceIntent.handle().object() ); - - // find the file for position updates - auto path = activity.callMethod( "homePath", "()Ljava/lang/String;" ); - QString pathString = path.toString(); - - mTrackingFile.setFileName( pathString + "/" + TRACKING_FILE_NAME ); - - // go see if something's left from previous run! - // (in future we could show a dialogue when project is opened and something is in the file) - sourceUpdatedPosition(); } diff --git a/app/position/tracking/androidtrackingbackend.h b/app/position/tracking/androidtrackingbackend.h index a12085b93..e461b26cf 100644 --- a/app/position/tracking/androidtrackingbackend.h +++ b/app/position/tracking/androidtrackingbackend.h @@ -29,17 +29,11 @@ class AndroidTrackingBackend : public AbstractTrackingBackend QList getAllUpdates() override; - void sourceUpdatedPosition(); - void sourceUpdatedState( const QString &statusMessage ); - private: void setupForegroundUpdates(); qreal mDistanceFilter = 0; qreal mUpdateInterval = 0; // ms - - QString TRACKING_FILE_NAME = "tracking_updates.txt"; - QFile mTrackingFile; // owned }; #endif // ANDROIDTRACKINGBACKEND_H diff --git a/app/position/tracking/androidtrackingbroadcast.cpp b/app/position/tracking/androidtrackingbroadcast.cpp deleted file mode 100644 index e67e0e702..000000000 --- a/app/position/tracking/androidtrackingbroadcast.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "position/tracking/androidtrackingbroadcast.h" -#include "coreutils.h" - -#include - -bool AndroidTrackingBroadcast::registerBroadcastPrivate() -{ - if ( !mBroadcastReceiver.isValid() ) - { - CoreUtils::log( QStringLiteral( "Android Tracking Broadcast" ), QStringLiteral( "Invalid broadcast receiver, aborting...!" ) ); - - return false; - } - - if ( mBroadcastIsRegistered ) - { - return true; // already registered - } - - JNINativeMethod methods[] - { - { - "notifyListenersPositionUpdated", - "()V", - reinterpret_cast( AndroidTrackingBroadcast::notifyListenersPositionUpdated ) - }, - { - "notifyListenersStatusUpdate", - "(Ljava/lang/String;)V", - reinterpret_cast( AndroidTrackingBroadcast::notifyListenersStatusUpdate ) - }, - { - "notifyListenersAliveResponse", - "(Z)V", - reinterpret_cast( AndroidTrackingBroadcast::notifyListenersAliveResponse ) - } - }; - - QJniEnvironment javaenv; - - jclass objectClass = javaenv->GetObjectClass( mBroadcastReceiver.object() ); - - javaenv->RegisterNatives( objectClass, methods, 3 ); - javaenv->DeleteLocalRef( objectClass ); - - mBroadcastReceiver.callMethod( - "registerBroadcastReceiver", - "(Landroid/content/Context;)V", - QNativeInterface::QAndroidApplication::context() - ); - - mBroadcastIsRegistered = true; - - return true; -} - -bool AndroidTrackingBroadcast::unregisterBroadcastPrivate() -{ - if ( !mBroadcastReceiver.isValid() ) - { - CoreUtils::log( QStringLiteral( "Android Tracking Broadcast" ), QStringLiteral( "Can not unregister invalid broadcast" ) ); - return false; - } - - if ( !mBroadcastIsRegistered ) - { - return true; // already unregistered - } - - mBroadcastReceiver.callMethod( "unregisterBroadcastReceiver", - "(Landroid/content/Context;)V", - QNativeInterface::QAndroidApplication::context() ); - - // unregister the natives - QJniEnvironment javaenv; - jclass objectClass = javaenv->GetObjectClass( mBroadcastReceiver.object() ); - - javaenv->UnregisterNatives( objectClass ); - javaenv->DeleteLocalRef( objectClass ); - - mBroadcastIsRegistered = false; - - return true; -} - -void AndroidTrackingBroadcast::sendAliveRequestAsyncPrivate() -{ - auto activity = QJniObject( QNativeInterface::QAndroidApplication::context() ); - QAndroidIntent serviceIntent( activity.object(), "uk/co/lutraconsulting/PositionTrackingService" ); - - serviceIntent.putExtra( QStringLiteral( "uk.co.lutraconsulting.tracking.alive" ), true ); - - QJniObject result = activity.callObjectMethod( - "startService", - "(Landroid/content/Intent;)Landroid/content/ComponentName;", - serviceIntent.handle().object() ); -} diff --git a/app/position/tracking/androidtrackingbroadcast.h b/app/position/tracking/androidtrackingbroadcast.h deleted file mode 100644 index e8e6572c1..000000000 --- a/app/position/tracking/androidtrackingbroadcast.h +++ /dev/null @@ -1,97 +0,0 @@ -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef ANDROIDTRACKINGBROADCAST_H -#define ANDROIDTRACKINGBROADCAST_H - -#include -#include - -/** - * \brief The AndroidTrackingBroadcast class is used as singleton to communicate - * with Java Broadcast receiver via JNI and send messages from Java to Qt. - */ -class AndroidTrackingBroadcast : public QObject -{ - Q_OBJECT - - public: - - virtual ~AndroidTrackingBroadcast() = default; - - static AndroidTrackingBroadcast &getInstance() - { - static AndroidTrackingBroadcast instance; - return instance; - } - - static void notifyListenersPositionUpdated( JNIEnv *env, jobject /*this*/ ) - { - AndroidTrackingBroadcast &trackingCallback = AndroidTrackingBroadcast::getInstance(); - - emit trackingCallback.positionUpdated(); - } - - static void notifyListenersStatusUpdate( JNIEnv *env, jobject /*this*/, jstring message ) - { - AndroidTrackingBroadcast &trackingCallback = AndroidTrackingBroadcast::getInstance(); - - emit trackingCallback.statusChanged( env->GetStringUTFChars( message, 0 ) ); - } - - static void notifyListenersAliveResponse( JNIEnv *env, jobject /*this*/, jboolean isAlive ) - { - AndroidTrackingBroadcast &trackingCallback = AndroidTrackingBroadcast::getInstance(); - - emit trackingCallback.aliveResponse( isAlive ); - } - - static bool registerBroadcast() - { - return AndroidTrackingBroadcast::getInstance().registerBroadcastPrivate(); - } - - static bool unregisterBroadcast() - { - return AndroidTrackingBroadcast::getInstance().unregisterBroadcastPrivate(); - } - - static void sendAliveRequestAsync() - { - AndroidTrackingBroadcast::getInstance().sendAliveRequestAsyncPrivate(); - } - - signals: - - // Emitted when Java reports a new position - void positionUpdated(); - - // Emitted when the Java position service wants to report an issue or other status - void statusChanged( const QString &message ); - - // Emitted to notify if the tracking service is running - void aliveResponse( bool isAlive ); - - private: - - explicit AndroidTrackingBroadcast( QObject *parent = nullptr ) - { - mBroadcastReceiver = QJniObject( "uk/co/lutraconsulting/PositionTrackingBroadcastMiddleware" ); - } - - bool registerBroadcastPrivate(); - bool unregisterBroadcastPrivate(); - void sendAliveRequestAsyncPrivate(); - - bool mBroadcastIsRegistered = false; - - QJniObject mBroadcastReceiver; -}; - -#endif // ANDROIDTRACKINGBROADCAST_H diff --git a/cmake_templates/AndroidManifest.xml.in b/cmake_templates/AndroidManifest.xml.in index 0a5c6ded4..c0c10b954 100644 --- a/cmake_templates/AndroidManifest.xml.in +++ b/cmake_templates/AndroidManifest.xml.in @@ -17,6 +17,7 @@ +