Skip to content

Commit

Permalink
Merge pull request #4670 from opengisch/update_local_project
Browse files Browse the repository at this point in the history
Update local project from archive functionality
  • Loading branch information
nirvn authored Oct 27, 2023
2 parents e539400 + b55ee16 commit 881e154
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 99 deletions.
24 changes: 0 additions & 24 deletions platform/android/res/menu/project_folder_menu.xml

This file was deleted.

17 changes: 0 additions & 17 deletions platform/android/res/menu/project_item_menu.xml

This file was deleted.

23 changes: 0 additions & 23 deletions platform/android/res/menu/project_menu.xml

This file was deleted.

12 changes: 1 addition & 11 deletions platform/android/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@
<string name="grant">Allow</string>
<string name="deny_always">Deny Permanently</string>
<string name="deny_once">Deny Once</string>
<string name="import_project_folder">Import Project from Folder</string>
<string name="import_project_archive">Import Project from ZIP</string>
<string name="import_dataset">Import Dataset(s)</string>
<string name="usb_cable_help">Storage Management Help</string>
<string name="import_error">Import Error</string>
<string name="import_dataset_error">The selected dataset(s) could not be imported properly.</string>
<string name="import_project_archive_error">The selected project archive was not imported properly.</string>
Expand All @@ -52,13 +48,6 @@
<string name="import_overwrite_cancel">Cancel</string>
<string name="export_error">Export Error</string>
<string name="export_to_folder_error">The selected project folder was not exported properly.</string>
<string name="send_to">Send to...</string>
<string name="send_compressed_to">Send Compressed Folder to...</string>
<string name="export_to_folder">Export to Folder...</string>
<string name="add_to_favorite">Add to Favorites</string>
<string name="remove_from_favorite">Remove from Favorites</string>
<string name="remove_dataset">Remove Dataset</string>
<string name="remove_folder">Remove Project Folder</string>
<string name="delete_confirm_title">Removal Confirmation</string>
<string name="delete_confirm_dataset">The dataset will be permamently deleted, proceed with removal?</string>
<string name="delete_confirm_folder">The project folder will be permamently deleted, proceed with removal?</string>
Expand All @@ -70,6 +59,7 @@
<string name="storage_information_dismiss">I\'ll read later</string>
<string name="import_project_wait">Please wait while QField is importing the project</string>
<string name="import_dataset_wait">Please wait while QField is importing the dataset(s)</string>
<string name="update_project_wait">Please wait while QField is updating the local project</string>
<string name="upload_pending_attachments">Currently uploading attachments to QFieldCloud</string>
<string name="operation_unsupported">Operation Unsupported</string>
<string name="import_operation_unsupported">Your device does not support this import operation.</string>
Expand Down
109 changes: 100 additions & 9 deletions platform/android/src/ch/opengis/qfield/QFieldActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

public class QFieldActivity extends QtActivity {

private static final int IMPORT_DATASET = 300;
private static final int IMPORT_PROJECT_FOLDER = 301;
private static final int IMPORT_PROJECT_ARCHIVE = 302;

private static final int UPDATE_PROJECT_FROM_ARCHIVE = 400;

private static final int EXPORT_TO_FOLDER = 500;

private SharedPreferences sharedPreferences;
private SharedPreferences.Editor sharedPreferenceEditor;
private ProgressDialog progressDialog;
Expand All @@ -112,6 +120,7 @@ public class QFieldActivity extends QtActivity {
private float originalBrightness;
private boolean handleVolumeKeys = false;
private String pathsToExport;
private String projectPath;
private double sceneTopMargin = 0;
private double sceneBottomMargin = 0;

Expand Down Expand Up @@ -608,7 +617,7 @@ private void triggerImportDatasets() {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setType("*/*");
try {
startActivityForResult(intent, R.id.import_dataset);
startActivityForResult(intent, IMPORT_DATASET);
} catch (ActivityNotFoundException e) {
displayAlertDialog(
getString(R.string.operation_unsupported),
Expand All @@ -625,7 +634,7 @@ private void triggerImportProjectFolder() {
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
try {
startActivityForResult(intent, R.id.import_project_folder);
startActivityForResult(intent, IMPORT_PROJECT_FOLDER);
} catch (ActivityNotFoundException e) {
displayAlertDialog(
getString(R.string.operation_unsupported),
Expand All @@ -643,7 +652,26 @@ private void triggerImportProjectArchive() {
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.setType("application/zip");
try {
startActivityForResult(intent, R.id.import_project_archive);
startActivityForResult(intent, IMPORT_PROJECT_ARCHIVE);
} catch (ActivityNotFoundException e) {
displayAlertDialog(
getString(R.string.operation_unsupported),
getString(R.string.import_operation_unsupported));
Log.w("QField", "No activity found for ACTION_OPEN_DOCUMENT.");
}
return;
}

private void triggerUpdateProjectFromArchive(String path) {
projectPath = path;
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.setType("application/zip");
try {
startActivityForResult(intent, UPDATE_PROJECT_FROM_ARCHIVE);
} catch (ActivityNotFoundException e) {
displayAlertDialog(
getString(R.string.operation_unsupported),
Expand Down Expand Up @@ -695,7 +723,7 @@ private void exportToFolder(String paths) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
try {
startActivityForResult(intent, R.id.export_to_folder);
startActivityForResult(intent, EXPORT_TO_FOLDER);
} catch (ActivityNotFoundException e) {
displayAlertDialog(
getString(R.string.operation_unsupported),
Expand Down Expand Up @@ -948,6 +976,54 @@ public void run() {
});
}

void updateProjectFromArchive(Uri archiveUri) {
File externalFilesDir = getExternalFilesDir(null);
if (externalFilesDir == null) {
return;
}

ProgressDialog progressDialog =
new ProgressDialog(this, R.style.DialogTheme);
progressDialog.setMessage(getString(R.string.update_project_wait));
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
progressDialog.show();

Context context = getApplication().getApplicationContext();
ContentResolver resolver = getContentResolver();

executorService.execute(new Runnable() {
@Override
public void run() {
DocumentFile documentFile =
DocumentFile.fromSingleUri(context, archiveUri);

String projectFolder =
new File(projectPath).getParentFile().getAbsolutePath() +
"/";
boolean imported = false;
try {
InputStream input = resolver.openInputStream(archiveUri);
imported = QFieldUtils.zipToFolder(input, projectFolder);
} catch (Exception e) {
e.printStackTrace();

if (!isFinishing()) {
displayAlertDialog(
getString(R.string.import_error),
getString(R.string.import_project_archive_error));
}
}

progressDialog.dismiss();
if (imported) {
// Trigger a project re-load
openProject(projectPath);
}
}
});
}

private void checkPermissions() {
List<String> permissionsList = new ArrayList<String>();
if (ContextCompat.checkSelfPermission(
Expand Down Expand Up @@ -1093,8 +1169,7 @@ public void onClick(

protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == R.id.import_dataset &&
resultCode == Activity.RESULT_OK) {
if (requestCode == IMPORT_DATASET && resultCode == Activity.RESULT_OK) {
Log.d("QField", "handling import dataset(s)");
File externalFilesDir = getExternalFilesDir(null);
if (externalFilesDir == null || data == null) {
Expand Down Expand Up @@ -1159,7 +1234,7 @@ public void onClick(DialogInterface dialog, int id) {
} else {
importDatasets(datasetUris);
}
} else if (requestCode == R.id.import_project_folder &&
} else if (requestCode == IMPORT_PROJECT_FOLDER &&
resultCode == Activity.RESULT_OK) {
Log.d("QField", "handling import project folder");
File externalFilesDir = getExternalFilesDir(null);
Expand Down Expand Up @@ -1199,7 +1274,7 @@ public void onClick(DialogInterface dialog, int id) {
} else {
importProjectFolder(uri);
}
} else if (requestCode == R.id.import_project_archive &&
} else if (requestCode == IMPORT_PROJECT_ARCHIVE &&
resultCode == Activity.RESULT_OK) {
Log.d("QField", "handling import project archive");
File externalFilesDir = getExternalFilesDir(null);
Expand Down Expand Up @@ -1248,7 +1323,23 @@ public void onClick(DialogInterface dialog, int id) {
} else {
importProjectArchive(uri);
}
} else if (requestCode == R.id.export_to_folder &&
} else if (requestCode == UPDATE_PROJECT_FROM_ARCHIVE &&
resultCode == Activity.RESULT_OK) {
Log.d("QField", "handling updating project from archive");
File externalFilesDir = getExternalFilesDir(null);
if (externalFilesDir == null || data == null) {
return;
}

Uri uri = data.getData();
Context context = getApplication().getApplicationContext();
ContentResolver resolver = getContentResolver();

DocumentFile documentFile =
DocumentFile.fromSingleUri(context, uri);

updateProjectFromArchive(uri);
} else if (requestCode == EXPORT_TO_FOLDER &&
resultCode == Activity.RESULT_OK) {
Log.d("QField", "handling export to folder");

Expand Down
21 changes: 20 additions & 1 deletion src/core/platforms/android/androidplatformutilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ AndroidPlatformUtilities::AndroidPlatformUtilities()

PlatformUtilities::Capabilities AndroidPlatformUtilities::capabilities() const
{
PlatformUtilities::Capabilities capabilities = Capabilities() | NativeCamera | AdjustBrightness | CustomLocalDataPicker | CustomImport | CustomExport | CustomSend | FilePicker | VolumeKeys;
PlatformUtilities::Capabilities capabilities = Capabilities() | NativeCamera | AdjustBrightness | CustomLocalDataPicker | CustomImport | CustomExport | CustomSend | FilePicker | VolumeKeys | UpdateProjectFromArchive;
#ifdef WITH_SENTRY
capabilities |= SentryFramework;
#endif
Expand Down Expand Up @@ -251,6 +251,25 @@ void AndroidPlatformUtilities::importDatasets() const
}
}

void AndroidPlatformUtilities::updateProjectFromArchive( const QString &projectPath ) const
{
if ( mActivity.isValid() )
{
runOnAndroidMainThread( [projectPath] {
auto activity = qtAndroidContext();
if ( activity.isValid() )
{
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QAndroidJniObject projectPathJni = QAndroidJniObject::fromString( projectPath );
#else
QJniObject projectPathJni = QJniObject::fromString( projectPath );
#endif
activity.callMethod<void>( "triggerUpdateProjectFromArchive", "(Ljava/lang/String;)V", projectPathJni.object<jstring>() );
}
} );
}
}

void AndroidPlatformUtilities::sendDatasetTo( const QString &path ) const
{
if ( mActivity.isValid() )
Expand Down
2 changes: 2 additions & 0 deletions src/core/platforms/android/androidplatformutilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class AndroidPlatformUtilities : public PlatformUtilities
void importProjectArchive() const override;
void importDatasets() const override;

void updateProjectFromArchive( const QString &projectPath ) const override;

void sendDatasetTo( const QString &path ) const override;
void exportDatasetTo( const QString &path ) const override;
void removeDataset( const QString &path ) const override;
Expand Down
5 changes: 5 additions & 0 deletions src/core/platforms/platformutilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ void PlatformUtilities::importProjectArchive() const
void PlatformUtilities::importDatasets() const
{}

void PlatformUtilities::updateProjectFromArchive( const QString &projectPath ) const
{
Q_UNUSED( projectPath )
}

void PlatformUtilities::exportFolderTo( const QString &path ) const
{
Q_UNUSED( path )
Expand Down
27 changes: 17 additions & 10 deletions src/core/platforms/platformutilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,17 @@ class QFIELD_CORE_EXPORT PlatformUtilities : public QObject
public:
enum Capability
{
NoCapabilities = 0, //!< No capabilities
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
CustomImport = 1 << 4, //!< Import project and dataset support
CustomExport = 1 << 5, //!< Export project and dataset support
CustomSend = 1 << 6, //!< Send/share files support
FilePicker = 1 << 7, //!< File picker support
VolumeKeys = 1 << 8, //!< Volume keys handling support
NoCapabilities = 0, //!< No capabilities
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
CustomImport = 1 << 4, //!< Import project and dataset support
CustomExport = 1 << 5, //!< Export project and dataset support
CustomSend = 1 << 6, //!< Send/share files support
FilePicker = 1 << 7, //!< File picker support
VolumeKeys = 1 << 8, //!< Volume keys handling support
UpdateProjectFromArchive = 1 << 9, //!< Update local project from a ZIP archive support
};
Q_DECLARE_FLAGS( Capabilities, Capability )
Q_FLAGS( Capabilities )
Expand Down Expand Up @@ -130,6 +131,12 @@ class QFIELD_CORE_EXPORT PlatformUtilities : public QObject
//! Requests and imports one or more datasets into QField's application directory action
Q_INVOKABLE virtual void importDatasets() const;

/**
* Update a local project content from a user-picked archive file action
* \param projectPath the project file path
*/
Q_INVOKABLE virtual void updateProjectFromArchive( const QString &projectPath ) const;

//! Exports a folder \a path to a user-specified location
Q_INVOKABLE virtual void exportFolderTo( const QString &path ) const;
//! Exports a dataset \a path to a user-specified location
Expand Down
Loading

1 comment on commit 881e154

@qfield-fairy
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.