/**
* Copyright 2008 - 2012
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.3.3
*/
package loon;
import loon.action.map.Config;
import loon.core.geom.Vector3f;
import loon.core.processes.RealtimeProcess;
import loon.core.processes.RealtimeProcessManager;
import loon.utils.MathUtils;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
//0.3.3版修改后重力感应方式,与原有的Screen重力感应方式不同,该类非绑定于Screen
public class LAccelerometer {
public static enum SensorDirection {
EMPTY, LEFT, RIGHT, UP, DOWN;
}
public interface Event {
public void onDirection(SensorDirection direction, float x, float y,
float z);
public void onShakeChanged(float force);
}
// 四方向手机朝向
private SensorDirection _direction = SensorDirection.EMPTY;
// 八方向手机朝向
private int _all_direction = Config.EMPTY;
private Event event;
private long lastUpdate;
private float currentX, currentY, currentZ, currenForce;
private float lastX, lastY, lastZ;
private float orientation, magnitude;
public LAccelerometer() {
}
private final void onOrientation(float x, float y, float z) {
// 换算手机翻转角度
orientation = 0;
magnitude = x * x + y * y;
if (magnitude * 4 >= z * z) {
float angle = MathUtils.atan2(-y, x) * MathUtils.RAD_TO_DEG;
orientation = 90 - MathUtils.round(angle);
while (orientation >= 360) {
orientation -= 360;
}
while (orientation < 0) {
orientation += 360;
}
}
// 将手机翻转角度转为手机朝向
if (CheckAngle(0, orientation) || CheckAngle(360, orientation)) {
_all_direction = Config.TUP;
_direction = SensorDirection.UP;
} else if (CheckAngle(45, orientation)) {
_all_direction = Config.LEFT;
_direction = SensorDirection.LEFT;
} else if (CheckAngle(90, orientation)) {
_all_direction = Config.TLEFT;
_direction = SensorDirection.LEFT;
} else if (CheckAngle(135, orientation)) {
_all_direction = Config.DOWN;
_direction = SensorDirection.LEFT;
} else if (CheckAngle(180, orientation)) {
_all_direction = Config.TDOWN;
_direction = SensorDirection.DOWN;
} else if (CheckAngle(225, orientation)) {
_all_direction = Config.RIGHT;
_direction = SensorDirection.RIGHT;
} else if (CheckAngle(270, orientation)) {
_all_direction = Config.TRIGHT;
_direction = SensorDirection.RIGHT;
} else if (CheckAngle(315, orientation)) {
_all_direction = Config.UP;
_direction = SensorDirection.RIGHT;
} else {
_all_direction = Config.EMPTY;
_direction = SensorDirection.EMPTY;
}
}
public static boolean CheckAngle(float angle, float actual) {
return actual > angle - 22.5f && actual < angle + 22.5f;
}
private final void onSensor(float[] values) {
synchronized (this) {
long curTime = System.currentTimeMillis();
if (LSystem.SCREEN_LANDSCAPE) {
currentX = -values[0];
currentY = -values[1];
currentZ = -values[2];
} else {
currentX = values[0];
currentY = values[1];
currentZ = values[2];
}
onOrientation(currentX, currentY, currentZ);
if ((curTime - lastUpdate) > 30) {
long diffTime = (curTime - lastUpdate);
lastUpdate = curTime;
currenForce = MathUtils.abs(currentX + currentY + currentZ
- lastX - lastY - lastZ)
/ diffTime * 10000;
if (currenForce > 500 && event != null) {
event.onShakeChanged(currenForce);
}
}
lastX = currentX;
lastY = currentY;
lastZ = currentZ;
if (event != null) {
event.onDirection(_direction, currentX, currentY, currentZ);
}
}
}
public class AccelerometerState {
boolean _isConnected;
final Vector3f _acceleration = new Vector3f();
public Vector3f getAcceleration() {
return _acceleration;
}
public boolean isConnected() {
return _isConnected;
}
}
private class SensorListener implements SensorEventListener {
final float[] accelerometerValues;
final float[] magneticFieldValues;
SensorListener(float[] accelerometerValues, float[] magneticFieldValues) {
this.accelerometerValues = accelerometerValues;
this.magneticFieldValues = magneticFieldValues;
}
@Override
public void onAccuracyChanged(Sensor a, int b) {
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
if (!LSystem.SCREEN_LANDSCAPE) {
System.arraycopy(event.values, 0, accelerometerValues, 0,
accelerometerValues.length);
} else {
accelerometerValues[0] = event.values[1];
accelerometerValues[1] = -event.values[0];
accelerometerValues[2] = event.values[2];
}
}
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
System.arraycopy(event.values, 0, magneticFieldValues, 0,
magneticFieldValues.length);
}
onSensor(accelerometerValues);
_state._acceleration.set(currentX, currentY, currentZ);
try {
Thread.sleep(_sleep);
} catch (InterruptedException e) {
}
}
}
class SensorProcess extends RealtimeProcess {
final float[] accelerometerValues;
public SensorProcess(float[] values) {
this.accelerometerValues = values;
this.setDelay(_sleep);
}
public void run() {
if (_state._isConnected) {
accelerometerValues[2] = -1f;
if (Key.isDown() && Key.getKeyCode() == Key.LEFT) {
accelerometerValues[0]--;
}
if (Key.isDown() && Key.getKeyCode() == Key.RIGHT) {
accelerometerValues[0]++;
}
if (Key.isDown() && Key.getKeyCode() == Key.UP) {
accelerometerValues[1]++;
}
if (Key.isDown() && Key.getKeyCode() == Key.DOWN) {
accelerometerValues[1]--;
}
onSensor(accelerometerValues);
_state._acceleration.set(currentX, currentY, currentZ);
}
}
}
private final AccelerometerState _state = new AccelerometerState();
private int _sleep = 30;
private SensorManager manager;
private final float[] accelerometerValues = new float[3];
private final float[] magneticFieldValues = new float[3];
private SensorEventListener accelerometerListener;
private SensorProcess sensorProcess;
public void start() {
// 模拟器下启动时键盘模拟重力
if (LSystem.isEmulator()) {
_state._isConnected = true;
sensorProcess = new SensorProcess(accelerometerValues);
RealtimeProcessManager.get().addProcess(sensorProcess);
return;
}
if (!_state._isConnected && manager == null) {
manager = (SensorManager) LSystem.getActivity().getSystemService(
Context.SENSOR_SERVICE);
if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() == 0) {
_state._isConnected = false;
} else {
Sensor accelerometer = manager.getSensorList(
Sensor.TYPE_ACCELEROMETER).get(0);
accelerometerListener = new SensorListener(
this.accelerometerValues, this.magneticFieldValues);
_state._isConnected = manager.registerListener(
accelerometerListener, accelerometer,
SensorManager.SENSOR_DELAY_GAME);
}
// 如果无法正常启动,则开启伪重力感应
if (!_state._isConnected) {
_state._isConnected = true;
sensorProcess = new SensorProcess(accelerometerValues);
RealtimeProcessManager.get().addProcess(sensorProcess);
}
}
}
public void stop() {
if (manager != null) {
if (accelerometerListener != null) {
manager.unregisterListener(accelerometerListener);
accelerometerListener = null;
}
manager = null;
_state._isConnected = false;
} else {
_state._isConnected = false;
}
}
public float getLastX() {
return lastX;
}
public float getLastY() {
return lastY;
}
public float getLastZ() {
return lastZ;
}
public float getX() {
return currentX;
}
public float getY() {
return currentY;
}
public float getZ() {
return currentZ;
}
public final AccelerometerState getState() {
return _state;
}
public int getSleep() {
return _sleep;
}
public void sleep(int sleep) {
this._sleep = sleep;
if (sensorProcess != null) {
sensorProcess.setDelay(sleep);
}
}
/**
* 屏幕方向(返回Config类中配置值)
*
* @return
*/
public int getAllDirection() {
return _all_direction;
}
public Event getEvent() {
return event;
}
/**
* 事件监听
*
* @param event
*/
public void setEvent(Event event) {
this.event = event;
}
/**
* 屏幕旋转度数
*
* @return
*/
public float getOrientation() {
return orientation;
}
/**
* 返回四方向手机朝向
*
* @return
*/
public SensorDirection getDirection() {
return _direction;
}
}