• 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 com.android.gallery3d.app;
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.SystemClock;
25 import android.util.FloatMath;
26 import android.view.Display;
27 import android.view.Surface;
28 import android.view.WindowManager;
29 
30 import com.android.gallery3d.common.Utils;
31 import com.android.gallery3d.util.GalleryUtils;
32 
33 public class EyePosition {
34     @SuppressWarnings("unused")
35     private static final String TAG = "EyePosition";
36 
37     public interface EyePositionListener {
onEyePositionChanged(float x, float y, float z)38         public void onEyePositionChanged(float x, float y, float z);
39     }
40 
41     private static final float GYROSCOPE_THRESHOLD = 0.15f;
42     private static final float GYROSCOPE_LIMIT = 10f;
43     private static final int GYROSCOPE_SETTLE_DOWN = 15;
44     private static final float GYROSCOPE_RESTORE_FACTOR = 0.995f;
45 
46     private static final float USER_ANGEL = (float) Math.toRadians(10);
47     private static final float USER_ANGEL_COS = FloatMath.cos(USER_ANGEL);
48     private static final float USER_ANGEL_SIN = FloatMath.sin(USER_ANGEL);
49     private static final float MAX_VIEW_RANGE = (float) 0.5;
50     private static final int NOT_STARTED = -1;
51 
52     private static final float USER_DISTANCE_METER = 0.3f;
53 
54     private Context mContext;
55     private EyePositionListener mListener;
56     private Display mDisplay;
57     // The eyes' position of the user, the origin is at the center of the
58     // device and the unit is in pixels.
59     private float mX;
60     private float mY;
61     private float mZ;
62 
63     private final float mUserDistance; // in pixel
64     private final float mLimit;
65     private long mStartTime = NOT_STARTED;
66     private Sensor mSensor;
67     private PositionListener mPositionListener = new PositionListener();
68 
69     private int mGyroscopeCountdown = 0;
70 
EyePosition(Context context, EyePositionListener listener)71     public EyePosition(Context context, EyePositionListener listener) {
72         mContext = context;
73         mListener = listener;
74         mUserDistance = GalleryUtils.meterToPixel(USER_DISTANCE_METER);
75         mLimit = mUserDistance * MAX_VIEW_RANGE;
76 
77         WindowManager wManager = (WindowManager) mContext
78                 .getSystemService(Context.WINDOW_SERVICE);
79         mDisplay = wManager.getDefaultDisplay();
80 
81         // The 3D effect where the photo albums fan out in 3D based on angle
82         // of device tilt is currently disabled.
83 /*
84         SensorManager sManager = (SensorManager) mContext
85                 .getSystemService(Context.SENSOR_SERVICE);
86         mSensor = sManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
87         if (mSensor == null) {
88             Log.w(TAG, "no gyroscope, use accelerometer instead");
89             mSensor = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
90         }
91         if (mSensor == null) {
92             Log.w(TAG, "no sensor available");
93         }
94 */
95     }
96 
resetPosition()97     public void resetPosition() {
98         mStartTime = NOT_STARTED;
99         mX = mY = 0;
100         mZ = -mUserDistance;
101         mListener.onEyePositionChanged(mX, mY, mZ);
102     }
103 
104     /*
105      * We assume the user is at the following position
106      *
107      *              /|\  user's eye
108      *               |   /
109      *   -G(gravity) |  /
110      *               |_/
111      *             / |/_____\ -Y (-y direction of device)
112      *     user angel
113      */
onAccelerometerChanged(float gx, float gy, float gz)114     private void onAccelerometerChanged(float gx, float gy, float gz) {
115 
116         float x = gx, y = gy, z = gz;
117 
118         switch (mDisplay.getRotation()) {
119             case Surface.ROTATION_90: x = -gy; y= gx; break;
120             case Surface.ROTATION_180: x = -gx; y = -gy; break;
121             case Surface.ROTATION_270: x = gy; y = -gx; break;
122         }
123 
124         float temp = x * x + y * y + z * z;
125         float t = -y /temp;
126 
127         float tx = t * x;
128         float ty = -1 + t * y;
129         float tz = t * z;
130 
131         float length = FloatMath.sqrt(tx * tx + ty * ty + tz * tz);
132         float glength = FloatMath.sqrt(temp);
133 
134         mX = Utils.clamp((x * USER_ANGEL_COS / glength
135                 + tx * USER_ANGEL_SIN / length) * mUserDistance,
136                 -mLimit, mLimit);
137         mY = -Utils.clamp((y * USER_ANGEL_COS / glength
138                 + ty * USER_ANGEL_SIN / length) * mUserDistance,
139                 -mLimit, mLimit);
140         mZ = -FloatMath.sqrt(
141                 mUserDistance * mUserDistance - mX * mX - mY * mY);
142         mListener.onEyePositionChanged(mX, mY, mZ);
143     }
144 
onGyroscopeChanged(float gx, float gy, float gz)145     private void onGyroscopeChanged(float gx, float gy, float gz) {
146         long now = SystemClock.elapsedRealtime();
147         float distance = (gx > 0 ? gx : -gx) + (gy > 0 ? gy : - gy);
148         if (distance < GYROSCOPE_THRESHOLD
149                 || distance > GYROSCOPE_LIMIT || mGyroscopeCountdown > 0) {
150             --mGyroscopeCountdown;
151             mStartTime = now;
152             float limit = mUserDistance / 20f;
153             if (mX > limit || mX < -limit || mY > limit || mY < -limit) {
154                 mX *= GYROSCOPE_RESTORE_FACTOR;
155                 mY *= GYROSCOPE_RESTORE_FACTOR;
156                 mZ = (float) -Math.sqrt(
157                         mUserDistance * mUserDistance - mX * mX - mY * mY);
158                 mListener.onEyePositionChanged(mX, mY, mZ);
159             }
160             return;
161         }
162 
163         float t = (now - mStartTime) / 1000f * mUserDistance * (-mZ);
164         mStartTime = now;
165 
166         float x = -gy, y = -gx;
167         switch (mDisplay.getRotation()) {
168             case Surface.ROTATION_90: x = -gx; y= gy; break;
169             case Surface.ROTATION_180: x = gy; y = gx; break;
170             case Surface.ROTATION_270: x = gx; y = -gy; break;
171         }
172 
173         mX = Utils.clamp((float) (mX + x * t / Math.hypot(mZ, mX)),
174                 -mLimit, mLimit) * GYROSCOPE_RESTORE_FACTOR;
175         mY = Utils.clamp((float) (mY + y * t / Math.hypot(mZ, mY)),
176                 -mLimit, mLimit) * GYROSCOPE_RESTORE_FACTOR;
177 
178         mZ = -FloatMath.sqrt(
179                 mUserDistance * mUserDistance - mX * mX - mY * mY);
180         mListener.onEyePositionChanged(mX, mY, mZ);
181     }
182 
183     private class PositionListener implements SensorEventListener {
184         @Override
onAccuracyChanged(Sensor sensor, int accuracy)185         public void onAccuracyChanged(Sensor sensor, int accuracy) {
186         }
187 
188         @Override
onSensorChanged(SensorEvent event)189         public void onSensorChanged(SensorEvent event) {
190             switch (event.sensor.getType()) {
191                 case Sensor.TYPE_GYROSCOPE: {
192                     onGyroscopeChanged(
193                             event.values[0], event.values[1], event.values[2]);
194                     break;
195                 }
196                 case Sensor.TYPE_ACCELEROMETER: {
197                     onAccelerometerChanged(
198                             event.values[0], event.values[1], event.values[2]);
199                 }
200             }
201         }
202     }
203 
pause()204     public void pause() {
205         if (mSensor != null) {
206             SensorManager sManager = (SensorManager) mContext
207                     .getSystemService(Context.SENSOR_SERVICE);
208             sManager.unregisterListener(mPositionListener);
209         }
210     }
211 
resume()212     public void resume() {
213         if (mSensor != null) {
214             SensorManager sManager = (SensorManager) mContext
215                     .getSystemService(Context.SENSOR_SERVICE);
216             sManager.registerListener(mPositionListener,
217                     mSensor, SensorManager.SENSOR_DELAY_GAME);
218         }
219 
220         mStartTime = NOT_STARTED;
221         mGyroscopeCountdown = GYROSCOPE_SETTLE_DOWN;
222         mX = mY = 0;
223         mZ = -mUserDistance;
224         mListener.onEyePositionChanged(mX, mY, mZ);
225     }
226 }
227