From 6c7bf6c1e7ccb32b263c58daf5982482a142ea0d Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Fri, 16 Mar 2018 14:49:03 +0100 Subject: [PATCH 01/22] Added iCloud support to Info.plist --- ios/Info.tmp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ios/Info.tmp b/ios/Info.tmp index 49e99200..cd941368 100644 --- a/ios/Info.tmp +++ b/ios/Info.tmp @@ -88,5 +88,17 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSUbiquitousContainers + + iCloud.cz.nic.mobile-datovka + + NSUbiquitousContainerIsDocumentScopePublic + + NSUbiquitousContainerSupportedFolderLevels + Any + NSUbiquitousContainerName + Datovka + + -- GitLab From b06da449a91d388172513972385ec2c2e6789f95 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Fri, 16 Mar 2018 14:59:32 +0100 Subject: [PATCH 02/22] Added iCloud objectiveC helper object --- ios/ios.pri | 2 + ios/src/icloud_helper.h | 83 +++++++++++++++++ ios/src/icloud_helper.mm | 192 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 ios/src/icloud_helper.h create mode 100644 ios/src/icloud_helper.mm diff --git a/ios/ios.pri b/ios/ios.pri index 20ef0c8a..9c358d9b 100644 --- a/ios/ios.pri +++ b/ios/ios.pri @@ -11,6 +11,7 @@ HEADERS += \ ios/src/app_delegate.h \ ios/src/doc_view_controller.h \ ios/src/qt_app_delegate.h \ + ios/src/icloud_helper.h \ ios/src/ios_file_opener.h \ ios/src/send_email_controller.h \ ios/src/url_opener.h @@ -18,6 +19,7 @@ HEADERS += \ OBJECTIVE_SOURCES += \ ios/src/app_delegate.mm \ ios/src/doc_view_controller.mm \ + ios/src/icloud_helper.mm \ ios/src/ios_file_opener.mm \ ios/src/send_email_controller.mm \ ios/src/url_opener.mm diff --git a/ios/src/icloud_helper.h b/ios/src/icloud_helper.h new file mode 100644 index 00000000..ad739584 --- /dev/null +++ b/ios/src/icloud_helper.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#pragma once + +#include +#include +#include + +/*! + * @brief Used for iCloud interaction on iOS. + */ +class ICloudHelper : public QObject { + Q_OBJECT + +public: + + /*! + * @brief Constructor. + * + * @param[in] parent Parent object. + */ + ICloudHelper(QObject *parent = Q_NULLPTR); + + /*! + * @brief Check if iCloud is on and active. + * + * @return Return true if success. + */ + bool isCloudOn(void); + + /*! + * @brief Store files to iCloud. + * + * @param[in] srcFilePaths List of file paths to be saved into iCloud. + * @param[in] targetPath Target iCloud path where files will store. + */ + void storeFilesToCloud(const QStringList &srcFilePaths, + const QString &targetPath); + +private: + + /*! + * @brief Return state describing what happened. + */ + enum ICloudResult { + ICLOUD_NOT_ON = 0, /*!< iCloud is not avalilable/turn on. */ + ICLOUD_FILE_UPLOAD_SUCCESS, /*!< File upload was successful. */ + ICLOUD_FILE_EXISTS, /*!< File exists on iCloud. */ + ICLOUD_FILE_UPLOAD_ERROR, /*!< File upload failed. */ + ICLOUD_TARGET_SAVE_DIR_ERROR /*!< Target dir is missing or not created. */ + }; + + /*! + * @brief Upload single file into iCloud. + * + * @param[in] srcFilePath Source file path. + * @param[in] destFilePath iCloud target path. + * @return Return operation error/success code. + */ + ICloudResult uploadFileToCloud(const QString &srcFilePath, + const QString &destFilePath); +}; diff --git a/ios/src/icloud_helper.mm b/ios/src/icloud_helper.mm new file mode 100644 index 00000000..fb9cbd2f --- /dev/null +++ b/ios/src/icloud_helper.mm @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#include +#include +#include +#include + +#ifdef Q_OS_IOS + +#include "ios/src/icloud_helper.h" +#include "ios/src/doc_view_controller.h" + +#define ICLOUD_DATOVKA_CONTAINER_NAME "Datovka" + +#endif /* Q_OS_IOS */ + +ICloudHelper::ICloudHelper(QObject *parent) + : QObject(parent) +{ +} + +bool ICloudHelper::isCloudOn(void) +{ +#ifdef Q_OS_IOS + + return ([[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]); + +#endif /* Q_OS_IOS */ + + return false; +} + +void ICloudHelper::storeFilesToCloud(const QStringList &srcFilePaths, + const QString &targetPath) +{ +#ifdef Q_OS_IOS + + /* Is iCloud configured and available? */ + if (!isCloudOn()) { + qCritical() << "Unable to access iCloud!"; + QMessageBox::critical(Q_NULLPTR, tr("iCloud error"), + tr("Unable to access iCloud Account. Open the Settings app and enter your Apple ID into iCloud settings."), + QMessageBox::Ok); + return; + } + + /* Upload files to iCloud */ + bool success = true; + QStringList errorUploads; + QString filePath; + + foreach (filePath, srcFilePaths) { + + ICloudResult retCode = uploadFileToCloud(filePath, targetPath); + + QFileInfo fi(filePath); + + switch (retCode) { + case ICloudHelper::ICLOUD_NOT_ON: + success &= false; + errorUploads.append(tr("Unable to access iCloud!")); + goto finish; + break; + case ICloudHelper::ICLOUD_TARGET_SAVE_DIR_ERROR: + success &= false; + errorUploads.append(tr("Cannot create subdir '%1' in iCloud.").arg(targetPath)); + goto finish; + break; + case ICloudHelper::ICLOUD_FILE_EXISTS: + success &= false; + errorUploads.append(tr("File '%1' already exists in iCloud.").arg(fi.fileName())); + break; + case ICloudHelper::ICLOUD_FILE_UPLOAD_ERROR: + success &= false; + errorUploads.append(tr("File '%1' upload failed.").arg(fi.fileName())); + break; + case ICloudHelper::ICLOUD_FILE_UPLOAD_SUCCESS: + errorUploads.append(tr("File '%1' has been uploaded in iCloud.").arg(fi.fileName())); + break; + default: + break; + } + } + +finish: + + /* Delete files and source dir */ + QFileInfo fi(filePath); + QDir dir(fi.absolutePath()); + dir.removeRecursively(); + + /* Show final notification */ + if (success) { + qCritical() << "Files have been saved to iCloud."; + QMessageBox::critical(Q_NULLPTR, tr("iCloud saving"), + tr("Files have been saved to iCloud.") + "\n\n" + + tr("Path: '%1'").arg(QString(ICLOUD_DATOVKA_CONTAINER_NAME) + + "/" + targetPath), + QMessageBox::Ok); + } else { + qCritical() << "Files upload to iCloud failed!"; + QString txt; + foreach (const QString &error, errorUploads) { + txt += "\n" + error; + } + QMessageBox::critical(Q_NULLPTR, + tr("iCloud saving problem"), + tr("Files have not been saved!") + "\n" + txt, + QMessageBox::Ok); + } + +#else /* !Q_OS_IOS */ + + Q_UNUSED(srcFilePaths); + Q_UNUSED(targetPath); + +#endif /* Q_OS_IOS */ +} + +ICloudHelper::ICloudResult ICloudHelper::uploadFileToCloud( + const QString &srcFilePath, const QString &destFilePath) +{ +#ifdef Q_OS_IOS + + NSURL *baseURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; + if (!baseURL) { + NSLog(@"iCloud: Unable to access iCloud."); + return ICLOUD_NOT_ON; + } + + // Create iCloud target path + NSURL *documentURL = [baseURL URLByAppendingPathComponent:@"Documents"]; + NSURL *messageURL = [documentURL URLByAppendingPathComponent:destFilePath.toNSString()]; + + // Create subdirectorues on iCloud + NSError *error = nil; + if (![[NSFileManager defaultManager] createDirectoryAtURL:messageURL + withIntermediateDirectories:YES attributes:nil error:&error]) { + NSLog(@"iCloud: Create message subdirectories error: %@", error); + return ICLOUD_TARGET_SAVE_DIR_ERROR; + } + + // Create target upload path for iCloud + NSURL *sourceFileUrl = [NSURL fileURLWithPath:srcFilePath.toNSString()]; + NSString *fileName = [srcFilePath.toNSString() lastPathComponent]; + NSURL *fileURL = [messageURL URLByAppendingPathComponent:fileName]; + + // Upload file to target directory on iCloud + if ([[NSFileManager defaultManager] setUbiquitous:YES + itemAtURL:sourceFileUrl destinationURL:fileURL error:&error]) { + NSLog(@"iCloud: File %@ has been uploaded.", fileName); + return ICLOUD_FILE_UPLOAD_SUCCESS; + } else { + // Code 516 = file exists in the iCloud. See NSFileManager error codes. + if (error.code == NSFileWriteFileExistsError) { + NSLog(@"iCloud: File with the same name already exists: %@", fileName); + return ICLOUD_FILE_EXISTS; + } else { + NSLog(@"iCloud: Error code: %zd", error.code); + NSLog(@"iCloud: %@", error); + return ICLOUD_FILE_UPLOAD_ERROR; + } + } + +#else /* !Q_OS_IOS */ + + Q_UNUSED(srcFilePath); + Q_UNUSED(destFilePath); + +#endif /* Q_OS_IOS */ +} -- GitLab From ca609eb38238d01a78d8b28aa8316ea300ad854a Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Fri, 16 Mar 2018 15:03:39 +0100 Subject: [PATCH 03/22] Added upload files to iCloud --- src/files.cpp | 41 +++++++++++++++++++++++++++++++++++------ src/io/filesystem.cpp | 5 ++--- src/io/filesystem.h | 9 +++++---- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index 2cf0f8f4..72b39336 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -27,9 +27,11 @@ #include #include + #if defined (Q_OS_ANDROID) #include "android/src/android_io.h" #endif +#include "ios/src/icloud_helper.h" #include "ios/src/url_opener.h" #include "src/auxiliaries/email_helper.h" #include "src/common.h" @@ -706,13 +708,27 @@ void Files::saveMsgFilesToDisk(const QString &userName, QString filePath(appMsgAttachDirPath(msgIdStr)); - QString destPath; + QString destFilePath; + QStringList destFilePaths; foreach (const Isds::Document &document, documents) { - destPath = writeFile(filePath, document.fileDescr(), + destFilePath = writeFile(filePath, document.fileDescr(), document.binaryContent()); + if (!destFilePath.isEmpty()) { + destFilePaths.append(destFilePath); + } } - attachmentSavingNotification(destPath); +#ifndef Q_OS_IOS + + attachmentSavingNotification(destFilePath); + +#else + + ICloudHelper iCloudHelper; + iCloudHelper.storeFilesToCloud(destFilePaths, joinDirs(userName, + msgIdStr)); + +#endif } void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant, @@ -730,16 +746,29 @@ void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant, QString targetPath(appMsgAttachDirPath(msgIdStr)); - QString destPath; + QString destFilePath; + QStringList destFilePaths; for (int row = 0; row < attachModel->rowCount(); ++row) { QModelIndex idx(attachModel->index(row)); - destPath = writeFile(targetPath, attachModel->data(idx, + destFilePath = writeFile(targetPath, attachModel->data(idx, FileListModel::ROLE_FILE_NAME).toString(), attachModel->data(idx, FileListModel::ROLE_BINARY_DATA).toByteArray()); + if (!destFilePath.isEmpty()) { + destFilePaths.append(destFilePath); + } } - attachmentSavingNotification(destPath); +#ifndef Q_OS_IOS + + attachmentSavingNotification(destFilePath); + +#else + + ICloudHelper iCloudHelper; + iCloudHelper.storeFilesToCloud(destFilePaths, msgIdStr); + +#endif } void Files::deleteTmpFileFromStorage(const QString &filePath) diff --git a/src/io/filesystem.cpp b/src/io/filesystem.cpp index 265d1e3f..81188aad 100644 --- a/src/io/filesystem.cpp +++ b/src/io/filesystem.cpp @@ -40,10 +40,9 @@ */ #define ILL_FNAME_REP "_" -QString appAttachDirPath(void) +QString joinDirs(const QString &dirName1, const QString &dirName2) { - return dfltAttachSavingLoc() + QDir::separator() + - QLatin1String(DATOVKA_BASE_DIR_NAME); + return dirName1 + QDir::separator() + dirName2; } QString appCertDirPath(void) diff --git a/src/io/filesystem.h b/src/io/filesystem.h index 77a1e6aa..22b9a175 100644 --- a/src/io/filesystem.h +++ b/src/io/filesystem.h @@ -41,11 +41,12 @@ #define DATOVKA_MAX_LOG_FILES 5 /*! - * @brief Return path to location where the application stores the attachments. - * - * @return Full path. + * @brief Join two directoris to path. + * @param[in] dirName1 First directory name. + * @param[in] dirName2 Second irectory name. + * @return Directory path. */ -QString appAttachDirPath(void); +QString joinDirs(const QString &dirName1, const QString &dirName2); /*! * @brief Return path to location where the application stores certificates. -- GitLab From eb82436dede710674bef90e9dd0a40adc3352f87 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 4 Dec 2018 16:34:05 +0100 Subject: [PATCH 04/22] Added support for file loading/sending from iCloud --- ios/ios.pri | 7 +- ios/src/icloud_controller.h | 32 ++++ ios/src/icloud_controller.mm | 67 ++++++++ ios/src/icloud_io.h | 112 +++++++++++++ ios/src/icloud_io.mm | 214 +++++++++++++++++++++++++ mobile-datovka.pro | 4 + qml/dialogues/FileDialogueIos.qml | 209 ++++++++++++++++++++++++ qml/pages/PageSendMessage.qml | 58 +++++-- res/qml.qrc | 1 + src/auxiliaries/icloud_helper.cpp | 253 ++++++++++++++++++++++++++++++ src/auxiliaries/icloud_helper.h | 122 ++++++++++++++ src/files.cpp | 17 +- src/files.h | 8 + src/io/filesystem.cpp | 8 +- src/io/filesystem.h | 12 +- src/main.cpp | 12 ++ src/models/cloudmodel.cpp | 222 ++++++++++++++++++++++++++ src/models/cloudmodel.h | 191 ++++++++++++++++++++++ 18 files changed, 1528 insertions(+), 21 deletions(-) create mode 100644 ios/src/icloud_controller.h create mode 100644 ios/src/icloud_controller.mm create mode 100644 ios/src/icloud_io.h create mode 100644 ios/src/icloud_io.mm create mode 100644 qml/dialogues/FileDialogueIos.qml create mode 100644 src/auxiliaries/icloud_helper.cpp create mode 100644 src/auxiliaries/icloud_helper.h create mode 100644 src/models/cloudmodel.cpp create mode 100644 src/models/cloudmodel.h diff --git a/ios/ios.pri b/ios/ios.pri index 9c358d9b..237ef3c1 100644 --- a/ios/ios.pri +++ b/ios/ios.pri @@ -11,7 +11,8 @@ HEADERS += \ ios/src/app_delegate.h \ ios/src/doc_view_controller.h \ ios/src/qt_app_delegate.h \ - ios/src/icloud_helper.h \ + ios/src/icloud_controller.h \ + ios/src/icloud_io.h \ ios/src/ios_file_opener.h \ ios/src/send_email_controller.h \ ios/src/url_opener.h @@ -19,7 +20,8 @@ HEADERS += \ OBJECTIVE_SOURCES += \ ios/src/app_delegate.mm \ ios/src/doc_view_controller.mm \ - ios/src/icloud_helper.mm \ + ios/src/icloud_controller.mm \ + ios/src/icloud_io.mm \ ios/src/ios_file_opener.mm \ ios/src/send_email_controller.mm \ ios/src/url_opener.mm @@ -33,4 +35,3 @@ QMAKE_BUNDLE_DATA += ios_icon app_launch_images.files = $$files($${_PRO_FILE_PWD_}/ios/Images.xcassets/LaunchImage.launchimage/*.png) QMAKE_BUNDLE_DATA += app_launch_images LIBS += -framework UIKit -framework MessageUI -framework MobileCoreServices - diff --git a/ios/src/icloud_controller.h b/ios/src/icloud_controller.h new file mode 100644 index 00000000..9cf4bf6a --- /dev/null +++ b/ios/src/icloud_controller.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#import + +@interface ICloudViewController : UIViewController + +- (void)getCloudHierarchyAsync; + +@property (strong) NSMetadataQuery *query; + +@end diff --git a/ios/src/icloud_controller.mm b/ios/src/icloud_controller.mm new file mode 100644 index 00000000..af010d82 --- /dev/null +++ b/ios/src/icloud_controller.mm @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#include + +#import "ios/src/icloud_controller.h" +#include "src/auxiliaries/icloud_helper.h" + +QStringList iCloudFileList; +bool isSearchRunning; + +@implementation ICloudViewController + +- (void)replyDataNotification:(NSNotification *)notification { + + NSMetadataQuery *query = [notification object]; + [query disableUpdates]; + [query stopQuery]; + + QStringList files; + + for (NSMetadataItem *item in [query results]) { + NSURL *documentURL = [item valueForAttribute:NSMetadataItemURLKey]; + files.append(QString::fromNSString(documentURL.absoluteString)); + //NSLog(@"iCloud:\n %@\n", documentURL); + } + + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:self.query]; + self.query = nil; + + iCloudFileList = files; + isSearchRunning = false; +} + +- (void)getCloudHierarchyAsync { + + isSearchRunning = true; + self.query = [[NSMetadataQuery alloc] init]; + //[self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDataScope]]; + [self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K like '*'", NSMetadataItemFSNameKey]; + [self.query setPredicate:predicate]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(replyDataNotification:) name:NSMetadataQueryDidFinishGatheringNotification object:self.query]; + [self.query startQuery]; +} + +@end diff --git a/ios/src/icloud_io.h b/ios/src/icloud_io.h new file mode 100644 index 00000000..c06cb59c --- /dev/null +++ b/ios/src/icloud_io.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#pragma once + +#include +#include +#include + +/*! + * @brief Provides objective-C IO methods for interaction with iCLoud. + */ +class ICloudIo : public QObject { + Q_OBJECT + +public: + + /*! + * @brief Return state describing what happened. + */ + enum ICloudResult { + ICLOUD_NOT_ON = 0, /*!< iCloud is not avalilable/turn on. */ + ICLOUD_FILE_UPLOAD_SUCCESS, /*!< File upload was successful. */ + ICLOUD_FILE_EXISTS, /*!< File exists on iCloud. */ + ICLOUD_FILE_UPLOAD_ERROR, /*!< File upload failed. */ + ICLOUD_TARGET_SAVE_DIR_ERROR /*!< Target dir is missing or not created. */ + }; + + /*! + * @brief Constructor. + * + * @param[in] parent Parent object. + */ + ICloudIo(QObject *parent = Q_NULLPTR); + + /*! + * @brief Test if iCloud is on. + * + * @return Return true if iCloud is on. + */ + static + bool isCloudOn(void); + + /*! + * @brief Create Datovka iCloud conteiner if not exists. + */ + static + void createCloudConteiner(void); + + /*! + * @brief Copy single file from iCloud continer to Datovka sandbox. + * + * @param[in] cloudFilePath Source file path. + * @param[in] localFilePath iCloud target path. + * @return Full path where file will stored. + */ + static + QString copyFileFromCloud(const QString &cloudFilePath, + const QString &localFilePath); + + /*! + * @brief Search for local iCloud hierarchy - not used now. + */ + static + QStringList getCloudHierarchy(const QString &dir); + + /*! + * @brief Create and send async search query for iCloud hierarchy. + */ + static + void getCloudHierarchyAsync(void); + + /*! + * @brief Upload single file into iCloud. + * + * @param[in] srcFilePath Source file path. + * @param[in] destFilePath iCloud target path. + * @return Return operation error/success code. + */ + static + ICloudResult moveFileToCloud(const QString &srcFilePath, + const QString &destFilePath); + + /*! + * @brief Test if file exists in local iCloud conteiner. + * + * @param[in] cloudFilePath iCloud file path. + * @return True if file exists in local iCloud conteiner. + */ + static + bool isDownloadedFromCloud(const QString &cloudFilePath); +}; diff --git a/ios/src/icloud_io.mm b/ios/src/icloud_io.mm new file mode 100644 index 00000000..94bea094 --- /dev/null +++ b/ios/src/icloud_io.mm @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#include "ios/src/icloud_controller.h" +#include "ios/src/icloud_io.h" + +ICloudIo::ICloudIo(QObject *parent) + : QObject(parent) +{ +} + +NSURL * getCloudBaseUrl() { + return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; +} + +NSURL * getCloudDocumentsUrl(NSURL *baseURL) { + return [baseURL URLByAppendingPathComponent:@"Documents"]; +} + +bool ICloudIo::isCloudOn(void) { + return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; +} + +void ICloudIo::createCloudConteiner(void) +{ + NSURL *baseURL = getCloudBaseUrl(); + if (!baseURL) { + NSLog(@"iCloud: Unable to access iCloud."); + return; + } + + // Create iCloud send message dir + NSURL *sendDirURL = [getCloudDocumentsUrl(baseURL) URLByAppendingPathComponent:@"Send"]; + + // Create send message subdirectorues for iCloud + NSError *error = nil; + if (![[NSFileManager defaultManager] createDirectoryAtURL:sendDirURL + withIntermediateDirectories:YES attributes:nil error:&error]) { + NSLog(@"iCloud: Create message subdirectories error: %@", error); + return; + } + + // Upload send dir into iCloud + if ([[NSFileManager defaultManager] setUbiquitous:YES + itemAtURL:sendDirURL destinationURL:sendDirURL error:&error]) { + NSLog(@"iCloud: File %@ has been uploaded.", sendDirURL); + } +} + +/* TODO - removed this tmp function */ +QStringList ICloudIo::getCloudHierarchy(const QString &dir) +{ + QStringList fileList; + + NSURL *baseURL = getCloudBaseUrl(); + if (!baseURL) { + NSLog(@"iCloud: Unable to access iCloud."); + return QStringList(); + } + + // Create iCloud target path + NSURL *documentURL = getCloudDocumentsUrl(baseURL); + NSURL *fileURL; + if (dir.isEmpty()) { + fileURL = documentURL; + } else { + fileURL = [documentURL URLByAppendingPathComponent:dir.toNSString()]; + } + + NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:fileURL includingPropertiesForKeys:nil options:0 error:nil]; + + for (NSUInteger i = 0; i < [directoryContent count]; i++) { + NSLog(@"iCloud:\n %@\n", directoryContent[i]); + NSURL *fileUrlx = directoryContent[i]; + fileList.append(QString::fromNSString(fileUrlx.absoluteString)); + } + + return fileList; +} + +void ICloudIo::getCloudHierarchyAsync(void) +{ + static ICloudViewController* iCloudCntlr = nil; + iCloudCntlr = [[ICloudViewController alloc] init]; + [iCloudCntlr getCloudHierarchyAsync]; +} + +ICloudIo::ICloudResult ICloudIo::moveFileToCloud( + const QString &srcFilePath, const QString &destFilePath) +{ + NSURL *baseURL = getCloudBaseUrl(); + if (!baseURL) { + return ICLOUD_NOT_ON; + } + + // Create iCloud target path + NSURL *messageURL = [getCloudDocumentsUrl(baseURL) URLByAppendingPathComponent:destFilePath.toNSString()]; + + // Create subdirectorues on iCloud + NSError *error = nil; + if (![[NSFileManager defaultManager] createDirectoryAtURL:messageURL + withIntermediateDirectories:YES attributes:nil error:&error]) { + return ICLOUD_TARGET_SAVE_DIR_ERROR; + } + + // Create target upload path for iCloud + NSURL *sourceFileUrl = [NSURL fileURLWithPath:srcFilePath.toNSString()]; + NSString *fileName = [srcFilePath.toNSString() lastPathComponent]; + NSURL *fileURL = [messageURL URLByAppendingPathComponent:fileName]; + + // Upload file to target directory on iCloud + if ([[NSFileManager defaultManager] setUbiquitous:YES + itemAtURL:sourceFileUrl destinationURL:fileURL error:&error]) { + return ICLOUD_FILE_UPLOAD_SUCCESS; + } else { + // Code 516 = file exists in the iCloud. See NSFileManager error codes. + if (error.code == NSFileWriteFileExistsError) { + return ICLOUD_FILE_EXISTS; + } else { + NSLog(@"iCloud: Error code: %zd", error.code); + NSLog(@"iCloud: %@", error); + return ICLOUD_FILE_UPLOAD_ERROR; + } + } +} + +QString ICloudIo::copyFileFromCloud(const QString &cloudFilePath, + const QString &localFilePath) +{ + if (!isDownloadedFromCloud(cloudFilePath)) { + NSLog(@"Local: File has not been downloaded yet from iCloud."); + return QString(); + } + + NSError *error = nil; + NSURL *fileCloudUrl = [NSURL URLWithString:cloudFilePath.toNSString()]; + NSURL* localFileUrl = [NSURL fileURLWithPath:localFilePath.toNSString()]; + NSString *fileName = [fileCloudUrl lastPathComponent]; + + // Create subdirectorues on Local + if (![[NSFileManager defaultManager] createDirectoryAtURL:localFileUrl + withIntermediateDirectories:YES attributes:nil error:&error]) { + NSLog(@"Local: Create message subdirectories error: %@", error.localizedDescription); + return QString(); + } + + NSURL *fileLocalUrl = [localFileUrl URLByAppendingPathComponent:fileName]; + NSLog(@"iCloud url: %@", fileCloudUrl); + NSLog(@"Local file url: %@", fileLocalUrl); + + if ([[NSFileManager defaultManager] copyItemAtURL:fileCloudUrl toURL:fileLocalUrl error:&error]) { + NSLog(@"Local: File has been uploaded."); + return QString(); + } else { + if (error.code == NSFileWriteFileExistsError) { + NSLog(@"Local: File with the same name already exists."); + return QString(); + } else if (error.code == NSFileReadNoSuchFileError) { + NSLog(@"Local: File has not been downloaded yet from iCloud."); + return QString(); + } else { + NSLog(@"Local: Error code: %zd %@", error.code, error.localizedDescription); + } + + } + + return QString(); +} + +bool ICloudIo::isDownloadedFromCloud(const QString &cloudFilePath) +{ + NSURL *baseURL = getCloudBaseUrl(); + if (!baseURL) { + NSLog(@"iCloud: Unable to access iCloud."); + return false; + } + + NSError *error = nil; + NSString *downloadStatus = nil; + + NSURL *fileCloudUrl = [NSURL URLWithString:cloudFilePath.toNSString()]; + + if ([fileCloudUrl getResourceValue:&downloadStatus forKey:NSURLUbiquitousItemDownloadingStatusKey error:&error] == YES) { + if ([downloadStatus isEqualToString:NSURLUbiquitousItemDownloadingStatusNotDownloaded] == YES) { + NSLog(@"File must be downloaded: %@", fileCloudUrl); + [[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL:fileCloudUrl error:&error]; + return false; + } else { + NSLog(@"File has been downloaded: %@", fileCloudUrl); + } + } + + return true; +} diff --git a/mobile-datovka.pro b/mobile-datovka.pro index aa956191..52de963e 100644 --- a/mobile-datovka.pro +++ b/mobile-datovka.pro @@ -90,6 +90,7 @@ TRANSLATIONS_FILES += \ SOURCES += \ src/accounts.cpp \ src/auxiliaries/email_helper.cpp \ + src/auxiliaries/icloud_helper.cpp \ src/datovka_shared/gov_services/helper.cpp \ src/datovka_shared/gov_services/service/gov_mv_crr_vbh.cpp \ src/datovka_shared/gov_services/service/gov_mv_ir_vp.cpp \ @@ -153,6 +154,7 @@ SOURCES += \ src/main.cpp \ src/messages.cpp \ src/models/accountmodel.cpp \ + src/models/cloudmodel.cpp \ src/models/databoxmodel.cpp \ src/models/filemodel.cpp \ src/models/list_sort_filter_proxy_model.cpp \ @@ -211,6 +213,7 @@ SOURCES += \ HEADERS += \ src/accounts.h \ src/auxiliaries/email_helper.h \ + src/auxiliaries/icloud_helper.h \ src/common.h \ src/datovka_shared/gov_services/helper.h \ src/datovka_shared/gov_services/service/gov_mv_crr_vbh.h \ @@ -277,6 +280,7 @@ HEADERS += \ src/log.h \ src/messages.h \ src/models/accountmodel.h \ + src/models/cloudmodel.h \ src/models/databoxmodel.h \ src/models/filemodel.h \ src/models/list_sort_filter_proxy_model.h \ diff --git a/qml/dialogues/FileDialogueIos.qml b/qml/dialogues/FileDialogueIos.qml new file mode 100644 index 00000000..c049c66d --- /dev/null +++ b/qml/dialogues/FileDialogueIos.qml @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import cz.nic.mobileDatovka 1.0 +import cz.nic.mobileDatovka.qmlInteraction 1.0 +import cz.nic.mobileDatovka.models 1.0 + +Dialog { + id: root + focus: true + modal: true + title: qsTr("Select files") + + footer: DialogButtonBox { + AccessibleButton { + text: qsTr("Cancel") + DialogButtonBox.buttonRole: DialogButtonBox.RejectRole + } + AccessibleButton { + text: qsTr("Add") + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + } + + /* Place the dialogue in the centre. */ + x: 2 * defaultMargin + y: 2 * defaultMargin + + height: parent.height - 4 * defaultMargin + width: parent.width - 4 * defaultMargin + + signal finished(variant pathListModel) + + function raise(title, filters, showFiles, targetLocation) { + cloudFileListModel.clearAll() + pathListModel.clear() + filterBar.filterField.text = "" + iCloudHelper.getCloudHierarchyAsync() + root.open() + } + + /* Holds cloud file list model */ + CloudFileListModel { + id: cloudFileListModel + Component.onCompleted: { + } + } + + ListSortFilterProxyModel { + id: proxyCloudFileListModel + Component.onCompleted: { + setFilterRoles([CloudFileListModel.ROLE_FILE_NAME, + CloudFileListModel.ROLE_FILE_SHORT_PATH]) + proxyCloudFileListModel.setSourceModel(cloudFileListModel) + } + } + + ListModel { + id: pathListModel + } + + contentItem: ColumnLayout { + spacing: formItemVerticalSpacing + Connections { + target: iCloudHelper + onCloudContentSig: { + iCloudHelper.setCloudFileModel(cloudFileListModel, iCloudFileList); + if (cloudFileListModel.rowCount() > 1) { + cloudFileList.visible = true + emptyList.visible = false + } + } + onCloudActivitySig: { + emptyList.text = txt + cloudFileList.visible = false + emptyList.visible = true + } + } + Component { + id: attachmentDelegate + Rectangle { + id: attachmentItem + width: parent.width + height: cloudFileList.delegateHeight + color: rFileSelected ? datovkaPalette.midlight : datovkaPalette.base + Image { + id: imageAttachment + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + sourceSize.height: cloudFileList.delegateHeight * 0.8 + source: files.getAttachmentFileIcon(rFileName) + } + Item { + anchors.left: imageAttachment.right + anchors.leftMargin: defaultMargin + height: parent.height + width: parent.width + Column { + width: parent.width + anchors.verticalCenter: parent.verticalCenter + spacing: defaultMargin + Text { + text: rFileName + color: datovkaPalette.text + font.bold: true + } + Text { + text: rFileShortPath + color: datovkaPalette.mid + font.pointSize: textFontSizeSmall + } + } + } + Rectangle { + anchors.top: parent.bottom + height: 1 + width: parent.width + color: datovkaPalette.dark + } + MouseArea { + anchors.fill: parent + onClicked: { + cloudFileListModel.setFileSelected(rFileCloudPath, !rFileSelected) + } + } + } // Rectangle + } // Component + FilterBar { + id: filterBar + visible: true + Layout.fillWidth: true + height: filterField.height + color: (filterField.text.length === 0) ? datovkaPalette.alternateBase : + (cloudFileList.count > 0) ? "#afffaf" : "#ffafaf" + border.color: filterField.activeFocus ? "#0066ff" : "#bdbebf" + + placeholderText: qsTr("Set filter") + fontPointSize: defaultTextFont.font.pointSize + buttonImageHeight: imgHeight + buttonAccessibleName: qsTr("Clear and hide filter field") + + onTextChanged: { + proxyCloudFileListModel.setFilterRegExpStr(text) + } + + onClearClicked: { + filterField.text.clear + } + } + AccessibleText { + id: emptyList + visible: true + color: datovkaPalette.text + Layout.fillHeight: true + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + text: qsTr("No files in the iCloud.") + } + ScrollableListView { + id: cloudFileList + visible: false + delegateHeight: headerHeight + Layout.fillHeight: true + Layout.fillWidth: true + clip: true + spacing: 1 + opacity: 1 + interactive: true + model: proxyCloudFileListModel + delegate: attachmentDelegate + ScrollIndicator.vertical: ScrollIndicator {} + } // Listview + } // ColumnLayout + onAccepted: { + for (var i = 0; i < cloudFileListModel.rowCount(); ++i) { + if (cloudFileListModel.fileSelected(i)) { + pathListModel.append({path: cloudFileListModel.fileCloudPathFromRow(i)}) + } + } + finished(pathListModel) + } + onRejected: { + pathListModel.clear() + } +} diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index 71a0e7ef..6dac2a4f 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -216,6 +216,32 @@ Item { } } + /* File dialog for choose of files from iCloud */ + FileDialogueIos { + id: fileDialogueIos + onFinished: { + var listLength = pathListModel.count + for (var j = 0; j < listLength; ++j) { + var localFilePath = iCloudHelper.downloadFileFromCloud(pathListModel.get(j).path) + var isInFiletList = false + for (var i = 0; i < sendMsgAttachmentModel.rowCount(); i++) { + if (sendMsgAttachmentModel.filePathFromRow(i) === localFilePath) { + isInFiletList = true + break + } + } + if (!isInFiletList) { + var fileName = getFileNameFromPath(localFilePath) + var fileSizeBytes = files.getAttachmentSizeInBytes(localFilePath) + sendMsgAttachmentModel.appendFileFromPath(FileIdType.NO_FILE_ID, + fileName, localFilePath, fileSizeBytes) + totalAttachmentSizeBytes = sendMsgAttachmentModel.dataSizeSum() + } + } + pathListModel.clear() + } + } + /* Holds send message recipent list model */ DataboxListModel { id: recipBoxModel @@ -488,15 +514,29 @@ Item { //----ATTACHMENT SECTION------------ Item { id: tabAttachments - AccessibleButton { - id: addFile - height: inputItemHeight + Row { + id: buttonBar + spacing: formItemVerticalSpacing * 5 anchors.horizontalCenter: parent.horizontalCenter - font.pointSize: defaultTextFont.font.pointSize - text: qsTr("Add file") - onClicked: { - fileDialogue.raise(qsTr("Select files"), ["*.*"], true, "") + AccessibleButton { + id: addFile + height: inputItemHeight + font.pointSize: defaultTextFont.font.pointSize + text: qsTr("Add file") + onClicked: { + fileDialogue.raise(qsTr("Select files"), ["*.*"], true, "") + } } + AccessibleButton { + id: icloud + visible: files.isIos() + height: inputItemHeight + font.pointSize: defaultTextFont.font.pointSize + text: qsTr("iCloud") + onClicked: { + fileDialogueIos.raise(qsTr("Select files"), ["*.*"], true, "") + } + } } Component { id: attachmentDelegate @@ -634,7 +674,7 @@ Item { color: datovkaPalette.text anchors.left: parent.left anchors.leftMargin: defaultMargin - anchors.top: addFile.bottom + anchors.top: buttonBar.bottom anchors.topMargin: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin @@ -645,9 +685,7 @@ Item { } ScrollableListView { id: attachmentListSend - delegateHeight: listItemHeight - anchors.top: attachmentsSizeLabel.bottom anchors.bottom: parent.bottom clip: true diff --git a/res/qml.qrc b/res/qml.qrc index b0ed8512..b56f2909 100644 --- a/res/qml.qrc +++ b/res/qml.qrc @@ -131,6 +131,7 @@ ../qml/components/ScrollableListView.qml ../qml/dialogues/CalendarDialogue.qml ../qml/dialogues/FileDialogue.qml + ../qml/dialogues/FileDialogueIos.qml ../qml/dialogues/InputDialogue.qml ../qml/dialogues/MessageDialogue.qml ../qml/dialogues/PasteInputDialogue.qml diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp new file mode 100644 index 00000000..676f2966 --- /dev/null +++ b/src/auxiliaries/icloud_helper.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#include +#include +#include + +#include "src/auxiliaries/icloud_helper.h" +#include "src/io/filesystem.h" +#include "src/models/cloudmodel.h" +#include "src/datovka_shared/log/log.h" + +#ifdef Q_OS_IOS +#include "ios/src/icloud_io.h" +#define ICLOUD_DATOVKA_CONTAINER_NAME "Datovka" +#endif /* Q_OS_IOS */ + +#ifndef Q_OS_IOS +QStringList iCloudFileList; +bool isSearchRunning; +#endif /* Q_OS_IOS */ + +ICloudHelper::ICloudHelper(QObject *parent) + : QObject(parent), + m_timer(new QTimer(this)) +{ +} + +/* TODO - removed this tmp function if all will done. */ +QStringList testCloudPathList(void) +{ + return QStringList() << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/Send/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/pokus/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/Send/app_delegate.mm" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6765996/priloha.txt" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6765996/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/pokus/dmg_background.png" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/pokus/datovka.svg" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/4i2xd7/623345688/DZ_623345688.zfo" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/Send/datovka.png" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/dokument.odt" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/4i2xd7/623345688/5000FC241550444E8BE9C217BF5247EA.pdf" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/notification.mp3" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/dokument.pdf" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/obrazek.png" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/4i2xd7/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/4i2xd7/623345688/"; +} + +void ICloudHelper::createCloudConteiner(void) +{ + debugSlotCall(); + +#ifdef Q_OS_IOS + + ICloudIo::createCloudConteiner(); + +#endif /* Q_OS_IOS */ +} + +void ICloudHelper::getCloudHierarchyAsync(void) +{ + debugSlotCall(); + +#ifdef Q_OS_IOS + + if (!ICloudIo::isCloudOn()) { + QMessageBox::critical(Q_NULLPTR, tr("iCloud error"), + tr("Unable to access iCloud Account. Open the Settings app and enter your Apple ID into iCloud settings."), + QMessageBox::Ok); + return; + } + + iCloudFileList.clear(); + isSearchRunning = true; + + connect(m_timer, SIGNAL(timeout()), this, SLOT(receivedCloudHierarchy())); + m_timer->start(100); + + ICloudIo::getCloudHierarchyAsync(); + +#else + + emit cloudContentSig(testCloudPathList()); + +#endif /* Q_OS_IOS */ +} + +void ICloudHelper::storeFilesToCloud(const QStringList &srcFilePaths, + const QString &targetPath) +{ + debugSlotCall(); + +#ifdef Q_OS_IOS + + if (!ICloudIo::isCloudOn()) { + QMessageBox::critical(Q_NULLPTR, tr("iCloud error"), + tr("Unable to access iCloud Account. Open the Settings app and enter your Apple ID into iCloud settings."), + QMessageBox::Ok); + return; + } + + /* Upload files to iCloud */ + bool success = true; + QStringList errorUploads; + QString filePath; + + foreach (filePath, srcFilePaths) { + + ICloudIo::ICloudResult retCode = ICloudIo::moveFileToCloud(filePath, targetPath); + + QFileInfo fi(filePath); + + switch (retCode) { + case ICloudIo::ICLOUD_NOT_ON: + success &= false; + errorUploads.append(tr("Unable to access iCloud!")); + goto finish; + break; + case ICloudIo::ICLOUD_TARGET_SAVE_DIR_ERROR: + success &= false; + errorUploads.append(tr("Cannot create subdir '%1' in iCloud.").arg(targetPath)); + goto finish; + break; + case ICloudIo::ICLOUD_FILE_EXISTS: + success &= false; + errorUploads.append(tr("File '%1' already exists in iCloud.").arg(fi.fileName())); + break; + case ICloudIo::ICLOUD_FILE_UPLOAD_ERROR: + success &= false; + errorUploads.append(tr("File '%1' upload failed.").arg(fi.fileName())); + break; + case ICloudIo::ICLOUD_FILE_UPLOAD_SUCCESS: + errorUploads.append(tr("File '%1' has been moved in iCloud.").arg(fi.fileName())); + break; + default: + break; + } + } + +finish: + + /* Delete files and source dir */ + QFileInfo fi(filePath); + QDir dir(fi.absolutePath()); + dir.removeRecursively(); + + /* Show final notification */ + if (success) { + qCritical() << "Files have been saved to iCloud."; + QMessageBox::critical(Q_NULLPTR, tr("iCloud saving"), + tr("Files have been saved to iCloud.") + "\n\n" + + tr("Path: '%1'").arg(QString(ICLOUD_DATOVKA_CONTAINER_NAME) + + "/" + targetPath), + QMessageBox::Ok); + } else { + qCritical() << "Files upload to iCloud failed!"; + QString txt; + foreach (const QString &error, errorUploads) { + txt += "\n" + error; + } + QMessageBox::critical(Q_NULLPTR, + tr("iCloud saving problem"), + tr("Files have not been saved!") + "\n" + txt, + QMessageBox::Ok); + } + +#else /* !Q_OS_IOS */ + + Q_UNUSED(srcFilePaths); + Q_UNUSED(targetPath); + +#endif /* Q_OS_IOS */ +} + +QString ICloudHelper::downloadFileFromCloud(const QString &cloudFilePath) +{ + debugSlotCall(); + +#ifdef Q_OS_IOS + + QString localFilePath; + QFileInfo fi(cloudFilePath); + localFilePath = appSendDirPath() + QDir::separator() + + QUrl::fromPercentEncoding(fi.fileName().toUtf8()); + + ICloudIo::copyFileFromCloud(cloudFilePath, appSendDirPath()); + return localFilePath; + +#else + Q_UNUSED(cloudFilePath); + return QString(); +#endif +} + +void ICloudHelper::setCloudFileModel(const QVariant &cloudFileModelVariant, + const QStringList &fileList) +{ + debugSlotCall(); + + /* Obtain pointer to attachment model. */ + CloudFileListModel *cloudModel = + CloudFileListModel::fromVariant(cloudFileModelVariant); + if (cloudModel == Q_NULLPTR) { + Q_ASSERT(0); + return; + } + + foreach (const QString &file, fileList) { + QFileInfo fi(file); + if (fi.fileName().isEmpty()) { + continue; + } + QString filePath = fi.path(); + QString pattern("iCloud~cz~nic~mobile-datovka/Documents"); + int pos = filePath.indexOf(pattern) + pattern.length(); + filePath = QStringLiteral("iCloud:Datovka") + filePath.mid(pos) + QStringLiteral("/"); + cloudModel->appendFileFromCloud(file, fi.fileName(), filePath, false); + } +} + +void ICloudHelper::clearSendDir(void) +{ + debugFuncCall(); + + QDir dir(appSendDirPath()); + dir.removeRecursively(); +} + +void ICloudHelper::receivedCloudHierarchy(void) +{ + debugSlotCall(); + +#ifdef Q_OS_IOS + + if (!isSearchRunning) { + m_timer->stop(); + disconnect(m_timer, SIGNAL(timeout()), + this, SLOT(receivedCloudHierarchy())); + if (!iCloudFileList.isEmpty()) { + emit cloudContentSig(iCloudFileList); + } + } else { + emit cloudActivitySig(tr("Searching for iCloud files...")); + } + +#endif +} diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/icloud_helper.h new file mode 100644 index 00000000..d3950d59 --- /dev/null +++ b/src/auxiliaries/icloud_helper.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#pragma once + +#include +#include +#include +#include + +/* Globals for iCloud search query results transfer. */ +extern QStringList iCloudFileList; +extern bool isSearchRunning; + +/*! + * @brief Provides iCloud interface for iOS. + */ +class ICloudHelper : public QObject { + Q_OBJECT + +public: + + /*! + * @brief Constructor. + * + * @param[in] parent Parent object. + */ + ICloudHelper(QObject *parent = Q_NULLPTR); + + /*! + * @brief Create Datovka iCloud conteiner if not exists. + */ + void createCloudConteiner(void); + + /*! + * @brief Create and send async search query for iCloud hierarchy. + */ + Q_INVOKABLE + void getCloudHierarchyAsync(void); + + /*! + * @brief Store files to iCloud. + * + * @param[in] srcFilePaths List of file paths to be saved into iCloud. + * @param[in] targetPath Target iCloud path where files will store. + */ + Q_INVOKABLE static + void storeFilesToCloud(const QStringList &srcFilePaths, + const QString &targetPath); + + /*! + * @brief Download file from iCloud if not exists in the local storage. + * + * @param[in] cloudFilePath File path in iCloud. + * @return Full path where file will stored. + */ + Q_INVOKABLE + QString downloadFileFromCloud(const QString &cloudFilePath); + + /*! + * @brief Set iCloud file model in QML. + * + * @param[in,out] cloudFileModelVariant iCloud file model from QML. + * @param[in] fileList Path file list of iCloud. + */ + Q_INVOKABLE + void setCloudFileModel(const QVariant &cloudFileModelVariant, + const QStringList &fileList); + + /*! + * @brief Clear send folder. + */ + void clearSendDir(void); + +signals: + + /*! + * @brief Is activated when iCloud hierarchy has been received. + * + * @param[in] iCloudFileList Path file list of iCloud for QML. + */ + void cloudContentSig(QStringList iCloudFileList); + + /*! + * @brief Is activated when an iCloud activity is processed. + * + * @param[in] txt Description what happened. + */ + void cloudActivitySig(QString txt); + +private slots: + + /*! + * @brief Is activated when iCloud hierarchy has been received. + */ + void receivedCloudHierarchy(void); + +private: + + /* Detect if async search response with iCloud hierarchy finished. */ + QTimer *m_timer; +}; diff --git a/src/files.cpp b/src/files.cpp index 72b39336..dbfdcb25 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -27,13 +27,13 @@ #include #include - #if defined (Q_OS_ANDROID) #include "android/src/android_io.h" #endif #include "ios/src/icloud_helper.h" #include "ios/src/url_opener.h" #include "src/auxiliaries/email_helper.h" +#include "src/auxiliaries/icloud_helper.h" #include "src/common.h" #include "src/crypto/crypto.h" #include "src/datovka_shared/log/log.h" @@ -724,8 +724,7 @@ void Files::saveMsgFilesToDisk(const QString &userName, #else - ICloudHelper iCloudHelper; - iCloudHelper.storeFilesToCloud(destFilePaths, joinDirs(userName, + ICloudHelper::storeFilesToCloud(destFilePaths, joinDirs(userName, msgIdStr)); #endif @@ -765,8 +764,7 @@ void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant, #else - ICloudHelper iCloudHelper; - iCloudHelper.storeFilesToCloud(destFilePaths, msgIdStr); + ICloudHelper::storeFilesToCloud(destFilePaths, msgIdStr); #endif } @@ -781,6 +779,15 @@ void Files::deleteTmpFileFromStorage(const QString &filePath) #endif } +bool Files::isIos(void) +{ +#ifdef Q_OS_IOS + return true; +#else + return false; +#endif +} + bool Files::parseXmlData(enum MsgInfo::ZfoType *type, QString *idStr, QString *annotation, QString *msgDescrHtml, FileListModel *attachModel, QString *emailBody, QByteArray xmlData) diff --git a/src/files.h b/src/files.h index 31ab960a..c07d3c0b 100644 --- a/src/files.h +++ b/src/files.h @@ -284,6 +284,14 @@ public: Q_INVOKABLE void deleteTmpFileFromStorage(const QString &filePath); + /*! + * @brief iOS check for QML. + * + * @return True if iOS. + */ + Q_INVOKABLE + bool isIos(void); + signals: /*! * @brief Set new statusbar text and active busy indicator to QML. diff --git a/src/io/filesystem.cpp b/src/io/filesystem.cpp index 81188aad..4c559c9e 100644 --- a/src/io/filesystem.cpp +++ b/src/io/filesystem.cpp @@ -45,6 +45,12 @@ QString joinDirs(const QString &dirName1, const QString &dirName2) return dirName1 + QDir::separator() + dirName2; } +QString appSendDirPath(void) +{ + return existingAppPath(dfltAttachSavingLoc(), DATOVKA_SEND_DIR_NAME); +} + + QString appCertDirPath(void) { return existingAppPath(dfltAttachSavingLoc(), DATOVKA_CERT_DIR_NAME); @@ -58,7 +64,7 @@ QString appEmailDirPath(const QString &msgIdStr) QString appLogDirPath(void) { - return existingAppPath(dfltAttachSavingLoc(), DATOVKA_LOG_DIR_NAME); + return existingAppPath(dfltAttachSavingLoc(), DATOVKA_LOGS_DIR_NAME); } QString appMsgAttachDirPath(const QString &msgIdStr) diff --git a/src/io/filesystem.h b/src/io/filesystem.h index 22b9a175..0a5d0b62 100644 --- a/src/io/filesystem.h +++ b/src/io/filesystem.h @@ -30,8 +30,9 @@ * @brief Datovka file store directory names. */ #define DATOVKA_BASE_DIR_NAME "Datovka" -#define DATOVKA_CERT_DIR_NAME "Certs" -#define DATOVKA_LOG_DIR_NAME "Logs" +#define DATOVKA_SEND_DIR_NAME "Send" +#define DATOVKA_CERT_DIR_NAME "Cert" +#define DATOVKA_LOGS_DIR_NAME "Logs" #define DATOVKA_MAIL_DIR_NAME "Email" #define DATOVKA_TEMP_DIR_NAME "Temp" @@ -48,6 +49,13 @@ */ QString joinDirs(const QString &dirName1, const QString &dirName2); +/*! + * @brief Return path to location where the application stores send files. + * + * @return Full path. + */ +QString appSendDirPath(void); + /*! * @brief Return path to location where the application stores certificates. * diff --git a/src/main.cpp b/src/main.cpp index a0681d9f..b9ae3611 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,6 +36,7 @@ #include #include "src/accounts.h" +#include "src/auxiliaries/icloud_helper.h" #include "src/datovka_shared/crypto/crypto_trusted_certs.h" #include "src/datovka_shared/io/records_management_db.h" #include "src/datovka_shared/localisation/localisation.h" @@ -56,6 +57,7 @@ #include "src/log.h" #include "src/net/isds_wrapper.h" #include "src/models/accountmodel.h" +#include "src/models/cloudmodel.h" #include "src/models/databoxmodel.h" #include "src/models/filemodel.h" #include "src/models/list_sort_filter_proxy_model.h" @@ -187,6 +189,7 @@ static const struct QmlTypeEntry qmlDialogues[] = { { "CalendarDialogue", 1, 0 }, { "FileDialogue", 1, 0 }, + { "FileDialogueIos", 1, 0 }, { "InputDialogue", 1, 0}, { "MessageDialogue", 1, 0}, { "PasteInputDialogue", 1, 0 }, @@ -445,6 +448,7 @@ int main(int argc, char *argv[]) Messages messages; Accounts accounts; Files files; + ICloudHelper iCloudHelper; Log log(&memLog); IsdsWrapper isds; GovWrapper gov(&isds); @@ -458,6 +462,12 @@ int main(int argc, char *argv[]) * Reaction on the iOS action "Open in..." */ #if defined Q_OS_IOS QtAppDelegateInitialize(&interactionZfoFile); + + /* Create Datovka iCloud conteiner if not exists .*/ + iCloudHelper.createCloudConteiner(); + + /* Clear send dir. */ + iCloudHelper.clearSendDir(); #endif /* @@ -485,6 +495,7 @@ int main(int argc, char *argv[]) /* Register types into QML. */ AccountListModel::declareQML(); + CloudFileListModel::declareQML(); DataboxListModel::declareQML(); DataboxModelEntry::declareQML(); Dialogues::declareQML(); @@ -506,6 +517,7 @@ int main(int argc, char *argv[]) ctx->setContextProperty("messages", &messages); ctx->setContextProperty("accounts", &accounts); ctx->setContextProperty("files", &files); + ctx->setContextProperty("iCloudHelper", &iCloudHelper); ctx->setContextProperty("gov", &gov); ctx->setContextProperty("settings", &settings); ctx->setContextProperty("strManipulation", &strManipulation); diff --git a/src/models/cloudmodel.cpp b/src/models/cloudmodel.cpp new file mode 100644 index 00000000..0acf362e --- /dev/null +++ b/src/models/cloudmodel.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#include /* qmlRegisterType */ + +#include "src/models/cloudmodel.h" + +CloudFileListModel::Entry::Entry(const Entry &fme) + : m_fileClaudPath(fme.m_fileClaudPath), + m_fileName(fme.m_fileName), + m_fileShortPath(fme.m_fileShortPath), + m_selected(fme.m_selected) +{ +} + +CloudFileListModel::Entry::Entry(const QString &fileClaudPath, + const QString &fileName, const QString &fileShortPath, bool selected) + : m_fileClaudPath(fileClaudPath), + m_fileName(fileName), + m_fileShortPath(fileShortPath), + m_selected(selected) +{ +} + +QString CloudFileListModel::Entry::fileClaudPath(void) const +{ + return m_fileClaudPath; +} + +void CloudFileListModel::Entry::setFileClaudPath(const QString &fileClaudPath) +{ + m_fileClaudPath = fileClaudPath; +} + +QString CloudFileListModel::Entry::fileName(void) const +{ + return m_fileName; +} + +void CloudFileListModel::Entry::setFileName(const QString &fileName) +{ + m_fileName = fileName; +} + +QString CloudFileListModel::Entry::fileShortPath(void) const +{ + return m_fileShortPath; +} + +void CloudFileListModel::Entry::setFileShortPath(const QString &fileShortPath) +{ + m_fileShortPath = fileShortPath; +} + +bool CloudFileListModel::Entry::selected(void) const +{ + return m_selected; +} + +void CloudFileListModel::Entry::setSelected(bool selected) +{ + m_selected = selected; +} + +void CloudFileListModel::declareQML(void) +{ + qmlRegisterType("cz.nic.mobileDatovka.models", 1, 0, "CloudFileListModel"); + qRegisterMetaType(); + qRegisterMetaType(); +} + +CloudFileListModel::CloudFileListModel(QObject *parent) + : QAbstractListModel(parent), + m_files() +{ +} + +CloudFileListModel::CloudFileListModel(const CloudFileListModel &model, QObject *parent) + : QAbstractListModel(parent), + m_files(model.m_files) +{ +} + +int CloudFileListModel::rowCount(const QModelIndex &parent) const +{ + return !parent.isValid() ? m_files.size() : 0; +} + +QHash CloudFileListModel::roleNames(void) const +{ + static QHash roles; + if (roles.isEmpty()) { + roles[ROLE_FILE_CLOUD_PATH] = "rFileCloudPath"; + roles[ROLE_FILE_NAME] = "rFileName"; + roles[ROLE_FILE_SHORT_PATH] = "rFileShortPath"; + roles[ROLE_FILE_SELECTED] = "rFileSelected"; + } + return roles; +} + +QVariant CloudFileListModel::data(const QModelIndex &index, int role) const +{ + if ((index.row() < 0) || (index.row() >= m_files.size())) { + return QVariant(); + } + + const Entry &file(m_files[index.row()]); + + switch (role) { + case ROLE_FILE_CLOUD_PATH: + return file.fileClaudPath(); + break; + case ROLE_FILE_NAME: + return file.fileName(); + break; + case ROLE_FILE_SHORT_PATH: + return file.fileShortPath(); + break; + case ROLE_FILE_SELECTED: + return file.selected(); + break; + default: + /* Do nothing. */ + break; + } + + return QVariant(); +} + +QList CloudFileListModel::allEntries(void) const +{ + QList entries; + + foreach (const CloudFileListModel::Entry &file, m_files) { + entries.append(file); + } + + return entries; +} + +QString CloudFileListModel::fileCloudPathFromRow(int row) +{ + if ((row < 0) || (row >= m_files.size())) { + return QString(); + } + + return m_files[row].fileClaudPath(); +} + +Qt::ItemFlags CloudFileListModel::flags(const QModelIndex &index) const +{ + return QAbstractListModel::flags(index); +} + +void CloudFileListModel::appendFileFromCloud(const QString &fileClaudPath, + const QString &fileName, const QString &fileShortPath, bool selected) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_files.append(CloudFileListModel::Entry(fileClaudPath, fileName, + fileShortPath, selected)); + endInsertRows(); +} + +void CloudFileListModel::setFileSelected(const QString &filePath, bool selected) +{ + if (Q_UNLIKELY(filePath.isEmpty())) { + return; + } + + for (int i = 0; i < m_files.size(); ++i) { + if (filePath == m_files.at(i).fileClaudPath()) { + beginResetModel(); + m_files[i].setSelected(selected); + endResetModel(); + } + } +} + +void CloudFileListModel::clearAll(void) +{ + beginResetModel(); + m_files.clear(); + endResetModel(); +} + +bool CloudFileListModel::fileSelected(int row) +{ + if ((row < 0) || (row >= m_files.size())) { + return false; + } + + return m_files[row].selected(); +} + +CloudFileListModel *CloudFileListModel::fromVariant(const QVariant &modelVariant) +{ + if (!modelVariant.canConvert()) { + return Q_NULLPTR; + } + QObject *obj = qvariant_cast(modelVariant); + return qobject_cast(obj); +} diff --git a/src/models/cloudmodel.h b/src/models/cloudmodel.h new file mode 100644 index 00000000..7063be55 --- /dev/null +++ b/src/models/cloudmodel.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#pragma once + +#include + +class CloudFileListModel : public QAbstractListModel { + Q_OBJECT + +public: + class Entry { + public: + Entry(const Entry &fme); + Entry(const QString &fileClaudPath, const QString &fileName, + const QString &fileShortPath, bool selected); + + QString fileClaudPath(void) const; + void setFileClaudPath(const QString &fileClaudPath); + QString fileName(void) const; + void setFileName(const QString &fileName); + QString fileShortPath(void) const; + void setFileShortPath(const QString &fileShortPath); + bool selected(void) const; + void setSelected(bool selected); + + private: + + QString m_fileClaudPath; /*!< Full file path to cloud. */ + QString m_fileName; /*!< File name. */ + QString m_fileShortPath; /*!< Short file path for QML. */ + bool m_selected; /*!< Is file selected for download. */ + }; + + /*! + * @brief Roles which this model supports. + */ + enum Roles { + ROLE_FILE_CLOUD_PATH = Qt::UserRole, + ROLE_FILE_NAME, + ROLE_FILE_SHORT_PATH, + ROLE_FILE_SELECTED + }; + Q_ENUM(Roles) + + /* Don't forget to declare various properties to the QML system. */ + static + void declareQML(void); + + /*! + * @brief Constructor. + * + * @param[in] parent Pointer to parent object. + */ + explicit CloudFileListModel(QObject *parent = Q_NULLPTR); + + /*! + * @brief Copy constructor. + * + * @note Needed for QVariant conversion. + * + * @param[in] model Model to be copied. + * @param[in] parent Pointer to parent object. + */ + explicit CloudFileListModel(const CloudFileListModel &model, + QObject *parent = Q_NULLPTR); + + /*! + * @brief Return number of rows under the given parent. + * + * @param[in] parent Parent node index. + * @return Number of rows. + */ + virtual + int rowCount(const QModelIndex &parent = QModelIndex()) const + Q_DECL_OVERRIDE; + + /*! + * @brief Returns the model's role names. + * + * @return Model's role names. + */ + virtual + QHash roleNames(void) const Q_DECL_OVERRIDE; + + /*! + * @brief Return data stored in given location under given role. + * + * @param[in] index Index specifying the item. + * @param[in] role Data role. + * @return Data from model. + */ + virtual + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + + /*! + * @brief Return list of all entries. + * + * @return List of all entries. + */ + QList allEntries(void) const; + + /*! + * @brief Return file path from attachment model. + * + * @param[in] row Row specifying the item. + * @return File path string. + */ + Q_INVOKABLE + QString fileCloudPathFromRow(int row); + + /*! + * @brief Returns item flags for given index. + * + * @brief[in] index Index specifying the item. + * @return Item flags. + */ + virtual + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + + /*! + * @brief Appends file from cloud to model. + * + * @param[in] fileClaudPath Full cloud file path. + * @param[in] fileName File name. + * @param[in] fileShortPath Shrot file path. + * @param[in] selected File selected. + */ + Q_INVOKABLE + void appendFileFromCloud(const QString &fileClaudPath, + const QString &fileName, const QString &fileShortPath, bool selected); + + /*! + * @brief Clears the model. + */ + Q_INVOKABLE + void clearAll(void); + + /*! + * @brief Set file selection to the model. + */ + Q_INVOKABLE + void setFileSelected(const QString &filePath, bool selected); + + /*! + * @brief Get file selection from the model. + */ + Q_INVOKABLE + bool fileSelected(int row); + + /*! + * @brief Converts QVariant obtained from QML into model pointer. + * + * @note Some weird stuff happens in QML when passing attachment model + * directly as constant reference. Wrong constructors are called + * and no data are passed. That's because we are using a QVariant. + * This function does not allocate a new model. + * + * @param[in] modelVariant QVariant holding the model. + * @return Pointer to model if it could be acquired, Q_NULLPTR else. + */ + static + CloudFileListModel *fromVariant(const QVariant &modelVariant); + +private: + QList m_files; /*!< List of attachment entries. */ +}; + +/* QML passes its arguments via QVariant. */ +Q_DECLARE_METATYPE(CloudFileListModel) +Q_DECLARE_METATYPE(CloudFileListModel::Roles) -- GitLab From 8003c88acf1890a9564492b7add15db6cee1fea0 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Mon, 10 Dec 2018 13:20:55 +0100 Subject: [PATCH 05/22] Added iCloud file downloading during its selection --- ios/src/icloud_io.mm | 34 +++++++----- qml/dialogues/FileDialogue.qml | 10 +++- qml/dialogues/FileDialogueIos.qml | 22 +++++--- qml/pages/PageSendMessage.qml | 72 +++++++++++-------------- src/auxiliaries/icloud_helper.cpp | 87 ++++++++++++++++++++++--------- src/auxiliaries/icloud_helper.h | 15 +++++- src/models/cloudmodel.cpp | 13 ++++- src/models/cloudmodel.h | 11 +++- 8 files changed, 169 insertions(+), 95 deletions(-) diff --git a/ios/src/icloud_io.mm b/ios/src/icloud_io.mm index 94bea094..d0c4d253 100644 --- a/ios/src/icloud_io.mm +++ b/ios/src/icloud_io.mm @@ -147,17 +147,19 @@ ICloudIo::ICloudResult ICloudIo::moveFileToCloud( QString ICloudIo::copyFileFromCloud(const QString &cloudFilePath, const QString &localFilePath) { + // Test if file was downloaded from iCloud if (!isDownloadedFromCloud(cloudFilePath)) { NSLog(@"Local: File has not been downloaded yet from iCloud."); return QString(); } - NSError *error = nil; + // Convert string path to URL NSURL *fileCloudUrl = [NSURL URLWithString:cloudFilePath.toNSString()]; NSURL* localFileUrl = [NSURL fileURLWithPath:localFilePath.toNSString()]; NSString *fileName = [fileCloudUrl lastPathComponent]; // Create subdirectorues on Local + NSError *error = nil; if (![[NSFileManager defaultManager] createDirectoryAtURL:localFileUrl withIntermediateDirectories:YES attributes:nil error:&error]) { NSLog(@"Local: Create message subdirectories error: %@", error.localizedDescription); @@ -165,23 +167,23 @@ QString ICloudIo::copyFileFromCloud(const QString &cloudFilePath, } NSURL *fileLocalUrl = [localFileUrl URLByAppendingPathComponent:fileName]; - NSLog(@"iCloud url: %@", fileCloudUrl); - NSLog(@"Local file url: %@", fileLocalUrl); + //NSLog(@"iCloud url: %@", fileCloudUrl); + //NSLog(@"Local file url: %@", fileLocalUrl); + + // Remove file from local + [[NSFileManager defaultManager] removeItemAtURL:fileLocalUrl error:&error]; if ([[NSFileManager defaultManager] copyItemAtURL:fileCloudUrl toURL:fileLocalUrl error:&error]) { - NSLog(@"Local: File has been uploaded."); - return QString(); + NSLog(@"Local: File has copied to app sandbox."); + return QString::fromNSString(fileLocalUrl.absoluteString); } else { if (error.code == NSFileWriteFileExistsError) { NSLog(@"Local: File with the same name already exists."); - return QString(); } else if (error.code == NSFileReadNoSuchFileError) { - NSLog(@"Local: File has not been downloaded yet from iCloud."); - return QString(); + NSLog(@"Local: File has not downloaded yet from iCloud."); } else { - NSLog(@"Local: Error code: %zd %@", error.code, error.localizedDescription); + NSLog(@"Local: Error code: %zd %@", error.code, error); } - } return QString(); @@ -197,18 +199,22 @@ bool ICloudIo::isDownloadedFromCloud(const QString &cloudFilePath) NSError *error = nil; NSString *downloadStatus = nil; - NSURL *fileCloudUrl = [NSURL URLWithString:cloudFilePath.toNSString()]; if ([fileCloudUrl getResourceValue:&downloadStatus forKey:NSURLUbiquitousItemDownloadingStatusKey error:&error] == YES) { if ([downloadStatus isEqualToString:NSURLUbiquitousItemDownloadingStatusNotDownloaded] == YES) { - NSLog(@"File must be downloaded: %@", fileCloudUrl); + NSLog(@"File missing and must be downloaded: %@", fileCloudUrl); + [[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL:fileCloudUrl error:&error]; + return false; + } else if ([downloadStatus isEqualToString:NSURLUbiquitousItemDownloadingStatusDownloaded] == YES) { + NSLog(@"Up-to-date version of file must be downloaded: %@", fileCloudUrl); [[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL:fileCloudUrl error:&error]; return false; } else { - NSLog(@"File has been downloaded: %@", fileCloudUrl); + NSLog(@"Up-to-date version of file is available: %@", fileCloudUrl); + return true; } } - return true; + return false; } diff --git a/qml/dialogues/FileDialogue.qml b/qml/dialogues/FileDialogue.qml index 4e8a39bb..1d956781 100644 --- a/qml/dialogues/FileDialogue.qml +++ b/qml/dialogues/FileDialogue.qml @@ -62,6 +62,7 @@ Dialog { property int selectedFileIndex: -1 property bool showDirs: true property bool showFiles: true + property bool iOS: false signal finished(variant pathListModel) @@ -69,7 +70,8 @@ Dialog { return (root.showDirs && !root.showFiles); } - function raise(title, filters, showFiles, targetLocation) { + function raise(title, filters, showFiles, targetLocation, isiOS) { + iOS = isiOS if (targetLocation !== "") { folderNavigation.visible = false folderModel.folder = "file://" + targetLocation @@ -80,6 +82,9 @@ Dialog { if (filters !== "") { folderModel.nameFilters = filters } + if (iOS) { + folderModel.folder = standardLocationUrl(InteractionFilesystem.DOCUMENTS_LOCATION) + } selectedFileIndex = -1 // clear add path list model pathListModel.clear() @@ -128,6 +133,7 @@ Dialog { ColumnLayout { id: folderNavigation AccessibleComboBox { + visible: !iOS anchors { left: parent.left; right: parent.right; @@ -207,7 +213,7 @@ Dialog { showFiles: root.showFiles showDirsFirst: true nameFilters: ["*.*"] - folder: standardLocationUrl(InteractionFilesystem.DESKTOP_LOCATION) + folder: standardLocationUrl(InteractionFilesystem.DOCUMENTS_LOCATION) onFolderChanged: { selectedFileIndex = -1 pathField.text = stripUrlPrefix(folder) diff --git a/qml/dialogues/FileDialogueIos.qml b/qml/dialogues/FileDialogueIos.qml index c049c66d..4f73d66c 100644 --- a/qml/dialogues/FileDialogueIos.qml +++ b/qml/dialogues/FileDialogueIos.qml @@ -88,15 +88,19 @@ Dialog { target: iCloudHelper onCloudContentSig: { iCloudHelper.setCloudFileModel(cloudFileListModel, iCloudFileList); - if (cloudFileListModel.rowCount() > 1) { + if (cloudFileListModel.rowCount() > 0) { cloudFileList.visible = true - emptyList.visible = false + emptyList.visible = !cloudFileList.visible + } else { + emptyList.text = qsTr("No files in the iCloud.") + cloudFileList.visible = false + emptyList.visible = !cloudFileList.visible } } onCloudActivitySig: { emptyList.text = txt cloudFileList.visible = false - emptyList.visible = true + emptyList.visible = !cloudFileList.visible } } Component { @@ -128,7 +132,7 @@ Dialog { font.bold: true } Text { - text: rFileShortPath + text: "iCloud:" + rFileShortPath color: datovkaPalette.mid font.pointSize: textFontSizeSmall } @@ -143,7 +147,10 @@ Dialog { MouseArea { anchors.fill: parent onClicked: { - cloudFileListModel.setFileSelected(rFileCloudPath, !rFileSelected) + if (!rFileSelected) { + iCloudHelper.downloadFileFromCloud(rFileCloudPath) + } + cloudFileListModel.setFileSelected(rFileCloudPath) } } } // Rectangle @@ -198,7 +205,10 @@ Dialog { onAccepted: { for (var i = 0; i < cloudFileListModel.rowCount(); ++i) { if (cloudFileListModel.fileSelected(i)) { - pathListModel.append({path: cloudFileListModel.fileCloudPathFromRow(i)}) + var filePath = iCloudHelper.copyFileFromCloud(cloudFileListModel.fileCloudPathFromRow(i), cloudFileListModel.fileShortCloudPathFromRow(i)) + if (filePath !== "") { + pathListModel.append({path: filePath}) + } } } finished(pathListModel) diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index 6dac2a4f..078c1869 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -54,6 +54,8 @@ Item { property string text_COLOR_RED: "#ff0000" + property bool iOS: false + /* This property holds total attachment size in bytes */ property int totalAttachmentSizeBytes: 0 @@ -163,7 +165,30 @@ Item { } } + /* Append selected files to send model */ + function appendFilesToSendModel(pathListModel) { + var listLength = pathListModel.count + for (var j = 0; j < listLength; ++j) { + var isInFiletList = false + for (var i = 0; i < sendMsgAttachmentModel.rowCount(); i++) { + if (sendMsgAttachmentModel.filePathFromRow(i) === pathListModel.get(j).path) { + isInFiletList = true + break + } + } + if (!isInFiletList) { + var fileName = getFileNameFromPath(pathListModel.get(j).path) + var fileSizeBytes = files.getAttachmentSizeInBytes(pathListModel.get(j).path) + sendMsgAttachmentModel.appendFileFromPath(FileIdType.NO_FILE_ID, + fileName, pathListModel.get(j).path, fileSizeBytes) + totalAttachmentSizeBytes = sendMsgAttachmentModel.dataSizeSum() + } + } + pathListModel.clear() + } + Component.onCompleted: { + iOS = files.isIos() actionButton.enabled = false initPDZ.visible = false initPDZ.checked = false @@ -194,52 +219,13 @@ Item { FileDialogue { id: fileDialogue multiSelect: true - onFinished: { - var listLength = pathListModel.count - for (var j = 0; j < listLength; ++j) { - var isInFiletList = false - for (var i = 0; i < sendMsgAttachmentModel.rowCount(); i++) { - if (sendMsgAttachmentModel.filePathFromRow(i) === pathListModel.get(j).path) { - isInFiletList = true - break - } - } - if (!isInFiletList) { - var fileName = getFileNameFromPath(pathListModel.get(j).path) - var fileSizeBytes = files.getAttachmentSizeInBytes(pathListModel.get(j).path) - sendMsgAttachmentModel.appendFileFromPath(FileIdType.NO_FILE_ID, - fileName, pathListModel.get(j).path, fileSizeBytes) - totalAttachmentSizeBytes = sendMsgAttachmentModel.dataSizeSum() - } - } - pathListModel.clear() - } + onFinished: appendFilesToSendModel(pathListModel) } /* File dialog for choose of files from iCloud */ FileDialogueIos { id: fileDialogueIos - onFinished: { - var listLength = pathListModel.count - for (var j = 0; j < listLength; ++j) { - var localFilePath = iCloudHelper.downloadFileFromCloud(pathListModel.get(j).path) - var isInFiletList = false - for (var i = 0; i < sendMsgAttachmentModel.rowCount(); i++) { - if (sendMsgAttachmentModel.filePathFromRow(i) === localFilePath) { - isInFiletList = true - break - } - } - if (!isInFiletList) { - var fileName = getFileNameFromPath(localFilePath) - var fileSizeBytes = files.getAttachmentSizeInBytes(localFilePath) - sendMsgAttachmentModel.appendFileFromPath(FileIdType.NO_FILE_ID, - fileName, localFilePath, fileSizeBytes) - totalAttachmentSizeBytes = sendMsgAttachmentModel.dataSizeSum() - } - } - pathListModel.clear() - } + onFinished: appendFilesToSendModel(pathListModel) } /* Holds send message recipent list model */ @@ -524,12 +510,12 @@ Item { font.pointSize: defaultTextFont.font.pointSize text: qsTr("Add file") onClicked: { - fileDialogue.raise(qsTr("Select files"), ["*.*"], true, "") + fileDialogue.raise(qsTr("Select files"), ["*.*"], true, "", iOS) } } AccessibleButton { id: icloud - visible: files.isIos() + visible: iOS height: inputItemHeight font.pointSize: defaultTextFont.font.pointSize text: qsTr("iCloud") diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp index 676f2966..ebe797b3 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/icloud_helper.cpp @@ -21,9 +21,11 @@ * the two. */ +#include #include #include #include +#include #include "src/auxiliaries/icloud_helper.h" #include "src/io/filesystem.h" @@ -33,6 +35,7 @@ #ifdef Q_OS_IOS #include "ios/src/icloud_io.h" #define ICLOUD_DATOVKA_CONTAINER_NAME "Datovka" +#define ICLOUD_FILE_CONTAINER_ID "iCloud~cz~nic~mobile-datovka/Documents" #endif /* Q_OS_IOS */ #ifndef Q_OS_IOS @@ -54,7 +57,7 @@ QStringList testCloudPathList(void) void ICloudHelper::createCloudConteiner(void) { - debugSlotCall(); + debugFuncCall(); #ifdef Q_OS_IOS @@ -65,7 +68,7 @@ void ICloudHelper::createCloudConteiner(void) void ICloudHelper::getCloudHierarchyAsync(void) { - debugSlotCall(); + debugFuncCall(); #ifdef Q_OS_IOS @@ -94,7 +97,7 @@ void ICloudHelper::getCloudHierarchyAsync(void) void ICloudHelper::storeFilesToCloud(const QStringList &srcFilePaths, const QString &targetPath) { - debugSlotCall(); + debugFuncCall(); #ifdef Q_OS_IOS @@ -152,14 +155,12 @@ finish: /* Show final notification */ if (success) { - qCritical() << "Files have been saved to iCloud."; QMessageBox::critical(Q_NULLPTR, tr("iCloud saving"), tr("Files have been saved to iCloud.") + "\n\n" + tr("Path: '%1'").arg(QString(ICLOUD_DATOVKA_CONTAINER_NAME) + "/" + targetPath), QMessageBox::Ok); } else { - qCritical() << "Files upload to iCloud failed!"; QString txt; foreach (const QString &error, errorUploads) { txt += "\n" + error; @@ -178,22 +179,58 @@ finish: #endif /* Q_OS_IOS */ } -QString ICloudHelper::downloadFileFromCloud(const QString &cloudFilePath) +bool ICloudHelper::downloadFileFromCloud(const QString &cloudFilePath) { - debugSlotCall(); + debugFuncCall(); #ifdef Q_OS_IOS - QString localFilePath; - QFileInfo fi(cloudFilePath); - localFilePath = appSendDirPath() + QDir::separator() + - QUrl::fromPercentEncoding(fi.fileName().toUtf8()); + return ICloudIo::isDownloadedFromCloud(cloudFilePath); + +#else + Q_UNUSED(cloudFilePath); + return false; +#endif +} + +QString ICloudHelper::copyFileFromCloud(const QString &cloudFilePath, + const QString &localFilePath) +{ + debugFuncCall(); + +#ifdef Q_OS_IOS - ICloudIo::copyFileFromCloud(cloudFilePath, appSendDirPath()); - return localFilePath; + QString lfPath = ICloudIo::copyFileFromCloud(cloudFilePath, + appSendDirPath() + localFilePath); + if (!lfPath.isEmpty()) { + lfPath = QUrl::fromPercentEncoding(lfPath.toUtf8()); + lfPath = lfPath.replace("file://", ""); + return lfPath; + } else { + return QString(); + } #else Q_UNUSED(cloudFilePath); + Q_UNUSED(localFilePath); + return QString(); +#endif +} + +/* + * Retrun short iCloud path. + */ +QString getShortPathFromCloudPath(const QString &fileCloudPath) +{ +#ifdef Q_OS_IOS + + // Get short file path from iCloud path + QString pattern(ICLOUD_FILE_CONTAINER_ID); + int pos = fileCloudPath.indexOf(pattern) + pattern.length(); + return fileCloudPath.mid(pos); + +#else + Q_UNUSED(fileCloudPath); return QString(); #endif } @@ -201,9 +238,9 @@ QString ICloudHelper::downloadFileFromCloud(const QString &cloudFilePath) void ICloudHelper::setCloudFileModel(const QVariant &cloudFileModelVariant, const QStringList &fileList) { - debugSlotCall(); + debugFuncCall(); - /* Obtain pointer to attachment model. */ + /* Obtain pointer to icloud file model from variant. */ CloudFileListModel *cloudModel = CloudFileListModel::fromVariant(cloudFileModelVariant); if (cloudModel == Q_NULLPTR) { @@ -211,16 +248,18 @@ void ICloudHelper::setCloudFileModel(const QVariant &cloudFileModelVariant, return; } - foreach (const QString &file, fileList) { - QFileInfo fi(file); + foreach (const QString &fileCloudPath, fileList) { + + QFileInfo fi(fileCloudPath); if (fi.fileName().isEmpty()) { + // Skip directories continue; } - QString filePath = fi.path(); - QString pattern("iCloud~cz~nic~mobile-datovka/Documents"); - int pos = filePath.indexOf(pattern) + pattern.length(); - filePath = QStringLiteral("iCloud:Datovka") + filePath.mid(pos) + QStringLiteral("/"); - cloudModel->appendFileFromCloud(file, fi.fileName(), filePath, false); + + // Append file to model, false means file is unselected + cloudModel->appendFileFromCloud(fileCloudPath, + QUrl::fromPercentEncoding(fi.fileName().toUtf8()), + getShortPathFromCloudPath(fi.path()), false); } } @@ -242,9 +281,7 @@ void ICloudHelper::receivedCloudHierarchy(void) m_timer->stop(); disconnect(m_timer, SIGNAL(timeout()), this, SLOT(receivedCloudHierarchy())); - if (!iCloudFileList.isEmpty()) { - emit cloudContentSig(iCloudFileList); - } + emit cloudContentSig(iCloudFileList); } else { emit cloudActivitySig(tr("Searching for iCloud files...")); } diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/icloud_helper.h index d3950d59..8fee4a17 100644 --- a/src/auxiliaries/icloud_helper.h +++ b/src/auxiliaries/icloud_helper.h @@ -72,10 +72,21 @@ public: * @brief Download file from iCloud if not exists in the local storage. * * @param[in] cloudFilePath File path in iCloud. - * @return Full path where file will stored. + * @return True if success. */ Q_INVOKABLE - QString downloadFileFromCloud(const QString &cloudFilePath); + bool downloadFileFromCloud(const QString &cloudFilePath); + + /*! + * @brief Copy single file from iCloud continer to Datovka sandbox. + * + * @param[in] cloudFilePath Source file path. + * @param[in] localFilePath iCloud target path. + * @return Full path to app sandbox where file was stored. + */ + Q_INVOKABLE + QString copyFileFromCloud(const QString &cloudFilePath, + const QString &localFilePath); /*! * @brief Set iCloud file model in QML. diff --git a/src/models/cloudmodel.cpp b/src/models/cloudmodel.cpp index 0acf362e..5a40b101 100644 --- a/src/models/cloudmodel.cpp +++ b/src/models/cloudmodel.cpp @@ -167,6 +167,15 @@ QString CloudFileListModel::fileCloudPathFromRow(int row) return m_files[row].fileClaudPath(); } +QString CloudFileListModel::fileShortCloudPathFromRow(int row) +{ + if ((row < 0) || (row >= m_files.size())) { + return QString(); + } + + return m_files[row].fileShortPath(); +} + Qt::ItemFlags CloudFileListModel::flags(const QModelIndex &index) const { return QAbstractListModel::flags(index); @@ -181,7 +190,7 @@ void CloudFileListModel::appendFileFromCloud(const QString &fileClaudPath, endInsertRows(); } -void CloudFileListModel::setFileSelected(const QString &filePath, bool selected) +void CloudFileListModel::setFileSelected(const QString &filePath) { if (Q_UNLIKELY(filePath.isEmpty())) { return; @@ -190,7 +199,7 @@ void CloudFileListModel::setFileSelected(const QString &filePath, bool selected) for (int i = 0; i < m_files.size(); ++i) { if (filePath == m_files.at(i).fileClaudPath()) { beginResetModel(); - m_files[i].setSelected(selected); + m_files[i].setSelected(!m_files[i].selected()); endResetModel(); } } diff --git a/src/models/cloudmodel.h b/src/models/cloudmodel.h index 7063be55..6890c126 100644 --- a/src/models/cloudmodel.h +++ b/src/models/cloudmodel.h @@ -129,6 +129,15 @@ public: Q_INVOKABLE QString fileCloudPathFromRow(int row); + /*! + * @brief Return file path from attachment model. + * + * @param[in] row Row specifying the item. + * @return File path string. + */ + Q_INVOKABLE + QString fileShortCloudPathFromRow(int row); + /*! * @brief Returns item flags for given index. * @@ -160,7 +169,7 @@ public: * @brief Set file selection to the model. */ Q_INVOKABLE - void setFileSelected(const QString &filePath, bool selected); + void setFileSelected(const QString &filePath); /*! * @brief Get file selection from the model. -- GitLab From 08695739a06a9e65cacfd61fdc0c3e937b151e51 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 18 Dec 2018 15:54:32 +0100 Subject: [PATCH 06/22] Added document picker view controller for iOS --- ios/ios.pri | 2 + ios/src/doc_picker_controller.h | 31 +++++++++++ ios/src/doc_picker_controller.mm | 64 ++++++++++++++++++++++ ios/src/icloud_controller.mm | 1 + ios/src/icloud_io.h | 20 ++++++- ios/src/icloud_io.mm | 61 ++++++++++++++++++++- qml/dialogues/FileDialogueIos.qml | 1 + qml/pages/PageSendMessage.qml | 44 +++++++++++---- src/auxiliaries/icloud_helper.cpp | 90 +++++++++++++++++++++++++++---- src/auxiliaries/icloud_helper.h | 46 ++++++++++++++-- src/files.cpp | 1 - 11 files changed, 335 insertions(+), 26 deletions(-) create mode 100644 ios/src/doc_picker_controller.h create mode 100644 ios/src/doc_picker_controller.mm diff --git a/ios/ios.pri b/ios/ios.pri index 237ef3c1..956eb60a 100644 --- a/ios/ios.pri +++ b/ios/ios.pri @@ -9,6 +9,7 @@ LIBS = \ HEADERS += \ ios/src/app_delegate.h \ + ios/src/doc_picker_controller.h \ ios/src/doc_view_controller.h \ ios/src/qt_app_delegate.h \ ios/src/icloud_controller.h \ @@ -19,6 +20,7 @@ HEADERS += \ OBJECTIVE_SOURCES += \ ios/src/app_delegate.mm \ + ios/src/doc_picker_controller.mm \ ios/src/doc_view_controller.mm \ ios/src/icloud_controller.mm \ ios/src/icloud_io.mm \ diff --git a/ios/src/doc_picker_controller.h b/ios/src/doc_picker_controller.h new file mode 100644 index 00000000..7c5e80b0 --- /dev/null +++ b/ios/src/doc_picker_controller.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#import + +@interface DocumentPickerController : UIViewController + +- (void)openImportDocumentPicker; + +@end + diff --git a/ios/src/doc_picker_controller.mm b/ios/src/doc_picker_controller.mm new file mode 100644 index 00000000..57280504 --- /dev/null +++ b/ios/src/doc_picker_controller.mm @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014-2018 CZ.NIC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + */ + +#include + +#import "ios/src/doc_picker_controller.h" + +// External globals for transporting of async result back to C++ and QML. +QString selectedFilePath; +bool isControllerOpen; + +@interface DocumentPickerController () + +@end + +@implementation DocumentPickerController + +- (void)viewDidLoad { + [super viewDidLoad]; +} + +- (void)openImportDocumentPicker { + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.data"] inMode:UIDocumentPickerModeImport]; + documentPicker.delegate = self; + // Next property does not work with iOS < 11. + // Apple bug: Selected files are not picked if multiple select is on. + //documentPicker.allowsMultipleSelection = YES; + documentPicker.modalPresentationStyle = UIModalPresentationFormSheet; + [self presentViewController:documentPicker animated:YES completion:nil]; +} + +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url { + if (controller.documentPickerMode == UIDocumentPickerModeImport) { + selectedFilePath = QString::fromNSString(url.absoluteString); + isControllerOpen = false; + } +} + +- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { + Q_UNUSED(controller); + isControllerOpen = false; +} + +@end diff --git a/ios/src/icloud_controller.mm b/ios/src/icloud_controller.mm index af010d82..ad8d8c13 100644 --- a/ios/src/icloud_controller.mm +++ b/ios/src/icloud_controller.mm @@ -26,6 +26,7 @@ #import "ios/src/icloud_controller.h" #include "src/auxiliaries/icloud_helper.h" +// External globals for transporting of async results back to C++ and QML. QStringList iCloudFileList; bool isSearchRunning; diff --git a/ios/src/icloud_io.h b/ios/src/icloud_io.h index c06cb59c..12799a2c 100644 --- a/ios/src/icloud_io.h +++ b/ios/src/icloud_io.h @@ -88,7 +88,7 @@ public: * @brief Create and send async search query for iCloud hierarchy. */ static - void getCloudHierarchyAsync(void); + bool getCloudHierarchyAsync(void); /*! * @brief Upload single file into iCloud. @@ -109,4 +109,22 @@ public: */ static bool isDownloadedFromCloud(const QString &cloudFilePath); + + /*! + * @brief Create and open document picker controller. + * + * @return True if document picker controller is created and opened. + */ + static + bool openDocumentPickerController(void); + + /*! + * @brief Move file from app temporary inbox to local app sandbox. + * + * @param[in] oldFilePath Source file path from inbox. + * @param[in] newFilePath Target path to local app sandbox. + * @return Full path where file was moved. + */ + static + QString moveFile(const QString &oldFilePath, const QString &newFilePath); }; diff --git a/ios/src/icloud_io.mm b/ios/src/icloud_io.mm index d0c4d253..345f16a8 100644 --- a/ios/src/icloud_io.mm +++ b/ios/src/icloud_io.mm @@ -21,6 +21,7 @@ * the two. */ +#include "ios/src/doc_picker_controller.h" #include "ios/src/icloud_controller.h" #include "ios/src/icloud_io.h" @@ -98,11 +99,12 @@ QStringList ICloudIo::getCloudHierarchy(const QString &dir) return fileList; } -void ICloudIo::getCloudHierarchyAsync(void) +bool ICloudIo::getCloudHierarchyAsync(void) { static ICloudViewController* iCloudCntlr = nil; iCloudCntlr = [[ICloudViewController alloc] init]; [iCloudCntlr getCloudHierarchyAsync]; + return true; } ICloudIo::ICloudResult ICloudIo::moveFileToCloud( @@ -218,3 +220,60 @@ bool ICloudIo::isDownloadedFromCloud(const QString &cloudFilePath) return false; } + +bool ICloudIo::openDocumentPickerController(void) +{ + static DocumentPickerController* dpc = nil; + if (dpc != nil) { + [dpc removeFromParentViewController]; + [dpc release]; + } + + UIViewController *rootv = [[[[UIApplication sharedApplication]windows] firstObject]rootViewController]; + + if (rootv != nil) { + dpc = [[DocumentPickerController alloc] init]; + [rootv addChildViewController:dpc]; + [dpc openImportDocumentPicker]; + return true; + } + + return false; +} + +QString ICloudIo::moveFile(const QString &oldFilePath, + const QString &newFilePath) +{ + // Convert string path to URL + NSURL *ofp = [NSURL URLWithString:oldFilePath.toNSString()]; + NSURL *np = [NSURL fileURLWithPath:newFilePath.toNSString()]; + NSString *fileName = [ofp lastPathComponent]; + + // Create subdirectorues in the sandbox local storage + NSError *error = nil; + if (![[NSFileManager defaultManager] createDirectoryAtURL:np + withIntermediateDirectories:YES attributes:nil error:&error]) { + NSLog(@"Local storage: Create message subdirectories error: %@", error); + return QString(); + } + + NSURL *nfp = [np URLByAppendingPathComponent:fileName]; + //NSLog(@"TMP url: %@", ofp); + //NSLog(@"NP url: %@", np); + //NSLog(@"SEND url: %@", nfp); + + // Remove file from sandbox local storage if exists + [[NSFileManager defaultManager] removeItemAtURL:nfp error:&error]; + + if ([[NSFileManager defaultManager] moveItemAtURL:ofp toURL:nfp error:&error]) { + NSLog(@"Local storage: File has moved to target path."); + return QString::fromNSString(nfp.absoluteString); + } else { + if (error.code == NSFileWriteFileExistsError) { + NSLog(@"Local storage: File with the same name already exists in the target path."); + } else { + NSLog(@"Local storage: Error code: %zd %@", error.code, error); + } + return QString(); + } +} diff --git a/qml/dialogues/FileDialogueIos.qml b/qml/dialogues/FileDialogueIos.qml index 4f73d66c..5e193d72 100644 --- a/qml/dialogues/FileDialogueIos.qml +++ b/qml/dialogues/FileDialogueIos.qml @@ -215,5 +215,6 @@ Dialog { } onRejected: { pathListModel.clear() + iCloudHelper.stopCloudHierarchyAsync() } } diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index 078c1869..97c39f97 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -165,24 +165,29 @@ Item { } } - /* Append selected files to send model */ - function appendFilesToSendModel(pathListModel) { - var listLength = pathListModel.count - for (var j = 0; j < listLength; ++j) { + /* Append one file into send model */ + function appendFileToSendModel(filePath) { var isInFiletList = false for (var i = 0; i < sendMsgAttachmentModel.rowCount(); i++) { - if (sendMsgAttachmentModel.filePathFromRow(i) === pathListModel.get(j).path) { + if (sendMsgAttachmentModel.filePathFromRow(i) === filePath) { isInFiletList = true break } } if (!isInFiletList) { - var fileName = getFileNameFromPath(pathListModel.get(j).path) - var fileSizeBytes = files.getAttachmentSizeInBytes(pathListModel.get(j).path) + var fileName = getFileNameFromPath(filePath) + var fileSizeBytes = files.getAttachmentSizeInBytes(filePath) sendMsgAttachmentModel.appendFileFromPath(FileIdType.NO_FILE_ID, - fileName, pathListModel.get(j).path, fileSizeBytes) + fileName, filePath, fileSizeBytes) totalAttachmentSizeBytes = sendMsgAttachmentModel.dataSizeSum() } + } + + /* Append file list into send model */ + function appendFilesToSendModel(pathListModel) { + var listLength = pathListModel.count + for (var j = 0; j < listLength; ++j) { + appendFileToSendModel(pathListModel.get(j).path) } pathListModel.clear() } @@ -212,6 +217,9 @@ Item { } Component.onDestruction: { + if (iOS) { + iCloudHelper.clearSendDir() + } statusBar.visible = false } @@ -500,9 +508,17 @@ Item { //----ATTACHMENT SECTION------------ Item { id: tabAttachments + Connections { + target: iCloudHelper + onFileSelectedSig: { + if (filePath !== "") { + appendFileToSendModel(filePath) + } + } + } Row { id: buttonBar - spacing: formItemVerticalSpacing * 5 + spacing: formItemVerticalSpacing * 2 anchors.horizontalCenter: parent.horizontalCenter AccessibleButton { id: addFile @@ -523,6 +539,16 @@ Item { fileDialogueIos.raise(qsTr("Select files"), ["*.*"], true, "") } } + AccessibleButton { + id: storage + visible: iOS + height: inputItemHeight + font.pointSize: defaultTextFont.font.pointSize + text: qsTr("Storage") + onClicked: { + iCloudHelper.openDocumentPickerController() + } + } } Component { id: attachmentDelegate diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp index ebe797b3..be795ecb 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/icloud_helper.cpp @@ -45,7 +45,8 @@ bool isSearchRunning; ICloudHelper::ICloudHelper(QObject *parent) : QObject(parent), - m_timer(new QTimer(this)) + m_icloudTimer(new QTimer(this)), + m_dpcTimer(new QTimer(this)) { } @@ -80,12 +81,11 @@ void ICloudHelper::getCloudHierarchyAsync(void) } iCloudFileList.clear(); - isSearchRunning = true; - - connect(m_timer, SIGNAL(timeout()), this, SLOT(receivedCloudHierarchy())); - m_timer->start(100); - - ICloudIo::getCloudHierarchyAsync(); + isSearchRunning = ICloudIo::getCloudHierarchyAsync(); + if (isSearchRunning) { + connect(m_icloudTimer, SIGNAL(timeout()), this, SLOT(receivedCloudHierarchy())); + m_icloudTimer->start(200); + } #else @@ -94,6 +94,21 @@ void ICloudHelper::getCloudHierarchyAsync(void) #endif /* Q_OS_IOS */ } +void ICloudHelper::stopCloudHierarchyAsync(void) +{ + debugFuncCall(); + +#ifdef Q_OS_IOS + + m_icloudTimer->stop(); + isSearchRunning = false; + disconnect(m_icloudTimer, SIGNAL(timeout()), + this, SLOT(receivedCloudHierarchy())); + iCloudFileList.clear(); + +#endif /* Q_OS_IOS */ +} + void ICloudHelper::storeFilesToCloud(const QStringList &srcFilePaths, const QString &targetPath) { @@ -273,13 +288,11 @@ void ICloudHelper::clearSendDir(void) void ICloudHelper::receivedCloudHierarchy(void) { - debugSlotCall(); - #ifdef Q_OS_IOS if (!isSearchRunning) { - m_timer->stop(); - disconnect(m_timer, SIGNAL(timeout()), + m_icloudTimer->stop(); + disconnect(m_icloudTimer, SIGNAL(timeout()), this, SLOT(receivedCloudHierarchy())); emit cloudContentSig(iCloudFileList); } else { @@ -288,3 +301,58 @@ void ICloudHelper::receivedCloudHierarchy(void) #endif } + +void ICloudHelper::openDocumentPickerController(void) +{ + debugFuncCall(); + +#ifdef Q_OS_IOS + + if (isControllerOpen) { + return; + } + + selectedFilePath.clear(); + isControllerOpen = ICloudIo::openDocumentPickerController(); + if (isControllerOpen) { + connect(m_dpcTimer, SIGNAL(timeout()), + this, SLOT(receivedSelectedFilePath())); + m_dpcTimer->start(200); + } + +#endif /* Q_OS_IOS */ +} + +void ICloudHelper::receivedSelectedFilePath(void) +{ +#ifdef Q_OS_IOS + + if (!isControllerOpen) { + m_dpcTimer->stop(); + disconnect(m_dpcTimer, SIGNAL(timeout()), + this, SLOT(receivedSelectedFilePath())); + if (!selectedFilePath.isEmpty()) { + moveFileToSendDir(selectedFilePath); + selectedFilePath.clear(); + } + } + +#endif +} + +void ICloudHelper::moveFileToSendDir(const QString &filePath) +{ +#ifdef Q_OS_IOS + + if (filePath.isEmpty()) { + return; + } + QString lfPath = ICloudIo::moveFile(filePath, appSendDirPath()); + if (!lfPath.isEmpty()) { + lfPath = QUrl::fromPercentEncoding(lfPath.toUtf8()); + lfPath = lfPath.replace("file://", ""); + emit fileSelectedSig(lfPath); + } + +#endif +} diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/icloud_helper.h index 8fee4a17..178a3540 100644 --- a/src/auxiliaries/icloud_helper.h +++ b/src/auxiliaries/icloud_helper.h @@ -28,9 +28,15 @@ #include #include -/* Globals for iCloud search query results transfer. */ +/* + * External global variables. These are using for transporting of async results + * from native objective-C methods back to C++ and QML. + * Used for iCloud file search async query and async document picker controller. + */ extern QStringList iCloudFileList; extern bool isSearchRunning; +extern QString selectedFilePath; +extern bool isControllerOpen; /*! * @brief Provides iCloud interface for iOS. @@ -58,6 +64,12 @@ public: Q_INVOKABLE void getCloudHierarchyAsync(void); + /*! + * @brief Stop async search query for iCloud hierarchy. + */ + Q_INVOKABLE + void stopCloudHierarchyAsync(void); + /*! * @brief Store files to iCloud. * @@ -101,8 +113,15 @@ public: /*! * @brief Clear send folder. */ + Q_INVOKABLE void clearSendDir(void); + /*! + * @brief Create and open document picker controller. + */ + Q_INVOKABLE + void openDocumentPickerController(void); + signals: /*! @@ -119,6 +138,13 @@ signals: */ void cloudActivitySig(QString txt); + /*! + * @brief Is activated when a file has been chosen with document picker. + * + * @param[in] filePath Path file for QML. + */ + void fileSelectedSig(QString filePath); + private slots: /*! @@ -126,8 +152,22 @@ private slots: */ void receivedCloudHierarchy(void); + /*! + * @brief Is activated when a file has been chosen with document picker. + */ + void receivedSelectedFilePath(void); + private: - /* Detect if async search response with iCloud hierarchy finished. */ - QTimer *m_timer; + /*! + * @brief Move file from app temporary inbox to send path. + * + * @param[in] filePath Source file path from inbox. + */ + void moveFileToSendDir(const QString &filePath); + + /* Detect moment when async search response with iCloud hierarchy finished. */ + QTimer *m_icloudTimer; + /* Detect moment when a file was selected with document picker contorller. */ + QTimer *m_dpcTimer; }; diff --git a/src/files.cpp b/src/files.cpp index dbfdcb25..3a9e6afc 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -30,7 +30,6 @@ #if defined (Q_OS_ANDROID) #include "android/src/android_io.h" #endif -#include "ios/src/icloud_helper.h" #include "ios/src/url_opener.h" #include "src/auxiliaries/email_helper.h" #include "src/auxiliaries/icloud_helper.h" -- GitLab From 685dabd5af3d8529e31f731d35afbdcc2dfe3c94 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 8 Jan 2019 15:02:08 +0100 Subject: [PATCH 07/22] Show short send file path in QML --- qml/pages/PageSendMessage.qml | 6 +++++- src/auxiliaries/icloud_helper.cpp | 22 +++++++++++++++++++++- src/auxiliaries/icloud_helper.h | 9 +++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index 97c39f97..74626050 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -622,7 +622,11 @@ Item { font.bold: true } Text { - text: (rFilePath != "") ? rFilePath : qsTr("Local database") + text: if (rFilePath != "") { + iOS ? iCloudHelper.getShortSendFilePath(rFilePath) : rFilePath + } else { + qsTr("Local database") + } color: datovkaPalette.mid font.pointSize: textFontSizeSmall } diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp index be795ecb..903b040e 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/icloud_helper.cpp @@ -36,6 +36,7 @@ #include "ios/src/icloud_io.h" #define ICLOUD_DATOVKA_CONTAINER_NAME "Datovka" #define ICLOUD_FILE_CONTAINER_ID "iCloud~cz~nic~mobile-datovka/Documents" +#define SEND_FILE_PATH_PREFIX "Documents/Datovka" #endif /* Q_OS_IOS */ #ifndef Q_OS_IOS @@ -235,6 +236,7 @@ QString ICloudHelper::copyFileFromCloud(const QString &cloudFilePath, /* * Retrun short iCloud path. */ +static QString getShortPathFromCloudPath(const QString &fileCloudPath) { #ifdef Q_OS_IOS @@ -242,7 +244,7 @@ QString getShortPathFromCloudPath(const QString &fileCloudPath) // Get short file path from iCloud path QString pattern(ICLOUD_FILE_CONTAINER_ID); int pos = fileCloudPath.indexOf(pattern) + pattern.length(); - return fileCloudPath.mid(pos); + return QUrl::fromPercentEncoding(fileCloudPath.mid(pos).toUtf8()); #else Q_UNUSED(fileCloudPath); @@ -250,6 +252,24 @@ QString getShortPathFromCloudPath(const QString &fileCloudPath) #endif } +/* + * Retrun short local send file path. + */ +QString ICloudHelper::getShortSendFilePath(const QString &sandBoxFilePath) +{ +#ifdef Q_OS_IOS + + // Get short local send file path + QString pattern(SEND_FILE_PATH_PREFIX); + int pos = sandBoxFilePath.indexOf(pattern) + pattern.length(); + return QUrl::fromPercentEncoding(sandBoxFilePath.mid(pos).toUtf8()); + +#else + Q_UNUSED(sandBoxFilePath); + return QString(); +#endif +} + void ICloudHelper::setCloudFileModel(const QVariant &cloudFileModelVariant, const QStringList &fileList) { diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/icloud_helper.h index 178a3540..d8fd13d9 100644 --- a/src/auxiliaries/icloud_helper.h +++ b/src/auxiliaries/icloud_helper.h @@ -122,6 +122,15 @@ public: Q_INVOKABLE void openDocumentPickerController(void); + /*! + * @brief Retrun short local send file path. + * + * @param[in] sandBoxFilePath Send file path. + * @return Short path to app sandbox where send file was stored. + */ + Q_INVOKABLE + QString getShortSendFilePath(const QString &sandBoxFilePath); + signals: /*! -- GitLab From a209d4ad38cfbe4162c39d2e758e8cbad5bc220e Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Wed, 9 Jan 2019 14:39:17 +0100 Subject: [PATCH 08/22] Allow to export files to local storage of device --- ios/src/doc_picker_controller.h | 4 +- ios/src/doc_picker_controller.mm | 41 ++++++++++++++++---- ios/src/icloud_io.h | 10 +++-- ios/src/icloud_io.mm | 36 ++++++++++++++---- src/auxiliaries/icloud_helper.cpp | 62 ++++++++++++++++++++----------- src/auxiliaries/icloud_helper.h | 17 +++++++-- src/files.cpp | 27 ++++++++++++-- 7 files changed, 147 insertions(+), 50 deletions(-) diff --git a/ios/src/doc_picker_controller.h b/ios/src/doc_picker_controller.h index 7c5e80b0..797f4e29 100644 --- a/ios/src/doc_picker_controller.h +++ b/ios/src/doc_picker_controller.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2018 CZ.NIC + * Copyright (C) 2014-2019 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,6 @@ @interface DocumentPickerController : UIViewController - (void)openImportDocumentPicker; +- (void)openExportDocumentPicker:(NSArray*)exportUrls; @end - diff --git a/ios/src/doc_picker_controller.mm b/ios/src/doc_picker_controller.mm index 57280504..2b168ff1 100644 --- a/ios/src/doc_picker_controller.mm +++ b/ios/src/doc_picker_controller.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2018 CZ.NIC + * Copyright (C) 2014-2019 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,13 +21,14 @@ * the two. */ -#include +#include +#include #import "ios/src/doc_picker_controller.h" // External globals for transporting of async result back to C++ and QML. -QString selectedFilePath; -bool isControllerOpen; +QList selectedFileUrls; // Url list of selected files +bool isControllerOpen; // holds information if DocumentPickerController is active. @interface DocumentPickerController () @@ -39,21 +40,45 @@ bool isControllerOpen; [super viewDidLoad]; } +- (void)openExportDocumentPicker:(NSArray *)exportUrls { + + //NSLog(@"EXPORT FILE URLs: %@", exportUrls); + + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithURLs:exportUrls inMode:UIDocumentPickerModeExportToService]; + documentPicker.delegate = self; + documentPicker.modalPresentationStyle = UIModalPresentationFormSheet; + [self presentViewController:documentPicker animated:YES completion:nil]; +} + - (void)openImportDocumentPicker { + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.data"] inMode:UIDocumentPickerModeImport]; documentPicker.delegate = self; // Next property does not work with iOS < 11. // Apple bug: Selected files are not picked if multiple select is on. - //documentPicker.allowsMultipleSelection = YES; + documentPicker.allowsMultipleSelection = YES; documentPicker.modalPresentationStyle = UIModalPresentationFormSheet; [self presentViewController:documentPicker animated:YES completion:nil]; } -- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url { +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { + if (controller.documentPickerMode == UIDocumentPickerModeImport) { - selectedFilePath = QString::fromNSString(url.absoluteString); - isControllerOpen = false; + + NSLog(@"SELECTED FILE URLs: %@", urls); + QList qurls; + for (NSURL *url in urls) { + QUrl tmpUrl = QUrl::fromNSURL(url); + qurls.append(tmpUrl); + } + selectedFileUrls = qurls; + + } else if (controller.documentPickerMode == UIDocumentPickerModeExportToService) { + + NSLog(@"STORAGE FILE URLs: %@", urls); + } + isControllerOpen = false; } - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { diff --git a/ios/src/icloud_io.h b/ios/src/icloud_io.h index 12799a2c..dc060638 100644 --- a/ios/src/icloud_io.h +++ b/ios/src/icloud_io.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2018 CZ.NIC + * Copyright (C) 2014-2019 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include #include #include +#include /*! * @brief Provides objective-C IO methods for interaction with iCLoud. @@ -113,18 +114,19 @@ public: /*! * @brief Create and open document picker controller. * + * @param[in] exportFilesPath File paths for export (can be empty). * @return True if document picker controller is created and opened. */ static - bool openDocumentPickerController(void); + bool openDocumentPickerController(const QStringList &exportFilesPath); /*! * @brief Move file from app temporary inbox to local app sandbox. * - * @param[in] oldFilePath Source file path from inbox. + * @param[in] sourceFileUrl Source file url from inbox. * @param[in] newFilePath Target path to local app sandbox. * @return Full path where file was moved. */ static - QString moveFile(const QString &oldFilePath, const QString &newFilePath); + QUrl moveFile(const QUrl &sourceFileUrl, const QString &newFilePath); }; diff --git a/ios/src/icloud_io.mm b/ios/src/icloud_io.mm index 345f16a8..62d557d4 100644 --- a/ios/src/icloud_io.mm +++ b/ios/src/icloud_io.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2018 CZ.NIC + * Copyright (C) 2014-2019 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -221,7 +221,7 @@ bool ICloudIo::isDownloadedFromCloud(const QString &cloudFilePath) return false; } -bool ICloudIo::openDocumentPickerController(void) +bool ICloudIo::openDocumentPickerController(const QStringList &exportFilesPath) { static DocumentPickerController* dpc = nil; if (dpc != nil) { @@ -234,18 +234,38 @@ bool ICloudIo::openDocumentPickerController(void) if (rootv != nil) { dpc = [[DocumentPickerController alloc] init]; [rootv addChildViewController:dpc]; - [dpc openImportDocumentPicker]; + if (exportFilesPath.isEmpty()) { + // exportFilesPath is empty so import Document Picker will open + [dpc openImportDocumentPicker]; + } else { + // exportFilesPath is not empty so export Document Picker will open + NSMutableArray *exportUrls = [NSMutableArray array]; + // covert export file paths to array of nsurl + for (int i = 0; i < exportFilesPath.count(); ++i) { + QUrl url = QUrl::fromLocalFile(exportFilesPath.at(i)); + if (url.isValid()) { + NSURL *fileUrl = url.toNSURL(); + [exportUrls addObject:fileUrl]; + //NSLog(@"ADD FILE URL to list: %@", fileUrl); + } else { + NSLog(@"ERROR FILE URL: %@", url.toNSURL()); + } + } + if ([exportUrls count] > 0) { + [dpc openExportDocumentPicker:exportUrls]; + } + } return true; } return false; } -QString ICloudIo::moveFile(const QString &oldFilePath, +QUrl ICloudIo::moveFile(const QUrl &sourceFileUrl, const QString &newFilePath) { // Convert string path to URL - NSURL *ofp = [NSURL URLWithString:oldFilePath.toNSString()]; + NSURL *ofp = sourceFileUrl.toNSURL(); NSURL *np = [NSURL fileURLWithPath:newFilePath.toNSString()]; NSString *fileName = [ofp lastPathComponent]; @@ -254,7 +274,7 @@ QString ICloudIo::moveFile(const QString &oldFilePath, if (![[NSFileManager defaultManager] createDirectoryAtURL:np withIntermediateDirectories:YES attributes:nil error:&error]) { NSLog(@"Local storage: Create message subdirectories error: %@", error); - return QString(); + return QUrl(); } NSURL *nfp = [np URLByAppendingPathComponent:fileName]; @@ -267,13 +287,13 @@ QString ICloudIo::moveFile(const QString &oldFilePath, if ([[NSFileManager defaultManager] moveItemAtURL:ofp toURL:nfp error:&error]) { NSLog(@"Local storage: File has moved to target path."); - return QString::fromNSString(nfp.absoluteString); + return QUrl::fromNSURL(nfp); } else { if (error.code == NSFileWriteFileExistsError) { NSLog(@"Local storage: File with the same name already exists in the target path."); } else { NSLog(@"Local storage: Error code: %zd %@", error.code, error); } - return QString(); + return QUrl(); } } diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp index 903b040e..c95d0bae 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/icloud_helper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2018 CZ.NIC + * Copyright (C) 2014-2019 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,11 +21,9 @@ * the two. */ -#include #include #include #include -#include #include "src/auxiliaries/icloud_helper.h" #include "src/io/filesystem.h" @@ -51,12 +49,6 @@ ICloudHelper::ICloudHelper(QObject *parent) { } -/* TODO - removed this tmp function if all will done. */ -QStringList testCloudPathList(void) -{ - return QStringList() << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/Send/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/pokus/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/Send/app_delegate.mm" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6765996/priloha.txt" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6765996/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/pokus/dmg_background.png" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/pokus/datovka.svg" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/4i2xd7/623345688/DZ_623345688.zfo" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/Send/datovka.png" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/dokument.odt" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/4i2xd7/623345688/5000FC241550444E8BE9C217BF5247EA.pdf" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/notification.mp3" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/dokument.pdf" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/wh5ef3/6766050/obrazek.png" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/4i2xd7/" << "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cz~nic~mobile-datovka/Documents/4i2xd7/623345688/"; -} - void ICloudHelper::createCloudConteiner(void) { debugFuncCall(); @@ -304,6 +296,11 @@ void ICloudHelper::clearSendDir(void) QDir dir(appSendDirPath()); dir.removeRecursively(); +/* + // TODO - used for experiments and testing only + QDir dir2(dfltAttachSavingLoc()); + dir2.removeRecursively(); +*/ } void ICloudHelper::receivedCloudHierarchy(void) @@ -332,8 +329,8 @@ void ICloudHelper::openDocumentPickerController(void) return; } - selectedFilePath.clear(); - isControllerOpen = ICloudIo::openDocumentPickerController(); + selectedFileUrls.clear(); + isControllerOpen = ICloudIo::openDocumentPickerController(QStringList()); if (isControllerOpen) { connect(m_dpcTimer, SIGNAL(timeout()), this, SLOT(receivedSelectedFilePath())); @@ -345,34 +342,57 @@ void ICloudHelper::openDocumentPickerController(void) void ICloudHelper::receivedSelectedFilePath(void) { + #ifdef Q_OS_IOS if (!isControllerOpen) { m_dpcTimer->stop(); disconnect(m_dpcTimer, SIGNAL(timeout()), this, SLOT(receivedSelectedFilePath())); - if (!selectedFilePath.isEmpty()) { - moveFileToSendDir(selectedFilePath); - selectedFilePath.clear(); + + if (!selectedFileUrls.isEmpty()) { + foreach (const QUrl &fileUrl, selectedFileUrls) { + moveFileToSendDir(fileUrl); + } + selectedFileUrls.clear(); } } #endif } -void ICloudHelper::moveFileToSendDir(const QString &filePath) +void ICloudHelper::moveFileToSendDir(const QUrl &sourceFileUrl) { #ifdef Q_OS_IOS - if (filePath.isEmpty()) { + if (Q_UNLIKELY(!sourceFileUrl.isValid())) { return; } - QString lfPath = ICloudIo::moveFile(filePath, appSendDirPath()); - if (!lfPath.isEmpty()) { - lfPath = QUrl::fromPercentEncoding(lfPath.toUtf8()); - lfPath = lfPath.replace("file://", ""); - emit fileSelectedSig(lfPath); + + QUrl targetFileUrl = ICloudIo::moveFile(sourceFileUrl, appSendDirPath()); + if (targetFileUrl.isValid()) { + targetFileUrl.setScheme(QString()); + emit fileSelectedSig(targetFileUrl.toString()); } #endif } + +void ICloudHelper::storeFilesToDeviceStorage(const QStringList &srcFilePaths) +{ + debugFuncCall(); + +#ifdef Q_OS_IOS + + if (Q_UNLIKELY(srcFilePaths.isEmpty())) { + return; + } + + ICloudIo::openDocumentPickerController(srcFilePaths); + +#else /* !Q_OS_IOS */ + + Q_UNUSED(srcFilePaths); + +#endif /* Q_OS_IOS */ +} diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/icloud_helper.h index d8fd13d9..3a9477a6 100644 --- a/src/auxiliaries/icloud_helper.h +++ b/src/auxiliaries/icloud_helper.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2018 CZ.NIC + * Copyright (C) 2014-2019 CZ.NIC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,7 @@ #include #include #include +#include /* * External global variables. These are using for transporting of async results @@ -35,7 +36,7 @@ */ extern QStringList iCloudFileList; extern bool isSearchRunning; -extern QString selectedFilePath; +extern QList selectedFileUrls; extern bool isControllerOpen; /*! @@ -80,6 +81,14 @@ public: void storeFilesToCloud(const QStringList &srcFilePaths, const QString &targetPath); + /*! + * @brief Store files to device local storage. + * + * @param[in] srcFilePaths List of file paths to be saved into storage. + */ + Q_INVOKABLE + void storeFilesToDeviceStorage(const QStringList &srcFilePaths); + /*! * @brief Download file from iCloud if not exists in the local storage. * @@ -171,9 +180,9 @@ private: /*! * @brief Move file from app temporary inbox to send path. * - * @param[in] filePath Source file path from inbox. + * @param[in] sourceFileUrl Source file url from inbox. */ - void moveFileToSendDir(const QString &filePath); + void moveFileToSendDir(const QUrl &sourceFileUrl); /* Detect moment when async search response with iCloud hierarchy finished. */ QTimer *m_icloudTimer; diff --git a/src/files.cpp b/src/files.cpp index 3a9e6afc..421019f2 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -639,6 +640,27 @@ void Files::sendAttachmentEmailZfo(const QVariant &attachModelVariant, QString::number(msgId)); } +#ifdef Q_OS_IOS +static +void exportFilesiOS(const QStringList &destFilePaths, + const QString &targetDir) +{ + QMessageBox msgBox; + msgBox.setText(QObject::tr("You can export files into iCloud or into device local storage.")); + msgBox.setDetailedText(QObject::tr("Do you want to export files to Datovka iCloud container?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::No); + int ret = msgBox.exec(); + + if (ret == QMessageBox::Yes) { + ICloudHelper::storeFilesToCloud(destFilePaths, targetDir); + } else if (ret == QMessageBox::No) { + ICloudHelper ich; + ich.storeFilesToDeviceStorage(destFilePaths); + } +} +#endif + void Files::saveMsgFilesToDisk(const QString &userName, const QString &msgIdStr, MsgAttachFlags attachFlags) { @@ -723,8 +745,7 @@ void Files::saveMsgFilesToDisk(const QString &userName, #else - ICloudHelper::storeFilesToCloud(destFilePaths, joinDirs(userName, - msgIdStr)); + exportFilesiOS(destFilePaths, joinDirs(userName, msgIdStr)); #endif } @@ -763,7 +784,7 @@ void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant, #else - ICloudHelper::storeFilesToCloud(destFilePaths, msgIdStr); + exportFilesiOS(destFilePaths, msgIdStr); #endif } -- GitLab From e5d3af96e2055d292a01a31507d7c6dc58daf2d6 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Thu, 10 Jan 2019 10:24:32 +0100 Subject: [PATCH 09/22] Remove forgotten tmp function --- src/auxiliaries/icloud_helper.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp index c95d0bae..dff542f3 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/icloud_helper.cpp @@ -79,11 +79,6 @@ void ICloudHelper::getCloudHierarchyAsync(void) connect(m_icloudTimer, SIGNAL(timeout()), this, SLOT(receivedCloudHierarchy())); m_icloudTimer->start(200); } - -#else - - emit cloudContentSig(testCloudPathList()); - #endif /* Q_OS_IOS */ } @@ -375,6 +370,10 @@ void ICloudHelper::moveFileToSendDir(const QUrl &sourceFileUrl) emit fileSelectedSig(targetFileUrl.toString()); } +#else /* !Q_OS_IOS */ + + Q_UNUSED(sourceFileUrl); + #endif } -- GitLab From 0f91a37dd6f4e06532dddd361474f3dd61313099 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Thu, 10 Jan 2019 16:16:21 +0100 Subject: [PATCH 10/22] Removed timer and some global variables --- ios/src/doc_picker_controller.mm | 16 +++++++------- src/auxiliaries/icloud_helper.cpp | 35 +++++-------------------------- src/auxiliaries/icloud_helper.h | 16 +++++++------- src/global.cpp | 4 +++- src/global.h | 6 +++++- src/main.cpp | 9 +++++++- 6 files changed, 35 insertions(+), 51 deletions(-) diff --git a/ios/src/doc_picker_controller.mm b/ios/src/doc_picker_controller.mm index 2b168ff1..fcf09771 100644 --- a/ios/src/doc_picker_controller.mm +++ b/ios/src/doc_picker_controller.mm @@ -24,11 +24,10 @@ #include #include -#import "ios/src/doc_picker_controller.h" +#include "src/auxiliaries/icloud_helper.h" +#include "src/global.h" -// External globals for transporting of async result back to C++ and QML. -QList selectedFileUrls; // Url list of selected files -bool isControllerOpen; // holds information if DocumentPickerController is active. +#import "ios/src/doc_picker_controller.h" @interface DocumentPickerController () @@ -66,24 +65,23 @@ bool isControllerOpen; // holds information if DocumentPickerController is activ if (controller.documentPickerMode == UIDocumentPickerModeImport) { NSLog(@"SELECTED FILE URLs: %@", urls); - QList qurls; + QList qUrls; + // Convert NSURL on QUrl for all selected files for (NSURL *url in urls) { QUrl tmpUrl = QUrl::fromNSURL(url); - qurls.append(tmpUrl); + qUrls.append(tmpUrl); } - selectedFileUrls = qurls; + GlobInstcs::iCloudHelperPtr->importFilesToAppInbox(qUrls); } else if (controller.documentPickerMode == UIDocumentPickerModeExportToService) { NSLog(@"STORAGE FILE URLs: %@", urls); } - isControllerOpen = false; } - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { Q_UNUSED(controller); - isControllerOpen = false; } @end diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp index dff542f3..9738e0ee 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/icloud_helper.cpp @@ -44,8 +44,7 @@ bool isSearchRunning; ICloudHelper::ICloudHelper(QObject *parent) : QObject(parent), - m_icloudTimer(new QTimer(this)), - m_dpcTimer(new QTimer(this)) + m_icloudTimer(new QTimer(this)) { } @@ -320,40 +319,16 @@ void ICloudHelper::openDocumentPickerController(void) #ifdef Q_OS_IOS - if (isControllerOpen) { - return; - } - - selectedFileUrls.clear(); - isControllerOpen = ICloudIo::openDocumentPickerController(QStringList()); - if (isControllerOpen) { - connect(m_dpcTimer, SIGNAL(timeout()), - this, SLOT(receivedSelectedFilePath())); - m_dpcTimer->start(200); - } + ICloudIo::openDocumentPickerController(QStringList()); #endif /* Q_OS_IOS */ } -void ICloudHelper::receivedSelectedFilePath(void) +void ICloudHelper::importFilesToAppInbox(QList selectedFileUrls) { - -#ifdef Q_OS_IOS - - if (!isControllerOpen) { - m_dpcTimer->stop(); - disconnect(m_dpcTimer, SIGNAL(timeout()), - this, SLOT(receivedSelectedFilePath())); - - if (!selectedFileUrls.isEmpty()) { - foreach (const QUrl &fileUrl, selectedFileUrls) { - moveFileToSendDir(fileUrl); - } - selectedFileUrls.clear(); - } + foreach (const QUrl &fileUrl, selectedFileUrls) { + moveFileToSendDir(fileUrl); } - -#endif } void ICloudHelper::moveFileToSendDir(const QUrl &sourceFileUrl) diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/icloud_helper.h index 3a9477a6..a49d544c 100644 --- a/src/auxiliaries/icloud_helper.h +++ b/src/auxiliaries/icloud_helper.h @@ -36,8 +36,6 @@ */ extern QStringList iCloudFileList; extern bool isSearchRunning; -extern QList selectedFileUrls; -extern bool isControllerOpen; /*! * @brief Provides iCloud interface for iOS. @@ -140,6 +138,13 @@ public: Q_INVOKABLE QString getShortSendFilePath(const QString &sandBoxFilePath); + /*! + * @brief Is activated when iCloud hierarchy has been received. + * + * @param[in] selectedFileUrls Path file list of iCloud for QML. + */ + void importFilesToAppInbox(QList selectedFileUrls); + signals: /*! @@ -170,11 +175,6 @@ private slots: */ void receivedCloudHierarchy(void); - /*! - * @brief Is activated when a file has been chosen with document picker. - */ - void receivedSelectedFilePath(void); - private: /*! @@ -186,6 +186,4 @@ private: /* Detect moment when async search response with iCloud hierarchy finished. */ QTimer *m_icloudTimer; - /* Detect moment when a file was selected with document picker contorller. */ - QTimer *m_dpcTimer; }; diff --git a/src/global.cpp b/src/global.cpp index be6f6cf2..a957ab5e 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -8,7 +8,7 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License @@ -40,3 +40,5 @@ class RecordsManagementDb *GlobInstcs::recMgmtDbPtr = Q_NULLPTR; class AccountsMap *GlobInstcs::acntMapPtr = Q_NULLPTR; class ImageProvider *GlobInstcs::imgProvPtr = Q_NULLPTR; + +class ICloudHelper *GlobInstcs::iCloudHelperPtr = Q_NULLPTR; diff --git a/src/global.h b/src/global.h index 87fbb166..298870e4 100644 --- a/src/global.h +++ b/src/global.h @@ -8,7 +8,7 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License @@ -43,6 +43,8 @@ class AccountsMap; class ImageProvider; +class ICloudHelper; + /*! * @brief The namespace holds pointers to all globally accessible structures. */ @@ -75,4 +77,6 @@ namespace GlobInstcs { extern class ImageProvider *imgProvPtr; /*!< Image provider. */ + extern + class ICloudHelper *iCloudHelperPtr; /*!< iCloud helper. */ } diff --git a/src/main.cpp b/src/main.cpp index b9ae3611..9b13e1ca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -448,7 +448,6 @@ int main(int argc, char *argv[]) Messages messages; Accounts accounts; Files files; - ICloudHelper iCloudHelper; Log log(&memLog); IsdsWrapper isds; GovWrapper gov(&isds); @@ -458,6 +457,14 @@ int main(int argc, char *argv[]) StringManipulation strManipulation; Zfo zfo; + ICloudHelper iCloudHelper; + GlobInstcs::iCloudHelperPtr = new (std::nothrow) ICloudHelper; + if (GlobInstcs::iCloudHelperPtr == Q_NULLPTR) { + logErrorNL("%s", "Cannot create iCloud helper."); + return EXIT_FAILURE; + } + GlobInstcs::iCloudHelperPtr = &iCloudHelper; + /* Inicialize app delegate component for interaction with iOS * Reaction on the iOS action "Open in..." */ #if defined Q_OS_IOS -- GitLab From cc5a0e8e815ea385840de8d8badc08793d1752bb Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Thu, 10 Jan 2019 16:25:36 +0100 Subject: [PATCH 11/22] Minor code refactoring --- src/auxiliaries/icloud_helper.h | 1 - src/files.cpp | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/icloud_helper.h index a49d544c..55232c9d 100644 --- a/src/auxiliaries/icloud_helper.h +++ b/src/auxiliaries/icloud_helper.h @@ -75,7 +75,6 @@ public: * @param[in] srcFilePaths List of file paths to be saved into iCloud. * @param[in] targetPath Target iCloud path where files will store. */ - Q_INVOKABLE static void storeFilesToCloud(const QStringList &srcFilePaths, const QString &targetPath); diff --git a/src/files.cpp b/src/files.cpp index 421019f2..5ba09f3d 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -653,10 +653,9 @@ void exportFilesiOS(const QStringList &destFilePaths, int ret = msgBox.exec(); if (ret == QMessageBox::Yes) { - ICloudHelper::storeFilesToCloud(destFilePaths, targetDir); + GlobInstcs::iCloudHelperPtr->storeFilesToCloud(destFilePaths, targetDir); } else if (ret == QMessageBox::No) { - ICloudHelper ich; - ich.storeFilesToDeviceStorage(destFilePaths); + GlobInstcs::iCloudHelperPtr->storeFilesToDeviceStorage(destFilePaths); } } #endif -- GitLab From 1de1b1ea9da216c1865bfcd642cd318bfb0cd4b9 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Wed, 9 Jan 2019 14:39:17 +0100 Subject: [PATCH 12/22] Allow to export files to local storage of device --- src/auxiliaries/icloud_helper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp index 9738e0ee..98282f3c 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/icloud_helper.cpp @@ -333,6 +333,7 @@ void ICloudHelper::importFilesToAppInbox(QList selectedFileUrls) void ICloudHelper::moveFileToSendDir(const QUrl &sourceFileUrl) { + #ifdef Q_OS_IOS if (Q_UNLIKELY(!sourceFileUrl.isValid())) { -- GitLab From 8099f49371687b73cddf8cda7a9a080d2dfbd463 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Thu, 10 Jan 2019 17:29:42 +0100 Subject: [PATCH 13/22] Removed obsolete and unused code --- ios/ios.pri | 2 - ios/src/icloud_controller.h | 32 ----- ios/src/icloud_controller.mm | 68 --------- ios/src/icloud_io.h | 38 ----- ios/src/icloud_io.mm | 145 +------------------ mobile-datovka.pro | 2 - qml/dialogues/FileDialogueIos.qml | 220 ---------------------------- qml/pages/PageSendMessage.qml | 16 --- res/qml.qrc | 1 - src/auxiliaries/icloud_helper.cpp | 165 +-------------------- src/auxiliaries/icloud_helper.h | 81 ----------- src/main.cpp | 9 +- src/models/cloudmodel.cpp | 231 ------------------------------ src/models/cloudmodel.h | 200 -------------------------- 14 files changed, 6 insertions(+), 1204 deletions(-) delete mode 100644 ios/src/icloud_controller.h delete mode 100644 ios/src/icloud_controller.mm delete mode 100644 qml/dialogues/FileDialogueIos.qml delete mode 100644 src/models/cloudmodel.cpp delete mode 100644 src/models/cloudmodel.h diff --git a/ios/ios.pri b/ios/ios.pri index 956eb60a..dfc35cb6 100644 --- a/ios/ios.pri +++ b/ios/ios.pri @@ -12,7 +12,6 @@ HEADERS += \ ios/src/doc_picker_controller.h \ ios/src/doc_view_controller.h \ ios/src/qt_app_delegate.h \ - ios/src/icloud_controller.h \ ios/src/icloud_io.h \ ios/src/ios_file_opener.h \ ios/src/send_email_controller.h \ @@ -22,7 +21,6 @@ OBJECTIVE_SOURCES += \ ios/src/app_delegate.mm \ ios/src/doc_picker_controller.mm \ ios/src/doc_view_controller.mm \ - ios/src/icloud_controller.mm \ ios/src/icloud_io.mm \ ios/src/ios_file_opener.mm \ ios/src/send_email_controller.mm \ diff --git a/ios/src/icloud_controller.h b/ios/src/icloud_controller.h deleted file mode 100644 index 9cf4bf6a..00000000 --- a/ios/src/icloud_controller.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2014-2018 CZ.NIC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, the copyright holders give - * permission to link the code of portions of this program with the - * OpenSSL library under certain conditions as described in each - * individual source file, and distribute linked combinations including - * the two. - */ - -#import - -@interface ICloudViewController : UIViewController - -- (void)getCloudHierarchyAsync; - -@property (strong) NSMetadataQuery *query; - -@end diff --git a/ios/src/icloud_controller.mm b/ios/src/icloud_controller.mm deleted file mode 100644 index ad8d8c13..00000000 --- a/ios/src/icloud_controller.mm +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2014-2018 CZ.NIC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, the copyright holders give - * permission to link the code of portions of this program with the - * OpenSSL library under certain conditions as described in each - * individual source file, and distribute linked combinations including - * the two. - */ - -#include - -#import "ios/src/icloud_controller.h" -#include "src/auxiliaries/icloud_helper.h" - -// External globals for transporting of async results back to C++ and QML. -QStringList iCloudFileList; -bool isSearchRunning; - -@implementation ICloudViewController - -- (void)replyDataNotification:(NSNotification *)notification { - - NSMetadataQuery *query = [notification object]; - [query disableUpdates]; - [query stopQuery]; - - QStringList files; - - for (NSMetadataItem *item in [query results]) { - NSURL *documentURL = [item valueForAttribute:NSMetadataItemURLKey]; - files.append(QString::fromNSString(documentURL.absoluteString)); - //NSLog(@"iCloud:\n %@\n", documentURL); - } - - [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:self.query]; - self.query = nil; - - iCloudFileList = files; - isSearchRunning = false; -} - -- (void)getCloudHierarchyAsync { - - isSearchRunning = true; - self.query = [[NSMetadataQuery alloc] init]; - //[self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDataScope]]; - [self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]]; - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K like '*'", NSMetadataItemFSNameKey]; - [self.query setPredicate:predicate]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(replyDataNotification:) name:NSMetadataQueryDidFinishGatheringNotification object:self.query]; - [self.query startQuery]; -} - -@end diff --git a/ios/src/icloud_io.h b/ios/src/icloud_io.h index dc060638..88f4ce65 100644 --- a/ios/src/icloud_io.h +++ b/ios/src/icloud_io.h @@ -62,35 +62,6 @@ public: static bool isCloudOn(void); - /*! - * @brief Create Datovka iCloud conteiner if not exists. - */ - static - void createCloudConteiner(void); - - /*! - * @brief Copy single file from iCloud continer to Datovka sandbox. - * - * @param[in] cloudFilePath Source file path. - * @param[in] localFilePath iCloud target path. - * @return Full path where file will stored. - */ - static - QString copyFileFromCloud(const QString &cloudFilePath, - const QString &localFilePath); - - /*! - * @brief Search for local iCloud hierarchy - not used now. - */ - static - QStringList getCloudHierarchy(const QString &dir); - - /*! - * @brief Create and send async search query for iCloud hierarchy. - */ - static - bool getCloudHierarchyAsync(void); - /*! * @brief Upload single file into iCloud. * @@ -102,15 +73,6 @@ public: ICloudResult moveFileToCloud(const QString &srcFilePath, const QString &destFilePath); - /*! - * @brief Test if file exists in local iCloud conteiner. - * - * @param[in] cloudFilePath iCloud file path. - * @return True if file exists in local iCloud conteiner. - */ - static - bool isDownloadedFromCloud(const QString &cloudFilePath); - /*! * @brief Create and open document picker controller. * diff --git a/ios/src/icloud_io.mm b/ios/src/icloud_io.mm index 62d557d4..4af178ac 100644 --- a/ios/src/icloud_io.mm +++ b/ios/src/icloud_io.mm @@ -22,7 +22,6 @@ */ #include "ios/src/doc_picker_controller.h" -#include "ios/src/icloud_controller.h" #include "ios/src/icloud_io.h" ICloudIo::ICloudIo(QObject *parent) @@ -30,11 +29,11 @@ ICloudIo::ICloudIo(QObject *parent) { } -NSURL * getCloudBaseUrl() { +NSURL *getCloudBaseUrl() { return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; } -NSURL * getCloudDocumentsUrl(NSURL *baseURL) { +NSURL *getCloudDocumentsUrl(NSURL *baseURL) { return [baseURL URLByAppendingPathComponent:@"Documents"]; } @@ -42,71 +41,6 @@ bool ICloudIo::isCloudOn(void) { return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; } -void ICloudIo::createCloudConteiner(void) -{ - NSURL *baseURL = getCloudBaseUrl(); - if (!baseURL) { - NSLog(@"iCloud: Unable to access iCloud."); - return; - } - - // Create iCloud send message dir - NSURL *sendDirURL = [getCloudDocumentsUrl(baseURL) URLByAppendingPathComponent:@"Send"]; - - // Create send message subdirectorues for iCloud - NSError *error = nil; - if (![[NSFileManager defaultManager] createDirectoryAtURL:sendDirURL - withIntermediateDirectories:YES attributes:nil error:&error]) { - NSLog(@"iCloud: Create message subdirectories error: %@", error); - return; - } - - // Upload send dir into iCloud - if ([[NSFileManager defaultManager] setUbiquitous:YES - itemAtURL:sendDirURL destinationURL:sendDirURL error:&error]) { - NSLog(@"iCloud: File %@ has been uploaded.", sendDirURL); - } -} - -/* TODO - removed this tmp function */ -QStringList ICloudIo::getCloudHierarchy(const QString &dir) -{ - QStringList fileList; - - NSURL *baseURL = getCloudBaseUrl(); - if (!baseURL) { - NSLog(@"iCloud: Unable to access iCloud."); - return QStringList(); - } - - // Create iCloud target path - NSURL *documentURL = getCloudDocumentsUrl(baseURL); - NSURL *fileURL; - if (dir.isEmpty()) { - fileURL = documentURL; - } else { - fileURL = [documentURL URLByAppendingPathComponent:dir.toNSString()]; - } - - NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:fileURL includingPropertiesForKeys:nil options:0 error:nil]; - - for (NSUInteger i = 0; i < [directoryContent count]; i++) { - NSLog(@"iCloud:\n %@\n", directoryContent[i]); - NSURL *fileUrlx = directoryContent[i]; - fileList.append(QString::fromNSString(fileUrlx.absoluteString)); - } - - return fileList; -} - -bool ICloudIo::getCloudHierarchyAsync(void) -{ - static ICloudViewController* iCloudCntlr = nil; - iCloudCntlr = [[ICloudViewController alloc] init]; - [iCloudCntlr getCloudHierarchyAsync]; - return true; -} - ICloudIo::ICloudResult ICloudIo::moveFileToCloud( const QString &srcFilePath, const QString &destFilePath) { @@ -146,81 +80,6 @@ ICloudIo::ICloudResult ICloudIo::moveFileToCloud( } } -QString ICloudIo::copyFileFromCloud(const QString &cloudFilePath, - const QString &localFilePath) -{ - // Test if file was downloaded from iCloud - if (!isDownloadedFromCloud(cloudFilePath)) { - NSLog(@"Local: File has not been downloaded yet from iCloud."); - return QString(); - } - - // Convert string path to URL - NSURL *fileCloudUrl = [NSURL URLWithString:cloudFilePath.toNSString()]; - NSURL* localFileUrl = [NSURL fileURLWithPath:localFilePath.toNSString()]; - NSString *fileName = [fileCloudUrl lastPathComponent]; - - // Create subdirectorues on Local - NSError *error = nil; - if (![[NSFileManager defaultManager] createDirectoryAtURL:localFileUrl - withIntermediateDirectories:YES attributes:nil error:&error]) { - NSLog(@"Local: Create message subdirectories error: %@", error.localizedDescription); - return QString(); - } - - NSURL *fileLocalUrl = [localFileUrl URLByAppendingPathComponent:fileName]; - //NSLog(@"iCloud url: %@", fileCloudUrl); - //NSLog(@"Local file url: %@", fileLocalUrl); - - // Remove file from local - [[NSFileManager defaultManager] removeItemAtURL:fileLocalUrl error:&error]; - - if ([[NSFileManager defaultManager] copyItemAtURL:fileCloudUrl toURL:fileLocalUrl error:&error]) { - NSLog(@"Local: File has copied to app sandbox."); - return QString::fromNSString(fileLocalUrl.absoluteString); - } else { - if (error.code == NSFileWriteFileExistsError) { - NSLog(@"Local: File with the same name already exists."); - } else if (error.code == NSFileReadNoSuchFileError) { - NSLog(@"Local: File has not downloaded yet from iCloud."); - } else { - NSLog(@"Local: Error code: %zd %@", error.code, error); - } - } - - return QString(); -} - -bool ICloudIo::isDownloadedFromCloud(const QString &cloudFilePath) -{ - NSURL *baseURL = getCloudBaseUrl(); - if (!baseURL) { - NSLog(@"iCloud: Unable to access iCloud."); - return false; - } - - NSError *error = nil; - NSString *downloadStatus = nil; - NSURL *fileCloudUrl = [NSURL URLWithString:cloudFilePath.toNSString()]; - - if ([fileCloudUrl getResourceValue:&downloadStatus forKey:NSURLUbiquitousItemDownloadingStatusKey error:&error] == YES) { - if ([downloadStatus isEqualToString:NSURLUbiquitousItemDownloadingStatusNotDownloaded] == YES) { - NSLog(@"File missing and must be downloaded: %@", fileCloudUrl); - [[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL:fileCloudUrl error:&error]; - return false; - } else if ([downloadStatus isEqualToString:NSURLUbiquitousItemDownloadingStatusDownloaded] == YES) { - NSLog(@"Up-to-date version of file must be downloaded: %@", fileCloudUrl); - [[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL:fileCloudUrl error:&error]; - return false; - } else { - NSLog(@"Up-to-date version of file is available: %@", fileCloudUrl); - return true; - } - } - - return false; -} - bool ICloudIo::openDocumentPickerController(const QStringList &exportFilesPath) { static DocumentPickerController* dpc = nil; diff --git a/mobile-datovka.pro b/mobile-datovka.pro index 52de963e..55725d55 100644 --- a/mobile-datovka.pro +++ b/mobile-datovka.pro @@ -154,7 +154,6 @@ SOURCES += \ src/main.cpp \ src/messages.cpp \ src/models/accountmodel.cpp \ - src/models/cloudmodel.cpp \ src/models/databoxmodel.cpp \ src/models/filemodel.cpp \ src/models/list_sort_filter_proxy_model.cpp \ @@ -280,7 +279,6 @@ HEADERS += \ src/log.h \ src/messages.h \ src/models/accountmodel.h \ - src/models/cloudmodel.h \ src/models/databoxmodel.h \ src/models/filemodel.h \ src/models/list_sort_filter_proxy_model.h \ diff --git a/qml/dialogues/FileDialogueIos.qml b/qml/dialogues/FileDialogueIos.qml deleted file mode 100644 index 5e193d72..00000000 --- a/qml/dialogues/FileDialogueIos.qml +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2014-2018 CZ.NIC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, the copyright holders give - * permission to link the code of portions of this program with the - * OpenSSL library under certain conditions as described in each - * individual source file, and distribute linked combinations including - * the two. - */ - -import QtQuick 2.7 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import cz.nic.mobileDatovka 1.0 -import cz.nic.mobileDatovka.qmlInteraction 1.0 -import cz.nic.mobileDatovka.models 1.0 - -Dialog { - id: root - focus: true - modal: true - title: qsTr("Select files") - - footer: DialogButtonBox { - AccessibleButton { - text: qsTr("Cancel") - DialogButtonBox.buttonRole: DialogButtonBox.RejectRole - } - AccessibleButton { - text: qsTr("Add") - DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole - } - } - - /* Place the dialogue in the centre. */ - x: 2 * defaultMargin - y: 2 * defaultMargin - - height: parent.height - 4 * defaultMargin - width: parent.width - 4 * defaultMargin - - signal finished(variant pathListModel) - - function raise(title, filters, showFiles, targetLocation) { - cloudFileListModel.clearAll() - pathListModel.clear() - filterBar.filterField.text = "" - iCloudHelper.getCloudHierarchyAsync() - root.open() - } - - /* Holds cloud file list model */ - CloudFileListModel { - id: cloudFileListModel - Component.onCompleted: { - } - } - - ListSortFilterProxyModel { - id: proxyCloudFileListModel - Component.onCompleted: { - setFilterRoles([CloudFileListModel.ROLE_FILE_NAME, - CloudFileListModel.ROLE_FILE_SHORT_PATH]) - proxyCloudFileListModel.setSourceModel(cloudFileListModel) - } - } - - ListModel { - id: pathListModel - } - - contentItem: ColumnLayout { - spacing: formItemVerticalSpacing - Connections { - target: iCloudHelper - onCloudContentSig: { - iCloudHelper.setCloudFileModel(cloudFileListModel, iCloudFileList); - if (cloudFileListModel.rowCount() > 0) { - cloudFileList.visible = true - emptyList.visible = !cloudFileList.visible - } else { - emptyList.text = qsTr("No files in the iCloud.") - cloudFileList.visible = false - emptyList.visible = !cloudFileList.visible - } - } - onCloudActivitySig: { - emptyList.text = txt - cloudFileList.visible = false - emptyList.visible = !cloudFileList.visible - } - } - Component { - id: attachmentDelegate - Rectangle { - id: attachmentItem - width: parent.width - height: cloudFileList.delegateHeight - color: rFileSelected ? datovkaPalette.midlight : datovkaPalette.base - Image { - id: imageAttachment - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - sourceSize.height: cloudFileList.delegateHeight * 0.8 - source: files.getAttachmentFileIcon(rFileName) - } - Item { - anchors.left: imageAttachment.right - anchors.leftMargin: defaultMargin - height: parent.height - width: parent.width - Column { - width: parent.width - anchors.verticalCenter: parent.verticalCenter - spacing: defaultMargin - Text { - text: rFileName - color: datovkaPalette.text - font.bold: true - } - Text { - text: "iCloud:" + rFileShortPath - color: datovkaPalette.mid - font.pointSize: textFontSizeSmall - } - } - } - Rectangle { - anchors.top: parent.bottom - height: 1 - width: parent.width - color: datovkaPalette.dark - } - MouseArea { - anchors.fill: parent - onClicked: { - if (!rFileSelected) { - iCloudHelper.downloadFileFromCloud(rFileCloudPath) - } - cloudFileListModel.setFileSelected(rFileCloudPath) - } - } - } // Rectangle - } // Component - FilterBar { - id: filterBar - visible: true - Layout.fillWidth: true - height: filterField.height - color: (filterField.text.length === 0) ? datovkaPalette.alternateBase : - (cloudFileList.count > 0) ? "#afffaf" : "#ffafaf" - border.color: filterField.activeFocus ? "#0066ff" : "#bdbebf" - - placeholderText: qsTr("Set filter") - fontPointSize: defaultTextFont.font.pointSize - buttonImageHeight: imgHeight - buttonAccessibleName: qsTr("Clear and hide filter field") - - onTextChanged: { - proxyCloudFileListModel.setFilterRegExpStr(text) - } - - onClearClicked: { - filterField.text.clear - } - } - AccessibleText { - id: emptyList - visible: true - color: datovkaPalette.text - Layout.fillHeight: true - Layout.fillWidth: true - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.Wrap - text: qsTr("No files in the iCloud.") - } - ScrollableListView { - id: cloudFileList - visible: false - delegateHeight: headerHeight - Layout.fillHeight: true - Layout.fillWidth: true - clip: true - spacing: 1 - opacity: 1 - interactive: true - model: proxyCloudFileListModel - delegate: attachmentDelegate - ScrollIndicator.vertical: ScrollIndicator {} - } // Listview - } // ColumnLayout - onAccepted: { - for (var i = 0; i < cloudFileListModel.rowCount(); ++i) { - if (cloudFileListModel.fileSelected(i)) { - var filePath = iCloudHelper.copyFileFromCloud(cloudFileListModel.fileCloudPathFromRow(i), cloudFileListModel.fileShortCloudPathFromRow(i)) - if (filePath !== "") { - pathListModel.append({path: filePath}) - } - } - } - finished(pathListModel) - } - onRejected: { - pathListModel.clear() - iCloudHelper.stopCloudHierarchyAsync() - } -} diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index 74626050..98edd7e2 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -230,12 +230,6 @@ Item { onFinished: appendFilesToSendModel(pathListModel) } - /* File dialog for choose of files from iCloud */ - FileDialogueIos { - id: fileDialogueIos - onFinished: appendFilesToSendModel(pathListModel) - } - /* Holds send message recipent list model */ DataboxListModel { id: recipBoxModel @@ -529,16 +523,6 @@ Item { fileDialogue.raise(qsTr("Select files"), ["*.*"], true, "", iOS) } } - AccessibleButton { - id: icloud - visible: iOS - height: inputItemHeight - font.pointSize: defaultTextFont.font.pointSize - text: qsTr("iCloud") - onClicked: { - fileDialogueIos.raise(qsTr("Select files"), ["*.*"], true, "") - } - } AccessibleButton { id: storage visible: iOS diff --git a/res/qml.qrc b/res/qml.qrc index b56f2909..b0ed8512 100644 --- a/res/qml.qrc +++ b/res/qml.qrc @@ -131,7 +131,6 @@ ../qml/components/ScrollableListView.qml ../qml/dialogues/CalendarDialogue.qml ../qml/dialogues/FileDialogue.qml - ../qml/dialogues/FileDialogueIos.qml ../qml/dialogues/InputDialogue.qml ../qml/dialogues/MessageDialogue.qml ../qml/dialogues/PasteInputDialogue.qml diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/icloud_helper.cpp index 98282f3c..aff3a43d 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/icloud_helper.cpp @@ -27,75 +27,19 @@ #include "src/auxiliaries/icloud_helper.h" #include "src/io/filesystem.h" -#include "src/models/cloudmodel.h" #include "src/datovka_shared/log/log.h" #ifdef Q_OS_IOS #include "ios/src/icloud_io.h" #define ICLOUD_DATOVKA_CONTAINER_NAME "Datovka" -#define ICLOUD_FILE_CONTAINER_ID "iCloud~cz~nic~mobile-datovka/Documents" #define SEND_FILE_PATH_PREFIX "Documents/Datovka" #endif /* Q_OS_IOS */ -#ifndef Q_OS_IOS -QStringList iCloudFileList; -bool isSearchRunning; -#endif /* Q_OS_IOS */ - ICloudHelper::ICloudHelper(QObject *parent) - : QObject(parent), - m_icloudTimer(new QTimer(this)) + : QObject(parent) { } -void ICloudHelper::createCloudConteiner(void) -{ - debugFuncCall(); - -#ifdef Q_OS_IOS - - ICloudIo::createCloudConteiner(); - -#endif /* Q_OS_IOS */ -} - -void ICloudHelper::getCloudHierarchyAsync(void) -{ - debugFuncCall(); - -#ifdef Q_OS_IOS - - if (!ICloudIo::isCloudOn()) { - QMessageBox::critical(Q_NULLPTR, tr("iCloud error"), - tr("Unable to access iCloud Account. Open the Settings app and enter your Apple ID into iCloud settings."), - QMessageBox::Ok); - return; - } - - iCloudFileList.clear(); - isSearchRunning = ICloudIo::getCloudHierarchyAsync(); - if (isSearchRunning) { - connect(m_icloudTimer, SIGNAL(timeout()), this, SLOT(receivedCloudHierarchy())); - m_icloudTimer->start(200); - } -#endif /* Q_OS_IOS */ -} - -void ICloudHelper::stopCloudHierarchyAsync(void) -{ - debugFuncCall(); - -#ifdef Q_OS_IOS - - m_icloudTimer->stop(); - isSearchRunning = false; - disconnect(m_icloudTimer, SIGNAL(timeout()), - this, SLOT(receivedCloudHierarchy())); - iCloudFileList.clear(); - -#endif /* Q_OS_IOS */ -} - void ICloudHelper::storeFilesToCloud(const QStringList &srcFilePaths, const QString &targetPath) { @@ -174,70 +118,11 @@ finish: } #else /* !Q_OS_IOS */ - Q_UNUSED(srcFilePaths); Q_UNUSED(targetPath); - #endif /* Q_OS_IOS */ } -bool ICloudHelper::downloadFileFromCloud(const QString &cloudFilePath) -{ - debugFuncCall(); - -#ifdef Q_OS_IOS - - return ICloudIo::isDownloadedFromCloud(cloudFilePath); - -#else - Q_UNUSED(cloudFilePath); - return false; -#endif -} - -QString ICloudHelper::copyFileFromCloud(const QString &cloudFilePath, - const QString &localFilePath) -{ - debugFuncCall(); - -#ifdef Q_OS_IOS - - QString lfPath = ICloudIo::copyFileFromCloud(cloudFilePath, - appSendDirPath() + localFilePath); - if (!lfPath.isEmpty()) { - lfPath = QUrl::fromPercentEncoding(lfPath.toUtf8()); - lfPath = lfPath.replace("file://", ""); - return lfPath; - } else { - return QString(); - } - -#else - Q_UNUSED(cloudFilePath); - Q_UNUSED(localFilePath); - return QString(); -#endif -} - -/* - * Retrun short iCloud path. - */ -static -QString getShortPathFromCloudPath(const QString &fileCloudPath) -{ -#ifdef Q_OS_IOS - - // Get short file path from iCloud path - QString pattern(ICLOUD_FILE_CONTAINER_ID); - int pos = fileCloudPath.indexOf(pattern) + pattern.length(); - return QUrl::fromPercentEncoding(fileCloudPath.mid(pos).toUtf8()); - -#else - Q_UNUSED(fileCloudPath); - return QString(); -#endif -} - /* * Retrun short local send file path. */ @@ -256,34 +141,6 @@ QString ICloudHelper::getShortSendFilePath(const QString &sandBoxFilePath) #endif } -void ICloudHelper::setCloudFileModel(const QVariant &cloudFileModelVariant, - const QStringList &fileList) -{ - debugFuncCall(); - - /* Obtain pointer to icloud file model from variant. */ - CloudFileListModel *cloudModel = - CloudFileListModel::fromVariant(cloudFileModelVariant); - if (cloudModel == Q_NULLPTR) { - Q_ASSERT(0); - return; - } - - foreach (const QString &fileCloudPath, fileList) { - - QFileInfo fi(fileCloudPath); - if (fi.fileName().isEmpty()) { - // Skip directories - continue; - } - - // Append file to model, false means file is unselected - cloudModel->appendFileFromCloud(fileCloudPath, - QUrl::fromPercentEncoding(fi.fileName().toUtf8()), - getShortPathFromCloudPath(fi.path()), false); - } -} - void ICloudHelper::clearSendDir(void) { debugFuncCall(); @@ -297,22 +154,6 @@ void ICloudHelper::clearSendDir(void) */ } -void ICloudHelper::receivedCloudHierarchy(void) -{ -#ifdef Q_OS_IOS - - if (!isSearchRunning) { - m_icloudTimer->stop(); - disconnect(m_icloudTimer, SIGNAL(timeout()), - this, SLOT(receivedCloudHierarchy())); - emit cloudContentSig(iCloudFileList); - } else { - emit cloudActivitySig(tr("Searching for iCloud files...")); - } - -#endif -} - void ICloudHelper::openDocumentPickerController(void) { debugFuncCall(); @@ -347,9 +188,7 @@ void ICloudHelper::moveFileToSendDir(const QUrl &sourceFileUrl) } #else /* !Q_OS_IOS */ - Q_UNUSED(sourceFileUrl); - #endif } @@ -366,8 +205,6 @@ void ICloudHelper::storeFilesToDeviceStorage(const QStringList &srcFilePaths) ICloudIo::openDocumentPickerController(srcFilePaths); #else /* !Q_OS_IOS */ - Q_UNUSED(srcFilePaths); - #endif /* Q_OS_IOS */ } diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/icloud_helper.h index 55232c9d..81464e6b 100644 --- a/src/auxiliaries/icloud_helper.h +++ b/src/auxiliaries/icloud_helper.h @@ -26,17 +26,8 @@ #include #include #include -#include #include -/* - * External global variables. These are using for transporting of async results - * from native objective-C methods back to C++ and QML. - * Used for iCloud file search async query and async document picker controller. - */ -extern QStringList iCloudFileList; -extern bool isSearchRunning; - /*! * @brief Provides iCloud interface for iOS. */ @@ -52,23 +43,6 @@ public: */ ICloudHelper(QObject *parent = Q_NULLPTR); - /*! - * @brief Create Datovka iCloud conteiner if not exists. - */ - void createCloudConteiner(void); - - /*! - * @brief Create and send async search query for iCloud hierarchy. - */ - Q_INVOKABLE - void getCloudHierarchyAsync(void); - - /*! - * @brief Stop async search query for iCloud hierarchy. - */ - Q_INVOKABLE - void stopCloudHierarchyAsync(void); - /*! * @brief Store files to iCloud. * @@ -83,39 +57,8 @@ public: * * @param[in] srcFilePaths List of file paths to be saved into storage. */ - Q_INVOKABLE void storeFilesToDeviceStorage(const QStringList &srcFilePaths); - /*! - * @brief Download file from iCloud if not exists in the local storage. - * - * @param[in] cloudFilePath File path in iCloud. - * @return True if success. - */ - Q_INVOKABLE - bool downloadFileFromCloud(const QString &cloudFilePath); - - /*! - * @brief Copy single file from iCloud continer to Datovka sandbox. - * - * @param[in] cloudFilePath Source file path. - * @param[in] localFilePath iCloud target path. - * @return Full path to app sandbox where file was stored. - */ - Q_INVOKABLE - QString copyFileFromCloud(const QString &cloudFilePath, - const QString &localFilePath); - - /*! - * @brief Set iCloud file model in QML. - * - * @param[in,out] cloudFileModelVariant iCloud file model from QML. - * @param[in] fileList Path file list of iCloud. - */ - Q_INVOKABLE - void setCloudFileModel(const QVariant &cloudFileModelVariant, - const QStringList &fileList); - /*! * @brief Clear send folder. */ @@ -146,20 +89,6 @@ public: signals: - /*! - * @brief Is activated when iCloud hierarchy has been received. - * - * @param[in] iCloudFileList Path file list of iCloud for QML. - */ - void cloudContentSig(QStringList iCloudFileList); - - /*! - * @brief Is activated when an iCloud activity is processed. - * - * @param[in] txt Description what happened. - */ - void cloudActivitySig(QString txt); - /*! * @brief Is activated when a file has been chosen with document picker. * @@ -167,13 +96,6 @@ signals: */ void fileSelectedSig(QString filePath); -private slots: - - /*! - * @brief Is activated when iCloud hierarchy has been received. - */ - void receivedCloudHierarchy(void); - private: /*! @@ -182,7 +104,4 @@ private: * @param[in] sourceFileUrl Source file url from inbox. */ void moveFileToSendDir(const QUrl &sourceFileUrl); - - /* Detect moment when async search response with iCloud hierarchy finished. */ - QTimer *m_icloudTimer; }; diff --git a/src/main.cpp b/src/main.cpp index 9b13e1ca..0a974e3b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -57,7 +57,6 @@ #include "src/log.h" #include "src/net/isds_wrapper.h" #include "src/models/accountmodel.h" -#include "src/models/cloudmodel.h" #include "src/models/databoxmodel.h" #include "src/models/filemodel.h" #include "src/models/list_sort_filter_proxy_model.h" @@ -189,7 +188,6 @@ static const struct QmlTypeEntry qmlDialogues[] = { { "CalendarDialogue", 1, 0 }, { "FileDialogue", 1, 0 }, - { "FileDialogueIos", 1, 0 }, { "InputDialogue", 1, 0}, { "MessageDialogue", 1, 0}, { "PasteInputDialogue", 1, 0 }, @@ -468,10 +466,8 @@ int main(int argc, char *argv[]) /* Inicialize app delegate component for interaction with iOS * Reaction on the iOS action "Open in..." */ #if defined Q_OS_IOS - QtAppDelegateInitialize(&interactionZfoFile); - /* Create Datovka iCloud conteiner if not exists .*/ - iCloudHelper.createCloudConteiner(); + QtAppDelegateInitialize(&interactionZfoFile); /* Clear send dir. */ iCloudHelper.clearSendDir(); @@ -502,7 +498,6 @@ int main(int argc, char *argv[]) /* Register types into QML. */ AccountListModel::declareQML(); - CloudFileListModel::declareQML(); DataboxListModel::declareQML(); DataboxModelEntry::declareQML(); Dialogues::declareQML(); @@ -767,6 +762,8 @@ int main(int argc, char *argv[]) delete GlobInstcs::workPoolPtr; GlobInstcs::workPoolPtr = Q_NULLPTR; delete GlobInstcs::msgProcEmitterPtr; GlobInstcs::msgProcEmitterPtr = Q_NULLPTR; + + delete GlobInstcs::iCloudHelperPtr; GlobInstcs::iCloudHelperPtr = Q_NULLPTR; } /* Finally, destroy global log object. */ diff --git a/src/models/cloudmodel.cpp b/src/models/cloudmodel.cpp deleted file mode 100644 index 5a40b101..00000000 --- a/src/models/cloudmodel.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2014-2018 CZ.NIC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, the copyright holders give - * permission to link the code of portions of this program with the - * OpenSSL library under certain conditions as described in each - * individual source file, and distribute linked combinations including - * the two. - */ - -#include /* qmlRegisterType */ - -#include "src/models/cloudmodel.h" - -CloudFileListModel::Entry::Entry(const Entry &fme) - : m_fileClaudPath(fme.m_fileClaudPath), - m_fileName(fme.m_fileName), - m_fileShortPath(fme.m_fileShortPath), - m_selected(fme.m_selected) -{ -} - -CloudFileListModel::Entry::Entry(const QString &fileClaudPath, - const QString &fileName, const QString &fileShortPath, bool selected) - : m_fileClaudPath(fileClaudPath), - m_fileName(fileName), - m_fileShortPath(fileShortPath), - m_selected(selected) -{ -} - -QString CloudFileListModel::Entry::fileClaudPath(void) const -{ - return m_fileClaudPath; -} - -void CloudFileListModel::Entry::setFileClaudPath(const QString &fileClaudPath) -{ - m_fileClaudPath = fileClaudPath; -} - -QString CloudFileListModel::Entry::fileName(void) const -{ - return m_fileName; -} - -void CloudFileListModel::Entry::setFileName(const QString &fileName) -{ - m_fileName = fileName; -} - -QString CloudFileListModel::Entry::fileShortPath(void) const -{ - return m_fileShortPath; -} - -void CloudFileListModel::Entry::setFileShortPath(const QString &fileShortPath) -{ - m_fileShortPath = fileShortPath; -} - -bool CloudFileListModel::Entry::selected(void) const -{ - return m_selected; -} - -void CloudFileListModel::Entry::setSelected(bool selected) -{ - m_selected = selected; -} - -void CloudFileListModel::declareQML(void) -{ - qmlRegisterType("cz.nic.mobileDatovka.models", 1, 0, "CloudFileListModel"); - qRegisterMetaType(); - qRegisterMetaType(); -} - -CloudFileListModel::CloudFileListModel(QObject *parent) - : QAbstractListModel(parent), - m_files() -{ -} - -CloudFileListModel::CloudFileListModel(const CloudFileListModel &model, QObject *parent) - : QAbstractListModel(parent), - m_files(model.m_files) -{ -} - -int CloudFileListModel::rowCount(const QModelIndex &parent) const -{ - return !parent.isValid() ? m_files.size() : 0; -} - -QHash CloudFileListModel::roleNames(void) const -{ - static QHash roles; - if (roles.isEmpty()) { - roles[ROLE_FILE_CLOUD_PATH] = "rFileCloudPath"; - roles[ROLE_FILE_NAME] = "rFileName"; - roles[ROLE_FILE_SHORT_PATH] = "rFileShortPath"; - roles[ROLE_FILE_SELECTED] = "rFileSelected"; - } - return roles; -} - -QVariant CloudFileListModel::data(const QModelIndex &index, int role) const -{ - if ((index.row() < 0) || (index.row() >= m_files.size())) { - return QVariant(); - } - - const Entry &file(m_files[index.row()]); - - switch (role) { - case ROLE_FILE_CLOUD_PATH: - return file.fileClaudPath(); - break; - case ROLE_FILE_NAME: - return file.fileName(); - break; - case ROLE_FILE_SHORT_PATH: - return file.fileShortPath(); - break; - case ROLE_FILE_SELECTED: - return file.selected(); - break; - default: - /* Do nothing. */ - break; - } - - return QVariant(); -} - -QList CloudFileListModel::allEntries(void) const -{ - QList entries; - - foreach (const CloudFileListModel::Entry &file, m_files) { - entries.append(file); - } - - return entries; -} - -QString CloudFileListModel::fileCloudPathFromRow(int row) -{ - if ((row < 0) || (row >= m_files.size())) { - return QString(); - } - - return m_files[row].fileClaudPath(); -} - -QString CloudFileListModel::fileShortCloudPathFromRow(int row) -{ - if ((row < 0) || (row >= m_files.size())) { - return QString(); - } - - return m_files[row].fileShortPath(); -} - -Qt::ItemFlags CloudFileListModel::flags(const QModelIndex &index) const -{ - return QAbstractListModel::flags(index); -} - -void CloudFileListModel::appendFileFromCloud(const QString &fileClaudPath, - const QString &fileName, const QString &fileShortPath, bool selected) -{ - beginInsertRows(QModelIndex(), rowCount(), rowCount()); - m_files.append(CloudFileListModel::Entry(fileClaudPath, fileName, - fileShortPath, selected)); - endInsertRows(); -} - -void CloudFileListModel::setFileSelected(const QString &filePath) -{ - if (Q_UNLIKELY(filePath.isEmpty())) { - return; - } - - for (int i = 0; i < m_files.size(); ++i) { - if (filePath == m_files.at(i).fileClaudPath()) { - beginResetModel(); - m_files[i].setSelected(!m_files[i].selected()); - endResetModel(); - } - } -} - -void CloudFileListModel::clearAll(void) -{ - beginResetModel(); - m_files.clear(); - endResetModel(); -} - -bool CloudFileListModel::fileSelected(int row) -{ - if ((row < 0) || (row >= m_files.size())) { - return false; - } - - return m_files[row].selected(); -} - -CloudFileListModel *CloudFileListModel::fromVariant(const QVariant &modelVariant) -{ - if (!modelVariant.canConvert()) { - return Q_NULLPTR; - } - QObject *obj = qvariant_cast(modelVariant); - return qobject_cast(obj); -} diff --git a/src/models/cloudmodel.h b/src/models/cloudmodel.h deleted file mode 100644 index 6890c126..00000000 --- a/src/models/cloudmodel.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2014-2018 CZ.NIC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, the copyright holders give - * permission to link the code of portions of this program with the - * OpenSSL library under certain conditions as described in each - * individual source file, and distribute linked combinations including - * the two. - */ - -#pragma once - -#include - -class CloudFileListModel : public QAbstractListModel { - Q_OBJECT - -public: - class Entry { - public: - Entry(const Entry &fme); - Entry(const QString &fileClaudPath, const QString &fileName, - const QString &fileShortPath, bool selected); - - QString fileClaudPath(void) const; - void setFileClaudPath(const QString &fileClaudPath); - QString fileName(void) const; - void setFileName(const QString &fileName); - QString fileShortPath(void) const; - void setFileShortPath(const QString &fileShortPath); - bool selected(void) const; - void setSelected(bool selected); - - private: - - QString m_fileClaudPath; /*!< Full file path to cloud. */ - QString m_fileName; /*!< File name. */ - QString m_fileShortPath; /*!< Short file path for QML. */ - bool m_selected; /*!< Is file selected for download. */ - }; - - /*! - * @brief Roles which this model supports. - */ - enum Roles { - ROLE_FILE_CLOUD_PATH = Qt::UserRole, - ROLE_FILE_NAME, - ROLE_FILE_SHORT_PATH, - ROLE_FILE_SELECTED - }; - Q_ENUM(Roles) - - /* Don't forget to declare various properties to the QML system. */ - static - void declareQML(void); - - /*! - * @brief Constructor. - * - * @param[in] parent Pointer to parent object. - */ - explicit CloudFileListModel(QObject *parent = Q_NULLPTR); - - /*! - * @brief Copy constructor. - * - * @note Needed for QVariant conversion. - * - * @param[in] model Model to be copied. - * @param[in] parent Pointer to parent object. - */ - explicit CloudFileListModel(const CloudFileListModel &model, - QObject *parent = Q_NULLPTR); - - /*! - * @brief Return number of rows under the given parent. - * - * @param[in] parent Parent node index. - * @return Number of rows. - */ - virtual - int rowCount(const QModelIndex &parent = QModelIndex()) const - Q_DECL_OVERRIDE; - - /*! - * @brief Returns the model's role names. - * - * @return Model's role names. - */ - virtual - QHash roleNames(void) const Q_DECL_OVERRIDE; - - /*! - * @brief Return data stored in given location under given role. - * - * @param[in] index Index specifying the item. - * @param[in] role Data role. - * @return Data from model. - */ - virtual - QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; - - /*! - * @brief Return list of all entries. - * - * @return List of all entries. - */ - QList allEntries(void) const; - - /*! - * @brief Return file path from attachment model. - * - * @param[in] row Row specifying the item. - * @return File path string. - */ - Q_INVOKABLE - QString fileCloudPathFromRow(int row); - - /*! - * @brief Return file path from attachment model. - * - * @param[in] row Row specifying the item. - * @return File path string. - */ - Q_INVOKABLE - QString fileShortCloudPathFromRow(int row); - - /*! - * @brief Returns item flags for given index. - * - * @brief[in] index Index specifying the item. - * @return Item flags. - */ - virtual - Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; - - /*! - * @brief Appends file from cloud to model. - * - * @param[in] fileClaudPath Full cloud file path. - * @param[in] fileName File name. - * @param[in] fileShortPath Shrot file path. - * @param[in] selected File selected. - */ - Q_INVOKABLE - void appendFileFromCloud(const QString &fileClaudPath, - const QString &fileName, const QString &fileShortPath, bool selected); - - /*! - * @brief Clears the model. - */ - Q_INVOKABLE - void clearAll(void); - - /*! - * @brief Set file selection to the model. - */ - Q_INVOKABLE - void setFileSelected(const QString &filePath); - - /*! - * @brief Get file selection from the model. - */ - Q_INVOKABLE - bool fileSelected(int row); - - /*! - * @brief Converts QVariant obtained from QML into model pointer. - * - * @note Some weird stuff happens in QML when passing attachment model - * directly as constant reference. Wrong constructors are called - * and no data are passed. That's because we are using a QVariant. - * This function does not allocate a new model. - * - * @param[in] modelVariant QVariant holding the model. - * @return Pointer to model if it could be acquired, Q_NULLPTR else. - */ - static - CloudFileListModel *fromVariant(const QVariant &modelVariant); - -private: - QList m_files; /*!< List of attachment entries. */ -}; - -/* QML passes its arguments via QVariant. */ -Q_DECLARE_METATYPE(CloudFileListModel) -Q_DECLARE_METATYPE(CloudFileListModel::Roles) -- GitLab From 4207598a7c31c15f83293935f9a1fc1592983799 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 22 Jan 2019 10:02:39 +0100 Subject: [PATCH 14/22] Code refactoring --- ios/src/doc_picker_controller.h | 4 +-- ios/src/doc_picker_controller.mm | 8 +++--- ios/src/icloud_io.h | 4 +-- ios/src/icloud_io.mm | 4 +-- mobile-datovka.pro | 4 +-- qml/pages/PageSendMessage.qml | 10 ++++---- .../{icloud_helper.cpp => ios_helper.cpp} | 25 ++++++++----------- .../{icloud_helper.h => ios_helper.h} | 16 ++++++------ src/files.cpp | 6 ++--- src/global.cpp | 4 +-- src/global.h | 6 ++--- src/main.cpp | 18 ++++++------- 12 files changed, 53 insertions(+), 56 deletions(-) rename src/auxiliaries/{icloud_helper.cpp => ios_helper.cpp} (86%) rename src/auxiliaries/{icloud_helper.h => ios_helper.h} (82%) diff --git a/ios/src/doc_picker_controller.h b/ios/src/doc_picker_controller.h index 797f4e29..276a506e 100644 --- a/ios/src/doc_picker_controller.h +++ b/ios/src/doc_picker_controller.h @@ -8,11 +8,11 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the diff --git a/ios/src/doc_picker_controller.mm b/ios/src/doc_picker_controller.mm index fcf09771..9cfa1c69 100644 --- a/ios/src/doc_picker_controller.mm +++ b/ios/src/doc_picker_controller.mm @@ -8,11 +8,11 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the @@ -24,7 +24,7 @@ #include #include -#include "src/auxiliaries/icloud_helper.h" +#include "src/auxiliaries/ios_helper.h" #include "src/global.h" #import "ios/src/doc_picker_controller.h" @@ -71,7 +71,7 @@ QUrl tmpUrl = QUrl::fromNSURL(url); qUrls.append(tmpUrl); } - GlobInstcs::iCloudHelperPtr->importFilesToAppInbox(qUrls); + GlobInstcs::iOSHelperPtr->importFilesToAppInbox(qUrls); } else if (controller.documentPickerMode == UIDocumentPickerModeExportToService) { diff --git a/ios/src/icloud_io.h b/ios/src/icloud_io.h index 88f4ce65..db330246 100644 --- a/ios/src/icloud_io.h +++ b/ios/src/icloud_io.h @@ -8,11 +8,11 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the diff --git a/ios/src/icloud_io.mm b/ios/src/icloud_io.mm index 4af178ac..66b23fbf 100644 --- a/ios/src/icloud_io.mm +++ b/ios/src/icloud_io.mm @@ -8,11 +8,11 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the diff --git a/mobile-datovka.pro b/mobile-datovka.pro index 55725d55..49e77d72 100644 --- a/mobile-datovka.pro +++ b/mobile-datovka.pro @@ -90,7 +90,7 @@ TRANSLATIONS_FILES += \ SOURCES += \ src/accounts.cpp \ src/auxiliaries/email_helper.cpp \ - src/auxiliaries/icloud_helper.cpp \ + src/auxiliaries/ios_helper.cpp \ src/datovka_shared/gov_services/helper.cpp \ src/datovka_shared/gov_services/service/gov_mv_crr_vbh.cpp \ src/datovka_shared/gov_services/service/gov_mv_ir_vp.cpp \ @@ -212,7 +212,7 @@ SOURCES += \ HEADERS += \ src/accounts.h \ src/auxiliaries/email_helper.h \ - src/auxiliaries/icloud_helper.h \ + src/auxiliaries/ios_helper.h \ src/common.h \ src/datovka_shared/gov_services/helper.h \ src/datovka_shared/gov_services/service/gov_mv_crr_vbh.h \ diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index 98edd7e2..e62e47d3 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -218,7 +218,7 @@ Item { Component.onDestruction: { if (iOS) { - iCloudHelper.clearSendDir() + iOSHelper.clearSendDir() } statusBar.visible = false } @@ -503,7 +503,7 @@ Item { Item { id: tabAttachments Connections { - target: iCloudHelper + target: iOSHelper onFileSelectedSig: { if (filePath !== "") { appendFileToSendModel(filePath) @@ -530,7 +530,7 @@ Item { font.pointSize: defaultTextFont.font.pointSize text: qsTr("Storage") onClicked: { - iCloudHelper.openDocumentPickerController() + iOSHelper.openDocumentPickerController() } } } @@ -607,10 +607,10 @@ Item { } Text { text: if (rFilePath != "") { - iOS ? iCloudHelper.getShortSendFilePath(rFilePath) : rFilePath + iOS ? iOSHelper.getShortSendFilePath(rFilePath) : rFilePath } else { qsTr("Local database") - } + } color: datovkaPalette.mid font.pointSize: textFontSizeSmall } diff --git a/src/auxiliaries/icloud_helper.cpp b/src/auxiliaries/ios_helper.cpp similarity index 86% rename from src/auxiliaries/icloud_helper.cpp rename to src/auxiliaries/ios_helper.cpp index aff3a43d..8e77d8fc 100644 --- a/src/auxiliaries/icloud_helper.cpp +++ b/src/auxiliaries/ios_helper.cpp @@ -8,11 +8,11 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the @@ -25,7 +25,7 @@ #include #include -#include "src/auxiliaries/icloud_helper.h" +#include "src/auxiliaries/ios_helper.h" #include "src/io/filesystem.h" #include "src/datovka_shared/log/log.h" @@ -35,12 +35,12 @@ #define SEND_FILE_PATH_PREFIX "Documents/Datovka" #endif /* Q_OS_IOS */ -ICloudHelper::ICloudHelper(QObject *parent) +IosHelper::IosHelper(QObject *parent) : QObject(parent) { } -void ICloudHelper::storeFilesToCloud(const QStringList &srcFilePaths, +void IosHelper::storeFilesToCloud(const QStringList &srcFilePaths, const QString &targetPath) { debugFuncCall(); @@ -123,10 +123,7 @@ finish: #endif /* Q_OS_IOS */ } -/* - * Retrun short local send file path. - */ -QString ICloudHelper::getShortSendFilePath(const QString &sandBoxFilePath) +QString IosHelper::getShortSendFilePath(const QString &sandBoxFilePath) { #ifdef Q_OS_IOS @@ -141,7 +138,7 @@ QString ICloudHelper::getShortSendFilePath(const QString &sandBoxFilePath) #endif } -void ICloudHelper::clearSendDir(void) +void IosHelper::clearSendDir(void) { debugFuncCall(); @@ -154,7 +151,7 @@ void ICloudHelper::clearSendDir(void) */ } -void ICloudHelper::openDocumentPickerController(void) +void IosHelper::openDocumentPickerController(void) { debugFuncCall(); @@ -165,14 +162,14 @@ void ICloudHelper::openDocumentPickerController(void) #endif /* Q_OS_IOS */ } -void ICloudHelper::importFilesToAppInbox(QList selectedFileUrls) +void IosHelper::importFilesToAppInbox(QList selectedFileUrls) { foreach (const QUrl &fileUrl, selectedFileUrls) { moveFileToSendDir(fileUrl); } } -void ICloudHelper::moveFileToSendDir(const QUrl &sourceFileUrl) +void IosHelper::moveFileToSendDir(const QUrl &sourceFileUrl) { #ifdef Q_OS_IOS @@ -192,7 +189,7 @@ void ICloudHelper::moveFileToSendDir(const QUrl &sourceFileUrl) #endif } -void ICloudHelper::storeFilesToDeviceStorage(const QStringList &srcFilePaths) +void IosHelper::storeFilesToDeviceStorage(const QStringList &srcFilePaths) { debugFuncCall(); diff --git a/src/auxiliaries/icloud_helper.h b/src/auxiliaries/ios_helper.h similarity index 82% rename from src/auxiliaries/icloud_helper.h rename to src/auxiliaries/ios_helper.h index 81464e6b..32cba4f1 100644 --- a/src/auxiliaries/icloud_helper.h +++ b/src/auxiliaries/ios_helper.h @@ -8,11 +8,11 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the @@ -29,9 +29,9 @@ #include /*! - * @brief Provides iCloud interface for iOS. + * @brief Provides QT interface for some iOS objective-C based methods. */ -class ICloudHelper : public QObject { +class IosHelper : public QObject { Q_OBJECT public: @@ -41,7 +41,7 @@ public: * * @param[in] parent Parent object. */ - ICloudHelper(QObject *parent = Q_NULLPTR); + IosHelper(QObject *parent = Q_NULLPTR); /*! * @brief Store files to iCloud. @@ -72,7 +72,7 @@ public: void openDocumentPickerController(void); /*! - * @brief Retrun short local send file path. + * @brief Retrun short local send file path (without full sandbox path). * * @param[in] sandBoxFilePath Send file path. * @return Short path to app sandbox where send file was stored. @@ -81,9 +81,9 @@ public: QString getShortSendFilePath(const QString &sandBoxFilePath); /*! - * @brief Is activated when iCloud hierarchy has been received. + * @brief Is activated when files have been selected with iOS file picker. * - * @param[in] selectedFileUrls Path file list of iCloud for QML. + * @param[in] selectedFileUrls Path file list for QML. */ void importFilesToAppInbox(QList selectedFileUrls); diff --git a/src/files.cpp b/src/files.cpp index 5ba09f3d..3d3a08f1 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -33,7 +33,7 @@ #endif #include "ios/src/url_opener.h" #include "src/auxiliaries/email_helper.h" -#include "src/auxiliaries/icloud_helper.h" +#include "src/auxiliaries/ios_helper.h" #include "src/common.h" #include "src/crypto/crypto.h" #include "src/datovka_shared/log/log.h" @@ -653,9 +653,9 @@ void exportFilesiOS(const QStringList &destFilePaths, int ret = msgBox.exec(); if (ret == QMessageBox::Yes) { - GlobInstcs::iCloudHelperPtr->storeFilesToCloud(destFilePaths, targetDir); + GlobInstcs::iOSHelperPtr->storeFilesToCloud(destFilePaths, targetDir); } else if (ret == QMessageBox::No) { - GlobInstcs::iCloudHelperPtr->storeFilesToDeviceStorage(destFilePaths); + GlobInstcs::iOSHelperPtr->storeFilesToDeviceStorage(destFilePaths); } } #endif diff --git a/src/global.cpp b/src/global.cpp index a957ab5e..5b38b749 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -8,7 +8,7 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License @@ -41,4 +41,4 @@ class AccountsMap *GlobInstcs::acntMapPtr = Q_NULLPTR; class ImageProvider *GlobInstcs::imgProvPtr = Q_NULLPTR; -class ICloudHelper *GlobInstcs::iCloudHelperPtr = Q_NULLPTR; +class IosHelper *GlobInstcs::iOSHelperPtr = Q_NULLPTR; diff --git a/src/global.h b/src/global.h index 298870e4..1c0ba587 100644 --- a/src/global.h +++ b/src/global.h @@ -8,7 +8,7 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License @@ -43,7 +43,7 @@ class AccountsMap; class ImageProvider; -class ICloudHelper; +class IosHelper; /*! * @brief The namespace holds pointers to all globally accessible structures. @@ -78,5 +78,5 @@ namespace GlobInstcs { class ImageProvider *imgProvPtr; /*!< Image provider. */ extern - class ICloudHelper *iCloudHelperPtr; /*!< iCloud helper. */ + class IosHelper *iOSHelperPtr; /*!< iOS helper. */ } diff --git a/src/main.cpp b/src/main.cpp index 0a974e3b..5398f25c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,7 +36,7 @@ #include #include "src/accounts.h" -#include "src/auxiliaries/icloud_helper.h" +#include "src/auxiliaries/ios_helper.h" #include "src/datovka_shared/crypto/crypto_trusted_certs.h" #include "src/datovka_shared/io/records_management_db.h" #include "src/datovka_shared/localisation/localisation.h" @@ -455,13 +455,13 @@ int main(int argc, char *argv[]) StringManipulation strManipulation; Zfo zfo; - ICloudHelper iCloudHelper; - GlobInstcs::iCloudHelperPtr = new (std::nothrow) ICloudHelper; - if (GlobInstcs::iCloudHelperPtr == Q_NULLPTR) { - logErrorNL("%s", "Cannot create iCloud helper."); + IosHelper iOSHelper; + GlobInstcs::iOSHelperPtr = new (std::nothrow) IosHelper; + if (GlobInstcs::iOSHelperPtr == Q_NULLPTR) { + logErrorNL("%s", "Cannot create iOS helper."); return EXIT_FAILURE; } - GlobInstcs::iCloudHelperPtr = &iCloudHelper; + GlobInstcs::iOSHelperPtr = &iOSHelper; /* Inicialize app delegate component for interaction with iOS * Reaction on the iOS action "Open in..." */ @@ -470,7 +470,7 @@ int main(int argc, char *argv[]) QtAppDelegateInitialize(&interactionZfoFile); /* Clear send dir. */ - iCloudHelper.clearSendDir(); + iOSHelper.clearSendDir(); #endif /* @@ -519,7 +519,7 @@ int main(int argc, char *argv[]) ctx->setContextProperty("messages", &messages); ctx->setContextProperty("accounts", &accounts); ctx->setContextProperty("files", &files); - ctx->setContextProperty("iCloudHelper", &iCloudHelper); + ctx->setContextProperty("iOSHelper", &iOSHelper); ctx->setContextProperty("gov", &gov); ctx->setContextProperty("settings", &settings); ctx->setContextProperty("strManipulation", &strManipulation); @@ -763,7 +763,7 @@ int main(int argc, char *argv[]) delete GlobInstcs::workPoolPtr; GlobInstcs::workPoolPtr = Q_NULLPTR; delete GlobInstcs::msgProcEmitterPtr; GlobInstcs::msgProcEmitterPtr = Q_NULLPTR; - delete GlobInstcs::iCloudHelperPtr; GlobInstcs::iCloudHelperPtr = Q_NULLPTR; + delete GlobInstcs::iOSHelperPtr; GlobInstcs::iOSHelperPtr = Q_NULLPTR; } /* Finally, destroy global log object. */ -- GitLab From 1d898372118c22c2f656ae37f4752eb5b66efcbc Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 22 Jan 2019 10:21:24 +0100 Subject: [PATCH 15/22] Delete ios temporary sources --- ios/src/icloud_helper.h | 83 ----------------- ios/src/icloud_helper.mm | 192 --------------------------------------- 2 files changed, 275 deletions(-) delete mode 100644 ios/src/icloud_helper.h delete mode 100644 ios/src/icloud_helper.mm diff --git a/ios/src/icloud_helper.h b/ios/src/icloud_helper.h deleted file mode 100644 index ad739584..00000000 --- a/ios/src/icloud_helper.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2014-2018 CZ.NIC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, the copyright holders give - * permission to link the code of portions of this program with the - * OpenSSL library under certain conditions as described in each - * individual source file, and distribute linked combinations including - * the two. - */ - -#pragma once - -#include -#include -#include - -/*! - * @brief Used for iCloud interaction on iOS. - */ -class ICloudHelper : public QObject { - Q_OBJECT - -public: - - /*! - * @brief Constructor. - * - * @param[in] parent Parent object. - */ - ICloudHelper(QObject *parent = Q_NULLPTR); - - /*! - * @brief Check if iCloud is on and active. - * - * @return Return true if success. - */ - bool isCloudOn(void); - - /*! - * @brief Store files to iCloud. - * - * @param[in] srcFilePaths List of file paths to be saved into iCloud. - * @param[in] targetPath Target iCloud path where files will store. - */ - void storeFilesToCloud(const QStringList &srcFilePaths, - const QString &targetPath); - -private: - - /*! - * @brief Return state describing what happened. - */ - enum ICloudResult { - ICLOUD_NOT_ON = 0, /*!< iCloud is not avalilable/turn on. */ - ICLOUD_FILE_UPLOAD_SUCCESS, /*!< File upload was successful. */ - ICLOUD_FILE_EXISTS, /*!< File exists on iCloud. */ - ICLOUD_FILE_UPLOAD_ERROR, /*!< File upload failed. */ - ICLOUD_TARGET_SAVE_DIR_ERROR /*!< Target dir is missing or not created. */ - }; - - /*! - * @brief Upload single file into iCloud. - * - * @param[in] srcFilePath Source file path. - * @param[in] destFilePath iCloud target path. - * @return Return operation error/success code. - */ - ICloudResult uploadFileToCloud(const QString &srcFilePath, - const QString &destFilePath); -}; diff --git a/ios/src/icloud_helper.mm b/ios/src/icloud_helper.mm deleted file mode 100644 index fb9cbd2f..00000000 --- a/ios/src/icloud_helper.mm +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2014-2018 CZ.NIC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In addition, as a special exception, the copyright holders give - * permission to link the code of portions of this program with the - * OpenSSL library under certain conditions as described in each - * individual source file, and distribute linked combinations including - * the two. - */ - -#include -#include -#include -#include - -#ifdef Q_OS_IOS - -#include "ios/src/icloud_helper.h" -#include "ios/src/doc_view_controller.h" - -#define ICLOUD_DATOVKA_CONTAINER_NAME "Datovka" - -#endif /* Q_OS_IOS */ - -ICloudHelper::ICloudHelper(QObject *parent) - : QObject(parent) -{ -} - -bool ICloudHelper::isCloudOn(void) -{ -#ifdef Q_OS_IOS - - return ([[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]); - -#endif /* Q_OS_IOS */ - - return false; -} - -void ICloudHelper::storeFilesToCloud(const QStringList &srcFilePaths, - const QString &targetPath) -{ -#ifdef Q_OS_IOS - - /* Is iCloud configured and available? */ - if (!isCloudOn()) { - qCritical() << "Unable to access iCloud!"; - QMessageBox::critical(Q_NULLPTR, tr("iCloud error"), - tr("Unable to access iCloud Account. Open the Settings app and enter your Apple ID into iCloud settings."), - QMessageBox::Ok); - return; - } - - /* Upload files to iCloud */ - bool success = true; - QStringList errorUploads; - QString filePath; - - foreach (filePath, srcFilePaths) { - - ICloudResult retCode = uploadFileToCloud(filePath, targetPath); - - QFileInfo fi(filePath); - - switch (retCode) { - case ICloudHelper::ICLOUD_NOT_ON: - success &= false; - errorUploads.append(tr("Unable to access iCloud!")); - goto finish; - break; - case ICloudHelper::ICLOUD_TARGET_SAVE_DIR_ERROR: - success &= false; - errorUploads.append(tr("Cannot create subdir '%1' in iCloud.").arg(targetPath)); - goto finish; - break; - case ICloudHelper::ICLOUD_FILE_EXISTS: - success &= false; - errorUploads.append(tr("File '%1' already exists in iCloud.").arg(fi.fileName())); - break; - case ICloudHelper::ICLOUD_FILE_UPLOAD_ERROR: - success &= false; - errorUploads.append(tr("File '%1' upload failed.").arg(fi.fileName())); - break; - case ICloudHelper::ICLOUD_FILE_UPLOAD_SUCCESS: - errorUploads.append(tr("File '%1' has been uploaded in iCloud.").arg(fi.fileName())); - break; - default: - break; - } - } - -finish: - - /* Delete files and source dir */ - QFileInfo fi(filePath); - QDir dir(fi.absolutePath()); - dir.removeRecursively(); - - /* Show final notification */ - if (success) { - qCritical() << "Files have been saved to iCloud."; - QMessageBox::critical(Q_NULLPTR, tr("iCloud saving"), - tr("Files have been saved to iCloud.") + "\n\n" + - tr("Path: '%1'").arg(QString(ICLOUD_DATOVKA_CONTAINER_NAME) - + "/" + targetPath), - QMessageBox::Ok); - } else { - qCritical() << "Files upload to iCloud failed!"; - QString txt; - foreach (const QString &error, errorUploads) { - txt += "\n" + error; - } - QMessageBox::critical(Q_NULLPTR, - tr("iCloud saving problem"), - tr("Files have not been saved!") + "\n" + txt, - QMessageBox::Ok); - } - -#else /* !Q_OS_IOS */ - - Q_UNUSED(srcFilePaths); - Q_UNUSED(targetPath); - -#endif /* Q_OS_IOS */ -} - -ICloudHelper::ICloudResult ICloudHelper::uploadFileToCloud( - const QString &srcFilePath, const QString &destFilePath) -{ -#ifdef Q_OS_IOS - - NSURL *baseURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; - if (!baseURL) { - NSLog(@"iCloud: Unable to access iCloud."); - return ICLOUD_NOT_ON; - } - - // Create iCloud target path - NSURL *documentURL = [baseURL URLByAppendingPathComponent:@"Documents"]; - NSURL *messageURL = [documentURL URLByAppendingPathComponent:destFilePath.toNSString()]; - - // Create subdirectorues on iCloud - NSError *error = nil; - if (![[NSFileManager defaultManager] createDirectoryAtURL:messageURL - withIntermediateDirectories:YES attributes:nil error:&error]) { - NSLog(@"iCloud: Create message subdirectories error: %@", error); - return ICLOUD_TARGET_SAVE_DIR_ERROR; - } - - // Create target upload path for iCloud - NSURL *sourceFileUrl = [NSURL fileURLWithPath:srcFilePath.toNSString()]; - NSString *fileName = [srcFilePath.toNSString() lastPathComponent]; - NSURL *fileURL = [messageURL URLByAppendingPathComponent:fileName]; - - // Upload file to target directory on iCloud - if ([[NSFileManager defaultManager] setUbiquitous:YES - itemAtURL:sourceFileUrl destinationURL:fileURL error:&error]) { - NSLog(@"iCloud: File %@ has been uploaded.", fileName); - return ICLOUD_FILE_UPLOAD_SUCCESS; - } else { - // Code 516 = file exists in the iCloud. See NSFileManager error codes. - if (error.code == NSFileWriteFileExistsError) { - NSLog(@"iCloud: File with the same name already exists: %@", fileName); - return ICLOUD_FILE_EXISTS; - } else { - NSLog(@"iCloud: Error code: %zd", error.code); - NSLog(@"iCloud: %@", error); - return ICLOUD_FILE_UPLOAD_ERROR; - } - } - -#else /* !Q_OS_IOS */ - - Q_UNUSED(srcFilePath); - Q_UNUSED(destFilePath); - -#endif /* Q_OS_IOS */ -} -- GitLab From 9b0be45f1be299c349f423b7e3db9fb2cc298ae4 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 22 Jan 2019 11:10:57 +0100 Subject: [PATCH 16/22] Fixed iOS global helper object --- ios/src/doc_picker_controller.mm | 7 +++---- src/main.cpp | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ios/src/doc_picker_controller.mm b/ios/src/doc_picker_controller.mm index 9cfa1c69..4c5eaadc 100644 --- a/ios/src/doc_picker_controller.mm +++ b/ios/src/doc_picker_controller.mm @@ -42,7 +42,6 @@ - (void)openExportDocumentPicker:(NSArray *)exportUrls { //NSLog(@"EXPORT FILE URLs: %@", exportUrls); - UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithURLs:exportUrls inMode:UIDocumentPickerModeExportToService]; documentPicker.delegate = self; documentPicker.modalPresentationStyle = UIModalPresentationFormSheet; @@ -71,12 +70,12 @@ QUrl tmpUrl = QUrl::fromNSURL(url); qUrls.append(tmpUrl); } - GlobInstcs::iOSHelperPtr->importFilesToAppInbox(qUrls); + if (Q_NULLPTR != GlobInstcs::iOSHelperPtr) { + GlobInstcs::iOSHelperPtr->importFilesToAppInbox(qUrls); + } } else if (controller.documentPickerMode == UIDocumentPickerModeExportToService) { - NSLog(@"STORAGE FILE URLs: %@", urls); - } } diff --git a/src/main.cpp b/src/main.cpp index 5398f25c..6d1fc20d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -408,6 +408,12 @@ int main(int argc, char *argv[]) logErrorNL("%s", "Cannot create image provider."); return EXIT_FAILURE; } + + GlobInstcs::iOSHelperPtr = new (std::nothrow) IosHelper; + if (GlobInstcs::iOSHelperPtr == Q_NULLPTR) { + logErrorNL("%s", "Cannot create iOS helper."); + return EXIT_FAILURE; + } } QStringList cmdLineFileNames; @@ -448,6 +454,7 @@ int main(int argc, char *argv[]) Files files; Log log(&memLog); IsdsWrapper isds; + IosHelper iOSHelper; GovWrapper gov(&isds); GlobalSettingsQmlWrapper settings; InteractionZfoFile interactionZfoFile; @@ -455,20 +462,14 @@ int main(int argc, char *argv[]) StringManipulation strManipulation; Zfo zfo; - IosHelper iOSHelper; - GlobInstcs::iOSHelperPtr = new (std::nothrow) IosHelper; - if (GlobInstcs::iOSHelperPtr == Q_NULLPTR) { - logErrorNL("%s", "Cannot create iOS helper."); - return EXIT_FAILURE; - } - GlobInstcs::iOSHelperPtr = &iOSHelper; - /* Inicialize app delegate component for interaction with iOS * Reaction on the iOS action "Open in..." */ #if defined Q_OS_IOS QtAppDelegateInitialize(&interactionZfoFile); + GlobInstcs::iOSHelperPtr = &iOSHelper; + /* Clear send dir. */ iOSHelper.clearSendDir(); #endif @@ -763,7 +764,7 @@ int main(int argc, char *argv[]) delete GlobInstcs::workPoolPtr; GlobInstcs::workPoolPtr = Q_NULLPTR; delete GlobInstcs::msgProcEmitterPtr; GlobInstcs::msgProcEmitterPtr = Q_NULLPTR; - delete GlobInstcs::iOSHelperPtr; GlobInstcs::iOSHelperPtr = Q_NULLPTR; + GlobInstcs::iOSHelperPtr = Q_NULLPTR; } /* Finally, destroy global log object. */ -- GitLab From 25fc832855906c84b8b5a05797d91b89e59022a6 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 22 Jan 2019 13:32:15 +0100 Subject: [PATCH 17/22] Delete tmp files after export --- qml/dialogues/FileDialogue.qml | 8 +------- qml/pages/PageSendMessage.qml | 32 +++++++++----------------------- src/auxiliaries/ios_helper.cpp | 9 +++------ src/auxiliaries/ios_helper.h | 4 ++-- src/files.cpp | 24 +++++++++++++++++++++--- src/io/filesystem.cpp | 6 ++++++ src/io/filesystem.h | 9 +++++++++ src/main.cpp | 4 ++-- 8 files changed, 53 insertions(+), 43 deletions(-) diff --git a/qml/dialogues/FileDialogue.qml b/qml/dialogues/FileDialogue.qml index 1d956781..3f5bafe1 100644 --- a/qml/dialogues/FileDialogue.qml +++ b/qml/dialogues/FileDialogue.qml @@ -62,7 +62,6 @@ Dialog { property int selectedFileIndex: -1 property bool showDirs: true property bool showFiles: true - property bool iOS: false signal finished(variant pathListModel) @@ -70,8 +69,7 @@ Dialog { return (root.showDirs && !root.showFiles); } - function raise(title, filters, showFiles, targetLocation, isiOS) { - iOS = isiOS + function raise(title, filters, showFiles, targetLocation) { if (targetLocation !== "") { folderNavigation.visible = false folderModel.folder = "file://" + targetLocation @@ -82,9 +80,6 @@ Dialog { if (filters !== "") { folderModel.nameFilters = filters } - if (iOS) { - folderModel.folder = standardLocationUrl(InteractionFilesystem.DOCUMENTS_LOCATION) - } selectedFileIndex = -1 // clear add path list model pathListModel.clear() @@ -133,7 +128,6 @@ Dialog { ColumnLayout { id: folderNavigation AccessibleComboBox { - visible: !iOS anchors { left: parent.left; right: parent.right; diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index e62e47d3..0ff0b96c 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -218,7 +218,7 @@ Item { Component.onDestruction: { if (iOS) { - iOSHelper.clearSendDir() + iOSHelper.clearSendAndTmpDirs() } statusBar.visible = false } @@ -510,29 +510,15 @@ Item { } } } - Row { - id: buttonBar - spacing: formItemVerticalSpacing * 2 + AccessibleButton { + id: addFile + height: inputItemHeight anchors.horizontalCenter: parent.horizontalCenter - AccessibleButton { - id: addFile - height: inputItemHeight - font.pointSize: defaultTextFont.font.pointSize - text: qsTr("Add file") - onClicked: { - fileDialogue.raise(qsTr("Select files"), ["*.*"], true, "", iOS) - } + font.pointSize: defaultTextFont.font.pointSize + text: qsTr("Add file") + onClicked: { + iOS ? iOSHelper.openDocumentPickerController() : fileDialogue.raise(qsTr("Select files"), ["*.*"], true, "") } - AccessibleButton { - id: storage - visible: iOS - height: inputItemHeight - font.pointSize: defaultTextFont.font.pointSize - text: qsTr("Storage") - onClicked: { - iOSHelper.openDocumentPickerController() - } - } } Component { id: attachmentDelegate @@ -674,7 +660,7 @@ Item { color: datovkaPalette.text anchors.left: parent.left anchors.leftMargin: defaultMargin - anchors.top: buttonBar.bottom + anchors.top: addFile.bottom anchors.topMargin: defaultMargin anchors.right: parent.right anchors.rightMargin: defaultMargin diff --git a/src/auxiliaries/ios_helper.cpp b/src/auxiliaries/ios_helper.cpp index 8e77d8fc..edceb03a 100644 --- a/src/auxiliaries/ios_helper.cpp +++ b/src/auxiliaries/ios_helper.cpp @@ -138,17 +138,14 @@ QString IosHelper::getShortSendFilePath(const QString &sandBoxFilePath) #endif } -void IosHelper::clearSendDir(void) +void IosHelper::clearSendAndTmpDirs(void) { debugFuncCall(); QDir dir(appSendDirPath()); dir.removeRecursively(); -/* - // TODO - used for experiments and testing only - QDir dir2(dfltAttachSavingLoc()); - dir2.removeRecursively(); -*/ + dir.setPath(appTmpDirPath()); + dir.removeRecursively(); } void IosHelper::openDocumentPickerController(void) diff --git a/src/auxiliaries/ios_helper.h b/src/auxiliaries/ios_helper.h index 32cba4f1..b9caabff 100644 --- a/src/auxiliaries/ios_helper.h +++ b/src/auxiliaries/ios_helper.h @@ -60,10 +60,10 @@ public: void storeFilesToDeviceStorage(const QStringList &srcFilePaths); /*! - * @brief Clear send folder. + * @brief Clear send and tmp folder. */ Q_INVOKABLE - void clearSendDir(void); + void clearSendAndTmpDirs(void); /*! * @brief Create and open document picker controller. diff --git a/src/files.cpp b/src/files.cpp index 3d3a08f1..c54971cc 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -726,12 +726,21 @@ void Files::saveMsgFilesToDisk(const QString &userName, documents.append(document); } - QString filePath(appMsgAttachDirPath(msgIdStr)); + QString targetPath; + +#ifdef Q_OS_IOS + + targetPath = appMsgAttachDirPathiOS(msgIdStr); + +#else + targetPath = appMsgAttachDirPath(msgIdStr); + +#endif QString destFilePath; QStringList destFilePaths; foreach (const Isds::Document &document, documents) { - destFilePath = writeFile(filePath, document.fileDescr(), + destFilePath = writeFile(targetPath, document.fileDescr(), document.binaryContent()); if (!destFilePath.isEmpty()) { destFilePaths.append(destFilePath); @@ -762,7 +771,16 @@ void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant, return; } - QString targetPath(appMsgAttachDirPath(msgIdStr)); + QString targetPath; + +#ifdef Q_OS_IOS + + targetPath = appMsgAttachDirPathiOS(msgIdStr); + +#else + targetPath = appMsgAttachDirPath(msgIdStr); + +#endif QString destFilePath; QStringList destFilePaths; diff --git a/src/io/filesystem.cpp b/src/io/filesystem.cpp index 4c559c9e..6d3f35d2 100644 --- a/src/io/filesystem.cpp +++ b/src/io/filesystem.cpp @@ -72,6 +72,12 @@ QString appMsgAttachDirPath(const QString &msgIdStr) return existingAppPath(dfltAttachSavingLoc(), msgIdStr); } +QString appMsgAttachDirPathiOS(const QString &msgIdStr) +{ + return existingAppPath(dfltAttachSavingLoc(), + QStringLiteral(DATOVKA_TEMP_DIR_NAME) + QDir::separator() + msgIdStr); +} + QString appTmpDirPath(void) { return existingAppPath(dfltAttachSavingLoc(), DATOVKA_TEMP_DIR_NAME); diff --git a/src/io/filesystem.h b/src/io/filesystem.h index 0a5d0b62..52cf3fe6 100644 --- a/src/io/filesystem.h +++ b/src/io/filesystem.h @@ -88,6 +88,15 @@ QString appLogDirPath(void); */ QString appMsgAttachDirPath(const QString &msgIdStr); +/*! + * @brief Return path to location where attachments of a particular message + * can be stored for iOS. + * + * @param[in] msgIdStr String containing message identifier. + * @return Full path containing the supplied identifier. + */ +QString appMsgAttachDirPathiOS(const QString &msgIdStr); + /*! * @brief Return path to location where temporary files can be stored. * diff --git a/src/main.cpp b/src/main.cpp index 6d1fc20d..42ca677f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -470,8 +470,8 @@ int main(int argc, char *argv[]) GlobInstcs::iOSHelperPtr = &iOSHelper; - /* Clear send dir. */ - iOSHelper.clearSendDir(); + /* Clear send and tmp dir (iOS only). */ + iOSHelper.clearSendAndTmpDirs(); #endif /* -- GitLab From 5bd05e6caa91262462ea4b165385fd76b08903c5 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 22 Jan 2019 13:46:25 +0100 Subject: [PATCH 18/22] Minor code refactoring --- qml/pages/PageSendMessage.qml | 2 +- src/auxiliaries/ios_helper.cpp | 9 +++++++++ src/auxiliaries/ios_helper.h | 8 ++++++++ src/files.cpp | 9 --------- src/files.h | 8 -------- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/qml/pages/PageSendMessage.qml b/qml/pages/PageSendMessage.qml index 0ff0b96c..29d6156a 100644 --- a/qml/pages/PageSendMessage.qml +++ b/qml/pages/PageSendMessage.qml @@ -193,7 +193,7 @@ Item { } Component.onCompleted: { - iOS = files.isIos() + iOS = iOSHelper.isIos() actionButton.enabled = false initPDZ.visible = false initPDZ.checked = false diff --git a/src/auxiliaries/ios_helper.cpp b/src/auxiliaries/ios_helper.cpp index edceb03a..b9d129c9 100644 --- a/src/auxiliaries/ios_helper.cpp +++ b/src/auxiliaries/ios_helper.cpp @@ -202,3 +202,12 @@ void IosHelper::storeFilesToDeviceStorage(const QStringList &srcFilePaths) Q_UNUSED(srcFilePaths); #endif /* Q_OS_IOS */ } + +bool IosHelper::isIos(void) +{ +#ifdef Q_OS_IOS + return true; +#else + return false; +#endif +} diff --git a/src/auxiliaries/ios_helper.h b/src/auxiliaries/ios_helper.h index b9caabff..e7dcc476 100644 --- a/src/auxiliaries/ios_helper.h +++ b/src/auxiliaries/ios_helper.h @@ -87,6 +87,14 @@ public: */ void importFilesToAppInbox(QList selectedFileUrls); + /*! + * @brief iOS check for QML. + * + * @return True if iOS. + */ + Q_INVOKABLE + bool isIos(void); + signals: /*! diff --git a/src/files.cpp b/src/files.cpp index c54971cc..d702d5b1 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -816,15 +816,6 @@ void Files::deleteTmpFileFromStorage(const QString &filePath) #endif } -bool Files::isIos(void) -{ -#ifdef Q_OS_IOS - return true; -#else - return false; -#endif -} - bool Files::parseXmlData(enum MsgInfo::ZfoType *type, QString *idStr, QString *annotation, QString *msgDescrHtml, FileListModel *attachModel, QString *emailBody, QByteArray xmlData) diff --git a/src/files.h b/src/files.h index c07d3c0b..31ab960a 100644 --- a/src/files.h +++ b/src/files.h @@ -284,14 +284,6 @@ public: Q_INVOKABLE void deleteTmpFileFromStorage(const QString &filePath); - /*! - * @brief iOS check for QML. - * - * @return True if iOS. - */ - Q_INVOKABLE - bool isIos(void); - signals: /*! * @brief Set new statusbar text and active busy indicator to QML. -- GitLab From 3754f8d4b084feb29d49839356bf15c65bbb7640 Mon Sep 17 00:00:00 2001 From: Martin Straka Date: Tue, 22 Jan 2019 14:15:48 +0100 Subject: [PATCH 19/22] Minor code refactoring again --- ios/ios.pri | 1 + src/files.cpp | 14 -------------- src/io/filesystem.cpp | 1 - 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/ios/ios.pri b/ios/ios.pri index dfc35cb6..f208c5f3 100644 --- a/ios/ios.pri +++ b/ios/ios.pri @@ -35,3 +35,4 @@ QMAKE_BUNDLE_DATA += ios_icon app_launch_images.files = $$files($${_PRO_FILE_PWD_}/ios/Images.xcassets/LaunchImage.launchimage/*.png) QMAKE_BUNDLE_DATA += app_launch_images LIBS += -framework UIKit -framework MessageUI -framework MobileCoreServices + diff --git a/src/files.cpp b/src/files.cpp index d702d5b1..4ec35175 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -729,12 +729,9 @@ void Files::saveMsgFilesToDisk(const QString &userName, QString targetPath; #ifdef Q_OS_IOS - targetPath = appMsgAttachDirPathiOS(msgIdStr); - #else targetPath = appMsgAttachDirPath(msgIdStr); - #endif QString destFilePath; @@ -748,13 +745,9 @@ void Files::saveMsgFilesToDisk(const QString &userName, } #ifndef Q_OS_IOS - attachmentSavingNotification(destFilePath); - #else - exportFilesiOS(destFilePaths, joinDirs(userName, msgIdStr)); - #endif } @@ -774,12 +767,9 @@ void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant, QString targetPath; #ifdef Q_OS_IOS - targetPath = appMsgAttachDirPathiOS(msgIdStr); - #else targetPath = appMsgAttachDirPath(msgIdStr); - #endif QString destFilePath; @@ -796,13 +786,9 @@ void Files::saveAttachmentsToDiskZfo(const QVariant &attachModelVariant, } #ifndef Q_OS_IOS - attachmentSavingNotification(destFilePath); - #else - exportFilesiOS(destFilePaths, msgIdStr); - #endif } diff --git a/src/io/filesystem.cpp b/src/io/filesystem.cpp index 6d3f35d2..5a432699 100644 --- a/src/io/filesystem.cpp +++ b/src/io/filesystem.cpp @@ -50,7 +50,6 @@ QString appSendDirPath(void) return existingAppPath(dfltAttachSavingLoc(), DATOVKA_SEND_DIR_NAME); } - QString appCertDirPath(void) { return existingAppPath(dfltAttachSavingLoc(), DATOVKA_CERT_DIR_NAME); -- GitLab From 6cac41867e2c0ddd63960e0f9601e1ebcaec65aa Mon Sep 17 00:00:00 2001 From: Karel Slany Date: Wed, 23 Jan 2019 16:33:39 +0100 Subject: [PATCH 20/22] Cleaned a little bit include/import statements in iOS code. --- ios/src/app_delegate.h | 3 ++- ios/src/app_delegate.mm | 5 +++-- ios/src/doc_picker_controller.h | 2 ++ ios/src/doc_picker_controller.mm | 3 +-- ios/src/doc_view_controller.mm | 4 ++-- ios/src/ios_file_opener.h | 5 ----- ios/src/send_email_controller.mm | 4 ++-- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/ios/src/app_delegate.h b/ios/src/app_delegate.h index 53e95826..cf60419e 100644 --- a/ios/src/app_delegate.h +++ b/ios/src/app_delegate.h @@ -24,7 +24,8 @@ #pragma once #import -#import + +#include @interface QtAppDelegate : UIResponder +(QtAppDelegate *)sharedQtAppDelegate; diff --git a/ios/src/app_delegate.mm b/ios/src/app_delegate.mm index adf3a2fd..b7a77c2a 100644 --- a/ios/src/app_delegate.mm +++ b/ios/src/app_delegate.mm @@ -22,9 +22,10 @@ */ #include -#include "src/qml_interaction/interaction_zfo_file.h" -#import "app_delegate.h" +#include "ios/src/app_delegate.h" +#include "ios/src/qt_app_delegate.h" +#include "src/qml_interaction/interaction_zfo_file.h" @implementation QtAppDelegate diff --git a/ios/src/doc_picker_controller.h b/ios/src/doc_picker_controller.h index 276a506e..19b860fd 100644 --- a/ios/src/doc_picker_controller.h +++ b/ios/src/doc_picker_controller.h @@ -21,6 +21,8 @@ * the two. */ +#pragma once + #import @interface DocumentPickerController : UIViewController diff --git a/ios/src/doc_picker_controller.mm b/ios/src/doc_picker_controller.mm index 4c5eaadc..90a1f359 100644 --- a/ios/src/doc_picker_controller.mm +++ b/ios/src/doc_picker_controller.mm @@ -24,11 +24,10 @@ #include #include +#include "ios/src/doc_picker_controller.h" #include "src/auxiliaries/ios_helper.h" #include "src/global.h" -#import "ios/src/doc_picker_controller.h" - @interface DocumentPickerController () @end diff --git a/ios/src/doc_view_controller.mm b/ios/src/doc_view_controller.mm index 9f9babe8..8a945b1b 100644 --- a/ios/src/doc_view_controller.mm +++ b/ios/src/doc_view_controller.mm @@ -21,10 +21,10 @@ * the two. */ -#import "doc_view_controller.h" - #include +#include "ios/src/doc_view_controller.h" + @interface DocViewController () @end diff --git a/ios/src/ios_file_opener.h b/ios/src/ios_file_opener.h index 04aab37c..f9db3ee6 100644 --- a/ios/src/ios_file_opener.h +++ b/ios/src/ios_file_opener.h @@ -23,9 +23,6 @@ #pragma once -#include - -#ifdef Q_OS_IOS #import @interface iOSFileOpener : NSObject @@ -35,5 +32,3 @@ - (void)openFile:(NSString *)path fromViewController:(UIViewController *)viewController; @end - -#endif /* Q_OS_IOS */ diff --git a/ios/src/send_email_controller.mm b/ios/src/send_email_controller.mm index e4a5d025..65742562 100644 --- a/ios/src/send_email_controller.mm +++ b/ios/src/send_email_controller.mm @@ -21,10 +21,10 @@ * the two. */ -#import "send_email_controller.h" - #include +#include "ios/src/send_email_controller.h" + @implementation SimpleEmailSendController - (void) showCantSendMailAlert -- GitLab From f8edac3018ff0bb4261e2a181412517173711bc6 Mon Sep 17 00:00:00 2001 From: Karel Slany Date: Wed, 23 Jan 2019 18:04:44 +0100 Subject: [PATCH 21/22] Made some methods static. --- ios/src/icloud_io.h | 5 ++- ios/src/icloud_io.mm | 15 +++++--- ios/src/url_opener.h | 7 +++- ios/src/url_opener.mm | 6 +-- src/auxiliaries/email_helper.cpp | 3 +- src/auxiliaries/ios_helper.cpp | 65 +++++++++++++++----------------- src/auxiliaries/ios_helper.h | 35 +++++++++-------- src/files.cpp | 7 ++-- src/main.cpp | 2 +- 9 files changed, 74 insertions(+), 71 deletions(-) diff --git a/ios/src/icloud_io.h b/ios/src/icloud_io.h index db330246..34d36176 100644 --- a/ios/src/icloud_io.h +++ b/ios/src/icloud_io.h @@ -35,7 +35,6 @@ class ICloudIo : public QObject { Q_OBJECT public: - /*! * @brief Return state describing what happened. */ @@ -47,13 +46,15 @@ public: ICLOUD_TARGET_SAVE_DIR_ERROR /*!< Target dir is missing or not created. */ }; +private: /*! * @brief Constructor. * * @param[in] parent Parent object. */ - ICloudIo(QObject *parent = Q_NULLPTR); + explicit ICloudIo(QObject *parent = Q_NULLPTR); +public: /*! * @brief Test if iCloud is on. * diff --git a/ios/src/icloud_io.mm b/ios/src/icloud_io.mm index 66b23fbf..7d03ee40 100644 --- a/ios/src/icloud_io.mm +++ b/ios/src/icloud_io.mm @@ -29,15 +29,20 @@ ICloudIo::ICloudIo(QObject *parent) { } -NSURL *getCloudBaseUrl() { +static +NSURL *getCloudBaseUrl(void) +{ return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; } -NSURL *getCloudDocumentsUrl(NSURL *baseURL) { +static +NSURL *getCloudDocumentsUrl(NSURL *baseURL) +{ return [baseURL URLByAppendingPathComponent:@"Documents"]; } -bool ICloudIo::isCloudOn(void) { +bool ICloudIo::isCloudOn(void) +{ return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; } @@ -82,7 +87,7 @@ ICloudIo::ICloudResult ICloudIo::moveFileToCloud( bool ICloudIo::openDocumentPickerController(const QStringList &exportFilesPath) { - static DocumentPickerController* dpc = nil; + static DocumentPickerController *dpc = nil; if (dpc != nil) { [dpc removeFromParentViewController]; [dpc release]; @@ -101,7 +106,7 @@ bool ICloudIo::openDocumentPickerController(const QStringList &exportFilesPath) NSMutableArray *exportUrls = [NSMutableArray array]; // covert export file paths to array of nsurl for (int i = 0; i < exportFilesPath.count(); ++i) { - QUrl url = QUrl::fromLocalFile(exportFilesPath.at(i)); + QUrl url(QUrl::fromLocalFile(exportFilesPath.at(i))); if (url.isValid()) { NSURL *fileUrl = url.toNSURL(); [exportUrls addObject:fileUrl]; diff --git a/ios/src/url_opener.h b/ios/src/url_opener.h index 420ab4b4..e529c8c9 100644 --- a/ios/src/url_opener.h +++ b/ios/src/url_opener.h @@ -37,19 +37,21 @@ class UrlOpener : public QObject { Q_OBJECT -public: +private: /*! * @brief Constructor. * * @param[in] parent Parent object. */ - UrlOpener(QObject *parent = 0); + explicit UrlOpener(QObject *parent = Q_NULLPTR); +public: /*! * @brief Open file on iOS. * * @param[in] filePath Path to file. */ + static void openFile(const QString &filePath); /*! @@ -60,6 +62,7 @@ public: * @param[in] subject Email subject. * @param[in] filePaths Paths to attachment files. */ + static void createEmail(const QString &bodyText, const QString &to, const QString &subject, const QStringList &filePaths); }; diff --git a/ios/src/url_opener.mm b/ios/src/url_opener.mm index f3321588..629f2481 100644 --- a/ios/src/url_opener.mm +++ b/ios/src/url_opener.mm @@ -48,8 +48,8 @@ void UrlOpener::openFile(const QString &filePath) #else /* Q_OS_IOS */ - NSString* url = filePath.toNSString(); - NSURL* fileURL = [NSURL fileURLWithPath:url]; + NSString *url = filePath.toNSString(); + NSURL *fileURL = [NSURL fileURLWithPath:url]; static DocViewController* mtv = nil; if (mtv != nil) { [mtv removeFromParentViewController]; @@ -94,7 +94,7 @@ void UrlOpener::createEmail(const QString &bodyText, const QString &to, } NSArray *filePath = [NSArray arrayWithArray:tmp]; - static SimpleEmailSendController* email = nil; + static SimpleEmailSendController *email = nil; if (email != nil) { [email removeFromParentViewController]; [email release]; diff --git a/src/auxiliaries/email_helper.cpp b/src/auxiliaries/email_helper.cpp index 64bfbd12..40dca51b 100644 --- a/src/auxiliaries/email_helper.cpp +++ b/src/auxiliaries/email_helper.cpp @@ -127,8 +127,7 @@ void sendEmail(const QString &emailMessage, const QStringList &fileList, #if defined Q_OS_IOS - UrlOpener urlOpener; - urlOpener.createEmail(body, to, subject, fileList); + UrlOpener::createEmail(body, to, subject, fileList); #elif defined Q_OS_ANDROID diff --git a/src/auxiliaries/ios_helper.cpp b/src/auxiliaries/ios_helper.cpp index b9d129c9..738e8237 100644 --- a/src/auxiliaries/ios_helper.cpp +++ b/src/auxiliaries/ios_helper.cpp @@ -49,7 +49,7 @@ void IosHelper::storeFilesToCloud(const QStringList &srcFilePaths, if (!ICloudIo::isCloudOn()) { QMessageBox::critical(Q_NULLPTR, tr("iCloud error"), - tr("Unable to access iCloud Account. Open the Settings app and enter your Apple ID into iCloud settings."), + tr("Unable to access iCloud account. Open the settings and check your iCloud settings."), QMessageBox::Ok); return; } @@ -67,25 +67,25 @@ void IosHelper::storeFilesToCloud(const QStringList &srcFilePaths, switch (retCode) { case ICloudIo::ICLOUD_NOT_ON: - success &= false; + success = false; errorUploads.append(tr("Unable to access iCloud!")); goto finish; break; case ICloudIo::ICLOUD_TARGET_SAVE_DIR_ERROR: - success &= false; - errorUploads.append(tr("Cannot create subdir '%1' in iCloud.").arg(targetPath)); + success = false; + errorUploads.append(tr("Cannot create subdirectory '%1' in iCloud.").arg(targetPath)); goto finish; break; case ICloudIo::ICLOUD_FILE_EXISTS: - success &= false; + success = false; errorUploads.append(tr("File '%1' already exists in iCloud.").arg(fi.fileName())); break; case ICloudIo::ICLOUD_FILE_UPLOAD_ERROR: - success &= false; + success = false; errorUploads.append(tr("File '%1' upload failed.").arg(fi.fileName())); break; case ICloudIo::ICLOUD_FILE_UPLOAD_SUCCESS: - errorUploads.append(tr("File '%1' has been moved in iCloud.").arg(fi.fileName())); + errorUploads.append(tr("File '%1' has been stored into iCloud.").arg(fi.fileName())); break; default: break; @@ -93,16 +93,15 @@ void IosHelper::storeFilesToCloud(const QStringList &srcFilePaths, } finish: - - /* Delete files and source dir */ + /* Delete files and source directory. */ QFileInfo fi(filePath); QDir dir(fi.absolutePath()); dir.removeRecursively(); /* Show final notification */ if (success) { - QMessageBox::critical(Q_NULLPTR, tr("iCloud saving"), - tr("Files have been saved to iCloud.") + "\n\n" + + QMessageBox::information(Q_NULLPTR, tr("Saved to iCloud"), + tr("Files have been stored into iCloud.") + "\n\n" + tr("Path: '%1'").arg(QString(ICLOUD_DATOVKA_CONTAINER_NAME) + "/" + targetPath), QMessageBox::Ok); @@ -111,8 +110,8 @@ finish: foreach (const QString &error, errorUploads) { txt += "\n" + error; } - QMessageBox::critical(Q_NULLPTR, - tr("iCloud saving problem"), + QMessageBox::warning(Q_NULLPTR, + tr("iCloud Problem"), tr("Files have not been saved!") + "\n" + txt, QMessageBox::Ok); } @@ -159,33 +158,13 @@ void IosHelper::openDocumentPickerController(void) #endif /* Q_OS_IOS */ } -void IosHelper::importFilesToAppInbox(QList selectedFileUrls) +void IosHelper::importFilesToAppInbox(const QList &selectedFileUrls) { foreach (const QUrl &fileUrl, selectedFileUrls) { moveFileToSendDir(fileUrl); } } -void IosHelper::moveFileToSendDir(const QUrl &sourceFileUrl) -{ - -#ifdef Q_OS_IOS - - if (Q_UNLIKELY(!sourceFileUrl.isValid())) { - return; - } - - QUrl targetFileUrl = ICloudIo::moveFile(sourceFileUrl, appSendDirPath()); - if (targetFileUrl.isValid()) { - targetFileUrl.setScheme(QString()); - emit fileSelectedSig(targetFileUrl.toString()); - } - -#else /* !Q_OS_IOS */ - Q_UNUSED(sourceFileUrl); -#endif -} - void IosHelper::storeFilesToDeviceStorage(const QStringList &srcFilePaths) { debugFuncCall(); @@ -211,3 +190,21 @@ bool IosHelper::isIos(void) return false; #endif } + +void IosHelper::moveFileToSendDir(const QUrl &sourceFileUrl) +{ +#ifdef Q_OS_IOS + if (Q_UNLIKELY(!sourceFileUrl.isValid())) { + return; + } + + QUrl targetFileUrl(ICloudIo::moveFile(sourceFileUrl, appSendDirPath())); + if (targetFileUrl.isValid()) { + targetFileUrl.setScheme(QString()); + emit fileSelectedSig(targetFileUrl.toString()); + } + +#else /* !Q_OS_IOS */ + Q_UNUSED(sourceFileUrl); +#endif +} diff --git a/src/auxiliaries/ios_helper.h b/src/auxiliaries/ios_helper.h index e7dcc476..84e8b56a 100644 --- a/src/auxiliaries/ios_helper.h +++ b/src/auxiliaries/ios_helper.h @@ -35,13 +35,12 @@ class IosHelper : public QObject { Q_OBJECT public: - /*! * @brief Constructor. * * @param[in] parent Parent object. */ - IosHelper(QObject *parent = Q_NULLPTR); + explicit IosHelper(QObject *parent = Q_NULLPTR); /*! * @brief Store files to iCloud. @@ -49,54 +48,55 @@ public: * @param[in] srcFilePaths List of file paths to be saved into iCloud. * @param[in] targetPath Target iCloud path where files will store. */ + static void storeFilesToCloud(const QStringList &srcFilePaths, const QString &targetPath); /*! - * @brief Store files to device local storage. + * @brief Retrun short local send file path (without full sandbox path). * - * @param[in] srcFilePaths List of file paths to be saved into storage. + * @param[in] sandBoxFilePath Send file path. + * @return Short path to app sandbox where send file was stored. */ - void storeFilesToDeviceStorage(const QStringList &srcFilePaths); + Q_INVOKABLE static + QString getShortSendFilePath(const QString &sandBoxFilePath); /*! * @brief Clear send and tmp folder. */ - Q_INVOKABLE + Q_INVOKABLE static void clearSendAndTmpDirs(void); /*! * @brief Create and open document picker controller. */ - Q_INVOKABLE + Q_INVOKABLE static void openDocumentPickerController(void); /*! - * @brief Retrun short local send file path (without full sandbox path). + * @brief Is activated when files have been selected with iOS file picker. * - * @param[in] sandBoxFilePath Send file path. - * @return Short path to app sandbox where send file was stored. + * @param[in] selectedFileUrls Path file list for QML. */ - Q_INVOKABLE - QString getShortSendFilePath(const QString &sandBoxFilePath); + void importFilesToAppInbox(const QList &selectedFileUrls); /*! - * @brief Is activated when files have been selected with iOS file picker. + * @brief Store files to device local storage. * - * @param[in] selectedFileUrls Path file list for QML. + * @param[in] srcFilePaths List of file paths to be saved into storage. */ - void importFilesToAppInbox(QList selectedFileUrls); + static + void storeFilesToDeviceStorage(const QStringList &srcFilePaths); /*! * @brief iOS check for QML. * * @return True if iOS. */ - Q_INVOKABLE + Q_INVOKABLE static bool isIos(void); signals: - /*! * @brief Is activated when a file has been chosen with document picker. * @@ -105,7 +105,6 @@ signals: void fileSelectedSig(QString filePath); private: - /*! * @brief Move file from app temporary inbox to send path. * diff --git a/src/files.cpp b/src/files.cpp index 4ec35175..9f44684b 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -264,8 +264,7 @@ void Files::openAttachmentFromPath(const QString &filePath) #ifdef Q_OS_IOS - UrlOpener urlOpener; - urlOpener.openFile(filePath); + UrlOpener::openFile(filePath); #elif defined (Q_OS_ANDROID) @@ -653,9 +652,9 @@ void exportFilesiOS(const QStringList &destFilePaths, int ret = msgBox.exec(); if (ret == QMessageBox::Yes) { - GlobInstcs::iOSHelperPtr->storeFilesToCloud(destFilePaths, targetDir); + IosHelper::storeFilesToCloud(destFilePaths, targetDir); } else if (ret == QMessageBox::No) { - GlobInstcs::iOSHelperPtr->storeFilesToDeviceStorage(destFilePaths); + IosHelper::storeFilesToDeviceStorage(destFilePaths); } } #endif diff --git a/src/main.cpp b/src/main.cpp index 42ca677f..279a796d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -471,7 +471,7 @@ int main(int argc, char *argv[]) GlobInstcs::iOSHelperPtr = &iOSHelper; /* Clear send and tmp dir (iOS only). */ - iOSHelper.clearSendAndTmpDirs(); + IosHelper::clearSendAndTmpDirs(); #endif /* -- GitLab From 14dd067ee79ec560a835de441580a15468194a70 Mon Sep 17 00:00:00 2001 From: Karel Slany Date: Thu, 24 Jan 2019 12:04:13 +0100 Subject: [PATCH 22/22] Removed QObject inheritance from classes that don't need it. --- ios/src/icloud_io.h | 9 ++------- ios/src/icloud_io.mm | 5 ----- ios/src/url_opener.h | 9 ++------- ios/src/url_opener.mm | 5 ----- src/auxiliaries/email_helper.cpp | 1 + 5 files changed, 5 insertions(+), 24 deletions(-) diff --git a/ios/src/icloud_io.h b/ios/src/icloud_io.h index 34d36176..10c5aa8c 100644 --- a/ios/src/icloud_io.h +++ b/ios/src/icloud_io.h @@ -23,7 +23,6 @@ #pragma once -#include #include #include #include @@ -31,9 +30,7 @@ /*! * @brief Provides objective-C IO methods for interaction with iCLoud. */ -class ICloudIo : public QObject { - Q_OBJECT - +class ICloudIo { public: /*! * @brief Return state describing what happened. @@ -49,10 +46,8 @@ public: private: /*! * @brief Constructor. - * - * @param[in] parent Parent object. */ - explicit ICloudIo(QObject *parent = Q_NULLPTR); + ICloudIo(void); public: /*! diff --git a/ios/src/icloud_io.mm b/ios/src/icloud_io.mm index 7d03ee40..d5e2bb3e 100644 --- a/ios/src/icloud_io.mm +++ b/ios/src/icloud_io.mm @@ -24,11 +24,6 @@ #include "ios/src/doc_picker_controller.h" #include "ios/src/icloud_io.h" -ICloudIo::ICloudIo(QObject *parent) - : QObject(parent) -{ -} - static NSURL *getCloudBaseUrl(void) { diff --git a/ios/src/url_opener.h b/ios/src/url_opener.h index e529c8c9..5a74955a 100644 --- a/ios/src/url_opener.h +++ b/ios/src/url_opener.h @@ -23,7 +23,6 @@ #pragma once -#include #include /* @@ -34,16 +33,12 @@ /*! * @brief Used for opening URLs and create email with attachments on iOS. */ -class UrlOpener : public QObject { - Q_OBJECT - +class UrlOpener { private: /*! * @brief Constructor. - * - * @param[in] parent Parent object. */ - explicit UrlOpener(QObject *parent = Q_NULLPTR); + UrlOpener(void); public: /*! diff --git a/ios/src/url_opener.mm b/ios/src/url_opener.mm index 629f2481..bebf64cb 100644 --- a/ios/src/url_opener.mm +++ b/ios/src/url_opener.mm @@ -35,11 +35,6 @@ #include "ios/src/url_opener.h" #endif /* Q_OS_IOS */ -UrlOpener::UrlOpener(QObject *parent) - : QObject(parent) -{ -} - void UrlOpener::openFile(const QString &filePath) { #ifndef Q_OS_IOS diff --git a/src/auxiliaries/email_helper.cpp b/src/auxiliaries/email_helper.cpp index 40dca51b..1ea00dbb 100644 --- a/src/auxiliaries/email_helper.cpp +++ b/src/auxiliaries/email_helper.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "ios/src/url_opener.h" -- GitLab