diff options
Diffstat (limited to 'src/plugins/platforms/ios/qiosfileengineassetslibrary.mm')
-rw-r--r-- | src/plugins/platforms/ios/qiosfileengineassetslibrary.mm | 469 |
1 files changed, 0 insertions, 469 deletions
diff --git a/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm deleted file mode 100644 index bb12c164d6..0000000000 --- a/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm +++ /dev/null @@ -1,469 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qiosfileengineassetslibrary.h" - -#import <UIKit/UIKit.h> -#import <AssetsLibrary/AssetsLibrary.h> - -#include <QtCore/QTimer> -#include <QtCore/private/qcoreapplication_p.h> -#include <QtCore/qurl.h> -#include <QtCore/qset.h> -#include <QtCore/qthreadstorage.h> - -static QThreadStorage<QString> g_iteratorCurrentUrl; -static QThreadStorage<QPointer<QIOSAssetData> > g_assetDataCache; - -static const int kBufferSize = 10; -static ALAsset *kNoAsset = 0; - -static bool ensureAuthorizationDialogNotBlocked() -{ - if ([ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusNotDetermined) - return true; - - if (static_cast<QCoreApplicationPrivate *>(QObjectPrivate::get(qApp))->in_exec) - return true; - - if ([NSThread isMainThread]) { - // The dialog is about to show, but since main has not finished, the dialog will be held - // back until the launch completes. This is problematic since we cannot successfully return - // back to the caller before the asset is ready, which also includes showing the dialog. To - // work around this, we create an event loop to that will complete the launch (return from the - // applicationDidFinishLaunching callback). But this will only work if we're on the main thread. - QEventLoop loop; - QTimer::singleShot(1, &loop, &QEventLoop::quit); - loop.exec(); - } else { - NSLog(@"QIOSFileEngine: unable to show assets authorization dialog from non-gui thread before QApplication is executing."); - return false; - } - - return true; -} - -// ------------------------------------------------------------------------- - -class QIOSAssetEnumerator -{ -public: - QIOSAssetEnumerator(ALAssetsLibrary *assetsLibrary, ALAssetsGroupType type) - : m_semWriteAsset(dispatch_semaphore_create(kBufferSize)) - , m_semReadAsset(dispatch_semaphore_create(0)) - , m_stop(false) - , m_assetsLibrary([assetsLibrary retain]) - , m_type(type) - , m_buffer(QVector<ALAsset *>(kBufferSize)) - , m_readIndex(0) - , m_writeIndex(0) - , m_nextAssetReady(false) - { - if (!ensureAuthorizationDialogNotBlocked()) - writeAsset(kNoAsset); - else - startEnumerate(); - } - - ~QIOSAssetEnumerator() - { - m_stop = true; - - // Flush and autorelease remaining assets in the buffer - while (hasNext()) - next(); - - // Documentation states that we need to balance out calls to 'wait' - // and 'signal'. Since the enumeration function always will be one 'wait' - // ahead, we need to signal m_semProceedToNextAsset one last time. - dispatch_semaphore_signal(m_semWriteAsset); - dispatch_release(m_semReadAsset); - dispatch_release(m_semWriteAsset); - - [m_assetsLibrary autorelease]; - } - - bool hasNext() - { - if (!m_nextAssetReady) { - dispatch_semaphore_wait(m_semReadAsset, DISPATCH_TIME_FOREVER); - m_nextAssetReady = true; - } - return m_buffer[m_readIndex] != kNoAsset; - } - - ALAsset *next() - { - Q_ASSERT(m_nextAssetReady); - Q_ASSERT(m_buffer[m_readIndex]); - - ALAsset *asset = [m_buffer[m_readIndex] autorelease]; - dispatch_semaphore_signal(m_semWriteAsset); - - m_readIndex = (m_readIndex + 1) % kBufferSize; - m_nextAssetReady = false; - return asset; - } - -private: - dispatch_semaphore_t m_semWriteAsset; - dispatch_semaphore_t m_semReadAsset; - std::atomic_bool m_stop; - - ALAssetsLibrary *m_assetsLibrary; - ALAssetsGroupType m_type; - QVector<ALAsset *> m_buffer; - int m_readIndex; - int m_writeIndex; - bool m_nextAssetReady; - - void writeAsset(ALAsset *asset) - { - dispatch_semaphore_wait(m_semWriteAsset, DISPATCH_TIME_FOREVER); - m_buffer[m_writeIndex] = [asset retain]; - dispatch_semaphore_signal(m_semReadAsset); - m_writeIndex = (m_writeIndex + 1) % kBufferSize; - } - - void startEnumerate() - { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [m_assetsLibrary enumerateGroupsWithTypes:m_type usingBlock:^(ALAssetsGroup *group, BOOL *stopEnumerate) { - - if (!group) { - writeAsset(kNoAsset); - return; - } - - if (m_stop) { - *stopEnumerate = true; - return; - } - - [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stopEnumerate) { - Q_UNUSED(index); - if (!asset || ![[asset valueForProperty:ALAssetPropertyType] isEqual:ALAssetTypePhoto]) - return; - - writeAsset(asset); - *stopEnumerate = m_stop; - }]; - } failureBlock:^(NSError *error) { - NSLog(@"QIOSFileEngine: %@", error); - writeAsset(kNoAsset); - }]; - }); - } - -}; - -// ------------------------------------------------------------------------- - -class QIOSAssetData : public QObject -{ -public: - QIOSAssetData(const QString &assetUrl, QIOSFileEngineAssetsLibrary *engine) - : m_asset(0) - , m_assetUrl(assetUrl) - , m_assetLibrary(0) - { - if (!ensureAuthorizationDialogNotBlocked()) - return; - - if (QIOSAssetData *assetData = g_assetDataCache.localData()) { - // 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. - if (assetData->m_assetUrl == assetUrl) { - m_assetLibrary = [assetData->m_assetLibrary retain]; - m_asset = [assetData->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:assetUrl.toNSString()]; - m_assetLibrary = [[ALAssetsLibrary alloc] init]; - [m_assetLibrary assetForURL:url resultBlock:^(ALAsset *asset) { - - if (!asset) { - // When an asset couldn't be loaded, chances are that it belongs to ALAssetsGroupPhotoStream. - // Such assets can be stored in the cloud and might need to be downloaded first. Unfortunately, - // forcing that to happen is hidden behind private APIs ([ALAsset requestDefaultRepresentation]). - // As a work-around, we search for it instead, since that will give us a pointer to the asset. - QIOSAssetEnumerator e(m_assetLibrary, ALAssetsGroupPhotoStream); - while (e.hasNext()) { - ALAsset *a = e.next(); - QString url = QUrl::fromNSURL([a valueForProperty:ALAssetPropertyAssetURL]).toString(); - if (url == assetUrl) { - asset = a; - break; - } - } - } - - if (!asset) - engine->setError(QFile::OpenError, QLatin1String("could not open image")); - - 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); - - g_assetDataCache.setLocalData(this); - } - - ~QIOSAssetData() - { - [m_assetLibrary release]; - [m_asset release]; - if (g_assetDataCache.localData() == this) - g_assetDataCache.setLocalData(0); - } - - ALAsset *m_asset; - -private: - QString m_assetUrl; - ALAssetsLibrary *m_assetLibrary; -}; - -// ------------------------------------------------------------------------- - -#ifndef QT_NO_FILESYSTEMITERATOR - -class QIOSFileEngineIteratorAssetsLibrary : public QAbstractFileEngineIterator -{ -public: - QIOSAssetEnumerator *m_enumerator; - - QIOSFileEngineIteratorAssetsLibrary( - QDir::Filters filters, const QStringList &nameFilters) - : QAbstractFileEngineIterator(filters, nameFilters) - , m_enumerator(new QIOSAssetEnumerator([[[ALAssetsLibrary alloc] init] autorelease], ALAssetsGroupAll)) - { - } - - ~QIOSFileEngineIteratorAssetsLibrary() - { - delete m_enumerator; - g_iteratorCurrentUrl.setLocalData(QString()); - } - - QString next() Q_DECL_OVERRIDE - { - // Cache the URL that we are about to return, since QDir will immediately create a - // new file engine on the file and ask if it exists. Unless we do this, we end up - // creating a new ALAsset just to verify its existence, which will be especially - // costly for assets belonging to ALAssetsGroupPhotoStream. - ALAsset *asset = m_enumerator->next(); - QString url = QUrl::fromNSURL([asset valueForProperty:ALAssetPropertyAssetURL]).toString(); - g_iteratorCurrentUrl.setLocalData(url); - return url; - } - - bool hasNext() const Q_DECL_OVERRIDE - { - return m_enumerator->hasNext(); - } - - QString currentFileName() const Q_DECL_OVERRIDE - { - return g_iteratorCurrentUrl.localData(); - } - - QFileInfo currentFileInfo() const Q_DECL_OVERRIDE - { - return QFileInfo(currentFileName()); - } -}; - -#endif - -// ------------------------------------------------------------------------- - -QIOSFileEngineAssetsLibrary::QIOSFileEngineAssetsLibrary(const QString &fileName) - : m_offset(0) - , m_data(0) -{ - setFileName(fileName); -} - -QIOSFileEngineAssetsLibrary::~QIOSFileEngineAssetsLibrary() -{ - close(); -} - -ALAsset *QIOSFileEngineAssetsLibrary::loadAsset() const -{ - if (!m_data) - m_data = new QIOSAssetData(m_assetUrl, 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; - const bool isDir = (m_assetUrl == QLatin1String("assets-library://")); - const bool exists = isDir || m_assetUrl == g_iteratorCurrentUrl.localData() || loadAsset(); - - if (!exists) - return flags; - - if (type & FlagsMask) - flags |= ExistsFlag; - if (type & PermsMask) { - ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus]; - if (status != ALAuthorizationStatusRestricted && status != ALAuthorizationStatusDenied) - flags |= ReadOwnerPerm | ReadUserPerm | ReadGroupPerm | ReadOtherPerm; - } - if (type & TypesMask) - flags |= isDir ? DirectoryType : 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; - // QUrl::fromLocalFile() will remove double slashes. Since the asset url is - // passed around as a file name in the app (and converted to/from a file url, e.g - // in QFileDialog), we need to ensure that m_assetUrl ends up being valid. - int index = file.indexOf(QLatin1String("/asset")); - if (index == -1) - m_assetUrl = QLatin1String("assets-library://"); - else - m_assetUrl = QLatin1String("assets-library:/") + file.mid(index); -} - -QStringList QIOSFileEngineAssetsLibrary::entryList(QDir::Filters filters, const QStringList &filterNames) const -{ - return QAbstractFileEngine::entryList(filters, filterNames); -} - -#ifndef QT_NO_FILESYSTEMITERATOR - -QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::beginEntryList( - QDir::Filters filters, const QStringList &filterNames) -{ - return new QIOSFileEngineIteratorAssetsLibrary(filters, filterNames); -} - -QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::endEntryList() -{ - return 0; -} - -#endif |