In qgroundcontrol Android I don’t see a way to share (import/export) files with other locations such as an email attachment.
Is this the expected behavior?
If so, does anyone have suggestions for implementing this on Android?
Background
Currently the Android app can upload and download files like .params and .plan to a connected aircraft.
Also the user can add waypoints and save them as a .plan file.
In Plan view / Plan Tools / File / Plan File / Open opens a window in the horizontal center of the screen.
Choosing directory Desktop navigates to 0/org/mavlink.qgroundcontrol/files and it contains a subdirectory QGCMapCache300.
Tapping the up arrow twice shows full path is /data/user/0/org/mavlink.qgroundcontrol/files.
However the path fills most of the line, making it difficult to select the end and append /my_plan.plan
Proposed file sharing
I read Ekke’s blog posts, and in a private branch implemented part of it (for more info see References and Appendix).
In AndroidManifest.xml register as a FileProvider.
In PlanMasterController.cc if the app is first launch it calls Ekke’s checkPermission() method to prompt the user for file permissions.
If the user taps “Allow”, and manually stops and restarts the app it changes the application data file path.
After that, the plan view file chooser appears on the right and the style is prettier, colors and font more consistent with the rest of qgroundcontrol.
At this point the user can use the Android Files app to access qgroundcontrol files in app subdirectories like Missions and Parameters.
I tried programmatically restarting the Android app- it quits but doesn’t automatically come back to the foreground.
References
qgroundcontrol git branch master, commit 4509f4b12c55266afe75c32df4d455dfdcaaf15e 2018-12-07
Appendix
applicationui.cpp similar to ekkesSHAREexample
#include "applicationui.hpp"
#include <QDebug>
#include <QtAndroid>
#include <qapplication.h>
#if defined(Q_OS_ANDROID)
/// The key under which the Android settings are saved
const char* ANDROID_SETTINGS_GROUP = "QGC_ANDROID";
QString IS_NOT_FIRST_LAUNCH_KEY = "IS_NOT_FIRST_LAUNCH";
#endif
ApplicationUI::ApplicationUI(QObject *parent) : QObject(parent)
{
}
#if defined(Q_OS_ANDROID)
// we don't need permissions if we only share files to other apps using FileProvider
// but we need permissions if other apps share their files with out app and we must access those files
bool ApplicationUI::checkPermission()
{
QtAndroid::PermissionResult r = QtAndroid::checkPermission(“android.permission.WRITE_EXTERNAL_STORAGE”);
if(r == QtAndroid::PermissionResult::Denied) {
QtAndroid::requestPermissionsSync( QStringList() << “android.permission.WRITE_EXTERNAL_STORAGE” );
r = QtAndroid::checkPermission(“android.permission.WRITE_EXTERNAL_STORAGE”);
if(r == QtAndroid::PermissionResult::Denied) {
qDebug() << “Permission denied”;
// emit noDocumentsWorkLocation();
return false;
}
}
qDebug() << “YEP: Permission OK”;
return true;
}
/// restarts app, e.g. so file write external storage permissions will take effect
void ApplicationUI::restartApp()
{
qDebug() << “ApplicationUI::restartApp()”;
saveSettingsIsNotFirstLaunch();
// https://richardstechnotes.com/2014/06/28/restarting-a-qt-app-programmatically/
// https://forum.qt.io/topic/9102/solved-which-method-of-the-main-widget-returns-the-qapplication-instance/4
// QStringList args = QApplication::arguments();
// args.removeFirst();
// QProcess::startDetached(QApplication::applicationFilePath(), args);
// QCoreApplication::quit();
// https://stackoverflow.com/questions/5129788/how-to-restart-my-own-qt-application
// qApp->quit();
// QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
// https://stackoverflow.com/questions/10068983/why-does-calling-quit-before-exec-not-quit-the-application
// QTimer::singleShot(0, qApp, &QCoreApplication::quit);
// QStringList args = QApplication::arguments();
// args.removeFirst();
// QProcess::startDetached(QApplication::applicationFilePath(), args);
// QTimer::singleShot(0, qApp, SLOT(quit()));
QStringList args = QApplication::arguments();
args.removeFirst();
QProcess::startDetached(QApplication::applicationFilePath(), args);
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
/// Use “negative” condition to avoid potential confusion about key-value pair never set
void ApplicationUI::saveSettingsIsNotFirstLaunch()
{
QSettings settings;
settings.beginGroup(ANDROID_SETTINGS_GROUP);
settings.setValue(IS_NOT_FIRST_LAUNCH_KEY, true);
settings.endGroup();
}
bool ApplicationUI::isNotFirstLaunch()
{
QSettings settings;
settings.beginGroup(ANDROID_SETTINGS_GROUP);
bool isNotFirst = settings.value(IS_NOT_FIRST_LAUNCH_KEY).toBool();
settings.endGroup();
return isNotFirst;
}
#endif