Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1123)

Side by Side Diff: content/browser/device_orientation/provider_impl.cc

Issue 10755002: Refactors DeviceOrientation to make it more extensible (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Handles null device_data in observer Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <cmath>
6 #include <set> 5 #include <set>
7 #include <vector> 6 #include <vector>
8 7
9 #include "base/bind.h" 8 #include "base/bind.h"
10 #include "base/logging.h" 9 #include "base/logging.h"
11 #include "base/message_loop.h" 10 #include "base/message_loop.h"
12 #include "base/threading/thread.h" 11 #include "base/threading/thread.h"
13 #include "base/threading/thread_restrictions.h" 12 #include "base/threading/thread_restrictions.h"
14 #include "content/browser/device_orientation/orientation.h"
15 #include "content/browser/device_orientation/provider_impl.h" 13 #include "content/browser/device_orientation/provider_impl.h"
16 14
17 namespace device_orientation { 15 namespace device_orientation {
18 16
19 ProviderImpl::ProviderImpl(const DataFetcherFactory factories[]) 17 ProviderImpl::ProviderImpl(const DataFetcherFactory factories[])
20 : creator_loop_(MessageLoop::current()), 18 : creator_loop_(MessageLoop::current()),
21 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { 19 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
22 for (const DataFetcherFactory* fp = factories; *fp; ++fp) 20 for (const DataFetcherFactory* fp = factories; *fp; ++fp)
23 factories_.push_back(*fp); 21 factories_.push_back(*fp);
24 } 22 }
25 23
26 ProviderImpl::~ProviderImpl() { 24 ProviderImpl::~ProviderImpl() {
27 } 25 }
28 26
29 void ProviderImpl::AddObserver(Observer* observer) { 27 void ProviderImpl::AddObserver(Observer* observer) {
30 DCHECK(MessageLoop::current() == creator_loop_); 28 DCHECK(MessageLoop::current() == creator_loop_);
31 29
30 DeviceData::Type device_data_type = observer->device_data_type();
hans 2012/07/18 17:21:02 maybe move this line down until when device_data_t
aousterh 2012/07/20 11:18:52 Done.
31
32 observers_.insert(observer); 32 observers_.insert(observer);
33 if (observers_.size() == 1) 33 if (observers_.size() == 1)
34 Start(); 34 Start();
35 else 35
36 observer->OnOrientationUpdate(last_notification_); 36 ScheduleDoAddObserverType(device_data_type);
37
38 if (!data_fetcher_.get()) {
hans 2012/07/18 17:21:02 just to double check: the idea is that there would
hans 2012/07/18 17:21:02 i'm not sure why this is being moved out of Start(
aousterh 2012/07/20 11:18:52 I was thinking that we could end up with observers
aousterh 2012/07/20 11:18:52 Correct. For now there are multiple data fetchers
39 ScheduleInitializePollingThread(device_data_type);
hans 2012/07/18 17:21:02 hmm, it makes me worried that we have to pass in d
aousterh 2012/07/20 11:18:52 The scenario I am trying to handle is that first a
40 return;
41 }
42
43 // Notify observer of most recent notification if one exists.
44 DeviceData* last_notification = last_notifications_map_.Lookup(
45 device_data_type);
46 if (last_notification)
hans 2012/07/18 17:21:02 if it fits on one line, it would be nice to write
aousterh 2012/07/20 11:18:52 This no longer applies since I switched to using a
47 observer->OnDeviceDataUpdate(last_notification, device_data_type);
37 } 48 }
38 49
39 void ProviderImpl::RemoveObserver(Observer* observer) { 50 void ProviderImpl::RemoveObserver(Observer* observer) {
40 DCHECK(MessageLoop::current() == creator_loop_); 51 DCHECK(MessageLoop::current() == creator_loop_);
41 52
42 observers_.erase(observer); 53 observers_.erase(observer);
43 if (observers_.empty()) 54 if (observers_.empty())
44 Stop(); 55 Stop();
45 } 56 }
46 57
47 void ProviderImpl::Start() { 58 void ProviderImpl::Start() {
48 DCHECK(MessageLoop::current() == creator_loop_); 59 DCHECK(MessageLoop::current() == creator_loop_);
49 DCHECK(!polling_thread_.get()); 60 DCHECK(!polling_thread_.get());
50 61
51 polling_thread_.reset(new base::Thread("Device orientation polling thread")); 62 // Reset members used on polling thread.
63 last_device_data_map_.reset(new IDMap<DeviceData, IDMapOwnPointer>);
64 observer_types_.clear();
65
66 polling_thread_.reset(new base::Thread("Device data polling thread"));
52 if (!polling_thread_->Start()) { 67 if (!polling_thread_->Start()) {
53 LOG(ERROR) << "Failed to start device orientation polling thread"; 68 LOG(ERROR) << "Failed to start device data polling thread";
54 polling_thread_.reset(); 69 polling_thread_.reset();
55 return; 70 return;
56 } 71 }
57 ScheduleInitializePollingThread();
58 } 72 }
59 73
60 void ProviderImpl::Stop() { 74 void ProviderImpl::Stop() {
61 DCHECK(MessageLoop::current() == creator_loop_); 75 DCHECK(MessageLoop::current() == creator_loop_);
62 76
63 // TODO(hans): Don't join the thread. See crbug.com/72286. 77 // TODO(hans): Don't join the thread. See crbug.com/72286.
64 base::ThreadRestrictions::ScopedAllowIO allow_io; 78 base::ThreadRestrictions::ScopedAllowIO allow_io;
65 79
66 polling_thread_.reset(); 80 polling_thread_.reset();
67 data_fetcher_.reset(); 81 data_fetcher_.reset();
68 } 82 }
69 83
84 void ProviderImpl::DoAddObserverType(DeviceData::Type device_data_type) {
85 DCHECK(MessageLoop::current() == polling_thread_->message_loop());
86
87 observer_types_.insert(device_data_type);
88 }
89
90 void ProviderImpl::ScheduleDoAddObserverType(
91 DeviceData::Type device_data_type) {
hans 2012/07/18 17:21:02 would it fit on one line if the parameter was just
aousterh 2012/07/20 11:18:52 Done.
92 DCHECK(MessageLoop::current() == creator_loop_);
93
94 MessageLoop* polling_loop = polling_thread_->message_loop();
95 polling_loop->PostTask(FROM_HERE,
96 base::Bind(&ProviderImpl::DoAddObserverType,
97 this,
98 device_data_type));
99 }
100
70 void ProviderImpl::DoInitializePollingThread( 101 void ProviderImpl::DoInitializePollingThread(
71 const std::vector<DataFetcherFactory>& factories) { 102 const std::vector<DataFetcherFactory>& factories,
103 DeviceData::Type device_data_type) {
72 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); 104 DCHECK(MessageLoop::current() == polling_thread_->message_loop());
73 105
74 typedef std::vector<DataFetcherFactory>::const_iterator Iterator; 106 typedef std::vector<DataFetcherFactory>::const_iterator Iterator;
75 for (Iterator i = factories_.begin(), e = factories_.end(); i != e; ++i) { 107 for (Iterator i = factories_.begin(), e = factories_.end(); i != e; ++i) {
76 DataFetcherFactory factory = *i; 108 DataFetcherFactory factory = *i;
77 scoped_ptr<DataFetcher> fetcher(factory()); 109 scoped_ptr<DataFetcher> fetcher(factory());
78 Orientation orientation;
79 110
80 if (fetcher.get() && fetcher->GetOrientation(&orientation)) { 111 if (!fetcher.get())
112 continue;
113
114 scoped_ptr<DeviceData> device_data(fetcher->GetDeviceData(
115 device_data_type));
116 if (device_data != NULL) {
81 // Pass ownership of fetcher to provider_. 117 // Pass ownership of fetcher to provider_.
82 data_fetcher_.swap(fetcher); 118 data_fetcher_.swap(fetcher);
83 last_orientation_ = orientation; 119 last_device_data_map_->AddWithID(device_data->Clone(), device_data_type);
84 120
85 // Notify observers. 121 // Notify observers.
86 if (!orientation.is_empty()) 122 ScheduleDoNotify(device_data.release(), device_data_type);
87 ScheduleDoNotify(orientation);
88 123
89 // Start polling. 124 // Start polling.
90 ScheduleDoPoll(); 125 ScheduleDoPoll();
91 return; 126 return;
92 } 127 }
93 } 128 }
94 129
95 // When no orientation data can be provided. 130 // When no device data can be provided.
96 ScheduleDoNotify(Orientation::Empty()); 131 ScheduleDoNotify(NULL, device_data_type);
97 } 132 }
98 133
99 void ProviderImpl::ScheduleInitializePollingThread() { 134 void ProviderImpl::ScheduleInitializePollingThread(
135 DeviceData::Type device_data_type) {
100 DCHECK(MessageLoop::current() == creator_loop_); 136 DCHECK(MessageLoop::current() == creator_loop_);
101 137
102 MessageLoop* polling_loop = polling_thread_->message_loop(); 138 MessageLoop* polling_loop = polling_thread_->message_loop();
103 polling_loop->PostTask(FROM_HERE, 139 polling_loop->PostTask(FROM_HERE,
104 base::Bind(&ProviderImpl::DoInitializePollingThread, 140 base::Bind(&ProviderImpl::DoInitializePollingThread,
105 this, 141 this,
106 factories_)); 142 factories_,
143 device_data_type));
107 } 144 }
108 145
109 void ProviderImpl::DoNotify(const Orientation& orientation) { 146 void ProviderImpl::DoNotify(const DeviceData* device_data,
147 DeviceData::Type device_data_type) {
110 DCHECK(MessageLoop::current() == creator_loop_); 148 DCHECK(MessageLoop::current() == creator_loop_);
111 149
112 last_notification_ = orientation; 150 scoped_ptr<const DeviceData> data(device_data);
113 151
114 typedef std::set<Observer*>::const_iterator Iterator; 152 // Update last notification of this type.
115 for (Iterator i = observers_.begin(), e = observers_.end(); i != e; ++i) 153 DeviceData* last_notification = last_notifications_map_.Lookup(
116 (*i)->OnOrientationUpdate(orientation); 154 device_data_type);
155 if (last_notification)
156 last_notifications_map_.Remove(device_data_type);
157 if (data != NULL)
158 last_notifications_map_.AddWithID(data->Clone(), device_data_type);
hans 2012/07/18 17:21:02 i think the if statements above would be simplifie
aousterh 2012/07/20 11:18:52 Done.
117 159
118 if (orientation.is_empty()) { 160 // Notify observers of this type of the new data.
119 // Notify observers about failure to provide data exactly once. 161 typedef std::set<Observer*>::const_iterator ConstIterator;
120 observers_.clear(); 162 for (ConstIterator i = observers_.begin(), e = observers_.end();
hans 2012/07/18 17:21:02 i think "i != observers_.end()" is much more commo
aousterh 2012/07/20 11:18:52 :-) ok, done. and i changed a couple other instanc
121 Stop(); 163 i != e; ++i) {
164 if ((*i)->device_data_type() == device_data_type)
165 (*i)->OnDeviceDataUpdate(data.get(), device_data_type);
166 }
167
168 if (data == NULL) {
169 // Notify observers exactly once about failure to provide data.
170 typedef std::set<Observer*>::iterator Iterator;
171 Iterator i = observers_.begin();
172 while (i != observers_.end()) {
173 Iterator current = i++;
174 if ((*current)->device_data_type() == device_data_type)
175 observers_.erase(current);
176 }
177
178 if (observers_.empty())
179 Stop();
122 } 180 }
123 } 181 }
124 182
125 void ProviderImpl::ScheduleDoNotify(const Orientation& orientation) { 183 void ProviderImpl::ScheduleDoNotify(const DeviceData* device_data,
184 DeviceData::Type device_data_type) {
126 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); 185 DCHECK(MessageLoop::current() == polling_thread_->message_loop());
127 186
128 creator_loop_->PostTask( 187 creator_loop_->PostTask(FROM_HERE,
129 FROM_HERE, base::Bind(&ProviderImpl::DoNotify, this, orientation)); 188 base::Bind(&ProviderImpl::DoNotify, this,
189 device_data, device_data_type));
130 } 190 }
131 191
132 void ProviderImpl::DoPoll() { 192 void ProviderImpl::DoPoll() {
133 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); 193 DCHECK(MessageLoop::current() == polling_thread_->message_loop());
134 194
135 Orientation orientation; 195 // Poll the fetcher for each type of data
hans 2012/07/18 17:21:02 end comment with period.
aousterh 2012/07/20 11:18:52 Done.
136 if (!data_fetcher_->GetOrientation(&orientation)) { 196 typedef std::set<DeviceData::Type>::const_iterator SetIterator;
137 LOG(ERROR) << "Failed to poll device orientation data fetcher."; 197 for (SetIterator i = observer_types_.begin(), e = observer_types_.end();
198 i != e; ++i) {
199 DeviceData::Type device_data_type = *i;
200 scoped_ptr<DeviceData> device_data(data_fetcher_->GetDeviceData(
201 device_data_type));
138 202
139 ScheduleDoNotify(Orientation::Empty()); 203 if (device_data == NULL) {
140 return; 204 LOG(ERROR) << "Failed to poll device data fetcher.";
141 } 205 ScheduleDoNotify(NULL, device_data_type);
206 continue;
207 }
142 208
143 if (!orientation.is_empty() && 209 // Update the last device data of this type and notify observers
144 SignificantlyDifferent(orientation, last_orientation_)) { 210 DeviceData* last_device_data = last_device_data_map_->Lookup(
145 last_orientation_ = orientation; 211 device_data_type);
146 ScheduleDoNotify(orientation); 212 if (!last_device_data) {
213 last_device_data_map_->AddWithID(device_data->Clone(), device_data_type);
214 ScheduleDoNotify(device_data.release(), device_data_type);
215 } else if (device_data->SignificantlyDifferentFrom(*last_device_data)) {
216 last_device_data_map_->Remove(device_data_type);
217 last_device_data_map_->AddWithID(device_data->Clone(), device_data_type);
218 ScheduleDoNotify(device_data.release(), device_data_type);
219 }
147 } 220 }
148 221
149 ScheduleDoPoll(); 222 ScheduleDoPoll();
150 } 223 }
151 224
152 void ProviderImpl::ScheduleDoPoll() { 225 void ProviderImpl::ScheduleDoPoll() {
153 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); 226 DCHECK(MessageLoop::current() == polling_thread_->message_loop());
154 227
155 MessageLoop* polling_loop = polling_thread_->message_loop(); 228 MessageLoop* polling_loop = polling_thread_->message_loop();
156 polling_loop->PostDelayedTask( 229 polling_loop->PostDelayedTask(
157 FROM_HERE, 230 FROM_HERE,
158 base::Bind(&ProviderImpl::DoPoll, weak_factory_.GetWeakPtr()), 231 base::Bind(&ProviderImpl::DoPoll, weak_factory_.GetWeakPtr()),
159 SamplingInterval()); 232 SamplingInterval());
160 } 233 }
161 234
162 namespace {
163
164 bool IsElementSignificantlyDifferent(bool can_provide_element1,
165 bool can_provide_element2,
166 double element1,
167 double element2) {
168 const double kThreshold = 0.1;
169
170 if (can_provide_element1 != can_provide_element2)
171 return true;
172 if (can_provide_element1 &&
173 std::fabs(element1 - element2) >= kThreshold)
174 return true;
175 return false;
176 }
177 } // namespace
178
179 // Returns true if two orientations are considered different enough that
180 // observers should be notified of the new orientation.
181 bool ProviderImpl::SignificantlyDifferent(const Orientation& o1,
182 const Orientation& o2) {
183 return IsElementSignificantlyDifferent(o1.can_provide_alpha(),
184 o2.can_provide_alpha(),
185 o1.alpha(),
186 o2.alpha()) ||
187 IsElementSignificantlyDifferent(o1.can_provide_beta(),
188 o2.can_provide_beta(),
189 o1.beta(),
190 o2.beta()) ||
191 IsElementSignificantlyDifferent(o1.can_provide_gamma(),
192 o2.can_provide_gamma(),
193 o1.gamma(),
194 o2.gamma()) ||
195 (o1.can_provide_absolute() != o2.can_provide_absolute() ||
196 o1.absolute() != o2.absolute());
197 }
198
199 base::TimeDelta ProviderImpl::SamplingInterval() const { 235 base::TimeDelta ProviderImpl::SamplingInterval() const {
200 DCHECK(MessageLoop::current() == polling_thread_->message_loop()); 236 DCHECK(MessageLoop::current() == polling_thread_->message_loop());
201 DCHECK(data_fetcher_.get()); 237 DCHECK(data_fetcher_.get());
202 238
203 // TODO(erg): There used to be unused code here, that called a default 239 // TODO(erg): There used to be unused code here, that called a default
204 // implementation on the DataFetcherInterface that was never defined. I'm 240 // implementation on the DataFetcherInterface that was never defined. I'm
205 // removing unused methods from headers. 241 // removing unused methods from headers.
206 return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs); 242 return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs);
207 } 243 }
208 244
209 } // namespace device_orientation 245 } // namespace device_orientation
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698