diff options
-rw-r--r-- | src/plugins/platforms/ios/ios.pro | 5 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosfileengineassetslibrary.h | 77 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosfileengineassetslibrary.mm | 253 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosfileenginefactory.h | 63 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosintegration.h | 2 |
5 files changed, 399 insertions, 1 deletions
diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro index 6b67a42f69..2da1c9b41e 100644 --- a/src/plugins/platforms/ios/ios.pro +++ b/src/plugins/platforms/ios/ios.pro @@ -6,7 +6,7 @@ PLUGIN_CLASS_NAME = QIOSIntegrationPlugin load(qt_plugin) QT += core-private gui-private platformsupport-private -LIBS += -framework Foundation -framework UIKit -framework QuartzCore +LIBS += -framework Foundation -framework UIKit -framework QuartzCore -framework AssetsLibrary OBJECTIVE_SOURCES = \ plugin.mm \ @@ -29,6 +29,7 @@ OBJECTIVE_SOURCES = \ qiosplatformaccessibility.mm \ qiostextresponder.mm \ qiosmenu.mm \ + qiosfileengineassetslibrary.mm HEADERS = \ qiosintegration.h \ @@ -50,6 +51,8 @@ HEADERS = \ qiosplatformaccessibility.h \ qiostextresponder.h \ qiosmenu.h \ + qiosfileenginefactory.h \ + qiosfileengineassetslibrary.h OTHER_FILES = \ quiview_textinput.mm \ diff --git a/src/plugins/platforms/ios/qiosfileengineassetslibrary.h b/src/plugins/platforms/ios/qiosfileengineassetslibrary.h new file mode 100644 index 0000000000..ef5b6e7415 --- /dev/null +++ b/src/plugins/platforms/ios/qiosfileengineassetslibrary.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIOSFILEENGINEASSETSLIBRARY_H +#define QIOSFILEENGINEASSETSLIBRARY_H + +#include <QtCore/private/qabstractfileengine_p.h> + +Q_FORWARD_DECLARE_OBJC_CLASS(ALAsset); +class QIOSAssetData; + +class QIOSFileEngineAssetsLibrary : public QAbstractFileEngine +{ +public: + QIOSFileEngineAssetsLibrary(const QString &fileName); + ~QIOSFileEngineAssetsLibrary(); + + bool open(QIODevice::OpenMode openMode) Q_DECL_OVERRIDE; + bool close() Q_DECL_OVERRIDE; + FileFlags fileFlags(FileFlags type) const Q_DECL_OVERRIDE; + qint64 size() const Q_DECL_OVERRIDE; + qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE; + qint64 pos() const Q_DECL_OVERRIDE; + bool seek(qint64 pos) Q_DECL_OVERRIDE; + QString fileName(FileName file) const Q_DECL_OVERRIDE; + void setFileName(const QString &file) Q_DECL_OVERRIDE; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const Q_DECL_OVERRIDE; + void setError(QFile::FileError error, const QString &str) { QAbstractFileEngine::setError(error, str); } + +private: + QString m_fileName; + qint64 m_offset; + mutable QIOSAssetData *m_data; + + ALAsset *loadAsset() const; +}; + +#endif // QIOSFILEENGINEASSETSLIBRARY_H + diff --git a/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm new file mode 100644 index 0000000000..427a205f3f --- /dev/null +++ b/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm @@ -0,0 +1,253 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qiosfileengineassetslibrary.h" + +#import <UIKit/UIKit.h> +#import <AssetsLibrary/AssetsLibrary.h> + +#include <QtCore/QTimer> +#include <QtCore/private/qcoreapplication_p.h> + +class QIOSAssetData : public QObject +{ +public: + QIOSAssetData(const QString &fileName, QIOSFileEngineAssetsLibrary *engine) + : m_asset(0) + , m_fileName(fileName) + , m_assetLibrary(0) + { + switch ([ALAssetsLibrary authorizationStatus]) { + case ALAuthorizationStatusRestricted: + case ALAuthorizationStatusDenied: + engine->setError(QFile::PermissionsError, QLatin1String("Unauthorized access")); + return; + case ALAuthorizationStatusNotDetermined: + if (!static_cast<QCoreApplicationPrivate *>(QObjectPrivate::get(qApp))->in_exec) { + // Since authorization status has not been determined, the user will be asked + // to authorize the app. But since main has not finished, the dialog will be held + // back until the launch completes. To avoid a dead-lock below, we start an event + // loop to complete the launch. + QEventLoop loop; + QTimer::singleShot(1, &loop, &QEventLoop::quit); + loop.exec(); + } + break; + default: + if (g_currentAssetData) { + // It's a common pattern that QFiles pointing to the same path are created and destroyed + // several times during a single event loop cycle. To avoid loading the same asset + // over and over, we check if the last loaded asset has not been destroyed yet, and try to + // reuse its data. Since QFile is (mostly) reentrant, we need to protect m_currentAssetData + // from being modified by several threads at the same time. + QMutexLocker lock(&g_mutex); + if (g_currentAssetData && g_currentAssetData->m_fileName == fileName) { + m_assetLibrary = [g_currentAssetData->m_assetLibrary retain]; + m_asset = [g_currentAssetData->m_asset retain]; + return; + } + } + } + + // We can only load images from the asset library async. And this might take time, since it + // involves showing the authorization dialog. But the QFile API is synchronuous, so we need to + // wait until we have access to the data. [ALAssetLibrary assetForUrl:] will shedule a block on + // the current thread. But instead of spinning the event loop to force the block to execute, we + // wrap the call inside a synchronuous dispatch queue so that it executes on another thread. + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSURL *url = [NSURL URLWithString:m_fileName.toNSString()]; + m_assetLibrary = [[ALAssetsLibrary alloc] init]; + [m_assetLibrary assetForURL:url resultBlock:^(ALAsset *asset) { + m_asset = [asset retain]; + dispatch_semaphore_signal(semaphore); + } failureBlock:^(NSError *error) { + engine->setError(QFile::OpenError, QString::fromNSString(error.localizedDescription)); + dispatch_semaphore_signal(semaphore); + }]; + }); + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_release(semaphore); + + QMutexLocker lock(&g_mutex); + g_currentAssetData = this; + } + + ~QIOSAssetData() + { + QMutexLocker lock(&g_mutex); + [m_assetLibrary release]; + [m_asset release]; + if (this == g_currentAssetData) + g_currentAssetData = 0; + } + + ALAsset *m_asset; + +private: + QString m_fileName; + ALAssetsLibrary *m_assetLibrary; + + static QBasicMutex g_mutex; + static QPointer<QIOSAssetData> g_currentAssetData; +}; + +QBasicMutex QIOSAssetData::g_mutex; +QPointer<QIOSAssetData> QIOSAssetData::g_currentAssetData = 0; + +// ------------------------------------------------------------------------- + +QIOSFileEngineAssetsLibrary::QIOSFileEngineAssetsLibrary(const QString &fileName) + : m_fileName(fileName) + , m_offset(0) + , m_data(0) +{ +} + +QIOSFileEngineAssetsLibrary::~QIOSFileEngineAssetsLibrary() +{ + close(); +} + +ALAsset *QIOSFileEngineAssetsLibrary::loadAsset() const +{ + if (!m_data) + m_data = new QIOSAssetData(m_fileName, const_cast<QIOSFileEngineAssetsLibrary *>(this)); + return m_data->m_asset; +} + +bool QIOSFileEngineAssetsLibrary::open(QIODevice::OpenMode openMode) +{ + if (openMode & (QIODevice::WriteOnly | QIODevice::Text)) + return false; + return loadAsset(); +} + +bool QIOSFileEngineAssetsLibrary::close() +{ + if (m_data) { + // Delete later, so that we can reuse the asset if a QFile is + // opened with the same path during the same event loop cycle. + m_data->deleteLater(); + m_data = 0; + } + return true; +} + +QAbstractFileEngine::FileFlags QIOSFileEngineAssetsLibrary::fileFlags(QAbstractFileEngine::FileFlags type) const +{ + QAbstractFileEngine::FileFlags flags = 0; + if (!loadAsset()) + return flags; + + if (type & FlagsMask) + flags |= ExistsFlag; + if (type & PermsMask) + flags |= ReadOwnerPerm | ReadUserPerm | ReadGroupPerm | ReadOtherPerm; + if (type & TypesMask) + flags |= FileType; + + return flags; +} + +qint64 QIOSFileEngineAssetsLibrary::size() const +{ + if (ALAsset *asset = loadAsset()) + return [[asset defaultRepresentation] size]; + return 0; +} + +qint64 QIOSFileEngineAssetsLibrary::read(char *data, qint64 maxlen) +{ + ALAsset *asset = loadAsset(); + if (!asset) + return -1; + + qint64 bytesRead = qMin(maxlen, size() - m_offset); + if (!bytesRead) + return 0; + + NSError *error = 0; + [[asset defaultRepresentation] getBytes:(uint8_t *)data fromOffset:m_offset length:bytesRead error:&error]; + + if (error) { + setError(QFile::ReadError, QString::fromNSString(error.localizedDescription)); + return -1; + } + + m_offset += bytesRead; + return bytesRead; +} + +qint64 QIOSFileEngineAssetsLibrary::pos() const +{ + return m_offset; +} + +bool QIOSFileEngineAssetsLibrary::seek(qint64 pos) +{ + if (pos >= size()) + return false; + m_offset = pos; + return true; +} + +QString QIOSFileEngineAssetsLibrary::fileName(FileName file) const +{ + Q_UNUSED(file); + return m_fileName; +} + +void QIOSFileEngineAssetsLibrary::setFileName(const QString &file) +{ + if (m_data) + close(); + m_fileName = file; +} + +QStringList QIOSFileEngineAssetsLibrary::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + Q_UNUSED(filters); + Q_UNUSED(filterNames); + return QStringList(); +} diff --git a/src/plugins/platforms/ios/qiosfileenginefactory.h b/src/plugins/platforms/ios/qiosfileenginefactory.h new file mode 100644 index 0000000000..cba5818e51 --- /dev/null +++ b/src/plugins/platforms/ios/qiosfileenginefactory.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIOSFILEENGINEFACTORY_H +#define QIOSFILEENGINEFACTORY_H + +#include <QtCore/qstandardpaths.h> +#include <QtCore/private/qabstractfileengine_p.h> +#include "qiosfileengineassetslibrary.h" + +class QIOSFileEngineFactory : public QAbstractFileEngineHandler +{ +public: + QAbstractFileEngine* create(const QString &fileName) const + { + static QLatin1String assetsScheme("assets-library:"); + + if (fileName.toLower().startsWith(assetsScheme)) + return new QIOSFileEngineAssetsLibrary(fileName); + + return 0; + } +}; + +#endif // QIOSFILEENGINEFACTORY_H diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index 05d0523dbf..8a27342d2f 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -39,6 +39,7 @@ #include <qpa/qwindowsysteminterface.h> #include "qiosapplicationstate.h" +#include "qiosfileenginefactory.h" QT_BEGIN_NAMESPACE @@ -90,6 +91,7 @@ private: QIOSApplicationState m_applicationState; QIOSServices *m_platformServices; mutable QPlatformAccessibility *m_accessibility; + QIOSFileEngineFactory m_fileEngineFactory; }; QT_END_NAMESPACE |