1 /*
2 * Copyright (C) 2012 Samsung Electronics
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "modules/vibration/NavigatorVibration.h"
22
23 #include "core/frame/LocalFrame.h"
24 #include "core/frame/Navigator.h"
25 #include "core/page/PageVisibilityState.h"
26 #include "public/platform/Platform.h"
27 #include "public/platform/WebVibration.h"
28
29 namespace blink {
30
31 // Maximum number of entries in a vibration pattern.
32 const unsigned kVibrationPatternLengthMax = 99;
33
NavigatorVibration(Page & page)34 NavigatorVibration::NavigatorVibration(Page& page)
35 : PageLifecycleObserver(&page)
36 , m_timerStart(this, &NavigatorVibration::timerStartFired)
37 , m_timerStop(this, &NavigatorVibration::timerStopFired)
38 , m_isVibrating(false)
39 {
40 }
41
~NavigatorVibration()42 NavigatorVibration::~NavigatorVibration()
43 {
44 if (m_isVibrating)
45 cancelVibration();
46 }
47
vibrate(const VibrationPattern & pattern)48 bool NavigatorVibration::vibrate(const VibrationPattern& pattern)
49 {
50 VibrationPattern sanitized = pattern;
51 size_t length = sanitized.size();
52
53 // If the pattern is too long then truncate it.
54 if (length > kVibrationPatternLengthMax) {
55 sanitized.shrink(kVibrationPatternLengthMax);
56 length = kVibrationPatternLengthMax;
57 }
58
59 // If any pattern entry is too long then truncate it.
60 for (size_t i = 0; i < length; ++i) {
61 if (sanitized[i] > kVibrationDurationMax)
62 sanitized[i] = kVibrationDurationMax;
63 }
64
65 // If the last item in the pattern is a pause then discard it.
66 if (length && !(length % 2))
67 sanitized.removeLast();
68
69 // Cancelling clears the stored pattern so do it before setting the new one.
70 if (m_isVibrating)
71 cancelVibration();
72
73 m_pattern = sanitized;
74
75 if (m_timerStart.isActive())
76 m_timerStart.stop();
77
78 if (!m_pattern.size())
79 return true;
80
81 if (m_pattern.size() == 1 && !m_pattern[0]) {
82 m_pattern.clear();
83 return true;
84 }
85
86 m_timerStart.startOneShot(0, FROM_HERE);
87 m_isVibrating = true;
88 return true;
89 }
90
cancelVibration()91 void NavigatorVibration::cancelVibration()
92 {
93 m_pattern.clear();
94 if (m_isVibrating) {
95 Platform::current()->cancelVibration();
96 m_isVibrating = false;
97 m_timerStop.stop();
98 }
99 }
100
timerStartFired(Timer<NavigatorVibration> * timer)101 void NavigatorVibration::timerStartFired(Timer<NavigatorVibration>* timer)
102 {
103 ASSERT_UNUSED(timer, timer == &m_timerStart);
104
105 if (m_pattern.size()) {
106 m_isVibrating = true;
107 Platform::current()->vibrate(m_pattern[0]);
108 m_timerStop.startOneShot(m_pattern[0] / 1000.0, FROM_HERE);
109 m_pattern.remove(0);
110 }
111 }
112
timerStopFired(Timer<NavigatorVibration> * timer)113 void NavigatorVibration::timerStopFired(Timer<NavigatorVibration>* timer)
114 {
115 ASSERT_UNUSED(timer, timer == &m_timerStop);
116
117 if (m_pattern.isEmpty())
118 m_isVibrating = false;
119
120 if (m_pattern.size()) {
121 m_timerStart.startOneShot(m_pattern[0] / 1000.0, FROM_HERE);
122 m_pattern.remove(0);
123 }
124 }
125
pageVisibilityChanged()126 void NavigatorVibration::pageVisibilityChanged()
127 {
128 if (page()->visibilityState() != PageVisibilityStateVisible)
129 cancelVibration();
130 }
131
didCommitLoad(LocalFrame * frame)132 void NavigatorVibration::didCommitLoad(LocalFrame* frame)
133 {
134 // A new load has been committed, which means the current page will be
135 // unloaded. Cancel all running vibrations.
136 cancelVibration();
137 }
138
vibrate(Navigator & navigator,unsigned time)139 bool NavigatorVibration::vibrate(Navigator& navigator, unsigned time)
140 {
141 VibrationPattern pattern;
142 pattern.append(time);
143 return NavigatorVibration::vibrate(navigator, pattern);
144 }
145
vibrate(Navigator & navigator,const VibrationPattern & pattern)146 bool NavigatorVibration::vibrate(Navigator& navigator, const VibrationPattern& pattern)
147 {
148 if (!navigator.frame())
149 return false;
150
151 Page* page = navigator.frame()->page();
152 if (!page)
153 return false;
154
155 if (page->visibilityState() != PageVisibilityStateVisible)
156 return false;
157
158 return NavigatorVibration::from(*page).vibrate(pattern);
159 }
160
from(Page & page)161 NavigatorVibration& NavigatorVibration::from(Page& page)
162 {
163 NavigatorVibration* navigatorVibration = static_cast<NavigatorVibration*>(WillBeHeapSupplement<Page>::from(page, supplementName()));
164 if (!navigatorVibration) {
165 navigatorVibration = new NavigatorVibration(page);
166 WillBeHeapSupplement<Page>::provideTo(page, supplementName(), adoptPtrWillBeNoop(navigatorVibration));
167 }
168 return *navigatorVibration;
169 }
170
supplementName()171 const char* NavigatorVibration::supplementName()
172 {
173 return "NavigatorVibration";
174 }
175
176 } // namespace blink
177