package com.android.internal.widget; import android.content.Context; import android.hardware.input.InputManager; import android.hardware.input.InputManager.InputDeviceListener; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.util.Log; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; public class SMouseTouchView extends View implements InputDeviceListener, SensorEventListener { private static final String TAG = "SMouseTouchView"; public static final boolean DEBUG = true; /* Device accessor */ private SMouseNative mSMouseNative; /* Members related touches */ private static final int SCREEN_WIDTH = 1080; // Nexus 5 private static final int SCREEN_HEIGHT = 1920; // Nexus 5 private final InputManager mIm; private int curWheel; private float curCoord; /* Members related sensors */ private SensorManager mSensorManager; private Sensor mAccelerometer; private Sensor mGyroscope; private Sensor mGravity; private float mVx; private float mVy; private float[] mGravityData; private float[] mAccelData; private float[] mGyroData; private int mAccelChangedCnt; private int mGravityChangedCnt; private float mCalibrationX; private float mCalibrationY; private float mCalibrationG; private int mGyroMovementFlag; private int mGravityMovementFlag; private int mSavedReactiveTime; private int mEndCheckCnt; private int mEndCheckFlag; /** * Constructor */ public SMouseTouchView(Context context) { super(context); /* Init device accessor */ mSMouseNative = new SMouseNative(); mSMouseNative.openDevice(); // Open device /* Init member variables */ setFocusableInTouchMode(true); mIm = (InputManager)context.getSystemService(Context.INPUT_SERVICE); mVx = 0; mVy = 0; mGravityData = new float[3]; mAccelData = new float[3]; mGyroData = new float[3]; mAccelChangedCnt = 0; mGravityChangedCnt = 0; mCalibrationX = 0.0f; mCalibrationY = 0.0f; mCalibrationG = 0.0f; mGyroMovementFlag = 0; mGravityMovementFlag = 0; mEndCheckCnt = 0; mEndCheckFlag = 0; /* Init sensors listener */ mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION); mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST); mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_FASTEST); mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_FASTEST); } /** * Destructor (User-defined) */ public void destroyView() { mSMouseNative.closeDevice(); // Close device mSMouseNative = null; mSensorManager.unregisterListener(this); } /** * Touches evnets handler */ public void addPointerEvent(MotionEvent event) { final int action = event.getAction(); final float x = SCREEN_WIDTH - event.getRawX(); final float y = SCREEN_HEIGHT - event.getRawY(); if (action == MotionEvent.ACTION_DOWN) { if (y > SCREEN_HEIGHT / 4) { return; } else if (x < SCREEN_WIDTH / 2 - 100) { // Left Button Down if (DEBUG) { Log.d(TAG, "Left Button Down"); } if (mSMouseNative != null && mSMouseNative.isOpened()) { mSMouseNative.writeValues(1, 0, 0, 0); } } else if (x > SCREEN_WIDTH / 2 + 100) { // Right Button Down if (DEBUG) { Log.d(TAG, "Right Button Down"); } if (mSMouseNative != null && mSMouseNative.isOpened()) { mSMouseNative.writeValues(2, 0, 0, 0); } } else { // Wheel Detect curCoord = y; curWheel = 0; if (DEBUG) { Log.d(TAG, "Wheel Detect " + curWheel); } } } else if (action == MotionEvent.ACTION_UP) { if (y > SCREEN_HEIGHT / 4) { return; } else if (x < SCREEN_WIDTH / 2 - 100) { // Left Button Up if (DEBUG) { Log.d(TAG, "Left Button Up"); } if (mSMouseNative != null && mSMouseNative.isOpened()) { mSMouseNative.writeValues(4, 0, 0, 0); } } else if (x > SCREEN_WIDTH / 2 + 100) { // Right Button Up if (DEBUG) { Log.d(TAG, "Right Button Up"); } if (mSMouseNative != null && mSMouseNative.isOpened()) { mSMouseNative.writeValues(5, 0, 0, 0); } } else { return; } } else if (action == MotionEvent.ACTION_MOVE) { if (y > SCREEN_HEIGHT / 4) return; else if (x < SCREEN_WIDTH / 2 - 100) return; else if (x > SCREEN_WIDTH / 2 + 100) return; else { if (curCoord - y > SCREEN_HEIGHT / 4 / 10) { // Wheel Up curCoord = y; curWheel++; if (DEBUG) { Log.d(TAG, "Wheel Up " + curWheel); } if (mSMouseNative != null && mSMouseNative.isOpened()) { mSMouseNative.writeValues(0, 1, 0, 0); } } else if (curCoord - y < -1 * (SCREEN_HEIGHT / 4 / 10)) { // Wheel Down curCoord = y; curWheel--; if (DEBUG) { Log.d(TAG, "Wheel Down " + curWheel); } if (mSMouseNative != null && mSMouseNative.isOpened()) { mSMouseNative.writeValues(0, -1, 0, 0); } } else { return; } } } else { } } @Override public boolean onTouchEvent(MotionEvent event) { addPointerEvent(event); if (event.getAction() == MotionEvent.ACTION_DOWN && !isFocused()) requestFocus(); return true; } @Override public boolean onGenericMotionEvent(MotionEvent event) { final int source = event.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) addPointerEvent(event); return true; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mIm.registerInputDeviceListener(this, getHandler()); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mIm.unregisterInputDeviceListener(this); } @Override public void onInputDeviceAdded(int deviceId) { } @Override public void onInputDeviceChanged(int deviceId) { } @Override public void onInputDeviceRemoved(int deviceId) { } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSensorChanged(SensorEvent event) { /** * LINEAR ACCELERATION Sensor */ if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) { mAccelChangedCnt++; /* #1 Reactive Lock Filter */ final int reactiveLockTerm = 30; if (mGravityMovementFlag == 3) { mSavedReactiveTime = mAccelChangedCnt; mGravityMovementFlag = 2; return; } else if (mGravityMovementFlag == 2 && mAccelChangedCnt < mSavedReactiveTime + reactiveLockTerm) { return; } else if (mGravityMovementFlag == 2 && mAccelChangedCnt == mSavedReactiveTime + reactiveLockTerm) { mGravityMovementFlag = 1; } else ; /* #2 Get Values */ mAccelData[0] = event.values[0]; mAccelData[1] = event.values[1]; /* #3 Calibration */ final float calibrationInit = 50; final float calibrationTerm = 100; if (mAccelChangedCnt < calibrationInit) { return; } else if (mAccelChangedCnt < calibrationInit + calibrationTerm) { mCalibrationX += mAccelData[0]; mCalibrationY += mAccelData[1]; return; } else if (mAccelChangedCnt == calibrationInit + calibrationTerm) { mCalibrationX /= 100; mCalibrationY /= 100; return; } else { mAccelData[0] -= mCalibrationX; mAccelData[1] -= mCalibrationY; } /* #4 Mechanical Filtering Window */ final float window = 0.06f; final int endCheckTime = 3; if ( (mAccelData[0] <= window && mAccelData[0] >= -window) && (mAccelData[1] <= window && mAccelData[1] >= -window) ) { mAccelData[0] = 0.0f; mAccelData[1] = 0.0f; /* #5 Movement End Check */ mEndCheckCnt++; if (mEndCheckFlag == 0) { mEndCheckFlag = 1; mEndCheckCnt = 1; } else if (mEndCheckCnt == endCheckTime) { mVx = 0.0f; mVy = 0.0f; } else ; } else if (mEndCheckFlag == 1) { mEndCheckFlag = 0; } else ; /* #6 Positioning */ final float sampleTime = 0.007f; final int scaleUp = 1000000; float moveX = (float) ((mVx * sampleTime) + (0.5 * mAccelData[0] * sampleTime * sampleTime)); float moveY = (float) ((mVy * sampleTime) + (0.5 * mAccelData[1] * sampleTime * sampleTime)); int xferX = (int) (moveX * scaleUp); int xferY = (int) (moveY * scaleUp); if (mGyroMovementFlag == 1 && mGravityMovementFlag == 1) { mVx = mVx + (mAccelData[0] * sampleTime); mVy = mVy + (mAccelData[1] * sampleTime); if (xferX != 0 && xferY != 0) { if (DEBUG) { Log.d(TAG, "Move X " + xferX + "\tY " + xferY); } if (mSMouseNative != null && mSMouseNative.isOpened()) { mSMouseNative.writeValues(0, 0, xferX, xferY); } } } else { mVx = 0.0f; mVy = 0.0f; } /** * GYROSCOPE Sensor */ } else if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { mGyroData[0] = event.values[0]; mGyroData[1] = event.values[1]; mGyroData[2] = event.values[2]; /* Gyroscope Movement Check */ if ( (Math.round(mGyroData[0] * 100d) == 0) && (Math.round(mGyroData[1] * 100d) == 0) && (Math.round(mGyroData[2] * 100d) == 0) ) mGyroMovementFlag = 0; else mGyroMovementFlag = 1; /** * GRAVITY Sensor */ } else if (event.sensor.getType() == Sensor.TYPE_GRAVITY) { mGravityChangedCnt++; mGravityData[0] = event.values[0]; mGravityData[1] = event.values[1]; mGravityData[2] = event.values[2]; /* Gravity Movement Check */ final float calibrationInit = 50; final float calibrationTerm = 100; final float window = 0.007f; if (mGravityChangedCnt < calibrationInit) { } else if (mGravityChangedCnt < calibrationInit + calibrationTerm) { mCalibrationG += mGravityData[2]; } else if (mGravityChangedCnt == calibrationInit + calibrationTerm) { mCalibrationG /= 100; } else { if ( ((mGravityData[2] - mCalibrationG) <= window) && ((mGravityData[2] - mCalibrationG) >= -window) ) { if (mGravityMovementFlag == 0) // Reactive mGravityMovementFlag = 3; else // Keep Active or Keep Lock ; } else mGravityMovementFlag = 0; } } else { } } /** * Load native functions for access device */ static { System.loadLibrary("smouse"); } private static native int nativeOpenDevice(); private static native int nativeCloseDevice(int fd); private static native int nativeWriteValues(int fd, int btn, int wheel, int moveX, int moveY); /** * Device access class */ class SMouseNative { private static final String TAG = "SMouseTouchView.SMouseNative"; private int mFD; private boolean mIsOpened; public SMouseNative() { mFD = -1; mIsOpened = false; } public boolean openDevice() { if (mIsOpened) { Log.e(TAG, "Couldn't open device, device already opened."); return true; } mFD = nativeOpenDevice(); if (mFD == -1) { Log.e(TAG, "Couldn't open device, device open failed."); return false; } mIsOpened = true; if (DEBUG) { Log.d(TAG, "Device opened."); } return true; } public boolean closeDevice() { if (!mIsOpened) { Log.e(TAG, "Couldn't close device, device not opened."); return false; } nativeCloseDevice(mFD); mIsOpened = false; if (DEBUG) { Log.d(TAG, "Device closed. " + mFD); } return true; } public boolean writeValues(int btn, int wheel, int moveX, int moveY) { if (!mIsOpened) { Log.e(TAG, "Couldn't write values, device not opened."); return false; } nativeWriteValues(mFD, btn, wheel, moveX, moveY); if (DEBUG) { Log.d(TAG, "Write values. " + mFD + " " + btn + " " + wheel + " " + moveX + " " + moveY); } return true; } public boolean isOpened() { return mIsOpened; } } }