Commit ddd1844a authored by hans@chromium.org's avatar hans@chromium.org

Implement device_orientation::Provider.

Provider provides its registered observers with device orientation data
by finding and polling a DataFetcher on a background thread.

BUG=44654
TEST=unit_tests --gtest_filter="DeviceOrientationProviderTest.*"

Review URL: http://codereview.chromium.org/3136008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57036 0039d316-1c4b-4281-b951-d872f2087c98
parent c9f57d35
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_H_
#define CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_H_
namespace device_orientation {
class Orientation;
class DataFetcher {
public:
virtual ~DataFetcher() {}
virtual bool GetOrientation(Orientation*) = 0;
virtual int MinSamplingIntervalMs() const { return 0; }
};
} // namespace device_orientation
#endif // CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_H_
...@@ -34,6 +34,12 @@ class Orientation { ...@@ -34,6 +34,12 @@ class Orientation {
can_provide_gamma_(false) { can_provide_gamma_(false) {
} }
static Orientation Empty() { return Orientation(); }
bool IsEmpty() {
return !can_provide_alpha_ && !can_provide_beta_ && !can_provide_gamma_;
}
double alpha_; double alpha_;
double beta_; double beta_;
double gamma_; double gamma_;
......
...@@ -4,13 +4,20 @@ ...@@ -4,13 +4,20 @@
#include "chrome/browser/device_orientation/provider.h" #include "chrome/browser/device_orientation/provider.h"
#include "base/logging.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/device_orientation/data_fetcher.h"
#include "chrome/browser/device_orientation/provider_impl.h"
namespace device_orientation { namespace device_orientation {
Provider* Provider::GetInstance() { Provider* Provider::GetInstance() {
if (!instance_) if (!instance_) {
// TODO(hans) This is not finished. We will create an instance of the real DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
// Provider implementation once it is implemented. const ProviderImpl::DataFetcherFactory default_factories[] = { NULL };
instance_ = new Provider();
instance_ = new ProviderImpl(MessageLoop::current(), default_factories);
}
return instance_; return instance_;
} }
......
...@@ -12,7 +12,7 @@ namespace device_orientation { ...@@ -12,7 +12,7 @@ namespace device_orientation {
class Orientation; class Orientation;
class Provider : public base::RefCounted<Provider> { class Provider : public base::RefCountedThreadSafe<Provider> {
public: public:
class Observer { class Observer {
public: public:
...@@ -33,8 +33,8 @@ class Provider : public base::RefCounted<Provider> { ...@@ -33,8 +33,8 @@ class Provider : public base::RefCounted<Provider> {
// injected object's reference count. // injected object's reference count.
static void SetInstanceForTests(Provider* provider); static void SetInstanceForTests(Provider* provider);
virtual void AddObserver(Observer* observer) {} virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(Observer* observer) {} virtual void RemoveObserver(Observer* observer) = 0;
protected: protected:
Provider() {} Provider() {}
...@@ -44,7 +44,7 @@ class Provider : public base::RefCounted<Provider> { ...@@ -44,7 +44,7 @@ class Provider : public base::RefCounted<Provider> {
} }
private: private:
friend class base::RefCounted<Provider>; friend class base::RefCountedThreadSafe<Provider>;
static Provider* instance_; static Provider* instance_;
DISALLOW_COPY_AND_ASSIGN(Provider); DISALLOW_COPY_AND_ASSIGN(Provider);
......
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cmath>
#include <set>
#include <vector>
#include "base/logging.h"
#include "base/task.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/device_orientation/orientation.h"
#include "chrome/browser/device_orientation/provider_impl.h"
namespace device_orientation {
ProviderImpl::ProviderImpl(MessageLoop* message_loop,
const DataFetcherFactory factories[])
: creator_loop_(message_loop),
ALLOW_THIS_IN_INITIALIZER_LIST(do_poll_method_factory_(this)) {
for (const DataFetcherFactory* fp = factories; *fp; ++fp)
factories_.push_back(*fp);
}
ProviderImpl::~ProviderImpl() {
}
void ProviderImpl::AddObserver(Observer* observer) {
DCHECK(MessageLoop::current() == creator_loop_);
observers_.insert(observer);
if (observers_.size() == 1)
Start();
else
observer->OnOrientationUpdate(last_notification_);
}
void ProviderImpl::RemoveObserver(Observer* observer) {
DCHECK(MessageLoop::current() == creator_loop_);
observers_.erase(observer);
if (observers_.empty())
Stop();
}
void ProviderImpl::Start() {
DCHECK(MessageLoop::current() == creator_loop_);
DCHECK(!polling_thread_.get());
polling_thread_.reset(new base::Thread("Device orientation polling thread"));
if (!polling_thread_->Start()) {
LOG(ERROR) << "Failed to start device orientation polling thread";
polling_thread_.reset();
return;
}
ScheduleInitializePollingThread();
}
void ProviderImpl::Stop() {
DCHECK(MessageLoop::current() == creator_loop_);
polling_thread_.reset();
data_fetcher_.reset();
}
void ProviderImpl::DoInitializePollingThread(
std::vector<DataFetcherFactory> factories) {
DCHECK(MessageLoop::current() == polling_thread_->message_loop());
typedef std::vector<DataFetcherFactory>::const_iterator Iterator;
for (Iterator i = factories_.begin(), e = factories_.end(); i != e; ++i) {
DataFetcherFactory factory = *i;
scoped_ptr<DataFetcher> fetcher(factory());
Orientation orientation;
if (fetcher.get() && fetcher->GetOrientation(&orientation)) {
// Pass ownership of fetcher to provider_.
data_fetcher_.swap(fetcher);
last_orientation_ = orientation;
// Notify observers.
ScheduleDoNotify(orientation);
// Start polling.
ScheduleDoPoll();
return;
}
}
// When no orientation data can be provided.
ScheduleDoNotify(Orientation::Empty());
}
void ProviderImpl::ScheduleInitializePollingThread() {
DCHECK(MessageLoop::current() == creator_loop_);
Task* task = NewRunnableMethod(this,
&ProviderImpl::DoInitializePollingThread,
factories_);
MessageLoop* polling_loop = polling_thread_->message_loop();
polling_loop->PostTask(FROM_HERE, task);
}
void ProviderImpl::DoNotify(Orientation orientation) {
DCHECK(MessageLoop::current() == creator_loop_);
last_notification_ = orientation;
typedef std::set<Observer*>::const_iterator Iterator;
for (Iterator i = observers_.begin(), e = observers_.end(); i != e; ++i)
(*i)->OnOrientationUpdate(orientation);
if (orientation.IsEmpty()) {
// Notify observers about failure to provide data exactly once.
observers_.clear();
Stop();
}
}
void ProviderImpl::ScheduleDoNotify(Orientation orientation) {
DCHECK(MessageLoop::current() == polling_thread_->message_loop());
Task* task = NewRunnableMethod(this, &ProviderImpl::DoNotify, orientation);
creator_loop_->PostTask(FROM_HERE, task);
}
void ProviderImpl::DoPoll() {
DCHECK(MessageLoop::current() == polling_thread_->message_loop());
Orientation orientation;
if (!data_fetcher_->GetOrientation(&orientation)) {
LOG(ERROR) << "Failed to poll device orientation data fetcher.";
ScheduleDoNotify(Orientation::Empty());
return;
}
if (SignificantlyDifferent(orientation, last_orientation_)) {
last_orientation_ = orientation;
ScheduleDoNotify(orientation);
}
ScheduleDoPoll();
}
void ProviderImpl::ScheduleDoPoll() {
DCHECK(MessageLoop::current() == polling_thread_->message_loop());
Task* task = do_poll_method_factory_.NewRunnableMethod(&ProviderImpl::DoPoll);
MessageLoop* polling_loop = polling_thread_->message_loop();
polling_loop->PostDelayedTask(FROM_HERE, task, SamplingIntervalMs());
}
namespace {
bool IsElementSignificantlyDifferent(bool can_provide_element1,
bool can_provide_element2,
double element1,
double element2) {
const double kThreshold = 0.1;
if (can_provide_element1 != can_provide_element2)
return true;
if (can_provide_element1 &&
std::fabs(element1 - element2) >= kThreshold)
return true;
return false;
}
} // namespace
// Returns true if two orientations are considered different enough that
// observers should be notified of the new orientation.
bool ProviderImpl::SignificantlyDifferent(const Orientation& o1,
const Orientation& o2) {
return IsElementSignificantlyDifferent(o1.can_provide_alpha_,
o2.can_provide_alpha_,
o1.alpha_,
o2.alpha_) ||
IsElementSignificantlyDifferent(o1.can_provide_beta_,
o2.can_provide_beta_,
o1.beta_,
o2.beta_) ||
IsElementSignificantlyDifferent(o1.can_provide_gamma_,
o2.can_provide_gamma_,
o1.gamma_,
o2.gamma_);
}
int ProviderImpl::SamplingIntervalMs() const {
DCHECK(MessageLoop::current() == polling_thread_->message_loop());
DCHECK(data_fetcher_.get());
int fetcher_interval = data_fetcher_->MinSamplingIntervalMs();
if (fetcher_interval > kDesiredSamplingIntervalMs)
return fetcher_interval;
else
return kDesiredSamplingIntervalMs;
}
} // namespace device_orientation
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DEVICE_ORIENTATION_PROVIDER_IMPL_H_
#define CHROME_BROWSER_DEVICE_ORIENTATION_PROVIDER_IMPL_H_
#include <set>
#include <vector>
#include "base/scoped_ptr.h"
#include "base/task.h"
#include "chrome/browser/device_orientation/data_fetcher.h"
#include "chrome/browser/device_orientation/orientation.h"
#include "chrome/browser/device_orientation/provider.h"
class MessageLoop;
namespace base {
class Thread;
}
namespace device_orientation {
class ProviderImpl : public Provider {
public:
typedef DataFetcher* (*DataFetcherFactory)();
// Create a ProviderImpl that expects calls to AddObserver and RemoveObserver
// on message_loop, sends notifications to observers on message_loop,
// and uses the NULL-terminated factories array to find a DataFetcher
// that can provide orientation data.
ProviderImpl(MessageLoop* message_loop, const DataFetcherFactory factories[]);
// From Provider.
virtual void AddObserver(Observer* observer);
virtual void RemoveObserver(Observer* observer);
private:
virtual ~ProviderImpl();
// Starts or Stops the provider. Called from creator_loop_.
void Start();
void Stop();
// Method for finding a suitable DataFetcher and starting the polling.
// Runs on the polling_thread_.
void DoInitializePollingThread(std::vector<DataFetcherFactory> factories);
void ScheduleInitializePollingThread();
// Method for polling a DataFetcher. Runs on the polling_thread_.
void DoPoll();
void ScheduleDoPoll();
// Method for notifying observers of an orientation update.
// Runs on the creator_thread_.
void DoNotify(Orientation orientation);
void ScheduleDoNotify(Orientation orientation);
static bool SignificantlyDifferent(const Orientation& orientation1,
const Orientation& orientation2);
enum { kDesiredSamplingIntervalMs = 100 };
int SamplingIntervalMs() const;
// The Message Loop on which this object was created.
// Typically the I/O loop, but may be something else during testing.
MessageLoop* creator_loop_;
// Members below are only to be used from the creator_loop_.
std::vector<DataFetcherFactory> factories_;
std::set<Observer*> observers_;
Orientation last_notification_;
// When polling_thread_ is running, members below are only to be used
// from that thread.
scoped_ptr<DataFetcher> data_fetcher_;
Orientation last_orientation_;
ScopedRunnableMethodFactory<ProviderImpl> do_poll_method_factory_;
// Polling is done on this background thread.
scoped_ptr<base::Thread> polling_thread_;
};
} // namespace device_orientation
#endif // CHROME_BROWSER_DEVICE_ORIENTATION_PROVIDER_IMPL_H_
This diff is collapsed.
...@@ -1118,11 +1118,14 @@ ...@@ -1118,11 +1118,14 @@
'browser/default_encoding_combo_model.h', 'browser/default_encoding_combo_model.h',
'browser/defaults.cc', 'browser/defaults.cc',
'browser/defaults.h', 'browser/defaults.h',
'browser/device_orientation/data_fetcher.h',
'browser/device_orientation/dispatcher_host.cc', 'browser/device_orientation/dispatcher_host.cc',
'browser/device_orientation/dispatcher_host.h', 'browser/device_orientation/dispatcher_host.h',
'browser/device_orientation/orientation.h', 'browser/device_orientation/orientation.h',
'browser/device_orientation/provider.cc', 'browser/device_orientation/provider.cc',
'browser/device_orientation/provider.h', 'browser/device_orientation/provider.h',
'browser/device_orientation/provider_impl.cc',
'browser/device_orientation/provider_impl.h',
'browser/diagnostics/diagnostics_main.cc', 'browser/diagnostics/diagnostics_main.cc',
'browser/diagnostics/diagnostics_main.h', 'browser/diagnostics/diagnostics_main.h',
'browser/diagnostics/diagnostics_model.h', 'browser/diagnostics/diagnostics_model.h',
......
...@@ -1033,6 +1033,7 @@ ...@@ -1033,6 +1033,7 @@
'browser/command_updater_unittest.cc', 'browser/command_updater_unittest.cc',
'browser/cookies_tree_model_unittest.cc', 'browser/cookies_tree_model_unittest.cc',
'browser/debugger/devtools_manager_unittest.cc', 'browser/debugger/devtools_manager_unittest.cc',
'browser/device_orientation/provider_unittest.cc',
'browser/dock_info_unittest.cc', 'browser/dock_info_unittest.cc',
'browser/dom_ui/dom_ui_theme_source_unittest.cc', 'browser/dom_ui/dom_ui_theme_source_unittest.cc',
'browser/dom_ui/dom_ui_unittest.cc', 'browser/dom_ui/dom_ui_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment