From ba0b7c62a48076c30f12e5daa11ceb92bbe67ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Sat, 28 May 2022 14:05:14 +0200 Subject: [PATCH] Android port --- .appveyor.yml | 2 +- gpxsee.pro | 47 ++++- pkg/android/AndroidManifest.xml | 18 ++ pkg/android/build.gradle | 78 ++++++++ pkg/android/gradle.properties | 14 ++ pkg/android/res/drawable-hdpi/icon.png | Bin 0 -> 2864 bytes pkg/android/res/drawable-ldpi/icon.png | Bin 0 -> 1134 bytes pkg/android/res/drawable-mdpi/icon.png | Bin 0 -> 1977 bytes pkg/android/res/drawable-xhdpi/icon.png | Bin 0 -> 4538 bytes pkg/android/res/drawable-xxhdpi/icon.png | Bin 0 -> 8648 bytes pkg/android/res/drawable-xxxhdpi/icon.png | Bin 0 -> 12359 bytes pkg/android/res/values/libs.xml | 20 ++ pkg/gpxsee64.nsi | 2 +- src/GUI/app.cpp | 12 ++ src/GUI/app.h | 7 +- src/GUI/cadencegraph.cpp | 9 +- src/GUI/colorbox.cpp | 22 +- src/GUI/dirselectwidget.cpp | 4 +- src/GUI/elevationgraph.cpp | 11 + src/GUI/filebrowser.cpp | 19 +- src/GUI/filebrowser.h | 9 +- src/GUI/fileselectwidget.cpp | 4 +- src/GUI/gearratiograph.cpp | 9 + src/GUI/gui.cpp | 234 ++++++++++++++++++++-- src/GUI/gui.h | 32 ++- src/GUI/heartrategraph.cpp | 7 + src/GUI/mapview.cpp | 55 ++--- src/GUI/mapview.h | 6 + src/GUI/navigationwidget.cpp | 101 ++++++++++ src/GUI/navigationwidget.h | 35 ++++ src/GUI/optionsdialog.cpp | 27 ++- src/GUI/pdfexportdialog.cpp | 10 + src/GUI/pngexportdialog.cpp | 10 + src/GUI/powergraph.cpp | 9 +- src/GUI/settings.h | 2 + src/GUI/speedgraph.cpp | 7 + src/GUI/temperaturegraph.cpp | 9 + src/GUI/thumbnail.cpp | 10 + src/common/programpaths.cpp | 97 +++++---- src/common/util.cpp | 86 +++++++- src/common/util.h | 1 + 41 files changed, 913 insertions(+), 112 deletions(-) create mode 100644 pkg/android/AndroidManifest.xml create mode 100644 pkg/android/build.gradle create mode 100644 pkg/android/gradle.properties create mode 100644 pkg/android/res/drawable-hdpi/icon.png create mode 100644 pkg/android/res/drawable-ldpi/icon.png create mode 100644 pkg/android/res/drawable-mdpi/icon.png create mode 100644 pkg/android/res/drawable-xhdpi/icon.png create mode 100644 pkg/android/res/drawable-xxhdpi/icon.png create mode 100644 pkg/android/res/drawable-xxxhdpi/icon.png create mode 100644 pkg/android/res/values/libs.xml create mode 100644 src/GUI/navigationwidget.cpp create mode 100644 src/GUI/navigationwidget.h diff --git a/.appveyor.yml b/.appveyor.yml index ece7fffc..12112de1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,4 +1,4 @@ -version: 10.8.{build} +version: 11.0.{build} configuration: - Release diff --git a/gpxsee.pro b/gpxsee.pro index 56e2f083..28ea5f1a 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -1,9 +1,9 @@ -unix:!macx { +unix:!macx:!android { TARGET = gpxsee } else { TARGET = GPXSee } -VERSION = 10.8 +VERSION = 11.0 QT += core \ gui \ @@ -13,7 +13,8 @@ QT += core \ concurrent \ widgets \ printsupport \ - positioning + positioning \ + svg greaterThan(QT_MAJOR_VERSION, 5) { QT += openglwidgets \ core5compat @@ -24,6 +25,7 @@ INCLUDEPATH += ./src HEADERS += src/common/config.h \ src/GUI/crosshairitem.h \ src/GUI/motioninfoitem.h \ + src/GUI/navigationwidget.h \ src/GUI/pluginparameters.h \ src/common/garmin.h \ src/common/coordinates.h \ @@ -246,6 +248,7 @@ HEADERS += src/common/config.h \ SOURCES += src/main.cpp \ src/GUI/crosshairitem.cpp \ src/GUI/motioninfoitem.cpp \ + src/GUI/navigationwidget.cpp \ src/GUI/pluginparameters.cpp \ src/common/coordinates.cpp \ src/common/rectc.cpp \ @@ -504,7 +507,7 @@ win32 { NOGDI } -unix:!macx { +unix:!macx:!android { isEmpty(PREFIX):PREFIX = /usr/local maps.files = $$files(pkg/maps/*) @@ -524,3 +527,39 @@ unix:!macx { target.path = $$PREFIX/bin INSTALLS += target maps csv symbols locale icon desktop mime } + +android { + defineReplace(versionCode) { + segments = $$split(1, ".") + for (segment, segments): \ + vCode = "$$first(vCode)$$format_number($$segment, width=3 zeropad)" + contains(ANDROID_TARGET_ARCH, armeabi-v7a): \ + suffix = 0 + contains(ANDROID_TARGET_ARCH, arm64-v8a): \ + suffix = 1 + contains(ANDROID_TARGET_ARCH, x86): \ + suffix = 2 + contains(ANDROID_TARGET_ARCH, x86_64): \ + suffix = 3 + + return($$first(vCode)$$first(suffix)) + } + + include($$OPENSSL_PATH/openssl.pri) + + ANDROID_VERSION_NAME = $$VERSION + ANDROID_VERSION_CODE = $$versionCode($$ANDROID_VERSION_NAME) + ANDROID_PACKAGE_SOURCE_DIR = $$PWD/pkg/android + DISTFILES += \ + pkg/android/AndroidManifest.xml \ + pkg/android/build.gradle \ + pkg/android/res/values/libs.xml + + maps.files = $$files(pkg/maps/*) + maps.path = /assets/maps + csv.files = $$files(pkg/csv/*) + csv.path = /assets/csv + symbols.files = $$files(icons/symbols/*.png) + symbols.path = /assets/symbols + INSTALLS += maps csv symbols +} diff --git a/pkg/android/AndroidManifest.xml b/pkg/android/AndroidManifest.xml new file mode 100644 index 00000000..39fae897 --- /dev/null +++ b/pkg/android/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/pkg/android/build.gradle b/pkg/android/build.gradle new file mode 100644 index 00000000..0a8601c4 --- /dev/null +++ b/pkg/android/build.gradle @@ -0,0 +1,78 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.0.2' + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qtAndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion.toInteger() + buildToolsVersion androidBuildToolsVersion + ndkVersion androidNdkVersion + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qtAndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qtAndroidDir + '/res', 'res'] + resources.srcDirs = ['resources'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + tasks.withType(JavaCompile) { + options.incremental = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + abortOnError false + } + + // Do not compress Qt binary resources file + aaptOptions { + noCompress 'rcc' + } + + defaultConfig { + resConfig "en" + minSdkVersion qtMinSdkVersion + targetSdkVersion qtTargetSdkVersion + ndk.abiFilters = qtTargetAbiList.split(",") + } +} diff --git a/pkg/android/gradle.properties b/pkg/android/gradle.properties new file mode 100644 index 00000000..263d7023 --- /dev/null +++ b/pkg/android/gradle.properties @@ -0,0 +1,14 @@ +# Project-wide Gradle settings. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2500m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# Enable building projects in parallel +org.gradle.parallel=true + +# Gradle caching allows reusing the build artifacts from a previous +# build with the same inputs. However, over time, the cache size will +# grow. Uncomment the following line to enable it. +#org.gradle.caching=true diff --git a/pkg/android/res/drawable-hdpi/icon.png b/pkg/android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..118bb5faf732537346e1cb03fb11a73d71160530 GIT binary patch literal 2864 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 O0000~E|g1kP$yR>DMgW#3pwT- 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 literal 0 HcmV?d00001 diff --git a/pkg/android/res/drawable-xxhdpi/icon.png b/pkg/android/res/drawable-xxhdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0371eb93e1d5128a02094bb86c00dd6670f98a16 GIT binary patch literal 8648 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 rkSG=Dnr^QpNG+INg!)Timy literal 0 HcmV?d00001 diff --git a/pkg/android/res/drawable-xxxhdpi/icon.png b/pkg/android/res/drawable-xxxhdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..46f8b0ac199b3d9eb5d55ea602b345d614dfc790 GIT binary patch literal 12359 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 literal 0 HcmV?d00001 diff --git a/pkg/android/res/values/libs.xml b/pkg/android/res/values/libs.xml new file mode 100644 index 00000000..beb15ca1 --- /dev/null +++ b/pkg/android/res/values/libs.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/pkg/gpxsee64.nsi b/pkg/gpxsee64.nsi index 7d1b083a..711bc32a 100644 --- a/pkg/gpxsee64.nsi +++ b/pkg/gpxsee64.nsi @@ -37,7 +37,7 @@ Unicode true ; The name of the installer Name "GPXSee" ; Program version -!define VERSION "10.8" +!define VERSION "11.0" ; The file to write OutFile "GPXSee-${VERSION}_x64.exe" diff --git a/src/GUI/app.cpp b/src/GUI/app.cpp index 2e232fc0..d2cb659c 100644 --- a/src/GUI/app.cpp +++ b/src/GUI/app.cpp @@ -61,6 +61,10 @@ App::App(int &argc, char **argv) : QApplication(argc, argv) Waypoint::loadSymbolIcons(ProgramPaths::symbolsDir()); _gui = new GUI(); + +#ifdef Q_OS_ANDROID + connect(this, &App::applicationStateChanged, this, &App::appStateChanged); +#endif // Q_OS_ANDROID } App::~App() @@ -93,6 +97,14 @@ int App::run() return exec(); } +#ifdef Q_OS_ANDROID +void App::appStateChanged(Qt::ApplicationState state) +{ + if (state == Qt::ApplicationSuspended) + _gui->writeSettings(); +} +#endif // Q_OS_ANDROID + bool App::event(QEvent *event) { if (event->type() == QEvent::FileOpen) { diff --git a/src/GUI/app.h b/src/GUI/app.h index 5c8d97c1..16a6f1dc 100644 --- a/src/GUI/app.h +++ b/src/GUI/app.h @@ -5,7 +5,7 @@ class GUI; -class App : QApplication +class App : public QApplication { Q_OBJECT @@ -17,6 +17,11 @@ public: protected: bool event(QEvent *event); +#ifdef Q_OS_ANDROID +private slots: + void appStateChanged(Qt::ApplicationState state); +#endif // Q_OS_ANDROID + private: void loadDatums(); void loadPCSs(); diff --git a/src/GUI/cadencegraph.cpp b/src/GUI/cadencegraph.cpp index 7b1d69ed..a8d49c58 100644 --- a/src/GUI/cadencegraph.cpp +++ b/src/GUI/cadencegraph.cpp @@ -24,10 +24,17 @@ void CadenceGraph::setInfo() if (_showTracks) { QLocale l(QLocale::system()); +#ifdef Q_OS_ANDROID + GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale() + yOffset(), + 'f', 1) + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Max"), l.toString(max() * yScale() + yOffset(), + 'f', 1) + UNIT_SPACE + yUnits()); +#else // Q_OS_ANDROID GraphView::addInfo(tr("Average"), l.toString(avg() * yScale() + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale() - + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); + + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); +#endif // Q_OS_ANDROID } else clearInfo(); } diff --git a/src/GUI/colorbox.cpp b/src/GUI/colorbox.cpp index b7fe891e..8e5f01e3 100644 --- a/src/GUI/colorbox.cpp +++ b/src/GUI/colorbox.cpp @@ -52,17 +52,19 @@ void ColorBox::paintEvent(QPaintEvent *event) void ColorBox::mousePressEvent(QMouseEvent *event) { - if (event->button() != Qt::LeftButton) - return; - QColorDialog::ColorDialogOptions options = _alpha - ? QColorDialog::ColorDialogOptions(QColorDialog::ShowAlphaChannel) - : QColorDialog::ColorDialogOptions(); - QColor color = QColorDialog::getColor(_color, this, QString(), options); - if (color.isValid()) { - _color = color; - update(); - emit colorChanged(_color); + if (event->button() == Qt::LeftButton) { + QColorDialog::ColorDialogOptions options = _alpha + ? QColorDialog::ColorDialogOptions(QColorDialog::ShowAlphaChannel) + : QColorDialog::ColorDialogOptions(); + QColor color = QColorDialog::getColor(_color, this, QString(), options); + if (color.isValid()) { + _color = color; + update(); + emit colorChanged(_color); + } } + + QWidget::mousePressEvent(event); } void ColorBox::setColor(const QColor &color) diff --git a/src/GUI/dirselectwidget.cpp b/src/GUI/dirselectwidget.cpp index f082fd6c..dc85cc04 100644 --- a/src/GUI/dirselectwidget.cpp +++ b/src/GUI/dirselectwidget.cpp @@ -9,10 +9,12 @@ DirSelectWidget::DirSelectWidget(QWidget *parent) : QWidget(parent) { - QFontMetrics fm(QApplication::font()); _edit = new QLineEdit(); +#ifndef Q_OS_ANDROID + QFontMetrics fm(QApplication::font()); _edit->setMinimumWidth(fm.averageCharWidth() * (QDir::homePath().length() + 12)); +#endif // Q_OS_ANDROID _edit->setPlaceholderText(tr("System default")); #ifdef Q_OS_WIN32 _button = new QPushButton("..."); diff --git a/src/GUI/elevationgraph.cpp b/src/GUI/elevationgraph.cpp index 9f644dc5..616c66ed 100644 --- a/src/GUI/elevationgraph.cpp +++ b/src/GUI/elevationgraph.cpp @@ -54,6 +54,16 @@ void ElevationGraph::setInfo() else { QLocale l(QLocale::system()); +#ifdef Q_OS_ANDROID + GraphView::addInfo(tr("Up"), l.toString(ascent() * yScale(), 'f', 0) + + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Down"), l.toString(descent() * yScale(), 'f', 0) + + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Max"), l.toString(max() * yScale(), 'f', 0) + + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Min"), l.toString(min() * yScale(), 'f', 0) + + UNIT_SPACE + yUnits()); +#else // Q_OS_ANDROID GraphView::addInfo(tr("Ascent"), l.toString(ascent() * yScale(), 'f', 0) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Descent"), l.toString(descent() * yScale(), @@ -62,6 +72,7 @@ void ElevationGraph::setInfo() 0) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Minimum"), l.toString(min() * yScale(), 'f', 0) + UNIT_SPACE + yUnits()); +#endif // Q_OS_ANDROID } } diff --git a/src/GUI/filebrowser.cpp b/src/GUI/filebrowser.cpp index a864bfbb..5946a9e5 100644 --- a/src/GUI/filebrowser.cpp +++ b/src/GUI/filebrowser.cpp @@ -5,14 +5,25 @@ FileBrowser::FileBrowser(QObject *parent) : QObject(parent) { +#ifndef Q_OS_ANDROID _watcher = new QFileSystemWatcher(this); - connect(_watcher, &QFileSystemWatcher::directoryChanged, this, &FileBrowser::reloadDirectory); +#endif // Q_OS_ANDROID _index = -1; } +#ifdef Q_OS_ANDROID +void FileBrowser::setCurrentDir(const QString &path) +{ + QDir dir(path); + _files = dir.entryInfoList(_filter, QDir::Files); + _index = _files.empty() ? -1 : 0; + + emit listChanged(); +} +#else // Q_OS_ANDROID void FileBrowser::setCurrent(const QString &path) { QFileInfo file(path); @@ -28,6 +39,7 @@ void FileBrowser::setCurrent(const QString &path) _index = _files.empty() ? -1 : _files.indexOf(file); } +#endif // Q_OS_ANDROID void FileBrowser::setFilter(const QStringList &filter) { @@ -46,6 +58,11 @@ bool FileBrowser::isFirst() const return (_files.size() > 0 && _index == 0); } +QString FileBrowser::current() +{ + return (_index >= 0) ? _files.at(_index).absoluteFilePath() : QString(); +} + QString FileBrowser::next() { if (_index < 0 || _index == _files.size() - 1) diff --git a/src/GUI/filebrowser.h b/src/GUI/filebrowser.h index 00a06ce7..ec447f1c 100644 --- a/src/GUI/filebrowser.h +++ b/src/GUI/filebrowser.h @@ -14,9 +14,14 @@ class FileBrowser : public QObject public: FileBrowser(QObject *parent = 0); - void setFilter(const QStringList &filter); +#ifdef Q_OS_ANDROID + void setCurrentDir(const QString &path); +#else // Q_OS_ANDROID void setCurrent(const QString &path); +#endif // Q_OS_ANDROID + void setFilter(const QStringList &filter); + QString current(); QString next(); QString prev(); QString last(); @@ -32,7 +37,9 @@ private slots: void reloadDirectory(const QString &path); private: +#ifndef Q_OS_ANDROID QFileSystemWatcher *_watcher; +#endif // Q_OS_ANDROID QStringList _filter; QFileInfoList _files; int _index; diff --git a/src/GUI/fileselectwidget.cpp b/src/GUI/fileselectwidget.cpp index 3a2458c6..6f2de6ab 100644 --- a/src/GUI/fileselectwidget.cpp +++ b/src/GUI/fileselectwidget.cpp @@ -10,10 +10,12 @@ FileSelectWidget::FileSelectWidget(QWidget *parent) : QWidget(parent) { - QFontMetrics fm(QApplication::font()); _edit = new QLineEdit(); +#ifndef Q_OS_ANDROID + QFontMetrics fm(QApplication::font()); _edit->setMinimumWidth(fm.averageCharWidth() * (QDir::homePath().length() + 12)); +#endif // Q_OS_ANDROID #ifdef Q_OS_WIN32 _button = new QPushButton("..."); _button->setMaximumWidth(_button->sizeHint().width() / 2); diff --git a/src/GUI/gearratiograph.cpp b/src/GUI/gearratiograph.cpp index 040c5788..1758cbcc 100644 --- a/src/GUI/gearratiograph.cpp +++ b/src/GUI/gearratiograph.cpp @@ -24,12 +24,21 @@ void GearRatioGraph::setInfo() if (_showTracks) { QLocale l(QLocale::system()); +#ifdef Q_OS_ANDROID + GraphView::addInfo(tr("Top"), l.toString(top() * yScale(), 'f', 2) + + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Min"), l.toString(min() * yScale(), 'f', 2) + + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Max"), l.toString(max() * yScale(), 'f', 2) + + UNIT_SPACE + yUnits()); +#else // Q_OS_ANDROID GraphView::addInfo(tr("Most used"), l.toString(top() * yScale(), 'f', 2) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Minimum"), l.toString(min() * yScale(), 'f', 2) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale(), 'f', 2) + UNIT_SPACE + yUnits()); +#endif // Q_OS_ANDROID } else clearInfo(); } diff --git a/src/GUI/gui.cpp b/src/GUI/gui.cpp index b15681bb..63d540b0 100644 --- a/src/GUI/gui.cpp +++ b/src/GUI/gui.cpp @@ -53,6 +53,7 @@ #include "mapitem.h" #include "mapaction.h" #include "poiaction.h" +#include "navigationwidget.h" #include "gui.h" @@ -72,7 +73,11 @@ GUI::GUI() createStatusBar(); createActions(); createMenus(); +#ifdef Q_OS_ANDROID + createNavigation(); +#else // Q_OS_ANDROID createToolBars(); +#endif // Q_OS_ANDROID createBrowser(); _splitter = new QSplitter(); @@ -83,6 +88,7 @@ GUI::GUI() _splitter->setContentsMargins(0, 0, 0, 0); _splitter->setStretchFactor(0, 255); _splitter->setStretchFactor(1, 1); + setCentralWidget(_splitter); setWindowIcon(QIcon(APP_ICON)); @@ -181,19 +187,23 @@ void GUI::createActions() _navigationActionGroup->setEnabled(false); // General actions +#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) _exitAction = new QAction(QIcon(QUIT_ICON), tr("Quit"), this); _exitAction->setShortcut(QUIT_SHORTCUT); _exitAction->setMenuRole(QAction::QuitRole); connect(_exitAction, &QAction::triggered, this, &GUI::close); addAction(_exitAction); +#endif // Q_OS_MAC + Q_OS_ANDROID // Help & About _pathsAction = new QAction(tr("Paths"), this); _pathsAction->setMenuRole(QAction::NoRole); connect(_pathsAction, &QAction::triggered, this, &GUI::paths); +#ifndef Q_OS_ANDROID _keysAction = new QAction(tr("Keyboard controls"), this); _keysAction->setMenuRole(QAction::NoRole); connect(_keysAction, &QAction::triggered, this, &GUI::keys); +#endif // Q_OS_ANDROID _aboutAction = new QAction(QIcon(APP_ICON), tr("About GPXSee"), this); _aboutAction->setMenuRole(QAction::AboutRole); connect(_aboutAction, &QAction::triggered, this, &GUI::about); @@ -205,6 +215,12 @@ void GUI::createActions() connect(_openFileAction, &QAction::triggered, this, QOverload<>::of(&GUI::openFile)); addAction(_openFileAction); +#ifdef Q_OS_ANDROID + _openDirAction = new QAction(QIcon(OPEN_FILE_ICON), tr("Open directory..."), + this); + _openDirAction->setMenuRole(QAction::NoRole); + connect(_openDirAction, &QAction::triggered, this, &GUI::openDir); +#endif // Q_OS_ANDROID _printFileAction = new QAction(QIcon(PRINT_FILE_ICON), tr("Print..."), this); _printFileAction->setMenuRole(QAction::NoRole); @@ -459,10 +475,12 @@ void GUI::createActions() &GUI::showGraphSliderInfo); // Settings actions +#ifndef Q_OS_ANDROID _showToolbarsAction = new QAction(tr("Show toolbars"), this); _showToolbarsAction->setMenuRole(QAction::NoRole); _showToolbarsAction->setCheckable(true); connect(_showToolbarsAction, &QAction::triggered, this, &GUI::showToolbars); +#endif // Q_OS_ANDROID ag = new QActionGroup(this); ag->setExclusive(true); _totalTimeAction = new QAction(tr("Total time"), this); @@ -514,6 +532,7 @@ void GUI::createActions() _dmsAction->setCheckable(true); _dmsAction->setActionGroup(ag); connect(_dmsAction, &QAction::triggered, this, &GUI::setDMS); +#ifndef Q_OS_ANDROID _fullscreenAction = new QAction(QIcon(FULLSCREEN_ICON), tr("Fullscreen mode"), this); _fullscreenAction->setMenuRole(QAction::NoRole); @@ -521,11 +540,13 @@ void GUI::createActions() _fullscreenAction->setShortcut(FULLSCREEN_SHORTCUT); connect(_fullscreenAction, &QAction::triggered, this, &GUI::showFullscreen); addAction(_fullscreenAction); +#endif // Q_OS_ANDROID _openOptionsAction = new QAction(tr("Options..."), this); _openOptionsAction->setMenuRole(QAction::PreferencesRole); connect(_openOptionsAction, &QAction::triggered, this, &GUI::openOptions); // Navigation actions +#ifndef Q_OS_ANDROID _nextAction = new QAction(QIcon(NEXT_FILE_ICON), tr("Next"), this); _nextAction->setActionGroup(_navigationActionGroup); _nextAction->setMenuRole(QAction::NoRole); @@ -542,6 +563,7 @@ void GUI::createActions() _firstAction->setMenuRole(QAction::NoRole); _firstAction->setActionGroup(_navigationActionGroup); connect(_firstAction, &QAction::triggered, this, &GUI::first); +#endif // Q_OS_ANDROID } void GUI::createMapNodeMenu(const TreeNode &node, QMenu *menu, @@ -574,8 +596,13 @@ void GUI::createMenus() { QMenu *fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(_openFileAction); +#ifdef Q_OS_ANDROID + fileMenu->addAction(_openDirAction); +#endif // Q_OS_ANDROID fileMenu->addSeparator(); +#ifndef Q_OS_ANDROID fileMenu->addAction(_printFileAction); +#endif // Q_OS_ANDROID fileMenu->addAction(_exportPDFFileAction); fileMenu->addAction(_exportPNGFileAction); fileMenu->addSeparator(); @@ -583,10 +610,10 @@ void GUI::createMenus() fileMenu->addSeparator(); fileMenu->addAction(_reloadFileAction); fileMenu->addAction(_closeFileAction); -#ifndef Q_OS_MAC +#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) fileMenu->addSeparator(); fileMenu->addAction(_exitAction); -#endif // Q_OS_MAC +#endif // Q_OS_MAC + Q_OS_ANDROID _mapMenu = menuBar()->addMenu(tr("&Map")); _mapsEnd = _mapMenu->addSeparator(); @@ -659,18 +686,31 @@ void GUI::createMenus() coordinatesMenu->addAction(_degreesMinutesAction); coordinatesMenu->addAction(_dmsAction); settingsMenu->addSeparator(); +#ifndef Q_OS_ANDROID settingsMenu->addAction(_showToolbarsAction); settingsMenu->addAction(_fullscreenAction); settingsMenu->addSeparator(); +#endif // Q_OS_ANDROID settingsMenu->addAction(_openOptionsAction); QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(_pathsAction); +#ifndef Q_OS_ANDROID helpMenu->addAction(_keysAction); +#endif // Q_OS_ANDROID helpMenu->addSeparator(); helpMenu->addAction(_aboutAction); } +#ifdef Q_OS_ANDROID +void GUI::createNavigation() +{ + _navigation = new NavigationWidget(_mapView); + + connect(_navigation, &NavigationWidget::next, this, &GUI::next); + connect(_navigation, &NavigationWidget::prev, this, &GUI::prev); +} +#else // Q_OS_ANDROID void GUI::createToolBars() { int is = style()->pixelMetric(QStyle::PM_ToolBarIconSize); @@ -706,6 +746,7 @@ void GUI::createToolBars() _navigationToolBar->addAction(_nextAction); _navigationToolBar->addAction(_lastAction); } +#endif // Q_OS_ANDROID void GUI::createMapView() { @@ -713,7 +754,11 @@ void GUI::createMapView() _mapView = new MapView(_map, _poi, 0, this); _mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding)); +#ifdef Q_OS_ANDROID + _mapView->setMinimumHeight(100); +#else // Q_OS_ANDROID _mapView->setMinimumHeight(200); +#endif // Q_OS_ANDROID #ifdef Q_OS_WIN32 _mapView->setFrameShape(QFrame::NoFrame); #endif // Q_OS_WIN32 @@ -765,7 +810,16 @@ void GUI::about() QUrl homepage(APP_HOMEPAGE); msgBox.setWindowTitle(tr("About GPXSee")); - msgBox.setText("

" + QString(APP_NAME) + "

" + tr("Version %1") +#ifdef Q_OS_ANDROID + msgBox.setText("

" + QString(APP_NAME) + "

" + tr("Version %1") + .arg(QString(APP_VERSION) + " (" + QSysInfo::buildCpuArchitecture() + + ", Qt " + QT_VERSION_STR + ")") + "

" + + tr("GPXSee is distributed under the terms of the GNU General Public " + "License version 3. For more info about GPXSee visit the project " + "homepage at %1.").arg("" + + homepage.toString(QUrl::RemoveScheme).mid(2) + "") + "

"); +#else // Q_OS_ANDROID + msgBox.setText("

" + QString(APP_NAME) + "

" + tr("Version %1") .arg(QString(APP_VERSION) + " (" + QSysInfo::buildCpuArchitecture() + ", Qt " + QT_VERSION_STR + ")") + "

"); msgBox.setInformativeText("
" @@ -778,10 +832,12 @@ void GUI::about() QIcon icon = msgBox.windowIcon(); QSize size = icon.actualSize(QSize(64, 64)); msgBox.setIconPixmap(icon.pixmap(size)); +#endif // Q_OS_ANDROID msgBox.exec(); } +#ifndef Q_OS_ANDROID void GUI::keys() { QMessageBox msgBox(this); @@ -820,12 +876,30 @@ void GUI::keys() msgBox.exec(); } +#endif // Q_OS_ANDROID void GUI::paths() { QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Paths")); +#ifdef Q_OS_ANDROID + msgBox.setText( + + "" + tr("Map directory:") + "
" + + QDir::cleanPath(ProgramPaths::mapDir(true)) + "

" + + tr("POI directory:") + "
" + + QDir::cleanPath(ProgramPaths::poiDir(true)) + "

" + + tr("GCS/PCS directory:") + "
" + + QDir::cleanPath(ProgramPaths::csvDir(true)) + "

" + + tr("DEM directory:") + "
" + + QDir::cleanPath(ProgramPaths::demDir(true)) + "

" + + tr("Styles directory:") + "
" + + QDir::cleanPath(ProgramPaths::styleDir(true)) + "

" + + tr("Symbols directory:") + "
" + + QDir::cleanPath(ProgramPaths::symbolsDir(true)) + "

" + + tr("Tile cache directory:") + "
" + + QDir::cleanPath(ProgramPaths::tilesDir()) + "
"); +#else // Q_OS_ANDROID msgBox.setText("

" + tr("Paths") + "

"); msgBox.setInformativeText( "
" @@ -844,14 +918,20 @@ void GUI::paths() + tr("Tile cache directory:") + "" + QDir::cleanPath(ProgramPaths::tilesDir()) + "
" ); +#endif // Q_OS_ANDROID msgBox.exec(); } void GUI::openFile() { +#ifdef Q_OS_ANDROID + QStringList files(QFileDialog::getOpenFileNames(this, tr("Open file"), + _dataDir)); +#else // Q_OS_ANDROID QStringList files(QFileDialog::getOpenFileNames(this, tr("Open file"), _dataDir, Data::formats())); +#endif // Q_OS_ANDROID for (int i = 0; i < files.size(); i++) openFile(files.at(i)); @@ -859,6 +939,19 @@ void GUI::openFile() _dataDir = QFileInfo(files.last()).path(); } +#ifdef Q_OS_ANDROID +void GUI::openDir() +{ + QString dir(QFileDialog::getExistingDirectory(this, tr("Open directory"), + _dataDir)); + + if (!dir.isEmpty()) { + _browser->setCurrentDir(dir); + openFile(_browser->current()); + } +} +#endif // Q_OS_ANDROID + bool GUI::openFile(const QString &fileName, bool silent) { if (_files.contains(fileName)) @@ -868,7 +961,9 @@ bool GUI::openFile(const QString &fileName, bool silent) return false; _files.append(fileName); +#ifndef Q_OS_ANDROID _browser->setCurrent(fileName); +#endif // Q_OS_ANDROID _fileActionGroup->setEnabled(true); // Explicitly enable the reload action as it may be disabled by loadMapDir() _reloadFileAction->setEnabled(true); @@ -898,7 +993,7 @@ bool GUI::loadFile(const QString &fileName, bool silent) _fileActionGroup->setEnabled(false); QString error = tr("Error loading data file:") + "\n\n" - + fileName + "\n\n" + data.errorString(); + + Util::displayName(fileName) + "\n\n" + data.errorString(); if (data.errorLine()) error.append("\n" + tr("Line: %1").arg(data.errorLine())); QMessageBox::critical(this, APP_NAME, error); @@ -967,8 +1062,13 @@ void GUI::loadData(const Data &data) void GUI::openPOIFile() { +#ifdef Q_OS_ANDROID + QStringList files(QFileDialog::getOpenFileNames(this, tr("Open POI file"), + _poiDir)); +#else // Q_OS_ANDROID QStringList files(QFileDialog::getOpenFileNames(this, tr("Open POI file"), _poiDir, Data::formats())); +#endif // Q_OS_ANDROID for (int i = 0; i < files.size(); i++) openPOIFile(files.at(i)); @@ -994,7 +1094,7 @@ bool GUI::openPOIFile(const QString &fileName) return true; } else { QString error = tr("Error loading POI file:") + "\n\n" - + fileName + "\n\n" + _poi->errorString(); + + Util::displayName(fileName) + "\n\n" + _poi->errorString(); if (_poi->errorLine()) error.append("\n" + tr("Line: %1").arg(_poi->errorLine())); QMessageBox::critical(this, APP_NAME, error); @@ -1207,12 +1307,72 @@ void GUI::exportPNGFile() void GUI::statistics() { QLocale l(QLocale::system()); + QMessageBox msgBox(this); + QString text; +#ifdef Q_OS_ANDROID + if (_showTracksAction->isChecked() && _trackCount > 1) + text.append("" + tr("Tracks") + ": " + + l.toString(_trackCount) + "
"); + if (_showRoutesAction->isChecked() && _routeCount > 1) + text.append("" + tr("Routes") + ": " + + l.toString(_routeCount) + "
"); + if (_showWaypointsAction->isChecked() && _waypointCount > 1) + text.append("" + tr("Waypoints") + ": " + + l.toString(_waypointCount) + "
"); + if (_showAreasAction->isChecked() && _areaCount > 1) + text.append("" + tr("Areas") + ": " + + l.toString(_areaCount) + "
"); + + if (_dateRange.first.isValid()) { + if (_dateRange.first == _dateRange.second) { + QString format = l.dateFormat(QLocale::LongFormat); + text.append("" + tr("Date") + ": " + + _dateRange.first.toString(format) + "
"); + } else { + QString format = l.dateFormat(QLocale::ShortFormat); + text.append("" + tr("Date") + ": " + + QString("%1 - %2").arg(_dateRange.first.toString(format), + _dateRange.second.toString(format)) + "
"); + } + } + + if (distance() > 0) + text.append("" + tr("Distance") + ": " + + Format::distance(distance(), units()) + "
"); + if (time() > 0) { + text.append("" + tr("Time") + ": " + + Format::timeSpan(time()) + "
"); + text.append("" + tr("Moving time") + ": " + + Format::timeSpan(movingTime()) + "
"); + } + text.append("
"); + + for (int i = 0; i < _tabs.count(); i++) { + const GraphTab *tab = _tabs.at(i); + if (tab->isEmpty()) + continue; + + text.append("" + tab->label() + "
"); + for (int j = 0; j < tab->info().size(); j++) { + const KV &kv = tab->info().at(j); + text.append("" + kv.key() + ": " + kv.value()); + if (j != tab->info().size() - 1) + text.append(" | "); + } + if (i != _tabs.count() - 1) + text.append("

"); + } + + msgBox.setWindowTitle(tr("Statistics")); + msgBox.setText(text); + +#else // Q_OS_ANDROID #ifdef Q_OS_WIN32 - QString text = ""; #else // Q_OS_WIN32 - QString text = "
"; #endif // Q_OS_WIN32 @@ -1267,11 +1427,11 @@ void GUI::statistics() text.append("
"); - - QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Statistics")); msgBox.setText("

" + tr("Statistics") + "

"); msgBox.setInformativeText(text); +#endif // Q_OS_ANDROID + msgBox.exec(); } @@ -1430,8 +1590,10 @@ void GUI::reloadFiles() updateWindowTitle(); if (_files.isEmpty()) _fileActionGroup->setEnabled(false); +#ifndef Q_OS_ANDROID else _browser->setCurrent(_files.last()); +#endif // Q_OS_ANDROID updateDEMDownloadAction(); } @@ -1466,6 +1628,10 @@ void GUI::closeAll() updateWindowTitle(); updateGraphTabs(); updateDEMDownloadAction(); + +#ifdef Q_OS_ANDROID + _browser->setCurrentDir(QString()); +#endif // Q_OS_ANDROID } void GUI::showGraphs(bool show) @@ -1473,6 +1639,7 @@ void GUI::showGraphs(bool show) _graphTabWidget->setHidden(!show); } +#ifndef Q_OS_ANDROID void GUI::showToolbars(bool show) { if (show) { @@ -1515,6 +1682,7 @@ void GUI::showFullscreen(bool show) showNormal(); } } +#endif // Q_OS_ANDROID void GUI::showTracks(bool show) { @@ -1583,8 +1751,13 @@ void GUI::showPathMarkerInfo(QAction *action) void GUI::loadMap() { +#ifdef Q_OS_ANDROID + QStringList files(QFileDialog::getOpenFileNames(this, tr("Open map file"), + _mapDir)); +#else // Q_OS_ANDROID QStringList files(QFileDialog::getOpenFileNames(this, tr("Open map file"), _mapDir, MapList::formats())); +#endif // Q_OS_ANDROID MapAction *a, *lastReady = 0; for (int i = 0; i < files.size(); i++) { @@ -1627,7 +1800,8 @@ bool GUI::loadMapNode(const TreeNode &node, MapAction *&action, if (!map->isValid()) { if (!silent) QMessageBox::critical(this, APP_NAME, - tr("Error loading map:") + "\n\n" + map->path() + "\n\n" + tr("Error loading map:") + "\n\n" + + Util::displayName(map->path()) + "\n\n" + map->errorString()); delete map; } else { @@ -1672,8 +1846,8 @@ void GUI::mapLoaded() _showMapAction->setEnabled(true); _clearMapCacheAction->setEnabled(true); } else { - QString error = tr("Error loading map:") + "\n\n" + map->path() + "\n\n" - + map->errorString(); + QString error = tr("Error loading map:") + "\n\n" + + Util::displayName(map->path()) + "\n\n" + map->errorString(); QMessageBox::critical(this, APP_NAME, error); action->deleteLater(); } @@ -1691,8 +1865,8 @@ void GUI::mapLoadedDir() actions.append(action); _mapView->loadMaps(actions); } else { - QString error = tr("Error loading map:") + "\n\n" + map->path() + "\n\n" - + map->errorString(); + QString error = tr("Error loading map:") + "\n\n" + + Util::displayName(map->path()) + "\n\n" + map->errorString(); QMessageBox::critical(this, APP_NAME, error); action->deleteLater(); } @@ -1714,7 +1888,8 @@ void GUI::loadMapDirNode(const TreeNode &node, QList &actions if (!(a = findMapAction(existingActions, map))) { if (!map->isValid()) { QMessageBox::critical(this, APP_NAME, tr("Error loading map:") - + "\n\n" + map->path() + "\n\n" + map->errorString()); + + "\n\n" + Util::displayName(map->path()) + "\n\n" + + map->errorString()); delete map; } else { a = new MapAction(map, _mapsActionGroup); @@ -1817,7 +1992,7 @@ void GUI::updateStatusBarInfo() if (_files.count() == 0) _fileNameLabel->setText(tr("No files loaded")); else if (_files.count() == 1) - _fileNameLabel->setText(_files.at(0)); + _fileNameLabel->setText(Util::displayName(_files.at(0))); else _fileNameLabel->setText(tr("%n files", "", _files.count())); @@ -1840,6 +2015,10 @@ void GUI::updateStatusBarInfo() _timeLabel->clear(); _timeLabel->setToolTip(QString()); } + +#ifdef Q_OS_ANDROID + statusBar()->setVisible(!_files.isEmpty()); +#endif // Q_OS_ANDROID } void GUI::updateWindowTitle() @@ -1929,10 +2108,17 @@ void GUI::graphChanged(int index) void GUI::updateNavigationActions() { +#ifdef Q_OS_ANDROID + _navigation->enableNext(!_browser->isLast() + && !_browser->current().isNull()); + _navigation->enablePrev(!_browser->isFirst() + && !_browser->current().isNull()); +#else // Q_OS_ANDROID _lastAction->setEnabled(!_browser->isLast()); _nextAction->setEnabled(!_browser->isLast()); _firstAction->setEnabled(!_browser->isFirst()); _prevAction->setEnabled(!_browser->isFirst()); +#endif // Q_OS_ANDROID } bool GUI::updateGraphTabs() @@ -2042,6 +2228,7 @@ void GUI::first() openFile(file); } +#ifndef Q_OS_ANDROID void GUI::keyPressEvent(QKeyEvent *event) { QString file; @@ -2096,11 +2283,12 @@ void GUI::keyPressEvent(QKeyEvent *event) QMainWindow::keyPressEvent(event); } +#endif // Q_OS_ANDROID void GUI::closeEvent(QCloseEvent *event) { writeSettings(); - event->accept(); + QMainWindow::closeEvent(event); } void GUI::dragEnterEvent(QDragEnterEvent *event) @@ -2167,6 +2355,7 @@ void GUI::writeSettings() QSettings settings(qApp->applicationName(), qApp->applicationName()); settings.clear(); +#ifndef Q_OS_ANDROID settings.beginGroup(WINDOW_SETTINGS_GROUP); if (!_windowStates.isEmpty() && !_windowGeometries.isEmpty()) { settings.setValue(WINDOW_STATE_SETTING, _windowStates.first()); @@ -2176,6 +2365,7 @@ void GUI::writeSettings() settings.setValue(WINDOW_GEOMETRY_SETTING, saveGeometry()); } settings.endGroup(); +#endif // Q_OS_ANDROID settings.beginGroup(SETTINGS_SETTINGS_GROUP); if ((_movingTimeAction->isChecked() ? Moving : Total) != @@ -2190,9 +2380,11 @@ void GUI::writeSettings() : _degreesMinutesAction->isChecked() ? DegreesMinutes : DecimalDegrees; if (format != COORDINATES_DEFAULT) settings.setValue(COORDINATES_SETTING, format); +#ifndef Q_OS_ANDROID if (_showToolbarsAction->isChecked() != SHOW_TOOLBARS_DEFAULT) settings.setValue(SHOW_TOOLBARS_SETTING, _showToolbarsAction->isChecked()); +#endif // Q_OS_ANDROID settings.endGroup(); settings.beginGroup(MAP_SETTINGS_GROUP); @@ -2485,10 +2677,12 @@ void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs) int value; QSettings settings(qApp->applicationName(), qApp->applicationName()); +#ifndef Q_OS_ANDROID settings.beginGroup(WINDOW_SETTINGS_GROUP); restoreGeometry(settings.value(WINDOW_GEOMETRY_SETTING).toByteArray()); restoreState(settings.value(WINDOW_STATE_SETTING).toByteArray()); settings.endGroup(); +#endif // Q_OS_ANDROID settings.beginGroup(SETTINGS_SETTINGS_GROUP); if (settings.value(TIME_TYPE_SETTING, TIME_TYPE_DEFAULT).toInt() == Moving) @@ -2512,10 +2706,12 @@ void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs) else _decimalDegreesAction->trigger(); +#ifndef Q_OS_ANDROID if (!settings.value(SHOW_TOOLBARS_SETTING, SHOW_TOOLBARS_DEFAULT).toBool()) showToolbars(false); else _showToolbarsAction->setChecked(true); +#endif // Q_OS_ANDROID settings.endGroup(); settings.beginGroup(MAP_SETTINGS_GROUP); @@ -2523,8 +2719,8 @@ void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs) _showMapAction->setChecked(true); else _mapView->showMap(false); - if (settings.value(SHOW_CURSOR_COORDINATES_SETTING, SHOW_CURSOR_COORDINATES_DEFAULT) - .toBool()) { + if (settings.value(SHOW_CURSOR_COORDINATES_SETTING, + SHOW_CURSOR_COORDINATES_DEFAULT).toBool()) { _showCoordinatesAction->setChecked(true); _mapView->showCursorCoordinates(true); } diff --git a/src/GUI/gui.h b/src/GUI/gui.h index a4419844..26fc01fa 100644 --- a/src/GUI/gui.h +++ b/src/GUI/gui.h @@ -35,6 +35,7 @@ class MapAction; class POIAction; class Data; class DEMLoader; +class NavigationWidget; class GUI : public QMainWindow { @@ -47,15 +48,21 @@ public: bool loadMap(const QString &fileName, MapAction *&action, bool silent = false); void show(); + void writeSettings(); private slots: void about(); +#ifndef Q_OS_ANDROID void keys(); +#endif // Q_OS_ANDROID void paths(); void printFile(); void exportPDFFile(); void exportPNGFile(); void openFile(); +#ifdef Q_OS_ANDROID + void openDir(); +#endif // Q_OS_ANDROID void closeAll(); void reloadFiles(); void statistics(); @@ -64,8 +71,10 @@ private slots: void showGraphGrids(bool show); void showGraphSliderInfo(bool show); void showPathMarkerInfo(QAction *action); +#ifndef Q_OS_ANDROID void showToolbars(bool show); void showFullscreen(bool show); +#endif // Q_OS_ANDROID void showTracks(bool show); void showRoutes(bool show); void showAreas(bool show); @@ -129,7 +138,11 @@ private: QAction *action = 0); void createActions(); void createMenus(); +#ifdef Q_OS_ANDROID + void createNavigation(); +#else // Q_OS_ANDROID void createToolBars(); +#endif // Q_OS_ANDROID void createStatusBar(); void createMapView(); void createGraphTabs(); @@ -160,19 +173,24 @@ private: QAction *mapAction(const QString &name); QGeoPositionInfoSource *positionSource(const Options &options); void readSettings(QString &activeMap, QStringList &disabledPOIs); - void writeSettings(); void loadInitialMaps(const QString &selected); void loadInitialPOIs(const QStringList &disabled); +#ifndef Q_OS_ANDROID void keyPressEvent(QKeyEvent *event); +#endif // Q_OS_ANDROID void closeEvent(QCloseEvent *event); void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); +#ifdef Q_OS_ANDROID + NavigationWidget *_navigation; +#else // Q_OS_ANDROID QToolBar *_fileToolBar; QToolBar *_showToolBar; QToolBar *_navigationToolBar; +#endif // Q_OS_ANDROID QMenu *_poiMenu; QMenu *_mapMenu; @@ -180,15 +198,16 @@ private: QActionGroup *_navigationActionGroup; QActionGroup *_mapsActionGroup; QActionGroup *_poisActionGroup; +#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) QAction *_exitAction; - QAction *_keysAction; +#endif // Q_OS_MAC + Q_OS_ANDROID QAction *_pathsAction; QAction *_aboutAction; - QAction *_aboutQtAction; QAction *_printFileAction; QAction *_exportPDFFileAction; QAction *_exportPNGFileAction; QAction *_openFileAction; + QAction *_openDirAction; QAction *_closeFileAction; QAction *_reloadFileAction; QAction *_statisticsAction; @@ -204,7 +223,6 @@ private: QAction *_followPositionAction; QAction *_showPositionCoordinatesAction; QAction *_showMotionInfo; - QAction *_fullscreenAction; QAction *_loadMapAction; QAction *_loadMapDirAction; QAction *_clearMapCacheAction; @@ -213,11 +231,15 @@ private: QAction *_showGraphSliderInfoAction; QAction *_distanceGraphAction; QAction *_timeGraphAction; +#ifndef Q_OS_ANDROID + QAction *_keysAction; + QAction *_fullscreenAction; QAction *_showToolbarsAction; QAction *_nextAction; QAction *_prevAction; QAction *_lastAction; QAction *_firstAction; +#endif // Q_OS_ANDROID QAction *_metricUnitsAction; QAction *_imperialUnitsAction; QAction *_nauticalUnitsAction; @@ -271,9 +293,11 @@ private: DateTimeRange _dateRange; QString _pathName; +#ifndef Q_OS_ANDROID QList _windowStates; QList _windowGeometries; int _frameStyle; +#endif // Q_OS_ANDROID PDFExport _pdfExport; PNGExport _pngExport; diff --git a/src/GUI/heartrategraph.cpp b/src/GUI/heartrategraph.cpp index b9983c2d..b5282e9c 100644 --- a/src/GUI/heartrategraph.cpp +++ b/src/GUI/heartrategraph.cpp @@ -24,10 +24,17 @@ void HeartRateGraph::setInfo() if (_showTracks) { QLocale l(QLocale::system()); +#ifdef Q_OS_ANDROID + GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale(), 'f', 0) + + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Max"), l.toString(max() * yScale(), 'f', 0) + + UNIT_SPACE + yUnits()); +#else // Q_OS_ANDROID GraphView::addInfo(tr("Average"), l.toString(avg() * yScale(), 'f', 0) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale(), 'f', 0) + UNIT_SPACE + yUnits()); +#endif // Q_OS_ANDROID } else clearInfo(); } diff --git a/src/GUI/mapview.cpp b/src/GUI/mapview.cpp index 152c5f1c..313c2624 100644 --- a/src/GUI/mapview.cpp +++ b/src/GUI/mapview.cpp @@ -662,20 +662,8 @@ void MapView::wheelEvent(QWheelEvent *event) #else // QT 5.15 zoom((delta > 0) ? 1 : -1, event->position().toPoint(), shift); #endif // QT 5.15 -} -void MapView::mouseDoubleClickEvent(QMouseEvent *event) -{ - bool shift = (event->modifiers() & MODIFIER) ? true : false; - - QGraphicsView::mouseDoubleClickEvent(event); - if (event->isAccepted()) - return; - - if (event->button() != Qt::LeftButton && event->button() != Qt::RightButton) - return; - - zoom((event->button() == Qt::LeftButton) ? 1 : -1, event->pos(), shift); + QGraphicsView::wheelEvent(event); } void MapView::keyPressEvent(QKeyEvent *event) @@ -713,15 +701,6 @@ void MapView::keyReleaseEvent(QKeyEvent *event) QGraphicsView::keyReleaseEvent(event); } -void MapView::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton && event->modifiers() & MODIFIER) - QApplication::clipboard()->setText(Format::coordinates( - _map->xy2ll(mapToScene(event->pos())), _cursorCoordinates->format())); - else - QGraphicsView::mousePressEvent(event); -} - void MapView::plot(QPainter *painter, const QRectF &target, qreal scale, PlotFlags flags) { @@ -1200,6 +1179,12 @@ void MapView::scrollContentsBy(int dx, int dy) } } +void MapView::leaveEvent(QEvent *event) +{ + _cursorCoordinates->setCoordinates(Coordinates()); + QGraphicsView::leaveEvent(event); +} + void MapView::mouseMoveEvent(QMouseEvent *event) { if (_cursorCoordinates->isVisible()) { @@ -1210,10 +1195,30 @@ void MapView::mouseMoveEvent(QMouseEvent *event) QGraphicsView::mouseMoveEvent(event); } -void MapView::leaveEvent(QEvent *event) + +void MapView::mousePressEvent(QMouseEvent *event) { - _cursorCoordinates->setCoordinates(Coordinates()); - QGraphicsView::leaveEvent(event); + if (event->button() == Qt::LeftButton) { + if (event->modifiers() & MODIFIER) + QApplication::clipboard()->setText(Format::coordinates(_map->xy2ll( + mapToScene(event->pos())), _cursorCoordinates->format())); +#ifdef Q_OS_ANDROID + else + emit clicked(event->pos()); +#endif // Q_OS_ANDROID + } + + QGraphicsView::mousePressEvent(event); +} + +void MapView::mouseDoubleClickEvent(QMouseEvent *event) +{ + bool shift = (event->modifiers() & MODIFIER) ? true : false; + + if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) + zoom((event->button() == Qt::LeftButton) ? 1 : -1, event->pos(), shift); + + QGraphicsView::mouseDoubleClickEvent(event); } bool MapView::event(QEvent *event) diff --git a/src/GUI/mapview.h b/src/GUI/mapview.h index e1e1ec92..a051e896 100644 --- a/src/GUI/mapview.h +++ b/src/GUI/mapview.h @@ -102,6 +102,11 @@ public: RectC boundingRect() const; +#ifdef Q_OS_ANDROID +signals: + void clicked(const QPoint &pos); +#endif // Q_OS_ANDROID + public slots: void showMap(bool show); void showPOI(bool show); @@ -162,6 +167,7 @@ private: void drawBackground(QPainter *painter, const QRectF &rect); void paintEvent(QPaintEvent *event); void leaveEvent(QEvent *event); + bool event(QEvent *event); void scrollContentsBy(int dx, int dy); diff --git a/src/GUI/navigationwidget.cpp b/src/GUI/navigationwidget.cpp new file mode 100644 index 00000000..00b8aefb --- /dev/null +++ b/src/GUI/navigationwidget.cpp @@ -0,0 +1,101 @@ +#include "navigationwidget.h" +#include +#include +#include + +#define MARGIN 5 +#define SIZE 40 + +#ifdef Q_OS_ANDROID + +NavigationWidget::NavigationWidget(MapView *view) + : QWidget(view), _showPrev(false), _showNext(false) +{ + setAttribute(Qt::WA_NoSystemBackground); + setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_TransparentForMouseEvents); + + newParent(); + + connect(view, &MapView::clicked, this, &NavigationWidget::viewClicked); +} + +bool NavigationWidget::eventFilter(QObject *obj, QEvent *ev) +{ + if (obj == parent()) { + if (ev->type() == QEvent::Resize) + resize(static_cast(ev)->size()); + else if (ev->type() == QEvent::ChildAdded) + raise(); + } + + return QWidget::eventFilter(obj, ev); +} + +bool NavigationWidget::event(QEvent* ev) +{ + if (ev->type() == QEvent::ParentAboutToChange) { + if (parent()) + parent()->removeEventFilter(this); + } else if (ev->type() == QEvent::ParentChange) + newParent(); + + return QWidget::event(ev); +} + +void NavigationWidget::paintEvent(QPaintEvent *ev) +{ + Q_UNUSED(ev); + QPainter p(this); + + QColor c(Qt::black); + c.setAlpha(64); + p.setBrush(c); + p.setPen(Qt::NoPen); + + if (_showPrev) { + QPainterPath path; + path.addEllipse(QRect(MARGIN, rect().center().y() - SIZE/2, SIZE, SIZE)); + path.moveTo(QPointF(MARGIN + 0.66*SIZE, rect().center().y() - SIZE/4)); + path.lineTo(QPointF(MARGIN + SIZE/4, rect().center().y())); + path.lineTo(QPointF(MARGIN + 0.66*SIZE, rect().center().y() + SIZE/4)); + path.closeSubpath(); + p.drawPath(path); + } + if (_showNext) { + QPainterPath path; + path.addEllipse(QRect(rect().right() - (MARGIN + SIZE), + rect().center().y() - SIZE/2, SIZE, SIZE)); + path.moveTo(QPointF(rect().right() - (MARGIN + 0.66*SIZE), + rect().center().y() - SIZE/4)); + path.lineTo(QPointF(rect().right() - (MARGIN + SIZE/4), + rect().center().y())); + path.lineTo(QPointF(rect().right() - (MARGIN + 0.66*SIZE), + rect().center().y() + SIZE/4)); + path.closeSubpath(); + p.drawPath(path); + } +} + +void NavigationWidget::newParent() +{ + if (!parent()) + return; + + parent()->installEventFilter(this); + raise(); +} + +void NavigationWidget::viewClicked(const QPoint &pos) +{ + QRect prevRect(MARGIN, rect().center().y() - SIZE/2, SIZE, SIZE); + QRect nextRect(rect().right() - (MARGIN + SIZE), rect().center().y() + - SIZE/2, SIZE, SIZE); + + if (prevRect.contains(pos)) + emit prev(); + else if (nextRect.contains(pos)) + emit next(); +} + +#endif // Q_OS_ANDROID diff --git a/src/GUI/navigationwidget.h b/src/GUI/navigationwidget.h new file mode 100644 index 00000000..3f679946 --- /dev/null +++ b/src/GUI/navigationwidget.h @@ -0,0 +1,35 @@ +#ifndef NAVIGATIONWIDGET_H +#define NAVIGATIONWIDGET_H + +#include +#include "mapview.h" + +#ifdef Q_OS_ANDROID +class NavigationWidget : public QWidget +{ + Q_OBJECT + +public: + NavigationWidget(MapView *view); + + void enableNext(bool enable) {_showNext = enable; update();} + void enablePrev(bool enable) {_showPrev = enable; update();} + +signals: + void next(); + void prev(); + +private slots: + void viewClicked(const QPoint &pos); + +private: + bool eventFilter(QObject *obj, QEvent *ev); + bool event(QEvent *ev); + void paintEvent(QPaintEvent *ev); + void newParent(); + + bool _showPrev, _showNext; +}; +#endif // Q_OS_ANDROID + +#endif // NAVIGATIONWIDGET_H diff --git a/src/GUI/optionsdialog.cpp b/src/GUI/optionsdialog.cpp index 09e9394c..923f0027 100644 --- a/src/GUI/optionsdialog.cpp +++ b/src/GUI/optionsdialog.cpp @@ -26,8 +26,11 @@ #include "pluginparameters.h" #include "optionsdialog.h" - +#ifdef Q_OS_ANDROID +#define MENU_MARGIN 0 +#else // Q_OS_ANDROID #define MENU_MARGIN 20 +#endif // Q_OS_ANDROID #define MENU_ICON_SIZE 32 #ifdef Q_OS_MAC @@ -779,6 +782,11 @@ QWidget *OptionsDialog::createSystemPage() OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent) : QDialog(parent), _options(options), _units(units) { +#ifdef Q_OS_ANDROID + setWindowFlags(Qt::Window); + setWindowState(Qt::WindowFullScreen); +#endif /* Q_OS_ANDROID */ + QStackedWidget *pages = new QStackedWidget(); pages->addWidget(createAppearancePage()); pages->addWidget(createMapPage()); @@ -791,16 +799,25 @@ OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent) QListWidget *menu = new QListWidget(); menu->setIconSize(QSize(MENU_ICON_SIZE, MENU_ICON_SIZE)); - new QListWidgetItem(QIcon(APPEARANCE_ICON), tr("Appearance"), - menu); +#ifdef Q_OS_ANDROID + new QListWidgetItem(QIcon(APPEARANCE_ICON), QString(), menu); + new QListWidgetItem(QIcon(MAPS_ICON), QString(), menu); + new QListWidgetItem(QIcon(DATA_ICON), QString(), menu); + new QListWidgetItem(QIcon(POI_ICON), QString(), menu); + new QListWidgetItem(QIcon(DEM_ICON), QString(), menu); + new QListWidgetItem(QIcon(POSITION_ICON), QString(), menu); + new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), QString(), menu); + new QListWidgetItem(QIcon(SYSTEM_ICON), QString(), menu); +#else // Q_OS_ANDROID + new QListWidgetItem(QIcon(APPEARANCE_ICON), tr("Appearance"), menu); new QListWidgetItem(QIcon(MAPS_ICON), tr("Maps"), menu); new QListWidgetItem(QIcon(DATA_ICON), tr("Data"), menu); new QListWidgetItem(QIcon(POI_ICON), tr("POI"), menu); new QListWidgetItem(QIcon(DEM_ICON), tr("DEM"), menu); new QListWidgetItem(QIcon(POSITION_ICON), tr("Position"), menu); - new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), tr("Print & Export"), - menu); + new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), tr("Print & Export"), menu); new QListWidgetItem(QIcon(SYSTEM_ICON), tr("System"), menu); +#endif // Q_OS_ANDROID QHBoxLayout *contentLayout = new QHBoxLayout(); contentLayout->addWidget(menu); diff --git a/src/GUI/pdfexportdialog.cpp b/src/GUI/pdfexportdialog.cpp index 788fd1e9..b4f5a55e 100644 --- a/src/GUI/pdfexportdialog.cpp +++ b/src/GUI/pdfexportdialog.cpp @@ -18,9 +18,16 @@ PDFExportDialog::PDFExportDialog(PDFExport &exp, Units units, QWidget *parent) { int index; +#ifdef Q_OS_ANDROID + setWindowFlags(Qt::Window); + setWindowState(Qt::WindowFullScreen); +#endif /* Q_OS_ANDROID */ + _fileSelect = new FileSelectWidget(); +#ifndef Q_OS_ANDROID _fileSelect->setFilter(tr("PDF files") + " (*.pdf);;" + tr("All files") + " (*)"); +#endif // Q_OS_ANDROID _fileSelect->setFile(_export.fileName); _paperSize = new QComboBox(); @@ -102,6 +109,9 @@ PDFExportDialog::PDFExportDialog(PDFExport &exp, Units units, QWidget *parent) #else // Q_OS_MAC layout->addWidget(pageSetupBox); layout->addWidget(outputFileBox); +#ifdef Q_OS_ANDROID + layout->addStretch(); +#endif // Q_OS_ANDROID #endif // Q_OS_MAC layout->addWidget(buttonBox); setLayout(layout); diff --git a/src/GUI/pngexportdialog.cpp b/src/GUI/pngexportdialog.cpp index 5a4ebd5b..4aeff14c 100644 --- a/src/GUI/pngexportdialog.cpp +++ b/src/GUI/pngexportdialog.cpp @@ -15,9 +15,16 @@ PNGExportDialog::PNGExportDialog(PNGExport &exp, QWidget *parent) : QDialog(parent), _export(exp) { +#ifdef Q_OS_ANDROID + setWindowFlags(Qt::Window); + setWindowState(Qt::WindowFullScreen); +#endif /* Q_OS_ANDROID */ + _fileSelect = new FileSelectWidget(); +#ifndef Q_OS_ANDROID _fileSelect->setFilter(tr("PNG files") + " (*.png);;" + tr("All files") + " (*)"); +#endif // Q_OS_ANDROID _fileSelect->setFile(_export.fileName); _width = new QSpinBox(); @@ -78,6 +85,9 @@ PNGExportDialog::PNGExportDialog(PNGExport &exp, QWidget *parent) #else // Q_OS_MAC layout->addWidget(pageSetupBox); layout->addWidget(outputFileBox); +#ifdef Q_OS_ANDROID + layout->addStretch(); +#endif // Q_OS_ANDROID #endif // Q_OS_MAC layout->addWidget(buttonBox); setLayout(layout); diff --git a/src/GUI/powergraph.cpp b/src/GUI/powergraph.cpp index 21456eb2..b1576992 100644 --- a/src/GUI/powergraph.cpp +++ b/src/GUI/powergraph.cpp @@ -24,10 +24,17 @@ void PowerGraph::setInfo() if (_showTracks) { QLocale l(QLocale::system()); +#ifdef Q_OS_ANDROID + GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale() + yOffset(), + 'f', 1) + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Max"), l.toString(max() * yScale() + yOffset(), + 'f', 1) + UNIT_SPACE + yUnits()); +#else // Q_OS_ANDROID GraphView::addInfo(tr("Average"), l.toString(avg() * yScale() + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale() - + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); + + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); +#endif // Q_OS_ANDROID } else clearInfo(); } diff --git a/src/GUI/settings.h b/src/GUI/settings.h index fe8251fe..c595b376 100644 --- a/src/GUI/settings.h +++ b/src/GUI/settings.h @@ -1,6 +1,8 @@ #ifndef SETTINGS_H #define SETTINGS_H +#include + #define IMPERIAL_UNITS() \ (QLocale::system().measurementSystem() == QLocale::ImperialSystem) #define POSITION_PLUGIN() \ diff --git a/src/GUI/speedgraph.cpp b/src/GUI/speedgraph.cpp index 305981bd..a1049a3e 100644 --- a/src/GUI/speedgraph.cpp +++ b/src/GUI/speedgraph.cpp @@ -31,10 +31,17 @@ void SpeedGraph::setInfo() QString pu = (_units == Metric) ? tr("min/km") : (_units == Imperial) ? tr("min/mi") : tr("min/nmi"); +#ifdef Q_OS_ANDROID + GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale(), 'f', 1) + + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Max"), l.toString(max() * yScale(), 'f', 1) + + UNIT_SPACE + yUnits()); +#else // Q_OS_ANDROID GraphView::addInfo(tr("Average"), l.toString(avg() * yScale(), 'f', 1) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale(), 'f', 1) + UNIT_SPACE + yUnits()); +#endif // Q_OS_ANDROID GraphView::addInfo(tr("Pace"), pace + UNIT_SPACE + pu); } else clearInfo(); diff --git a/src/GUI/temperaturegraph.cpp b/src/GUI/temperaturegraph.cpp index e6b2e076..12ec98e9 100644 --- a/src/GUI/temperaturegraph.cpp +++ b/src/GUI/temperaturegraph.cpp @@ -24,12 +24,21 @@ void TemperatureGraph::setInfo() if (_showTracks) { QLocale l(QLocale::system()); +#ifdef Q_OS_ANDROID + GraphView::addInfo(tr("Avg"), l.toString(avg() * yScale() + yOffset(), + 'f', 1) + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Min"), l.toString(min() * yScale() + yOffset(), + 'f', 1) + UNIT_SPACE + yUnits()); + GraphView::addInfo(tr("Max"), l.toString(max() * yScale() + yOffset(), + 'f', 1) + UNIT_SPACE + yUnits()); +#else // Q_OS_ANDROID GraphView::addInfo(tr("Average"), l.toString(avg() * yScale() + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Minimum"), l.toString(min() * yScale() + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); GraphView::addInfo(tr("Maximum"), l.toString(max() * yScale() + yOffset(), 'f', 1) + UNIT_SPACE + yUnits()); +#endif // Q_OS_ANDROID } else clearInfo(); } diff --git a/src/GUI/thumbnail.cpp b/src/GUI/thumbnail.cpp index 6201c8a8..98b7da48 100644 --- a/src/GUI/thumbnail.cpp +++ b/src/GUI/thumbnail.cpp @@ -32,11 +32,21 @@ Thumbnail::Thumbnail(const QString &path, int limit, QWidget *parent) setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +#ifdef Q_OS_ANDROID + _path = path; +#else //Q_OS_ANDROID _path = QFileInfo(path).absoluteFilePath(); +#endif // Q_OS_ANDROID } void Thumbnail::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) +#ifdef Q_OS_ANDROID + QDesktopServices::openUrl(_path); +#else // Q_OS_ANDROID QDesktopServices::openUrl(QUrl::fromLocalFile(_path)); +#endif // Q_OS_ANDROID + + QLabel::mousePressEvent(event); } diff --git a/src/common/programpaths.cpp b/src/common/programpaths.cpp index 7c03595c..b437e261 100644 --- a/src/common/programpaths.cpp +++ b/src/common/programpaths.cpp @@ -19,65 +19,97 @@ #define TYP_FILE "style.typ" #define RENDERTHEME_FILE "style.xml" +#ifdef Q_OS_ANDROID +#define DATA_LOCATION QStandardPaths::GenericDataLocation +#else // Q_OS_ANDROID +#define DATA_LOCATION QStandardPaths::AppDataLocation +#endif // Q_OS_ANDROID + +#ifdef Q_OS_ANDROID +static QString assetsPath(const QString &path, const QString &dir) +{ + QDir pd(path); + + if (pd.isAbsolute() && pd.exists()) + return pd.absolutePath(); + else + return QString("assets://") + dir; +} +#endif // Q_OS_ANDROID QString ProgramPaths::mapDir(bool writable) { if (writable) - return QDir(QStandardPaths::writableLocation( - QStandardPaths::AppDataLocation)).filePath(MAP_DIR); + return QDir(QStandardPaths::writableLocation(DATA_LOCATION)) + .filePath(MAP_DIR); else - return QStandardPaths::locate(QStandardPaths::AppDataLocation, +#ifdef Q_OS_ANDROID + return assetsPath(QStandardPaths::locate(DATA_LOCATION, MAP_DIR, + QStandardPaths::LocateDirectory), MAP_DIR); +#else // Q_OS_ANDROID + return QStandardPaths::locate(DATA_LOCATION, MAP_DIR, QStandardPaths::LocateDirectory); +#endif // Q_OS_ANDROID } QString ProgramPaths::poiDir(bool writable) { if (writable) - return QDir(QStandardPaths::writableLocation( - QStandardPaths::AppDataLocation)).filePath(POI_DIR); + return QDir(QStandardPaths::writableLocation(DATA_LOCATION)) + .filePath(POI_DIR); else - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - POI_DIR, QStandardPaths::LocateDirectory); + return QStandardPaths::locate(DATA_LOCATION, POI_DIR, + QStandardPaths::LocateDirectory); } QString ProgramPaths::csvDir(bool writable) { if (writable) - return QDir(QStandardPaths::writableLocation( - QStandardPaths::AppDataLocation)).filePath(CSV_DIR); + return QDir(QStandardPaths::writableLocation(DATA_LOCATION)) + .filePath(CSV_DIR); else - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - CSV_DIR, QStandardPaths::LocateDirectory); +#ifdef Q_OS_ANDROID + return assetsPath(QStandardPaths::locate(DATA_LOCATION, CSV_DIR, + QStandardPaths::LocateDirectory), CSV_DIR); +#else // Q_OS_ANDROID + return QStandardPaths::locate(DATA_LOCATION, CSV_DIR, + QStandardPaths::LocateDirectory); +#endif // Q_OS_ANDROID } QString ProgramPaths::demDir(bool writable) { if (writable) - return QDir(QStandardPaths::writableLocation( - QStandardPaths::AppDataLocation)).filePath(DEM_DIR); + return QDir(QStandardPaths::writableLocation(DATA_LOCATION)) + .filePath(DEM_DIR); else - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - DEM_DIR, QStandardPaths::LocateDirectory); + return QStandardPaths::locate(DATA_LOCATION, DEM_DIR, + QStandardPaths::LocateDirectory); } QString ProgramPaths::styleDir(bool writable) { if (writable) - return QDir(QStandardPaths::writableLocation( - QStandardPaths::AppDataLocation)).filePath(STYLE_DIR); + return QDir(QStandardPaths::writableLocation(DATA_LOCATION)) + .filePath(STYLE_DIR); else - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - STYLE_DIR, QStandardPaths::LocateDirectory); + return QStandardPaths::locate(DATA_LOCATION, STYLE_DIR, + QStandardPaths::LocateDirectory); } QString ProgramPaths::symbolsDir(bool writable) { if (writable) - return QDir(QStandardPaths::writableLocation( - QStandardPaths::AppDataLocation)).filePath(SYMBOLS_DIR); + return QDir(QStandardPaths::writableLocation(DATA_LOCATION)) + .filePath(SYMBOLS_DIR); else - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - SYMBOLS_DIR, QStandardPaths::LocateDirectory); +#ifdef Q_OS_ANDROID + return assetsPath(QStandardPaths::locate(DATA_LOCATION, SYMBOLS_DIR, + QStandardPaths::LocateDirectory), SYMBOLS_DIR); +#else // Q_OS_ANDROID + return QStandardPaths::locate(DATA_LOCATION, SYMBOLS_DIR, + QStandardPaths::LocateDirectory); +#endif // Q_OS_ANDROID } QString ProgramPaths::tilesDir() @@ -88,36 +120,31 @@ QString ProgramPaths::tilesDir() QString ProgramPaths::translationsDir() { - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - TRANSLATIONS_DIR, QStandardPaths::LocateDirectory); + return QStandardPaths::locate(DATA_LOCATION, TRANSLATIONS_DIR, + QStandardPaths::LocateDirectory); } QString ProgramPaths::ellipsoidsFile() { - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - CSV_DIR "/" ELLIPSOID_FILE, QStandardPaths::LocateFile); + return QDir(csvDir()).filePath(ELLIPSOID_FILE); } QString ProgramPaths::gcsFile() { - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - CSV_DIR "/" GCS_FILE, QStandardPaths::LocateFile); + return QDir(csvDir()).filePath(GCS_FILE); } QString ProgramPaths::pcsFile() { - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - CSV_DIR "/" PCS_FILE, QStandardPaths::LocateFile); + return QDir(csvDir()).filePath(PCS_FILE); } QString ProgramPaths::typFile() { - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - STYLE_DIR "/" TYP_FILE, QStandardPaths::LocateFile); + return QDir(styleDir()).filePath(TYP_FILE); } QString ProgramPaths::renderthemeFile() { - return QStandardPaths::locate(QStandardPaths::AppDataLocation, - STYLE_DIR "/" RENDERTHEME_FILE, QStandardPaths::LocateFile); + return QDir(styleDir()).filePath(RENDERTHEME_FILE); } diff --git a/src/common/util.cpp b/src/common/util.cpp index 7a198f5a..e44a1439 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,9 +1,71 @@ #include #include #include +#ifdef Q_OS_ANDROID +#include +#include +#include +#include +#endif // Q_OS_ANDROID #include "util.h" +#ifdef Q_OS_ANDROID +static QString documentName(const QString &path) +{ + QJniEnvironment env; + + QJniObject urlString = QJniObject::fromString(path); + QJniObject uri = QJniObject::callStaticObjectMethod("android/net/Uri", + "parse", "(Ljava/lang/String;)Landroid/net/Uri;", + urlString.object()); + if (!uri.isValid()) { + env->ExceptionClear(); + return QString(); + } + QJniObject context = QNativeInterface::QAndroidApplication::context(); + if (!context.isValid()) { + env->ExceptionClear(); + return QString(); + } + QJniObject contentResolver = context.callObjectMethod( + "getContentResolver", "()Landroid/content/ContentResolver;"); + if (!contentResolver.isValid()) { + env->ExceptionClear(); + return QString(); + } + QJniObject columnName = QJniObject::getStaticObjectField( + "android/provider/MediaStore$MediaColumns", "DISPLAY_NAME"); + if (!columnName.isValid()) { + env->ExceptionClear(); + return QString(); + } + jobjectArray stringArray = env->NewObjectArray( + 1, env->FindClass("java/lang/String"), 0); + env->SetObjectArrayElement(stringArray, 0, columnName.object()); + QJniObject cursor = contentResolver.callObjectMethod("query", + "(Landroid/net/Uri;[Ljava/lang/String;Landroid/os/Bundle;" + "Landroid/os/CancellationSignal;)Landroid/database/Cursor;", + uri.object(), stringArray, 0, 0); + if (!cursor.isValid()) { + env->ExceptionClear(); + return QString(); + } + if (!cursor.callMethod("moveToFirst")) { + env->ExceptionClear(); + return QString(); + } + QJniObject str = cursor.callObjectMethod("getString", + "(I)Ljava/lang/String;", 0); + if (!str.isValid()) { + env->ExceptionClear(); + return QString(); + } + + return str.toString(); +} +#endif // Q_OS_ANDROID + int Util::str2int(const char *str, int len) { int res = 0; @@ -52,6 +114,28 @@ double Util::niceNum(double x, bool round) QString Util::file2name(const QString &path) { - QFileInfo fi(path); + QFileInfo fi(displayName(path)); return fi.baseName().replace('_', ' '); } + +QString Util::displayName(const QString &path) +{ +#ifdef Q_OS_ANDROID + QUrl url(path); + + // Not an Android URL, return standard filename. + if (url.scheme() != "content") { + QFileInfo fi(path); + return fi.fileName(); + // Directory browsing URLs. Those can not be translated using the Android + // content resolver but we can get the filename from the URL path. + } else if (url.path().startsWith("/tree/")) { + QFileInfo fi(url.fileName()); + return fi.fileName(); + // Translate all "regular" android URLs using the Android content resolver. + } else + return documentName(path); +#else + return path; +#endif // Q_OS_ANDROID +} diff --git a/src/common/util.h b/src/common/util.h index ccd6ca1c..2b1e2b44 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -8,6 +8,7 @@ namespace Util int str2int(const char *str, int len); double niceNum(double x, bool round); QString file2name(const QString &path); + QString displayName(const QString &path); } #endif // UTIL_H