From fa9bf8a00fb064a8f4f9770894e1e019a581f72a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 29 Jul 2023 14:24:48 -0700 Subject: [PATCH] Allow app data to save to sd card on android --- android.pri | 9 ++++++ android/src/AndroidInterface.cc | 21 ++++++++++++++ android/src/AndroidInterface.h | 21 ++++++++++++++ .../org/mavlink/qgroundcontrol/QGCActivity.java | 32 ++++++++++++++++++++++ src/Settings/App.SettingsGroup.json | 10 ++++++- src/Settings/AppSettings.cc | 27 ++++++++++++++++-- src/Settings/AppSettings.h | 1 + src/api/QGCCorePlugin.cc | 7 +++++ src/ui/preferences/GeneralSettings.qml | 9 ++++-- 9 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 android/src/AndroidInterface.cc create mode 100644 android/src/AndroidInterface.h diff --git a/android.pri b/android.pri index 3962196..a0397a6 100644 --- a/android.pri +++ b/android.pri @@ -53,3 +53,12 @@ DISTFILES += \ $$PWD/android/build.gradle \ $$PWD/android/gradle/wrapper/gradle-wrapper.properties \ $$PWD/android/gradlew.bat + +SOURCES += \ + $$PWD/android/src/AndroidInterface.cc + +HEADERS += \ + $$PWD/android/src/AndroidInterface.h + +INCLUDEPATH += \ + $$PWD/android/src diff --git a/android/src/AndroidInterface.cc b/android/src/AndroidInterface.cc new file mode 100644 index 0000000..87b2441 --- /dev/null +++ b/android/src/AndroidInterface.cc @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include +#include +#include "QGCApplication.h" +#include "AndroidInterface.h" +#include + +QString AndroidInterface::getSDCardPath() +{ + QAndroidJniObject value = QAndroidJniObject::callStaticObjectMethod("org/mavlink/qgroundcontrol/QGCActivity", "getSDCardPath", + "()Ljava/lang/String;"); + return value.toString(); +} diff --git a/android/src/AndroidInterface.h b/android/src/AndroidInterface.h new file mode 100644 index 0000000..a03370a --- /dev/null +++ b/android/src/AndroidInterface.h @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * Copyright (C) 2018 Pinecone Inc. All rights reserved. + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +class AndroidInterface +{ +public: + static QString getSDCardPath(); +}; diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index 14fb760..c46192b 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -38,6 +38,7 @@ import java.util.concurrent.Executors; import java.util.Timer; import java.util.TimerTask; import java.io.IOException; +import java.lang.reflect.Method; import android.app.Activity; import android.app.PendingIntent; @@ -58,6 +59,8 @@ import android.app.PendingIntent; import android.view.WindowManager; import android.os.Bundle; import android.bluetooth.BluetoothDevice; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import com.hoho.android.usbserial.driver.*; import org.qtproject.qt5.android.bindings.QtActivity; @@ -762,5 +765,34 @@ public class QGCActivity extends QtActivity } }).start(); } + + public static String getSDCardPath() { + StorageManager storageManager = (StorageManager)_instance.getSystemService(Activity.STORAGE_SERVICE); + List volumes = storageManager.getStorageVolumes(); + Method mMethodGetPath; + String path = ""; + for (StorageVolume vol : volumes) { + try { + mMethodGetPath = vol.getClass().getMethod("getPath"); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + continue; + } + try { + path = (String) mMethodGetPath.invoke(vol); + } catch (Exception e) { + e.printStackTrace(); + continue; + } + + if (vol.isRemovable() == true) { + Log.i(TAG, "removable sd card mounted " + path); + return path; + } else { + Log.i(TAG, "storage mounted " + path); + } + } + return ""; + } } diff --git a/src/Settings/App.SettingsGroup.json b/src/Settings/App.SettingsGroup.json index 8682532..fe58bb3 100644 --- a/src/Settings/App.SettingsGroup.json +++ b/src/Settings/App.SettingsGroup.json @@ -174,11 +174,19 @@ { "name": "savePath", "shortDesc": "Application save directory", - "longDesc": "Directory to which all data files are saved/loaded from", + "longDesc": "Directory to which all data files are saved/loaded from", "type": "string", "default": "" }, { + "name": "androidSaveToSDCard", + "shortDesc": "Save to SD card", + "longDesc": "Application data is saved to the sd card", + "type": "bool", + "default": false, + "qgcRebootRequired": true +}, +{ "name": "userBrandImageIndoor", "shortDesc": "User-selected brand image", "longDesc": "Location in file system of user-selected brand image (indoor)", diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index 2324783..6da0652 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -12,6 +12,10 @@ #include "QGCApplication.h" #include "ParameterManager.h" +#ifdef __android__ +#include "AndroidInterface.h" +#endif + #include #include #include @@ -95,10 +99,26 @@ DECLARE_SETTINGGROUP(App, "") QDir rootDir = QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); savePathFact->setRawValue(rootDir.absolutePath()); #else - QDir rootDir = QDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)); - savePathFact->setRawValue(rootDir.filePath(appName)); + QString rootDirPath; + #ifdef __android__ + if (androidSaveToSDCard()->rawValue().toBool()) { + rootDirPath = AndroidInterface::getSDCardPath(); + qDebug() << "AndroidInterface::getSDCardPath();" << rootDirPath; + if (rootDirPath.isEmpty() || !QDir(rootDirPath).exists()) { + rootDirPath.clear(); + qgcApp()->showAppMessage(tr("Save to SD card specified for application data. But no SD card present. Using internal storage.")); + } else if (!QFileInfo(rootDirPath).isWritable()) { + rootDirPath.clear(); + qgcApp()->showAppMessage(tr("Save to SD card specified for application data. But SD card is write protected. Using internal storage.")); + } + } + #endif + if (rootDirPath.isEmpty()) { + rootDirPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + } + savePathFact->setRawValue(QDir(rootDirPath).filePath(appName)); #endif - savePathFact->setVisible(false); + savePathFact->setVisible(false); #else QDir rootDir = QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); savePathFact->setRawValue(rootDir.filePath(appName)); @@ -128,6 +148,7 @@ DECLARE_SETTINGSFACT(AppSettings, virtualJoystickAutoCenterThrottle) DECLARE_SETTINGSFACT(AppSettings, appFontPointSize) DECLARE_SETTINGSFACT(AppSettings, showLargeCompass) DECLARE_SETTINGSFACT(AppSettings, savePath) +DECLARE_SETTINGSFACT(AppSettings, androidSaveToSDCard) DECLARE_SETTINGSFACT(AppSettings, useChecklist) DECLARE_SETTINGSFACT(AppSettings, enforceChecklist) DECLARE_SETTINGSFACT(AppSettings, mapboxToken) diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index ee7e618..c2e1901 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -44,6 +44,7 @@ public: DEFINE_SETTINGFACT(indoorPalette) DEFINE_SETTINGFACT(showLargeCompass) DEFINE_SETTINGFACT(savePath) + DEFINE_SETTINGFACT(androidSaveToSDCard) DEFINE_SETTINGFACT(useChecklist) DEFINE_SETTINGFACT(enforceChecklist) DEFINE_SETTINGFACT(mapboxToken) diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index 45db47b..318c7a9 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -248,6 +248,13 @@ bool QGCCorePlugin::adjustSettingMetaData(const QString& settingsGroup, FactMeta return true; } #endif + +#ifndef __android__ + if (metaData.name() == AppSettings::androidSaveToSDCardName) { + // This only shows on android builds + return false; + } +#endif } return true; // Show setting in ui diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index f006759..1b9b9d6 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -686,6 +686,13 @@ Rectangle { } FactCheckBox { + text: qsTr("Save application data to SD Card") + fact: _androidSaveToSDCard + visible: _androidSaveToSDCard.visible + property Fact _androidSaveToSDCard: QGroundControl.settingsManager.appSettings.androidSaveToSDCard + } + + FactCheckBox { text: qsTr("Check for Internet connection") fact: _checkInternet visible: _checkInternet && _checkInternet.visible @@ -727,8 +734,6 @@ Rectangle { } } - //----------------------------------------------------------------- - //-- Save path RowLayout { id: pathRow anchors.margins: _margins