Qgroundcontrol Android file sharing

#1

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
#2

Plan files by default should be stored in the QGroundControl/Mission directory on the device. You should be able to acces this from My Files and move things in and out of there as you like. You don’t need a different build.

Are you using the latest Stable 3.5 version of QGC. It sounds like you are getting a regular filer picker somehow. But I’m not sure. A screenshot would be helpful. On mobile builds you should get a simplified file chooser as a right dialog.

#3

Thanks. I’ve been working on doing something similar to this and hadn’t seen you already did much of it!
After your reply I downloaded release 3.5.0 from Google play store.
Also I found your commit 2019-01-20 “Fix write storage permission problem”

In Android if the user allows file permissions it seems they don’t take effect until the app is stopped (not just paused) and restarted.
On first launch, the file dialog appears in the center. The user can save files:

After stopping and restarting the app, the root file path changes.
I think any files saved during the app’s first launch are unreachable.
The file dialog appears on the right:

On second launch files are available for sharing in the Files app.

That’s why I was trying to programmatically stop and restart the app immediately after the user allows file permission.

#4

Are you sure the version you started which gave you the file picker was the new 3.5? An not an older version which was already running in the background on the device?