Back to project page p1keyboard.
The source code is released under:
GNU Lesser General Public License
If you think the Android project p1keyboard listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package mobi.omegacentauri.p1keyboard; /*from ww w . j a v a 2 s . co m*/ import java.lang.reflect.InvocationTargetException; import java.util.UUID; import mobi.omegacentauri.p1keyboard.R; import android.content.Context; import android.util.Log; import android.view.KeyEvent; public class GameStopReader extends RfcommReader { private static final boolean D = false; public static final String DRIVER_NAME = "gamestop"; public static final String DISPLAY_NAME = "GameStop Red Samurai Controller"; private final byte MAGIC_NUMBER = (byte)0xA1; private final byte MAGIC_NUMBER_MESSAGE = (byte)0x01; private final byte MAGIC_NUMBER_HEADER = (byte)0xfe; private final byte MAGIC_NUMBER_BATTERY= (byte)0xff; private final int MESSAGE_LENGTH = 8; private final int HEADER_LENGTH = 18; private final int BATTERY_LENGTH = 7; //The max value a nub can report private static int ANALOG_NUB_MAX_VALUE = 127; //How far the nub must be pressed for it to issue an emulated keypress private static int ANALOG_NUB_THRESHOLD = ANALOG_NUB_MAX_VALUE / 2; //This is the number of directions supported, hardcoded private static final int SUPPORTED_DIRECTIONS = 8; //This is the number of buttons supported, hardcoded private static final int SUPPORTED_BUTTONS = 14; //These three keep track of the last known state of buttons, directions and emulated direction-buttons private boolean[] m_buttons = new boolean[SUPPORTED_BUTTONS]; private int[] m_directions = new int[SUPPORTED_DIRECTIONS / 2]; private boolean[] m_lastDirectionsKeys = new boolean[SUPPORTED_DIRECTIONS]; //These are buffers that are used to read/parse input data, // they are reused to prevent re-allocation and garbage collections. //If they have the wrong size, they will be re-allocated once private int[] _directionValues = new int[SUPPORTED_DIRECTIONS / 2]; //This is the reason we only support 8 directions (and my device only has 4) private static final int[] ANALOG_KEYCODES = new int[] { KeyEvent.KEYCODE_W, //Left knob right KeyEvent.KEYCODE_A, //Left knob left KeyEvent.KEYCODE_S, //Left knob down KeyEvent.KEYCODE_D, //Left knob up KeyEvent.KEYCODE_6, //Right knob right KeyEvent.KEYCODE_4, //Right knob left KeyEvent.KEYCODE_5, //Right knob down KeyEvent.KEYCODE_8 //Right knob up }; //Mapping of reported scan-codes to Android keypress values private static final int[] KEYCODE_MAPPINGS = { FutureKeyCodes.KEYCODE_BUTTON_1, //Button 1 FutureKeyCodes.KEYCODE_BUTTON_2, //Button 2 FutureKeyCodes.KEYCODE_BUTTON_3, //Button 3 FutureKeyCodes.KEYCODE_BUTTON_4, //Button 4 FutureKeyCodes.KEYCODE_DPAD_UP, //DPAD Up FutureKeyCodes.KEYCODE_DPAD_LEFT, //DPAD Left FutureKeyCodes.KEYCODE_DPAD_DOWN, //DPAD Down FutureKeyCodes.KEYCODE_DPAD_RIGHT, //DPAD Right FutureKeyCodes.KEYCODE_BUTTON_SELECT, //Select FutureKeyCodes.KEYCODE_BUTTON_START, //Start FutureKeyCodes.KEYCODE_BUTTON_5, //L3 FutureKeyCodes.KEYCODE_BUTTON_6, //R3 FutureKeyCodes.KEYCODE_BUTTON_L1, //L1 FutureKeyCodes.KEYCODE_BUTTON_L2, //R1 FutureKeyCodes.KEYCODE_BUTTON_R1, //L2 FutureKeyCodes.KEYCODE_BUTTON_R2 //R2 }; public GameStopReader(String address, String sessionId, Context context, boolean startnotification) throws Exception { super(address, sessionId, context, startnotification); } @Override public String getDriverName() { return DRIVER_NAME; } @Override protected int parseInputData(byte[] data, int read) { int offset = 0; int remaining = read; //This should always be true while (remaining > 3 && data[offset + 0] == MAGIC_NUMBER) { if (data[offset + 1] == MAGIC_NUMBER_BATTERY) { remaining -= BATTERY_LENGTH; offset += BATTERY_LENGTH; } else if (data[offset + 1] == MAGIC_NUMBER_HEADER) { remaining -= HEADER_LENGTH; offset += HEADER_LENGTH; } else if (data[offset + 1] == MAGIC_NUMBER_MESSAGE) { int buttons = ((data[offset + 6] & 0xff) << 8) | (data[offset + 7] & 0xff); //For some strange reason, the UP bit is flipped int up = (buttons & (1 << 8)) == 0 ? 1 : 0; buttons = (buttons & ~(1 << 8)) | (up << 8); _directionValues[0] = (data[offset + 2] & 0xff) - 0x80; _directionValues[1] = (data[offset + 3] & 0xff) - 0x80; _directionValues[2] = (data[offset + 4] & 0xff) - 0x80; _directionValues[3] = (data[offset + 5] & 0xff) - 0x80; for(int i = 0; i < m_buttons.length; i++) { boolean state = (buttons & (1 << (15 - i))) != 0; if (state != m_buttons[i]) { m_buttons[i] = state; keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_ACTION, state ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP); keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_KEY, KEYCODE_MAPPINGS[i]); keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_MODIFIERS, 0); keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_ANALOG_EMULATED, false); m_context.sendBroadcast(keypressBroadcast); } } for(int i = 0; i < m_directions.length; i++) { boolean large = _directionValues[i] > ANALOG_NUB_THRESHOLD; boolean small = _directionValues[i] < -ANALOG_NUB_THRESHOLD; if (large != m_lastDirectionsKeys[i * 2]) { m_lastDirectionsKeys[i * 2] = large; keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_ACTION, large ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP); keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_KEY, ANALOG_KEYCODES[i]); keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_ANALOG_EMULATED, true); m_context.sendBroadcast(keypressBroadcast); } if (small != m_lastDirectionsKeys[(i * 2) + 1]) { m_lastDirectionsKeys[(i * 2) + 1] = small; keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_ACTION, small ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP); keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_KEY, ANALOG_KEYCODES[i]); keypressBroadcast.putExtra(BluezService.EVENT_KEYPRESS_ANALOG_EMULATED, true); m_context.sendBroadcast(keypressBroadcast); } if (_directionValues[i] != m_directions[i]) { directionBroadcast.putExtra(BluezService.EVENT_DIRECTIONALCHANGE_DIRECTION, i); directionBroadcast.putExtra(BluezService.EVENT_DIRECTIONALCHANGE_VALUE, _directionValues[i]); m_context.sendBroadcast(directionBroadcast); m_directions[i] = _directionValues[i]; } } remaining -= MESSAGE_LENGTH; offset += MESSAGE_LENGTH; } else { //Ditch the rest remaining = 0; } } return remaining; } @Override protected int setupConnection(ImprovedBluetoothDevice device, byte[] readBuffer) throws Exception { try { //Most devices supports using the reflection method if (D) Log.d(getDriverName(), "Attempting reflection connect"); return super.setupConnection(device, readBuffer); } catch (Exception ex) { if (D) Log.d(getDriverName(), "Reflection connect failed, error: " + ex.getMessage()); if (D && ex instanceof InvocationTargetException) { InvocationTargetException tex = (InvocationTargetException)ex; Log.e(getDriverName(), "TargetInvocation cause: " + (tex.getCause() == null ? "<null>" : tex.getCause().toString())); Log.e(getDriverName(), "TargetInvocation target: " + (tex.getTargetException() == null ? "<null>" : tex.getTargetException().toString())); } try { if (D) Log.d(getDriverName(), "Attempting createRfcommSocketToServiceRecord connect"); //In case the reflection method was not present, we try the correct method m_socket = device.createRfcommSocketToServiceRecord(UUID.fromString("8e1f0cf7-508f-4875-b62c-fbb67fd34812")); m_socket.connect(); if (D) Log.d(getDriverName(), "Connected with createRfcommSocketToServiceRecord() to " + m_address); m_input = m_socket.getInputStream(); return m_input.read(readBuffer); } catch (Exception ex2) { if (D) Log.e(getDriverName(), "Failed on createRfcommSocketToServiceRecord: " + ex2.getMessage()); //Report the original error, not the secondary throw ex; } } } @Override protected void validateWelcomeMessage(byte[] data, int read) { //TODO: Find some documentation that explains how to parse the message } public static int[] getButtonCodes() { return new int[] { FutureKeyCodes.KEYCODE_DPAD_UP, //DPAD Up FutureKeyCodes.KEYCODE_DPAD_LEFT, //DPAD Left FutureKeyCodes.KEYCODE_DPAD_DOWN, //DPAD Down FutureKeyCodes.KEYCODE_DPAD_RIGHT, //DPAD Right FutureKeyCodes.KEYCODE_BUTTON_1, //Button 1 FutureKeyCodes.KEYCODE_BUTTON_2, //Button 2 FutureKeyCodes.KEYCODE_BUTTON_3, //Button 3 FutureKeyCodes.KEYCODE_BUTTON_4, //Button 4 FutureKeyCodes.KEYCODE_BUTTON_SELECT, //Select FutureKeyCodes.KEYCODE_BUTTON_START, //Start FutureKeyCodes.KEYCODE_BUTTON_5, //L3 FutureKeyCodes.KEYCODE_BUTTON_6, //R3 FutureKeyCodes.KEYCODE_BUTTON_L1, //L1 FutureKeyCodes.KEYCODE_BUTTON_L2, //R1 FutureKeyCodes.KEYCODE_BUTTON_R1, //L2 FutureKeyCodes.KEYCODE_BUTTON_R2, //R2 KeyEvent.KEYCODE_W, //Left knob right KeyEvent.KEYCODE_A, //Left knob left KeyEvent.KEYCODE_S, //Left knob down KeyEvent.KEYCODE_D, //Left knob up KeyEvent.KEYCODE_6, //Right knob right KeyEvent.KEYCODE_4, //Right knob left KeyEvent.KEYCODE_5, //Right knob down KeyEvent.KEYCODE_8 //Right knob up }; } public static int[] getButtonNames() { return new int[] { R.string.gamestop_dpad_up, R.string.gamestop_dpad_left, R.string.gamestop_dpad_down, R.string.gamestop_dpad_right, R.string.gamestop_button_1, R.string.gamestop_button_2, R.string.gamestop_button_3, R.string.gamestop_button_4, R.string.gamestop_button_start, R.string.gamestop_button_select, R.string.gamestop_button_l3, R.string.gamestop_button_r3, R.string.gamestop_button_l1, R.string.gamestop_button_l2, R.string.gamestop_button_r1, R.string.gamestop_button_r2, R.string.gamestop_leftknob_left, R.string.gamestop_leftknob_right, R.string.gamestop_leftknob_up, R.string.gamestop_leftknob_down, R.string.gamestop_rightknob_left, R.string.gamestop_rightknob_right, R.string.gamestop_rightknob_up, R.string.gamestop_rightknob_down, }; } }