1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.app;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.Sensor;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.SensorEvent;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.SensorEventListener;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.hardware.SensorManager;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.SystemClock;
25fd91413ab46e2960803a33652025aebe3e05f2d9Chih-Chung Changimport android.util.FloatMath;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.Display;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.Surface;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.WindowManager;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
302b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.common.Utils;
312b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport com.android.gallery3d.util.GalleryUtils;
322b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Lin
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class EyePosition {
347817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @SuppressWarnings("unused")
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "EyePosition";
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public interface EyePositionListener {
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onEyePositionChanged(float x, float y, float z);
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float GYROSCOPE_THRESHOLD = 0.15f;
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float GYROSCOPE_LIMIT = 10f;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int GYROSCOPE_SETTLE_DOWN = 15;
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float GYROSCOPE_RESTORE_FACTOR = 0.995f;
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
46fd91413ab46e2960803a33652025aebe3e05f2d9Chih-Chung Chang    private static final float USER_ANGEL = (float) Math.toRadians(10);
47fd91413ab46e2960803a33652025aebe3e05f2d9Chih-Chung Chang    private static final float USER_ANGEL_COS = FloatMath.cos(USER_ANGEL);
48fd91413ab46e2960803a33652025aebe3e05f2d9Chih-Chung Chang    private static final float USER_ANGEL_SIN = FloatMath.sin(USER_ANGEL);
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float MAX_VIEW_RANGE = (float) 0.5;
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int NOT_STARTED = -1;
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final float USER_DISTANCE_METER = 0.3f;
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Context mContext;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private EyePositionListener mListener;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Display mDisplay;
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The eyes' position of the user, the origin is at the center of the
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // device and the unit is in pixels.
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private float mX;
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private float mY;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private float mZ;
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final float mUserDistance; // in pixel
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final float mLimit;
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private long mStartTime = NOT_STARTED;
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Sensor mSensor;
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private PositionListener mPositionListener = new PositionListener();
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mGyroscopeCountdown = 0;
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public EyePosition(Context context, EyePositionListener listener) {
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mContext = context;
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListener = listener;
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mUserDistance = GalleryUtils.meterToPixel(USER_DISTANCE_METER);
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLimit = mUserDistance * MAX_VIEW_RANGE;
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        WindowManager wManager = (WindowManager) mContext
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                .getSystemService(Context.WINDOW_SERVICE);
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mDisplay = wManager.getDefaultDisplay();
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
81fd06154c0d51fd8686ffec3955e54f5c18e87da0Chih-Chung Chang        // The 3D effect where the photo albums fan out in 3D based on angle
82fd06154c0d51fd8686ffec3955e54f5c18e87da0Chih-Chung Chang        // of device tilt is currently disabled.
83fd06154c0d51fd8686ffec3955e54f5c18e87da0Chih-Chung Chang/*
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        SensorManager sManager = (SensorManager) mContext
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                .getSystemService(Context.SENSOR_SERVICE);
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSensor = sManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mSensor == null) {
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Log.w(TAG, "no gyroscope, use accelerometer instead");
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSensor = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mSensor == null) {
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Log.w(TAG, "no sensor available");
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
94fd06154c0d51fd8686ffec3955e54f5c18e87da0Chih-Chung Chang*/
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resetPosition() {
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mStartTime = NOT_STARTED;
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mX = mY = 0;
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mZ = -mUserDistance;
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListener.onEyePositionChanged(mX, mY, mZ);
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /*
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * We assume the user is at the following position
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *              /|\  user's eye
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *               |   /
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *   -G(gravity) |  /
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *               |_/
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *             / |/_____\ -Y (-y direction of device)
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *     user angel
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void onAccelerometerChanged(float gx, float gy, float gz) {
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float x = gx, y = gy, z = gz;
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (mDisplay.getRotation()) {
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case Surface.ROTATION_90: x = -gy; y= gx; break;
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case Surface.ROTATION_180: x = -gx; y = -gy; break;
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case Surface.ROTATION_270: x = gy; y = -gx; break;
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float temp = x * x + y * y + z * z;
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float t = -y /temp;
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float tx = t * x;
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float ty = -1 + t * y;
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float tz = t * z;
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
131fd91413ab46e2960803a33652025aebe3e05f2d9Chih-Chung Chang        float length = FloatMath.sqrt(tx * tx + ty * ty + tz * tz);
132fd91413ab46e2960803a33652025aebe3e05f2d9Chih-Chung Chang        float glength = FloatMath.sqrt(temp);
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mX = Utils.clamp((x * USER_ANGEL_COS / glength
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                + tx * USER_ANGEL_SIN / length) * mUserDistance,
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                -mLimit, mLimit);
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mY = -Utils.clamp((y * USER_ANGEL_COS / glength
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                + ty * USER_ANGEL_SIN / length) * mUserDistance,
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                -mLimit, mLimit);
140fd91413ab46e2960803a33652025aebe3e05f2d9Chih-Chung Chang        mZ = -FloatMath.sqrt(
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mUserDistance * mUserDistance - mX * mX - mY * mY);
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListener.onEyePositionChanged(mX, mY, mZ);
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void onGyroscopeChanged(float gx, float gy, float gz) {
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        long now = SystemClock.elapsedRealtime();
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float distance = (gx > 0 ? gx : -gx) + (gy > 0 ? gy : - gy);
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (distance < GYROSCOPE_THRESHOLD
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                || distance > GYROSCOPE_LIMIT || mGyroscopeCountdown > 0) {
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            --mGyroscopeCountdown;
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mStartTime = now;
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float limit = mUserDistance / 20f;
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mX > limit || mX < -limit || mY > limit || mY < -limit) {
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mX *= GYROSCOPE_RESTORE_FACTOR;
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mY *= GYROSCOPE_RESTORE_FACTOR;
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mZ = (float) -Math.sqrt(
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mUserDistance * mUserDistance - mX * mX - mY * mY);
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mListener.onEyePositionChanged(mX, mY, mZ);
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return;
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float t = (now - mStartTime) / 1000f * mUserDistance * (-mZ);
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mStartTime = now;
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        float x = -gy, y = -gx;
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (mDisplay.getRotation()) {
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case Surface.ROTATION_90: x = -gx; y= gy; break;
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case Surface.ROTATION_180: x = gy; y = gx; break;
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case Surface.ROTATION_270: x = gx; y = -gy; break;
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mX = Utils.clamp((float) (mX + x * t / Math.hypot(mZ, mX)),
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                -mLimit, mLimit) * GYROSCOPE_RESTORE_FACTOR;
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mY = Utils.clamp((float) (mY + y * t / Math.hypot(mZ, mY)),
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                -mLimit, mLimit) * GYROSCOPE_RESTORE_FACTOR;
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
178fd91413ab46e2960803a33652025aebe3e05f2d9Chih-Chung Chang        mZ = -FloatMath.sqrt(
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mUserDistance * mUserDistance - mX * mX - mY * mY);
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListener.onEyePositionChanged(mX, mY, mZ);
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class PositionListener implements SensorEventListener {
1847817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onAccuracyChanged(Sensor sensor, int accuracy) {
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1887817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onSensorChanged(SensorEvent event) {
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            switch (event.sensor.getType()) {
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                case Sensor.TYPE_GYROSCOPE: {
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    onGyroscopeChanged(
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            event.values[0], event.values[1], event.values[2]);
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    break;
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                case Sensor.TYPE_ACCELEROMETER: {
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    onAccelerometerChanged(
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            event.values[0], event.values[1], event.values[2]);
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mSensor != null) {
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            SensorManager sManager = (SensorManager) mContext
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    .getSystemService(Context.SENSOR_SERVICE);
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sManager.unregisterListener(mPositionListener);
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resume() {
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mSensor != null) {
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            SensorManager sManager = (SensorManager) mContext
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    .getSystemService(Context.SENSOR_SERVICE);
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sManager.registerListener(mPositionListener,
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mSensor, SensorManager.SENSOR_DELAY_GAME);
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mStartTime = NOT_STARTED;
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mGyroscopeCountdown = GYROSCOPE_SETTLE_DOWN;
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mX = mY = 0;
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mZ = -mUserDistance;
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListener.onEyePositionChanged(mX, mY, mZ);
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
227