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 48 public DeviceOrientationService(DeviceMotionAndOrientationManager manager, Context context) { 49 mManager = manager; 50 assert(mManager != null); 51 mContext = context; 52 assert(mContext != null); 53 } 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()); 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 95 private void registerForSensors() { 96 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); 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 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); 162 } 163 164 private void unregisterFromSensors() { 165 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); 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 */ 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 220 public void onAccuracyChanged(Sensor sensor, int accuracy) { 221 assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); 222 } 223 } 224