blob: a4d240dc9f3a25513e02e8f9a135cd2c32fb92fa [file] [log] [blame]
Steve Blockc43565b2010-08-11 13:12:37 +01001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
Steve Blockbb9db222010-08-18 11:12:07 +010019import android.content.Context;
20import android.hardware.Sensor;
21import android.hardware.SensorEvent;
22import android.hardware.SensorEventListener;
23import android.hardware.SensorManager;
Steve Blockc43565b2010-08-11 13:12:37 +010024import android.os.Handler;
Steve Block1be73392010-10-15 16:31:01 +010025import android.webkit.DeviceMotionAndOrientationManager;
Steve Blockc43565b2010-08-11 13:12:37 +010026import java.lang.Runnable;
Steve Blockbb9db222010-08-18 11:12:07 +010027import java.util.List;
Steve Blockc43565b2010-08-11 13:12:37 +010028
29
Steve Blockbb9db222010-08-18 11:12:07 +010030final class DeviceOrientationService implements SensorEventListener {
31 // The gravity vector expressed in the body frame.
32 private float[] mGravityVector;
33 // The geomagnetic vector expressed in the body frame.
34 private float[] mMagneticFieldVector;
35
Steve Block1be73392010-10-15 16:31:01 +010036 private DeviceMotionAndOrientationManager mManager;
Steve Blockc43565b2010-08-11 13:12:37 +010037 private boolean mIsRunning;
38 private Handler mHandler;
Steve Blockbb9db222010-08-18 11:12:07 +010039 private SensorManager mSensorManager;
40 private Context mContext;
41 private Double mAlpha;
42 private Double mBeta;
43 private Double mGamma;
Steve Block89803432010-08-25 11:56:09 +010044 private boolean mHaveSentErrorEvent;
Steve Blockc43565b2010-08-11 13:12:37 +010045
Steve Blockbb9db222010-08-18 11:12:07 +010046 private static final double DELTA_DEGRESS = 1.0;
47
Steve Block1be73392010-10-15 16:31:01 +010048 public DeviceOrientationService(DeviceMotionAndOrientationManager manager, Context context) {
Steve Blockc43565b2010-08-11 13:12:37 +010049 mManager = manager;
50 assert(mManager != null);
Steve Blockbb9db222010-08-18 11:12:07 +010051 mContext = context;
52 assert(mContext != null);
Steve Blockc43565b2010-08-11 13:12:37 +010053 }
54
55 public void start() {
56 mIsRunning = true;
57 registerForSensors();
58 }
59
60 public void stop() {
61 mIsRunning = false;
62 unregisterFromSensors();
63 }
64
65 public void suspend() {
66 if (mIsRunning) {
67 unregisterFromSensors();
68 }
69 }
70
71 public void resume() {
72 if (mIsRunning) {
73 registerForSensors();
74 }
75 }
76
77 private void sendErrorEvent() {
78 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
Steve Block89803432010-08-25 11:56:09 +010079 // The spec requires that each listener receives the error event only once.
80 if (mHaveSentErrorEvent)
81 return;
82 mHaveSentErrorEvent = true;
Steve Blockc43565b2010-08-11 13:12:37 +010083 mHandler.post(new Runnable() {
84 @Override
85 public void run() {
86 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
87 if (mIsRunning) {
Steve Block89803432010-08-25 11:56:09 +010088 // The special case of all nulls is used to signify a failure to get data.
Steve Blockc43565b2010-08-11 13:12:37 +010089 mManager.onOrientationChange(null, null, null);
90 }
91 }
92 });
93 }
94
95 private void registerForSensors() {
Steve Blockbb9db222010-08-18 11:12:07 +010096 if (mHandler == null) {
97 mHandler = new Handler();
98 }
99 if (!registerForAccelerometerSensor() || !registerForMagneticFieldSensor()) {
100 unregisterFromSensors();
101 sendErrorEvent();
102 }
103 }
104
105 private void getOrientationUsingGetRotationMatrix() {
106 if (mGravityVector == null || mMagneticFieldVector == null) {
107 return;
108 }
109
110 // Get the rotation matrix.
111 // The rotation matrix that transforms from the body frame to the earth frame.
112 float[] deviceRotationMatrix = new float[9];
113 if (!SensorManager.getRotationMatrix(
114 deviceRotationMatrix, null, mGravityVector, mMagneticFieldVector)) {
115 return;
116 }
117
118 // Convert rotation matrix to rotation angles.
119 // Assuming that the rotations are appied in the order listed at
120 // http://developer.android.com/reference/android/hardware/SensorEvent.html#values
121 // the rotations are applied about the same axes and in the same order as required by the
122 // API. The only conversions are sign changes as follows.
123 // The angles are in radians
124 float[] rotationAngles = new float[3];
125 SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
Steve Blockd5be0552012-08-15 15:52:12 +0100126 double alpha = Math.toDegrees(-rotationAngles[0]);
Steve Blockbb9db222010-08-18 11:12:07 +0100127 while (alpha < 0.0) { alpha += 360.0; } // [0, 360)
128 double beta = Math.toDegrees(-rotationAngles[1]);
129 while (beta < -180.0) { beta += 360.0; } // [-180, 180)
130 double gamma = Math.toDegrees(rotationAngles[2]);
131 while (gamma < -90.0) { gamma += 360.0; } // [-90, 90)
132
133 maybeSendChange(alpha, beta, gamma);
134 }
135
136 private SensorManager getSensorManager() {
137 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
138 if (mSensorManager == null) {
139 mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
140 }
141 return mSensorManager;
142 }
143
144 private boolean registerForAccelerometerSensor() {
145 List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER);
146 if (sensors.isEmpty()) {
147 return false;
148 }
149 // TODO: Consider handling multiple sensors.
150 return getSensorManager().registerListener(
151 this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
152 }
153
154 private boolean registerForMagneticFieldSensor() {
155 List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
156 if (sensors.isEmpty()) {
157 return false;
158 }
159 // TODO: Consider handling multiple sensors.
160 return getSensorManager().registerListener(
161 this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
Steve Blockc43565b2010-08-11 13:12:37 +0100162 }
163
164 private void unregisterFromSensors() {
Steve Blockbb9db222010-08-18 11:12:07 +0100165 getSensorManager().unregisterListener(this);
166 }
167
168 private void maybeSendChange(double alpha, double beta, double gamma) {
169 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
170 if (mAlpha == null || mBeta == null || mGamma == null
171 || Math.abs(alpha - mAlpha) > DELTA_DEGRESS
172 || Math.abs(beta - mBeta) > DELTA_DEGRESS
173 || Math.abs(gamma - mGamma) > DELTA_DEGRESS) {
174 mAlpha = alpha;
175 mBeta = beta;
176 mGamma = gamma;
177 mManager.onOrientationChange(mAlpha, mBeta, mGamma);
Steve Block89803432010-08-25 11:56:09 +0100178 // Now that we have successfully sent some data, reset whether we've sent an error.
179 mHaveSentErrorEvent = false;
Steve Blockbb9db222010-08-18 11:12:07 +0100180 }
181 }
182
183 /**
184 * SensorEventListener implementation.
185 * Callbacks happen on the thread on which we registered - the WebCore thread.
186 */
Steve Block5ba2efe2011-08-03 13:57:49 +0100187 @Override
Steve Blockbb9db222010-08-18 11:12:07 +0100188 public void onSensorChanged(SensorEvent event) {
189 assert(event.values.length == 3);
190 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
191
Steve Blockd09001f2011-08-03 14:03:24 +0100192 // We may get callbacks after the call to getSensorManager().unregisterListener() returns.
Steve Blockbb9db222010-08-18 11:12:07 +0100193 if (!mIsRunning) {
194 return;
195 }
196
197 switch (event.sensor.getType()) {
198 case Sensor.TYPE_ACCELEROMETER:
199 if (mGravityVector == null) {
200 mGravityVector = new float[3];
201 }
202 mGravityVector[0] = event.values[0];
203 mGravityVector[1] = event.values[1];
204 mGravityVector[2] = event.values[2];
205 getOrientationUsingGetRotationMatrix();
206 break;
207 case Sensor.TYPE_MAGNETIC_FIELD:
208 if (mMagneticFieldVector == null) {
209 mMagneticFieldVector = new float[3];
210 }
211 mMagneticFieldVector[0] = event.values[0];
212 mMagneticFieldVector[1] = event.values[1];
213 mMagneticFieldVector[2] = event.values[2];
214 getOrientationUsingGetRotationMatrix();
215 break;
216 default:
217 assert(false);
218 }
219 }
220
Steve Block5ba2efe2011-08-03 13:57:49 +0100221 @Override
Steve Blockbb9db222010-08-18 11:12:07 +0100222 public void onAccuracyChanged(Sensor sensor, int accuracy) {
223 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
Steve Blockc43565b2010-08-11 13:12:37 +0100224 }
225}