Update permissions and start porting activity to qt6

qt6-qmake-android
Simon Stürz 2025-10-16 12:39:46 +02:00
parent 00b3a6fcbd
commit ebdb7252ff
9 changed files with 231 additions and 155 deletions

View File

@ -11,7 +11,7 @@ LIBS += -L$${top_builddir}/libnymea-app/ -lnymea-app
android: {
LIBS += -L$${top_builddir}/libnymea-app/$${ANDROID_TARGET_ARCH}
PRE_TARGETDEPS += $$top_builddir/libnymea-app/$${ANDROID_TARGET_ARCH}/libnymea-app.a
PRE_TARGETDEPS += $$top_builddir/libnymea-app/$${ANDROID_TARGET_ARCH}/libnymea-app_$${ANDROID_TARGET_ARCH}.a
}
INCLUDEPATH += $${top_srcdir}/libnymea-app/

View File

@ -12,11 +12,11 @@ qtHaveModule(webview) {
DEFINES += HAVE_WEBVIEW
}
INCLUDEPATH += $$top_srcdir/libnymea-app \
$$top_srcdir/experiences/airconditioning
LIBS += -L$$top_builddir/libnymea-app/ -lnymea-app \
-L$$top_builddir/experiences/airconditioning -lnymea-app-airconditioning
linux:!android: LIBS += -L$$top_builddir/libnymea-app/ -lnymea-app -L$$top_builddir/experiences/airconditioning -lnymea-app-airconditioning
win32:Debug:LIBS += -L$$top_builddir/libnymea-app/debug \
-L$$top_builddir/experiences/airconditioning/debug
@ -98,12 +98,11 @@ android {
SOURCES += platformintegration/android/platformhelperandroid.cpp \
platformintegration/android/platformpermissionsandroid.cpp \
# https://bugreports.qt.io/browse/QTBUG-83165
CORE_LIBS += -L$${top_builddir}/libnymea-app/$${ANDROID_TARGET_ARCH}
AIRCONDITIONING_LIBS += -L$${top_builddir}/experiences/airconditioning/$${ANDROID_TARGET_ARCH}
LIBS += $${CORE_LIBS} $${AIRCONDITIONING_LIBS}
message("CORE_LIBS: $${CORE_LIBS}")
LIBS += $${CORE_LIBS} -lnymea-app_$${ANDROID_TARGET_ARCH} \
$${AIRCONDITIONING_LIBS} -lnymea-app-airconditioning_$${ANDROID_TARGET_ARCH}
versioninfo.files = ../version.txt
versioninfo.path = /

View File

@ -38,7 +38,6 @@
#include <QJsonDocument>
#if defined Q_OS_ANDROID
#include <QtAndroidExtras/QtAndroid>
#include "platformintegration/android/platformhelperandroid.h"
#elif defined Q_OS_IOS
#include "platformintegration/ios/platformhelperios.h"

View File

@ -21,7 +21,7 @@ import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import android.view.WindowInsets;
public class NymeaAppActivity extends org.qtproject.qt5.android.bindings.QtActivity
public class NymeaAppActivity extends QtActivity
{
private static final String TAG = "nymea-app: NymeaAppActivity";
private static Context context = null;

View File

@ -1,40 +1,156 @@
// #include "platformpermissionsandroid.h"
#include "platformpermissionsandroid.h"
// #include <QDebug>
// #include <QApplication>
// #include <QOperatingSystemVersion>
#include <QDebug>
#include <QApplication>
#include <QPermission>
#include <QOperatingSystemVersion>
// #include "logging.h"
// NYMEA_LOGGING_CATEGORY(dcPlatformPermissions, "PlatformPermissions")
#include "logging.h"
NYMEA_LOGGING_CATEGORY(dcPlatformPermissions, "PlatformPermissions")
// PlatformPermissionsAndroid * PlatformPermissionsAndroid::s_instance = nullptr;
PlatformPermissionsAndroid * PlatformPermissionsAndroid::s_instance = nullptr;
// #define FLAG_ACTIVITY_NEW_TASK 0x10000000
#define FLAG_ACTIVITY_NEW_TASK 0x10000000
// PlatformPermissionsAndroid::PlatformPermissionsAndroid(QObject *parent)
// : PlatformPermissions{parent}
// {
// s_instance = this;
// // If the user switches to the settings app and changes permission settings there, we won't get notified
// // in any way, so let's just refresh when we become active
// connect(qApp, &QApplication::applicationStateChanged, this, [this](Qt::ApplicationState state){
// if (state == Qt::ApplicationActive) {
// emit bluetoothPermissionChanged();
// emit locationPermissionChanged();
// emit backgroundLocationPermissionChanged();
// emit notificationsPermissionChanged();
// }
// });
PlatformPermissionsAndroid::PlatformPermissionsAndroid(QObject *parent)
: PlatformPermissions{parent}
{
s_instance = this;
// If the user switches to the settings app and changes permission settings there, we won't get notified
// in any way, so let's just refresh when we become active
connect(qApp, &QApplication::applicationStateChanged, this, [this](Qt::ApplicationState state){
if (state == Qt::ApplicationActive) {
emit bluetoothPermissionChanged();
emit locationPermissionChanged();
emit backgroundLocationPermissionChanged();
emit notificationsPermissionChanged();
}
});
// }
}
// void PlatformPermissionsAndroid::requestPermission(PlatformPermissions::Permission permission)
// {
// if (permissionMap().contains(permission)) {
// qCDebug(dcPlatformPermissions()) << "Requesting permissions:" << permissionMap().value(permission);
// QtAndroid::requestPermissions({permissionMap().value(permission)}, &permissionResultCallback);
// }
// }
PlatformPermissions::PermissionStatus PlatformPermissionsAndroid::checkPermission(Permission platformPermission) const
{
PermissionStatus status = PermissionStatusGranted;
qCDebug(dcPlatformPermissions()) << "Checking permission" << platformPermission;
switch (platformPermission) {
case PlatformPermissions::PermissionBluetooth: {
QBluetoothPermission permission;
// Status prüfen
auto status = qApp->checkPermission(permission);
switch (status) {
case Qt::PermissionStatus::Granted:
qCDebug(dcPlatformPermissions()) << "Bluetooth permission already granted.";
break;
case Qt::PermissionStatus::Denied:
qCDebug(dcPlatformPermissions()) << "Bluetooth permission denied.";
break;
case Qt::PermissionStatus::Undetermined:
qCDebug(dcPlatformPermissions()) << "Bluetooth permission not yet requested. Requesting...";
qApp->requestPermission(permission, [](const QPermission &perm){
if (perm.status() == Qt::PermissionStatus::Granted)
qCDebug(dcPlatformPermissions()) << "Bluetooth permission granted after request.";
else
qCDebug(dcPlatformPermissions()) << "Bluetooth permission denied after request.";
});
break;
}
break;
}
case PlatformPermissions::PermissionLocalNetwork: {
QLocationPermission permission;
permission.setAccuracy(QLocationPermission::Precise);
// Status prüfen
auto status = qApp->checkPermission(permission);
switch (status) {
case Qt::PermissionStatus::Granted:
qCDebug(dcPlatformPermissions()) << "Location permission already granted.";
break;
case Qt::PermissionStatus::Denied:
qCDebug(dcPlatformPermissions()) << "Location permission denied.";
break;
case Qt::PermissionStatus::Undetermined:
qCDebug(dcPlatformPermissions()) << "Location permission not yet requested. Requesting...";
qApp->requestPermission(permission, [](const QPermission &perm){
if (perm.status() == Qt::PermissionStatus::Granted)
qCDebug(dcPlatformPermissions()) << "Location permission granted after request.";
else
qCDebug(dcPlatformPermissions()) << "Location permission denied after request.";
});
break;
}
}
default:
qCWarning(dcPlatformPermissions()) << "Requested status of platform permission" << platformPermission << "but is not implemented yet.";
break;
}
return status;
}
void PlatformPermissionsAndroid::requestPermission(PlatformPermissions::Permission platformPermission)
{
switch (platformPermission) {
case PlatformPermissions::PermissionBluetooth:
qCDebug(dcPlatformPermissions()) << "Requesting bluetooth permission";
qApp->requestPermission(QLocationPermission{}, [platformPermission](const QPermission &permission) {
if (permission.status() == Qt::PermissionStatus::Denied) {
qCWarning(dcPlatformPermissions()) << "Bluetooth permission denied.";
s_instance->m_requestedButDeniedPermissions.append(platformPermission);
}
if (permission.status() == Qt::PermissionStatus::Granted)
qCDebug(dcPlatformPermissions()) << "Bluetooth permission granted.";
emit s_instance->bluetoothPermissionChanged();
});
break;
case PlatformPermissions::PermissionLocation: {
QLocationPermission locationPermission;
locationPermission.setAccuracy(QLocationPermission::Precise);
qApp->requestPermission(locationPermission, [platformPermission](const QPermission &permission) {
if (permission.status() == Qt::PermissionStatus::Denied) {
qCWarning(dcPlatformPermissions()) << "Location permission denied.";
s_instance->m_requestedButDeniedPermissions.append(platformPermission);
}
if (permission.status() == Qt::PermissionStatus::Granted)
qCDebug(dcPlatformPermissions()) << "Location permission granted.";
emit s_instance->locationPermissionChanged();
});
break;
}
default:
qCWarning(dcPlatformPermissions()) << "Requested platform permission" << platformPermission << "but is not implemented yet.";
break;
}
emit s_instance->locationPermissionChanged();
emit s_instance->backgroundLocationPermissionChanged();
emit s_instance->notificationsPermissionChanged();
// if (permissionMap().contains(permission)) {
// qCDebug(dcPlatformPermissions()) << "Requesting permissions:" << permissionMap().value(permission);
// qApp->requestPermission(QCameraPermission{}, [](const QPermission &permission) {
// if (permission.status() == Qt::PermissionStatus::Granted)
// takePhoto();
// });
// // QtAndroid::requestPermissions({permissionMap().value(permission)}, &permissionResultCallback);
// }
}
// void PlatformPermissionsAndroid::openPermissionSettings()
// {

View File

@ -1,31 +1,24 @@
// #ifndef PLATFORMPERMISSIONSANDROID_H
// #define PLATFORMPERMISSIONSANDROID_H
#ifndef PLATFORMPERMISSIONSANDROID_H
#define PLATFORMPERMISSIONSANDROID_H
// #include "../platformpermissions.h"
// #include <QtCore/private/qandroidextras_p.h>
// //#include <QtAndroidExtras/QtAndroid>
#include "../platformpermissions.h"
#include <QtCore/private/qandroidextras_p.h>
// class PlatformPermissionsAndroid : public PlatformPermissions
// {
// Q_OBJECT
// public:
// explicit PlatformPermissionsAndroid(QObject *parent = nullptr);
class PlatformPermissionsAndroid : public PlatformPermissions
{
Q_OBJECT
public:
explicit PlatformPermissionsAndroid(QObject *parent = nullptr);
// PermissionStatus checkPermission(Permission permission) const override;
PermissionStatus checkPermission(Permission platformPermission) const override;
void requestPermission(Permission platformPermission) override;
// void requestPermission(Permission permission) override;
// void openPermissionSettings() override;
private:
static PlatformPermissionsAndroid *s_instance;
// signals:
QList<PlatformPermissions::Permission> m_requestedButDeniedPermissions;
QList<PlatformPermissions::Permission> m_grantedPermission;
// private:
// QHash<PlatformPermissions::Permission, QStringList> permissionMap() const;
};
// QStringList m_requestedButDeniedPermissions;
// static PlatformPermissionsAndroid *s_instance;
// // static void permissionResultCallback(const QtAndroid::PermissionResultMap &results);
// };
// #endif // PLATFORMPERMISSIONSANDROID_H
#endif // PLATFORMPERMISSIONSANDROID_H

View File

@ -32,11 +32,16 @@
#include "platformhelper.h"
#include <QDebug>
#include <QCoreApplication>
#if defined Q_OS_ANDROID
#include <QtAndroid>
#include <QtAndroidExtras>
#include <QJniObject>
#include <QJniEnvironment>
#include <QtCore/qjnienvironment.h> // QJniEnvironment
#include <QtCore/qjniobject.h> // QJniObject
#include <QtCore/qjnitypes.h> // QtJniTypes::Context / Activity
#include <QtCore/qnativeinterface.h>
static PushNotifications *m_client_pointer;
#endif
@ -90,12 +95,48 @@ void PushNotifications::registerForPush()
qDebug() << "Checking for play services";
jboolean playServicesAvailable = QJniObject::callStaticMethod<jboolean>("io.guh.nymeaapp.NymeaAppNotificationService", "checkPlayServices", "()Z");
if (playServicesAvailable) {
qDebug() << "Setting up firebase";
m_client_pointer = this;
m_firebaseApp = ::firebase::App::Create(::firebase::AppOptions(), QAndroidJniEnvironment(), QtAndroid::androidActivity().object());
m_firebase_initializer.Initialize(m_firebaseApp, nullptr, [](::firebase::App * fapp, void *) {
return ::firebase::messaging::Initialize( *fapp, (::firebase::messaging::Listener *)m_client_pointer);
});
JNIEnv *jni = QJniEnvironment().jniEnv();
QtJniTypes::Context ctx = QNativeInterface::QAndroidApplication::context();
jobject contextObj = ctx.object<jobject>();
m_firebaseApp = firebase::App::Create(firebase::AppOptions(), jni, contextObj);
firebase::messaging::Initialize(*m_firebaseApp, this);
firebase::messaging::SetListener(this);
// (Optional, Android 13+): Benachrichtigungs-Erlaubnis anfragen
// firebase::messaging::RequestPermission();
// // Activity + JNIEnv besorgen
// JNIEnv* env = QNativeInterface::QAndroidApplication::jniEnv();
// jobject activity = QNativeInterface::QAndroidApplication::context();
// // Firebase App erstellen
// m_firebaseApp = firebase::App::Create(firebase::AppOptions(), env, activity);
// // Messaging initialisieren und Listener setzen
// auto initResult = firebase::messaging::Initialize(*m_firebaseApp);
// if (initResult != firebase::kFutureStatusComplete) {
// // optional: warten oder loggen
// }
// firebase::messaging::SetListener(this);
// // Optional: Token anfordern (wird i.d.R. via OnTokenReceived geliefert)
// firebase::messaging::RequestPermission(); // Android 13+ für Notifications sinnvoll
// m_firebaseApp = ::firebase::App::Create(::firebase::AppOptions(), QAndroidJniEnvironment(), QtAndroid::androidActivity().object());
// m_firebase_initializer.Initialize(m_firebaseApp, nullptr, [](::firebase::App * fapp, void *) {
// return ::firebase::messaging::Initialize( *fapp, (::firebase::messaging::Listener *)m_client_pointer);
// });
} else {
qDebug() << "Google Play Services not available. Cannot connect to push client.";
}

View File

@ -1,10 +1,23 @@
<?xml version="1.0"?>
<manifest package="io.guh.nymeaapp" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<!-- Declare Bluetooth feature (LE optional: set required="true" if you *require* BLE) -->
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />
<!-- Android 12+ runtime permissions -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- PreAndroid 12 (API ≤ 30) legacy permissions -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<!-- Location is needed for BLE scanning prior to Android 12 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="nymea:app" android:icon="@mipmap/icon" android:roundIcon="@mipmap/round_icon" android:extractNativeLibs="true">
@ -24,27 +37,6 @@
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splash"/>
<meta-data android:name="android.app.splash_screen_sticky" android:value="true"/>
@ -72,71 +64,8 @@
<!-- extract android style -->
</activity>
<activity android:process=":qt_controlsActivity" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="io.guh.nymeaapp.NymeaAppControlsActivity" android:label="nymea:app" android:screenOrientation="unspecified" android:launchMode="standard" android:exported="true">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="nymea"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="service"/>
<meta-data android:name="android.app.arguments" android:value="--controlActivity"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<meta-data android:name="android.app.extract_android_style" android:value="minimal"/>
<!-- Splash screen -->
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splash"/>
<meta-data android:name="android.app.splash_screen_sticky" android:value="true"/>
<!-- Splash screen -->
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<service android:process=":qt_service" android:name="io.guh.nymeaapp.NymeaAppService" android:exported="true">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="xbmc"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="service"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Run with local libs -->
<!-- Background running -->
<meta-data android:name="android.app.background_running" android:value="true"/>
<!-- Background running -->
</service>
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notificationicon"/>
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color"/>

View File

@ -87,7 +87,6 @@ android {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [
qt5AndroidDir + '/src',
nymeaAppRoot + '/androidservice/java',
nymeaAppRoot + '/nymea-app/platformintegration/android/java',
nymeaAppRoot + '/QtZeroConf/android',
'src',