Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a UI to open projects/datasets via native OS file picker on windows/linux/macos #5952

Merged
merged 5 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions images/images.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
<file>themes/qfield/nodpi/ic_chevron_up.svg</file>
<file>themes/qfield/nodpi/ic_arrow_left_white_24dp.svg</file>
<file>themes/qfield/nodpi/ic_opacity_black_24dp.svg</file>
<file>themes/qfield/nodpi/ic_open_black_24dp.svg</file>
<file>themes/qfield/nodpi/ic_common_angle_white_24dp.svg</file>
<file>themes/qfield/nodpi/ic_password_48dp.svg</file>
<file>themes/qfield/nodpi/ic_arrow_drop_down_48dp.svg</file>
Expand Down
4 changes: 4 additions & 0 deletions images/themes/qfield/nodpi/ic_open_black_24dp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 45 additions & 19 deletions src/core/platforms/android/androidplatformutilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ AndroidPlatformUtilities::AndroidPlatformUtilities()

PlatformUtilities::Capabilities AndroidPlatformUtilities::capabilities() const
{
PlatformUtilities::Capabilities capabilities = Capabilities() | NativeCamera | AdjustBrightness | CustomLocalDataPicker | CustomImport | CustomExport | CustomSend | FilePicker | VolumeKeys | UpdateProjectFromArchive;
PlatformUtilities::Capabilities capabilities = Capabilities() | NativeCamera | AdjustBrightness | CustomImport | CustomExport | CustomSend | FilePicker | VolumeKeys | UpdateProjectFromArchive;
#ifdef WITH_SENTRY
capabilities |= SentryFramework;
#endif
Expand Down Expand Up @@ -287,16 +287,29 @@ void AndroidPlatformUtilities::exportDatasetTo( const QString &path ) const

void AndroidPlatformUtilities::removeDataset( const QString &path ) const
{
if ( mActivity.isValid() )
bool allowed = false;
const QStringList allowedDirectories = QStringList() << applicationDirectory() << additionalApplicationDirectories();
for ( const QString &directory : allowedDirectories )
{
runOnAndroidMainThread( [path] {
auto activity = qtAndroidContext();
if ( activity.isValid() )
{
QJniObject pathJni = QJniObject::fromString( path );
activity.callMethod<void>( "removeDataset", "(Ljava/lang/String;)V", pathJni.object<jstring>() );
}
} );
if ( path.startsWith( directory ) )
{
allowed = true;
break;
}
}
if ( allowed )
{
if ( mActivity.isValid() )
{
runOnAndroidMainThread( [path] {
auto activity = qtAndroidContext();
if ( activity.isValid() )
{
QJniObject pathJni = QJniObject::fromString( path );
activity.callMethod<void>( "removeDataset", "(Ljava/lang/String;)V", pathJni.object<jstring>() );
}
} );
}
}
}

Expand Down Expand Up @@ -332,16 +345,29 @@ void AndroidPlatformUtilities::sendCompressedFolderTo( const QString &path ) con

void AndroidPlatformUtilities::removeFolder( const QString &path ) const
{
if ( mActivity.isValid() )
bool allowed = false;
const QStringList allowedDirectories = QStringList() << applicationDirectory() << additionalApplicationDirectories();
for ( const QString &directory : allowedDirectories )
{
runOnAndroidMainThread( [path] {
auto activity = qtAndroidContext();
if ( activity.isValid() )
{
QJniObject pathJni = QJniObject::fromString( path );
activity.callMethod<void>( "removeProjectFolder", "(Ljava/lang/String;)V", pathJni.object<jstring>() );
}
} );
if ( path.startsWith( directory ) )
{
allowed = true;
break;
}
}
if ( allowed )
{
if ( mActivity.isValid() )
{
runOnAndroidMainThread( [path] {
auto activity = qtAndroidContext();
if ( activity.isValid() )
{
QJniObject pathJni = QJniObject::fromString( path );
activity.callMethod<void>( "removeProjectFolder", "(Ljava/lang/String;)V", pathJni.object<jstring>() );
}
} );
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/platforms/ios/iosplatformutilities.mm
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ - (void)documentInteractionControllerDidEndPreview:

PlatformUtilities::Capabilities IosPlatformUtilities::capabilities() const {
PlatformUtilities::Capabilities capabilities =
Capabilities() | NativeCamera | AdjustBrightness | CustomLocalDataPicker;
Capabilities() | NativeCamera | AdjustBrightness | FilePicker;
#ifdef WITH_SENTRY
capabilities |= SentryFramework;
#endif
Expand Down
47 changes: 44 additions & 3 deletions src/core/platforms/platformutilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <QDir>
#include <QFileDialog>
#include <QMargins>
#include <QMessageBox>
#include <QQuickWindow>
#include <QStandardPaths>
#include <QStorageInfo>
Expand All @@ -58,7 +59,7 @@ PlatformUtilities::~PlatformUtilities()

PlatformUtilities::Capabilities PlatformUtilities::capabilities() const
{
PlatformUtilities::Capabilities capabilities = FilePicker;
PlatformUtilities::Capabilities capabilities = PlatformUtilities::Capabilities() | FilePicker | NativeLocalDataPicker;
#if WITH_SENTRY
capabilities |= SentryFramework;
#endif
Expand Down Expand Up @@ -274,12 +275,52 @@ void PlatformUtilities::sendCompressedFolderTo( const QString &path ) const

void PlatformUtilities::removeDataset( const QString &path ) const
{
Q_UNUSED( path )
bool allowed = false;
const QStringList allowedDirectories = QStringList() << applicationDirectory() << additionalApplicationDirectories();
for ( const QString &directory : allowedDirectories )
{
if ( path.startsWith( directory ) )
{
allowed = true;
break;
}
}
if ( allowed )
{
if ( QMessageBox::warning( nullptr,
tr( "Removal Confirmation" ),
tr( "The dataset will be deleted, proceed with removal?" ),
QMessageBox::StandardButtons() | QMessageBox::Ok | QMessageBox::Abort )
== QMessageBox::Ok )
{
QFile::moveToTrash( path );
}
}
}

void PlatformUtilities::removeFolder( const QString &path ) const
{
Q_UNUSED( path )
bool allowed = false;
const QStringList allowedDirectories = QStringList() << applicationDirectory() << additionalApplicationDirectories();
for ( const QString &directory : allowedDirectories )
{
if ( path.startsWith( directory ) )
{
allowed = true;
break;
}
}
if ( allowed )
{
if ( QMessageBox::warning( nullptr,
tr( "Removal Confirmation" ),
tr( "The project folder will be deleted, proceed with removal?" ),
QMessageBox::StandardButtons() | QMessageBox::Ok | QMessageBox::Abort )
== QMessageBox::Ok )
{
QFile::moveToTrash( path );
}
}
}

ResourceSource *PlatformUtilities::getCameraPicture( const QString &, const QString &, const QString &, QObject * )
Expand Down
2 changes: 1 addition & 1 deletion src/core/platforms/platformutilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class QFIELD_CORE_EXPORT PlatformUtilities : public QObject
NativeCamera = 1, //!< Native camera handling support
AdjustBrightness = 1 << 1, //!< Screen brightness adjustment support
SentryFramework = 1 << 2, //!< Sentry framework support
CustomLocalDataPicker = 1 << 3, //!< Custom QML local data picker support
NativeLocalDataPicker = 1 << 3, //!< Native local data picker support
CustomImport = 1 << 4, //!< Import project and dataset support
CustomExport = 1 << 5, //!< Export project and dataset support
CustomSend = 1 << 6, //!< Send/share files support
Expand Down
40 changes: 37 additions & 3 deletions src/qml/QFieldLocalDataPickerScreen.qml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Page {
property string itemPath: ItemPath
property bool itemIsFavorite: ItemIsFavorite
property bool itemMenuLoadable: !projectFolderView && (ItemMetaType === LocalFilesModel.Project || ItemMetaType === LocalFilesModel.Dataset)
property bool itemMenuVisible: ((ItemType === LocalFilesModel.SimpleFolder || ItemMetaType == LocalFilesModel.File) && table.model.currentPath !== 'root') || ((platformUtilities.capabilities & PlatformUtilities.CustomExport || platformUtilities.capabilities & PlatformUtilities.CustomSend) && (ItemMetaType === LocalFilesModel.Dataset)) || (ItemMetaType === LocalFilesModel.Dataset && ItemType === LocalFilesModel.RasterDataset && cloudProjectsModel.currentProjectId)
property bool itemMenuVisible: ((ItemType === LocalFilesModel.SimpleFolder || ItemMetaType == LocalFilesModel.Dataset || ItemMetaType == LocalFilesModel.File) && table.model.currentPath !== 'root') || ((platformUtilities.capabilities & PlatformUtilities.CustomExport || platformUtilities.capabilities & PlatformUtilities.CustomSend) && (ItemMetaType === LocalFilesModel.Dataset)) || (ItemMetaType === LocalFilesModel.Dataset && ItemType === LocalFilesModel.RasterDataset && cloudProjectsModel.currentProjectId)

width: parent ? parent.width : undefined
height: line.height
Expand Down Expand Up @@ -325,6 +325,37 @@ Page {
}
}

Connections {
target: nativeLocalDataPickerButton.__projectSource

function onProjectOpened(path) {
finished(true);
iface.loadFile(path);
}
}

QfToolButton {
id: nativeLocalDataPickerButton
round: true

property ProjectSource __projectSource

visible: platformUtilities.capabilities & PlatformUtilities.NativeLocalDataPicker && table.model.currentPath === 'root'

anchors.bottom: actionButton.top
anchors.right: parent.right
anchors.bottomMargin: 4
anchors.rightMargin: 10

bgcolor: Theme.mainColor
iconSource: Theme.getThemeVectorIcon("ic_open_black_24dp")
iconColor: Theme.toolButtonColor

onClicked: {
__projectSource = platformUtilities.openProject(this);
}
}

QfToolButton {
id: actionButton
round: true
Expand All @@ -341,6 +372,7 @@ Page {

bgcolor: Theme.mainColor
iconSource: Theme.getThemeVectorIcon("ic_add_white_24dp")
iconColor: Theme.toolButtonColor

onClicked: {
var xy = mapToItem(mainWindow.contentItem, actionButton.width, actionButton.height);
Expand Down Expand Up @@ -451,7 +483,7 @@ Page {
}

MenuSeparator {
enabled: toggleFavoriteState.visible
enabled: toggleFavoriteState.visible && (exportFolderTo.visible || sendCompressedFolderTo.visible || uploadFolderToWebdav.visible || downloadFolderFromWebdav.visible)
visible: enabled
width: parent.width
height: enabled ? undefined : 0
Expand Down Expand Up @@ -526,7 +558,7 @@ Page {
}

MenuSeparator {
enabled: removeProjectFolder.visible
enabled: removeDataset.visible || removeProjectFolder.visible
visible: enabled
width: parent.width
height: enabled ? undefined : 0
Expand All @@ -545,6 +577,7 @@ Page {
text: qsTr("Remove dataset")
onTriggered: {
platformUtilities.removeDataset(itemMenu.itemPath);
table.model.resetToPath(table.model.currentPath);
}
}

Expand All @@ -561,6 +594,7 @@ Page {
text: qsTr("Remove folder")
onTriggered: {
platformUtilities.removeFolder(itemMenu.itemPath);
table.model.resetToPath(table.model.currentPath);
}
}
}
Expand Down
11 changes: 1 addition & 10 deletions src/qml/qgismobileapp.qml
Original file line number Diff line number Diff line change
Expand Up @@ -3918,7 +3918,6 @@ ApplicationWindow {
model: RecentProjectListModel {
id: recentProjectListModel
}
property ProjectSource __projectSource

anchors.fill: parent

Expand Down Expand Up @@ -4113,14 +4112,6 @@ ApplicationWindow {
}
}

Connections {
target: welcomeScreen.__projectSource

function onProjectOpened(path) {
iface.loadFile(path);
}
}

// ! MODELS !
FeatureModel {
id: geometryEditingFeature
Expand Down Expand Up @@ -4180,7 +4171,7 @@ ApplicationWindow {
standardButtons: Dialog.Yes | Dialog.No
}

Dialog {
QfDialog {
id: cancelAlgorithmDialog
parent: mainWindow.contentItem

Expand Down
Loading