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