blob: 4ff849ea0162f6a638965b5ce552dd3b785b5fbf [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;
25import android.webkit.DeviceOrientationManager;
26import 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 Blockc43565b2010-08-11 13:12:37 +010036 private DeviceOrientationManager mManager;
37 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 Blockc43565b2010-08-11 13:12:37 +010044
Steve Blockbb9db222010-08-18 11:12:07 +010045 private static final double DELTA_DEGRESS = 1.0;
46
47 public DeviceOrientationService(DeviceOrientationManager manager, Context context) {
Steve Blockc43565b2010-08-11 13:12:37 +010048 mManager = manager;
49 assert(mManager != null);
Steve Blockbb9db222010-08-18 11:12:07 +010050 mContext = context;
51 assert(mContext != null);
Steve Blockc43565b2010-08-11 13:12:37 +010052 }
53
54 public void start() {
55 mIsRunning = true;
56 registerForSensors();
57 }
58
59 public void stop() {
60 mIsRunning = false;
61 unregisterFromSensors();
62 }
63
64 public void suspend() {
65 if (mIsRunning) {
66 unregisterFromSensors();
67 }
68 }
69
70 public void resume() {
71 if (mIsRunning) {
72 registerForSensors();
73 }
74 }
75
76 private void sendErrorEvent() {
77 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
Steve Blockc43565b2010-08-11 13:12:37 +010078 mHandler.post(new Runnable() {
79 @Override
80 public void run() {
81 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
82 if (mIsRunning) {
83 mManager.onOrientationChange(null, null, null);
84 }
85 }
86 });
87 }
88
89 private void registerForSensors() {
Steve Blockbb9db222010-08-18 11:12:07 +010090 if (mHandler == null) {
91 mHandler = new Handler();
92 }
93 if (!registerForAccelerometerSensor() || !registerForMagneticFieldSensor()) {
94 unregisterFromSensors();
95 sendErrorEvent();
96 }
97 }
98
99 private void getOrientationUsingGetRotationMatrix() {
100 if (mGravityVector == null || mMagneticFieldVector == null) {
101 return;
102 }
103
104 // Get the rotation matrix.
105 // The rotation matrix that transforms from the body frame to the earth frame.
106 float[] deviceRotationMatrix = new float[9];
107 if (!SensorManager.getRotationMatrix(
108 deviceRotationMatrix, null, mGravityVector, mMagneticFieldVector)) {
109 return;
110 }
111
112 // Convert rotation matrix to rotation angles.
113 // Assuming that the rotations are appied in the order listed at
114 // http://developer.android.com/reference/android/hardware/SensorEvent.html#values
115 // the rotations are applied about the same axes and in the same order as required by the
116 // API. The only conversions are sign changes as follows.
117 // The angles are in radians
118 float[] rotationAngles = new float[3];
119 SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
120 double alpha = Math.toDegrees(-rotationAngles[0]) - 90.0;
121 while (alpha < 0.0) { alpha += 360.0; } // [0, 360)
122 double beta = Math.toDegrees(-rotationAngles[1]);
123 while (beta < -180.0) { beta += 360.0; } // [-180, 180)
124 double gamma = Math.toDegrees(rotationAngles[2]);
125 while (gamma < -90.0) { gamma += 360.0; } // [-90, 90)
126
127 maybeSendChange(alpha, beta, gamma);
128 }
129
130 private SensorManager getSensorManager() {
131 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
132 if (mSensorManager == null) {
133 mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
134 }
135 return mSensorManager;
136 }
137
138 private boolean registerForAccelerometerSensor() {
139 List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER);
140 if (sensors.isEmpty()) {
141 return false;
142 }
143 // TODO: Consider handling multiple sensors.
144 return getSensorManager().registerListener(
145 this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
146 }
147
148 private boolean registerForMagneticFieldSensor() {
149 List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
150 if (sensors.isEmpty()) {
151 return false;
152 }
153 // TODO: Consider handling multiple sensors.
154 return getSensorManager().registerListener(
155 this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
Steve Blockc43565b2010-08-11 13:12:37 +0100156 }
157
158 private void unregisterFromSensors() {
Steve Blockbb9db222010-08-18 11:12:07 +0100159 getSensorManager().unregisterListener(this);
160 }
161
162 private void maybeSendChange(double alpha, double beta, double gamma) {
163 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
164 if (mAlpha == null || mBeta == null || mGamma == null
165 || Math.abs(alpha - mAlpha) > DELTA_DEGRESS
166 || Math.abs(beta - mBeta) > DELTA_DEGRESS
167 || Math.abs(gamma - mGamma) > DELTA_DEGRESS) {
168 mAlpha = alpha;
169 mBeta = beta;
170 mGamma = gamma;
171 mManager.onOrientationChange(mAlpha, mBeta, mGamma);
172 }
173 }
174
175 /**
176 * SensorEventListener implementation.
177 * Callbacks happen on the thread on which we registered - the WebCore thread.
178 */
179 public void onSensorChanged(SensorEvent event) {
180 assert(event.values.length == 3);
181 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
182
183 if (!mIsRunning) {
184 return;
185 }
186
187 switch (event.sensor.getType()) {
188 case Sensor.TYPE_ACCELEROMETER:
189 if (mGravityVector == null) {
190 mGravityVector = new float[3];
191 }
192 mGravityVector[0] = event.values[0];
193 mGravityVector[1] = event.values[1];
194 mGravityVector[2] = event.values[2];
195 getOrientationUsingGetRotationMatrix();
196 break;
197 case Sensor.TYPE_MAGNETIC_FIELD:
198 if (mMagneticFieldVector == null) {
199 mMagneticFieldVector = new float[3];
200 }
201 mMagneticFieldVector[0] = event.values[0];
202 mMagneticFieldVector[1] = event.values[1];
203 mMagneticFieldVector[2] = event.values[2];
204 getOrientationUsingGetRotationMatrix();
205 break;
206 default:
207 assert(false);
208 }
209 }
210
211 public void onAccuracyChanged(Sensor sensor, int accuracy) {
212 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
Steve Blockc43565b2010-08-11 13:12:37 +0100213 }
214}