From f954e15fb66dfa353df9de2dcac0c7667593806c Mon Sep 17 00:00:00 2001 From: Morgan McMillian Date: Thu, 16 Nov 2017 08:37:13 -0800 Subject: [PATCH] Add NetImageManager and NetImageTracker classes from BlackBerry --- src/applicationui.cpp | 4 + src/netimagemanager.cpp | 199 ++++++++++++++++++++++++++++++++++++++++ src/netimagemanager.h | 146 +++++++++++++++++++++++++++++ src/netimagetracker.cpp | 108 ++++++++++++++++++++++ src/netimagetracker.h | 115 +++++++++++++++++++++++ 5 files changed, 572 insertions(+) create mode 100644 src/netimagemanager.cpp create mode 100644 src/netimagemanager.h create mode 100644 src/netimagetracker.cpp create mode 100644 src/netimagetracker.h diff --git a/src/applicationui.cpp b/src/applicationui.cpp index e1fe9fe..fe838fc 100644 --- a/src/applicationui.cpp +++ b/src/applicationui.cpp @@ -18,6 +18,8 @@ #include "Pnut.h" #include "WebImageView.h" #include "ActiveFrameQML.h" +#include "netimagemanager.h" +#include "netimagetracker.h" #include #include @@ -56,6 +58,8 @@ ApplicationUI::ApplicationUI() : qmlRegisterType("com.monkeystew.pnut", 1, 0, "Pnut"); qmlRegisterType ("com.monkeystew.qtimer", 1, 0, "QTimer"); qmlRegisterType("org.labsquare", 1, 0, "WebImageView"); + qmlRegisterType("com.netimage", 1, 0, "NetImageTracker"); + qmlRegisterType("com.netimage", 1, 0, "NetImageManager"); m_appSettings = new QSettings("Morgan McMillian", "Goober"); diff --git a/src/netimagemanager.cpp b/src/netimagemanager.cpp new file mode 100644 index 0000000..b7d42a4 --- /dev/null +++ b/src/netimagemanager.cpp @@ -0,0 +1,199 @@ +/* Copyright (c) 2012 Research In Motion Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "netimagemanager.h" + +#include +#include + +#include + +using namespace bb::cascades; + + +const char* const NetImageManager::mDefaultId = "netimagemanager"; + +NetImageManager::NetImageManager(QObject *parent) : + QObject(parent) { + mCacheId = mDefaultId; + mCacheSize = 125; + + QString diskPath = QDir::homePath() + "/" + mCacheId; + + if (!QDir(diskPath).exists()) { + QDir().mkdir(diskPath); + } + + // Connect to the sslErrors signal to the onSslErrors() function. This will help us see what errors + // we get when connecting to the address given by mWeatherAdress. + connect(&mAccessManager, + SIGNAL(sslErrors ( QNetworkReply * , const QList & )), + this, + SLOT(onSslErrors ( QNetworkReply * , const QList & ))); + + // Connect to the reply finished signal to httpFinsihed() Slot function. + connect(&mAccessManager, SIGNAL(finished(QNetworkReply *)), this, + SLOT(httpFinished(QNetworkReply *))); +} + +NetImageManager::~NetImageManager() { +} + +void NetImageManager::lookUpImage(const QString imageName) { + QUrl url = QUrl(imageName); + // Check if image is stored on disc + // The qHash is a bucket type hash so the doubling is to remove possible collisions. + QString diskPath = QDir::homePath() + "/" + mCacheId + "/" + + QString::number(qHash(url.host())) + "_" + + QString::number(qHash(url.path())) + ".JPG"; + + QFile imageFile(diskPath); + + // If the file exists, send a signal the image is ready + if (imageFile.exists()) { + emit imageReady(diskPath, url.toString()); + } else { + // otherwise let's download the file, but first we show a loading image + emit imageReady(imageName, "loading"); + + QNetworkRequest request(url); + if (mQueue.isEmpty()) { + mAccessManager.get(request); + } + + mQueue.append(request); + } + +} + +void NetImageManager::setCacheId(QString cacheId) { + if (mCacheId != cacheId) { + mCacheId = cacheId; + + QString diskPath = QDir::homePath() + "/" + mCacheId; + + if (!QDir(diskPath).exists()) { + QDir().mkdir(diskPath); + } + + emit cacheIdChanged(mCacheId); + } + houseKeep(); +} + +QString NetImageManager::cacheId() { + return mCacheId; +} + +void NetImageManager::setCacheSize(int cacheSize) { + if (mCacheSize != cacheSize) { + mCacheSize = cacheSize; + emit cacheSizeChanged(mCacheSize); + } + houseKeep(); +} + +int NetImageManager::cacheSize() { + return mCacheSize; +} + +void NetImageManager::houseKeep() { + QString diskPath = QDir::homePath() + "/" + mCacheId + "/"; + + QDir directory(diskPath); + + if (directory.count() > (uint) mCacheSize) { + //Find the oldest file and delete it. + QFileInfoList list = directory.entryInfoList(QDir::Files, QDir::Time); + QFile::remove(list.at(list.size() - 1).absoluteFilePath()); + //maybe there are more then the permitted amount of files here, let's call housekeeping again + houseKeep(); + } +} + +void NetImageManager::httpFinished(QNetworkReply * reply) { + if (reply->error() == QNetworkReply::NoError) { + QImage qImage; + qImage.loadFromData(reply->readAll()); + + if (qImage.isNull()) { + return; + } + + // When the download is finished we make a hash-tag for the image out of it's url so we can find it again, + // then we save it as a .JPG. The qHash is a bucket type hash so the doubling is to remove possible collisions. + QString diskPath = QDir::homePath() + "/" + mCacheId + "/" + + QString::number(qHash(reply->url().host())) + "_" + + QString::number(qHash(reply->url().path())) + ".JPG"; + + if (qImage.save(diskPath)) { + // houseKeep() is called to see that we don't save more then we are allowed in the cache + houseKeep(); + emit imageReady(diskPath, reply->url().toString()); + } + + //we remove the first item in the download queue + if (!mQueue.isEmpty()) { + mQueue.removeFirst(); + + if (!mQueue.isEmpty()) { + QNetworkRequest request = mQueue.first(); + mAccessManager.get(request); + } + } + } else { + //Handle error + qDebug() << "Could Not access image" << reply->url().toString(); + } + reply->deleteLater(); +} +void NetImageManager::onDialogFinished(bb::system::SystemUiResult::Type type) +{ + exit(0); +} + +void NetImageManager::onSslErrors(QNetworkReply * reply, + const QList & errors) { + + foreach (QSslError e, errors) + qDebug() << "SSL error: " << e; + + + + SystemDialog *dialog = new SystemDialog("OK"); + + dialog->setTitle(tr("SSL errors received")); + dialog->setBody(tr("We have received information about a security breach in the protocol. Press \"OK\" to terminate the application")); + + + // Connect your functions to handle the predefined signals for the buttons. + // The slot will check the SystemUiResult to see which button was clicked. + + bool success = connect(dialog, + SIGNAL(finished(bb::system::SystemUiResult::Type)), + this, + SLOT(onDialogFinished(bb::system::SystemUiResult::Type))); + + if (success) { + // Signal was successfully connected. + // Now show the dialog box in your UI + + dialog->show(); + } else { + // Failed to connect to signal. + dialog->deleteLater(); + } + +} diff --git a/src/netimagemanager.h b/src/netimagemanager.h new file mode 100644 index 0000000..f8f3a77 --- /dev/null +++ b/src/netimagemanager.h @@ -0,0 +1,146 @@ +/* Copyright (c) 2012 Research In Motion Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _NETIMAGECACHE_H_ +#define _NETIMAGECACHE_H_ + +#include +#include +#include + +#include +#include + +using namespace bb::cascades; +using namespace bb::system; + +/** + * NetImageManager is a cache service for our Internet downloaded images. + * You can set the size of the cache and an id for the cache. + * If you want to reuse the cache between different pages it's possible. + */ +class NetImageManager: public QObject +{ + Q_OBJECT + + /** + * This property sets the name of the image cache, if none is set we will use it to the + * default "netimagemanager". + */ + Q_PROPERTY(QString cacheId READ cacheId WRITE setCacheId NOTIFY cacheIdChanged) + + /** + * Sets the size of the cache in number of files in the directory, if not set, it defaults + * to 125 files. + */ + Q_PROPERTY(int cacheSize READ cacheSize WRITE setCacheSize NOTIFY cacheSizeChanged) + +public: + /** + * This is our constructor which initializes the member variables. + * @param parent The parent QObject, if not specified, 0 is used. + */ + NetImageManager(QObject *parent = 0); + ~NetImageManager(); + + /** + * This function sets the cacheId property. + * + * @param cacheId The cacheId used for storing downloaded in a folder. + */ + void setCacheId(QString cacheId); + + /** + * This function return the chacheId, that is the location of the folder where images + * are stored for the cache object. + * + * @return The cacheId + */ + QString cacheId(); + + /** + * This function sets the cacheSize property. + * + * @param cacheId The cacheSize used for cleaning the cache folder. + */ + void setCacheSize(int cacheSize); + + /** + * This function return the chacheSize. + * + * @return The cacheId + */ + int cacheSize(); + + /** + * Check if the image exists in cache + * + * @return the full path to the image if it exists otherwise 0 + */ + QString getNetImage(QString imageName); + + void lookUpImage(const QString imageName); + + /** + * Check if the cache is full and if so deletes the oldest + */ + void houseKeep(); + + + public slots: + /** + * Slot called for by the dialog that you get with SSL-errors + */ + void onDialogFinished(bb::system::SystemUiResult::Type type); + + signals: + /** + * This signal is emitted when a new cacheId has been set + */ + void cacheIdChanged(QString cacheId); + void cacheSizeChanged(int cacheSize); + + void imageReady(const QString filePath, const QString imageName); + +private slots: + /** + * This Slot function is called when the network request is complete. + */ + void httpFinished( QNetworkReply * reply ); + + /** + * This Slot function is connected to the mAccessManager sslErrors signal. This function + * allows us to see what errors we get when connecting to the address given by mWeatherAdress. + * + * @param reply The network reply + * @param errors SSL Error List + */ + void onSslErrors(QNetworkReply * reply, const QList & errors); + +private: + + // Property variables + QString mCacheId; + int mCacheSize; + + // String constant for the default id of the image cache + static const char* const mDefaultId; + + // The network parameters; used for accessing a file from the Internet + QNetworkAccessManager mAccessManager; + + QList mQueue; +}; + +#endif // _NETIMAGECACHE_H_ diff --git a/src/netimagetracker.cpp b/src/netimagetracker.cpp new file mode 100644 index 0000000..9ccea1b --- /dev/null +++ b/src/netimagetracker.cpp @@ -0,0 +1,108 @@ +/* Copyright (c) 2012 Research In Motion Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "netimagetracker.h" +#include + +using namespace bb; +using namespace bb::cascades; + +NetImageTracker::NetImageTracker(QObject *parent) : + ImageTracker(parent), mManager(0) +{ + mIsCreated = false; + + connect(this, SIGNAL(creationCompleted()), this, SLOT(onCreationCompleted())); +} + +void NetImageTracker::onCreationCompleted() +{ + mIsCreated = true; + + if (!mSource.isEmpty() && mManager) { + // Once creation of the tracker has completed, the lookup of the image is ready to + // be executed. If the tracker is used within a list item this lookup is not needed + // since the update of list item data will enforce a refresh of the imageSource that + // will perform the lookup. But for the tracker to work in ImageViews that are not + // part of a list item we need this here. + mManager->lookUpImage(mSource); + } +} + +void NetImageTracker::onImageReady(const QString filePath, const QString imageName) +{ + // The NetImageManager will emit a signal to all NetImageTrackers, make sure the + // image that is ready belongs to this tracker. + if (imageName.compare(mSource) == 0) { + if (imageName.compare("loading") == 0) { + // If we don't have an image to display, let's display a loading image + QUrl url = QUrl("asset:///images/ca_rss_unread.png"); + setImageSource(url); + } else { + // Set the path to the image that is now downloaded and cached in the data folder on the device. + QUrl url = QUrl(filePath); + setImageSource(url); + + } + } +} + +void NetImageTracker::setSource(const QString source) +{ + if (!source.isEmpty() && mSource.compare(source) != 0) { + mSource = source; + + if (mManager) { + // If a manger has been set make a request to look up the image. Otherwise + // the request is delayed to onCreationCompleted or at the next time a call + // to set the source is made. + mManager->lookUpImage(mSource); + } else { + qWarning() + << "This NetImageTracker does not have any NetImageManager, set up one as an attached object and add it to the property."; + } + + emit sourceChanged(mSource); + } +} + +QString NetImageTracker::source() +{ + return mSource; +} + +void NetImageTracker::setManager(NetImageManager *manager) +{ + if (mManager != manager) { + + // Change the manager that is used for the tracker. + if (mManager) { + disconnect(mManager, SIGNAL(imageReady(const QString , const QString )), this, + SLOT(onImageReady( const QString , const QString ))); + delete (mManager); + } + + mManager = manager; + emit managerChanged(mManager); + + connect(mManager, SIGNAL(imageReady(const QString , const QString )), this, + SLOT(onImageReady( const QString , const QString ))); + } +} + +NetImageManager *NetImageTracker::manager() +{ + return mManager; +} + diff --git a/src/netimagetracker.h b/src/netimagetracker.h new file mode 100644 index 0000000..a1fe6e1 --- /dev/null +++ b/src/netimagetracker.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2012 Research In Motion Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _NETIMAGETRACKER_H_ +#define _NETIMAGETRACKER_H_ + +#include "netimagemanager.h" +#include + +using namespace bb::cascades; + +/** + * The NetImageTracker is used so that Cascades can be informed when an image is downloaded + * via a NetImageManager. + */ +class NetImageTracker: public bb::cascades::ImageTracker +{ +Q_OBJECT + + /** + * The NetImageManager property points to a manager that is used for downloading and caching + * images. + */ +Q_PROPERTY(NetImageManager *manager READ manager WRITE setManager NOTIFY managerChanged) + + /** + * Sets the NetImageTracker source, the remote networked url that points to the image. + * You need to set one as an attached object in your QML files. + */ +Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged) + +public: + /** + * Constructor; Sets up the net image view + * @param parent The parent Container. + */ + NetImageTracker(QObject *parent = 0); + +public slots: + + /** + * Setter function for setting the trackers net image manager + * + * @param manager in manager that we're going to use. + */ + void setManager(NetImageManager *manager); + + /** + * Getter for Trackers Manager object + * @return + */ + NetImageManager *manager(); + + /** + * Setter for the source object. + * + * @param source in string with path to the source. + */ + void setSource(const QString source); + + /** + * Getter for source + * @return a string of the source + */ + QString source(); + +signals: + /** + * Signal that emits when the source have changed, wont happen automatically + * + * @param source the new source + */ + void sourceChanged(QString source); + + /** + * signal that is emitted if the manager is changed, will not happen automatically. + * + * @param imageCache the new NetImageManager we want to have . + */ + void managerChanged(NetImageManager *imageCache); + +private slots: + /** + * Emitted when we are done with the setup of this class + */ + void onCreationCompleted(); + + /** + * Emitted when we have a image that is ready for consumption + * + * @param filePath the path to the image that we can do what we want with + * @param imageName the actual name of the file, useful! + */ + void onImageReady(const QString filePath, const QString imageName); + +private: + QString mSource; + + NetImageManager *mManager; + bool mIsCreated; +}; + +#endif // ifndef _NETIMAGETRACKER_H_