2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.app;
19 import com.android.gallery3d.common.Utils;
20 import com.android.gallery3d.util.GalleryUtils;
22 import android.content.Context;
23 import android.hardware.Sensor;
24 import android.hardware.SensorEvent;
25 import android.hardware.SensorEventListener;
26 import android.hardware.SensorManager;
27 import android.os.SystemClock;
28 import android.util.FloatMath;
29 import android.view.Display;
30 import android.view.Surface;
31 import android.view.WindowManager;
33 public class EyePosition {
34 private static final String TAG = "EyePosition";
36 public interface EyePositionListener {
37 public void onEyePositionChanged(float x, float y, float z);
40 private static final float GYROSCOPE_THRESHOLD = 0.15f;
41 private static final float GYROSCOPE_LIMIT = 10f;
42 private static final int GYROSCOPE_SETTLE_DOWN = 15;
43 private static final float GYROSCOPE_RESTORE_FACTOR = 0.995f;
45 private static final double USER_ANGEL = Math.toRadians(10);
46 private static final float USER_ANGEL_COS = FloatMath.cos(USER_ANGEL);
47 private static final float USER_ANGEL_SIN = FloatMath.sin(USER_ANGEL);
48 private static final float MAX_VIEW_RANGE = (float) 0.5;
49 private static final int NOT_STARTED = -1;
51 private static final float USER_DISTANCE_METER = 0.3f;
53 private Context mContext;
54 private EyePositionListener mListener;
55 private Display mDisplay;
56 // The eyes' position of the user, the origin is at the center of the
57 // device and the unit is in pixels.
62 private final float mUserDistance; // in pixel
63 private final float mLimit;
64 private long mStartTime = NOT_STARTED;
65 private Sensor mSensor;
66 private PositionListener mPositionListener = new PositionListener();
68 private int mGyroscopeCountdown = 0;
70 public EyePosition(Context context, EyePositionListener listener) {
73 mUserDistance = GalleryUtils.meterToPixel(USER_DISTANCE_METER);
74 mLimit = mUserDistance * MAX_VIEW_RANGE;
76 WindowManager wManager = (WindowManager) mContext
77 .getSystemService(Context.WINDOW_SERVICE);
78 mDisplay = wManager.getDefaultDisplay();
80 // The 3D effect where the photo albums fan out in 3D based on angle
81 // of device tilt is currently disabled.
83 SensorManager sManager = (SensorManager) mContext
84 .getSystemService(Context.SENSOR_SERVICE);
85 mSensor = sManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
86 if (mSensor == null) {
87 Log.w(TAG, "no gyroscope, use accelerometer instead");
88 mSensor = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
90 if (mSensor == null) {
91 Log.w(TAG, "no sensor available");
96 public void resetPosition() {
97 mStartTime = NOT_STARTED;
100 mListener.onEyePositionChanged(mX, mY, mZ);
104 * We assume the user is at the following position
110 * / |/_____\ -Y (-y direction of device)
113 private void onAccelerometerChanged(float gx, float gy, float gz) {
115 float x = gx, y = gy, z = gz;
117 switch (mDisplay.getRotation()) {
118 case Surface.ROTATION_90: x = -gy; y= gx; break;
119 case Surface.ROTATION_180: x = -gx; y = -gy; break;
120 case Surface.ROTATION_270: x = gy; y = -gx; break;
123 float temp = x * x + y * y + z * z;
127 float ty = -1 + t * y;
130 float length = FloatMath.sqrt(tx * tx + ty * ty + tz * tz);
131 float glength = FloatMath.sqrt(temp);
133 mX = Utils.clamp((x * USER_ANGEL_COS / glength
134 + tx * USER_ANGEL_SIN / length) * mUserDistance,
136 mY = -Utils.clamp((y * USER_ANGEL_COS / glength
137 + ty * USER_ANGEL_SIN / length) * mUserDistance,
139 mZ = -FloatMath.sqrt(
140 mUserDistance * mUserDistance - mX * mX - mY * mY);
141 mListener.onEyePositionChanged(mX, mY, mZ);
144 private void onGyroscopeChanged(float gx, float gy, float gz) {
145 long now = SystemClock.elapsedRealtime();
146 float distance = (gx > 0 ? gx : -gx) + (gy > 0 ? gy : - gy);
147 if (distance < GYROSCOPE_THRESHOLD
148 || distance > GYROSCOPE_LIMIT || mGyroscopeCountdown > 0) {
149 --mGyroscopeCountdown;
151 float limit = mUserDistance / 20f;
152 if (mX > limit || mX < -limit || mY > limit || mY < -limit) {
153 mX *= GYROSCOPE_RESTORE_FACTOR;
154 mY *= GYROSCOPE_RESTORE_FACTOR;
155 mZ = (float) -Math.sqrt(
156 mUserDistance * mUserDistance - mX * mX - mY * mY);
157 mListener.onEyePositionChanged(mX, mY, mZ);
162 float t = (now - mStartTime) / 1000f * mUserDistance * (-mZ);
165 float x = -gy, y = -gx;
166 switch (mDisplay.getRotation()) {
167 case Surface.ROTATION_90: x = -gx; y= gy; break;
168 case Surface.ROTATION_180: x = gy; y = gx; break;
169 case Surface.ROTATION_270: x = gx; y = -gy; break;
172 mX = Utils.clamp((float) (mX + x * t / Math.hypot(mZ, mX)),
173 -mLimit, mLimit) * GYROSCOPE_RESTORE_FACTOR;
174 mY = Utils.clamp((float) (mY + y * t / Math.hypot(mZ, mY)),
175 -mLimit, mLimit) * GYROSCOPE_RESTORE_FACTOR;
177 mZ = -FloatMath.sqrt(
178 mUserDistance * mUserDistance - mX * mX - mY * mY);
179 mListener.onEyePositionChanged(mX, mY, mZ);
182 private class PositionListener implements SensorEventListener {
183 public void onAccuracyChanged(Sensor sensor, int accuracy) {
186 public void onSensorChanged(SensorEvent event) {
187 switch (event.sensor.getType()) {
188 case Sensor.TYPE_GYROSCOPE: {
190 event.values[0], event.values[1], event.values[2]);
193 case Sensor.TYPE_ACCELEROMETER: {
194 onAccelerometerChanged(
195 event.values[0], event.values[1], event.values[2]);
201 public void pause() {
202 if (mSensor != null) {
203 SensorManager sManager = (SensorManager) mContext
204 .getSystemService(Context.SENSOR_SERVICE);
205 sManager.unregisterListener(mPositionListener);
209 public void resume() {
210 if (mSensor != null) {
211 SensorManager sManager = (SensorManager) mContext
212 .getSystemService(Context.SENSOR_SERVICE);
213 sManager.registerListener(mPositionListener,
214 mSensor, SensorManager.SENSOR_DELAY_GAME);
217 mStartTime = NOT_STARTED;
218 mGyroscopeCountdown = GYROSCOPE_SETTLE_DOWN;
221 mListener.onEyePositionChanged(mX, mY, mZ);