• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.webkit;
18 
19 import android.content.Context;
20 import android.hardware.Sensor;
21 import android.hardware.SensorEvent;
22 import android.hardware.SensorEventListener;
23 import android.hardware.SensorManager;
24 import android.os.Handler;
25 import android.webkit.DeviceMotionAndOrientationManager;
26 import java.lang.Runnable;
27 import java.util.List;
28 
29 
30 final 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 
36     private DeviceMotionAndOrientationManager mManager;
37     private boolean mIsRunning;
38     private Handler mHandler;
39     private SensorManager mSensorManager;
40     private Context mContext;
41     private Double mAlpha;
42     private Double mBeta;
43     private Double mGamma;
44     private boolean mHaveSentErrorEvent;
45 
46     private static final double DELTA_DEGRESS = 1.0;
47 
DeviceOrientationService(DeviceMotionAndOrientationManager manager, Context context)48     public DeviceOrientationService(DeviceMotionAndOrientationManager manager, Context context) {
49         mManager = manager;
50         assert(mManager != null);
51         mContext = context;
52         assert(mContext != null);
53      }
54 
start()55     public void start() {
56         mIsRunning = true;
57         registerForSensors();
58     }
59 
stop()60     public void stop() {
61         mIsRunning = false;
62         unregisterFromSensors();
63     }
64 
suspend()65     public void suspend() {
66         if (mIsRunning) {
67             unregisterFromSensors();
68         }
69     }
70 
resume()71     public void resume() {
72         if (mIsRunning) {
73             registerForSensors();
74         }
75     }
76 
sendErrorEvent()77     private void sendErrorEvent() {
78         assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
79         // The spec requires that each listener receives the error event only once.
80         if (mHaveSentErrorEvent)
81             return;
82         mHaveSentErrorEvent = true;
83         mHandler.post(new Runnable() {
84             @Override
85             public void run() {
86                 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
87                 if (mIsRunning) {
88                     // The special case of all nulls is used to signify a failure to get data.
89                     mManager.onOrientationChange(null, null, null);
90                 }
91             }
92         });
93     }
94 
registerForSensors()95     private void registerForSensors() {
96         if (mHandler == null) {
97             mHandler = new Handler();
98         }
99         if (!registerForAccelerometerSensor() || !registerForMagneticFieldSensor()) {
100             unregisterFromSensors();
101             sendErrorEvent();
102         }
103     }
104 
getOrientationUsingGetRotationMatrix()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);
126         double alpha = Math.toDegrees(-rotationAngles[0]) - 90.0;
127         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 
getSensorManager()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 
registerForAccelerometerSensor()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 
registerForMagneticFieldSensor()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);
162     }
163 
unregisterFromSensors()164     private void unregisterFromSensors() {
165         getSensorManager().unregisterListener(this);
166     }
167 
maybeSendChange(double alpha, double beta, double gamma)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);
178             // Now that we have successfully sent some data, reset whether we've sent an error.
179             mHaveSentErrorEvent = false;
180         }
181     }
182 
183     /**
184      * SensorEventListener implementation.
185      * Callbacks happen on the thread on which we registered - the WebCore thread.
186      */
onSensorChanged(SensorEvent event)187     public void onSensorChanged(SensorEvent event) {
188         assert(event.values.length == 3);
189         assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
190 
191         // We may get callbacks after the call to getSensorManager().unregisterListener() returns.
192         if (!mIsRunning) {
193             return;
194         }
195 
196         switch (event.sensor.getType()) {
197           case Sensor.TYPE_ACCELEROMETER:
198             if (mGravityVector == null) {
199                 mGravityVector = new float[3];
200             }
201             mGravityVector[0] = event.values[0];
202             mGravityVector[1] = event.values[1];
203             mGravityVector[2] = event.values[2];
204             getOrientationUsingGetRotationMatrix();
205             break;
206           case Sensor.TYPE_MAGNETIC_FIELD:
207             if (mMagneticFieldVector == null) {
208                 mMagneticFieldVector = new float[3];
209             }
210             mMagneticFieldVector[0] = event.values[0];
211             mMagneticFieldVector[1] = event.values[1];
212             mMagneticFieldVector[2] = event.values[2];
213             getOrientationUsingGetRotationMatrix();
214             break;
215           default:
216             assert(false);
217         }
218     }
219 
onAccuracyChanged(Sensor sensor, int accuracy)220     public void onAccuracyChanged(Sensor sensor, int accuracy) {
221         assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
222     }
223 }
224