From 43bbe790d6389396d37306c985b5c80b098e20a4 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 5 Aug 2016 17:01:33 -0700 Subject: [PATCH] Add atp support to qml --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 3 + .../networking/src/AssetResourceRequest.cpp | 5 -- libraries/networking/src/QmlAtpReply.cpp | 88 +++++++++++++++++++ libraries/networking/src/QmlAtpReply.h | 40 +++++++++ .../src/QmlNetworkAccessManager.cpp | 29 ++++++ .../networking/src/QmlNetworkAccessManager.h | 33 +++++++ libraries/ui/src/QmlWindowClass.cpp | 7 +- 7 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 libraries/networking/src/QmlAtpReply.cpp create mode 100644 libraries/networking/src/QmlAtpReply.h create mode 100644 libraries/networking/src/QmlNetworkAccessManager.cpp create mode 100644 libraries/networking/src/QmlNetworkAccessManager.h diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 8c167fafdc..ebccc8a1fc 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "OffscreenGLCanvas.h" #include "GLEscrow.h" @@ -402,6 +403,8 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // Create a QML engine. _qmlEngine = new QQmlEngine; + _qmlEngine->setNetworkAccessManagerFactory(new QmlNetworkAccessManagerFactory); + auto importList = _qmlEngine->importPathList(); importList.insert(importList.begin(), PathUtils::resourcesPath()); _qmlEngine->setImportPathList(importList); diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 6b94ee152a..a8311c6146 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -33,12 +33,7 @@ bool AssetResourceRequest::urlIsAssetHash() const { } void AssetResourceRequest::doSend() { - auto parts = _url.path().split(".", QString::SkipEmptyParts); - auto hash = parts.length() > 0 ? parts[0] : ""; - auto extension = parts.length() > 1 ? parts[1] : ""; - // We'll either have a hash or an ATP path to a file (that maps to a hash) - if (urlIsAssetHash()) { // We've detected that this is a hash - simply use AssetClient to request that asset auto parts = _url.path().split(".", QString::SkipEmptyParts); diff --git a/libraries/networking/src/QmlAtpReply.cpp b/libraries/networking/src/QmlAtpReply.cpp new file mode 100644 index 0000000000..fd1e1e5023 --- /dev/null +++ b/libraries/networking/src/QmlAtpReply.cpp @@ -0,0 +1,88 @@ +// +// QmlAtpReply.cpp +// libraries/networking/src +// +// Created by Zander Otavka on 8/4/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ResourceManager.h" +#include "QmlAtpReply.h" + +QmlAtpReply::QmlAtpReply(const QUrl& url, QObject* parent) : + _resourceRequest(ResourceManager::createResourceRequest(parent, url)) { + setOperation(QNetworkAccessManager::GetOperation); + + connect(_resourceRequest, &AssetResourceRequest::progress, this, &QmlAtpReply::downloadProgress); + connect(_resourceRequest, &AssetResourceRequest::finished, this, &QmlAtpReply::handleRequestFinish); + + _resourceRequest->send(); +} + +QmlAtpReply::~QmlAtpReply() { + if (_resourceRequest) { + _resourceRequest->deleteLater(); + _resourceRequest = nullptr; + } +} + +qint64 QmlAtpReply::bytesAvailable() const { + return _content.size() - _readOffset + QIODevice::bytesAvailable(); +} + +qint64 QmlAtpReply::readData(char* data, qint64 maxSize) { + if (_readOffset < _content.size()) { + qint64 readSize = qMin(maxSize, _content.size() - _readOffset); + memcpy(data, _content.constData() + _readOffset, readSize); + _readOffset += readSize; + return readSize; + } else { + return -1; + } +} + +void QmlAtpReply::handleRequestFinish() { + Q_ASSERT(_resourceRequest->getState() == ResourceRequest::State::Finished); + + switch (_resourceRequest->getResult()) { + case ResourceRequest::Result::Success: + setError(NoError, "Success"); + _content = _resourceRequest->getData(); + break; + case ResourceRequest::Result::InvalidURL: + setError(ContentNotFoundError, "Invalid URL"); + break; + case ResourceRequest::Result::NotFound: + setError(ContentNotFoundError, "Not found"); + break; + case ResourceRequest::Result::ServerUnavailable: + setError(ServiceUnavailableError, "Service unavailable"); + break; + case ResourceRequest::Result::AccessDenied: + setError(ContentAccessDenied, "Access denied"); + break; + case ResourceRequest::Result::Timeout: + setError(TimeoutError, "Timeout"); + break; + default: + setError(UnknownNetworkError, "Unknown error"); + break; + } + + open(ReadOnly | Unbuffered); + setHeader(QNetworkRequest::ContentLengthHeader, QVariant(_content.size())); + + if (error() != NoError) { + emit error(error()); + } + + setFinished(true); + emit readyRead(); + emit finished(); + + _resourceRequest->deleteLater(); + _resourceRequest = nullptr; +} \ No newline at end of file diff --git a/libraries/networking/src/QmlAtpReply.h b/libraries/networking/src/QmlAtpReply.h new file mode 100644 index 0000000000..7b0fa5686e --- /dev/null +++ b/libraries/networking/src/QmlAtpReply.h @@ -0,0 +1,40 @@ +// +// QmlAtpReply.h +// libraries/networking/src +// +// Created by Zander Otavka on 8/4/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_QmlAtpReply_h +#define hifi_QmlAtpReply_h + +#include +#include + +#include "AssetResourceRequest.h" + +class QmlAtpReply : public QNetworkReply { + Q_OBJECT +public: + QmlAtpReply(const QUrl& url, QObject* parent = Q_NULLPTR); + ~QmlAtpReply(); + qint64 bytesAvailable() const override; + void abort() override { } + bool isSequential() const override { return true; } + +protected: + qint64 readData(char* data, qint64 maxSize) override; + +private: + void handleRequestFinish(); + + ResourceRequest* _resourceRequest { nullptr }; + QByteArray _content; + qint64 _readOffset { 0 }; +}; + +#endif // hifi_QmlAtpReply_h \ No newline at end of file diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp new file mode 100644 index 0000000000..9e86d3b85f --- /dev/null +++ b/libraries/networking/src/QmlNetworkAccessManager.cpp @@ -0,0 +1,29 @@ +// +// QmlNetworkAccessManager.cpp +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "QmlAtpReply.h" +#include "QmlNetworkAccessManager.h" + +QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { + return new QmlNetworkAccessManager(parent); +} + +QNetworkReply* QmlNetworkAccessManager::createRequest(Operation operation, const QNetworkRequest& request, QIODevice* device) { + if (request.url().scheme() == "atp" && operation == GetOperation) { + return new QmlAtpReply(request.url()); + //auto url = request.url().toString(); + //return QNetworkAccessManager::createRequest(operation, request, device); + } else { + return QNetworkAccessManager::createRequest(operation, request, device); + } +} \ No newline at end of file diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h new file mode 100644 index 0000000000..72ca0a4cb4 --- /dev/null +++ b/libraries/networking/src/QmlNetworkAccessManager.h @@ -0,0 +1,33 @@ +// +// QmlNetworkAccessManager.h +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_QmlNetworkAccessManager_h +#define hifi_QmlNetworkAccessManager_h + +#include +#include +#include + +class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { +public: + QNetworkAccessManager* create(QObject* parent); +}; + + +class QmlNetworkAccessManager : public QNetworkAccessManager { + Q_OBJECT +public: + QmlNetworkAccessManager(QObject* parent = Q_NULLPTR) : QNetworkAccessManager(parent) { } +protected: + QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR); +}; + +#endif // hifi_QmlNetworkAccessManager_h \ No newline at end of file diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index c3ca5f54d9..554ae7d8c2 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -59,9 +59,10 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) { properties = context->argument(0).toVariant().toMap(); } - QString url = properties[SOURCE_PROPERTY].toString(); - if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:")) { - properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url).toString(); + QUrl url { properties[SOURCE_PROPERTY].toString() }; + if (url.scheme() != "http" && url.scheme() != "https" && url.scheme() != "file" && url.scheme() != "about" && + url.scheme() != "atp") { + properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url.toString()).toString(); } return properties;