blob: 433e806cb7784b8b9e652ad9c2aecdc53b3d4a35 [file] [log] [blame]
// 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/message_loop.h"
#include "base/task.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "content/browser/device_orientation/orientation.h"
#include "content/browser/device_orientation/provider_impl.h"
namespace device_orientation {
ProviderImpl::ProviderImpl(const DataFetcherFactory factories[])
: creator_loop_(MessageLoop::current()),
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_);
// TODO(hans): Don't join the thread. See crbug.com/72286.
base::ThreadRestrictions::ScopedAllowIO allow_io;
polling_thread_.reset();
data_fetcher_.reset();
}
void ProviderImpl::DoInitializePollingThread(
const 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(const 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(const 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());
// TODO(erg): There used to be unused code here, that called a default
// implementation on the DataFetcherInterface that was never defined. I'm
// removing unused methods from headers.
return kDesiredSamplingIntervalMs;
}
} // namespace device_orientation