Added the ability to control playback volume from post capture
[harmattan/cameraplus] / src / cameraresources.cpp
index ef37f0f..cbaed5c 100644 (file)
@@ -1,7 +1,7 @@
 /*!
  * This file is part of CameraPlus.
  *
- * Copyright (C) 2012 Mohammed Sameer <msameer@foolab.org>
+ * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  */
 
 #include "cameraresources.h"
+#if defined(QT4)
+#include <dbusconnectioneventloop.h>
+#endif
 #include <QDebug>
 
-using namespace ResourcePolicy;
+#define APPLICATION_CLASS "camera"
 
 CameraResources::CameraResources(QObject *parent) :
   QObject(parent),
-  m_set(new ResourceSet("camera", this, true, true)),
-  m_mode(None), m_acquired(false) {
+  m_worker(new CameraResourcesWorker) {
+
+  m_worker->moveToThread(&m_thread);
+
+#if defined(QT4)
+  DBUSConnectionEventLoop::getInstance().moveToThread(&m_thread);
+#endif
+
+  QObject::connect(&m_thread, SIGNAL(started()), m_worker, SLOT(init()));
+  m_thread.start();
+
+  qRegisterMetaType<CameraResources::Mode>("CameraResources::Mode");
+  qRegisterMetaType<bool *>("bool *");
+
+  QObject::connect(m_worker, SIGNAL(acquiredChanged()), this, SIGNAL(acquiredChanged()));
+  QObject::connect(m_worker, SIGNAL(hijackedChanged()), this, SIGNAL(hijackedChanged()));
+  QObject::connect(m_worker, SIGNAL(updated()), this, SIGNAL(updated()));
+  QObject::connect(m_worker, SIGNAL(acquiredChanged()), this, SIGNAL(scaleAcquisitionChanged()));
+  QObject::connect(m_worker, SIGNAL(hijackedChanged()), this, SIGNAL(scaleAcquisitionChanged()));
+  QObject::connect(m_worker, SIGNAL(updated()), this, SIGNAL(scaleAcquisitionChanged()));
+}
+
+CameraResources::~CameraResources() {
+  acquire(CameraResources::None);
+  m_thread.exit(0);
+
+  while (m_thread.isRunning()) {
+    m_thread.wait(10);
+  }
+
+  delete m_worker;
+  m_worker = 0;
+}
+
+bool CameraResources::isResourceGranted(const ResourcePolicy::ResourceType& resource) const {
+  bool ok = false;
+
+  QMetaObject::invokeMethod(m_worker, "isResourceGranted", Qt::BlockingQueuedConnection,
+                           Q_ARG(bool *, &ok), Q_ARG(int, resource));
+
+  return ok;
+}
+
+bool CameraResources::acquire(const Mode& mode) {
+  bool ok = false;
+
+  QMetaObject::invokeMethod(m_worker, "acquire", Qt::BlockingQueuedConnection,
+                           Q_ARG(bool *, &ok), Q_ARG(CameraResources::Mode, mode));
+
+  return ok;
+}
+
+bool CameraResources::acquired() const {
+  bool ok = false;
+
+  QMetaObject::invokeMethod(m_worker, "acquired", Qt::BlockingQueuedConnection,
+                           Q_ARG(bool *, &ok));
+
+  return ok;
+}
+
+bool CameraResources::hijacked() const {
+  bool ok = false;
+
+  QMetaObject::invokeMethod(m_worker, "hijacked", Qt::BlockingQueuedConnection,
+                           Q_ARG(bool *, &ok));
+
+  return ok;
+}
+
+bool CameraResources::isScaleAcquired() const {
+  return isResourceGranted(ResourcePolicy::ScaleButtonType);
+}
+
+CameraResourcesWorker::CameraResourcesWorker(QObject *parent) :
+  QObject(parent),
+  m_set(0),
+  m_mode(CameraResources::None),
+  m_acquired(false),
+  m_acquiring(false),
+  m_hijacked(false) {
+
+}
+
+CameraResourcesWorker::~CameraResourcesWorker() {
+
+}
+
+void CameraResourcesWorker::init() {
+  m_set = new ResourcePolicy::ResourceSet(APPLICATION_CLASS, this);
+  m_set->setAlwaysReply();
 
   QObject::connect(m_set, SIGNAL(resourcesReleased()), this, SLOT(resourcesReleased()));
   QObject::connect(m_set, SIGNAL(lostResources()), this, SLOT(lostResources()));
   QObject::connect(m_set, SIGNAL(resourcesGranted(const QList<ResourcePolicy::ResourceType>&)),
                   this, SLOT(resourcesGranted(const QList<ResourcePolicy::ResourceType>&)));
-  QObject::connect(m_set, SIGNAL(updateOK()), this, SLOT(updateOK()));
+  QObject::connect(m_set, SIGNAL(resourcesDenied()), this, SLOT(resourcesDenied()));
 
   if (!m_set->initAndConnect()) {
     qCritical() << "Failed to connect to resource policy engine";
   }
 }
 
-CameraResources::~CameraResources() {
-  acquire(None);
+void CameraResourcesWorker::resourcesReleased() {
+  setHijacked(false);
+  setAcquired(false);
+
+  m_acquiring = false;
+}
+
+void CameraResourcesWorker::lostResources() {
+  setAcquired(false);
+  setHijacked(true);
+
+  m_acquiring = false;
+}
+
+void CameraResourcesWorker::resourcesGranted(const QList<ResourcePolicy::ResourceType>& optional) {
+  Q_UNUSED(optional);
+
+  if (!m_acquiring) {
+    // This can happen when:
+    // 1) We lose/gain optional resources.
+    // 2) A higher priority application releases resources back.
+    emit updated();
+  }
+
+  m_acquiring = false;
+
+  setAcquired(true);
+  setHijacked(false);
+}
+
+void CameraResourcesWorker::resourcesDenied() {
+  setAcquired(false);
+  setHijacked(true);
+
+  m_acquiring = false;
 }
 
-void CameraResources::acquire(const CameraResources::Mode& mode) {
+QList<ResourcePolicy::ResourceType> CameraResourcesWorker::listSet() {
+  QList<ResourcePolicy::Resource *> resources = m_set->resources();
+
+  QList<ResourcePolicy::ResourceType> set;
+
+  foreach (ResourcePolicy::Resource *r, resources) {
+    set << r->type();
+  }
+
+  return set;
+}
+
+void CameraResourcesWorker::acquire(bool *ok, const CameraResources::Mode& mode) {
   if (mode == m_mode) {
-    // We need to emit this because the UI migh be waiting
-    emit acquiredChanged();
+    *ok = true;
     return;
   }
 
   m_mode = mode;
 
+  *ok = false;
+
   switch (m_mode) {
-  case None:
-    m_set->release();
+  case CameraResources::None:
+    *ok = release();
     break;
 
-  case Image:
-    updateSet(QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::VideoPlaybackType
-             << ResourcePolicy::VideoRecorderType
-             << ResourcePolicy::ScaleButtonType
-             << ResourcePolicy::SnapButtonType);
+  case CameraResources::Image:
+    *ok = updateSet(QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::VideoPlaybackType
+                   << ResourcePolicy::VideoRecorderType,
+                   QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::ScaleButtonType);
     break;
 
-  case Video:
-    updateSet(QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::VideoPlaybackType
-             << ResourcePolicy::VideoRecorderType
-             << ResourcePolicy::ScaleButtonType
-             << ResourcePolicy::SnapButtonType);
+  case CameraResources::Video:
+    *ok = updateSet(QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::VideoPlaybackType
+                   << ResourcePolicy::VideoRecorderType,
+                   QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::ScaleButtonType);
     break;
 
-  case Recording:
-    updateSet(QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::VideoPlaybackType
-             << ResourcePolicy::VideoRecorderType
-             << ResourcePolicy::ScaleButtonType
-             << ResourcePolicy::SnapButtonType
-             << ResourcePolicy::AudioRecorderType,
-             QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::AudioPlaybackType);
+  case CameraResources::Recording:
+    *ok = updateSet(QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::VideoPlaybackType
+                   << ResourcePolicy::VideoRecorderType,
+                   QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::ScaleButtonType
+                   << ResourcePolicy::AudioRecorderType
+                   << ResourcePolicy::AudioPlaybackType);
     break;
 
-  case PostCapture:
-    updateSet(QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::VideoPlaybackType
-             << ResourcePolicy::ScaleButtonType,
-             QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::AudioPlaybackType);
-
+  case CameraResources::Player:
+    *ok = updateSet(QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::VideoPlaybackType
+                   << ResourcePolicy::AudioPlaybackType,
+                   QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::ScaleButtonType);
     break;
 
   default:
     qWarning() << "Unknown mode" << mode;
 
-    break;
+    *ok = false;
   }
 }
 
-bool CameraResources::acquired() const {
-  return m_acquired;
+void CameraResourcesWorker::acquired(bool *ok) {
+  *ok = m_acquired;
 }
 
-void CameraResources::resourcesReleased() {
-  m_mode = None;
-  m_acquired = false;
-  emit acquiredChanged();
+void CameraResourcesWorker::hijacked(bool *ok) {
+  *ok = m_hijacked;
 }
 
-void CameraResources::lostResources() {
-  m_mode = None;
-  m_acquired = false;
-  emit acquiredChanged();
-}
+bool CameraResourcesWorker::updateSet(const QList<ResourcePolicy::ResourceType>& required,
+                                     const QList<ResourcePolicy::ResourceType>& optional) {
 
-void CameraResources::resourcesGranted(const QList<ResourcePolicy::ResourceType>& optional) {
-  Q_UNUSED(optional);
 
-  m_acquired = true;
-  emit acquiredChanged();
-}
+  QList<ResourcePolicy::ResourceType> set = listSet();
 
-void CameraResources::updateOK() {
-  m_acquired = true;
-  emit acquiredChanged();
-}
+  foreach (ResourcePolicy::ResourceType r, set) {
+    if (required.indexOf(r) != -1) {
+      m_set->resource(r)->setOptional(false);
+    }
+    else if (optional.indexOf(r) != -1) {
+      m_set->resource(r)->setOptional(true);
+    }
+    else {
+      m_set->deleteResource(r);
+    }
+  }
 
-QList<ResourcePolicy::ResourceType> CameraResources::listSet() {
-  QList<Resource *> resources = m_set->resources();
-  QList<ResourcePolicy::ResourceType> set;
+  foreach (ResourcePolicy::ResourceType r, required) {
+    m_set->addResource(r);
+  }
 
-  foreach (Resource *r, resources) {
-    set << r->type();
+  foreach (ResourcePolicy::ResourceType r, optional) {
+    m_set->addResource(r);
+    ResourcePolicy::Resource *res = m_set->resource(r);
+    if (res) {
+      res->setOptional(true);
+    }
   }
 
-  return set;
+  if (m_set->contains(ResourcePolicy::AudioPlaybackType)) {
+    bool isOptional = m_set->resource(ResourcePolicy::AudioPlaybackType)->isOptional();
+
+    ResourcePolicy::AudioResource *audio = new ResourcePolicy::AudioResource(APPLICATION_CLASS);
+    audio->setProcessID(QCoreApplication::applicationPid());
+    audio->setStreamTag("media.name", "*");
+    audio->setOptional(isOptional);
+    m_set->addResourceObject(audio);
+  }
+
+  m_acquiring = true;
+
+  m_set->update();
+  m_set->acquire();
+
+  while (m_acquiring) {
+    QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+  }
+
+  return m_acquired;
 }
 
-void CameraResources::updateSet(const QList<ResourcePolicy::ResourceType>& required,
-                               const QList<ResourcePolicy::ResourceType>& optional) {
+bool CameraResourcesWorker::release() {
+  m_acquiring = true;
 
-  Q_UNUSED(optional);
+  m_set->release();
 
-  bool isEmpty = m_set->resources().isEmpty();
+  while (m_acquiring) {
+    QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+  }
 
-  QList<ResourcePolicy::ResourceType> set = listSet();
+  m_mode = CameraResources::None;
 
-  foreach (ResourceType r, set) {
-    // Check for acquired resources that should be dropped.
-    if (required.indexOf(r) == -1) {
-      m_set->deleteResource(r);
-    }
+  return true;
+}
+
+void CameraResourcesWorker::setAcquired(bool acquired) {
+  if (m_acquired != acquired) {
+    m_acquired = acquired;
+    emit acquiredChanged();
   }
+}
 
-  foreach (ResourceType r, required) {
-    // TODO: AudioPlayback needs special handling
-    m_set->addResource(r);
+void CameraResourcesWorker::setHijacked(bool hijacked) {
+  if (m_hijacked != hijacked) {
+    m_hijacked = hijacked;
+    emit hijackedChanged();
   }
+}
 
-  // TODO: optional resources
+void CameraResourcesWorker::isResourceGranted(bool *ok, int resource) {
 
-  // Odd. If we don't do it that way then policy ignores our requests
-  // when we get minimized then maximized.
-  if (isEmpty) {
-    m_set->update();
-  }
-  else {
-    m_set->acquire();
+  ResourcePolicy::ResourceType rt = (ResourcePolicy::ResourceType)resource;
+
+  ResourcePolicy::Resource *r = m_set->resource(rt);
+  if (r) {
+    *ok = r->isGranted();
   }
 }