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
17package android.webkit;
18
19import android.content.Context;
20import android.hardware.Sensor;
21import android.hardware.SensorEvent;
22import android.hardware.SensorEventListener;
23import android.hardware.SensorManager;
24import android.os.Handler;
25import android.os.Message;
26import android.webkit.DeviceMotionAndOrientationManager;
27import java.lang.Runnable;
28import java.util.List;
29
30
31final class DeviceMotionService implements SensorEventListener {
32    private DeviceMotionAndOrientationManager mManager;
33    private boolean mIsRunning;
34    private Handler mHandler;
35    private SensorManager mSensorManager;
36    private Context mContext;
37    private boolean mHaveSentErrorEvent;
38    private Runnable mUpdateRunnable;
39    private float mLastAcceleration[];
40
41    private static final int INTERVAL_MILLIS = 100;
42
43    public DeviceMotionService(DeviceMotionAndOrientationManager manager, Context context) {
44        mManager = manager;
45        assert(mManager != null);
46        mContext = context;
47        assert(mContext != null);
48     }
49
50    public void start() {
51        mIsRunning = true;
52        registerForSensor();
53    }
54
55    public void stop() {
56        mIsRunning = false;
57        stopSendingUpdates();
58        unregisterFromSensor();
59    }
60
61    public void suspend() {
62        if (mIsRunning) {
63            stopSendingUpdates();
64            unregisterFromSensor();
65        }
66    }
67
68    public void resume() {
69        if (mIsRunning) {
70            registerForSensor();
71        }
72    }
73
74    private void sendErrorEvent() {
75        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
76        // The spec requires that each listener receives the error event only once.
77        if (mHaveSentErrorEvent)
78            return;
79        mHaveSentErrorEvent = true;
80        createHandler();
81        mHandler.post(new Runnable() {
82            @Override
83            public void run() {
84                assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
85                if (mIsRunning) {
86                    // The special case of all nulls is used to signify a failure to get data.
87                    mManager.onMotionChange(null, null, null, 0.0);
88                }
89            }
90        });
91    }
92
93    private void createHandler() {
94        if (mHandler != null) {
95            return;
96        }
97
98        mHandler = new Handler();
99        mUpdateRunnable = new Runnable() {
100            @Override
101            public void run() {
102                assert mIsRunning;
103                mManager.onMotionChange(new Double(mLastAcceleration[0]),
104                        new Double(mLastAcceleration[1]), new Double(mLastAcceleration[2]),
105                        INTERVAL_MILLIS);
106                mHandler.postDelayed(mUpdateRunnable, INTERVAL_MILLIS);
107                // Now that we have successfully sent some data, reset whether we've sent an error.
108                mHaveSentErrorEvent = false;
109            }
110        };
111    }
112
113    private void startSendingUpdates() {
114        createHandler();
115        mUpdateRunnable.run();
116    }
117
118    private void stopSendingUpdates() {
119        mHandler.removeCallbacks(mUpdateRunnable);
120        mLastAcceleration = null;
121    }
122
123    private void registerForSensor() {
124        if (!registerForAccelerometerSensor()) {
125            sendErrorEvent();
126        }
127    }
128
129    private SensorManager getSensorManager() {
130        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
131        if (mSensorManager == null) {
132            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
133        }
134        return mSensorManager;
135    }
136
137    private boolean registerForAccelerometerSensor() {
138        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER);
139        if (sensors.isEmpty()) {
140            return false;
141        }
142        createHandler();
143        // TODO: Consider handling multiple sensors.
144        return getSensorManager().registerListener(
145                this, sensors.get(0), SensorManager.SENSOR_DELAY_UI, mHandler);
146    }
147
148    private void unregisterFromSensor() {
149        getSensorManager().unregisterListener(this);
150    }
151
152    /**
153     * SensorEventListener implementation.
154     * Callbacks happen on the thread on which we registered - the WebCore thread.
155     */
156    @Override
157    public void onSensorChanged(SensorEvent event) {
158        assert(event.values.length == 3);
159        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
160        assert(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER);
161
162        // We may get callbacks after the call to getSensorManager().unregisterListener() returns.
163        if (!mIsRunning) {
164            return;
165        }
166
167        boolean firstData = mLastAcceleration == null;
168        mLastAcceleration = event.values;
169        if (firstData) {
170            startSendingUpdates();
171        }
172    }
173
174    @Override
175    public void onAccuracyChanged(Sensor sensor, int accuracy) {
176        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
177    }
178}
179