[app] Made root directory configurable for removable storage

The dconf entry is /apps/harbour-books/removableRoot
This commit is contained in:
Slava Monich 2016-10-22 20:43:00 +03:00
parent 1d4fdeaea9
commit af9271dff5
12 changed files with 273 additions and 166 deletions

View file

@ -39,9 +39,9 @@ ApplicationWindow {
allowedOrientations: { allowedOrientations: {
switch (Settings.orientation) { switch (Settings.orientation) {
default: default:
case BooksSettings.OrientationAny: return Orientation.All case Settings.OrientationAny: return Orientation.All
case BooksSettings.OrientationPortrait: return Orientation.Portrait case Settings.OrientationPortrait: return Orientation.Portrait
case BooksSettings.OrientationLandscape: return Orientation.Landscape case Settings.OrientationLandscape: return Orientation.Landscape
} }
} }

View file

@ -51,7 +51,6 @@
#define BOOKS_QML_FILE BOOKS_QML_DIR "/BooksMain.qml" #define BOOKS_QML_FILE BOOKS_QML_DIR "/BooksMain.qml"
#define BOOKS_INTERNAL_ROOT "Documents/Books" #define BOOKS_INTERNAL_ROOT "Documents/Books"
#define BOOKS_REMOVABLE_ROOT "Books"
#define BOOKS_QML_PLUGIN "harbour.books" #define BOOKS_QML_PLUGIN "harbour.books"
#define BOOKS_QML_PLUGIN_V1 1 #define BOOKS_QML_PLUGIN_V1 1

View file

@ -47,12 +47,14 @@
#define KEY_PAGE_DETAILS "pageDetails" #define KEY_PAGE_DETAILS "pageDetails"
#define KEY_CURRENT_BOOK "currentBook" #define KEY_CURRENT_BOOK "currentBook"
#define KEY_CURRENT_FOLDER "currentFolder" #define KEY_CURRENT_FOLDER "currentFolder"
#define KEY_REMOVABLE_ROOT "removableRoot"
#define KEY_INVERT_COLORS "invertColors" #define KEY_INVERT_COLORS "invertColors"
#define KEY_ORIENTATION "orientation" #define KEY_ORIENTATION "orientation"
#define DEFAULT_FONT_SIZE 0 #define DEFAULT_FONT_SIZE 0
#define DEFAULT_PAGE_DETAILS 0 #define DEFAULT_PAGE_DETAILS 0
#define DEFAULT_CURRENT_BOOK QString() #define DEFAULT_CURRENT_BOOK QString()
#define DEFAULT_CURRENT_FOLDER QString() #define DEFAULT_CURRENT_FOLDER QString()
#define DEFAULT_REMOVABLE_ROOT "Books"
#define DEFAULT_INVERT_COLORS false #define DEFAULT_INVERT_COLORS false
#define DEFAULT_ORIENTATION (BooksSettings::OrientationAny) #define DEFAULT_ORIENTATION (BooksSettings::OrientationAny)
@ -227,6 +229,7 @@ public:
MGConfItem* iCurrentFolderConf; MGConfItem* iCurrentFolderConf;
MGConfItem* iCurrentBookPathConf; MGConfItem* iCurrentBookPathConf;
MGConfItem* iOrientationConf; MGConfItem* iOrientationConf;
MGConfItem* iRemovableRootConf;
mutable shared_ptr<ZLTextStyle> iTextStyle[FontSizeSteps+1]; mutable shared_ptr<ZLTextStyle> iTextStyle[FontSizeSteps+1];
BooksBook* iCurrentBook; BooksBook* iCurrentBook;
QString iCurrentStorageDevice; QString iCurrentStorageDevice;
@ -244,11 +247,10 @@ BooksSettings::Private::Private(BooksSettings* aParent) :
iCurrentFolderConf(new MGConfItem(DCONF_PATH KEY_CURRENT_FOLDER, this)), iCurrentFolderConf(new MGConfItem(DCONF_PATH KEY_CURRENT_FOLDER, this)),
iCurrentBookPathConf(new MGConfItem(DCONF_PATH KEY_CURRENT_BOOK, this)), iCurrentBookPathConf(new MGConfItem(DCONF_PATH KEY_CURRENT_BOOK, this)),
iOrientationConf(new MGConfItem(DCONF_PATH KEY_ORIENTATION, this)), iOrientationConf(new MGConfItem(DCONF_PATH KEY_ORIENTATION, this)),
iRemovableRootConf(new MGConfItem(DCONF_PATH KEY_REMOVABLE_ROOT, this)),
iCurrentBook(NULL) iCurrentBook(NULL)
{ {
iFontSize = currentFontSize(); iFontSize = currentFontSize();
updateCurrentBook();
updateCurrentStorage();
connect(iFontSizeConf, SIGNAL(valueChanged()), SLOT(onFontSizeValueChanged())); connect(iFontSizeConf, SIGNAL(valueChanged()), SLOT(onFontSizeValueChanged()));
connect(iCurrentFolderConf, SIGNAL(valueChanged()), SLOT(onCurrentFolderChanged())); connect(iCurrentFolderConf, SIGNAL(valueChanged()), SLOT(onCurrentFolderChanged()));
connect(iCurrentBookPathConf, SIGNAL(valueChanged()), SLOT(onCurrentBookPathChanged())); connect(iCurrentBookPathConf, SIGNAL(valueChanged()), SLOT(onCurrentBookPathChanged()));
@ -256,6 +258,7 @@ BooksSettings::Private::Private(BooksSettings* aParent) :
connect(iInvertColorsConf, SIGNAL(valueChanged()), iParent, SIGNAL(invertColorsChanged())); connect(iInvertColorsConf, SIGNAL(valueChanged()), iParent, SIGNAL(invertColorsChanged()));
connect(iInvertColorsConf, SIGNAL(valueChanged()), iParent, SIGNAL(pageBackgroundColorChanged())); connect(iInvertColorsConf, SIGNAL(valueChanged()), iParent, SIGNAL(pageBackgroundColorChanged()));
connect(iOrientationConf, SIGNAL(valueChanged()), iParent, SIGNAL(orientationChanged())); connect(iOrientationConf, SIGNAL(valueChanged()), iParent, SIGNAL(orientationChanged()));
connect(iRemovableRootConf, SIGNAL(valueChanged()), iParent, SIGNAL(removableRootChanged()));
} }
int int
@ -406,7 +409,7 @@ BooksSettings::Private::onCurrentBookPathChanged()
// BooksSettings // BooksSettings
// ========================================================================== // ==========================================================================
BooksSettings::BooksSettings(QObject* aParent) : QObject(aParent), BooksSettings::BooksSettings() :
iPrivate(new Private(this)) iPrivate(new Private(this))
{ {
} }
@ -420,6 +423,15 @@ BooksSettings::sharedInstance()
// recipient of the signal drops the last shared reference. // recipient of the signal drops the last shared reference.
instance = QSharedPointer<BooksSettings>(new BooksSettings, &QObject::deleteLater); instance = QSharedPointer<BooksSettings>(new BooksSettings, &QObject::deleteLater);
Private::sSharedInstance = instance; Private::sSharedInstance = instance;
// Finish initialization. These invoke BooksStorageManager::instance()
// which in turn calls BooksSettings::sharedInstance() to call
// removableRoot(). Now that Private::sSharedInstance is set, it
// won't cause infinite recursion although the returned BooksSettings
// object will be slightly under-initialized, so to speak. But that's
// ok as long as BooksStorageManager::instance() doesn't need anything
// from BooksSettings other than removableRoot()
instance->iPrivate->updateCurrentBook();
instance->iPrivate->updateCurrentStorage();
} }
return instance; return instance;
} }
@ -501,6 +513,12 @@ BooksSettings::setInvertColors(
iPrivate->iInvertColorsConf->set(aValue); iPrivate->iInvertColorsConf->set(aValue);
} }
QString
BooksSettings::removableRoot() const
{
return iPrivate->iRemovableRootConf->value(DEFAULT_REMOVABLE_ROOT).toString();
}
QString QString
BooksSettings::relativePath() const BooksSettings::relativePath() const
{ {

View file

@ -36,8 +36,8 @@
#include "BooksTypes.h" #include "BooksTypes.h"
#include "ZLTextStyle.h" #include "ZLTextStyle.h"
#include <QObject>
#include <QColor> #include <QColor>
#include <QtQml>
#include <QSharedPointer> #include <QSharedPointer>
class BooksSettings : public QObject class BooksSettings : public QObject
@ -52,12 +52,16 @@ class BooksSettings : public QObject
Q_PROPERTY(QString currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged) Q_PROPERTY(QString currentFolder READ currentFolder WRITE setCurrentFolder NOTIFY currentFolderChanged)
Q_PROPERTY(QString currentStorage READ currentStorage NOTIFY currentStorageChanged) Q_PROPERTY(QString currentStorage READ currentStorage NOTIFY currentStorageChanged)
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged) Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
Q_PROPERTY(QString removableRoot READ removableRoot NOTIFY removableRootChanged)
Q_PROPERTY(QColor primaryPageToolColor READ primaryPageToolColor CONSTANT) Q_PROPERTY(QColor primaryPageToolColor READ primaryPageToolColor CONSTANT)
Q_PROPERTY(QColor highlightPageToolColor READ highlightPageToolColor NOTIFY invertColorsChanged) Q_PROPERTY(QColor highlightPageToolColor READ highlightPageToolColor NOTIFY invertColorsChanged)
Q_PROPERTY(QColor pageBackgroundColor READ pageBackgroundColor NOTIFY pageBackgroundColorChanged) Q_PROPERTY(QColor pageBackgroundColor READ pageBackgroundColor NOTIFY pageBackgroundColorChanged)
Q_PROPERTY(int orientation READ orientation NOTIFY orientationChanged) Q_PROPERTY(int orientation READ orientation NOTIFY orientationChanged)
class TextStyle; class TextStyle;
// Use sharedInstance() to instantiate this class
BooksSettings();
public: public:
enum FontSize { enum FontSize {
MinFontSize = -5, MinFontSize = -5,
@ -72,8 +76,6 @@ public:
OrientationLandscape OrientationLandscape
}; };
explicit BooksSettings(QObject* aParent = NULL);
static QSharedPointer<BooksSettings> sharedInstance(); static QSharedPointer<BooksSettings> sharedInstance();
Q_INVOKABLE bool increaseFontSize(); Q_INVOKABLE bool increaseFontSize();
@ -94,6 +96,7 @@ public:
void setCurrentBook(QObject* aBook); void setCurrentBook(QObject* aBook);
QString relativePath() const; QString relativePath() const;
QString removableRoot() const;
QString currentFolder() const; QString currentFolder() const;
void setCurrentFolder(QString aValue); void setCurrentFolder(QString aValue);
@ -113,6 +116,7 @@ Q_SIGNALS:
void currentFolderChanged(); void currentFolderChanged();
void currentStorageChanged(); void currentStorageChanged();
void relativePathChanged(); void relativePathChanged();
void removableRootChanged();
void pageBackgroundColorChanged(); void pageBackgroundColorChanged();
void orientationChanged(); void orientationChanged();
@ -121,6 +125,4 @@ private:
Private* iPrivate; Private* iPrivate;
}; };
QML_DECLARE_TYPE(BooksSettings)
#endif // BOOKS_SETTINGS_H #endif // BOOKS_SETTINGS_H

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015 Jolla Ltd. * Copyright (C) 2015-2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com> * Contact: Slava Monich <slava.monich@jolla.com>
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
@ -14,7 +14,7 @@
* notice, this list of conditions and the following disclaimer in * notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the * the documentation and/or other materials provided with the
* distribution. * distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors * * Neither the name of Jolla Ltd nor the names of its contributors
* may be used to endorse or promote products derived from this * may be used to endorse or promote products derived from this
* software without specific prior written permission. * software without specific prior written permission.
* *
@ -516,7 +516,9 @@ void BooksShelf::init()
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
setRoleNames(roleNames()); setRoleNames(roleNames());
#endif #endif
QQmlEngine::setObjectOwnership(&iStorage, QQmlEngine::CppOwnership); connect(BooksStorageManager::instance(),
SIGNAL(storageReplaced(BooksStorage,BooksStorage)),
SLOT(onStorageReplaced(BooksStorage,BooksStorage)));
} }
void BooksShelf::setRelativePath(QString aPath) void BooksShelf::setRelativePath(QString aPath)
@ -537,6 +539,14 @@ void BooksShelf::setDevice(QString aDevice)
} }
} }
void BooksShelf::onStorageReplaced(BooksStorage aOld, BooksStorage aNew)
{
if (iStorage == aOld) {
iStorage = aNew;
updatePath();
}
}
void BooksShelf::setName(QString aName) void BooksShelf::setName(QString aName)
{ {
if (iStorage.isValid() && if (iStorage.isValid() &&

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015 Jolla Ltd. * Copyright (C) 2015-2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com> * Contact: Slava Monich <slava.monich@jolla.com>
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
@ -14,7 +14,7 @@
* notice, this list of conditions and the following disclaimer in * notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the * the documentation and/or other materials provided with the
* distribution. * distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors * * Neither the name of Jolla Ltd nor the names of its contributors
* may be used to endorse or promote products derived from this * may be used to endorse or promote products derived from this
* software without specific prior written permission. * software without specific prior written permission.
* *
@ -147,6 +147,7 @@ private Q_SLOTS:
void onCopyTaskProgressChanged(); void onCopyTaskProgressChanged();
void onCopyTaskDone(); void onCopyTaskDone();
void onDeleteTaskDone(); void onDeleteTaskDone();
void onStorageReplaced(BooksStorage aOldStorage, BooksStorage aNewStorage);
void saveState(); void saveState();
private: private:

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015 Jolla Ltd. * Copyright (C) 2015-2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com> * Contact: Slava Monich <slava.monich@jolla.com>
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
@ -14,7 +14,7 @@
* notice, this list of conditions and the following disclaimer in * notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the * the documentation and/or other materials provided with the
* distribution. * distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors * * Neither the name of Jolla Ltd nor the names of its contributors
* may be used to endorse or promote products derived from this * may be used to endorse or promote products derived from this
* software without specific prior written permission. * software without specific prior written permission.
* *
@ -32,6 +32,7 @@
*/ */
#include "BooksStorage.h" #include "BooksStorage.h"
#include "BooksSettings.h"
#include "BooksDefs.h" #include "BooksDefs.h"
#include "HarbourDebug.h" #include "HarbourDebug.h"
@ -62,7 +63,8 @@ class BooksStorage::Private: public QObject
Q_OBJECT Q_OBJECT
public: public:
Private(QString aDevice, QDir aBooksDir, bool aInternal); Private(QString aDevice, QString aMountPoint, QString aBooksDir,
bool aInternal);
bool isRemoved() const; bool isRemoved() const;
bool equal(const Private& aData) const; bool equal(const Private& aData) const;
@ -76,14 +78,23 @@ Q_SIGNALS:
public: public:
QAtomicInt iRef; QAtomicInt iRef;
QString iDevice; QString iDevice;
QString iMountPoint;
QDir iBooksDir; QDir iBooksDir;
QDir iConfigDir; QDir iConfigDir;
bool iInternal; bool iInternal;
bool iPresent; bool iPresent;
}; };
BooksStorage::Private::Private(QString aDevice, QDir aBooksDir, bool aInternal) : BooksStorage::Private::Private(
iRef(1), iDevice(aDevice), iBooksDir(aBooksDir), iInternal(aInternal), QString aDevice,
QString aMountPoint,
QString aBooksDir,
bool aInternal) :
iRef(1),
iDevice(aDevice),
iMountPoint(aMountPoint),
iBooksDir(aBooksDir),
iInternal(aInternal),
iPresent(true) iPresent(true)
{ {
QString cfgDir; QString cfgDir;
@ -97,7 +108,7 @@ BooksStorage::Private::Private(QString aDevice, QDir aBooksDir, bool aInternal)
cfgDir += subDir; cfgDir += subDir;
} else { } else {
cfgDir += REMOVABLE_STATE_DIR "/"; cfgDir += REMOVABLE_STATE_DIR "/";
QString label = QDir(Private::mountPoint(aBooksDir.path())).dirName(); QString label = QDir(Private::mountPoint(aBooksDir)).dirName();
if (label.isEmpty()) label = "sdcard"; if (label.isEmpty()) label = "sdcard";
cfgDir += label; cfgDir += label;
} }
@ -106,10 +117,12 @@ BooksStorage::Private::Private(QString aDevice, QDir aBooksDir, bool aInternal)
bool BooksStorage::Private::equal(const BooksStorage::Private& aData) const bool BooksStorage::Private::equal(const BooksStorage::Private& aData) const
{ {
return iInternal == iInternal && return iInternal == aData.iInternal &&
iPresent == iPresent && iPresent == aData.iPresent &&
iMountPoint == aData.iMountPoint &&
iDevice == aData.iDevice && iDevice == aData.iDevice &&
iBooksDir == aData.iBooksDir; iBooksDir == aData.iBooksDir &&
iConfigDir == aData.iConfigDir;
} }
bool BooksStorage::Private::isMountPoint(QString aPath) bool BooksStorage::Private::isMountPoint(QString aPath)
@ -153,9 +166,9 @@ BooksStorage::BooksStorage(const BooksStorage& aStorage) :
if (iPrivate) iPrivate->iRef.ref(); if (iPrivate) iPrivate->iRef.ref();
} }
BooksStorage::BooksStorage(QString aDevice, QDir aBooksDir, bool aInternal) : BooksStorage::BooksStorage(QString aDevice, QString aMount, QString aBooksDir,
QObject(NULL), bool aInternal) : QObject(NULL),
iPrivate(new Private(aDevice, aBooksDir, aInternal)), iPrivate(new Private(aDevice, aMount, aBooksDir, aInternal)),
iPassThrough(false) iPassThrough(false)
{ {
HDEBUG("config dir" << qPrintable(configDir().path())); HDEBUG("config dir" << qPrintable(configDir().path()));
@ -253,17 +266,26 @@ void BooksStorage::set(const BooksStorage& aStorage)
#define STORAGE_SCAN_INTERVAL 100 #define STORAGE_SCAN_INTERVAL 100
#define STORAGE_SCAN_TIMEOUT 5000 #define STORAGE_SCAN_TIMEOUT 5000
class BooksStorageManager::Private { class BooksStorageManager::Private : public QObject {
Q_OBJECT
public: public:
static BooksStorageManager* gInstance; static BooksStorageManager* gInstance;
Private(); Private(BooksStorageManager* aParent);
~Private(); ~Private();
int findDevice(QString aDevice) const; int findDevice(QString aDevice) const;
int findPath(QString aPath, QString* aRelPath) const; int findPath(QString aPath, QString* aRelPath) const;
bool scanMounts();
public Q_SLOTS:
void onDeviceEvent(int);
void onRemovableRootChanged();
void onScanMounts();
public: public:
BooksStorageManager* iParent;
QSharedPointer<BooksSettings> iSettings;
QList<BooksStorage> iStorageList; QList<BooksStorage> iStorageList;
struct udev* iUdev; struct udev* iUdev;
struct udev_monitor* iMonitor; struct udev_monitor* iMonitor;
@ -275,7 +297,10 @@ public:
BooksStorageManager* BooksStorageManager::Private::gInstance = NULL; BooksStorageManager* BooksStorageManager::Private::gInstance = NULL;
BooksStorageManager::Private::Private() : BooksStorageManager::Private::Private(BooksStorageManager* aParent) :
QObject(aParent),
iParent(aParent),
iSettings(BooksSettings::sharedInstance()),
iUdev(udev_new()), iUdev(udev_new()),
iMonitor(NULL), iMonitor(NULL),
iDescriptor(-1), iDescriptor(-1),
@ -303,21 +328,22 @@ BooksStorageManager::Private::Private() :
QString mount(entries.at(1)); QString mount(entries.at(1));
if (mount == homeMount) { if (mount == homeMount) {
homeDevice = entries.at(0); homeDevice = entries.at(0);
HDEBUG("home device" << homeDevice); HDEBUG("internal" << homeDevice);
} else if (mount.startsWith(mediaPrefix)) { } else if (mount.startsWith(mediaPrefix)) {
QString dev = entries.at(0); QString dev = entries.at(0);
QString path = mount; QString path(mount);
if (!path.endsWith('/')) path += '/'; if (!path.endsWith('/')) path += '/';
path += QLatin1String(BOOKS_REMOVABLE_ROOT); path += iSettings->removableRoot();
HDEBUG("removable device" << dev << path); BooksStorage bs(dev, mount, path, false);
iStorageList.append(BooksStorage(dev, path, false)); HDEBUG("removable" << dev << bs.booksDir().path());
iStorageList.append(bs);
} }
} }
} }
mounts.close(); mounts.close();
} }
iStorageList.insert(0, BooksStorage(homeDevice, homeBooks, true)); iStorageList.insert(0, BooksStorage(homeDevice, homeMount, homeBooks, true));
if (iUdev) { if (iUdev) {
iMonitor = udev_monitor_new_from_netlink(iUdev, "udev"); iMonitor = udev_monitor_new_from_netlink(iUdev, "udev");
@ -329,9 +355,28 @@ BooksStorageManager::Private::Private() :
if (iDescriptor >= 0) { if (iDescriptor >= 0) {
iNotifier = new QSocketNotifier(iDescriptor, iNotifier = new QSocketNotifier(iDescriptor,
QSocketNotifier::Read); QSocketNotifier::Read);
connect(iNotifier, SIGNAL(activated(int)),
SLOT(onDeviceEvent(int)));
} }
} }
} }
connect(iSettings.data(), SIGNAL(removableRootChanged()),
SLOT(onRemovableRootChanged()));
}
BooksStorageManager::Private::~Private()
{
if (iUdev) {
if (iMonitor) {
if (iDescriptor >= 0) {
delete iNotifier;
close(iDescriptor);
}
udev_monitor_unref(iMonitor);
}
udev_unref(iUdev);
}
} }
int BooksStorageManager::Private::findDevice(QString aDevice) const int BooksStorageManager::Private::findDevice(QString aDevice) const
@ -369,17 +414,127 @@ int BooksStorageManager::Private::findPath(QString aPath, QString* aRelPath) con
return -1; return -1;
} }
BooksStorageManager::Private::~Private() bool BooksStorageManager::Private::scanMounts()
{ {
if (iUdev) { bool newStorageFound = false;
if (iMonitor) { QList<BooksStorage> newMounts;
if (iDescriptor >= 0) { QFile mounts(STORAGE_MOUNTS_FILE);
delete iNotifier; if (mounts.open(QIODevice::ReadOnly | QIODevice::Text)) {
close(iDescriptor); // For some reason QTextStream can't read /proc/mounts line by line
QByteArray contents = mounts.readAll();
QTextStream in(&contents);
QString mediaPrefix(STORAGE_MOUNT_PREFIX);
while (!in.atEnd()) {
QString line = in.readLine();
QStringList entries = line.split(' ', QString::SkipEmptyParts);
if (entries.count() > 2) {
QString mount(entries.at(1));
if (mount.startsWith(mediaPrefix)) {
QString dev = entries.at(0);
int index = findDevice(dev);
if (index < 0) {
QString path = mount;
if (!path.endsWith('/')) path += '/';
path += iSettings->removableRoot();
HDEBUG("new removable device" << dev << path);
BooksStorage storage(dev, mount, path, false);
iStorageList.append(storage);
Q_EMIT iParent->storageAdded(storage);
newStorageFound = true;
} }
udev_monitor_unref(iMonitor);
} }
udev_unref(iUdev); }
}
mounts.close();
}
return newStorageFound;
}
void BooksStorageManager::Private::onScanMounts()
{
if (scanMounts()) {
iScanMountsTimer->stop();
} else {
QDateTime now = QDateTime::currentDateTime();
if (now > iScanDeadline) {
HDEBUG("timeout waiting for new mount to appear");
iScanMountsTimer->stop();
} else {
HDEBUG("no new mounts found");
}
}
}
void BooksStorageManager::Private::onDeviceEvent(int)
{
struct udev_device* dev = udev_monitor_receive_device(iMonitor);
if (dev) {
const char* devnode = udev_device_get_devnode(dev);
const char* action = udev_device_get_action(dev);
HDEBUG("got device");
HDEBUG(" node:" << devnode);
HDEBUG(" subsystem:" << udev_device_get_subsystem(dev));
HDEBUG(" devtype:" << udev_device_get_devtype(dev));
HDEBUG(" action:" << action);
if (devnode && action) {
if (!(strcmp(action, STORAGE_ACTION_ADD))) {
// Mount list isn't updated yet when we receive this
// notification. It takes hundreds of milliseconds until
// it gets mounted and becomes accessible.
if (!scanMounts()) {
HDEBUG("no new mounts found");
if (!iScanMountsTimer) {
QTimer* timer = new QTimer(this);
timer->setSingleShot(false);
timer->setInterval(STORAGE_SCAN_INTERVAL);
connect(timer, SIGNAL(timeout()), SLOT(onScanMounts()));
iScanMountsTimer = timer;
}
iScanMountsTimer->start();
iScanDeadline = QDateTime::currentDateTime().
addMSecs(STORAGE_SCAN_TIMEOUT);
}
} else if (!(strcmp(action, STORAGE_ACTION_REMOVE))) {
int pos = findDevice(devnode);
if (pos >= 0) {
HDEBUG("removable device is gone");
BooksStorage storage = iStorageList.takeAt(pos);
storage.iPrivate->iPresent = false;
Q_EMIT storage.iPrivate->removed();
Q_EMIT iParent->storageRemoved(storage);
}
}
}
udev_device_unref(dev);
} else {
HWARN("no device!");
}
}
void BooksStorageManager::Private::onRemovableRootChanged()
{
int i;
HDEBUG(iSettings->removableRoot());
QList<BooksStorage> replaced; // old-new pairs
for (i=iStorageList.count()-1; i>=0; i--) {
BooksStorage storage = iStorageList.at(i);
if (!storage.isInternal()) {
QString path(storage.iPrivate->iMountPoint);
if (!path.endsWith('/')) path += '/';
path += iSettings->removableRoot();
BooksStorage updated(storage.iPrivate->iDevice,
storage.iPrivate->iMountPoint, path, false);
if (!storage.equal(updated)) {
replaced.append(storage);
replaced.append(updated);
iStorageList.replace(i, updated);
} else {
HWARN(storage.root() << "didn't change");
}
}
}
for (i=0; (i+1)<replaced.count(); i+=2) {
Q_EMIT iParent->storageReplaced(replaced.at(i), replaced.at(i+1));
} }
} }
@ -402,12 +557,8 @@ void BooksStorageManager::deleteInstance()
} }
BooksStorageManager::BooksStorageManager() : BooksStorageManager::BooksStorageManager() :
iPrivate(new Private) iPrivate(new Private(this))
{ {
if (iPrivate->iNotifier) {
connect(iPrivate->iNotifier, SIGNAL(activated(int)),
SLOT(onDeviceEvent(int)));
}
} }
BooksStorageManager::~BooksStorageManager() BooksStorageManager::~BooksStorageManager()
@ -415,7 +566,6 @@ BooksStorageManager::~BooksStorageManager()
if (Private::gInstance == this) { if (Private::gInstance == this) {
Private::gInstance = NULL; Private::gInstance = NULL;
} }
delete iPrivate;
} }
int BooksStorageManager::count() const int BooksStorageManager::count() const
@ -440,100 +590,4 @@ BooksStorage BooksStorageManager::storageForPath(QString aPath, QString* aRelPat
return (index >= 0) ? iPrivate->iStorageList.at(index) : BooksStorage(); return (index >= 0) ? iPrivate->iStorageList.at(index) : BooksStorage();
} }
void BooksStorageManager::onDeviceEvent(int)
{
struct udev_device* dev = udev_monitor_receive_device(iPrivate->iMonitor);
if (dev) {
const char* devnode = udev_device_get_devnode(dev);
const char* action = udev_device_get_action(dev);
HDEBUG("got device");
HDEBUG(" node:" << devnode);
HDEBUG(" subsystem:" << udev_device_get_subsystem(dev));
HDEBUG(" devtype:" << udev_device_get_devtype(dev));
HDEBUG(" action:" << action);
if (devnode && action) {
if (!(strcmp(action, STORAGE_ACTION_ADD))) {
// Mount list isn't updated yet when we receive this
// notification. It takes hundreds of milliseconds until
// it gets mounted and becomes accessible.
if (!scanMounts()) {
HDEBUG("no new mounts found");
if (!iPrivate->iScanMountsTimer) {
QTimer* timer = new QTimer(this);
timer->setSingleShot(false);
timer->setInterval(STORAGE_SCAN_INTERVAL);
connect(timer, SIGNAL(timeout()), SLOT(onScanMounts()));
iPrivate->iScanMountsTimer = timer;
}
iPrivate->iScanMountsTimer->start();
iPrivate->iScanDeadline = QDateTime::currentDateTime().
addMSecs(STORAGE_SCAN_TIMEOUT);
}
} else if (!(strcmp(action, STORAGE_ACTION_REMOVE))) {
int pos = iPrivate->findDevice(devnode);
if (pos >= 0) {
HDEBUG("removable device is gone");
BooksStorage storage = iPrivate->iStorageList.takeAt(pos);
storage.iPrivate->iPresent = false;
Q_EMIT storage.iPrivate->removed();
Q_EMIT storageRemoved(storage);
}
}
}
udev_device_unref(dev);
} else {
HWARN("no device!");
}
}
bool BooksStorageManager::scanMounts()
{
bool newStorageFound = false;
QFile mounts(STORAGE_MOUNTS_FILE);
if (mounts.open(QIODevice::ReadOnly | QIODevice::Text)) {
// For some reason QTextStream can't read /proc/mounts line by line
QByteArray contents = mounts.readAll();
QTextStream in(&contents);
QString mediaPrefix(STORAGE_MOUNT_PREFIX);
while (!in.atEnd()) {
QString line = in.readLine();
QStringList entries = line.split(' ', QString::SkipEmptyParts);
if (entries.count() > 2) {
QString mount(entries.at(1));
if (mount.startsWith(mediaPrefix)) {
QString dev = entries.at(0);
int index = iPrivate->findDevice(dev);
if (index < 0) {
QString path = mount;
if (!path.endsWith('/')) path += '/';
path += QLatin1String(BOOKS_REMOVABLE_ROOT);
HDEBUG("new removable device" << dev << path);
BooksStorage storage(dev, path, false);
iPrivate->iStorageList.append(storage);
Q_EMIT storageAdded(storage);
newStorageFound = true;
}
}
}
}
mounts.close();
}
return newStorageFound;
}
void BooksStorageManager::onScanMounts()
{
if (scanMounts()) {
iPrivate->iScanMountsTimer->stop();
} else {
QDateTime now = QDateTime::currentDateTime();
if (now > iPrivate->iScanDeadline) {
HDEBUG("timeout waiting for new mount to appear");
iPrivate->iScanMountsTimer->stop();
} else {
HDEBUG("no new mounts found");
}
}
}
#include "BooksStorage.moc" #include "BooksStorage.moc"

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015 Jolla Ltd. * Copyright (C) 2015-2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com> * Contact: Slava Monich <slava.monich@jolla.com>
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
@ -14,7 +14,7 @@
* notice, this list of conditions and the following disclaimer in * notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the * the documentation and/or other materials provided with the
* distribution. * distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors * * Neither the name of Jolla Ltd nor the names of its contributors
* may be used to endorse or promote products derived from this * may be used to endorse or promote products derived from this
* software without specific prior written permission. * software without specific prior written permission.
* *
@ -38,6 +38,7 @@
#include <QList> #include <QList>
#include <QDir> #include <QDir>
class BooksSettings;
class BooksStorageManager; class BooksStorageManager;
class BooksStorage: public QObject class BooksStorage: public QObject
@ -76,7 +77,7 @@ Q_SIGNALS:
private: private:
friend class BooksStorageManager; friend class BooksStorageManager;
BooksStorage(QString, QDir, bool); BooksStorage(QString, QString, QString, bool);
void connectNotify(const QMetaMethod& aSignal); void connectNotify(const QMetaMethod& aSignal);
private: private:
@ -102,14 +103,10 @@ public:
Q_SIGNALS: Q_SIGNALS:
void storageAdded(BooksStorage aStorage); void storageAdded(BooksStorage aStorage);
void storageRemoved(BooksStorage aStorage); void storageRemoved(BooksStorage aStorage);
void storageReplaced(BooksStorage aOldStorage, BooksStorage aNewStorage);
private: private:
BooksStorageManager(); BooksStorageManager();
bool scanMounts();
private Q_SLOTS:
void onDeviceEvent(int);
void onScanMounts();
private: private:
class Private; class Private;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015 Jolla Ltd. * Copyright (C) 2015-2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com> * Contact: Slava Monich <slava.monich@jolla.com>
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
@ -14,7 +14,7 @@
* notice, this list of conditions and the following disclaimer in * notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the * the documentation and/or other materials provided with the
* distribution. * distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors * * Neither the name of Jolla Ltd nor the names of its contributors
* may be used to endorse or promote products derived from this * may be used to endorse or promote products derived from this
* software without specific prior written permission. * software without specific prior written permission.
* *
@ -90,6 +90,9 @@ BooksStorageModel::BooksStorageModel(QObject* aParent) :
connect(mgr, connect(mgr,
SIGNAL(storageRemoved(BooksStorage)), SIGNAL(storageRemoved(BooksStorage)),
SLOT(onStorageRemoved(BooksStorage))); SLOT(onStorageRemoved(BooksStorage)));
connect(mgr,
SIGNAL(storageReplaced(BooksStorage,BooksStorage)),
SLOT(onStorageReplaced(BooksStorage,BooksStorage)));
} }
BooksStorageModel::~BooksStorageModel() BooksStorageModel::~BooksStorageModel()
@ -210,6 +213,24 @@ void BooksStorageModel::onStorageRemoved(BooksStorage aStorage)
endRemoveRows(); endRemoveRows();
Q_EMIT countChanged(); Q_EMIT countChanged();
} else { } else {
HWARN("device not dfound on the list"); HWARN("device not found on the list");
}
}
void BooksStorageModel::onStorageReplaced(BooksStorage aOld, BooksStorage aNew)
{
int index = find(aOld);
if (index >=0) {
QModelIndex modelIndex(createIndex(index, 0));
QVector<int> roles(4);
roles.append(BooksStorageRoot);
roles.append(BooksStorageDevice);
roles.append(BooksStorageRemovable);
roles.append(BooksStorageDeleteAllRequest);
HWARN(aOld.root() << "->" << aNew.root());
iList.at(index)->iStorage = aNew;
Q_EMIT dataChanged(modelIndex, modelIndex, roles);
} else {
HWARN("device not found on the list");
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015 Jolla Ltd. * Copyright (C) 2015-2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com> * Contact: Slava Monich <slava.monich@jolla.com>
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
@ -14,7 +14,7 @@
* notice, this list of conditions and the following disclaimer in * notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the * the documentation and/or other materials provided with the
* distribution. * distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors * * Neither the name of Jolla Ltd nor the names of its contributors
* may be used to endorse or promote products derived from this * may be used to endorse or promote products derived from this
* software without specific prior written permission. * software without specific prior written permission.
* *
@ -72,6 +72,7 @@ Q_SIGNALS:
private Q_SLOTS: private Q_SLOTS:
void onStorageAdded(BooksStorage aStorage); void onStorageAdded(BooksStorage aStorage);
void onStorageRemoved(BooksStorage aStorage); void onStorageRemoved(BooksStorage aStorage);
void onStorageReplaced(BooksStorage aOldStorage, BooksStorage aNewStorage);
private: private:
bool validIndex(int aIndex) const; bool validIndex(int aIndex) const;

View file

@ -171,7 +171,6 @@ bool ZLibrary::init(int& aArgc, char** &aArgv)
} }
HDEBUG("screen" << booksPPI << "dpi"); HDEBUG("screen" << booksPPI << "dpi");
BooksStorageManager::instance();
ZLQtTimeManager::createInstance(); ZLQtTimeManager::createInstance();
ZLQtFSManager::createInstance(); ZLQtFSManager::createInstance();
BooksDialogManager::createInstance(); BooksDialogManager::createInstance();
@ -179,6 +178,13 @@ bool ZLibrary::init(int& aArgc, char** &aArgv)
ZLEncodingCollection::Instance().registerProvider(new IConvEncodingConverterProvider()); ZLEncodingCollection::Instance().registerProvider(new IConvEncodingConverterProvider());
ZLApplication::Instance(); ZLApplication::Instance();
ZLFile::initCache(); ZLFile::initCache();
// Due to the weird inter-dependency between BooksSettings and
// BooksStorageManager, BooksSettings has to be created first.
// Doing it the other way around will result in two instances of
// BooksStorageManager being created :)
QSharedPointer<BooksSettings> settings = BooksSettings::sharedInstance();
BooksStorageManager::instance();
return true; return true;
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015 Jolla Ltd. * Copyright (C) 2015-2016 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com> * Contact: Slava Monich <slava.monich@jolla.com>
* *
* You may use this file under the terms of the BSD license as follows: * You may use this file under the terms of the BSD license as follows:
@ -14,7 +14,7 @@
* notice, this list of conditions and the following disclaimer in * notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the * the documentation and/or other materials provided with the
* distribution. * distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors * * Neither the name of Jolla Ltd nor the names of its contributors
* may be used to endorse or promote products derived from this * may be used to endorse or promote products derived from this
* software without specific prior written permission. * software without specific prior written permission.
* *
@ -37,7 +37,6 @@
#include "BooksBookModel.h" #include "BooksBookModel.h"
#include "BooksCoverModel.h" #include "BooksCoverModel.h"
#include "BooksConfig.h" #include "BooksConfig.h"
#include "BooksSettings.h"
#include "BooksImportModel.h" #include "BooksImportModel.h"
#include "BooksPathModel.h" #include "BooksPathModel.h"
#include "BooksStorageModel.h" #include "BooksStorageModel.h"
@ -79,7 +78,6 @@ Q_DECL_EXPORT int main(int argc, char **argv)
BOOKS_QML_REGISTER(BooksPageWidget, "PageWidget"); BOOKS_QML_REGISTER(BooksPageWidget, "PageWidget");
BOOKS_QML_REGISTER(BooksListWatcher, "ListWatcher"); BOOKS_QML_REGISTER(BooksListWatcher, "ListWatcher");
BOOKS_QML_REGISTER(BooksCoverWidget, "BookCover"); BOOKS_QML_REGISTER(BooksCoverWidget, "BookCover");
BOOKS_QML_REGISTER(BooksSettings, "BooksSettings");
BOOKS_QML_REGISTER(BooksHints, "BooksHints"); BOOKS_QML_REGISTER(BooksHints, "BooksHints");
HarbourLib::registerTypes(BOOKS_QML_PLUGIN, HarbourLib::registerTypes(BOOKS_QML_PLUGIN,
BOOKS_QML_PLUGIN_V1, BOOKS_QML_PLUGIN_V2); BOOKS_QML_PLUGIN_V1, BOOKS_QML_PLUGIN_V2);