Commit 08695739 authored by Martin Straka's avatar Martin Straka

Added document picker view controller for iOS

parent 8003c88a
......@@ -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 \
......
/*
* 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 <http://www.gnu.org/licenses/>.
*
* 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 <UIKit/UIKit.h>
@interface DocumentPickerController : UIViewController
- (void)openImportDocumentPicker;
@end
/*
* 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 <http://www.gnu.org/licenses/>.
*
* 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 <QString>
#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 () <UIDocumentPickerDelegate>
@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
......@@ -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;
......
......@@ -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);
};
......@@ -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();
}
}
......@@ -215,5 +215,6 @@ Dialog {
}
onRejected: {
pathListModel.clear()
iCloudHelper.stopCloudHierarchyAsync()
}
}
......@@ -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
......
......@@ -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
}
......@@ -28,9 +28,15 @@
#include <QStringList>
#include <QTimer>
/* 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;
};
......@@ -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"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment