updated InputStick API and added more languages (change by Jakub)

updated version code
This commit is contained in:
Philipp Crocoll 2014-09-29 21:29:52 +02:00
parent 97bcf753a4
commit 3c5c9105b7
29 changed files with 1235 additions and 249 deletions

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.inputstick.api" package="com.inputstick.api"
android:versionCode="1" android:versionCode="1"

View File

@ -10,11 +10,30 @@ package com.inputstick.api;
public final class R { public final class R {
public static final class attr { public static final class attr {
} }
public static final class dimen {
/** Default screen margins, per the Android Design guidelines.
Customize dimensions originally defined in res/values/dimens.xml (such as
screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
*/
public static int activity_horizontal_margin=0x7f030000;
public static int activity_vertical_margin=0x7f030001;
}
public static final class drawable { public static final class drawable {
public static int ic_launcher=0x7f020000; public static int ic_launcher=0x7f020000;
} }
public static final class id {
public static int action_settings=0x7f070000;
}
public static final class menu {
public static int install_utility=0x7f060000;
}
public static final class string { public static final class string {
public static int app_name=0x7f030000; public static int action_settings=0x7f040002;
public static int app_name=0x7f040000;
public static int hello_world=0x7f040003;
public static int title_activity_install_utility=0x7f040001;
} }
public static final class style { public static final class style {
/** /**
@ -38,10 +57,10 @@ public final class R {
API 14 theme customizations can go here. API 14 theme customizations can go here.
*/ */
public static int AppBaseTheme=0x7f040000; public static int AppBaseTheme=0x7f050000;
/** Application theme. /** Application theme.
All customizations that are NOT specific to a particular API-level can go here. All customizations that are NOT specific to a particular API-level can go here.
*/ */
public static int AppTheme=0x7f040001; public static int AppTheme=0x7f050001;
} }
} }

View File

@ -11,5 +11,5 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target. # Project target.
target=android-19 target=android-20
android.library=true android.library=true

View File

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">InputStickAPI</string> <string name="app_name">InputStickAPI</string>
<string name="title_activity_install_utility">Download InputStickUtility</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
</resources> </resources>

View File

@ -10,8 +10,10 @@ public class AES {
private Cipher mCipherEncr; private Cipher mCipherEncr;
private Cipher mCipherDecr; private Cipher mCipherDecr;
private SecretKeySpec mKey; private SecretKeySpec mKey;
private boolean ready;
public AES() { public AES() {
ready = false;
} }
public static byte[] getMD5(String s) { public static byte[] getMD5(String s) {
@ -31,10 +33,10 @@ public class AES {
mCipherEncr = Cipher.getInstance("AES/CBC/NoPadding"); mCipherEncr = Cipher.getInstance("AES/CBC/NoPadding");
mCipherEncr.init(Cipher.ENCRYPT_MODE, mKey); mCipherEncr.init(Cipher.ENCRYPT_MODE, mKey);
iv = mCipherEncr.getIV(); iv = mCipherEncr.getIV();
//System.out.println("AES IV: "); Util.printHex(iv, "AES IV: ");
Util.printHex(iv);
mCipherDecr = Cipher.getInstance("AES/CBC/NoPadding"); mCipherDecr = Cipher.getInstance("AES/CBC/NoPadding");
mCipherDecr.init(Cipher.DECRYPT_MODE, mKey, new IvParameterSpec(iv)); mCipherDecr.init(Cipher.DECRYPT_MODE, mKey, new IvParameterSpec(iv));
ready = true;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -49,4 +51,7 @@ public class AES {
return mCipherDecr.update(data); return mCipherDecr.update(data);
} }
public boolean isReady() {
return ready;
}
} }

View File

@ -21,7 +21,6 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
private Application mApp; private Application mApp;
private BTService mBTService; private BTService mBTService;
private PacketManager mPacketManager; private PacketManager mPacketManager;
//private PacketQueue mPacketQueue;
private final BTHandler mBTHandler = new BTHandler(this); private final BTHandler mBTHandler = new BTHandler(this);
@ -45,31 +44,12 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
break; break;
case BTService.EVENT_CANCELLED: case BTService.EVENT_CANCELLED:
manager.onDisconnected(); manager.onDisconnected();
break; break;
case BTService.EVENT_CONNECTION_FAILED: case BTService.EVENT_ERROR:
manager.onFailure(1); manager.onFailure(msg.arg1);
break; break;
case BTService.EVENT_CONNECTION_LOST: default:
manager.onFailure(1); manager.onFailure(InputStickError.ERROR_BLUETOOTH);
break;
case BTService.EVENT_NO_BT_HW:
manager.onFailure(1);
break;
case BTService.EVENT_INVALID_MAC:
manager.onFailure(1);
break;
case BTService.EVENT_CMD_TIMEOUT:
manager.onFailure(1);
break;
case BTService.EVENT_INTERVAL_TIMEOUT:
manager.onFailure(1);
break;
case BTService.EVENT_TURN_ON_TIMEOUT:
manager.onFailure(1);
break;
case BTService.EVENT_OTHER_ERROR:
manager.onFailure(1);
break;
} }
} }
} }
@ -80,6 +60,7 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
private void onConnected() { private void onConnected() {
stateNotify(ConnectionManager.STATE_CONNECTED); stateNotify(ConnectionManager.STATE_CONNECTED);
//mInitManager.startTimeoutCountdown(InitManager.DEFAULT_INIT_TIMEOUT);
mInitManager.onConnected(); mInitManager.onConnected();
} }
@ -89,7 +70,8 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
private void onFailure(int code) { private void onFailure(int code) {
mErrorCode = code; mErrorCode = code;
stateNotify(ConnectionManager.STATE_FAILURE); stateNotify(ConnectionManager.STATE_FAILURE);
disconnect();
} }
private void onData(byte[] rawData) { private void onData(byte[] rawData) {
@ -97,16 +79,15 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
data = mPacketManager.bytesToPacket(rawData); data = mPacketManager.bytesToPacket(rawData);
if (data == null) { if (data == null) {
//TODO //TODO failure?
return; return;
} }
mInitManager.onData(data); mInitManager.onData(data);
//sendNext(); TODO
for (InputStickDataListener listener : mDataListeners) { for (InputStickDataListener listener : mDataListeners) {
listener.onInputStickData(data); listener.onInputStickData(data);
} }
} }
@ -124,7 +105,7 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
public void connect(boolean reflection, int timeout) { public void connect(boolean reflection, int timeout) {
mErrorCode = ConnectionManager.ERROR_NONE; mErrorCode = InputStickError.ERROR_NONE;
if (mBTService == null) { if (mBTService == null) {
mBTService = new BTService(mApp, mBTHandler); mBTService = new BTService(mApp, mBTHandler);
mPacketManager = new PacketManager(mBTService, mKey); mPacketManager = new PacketManager(mBTService, mKey);
@ -143,10 +124,14 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
} }
} }
public void disconnect(int failureCode) {
onFailure(failureCode);
}
@Override @Override
public void sendPacket(Packet p) { public void sendPacket(Packet p) {
mPacketManager.sendPacket(p); //TODO tmp; zalozmy z beda same NO_RESP ??? mPacketManager.sendPacket(p);
} }
@ -162,7 +147,7 @@ public class BTConnectionManager extends ConnectionManager implements InitManage
@Override @Override
public void onInitFailure(int code) { public void onInitFailure(int code) {
onFailure(code); onFailure(code);
} }
} }

View File

@ -11,13 +11,6 @@ public abstract class ConnectionManager {
public static final int STATE_READY = 4; public static final int STATE_READY = 4;
public static final int ERROR_NONE = 0;
public static final int ERROR_UNSUPPORTED_FIRMWARE = 10;
public static final int ERROR_PASSWORD_PROTECTED = 11;
public static final int ERROR_INVALID_KEY = 12;
protected Vector<InputStickStateListener> mStateListeners = new Vector<InputStickStateListener>(); protected Vector<InputStickStateListener> mStateListeners = new Vector<InputStickStateListener>();
protected Vector<InputStickDataListener> mDataListeners = new Vector<InputStickDataListener>(); protected Vector<InputStickDataListener> mDataListeners = new Vector<InputStickDataListener>();
@ -47,7 +40,9 @@ public abstract class ConnectionManager {
public void addStateListener(InputStickStateListener listener) { public void addStateListener(InputStickStateListener listener) {
if (listener != null) { if (listener != null) {
mStateListeners.add(listener); if ( !mStateListeners.contains(listener)) {
mStateListeners.add(listener);
}
} }
} }
@ -59,7 +54,9 @@ public abstract class ConnectionManager {
public void addDataListener(InputStickDataListener listener) { public void addDataListener(InputStickDataListener listener) {
if (listener != null) { if (listener != null) {
mDataListeners.add(listener); if ( !mDataListeners.contains(listener)) {
mDataListeners.add(listener);
}
} }
} }

View File

@ -15,10 +15,16 @@ public class HIDInfo {
private boolean mouseReady; private boolean mouseReady;
private boolean consumerReady; private boolean consumerReady;
// >= 0.93
private boolean sentToHostInfo;
private int keyboardReportsSentToHost;
private int mouseReportsSentToHost;
private int consumerReportsSentToHost;
public HIDInfo() { public HIDInfo() {
keyboardReportProtocol = true; keyboardReportProtocol = true;
mouseReportProtocol = true; mouseReportProtocol = true;
sentToHostInfo = false;
} }
public void update(byte[] data) { public void update(byte[] data) {
@ -70,6 +76,14 @@ public class HIDInfo {
} else { } else {
consumerReady = true; consumerReady = true;
} }
if (data.length >= 12) {
if (data[11] == (byte)0xFF) {
sentToHostInfo = true;
keyboardReportsSentToHost = data[8] & 0xFF;
mouseReportsSentToHost = data[9] & 0xFF;
consumerReportsSentToHost = data[10] & 0xFF;
}
}
} }
public void setKeyboardBusy() { public void setKeyboardBusy() {
@ -112,4 +126,24 @@ public class HIDInfo {
return consumerReady; return consumerReady;
} }
// > v0.93 firmware only
public boolean isSentToHostInfoAvailable() {
return sentToHostInfo;
}
public int getKeyboardReportsSentToHost() {
return keyboardReportsSentToHost;
}
public int getMouseReportsSentToHost() {
return mouseReportsSentToHost;
}
public int getConsumerReportsSentToHost() {
return consumerReportsSentToHost;
}
} }

View File

@ -52,7 +52,6 @@ public class IPCConnectionManager extends ConnectionManager {
} }
break; break;
case SERVICE_CMD_STATE: case SERVICE_CMD_STATE:
//System.out.println("CMD STATE: "+msg.arg1);
manager.stateNotify(msg.arg1); manager.stateNotify(msg.arg1);
break; break;
} }
@ -61,7 +60,6 @@ public class IPCConnectionManager extends ConnectionManager {
private ServiceConnection mConnection = new ServiceConnection() { private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
//System.out.println("onServiceConnected!");
mService = new Messenger(service); mService = new Messenger(service);
mBound = true; mBound = true;
sendMessage(SERVICE_CMD_CONNECT, 0, 0); sendMessage(SERVICE_CMD_CONNECT, 0, 0);
@ -69,9 +67,10 @@ public class IPCConnectionManager extends ConnectionManager {
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
// unexpectedly disconnected from service // unexpectedly disconnected from service
//System.out.println("onService DISCONNECTED!");
mService = null; mService = null;
mBound = false; mBound = false;
mErrorCode = InputStickError.ERROR_ANDROID_SERVICE_DISCONNECTED;
stateNotify(STATE_FAILURE);
stateNotify(STATE_DISCONNECTED); stateNotify(STATE_DISCONNECTED);
} }
}; };
@ -130,34 +129,32 @@ public class IPCConnectionManager extends ConnectionManager {
} }
if (exists) { if (exists) {
mErrorCode = ConnectionManager.ERROR_NONE; mErrorCode = InputStickError.ERROR_NONE;
Intent intent = new Intent(); Intent intent = new Intent();
intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService")); intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService"));
mCtx.startService(intent); mCtx.startService(intent);
mCtx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); mCtx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
if (mBound) { if (mBound) {
//already bound? //already bound
//System.out.println("Service already Connected");
sendMessage(SERVICE_CMD_CONNECT, 0, 0); sendMessage(SERVICE_CMD_CONNECT, 0, 0);
} }
} else { } else {
mErrorCode = 1; //TODO mErrorCode = InputStickError.ERROR_ANDROID_NO_UTILITY_APP;
stateNotify(STATE_FAILURE); stateNotify(STATE_FAILURE);
stateNotify(STATE_DISCONNECTED);
} }
} }
@Override @Override
public void disconnect() { public void disconnect() {
if (mBound) { if (mBound) {
//System.out.println("UNBIND");
sendMessage(SERVICE_CMD_DISCONNECT, 0, 0); sendMessage(SERVICE_CMD_DISCONNECT, 0, 0);
Intent intent = new Intent(); Intent intent = new Intent();
intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService")); intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService"));
mCtx.unbindService(mConnection); mCtx.unbindService(mConnection);
mCtx.stopService(intent); mCtx.stopService(intent);
mBound = false; mBound = false;
//TODO stateNotify //service will pass notification message (disconnected)
//service will pass notification message
} else { } else {
//just set state, there is nothing else to do //just set state, there is nothing else to do
stateNotify(STATE_DISCONNECTED); stateNotify(STATE_DISCONNECTED);
@ -172,9 +169,8 @@ public class IPCConnectionManager extends ConnectionManager {
} else { } else {
sendMessage(IPCConnectionManager.SERVICE_CMD_DATA, 0, 0, p.getBytes()); sendMessage(IPCConnectionManager.SERVICE_CMD_DATA, 0, 0, p.getBytes());
} }
} }
} }
} }

View File

@ -22,6 +22,12 @@ public class Packet {
public static final byte CMD_FW_INFO = 0x10; public static final byte CMD_FW_INFO = 0x10;
public static final byte CMD_INIT = 0x11; public static final byte CMD_INIT = 0x11;
public static final byte CMD_INIT_AUTH = 0x12;
public static final byte CMD_INIT_CON = 0x13;
//public static final byte CMD_SET_KEY = 0x14;
public static final byte CMD_SET_VALUE = 0x14;
public static final byte CMD_RESTORE_DEFAULTS = 0x15;
public static final byte CMD_RESTORE_STATUS = 0x16;
public static final byte CMD_HID_STATUS_REPORT = 0x20; public static final byte CMD_HID_STATUS_REPORT = 0x20;
@ -37,6 +43,7 @@ public class Packet {
public static final byte RESP_OK = 0x01; public static final byte RESP_OK = 0x01;
public static final byte RESP_UNKNOWN_CMD = (byte)0xFF;
public static final byte[] RAW_OLD_BOOTLOADER = new byte[] {START_TAG, (byte)0x00, (byte)0x02, (byte)0x83, (byte)0x00, (byte)0xDA}; public static final byte[] RAW_OLD_BOOTLOADER = new byte[] {START_TAG, (byte)0x00, (byte)0x02, (byte)0x83, (byte)0x00, (byte)0xDA};
@ -119,4 +126,8 @@ public class Packet {
return mRespond; return mRespond;
} }
public void print() {
Util.printHex(mData, "PACKET DATA:");
}
} }

View File

@ -39,9 +39,13 @@ public class PacketManager {
} }
} }
public boolean isEncrypted() {
return mEncryption;
}
public Packet encPacket(boolean enable) { public Packet encPacket(boolean enable) {
Random r = new Random(); Random r = new Random();
Packet p = new Packet(true, Packet.CMD_INIT); Packet p = new Packet(true, Packet.CMD_INIT_AUTH);
if (enable) { if (enable) {
p.addByte((byte)1); p.addByte((byte)1);
} else { } else {
@ -68,13 +72,13 @@ public class PacketManager {
initData = mAes.encrypt(initData); initData = mAes.encrypt(initData);
p.addBytes(initData); p.addBytes(initData);
//Util.printHex(initData, "InitData: "); Util.printHex(initData, "InitData: ");
cmpData = new byte[16]; cmpData = new byte[16];
r.nextBytes(cmpData); r.nextBytes(cmpData);
p.addBytes(cmpData); p.addBytes(cmpData);
//Util.printHex(cmpData, "CmpData: "); Util.printHex(cmpData, "CmpData: ");
return p; return p;
} }
@ -87,11 +91,15 @@ public class PacketManager {
payload = Arrays.copyOfRange(data, 2, data.length); //remove TAG, info payload = Arrays.copyOfRange(data, 2, data.length); //remove TAG, info
if ((data[1] & Packet.FLAG_ENCRYPTED) != 0) { if ((data[1] & Packet.FLAG_ENCRYPTED) != 0) {
Util.log("DECRYPT"); //Util.log("DECRYPT");
payload = mAes.decrypt(payload); if (mAes.isReady()) {
payload = mAes.decrypt(payload);
} else {
return null;
}
} }
Util.printHex(payload, "DATA IN: "); //Util.printHex(payload, "DATA IN: ");
//check CRC //check CRC
crcCompare = Util.getLong(payload[0], payload[1], payload[2], payload[3]); crcCompare = Util.getLong(payload[0], payload[1], payload[2], payload[3]);
@ -104,17 +112,19 @@ public class PacketManager {
payload = Arrays.copyOfRange(payload, 4, payload.length); //remove CRC payload = Arrays.copyOfRange(payload, 4, payload.length); //remove CRC
return payload; return payload;
} else { } else {
return null; //TODO return null; //TODO
} }
} }
public void sendRAW(byte[] data) { public void sendRAW(byte[] data) {
mBTService.write(data); mBTService.write(data);
} }
public void sendPacket(Packet p) { public void sendPacket(Packet p) {
sendPacket(p, mEncryption); if (p != null) {
sendPacket(p, mEncryption);
}
} }
public void sendPacket(Packet p, boolean encrypt) { public void sendPacket(Packet p, boolean encrypt) {
@ -137,7 +147,7 @@ public class PacketManager {
mCrc.reset(); mCrc.reset();
mCrc.update(result, CRC_OFFSET, result.length - CRC_OFFSET); mCrc.update(result, CRC_OFFSET, result.length - CRC_OFFSET);
crcValue = mCrc.getValue(); crcValue = mCrc.getValue();
Util.log("CRC: "+crcValue); //Util.log("CRC: "+crcValue);
result[3] = (byte)crcValue; result[3] = (byte)crcValue;
crcValue >>= 8; crcValue >>= 8;
result[2] = (byte)crcValue; result[2] = (byte)crcValue;

View File

@ -1,13 +1,25 @@
package com.inputstick.api; package com.inputstick.api;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public abstract class Util { public abstract class Util {
private static final boolean debug = false; public static boolean debug = false;
public static void log(String msg) { public static void log(String msg) {
log(msg, false);
}
public static void log(String msg, boolean displayTime) {
if (debug) { if (debug) {
System.out.println("LOG: " + msg); System.out.print("LOG: " + msg);
if (displayTime) {
System.out.print(" @ " + System.currentTimeMillis());
}
System.out.println();
} }
} }
@ -21,29 +33,35 @@ public abstract class Util {
public static void printHex(byte[] toPrint) { public static void printHex(byte[] toPrint) {
if (debug) { if (debug) {
int cnt = 0; if (toPrint != null) {
String s; int cnt = 0;
byte b; String s;
for (int i = 0; i < toPrint.length; i++) { byte b;
b = toPrint[i]; for (int i = 0; i < toPrint.length; i++) {
if ((b < 10) && (b >= 0)) { b = toPrint[i];
s = Integer.toHexString((int)b); //0x0..0xF = 0x00..0x0F
s = "0" + s; if ((b < 0x10) && (b >= 0)) {
} else { s = Integer.toHexString((int)b);
s = Integer.toHexString((int)b); s = "0" + s;
if (s.length() > 2) { } else {
s = s.substring(s.length() - 2); s = Integer.toHexString((int)b);
if (s.length() > 2) {
s = s.substring(s.length() - 2);
}
}
s = s.toUpperCase();
System.out.print("0x" + s + " ");
cnt++;
if (cnt == 8) {
System.out.println("");
cnt = 0;
} }
} }
s = s.toUpperCase();
System.out.print("0x" + s + " "); } else {
cnt++; System.out.println("null");
if (cnt == 8) { }
System.out.println(""); System.out.println("\n#####");
cnt = 0;
}
}
System.out.println("\n#####");
} }
} }
@ -79,5 +97,18 @@ public abstract class Util {
return result; return result;
} }
public static byte[] getPasswordBytes(String plainText) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
return md.digest(plainText.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
} }

View File

@ -1,35 +1,54 @@
package com.inputstick.api.basic; package com.inputstick.api.basic;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector; import java.util.Vector;
import android.app.AlertDialog;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import com.inputstick.api.BTConnectionManager; import com.inputstick.api.BTConnectionManager;
import com.inputstick.api.ConnectionManager; import com.inputstick.api.ConnectionManager;
import com.inputstick.api.HIDInfo; import com.inputstick.api.HIDInfo;
import com.inputstick.api.IPCConnectionManager; import com.inputstick.api.IPCConnectionManager;
import com.inputstick.api.InputStickDataListener; import com.inputstick.api.InputStickDataListener;
import com.inputstick.api.InputStickError;
import com.inputstick.api.InputStickStateListener; import com.inputstick.api.InputStickStateListener;
import com.inputstick.api.OnEmptyBufferListener;
import com.inputstick.api.Packet; import com.inputstick.api.Packet;
import com.inputstick.api.Util;
import com.inputstick.api.hid.HIDTransaction; import com.inputstick.api.hid.HIDTransaction;
import com.inputstick.api.hid.HIDTransactionQueue; import com.inputstick.api.hid.HIDTransactionQueue;
import com.inputstick.init.InitManager; import com.inputstick.init.InitManager;
public class InputStickHID implements InputStickStateListener, InputStickDataListener { public class InputStickHID implements InputStickStateListener, InputStickDataListener {
//private static final String mTag = "InputStickBasic"; public static final int INTERFACE_KEYBOARD = 0;
public static final int INTERFACE_CONSUMER = 1;
public static final int INTERFACE_MOUSE = 2;
//private static final String mTag = "InputStickBasic";
private static ConnectionManager mConnectionManager; private static ConnectionManager mConnectionManager;
private static Vector<InputStickStateListener> mStateListeners = new Vector<InputStickStateListener>(); private static Vector<InputStickStateListener> mStateListeners = new Vector<InputStickStateListener>();
private static InputStickHID instance = new InputStickHID(); private static InputStickHID instance = new InputStickHID();
private static HIDInfo mHIDInfo = new HIDInfo(); private static HIDInfo mHIDInfo;
private static HIDTransactionQueue keyboardQueue; private static HIDTransactionQueue keyboardQueue;
private static HIDTransactionQueue mouseQueue; private static HIDTransactionQueue mouseQueue;
private static HIDTransactionQueue consumerQueue; private static HIDTransactionQueue consumerQueue;
// >= FW 0.93
private static Timer t1;
private static boolean constantUpdateMode;
private InputStickHID() { private InputStickHID() {
} }
@ -38,9 +57,11 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
} }
private static void init() { private static void init() {
keyboardQueue = new HIDTransactionQueue(HIDTransactionQueue.KEYBOARD, mConnectionManager); mHIDInfo = new HIDInfo();
mouseQueue = new HIDTransactionQueue(HIDTransactionQueue.MOUSE, mConnectionManager); constantUpdateMode = false;
consumerQueue = new HIDTransactionQueue(HIDTransactionQueue.CONSUMER, mConnectionManager); keyboardQueue = new HIDTransactionQueue(INTERFACE_KEYBOARD, mConnectionManager);
mouseQueue = new HIDTransactionQueue(INTERFACE_MOUSE, mConnectionManager);
consumerQueue = new HIDTransactionQueue(INTERFACE_CONSUMER, mConnectionManager);
mConnectionManager.addStateListener(instance); mConnectionManager.addStateListener(instance);
mConnectionManager.addDataListener(instance); mConnectionManager.addDataListener(instance);
@ -55,8 +76,6 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
//direct Bluetooth connection //direct Bluetooth connection
public static void connect(Application app, String mac, byte[] key) { public static void connect(Application app, String mac, byte[] key) {
//mConnectionManager = new BTConnectionManager(new BasicInitManager(key), app, mac, reflections, key);
//mConnectionManager = new BTConnectionManager(new BasicInitManager(key), app, mac, key);
mConnectionManager = new BTConnectionManager(new InitManager(key), app, mac, key); mConnectionManager = new BTConnectionManager(new InitManager(key), app, mac, key);
init(); init();
} }
@ -80,6 +99,16 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
} }
} }
public static int getErrorCode() {
if (mConnectionManager != null) {
return mConnectionManager.getErrorCode();
} else {
return InputStickError.ERROR_UNKNOWN;
}
}
public static boolean isReady() { public static boolean isReady() {
if (getState() == ConnectionManager.STATE_READY) { if (getState() == ConnectionManager.STATE_READY) {
return true; return true;
@ -90,7 +119,9 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
public static void addStateListener(InputStickStateListener listener) { public static void addStateListener(InputStickStateListener listener) {
if (listener != null) { if (listener != null) {
mStateListeners.add(listener); if ( !mStateListeners.contains(listener)) {
mStateListeners.add(listener);
}
} }
} }
@ -99,6 +130,22 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
mStateListeners.remove(listener); mStateListeners.remove(listener);
} }
} }
public static void addBufferEmptyListener(OnEmptyBufferListener listener) {
if (listener != null) {
keyboardQueue.addBufferEmptyListener(listener);
mouseQueue.addBufferEmptyListener(listener);
consumerQueue.addBufferEmptyListener(listener);
}
}
public static void removeBufferEmptyListener(OnEmptyBufferListener listener) {
if (listener != null) {
keyboardQueue.removeBufferEmptyListener(listener);
mouseQueue.removeBufferEmptyListener(listener);
consumerQueue.removeBufferEmptyListener(listener);
}
}
public static void addKeyboardTransaction(HIDTransaction transaction) { public static void addKeyboardTransaction(HIDTransaction transaction) {
keyboardQueue.addTransaction(transaction); keyboardQueue.addTransaction(transaction);
@ -112,6 +159,18 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
consumerQueue.addTransaction(transaction); consumerQueue.addTransaction(transaction);
} }
public static void clearKeyboardBuffer() {
keyboardQueue.clearBuffer();
}
public static void clearMouseBuffer() {
mouseQueue.clearBuffer();
}
public static void clearConsumerBuffer() {
consumerQueue.clearBuffer();
}
public static boolean sendPacket(Packet p) { public static boolean sendPacket(Packet p) {
if (mConnectionManager != null) { if (mConnectionManager != null) {
mConnectionManager.sendPacket(p); mConnectionManager.sendPacket(p);
@ -122,31 +181,112 @@ public class InputStickHID implements InputStickStateListener, InputStickDataLis
} }
@Override @Override
public void onStateChanged(int state) { public void onStateChanged(int state) {
if ((state == ConnectionManager.STATE_DISCONNECTED) && (t1 != null)) {
t1.cancel();
t1 = null;
}
for (InputStickStateListener listener : mStateListeners) { for (InputStickStateListener listener : mStateListeners) {
listener.onStateChanged(state); listener.onStateChanged(state);
} }
} }
public static boolean isKeyboardLocalBufferEmpty() {
return keyboardQueue.isLocalBufferEmpty();
}
public static boolean isMouseLocalBufferEmpty() {
return mouseQueue.isLocalBufferEmpty();
}
public static boolean isConsumerLocalBufferEmpty() {
return consumerQueue.isLocalBufferEmpty();
}
public static boolean isKeyboardRemoteBufferEmpty() {
return keyboardQueue.isRemoteBufferEmpty();
}
public static boolean isMouseRemoteBufferEmpty() {
return mouseQueue.isRemoteBufferEmpty();
}
public static boolean isConsumerRemoteBufferEmpty() {
return consumerQueue.isRemoteBufferEmpty();
}
@Override @Override
public void onInputStickData(byte[] data) { public void onInputStickData(byte[] data) {
if (data[0] == Packet.CMD_HID_STATUS) { if (data[0] == Packet.CMD_HID_STATUS) {
mHIDInfo.update(data); mHIDInfo.update(data);
if (mHIDInfo.isKeyboardReady()) { if (mHIDInfo.isSentToHostInfoAvailable()) {
keyboardQueue.deviceReady(); // >= FW 0.93
keyboardQueue.deviceReady(mHIDInfo, mHIDInfo.getKeyboardReportsSentToHost());
mouseQueue.deviceReady(mHIDInfo, mHIDInfo.getMouseReportsSentToHost());
consumerQueue.deviceReady(mHIDInfo, mHIDInfo.getConsumerReportsSentToHost());
if ( !constantUpdateMode) {
Util.log("Constatnt update mode enabled");
constantUpdateMode = true;
t1 = new Timer();
t1.schedule(new TimerTask() {
@Override
public void run() {
keyboardQueue.sendToBuffer(false);
mouseQueue.sendToBuffer(false);
consumerQueue.sendToBuffer(false);
}
}, 5,5);
}
} else {
//previous FW versions
if (mHIDInfo.isKeyboardReady()) {
keyboardQueue.deviceReady(null, 0);
}
if (mHIDInfo.isMouseReady()) {
mouseQueue.deviceReady(null, 0);
}
if (mHIDInfo.isConsumerReady()) {
consumerQueue.deviceReady(null, 0);
}
} }
if (mHIDInfo.isMouseReady()) {
mouseQueue.deviceReady();
}
if (mHIDInfo.isConsumerReady()) {
consumerQueue.deviceReady();
}
InputStickKeyboard.setLEDs(mHIDInfo.getNumLock(), mHIDInfo.getCapsLock(), mHIDInfo.getScrollLock()); InputStickKeyboard.setLEDs(mHIDInfo.getNumLock(), mHIDInfo.getCapsLock(), mHIDInfo.getScrollLock());
} }
} }
public static AlertDialog getDownloadDialog(final Context ctx) {
if (mConnectionManager.getErrorCode() == InputStickError.ERROR_ANDROID_NO_UTILITY_APP) {
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(ctx);
downloadDialog.setTitle("No InputStickUtility app installed");
downloadDialog.setMessage("InputStickUtility is required to run this application. Download now?");
downloadDialog.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
final String appPackageName = "com.inputstick.apps.inputstickutility";
try {
ctx.startActivity(new Intent(
Intent.ACTION_VIEW, Uri
.parse("market://details?id="
+ appPackageName)));
} catch (android.content.ActivityNotFoundException anfe) {
ctx.startActivity(new Intent(
Intent.ACTION_VIEW,
Uri.parse("http://play.google.com/store/apps/details?id="
+ appPackageName)));
}
}
});
downloadDialog.setNegativeButton("No",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
return downloadDialog.show();
} else {
return null;
}
}
} }

View File

@ -2,6 +2,8 @@ package com.inputstick.api.basic;
import java.util.Vector; import java.util.Vector;
import android.util.SparseArray;
import com.inputstick.api.InputStickKeyboardListener; import com.inputstick.api.InputStickKeyboardListener;
import com.inputstick.api.hid.HIDKeycodes; import com.inputstick.api.hid.HIDKeycodes;
import com.inputstick.api.hid.HIDTransaction; import com.inputstick.api.hid.HIDTransaction;
@ -13,6 +15,10 @@ public class InputStickKeyboard {
private static final byte NONE = (byte)0; private static final byte NONE = (byte)0;
private static final byte LED_NUM_LOCK = 1;
private static final byte LED_CAPS_LOCK = 2;
private static final byte LED_SCROLL_LOCK = 4;
private static boolean mReportProtocol; private static boolean mReportProtocol;
private static boolean mNumLock; private static boolean mNumLock;
private static boolean mCapsLock; private static boolean mCapsLock;
@ -20,12 +26,23 @@ public class InputStickKeyboard {
private static Vector<InputStickKeyboardListener> mKeyboardListeners = new Vector<InputStickKeyboardListener>(); private static Vector<InputStickKeyboardListener> mKeyboardListeners = new Vector<InputStickKeyboardListener>();
private static final SparseArray<String> ledsMap;
static
{
ledsMap = new SparseArray<String>();
ledsMap.put(LED_NUM_LOCK, "NumLock");
ledsMap.put(LED_CAPS_LOCK, "CapsLock");
ledsMap.put(LED_SCROLL_LOCK, "ScrollLock");
}
private InputStickKeyboard() { private InputStickKeyboard() {
} }
public static void addKeyboardListener(InputStickKeyboardListener listener) { public static void addKeyboardListener(InputStickKeyboardListener listener) {
if (listener != null) { if (listener != null) {
mKeyboardListeners.add(listener); if ( !mKeyboardListeners.contains(listener)) {
mKeyboardListeners.add(listener);
}
} }
} }
@ -114,5 +131,25 @@ public class InputStickKeyboard {
t.addReport(report); t.addReport(report);
InputStickHID.addKeyboardTransaction(t); InputStickHID.addKeyboardTransaction(t);
}*/ }*/
public static String ledsToString(byte leds) {
String result = "None";
boolean first = true;
byte mod;
for (int i = 0; i < 8; i++) {
mod = (byte)(LED_NUM_LOCK << i);
if ((leds & mod) != 0) {
if ( !first) {
result += ", ";
} else {
result = "";
}
first = false;
result += ledsMap.get(mod);
}
}
return result;
}
} }

View File

@ -1,5 +1,7 @@
package com.inputstick.api.basic; package com.inputstick.api.basic;
import android.util.SparseArray;
import com.inputstick.api.hid.HIDTransaction; import com.inputstick.api.hid.HIDTransaction;
import com.inputstick.api.hid.MouseReport; import com.inputstick.api.hid.MouseReport;
@ -12,6 +14,15 @@ public class InputStickMouse {
public static final byte BUTTON_RIGHT = 0x02; public static final byte BUTTON_RIGHT = 0x02;
public static final byte BUTTON_MIDDLE = 0x04; public static final byte BUTTON_MIDDLE = 0x04;
private static final SparseArray<String> buttonsMap;
static
{
buttonsMap = new SparseArray<String>();
buttonsMap.put(BUTTON_LEFT, "Left");
buttonsMap.put(BUTTON_RIGHT, "Right");
buttonsMap.put(BUTTON_MIDDLE, "Middle");
}
private static boolean mReportProtocol; private static boolean mReportProtocol;
private InputStickMouse() { private InputStickMouse() {
@ -53,5 +64,25 @@ public class InputStickMouse {
t.addReport(new MouseReport(buttons, x, y, wheel)); t.addReport(new MouseReport(buttons, x, y, wheel));
InputStickHID.addMouseTransaction(t); InputStickHID.addMouseTransaction(t);
} }
public static String buttonsToString(byte buttons) {
String result = "None";
boolean first = true;
byte mod;
for (int i = 0; i < 8; i++) {
mod = (byte)(BUTTON_LEFT << i);
if ((buttons & mod) != 0) {
if ( !first) {
result += ", ";
} else {
result = "";
}
first = false;
result += buttonsMap.get(mod);
}
}
return result;
}
} }

View File

@ -19,6 +19,7 @@ import android.os.Message;
import com.inputstick.api.Packet; import com.inputstick.api.Packet;
import com.inputstick.api.Util; import com.inputstick.api.Util;
import com.inputstick.api.InputStickError;
public class BTService { public class BTService {
@ -28,16 +29,7 @@ public class BTService {
public static final int EVENT_DATA = 1; public static final int EVENT_DATA = 1;
public static final int EVENT_CONNECTED = 2; public static final int EVENT_CONNECTED = 2;
public static final int EVENT_CANCELLED = 3; public static final int EVENT_CANCELLED = 3;
public static final int EVENT_CONNECTION_FAILED = 4; public static final int EVENT_ERROR = 4;
public static final int EVENT_CONNECTION_LOST = 5;
public static final int EVENT_NO_BT_HW = 6;
public static final int EVENT_INVALID_MAC = 7;
//TODO:
public static final int EVENT_CMD_TIMEOUT = 8;
public static final int EVENT_INTERVAL_TIMEOUT = 9;
public static final int EVENT_TURN_ON_TIMEOUT = 10;
public static final int EVENT_OTHER_ERROR = 11;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //SPP private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //SPP
@ -71,8 +63,7 @@ public class BTService {
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
final String action = intent.getAction(); final String action = intent.getAction();
System.out.println("ACTION: "+action);
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if ((state == BluetoothAdapter.STATE_ON) && (turnBluetoothOn)) { if ((state == BluetoothAdapter.STATE_ON) && (turnBluetoothOn)) {
@ -102,12 +93,12 @@ public class BTService {
public void enableReflection(boolean enabled) { public void enableReflection(boolean enabled) {
mUseReflection = enabled; mUseReflection = enabled;
} }
private synchronized void event(int event) {
private synchronized void event(int event, int arg1) {
Util.log("event() " + mLastEvent + " -> " + event); Util.log("event() " + mLastEvent + " -> " + event);
mLastEvent = event; mLastEvent = event;
Message msg = Message.obtain(null, mLastEvent, arg1, 0);
Message msg = Message.obtain(null, mLastEvent, 0, 0);
mHandler.sendMessage(msg); mHandler.sendMessage(msg);
} }
@ -163,7 +154,7 @@ public class BTService {
if (BluetoothAdapter.checkBluetoothAddress(mac)) { if (BluetoothAdapter.checkBluetoothAddress(mac)) {
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) { if (mBluetoothAdapter == null) {
event(EVENT_NO_BT_HW); event(EVENT_ERROR, InputStickError.ERROR_BLUETOOTH_NOT_SUPPORTED);
} else { } else {
if (mBluetoothAdapter.isEnabled()) { if (mBluetoothAdapter.isEnabled()) {
doConnect(false); doConnect(false);
@ -172,7 +163,7 @@ public class BTService {
} }
} }
} else { } else {
event(EVENT_INVALID_MAC); event(EVENT_ERROR, InputStickError.ERROR_BLUETOOTH_INVALID_MAC);
} }
} }
@ -180,7 +171,7 @@ public class BTService {
Util.log("disconnect"); Util.log("disconnect");
disconnecting = true; disconnecting = true;
cancelThreads(); cancelThreads();
event(EVENT_CANCELLED); event(EVENT_CANCELLED, 0);
} }
@ -226,7 +217,7 @@ public class BTService {
mConnectedThread.start(); mConnectedThread.start();
connected = true; connected = true;
event(EVENT_CONNECTED); event(EVENT_CONNECTED, 0);
} }
@ -239,7 +230,7 @@ public class BTService {
Util.log("RETRY: "+retryCnt + " time left: " + (timeout - System.currentTimeMillis())); Util.log("RETRY: "+retryCnt + " time left: " + (timeout - System.currentTimeMillis()));
doConnect(true); doConnect(true);
} else { } else {
event(EVENT_CONNECTION_FAILED); event(EVENT_ERROR, InputStickError.ERROR_BLUETOOTH_CONNECTION_FAILED);
} }
} }
} }
@ -249,7 +240,7 @@ public class BTService {
if (disconnecting) { if (disconnecting) {
disconnecting = false; disconnecting = false;
} else { } else {
event(EVENT_CONNECTION_LOST); event(EVENT_ERROR, InputStickError.ERROR_BLUETOOTH_CONNECTION_LOST);
} }
} }
@ -356,11 +347,13 @@ public class BTService {
byte[] buffer = null; byte[] buffer = null;
int rxTmp; int rxTmp;
int lengthByte; int lengthByte;
int length; int length;
int wdgCnt = 0;
while (true) { while (true) {
try { try {
rxTmp = mmInStream.read(); rxTmp = mmInStream.read();
if (rxTmp == Packet.START_TAG) { if (rxTmp == Packet.START_TAG) {
wdgCnt = 0;
lengthByte = mmInStream.read(); lengthByte = mmInStream.read();
length = lengthByte; length = lengthByte;
length &= 0x3F; length &= 0x3F;
@ -373,8 +366,13 @@ public class BTService {
} }
mHandler.obtainMessage(EVENT_DATA, 0, 0, buffer).sendToTarget(); mHandler.obtainMessage(EVENT_DATA, 0, 0, buffer).sendToTarget();
} else { } else {
System.out.println("RX: " + rxTmp); Util.log("Unexpected RX byte" + rxTmp);
//possible WDG reset if (rxTmp == 0xAF) {
wdgCnt++;
}
if (wdgCnt > 1024) {
//TODO
}
} }
} catch (IOException e) { } catch (IOException e) {
connectionLost(); connectionLost();
@ -388,7 +386,7 @@ public class BTService {
mmOutStream.write(buffer); mmOutStream.write(buffer);
mmOutStream.flush(); mmOutStream.flush();
} catch (IOException e) { } catch (IOException e) {
Util.log("Exception during write"); Util.log("write() exception");
} }
} }
@ -396,7 +394,7 @@ public class BTService {
try { try {
mmSocket.close(); mmSocket.close();
} catch (IOException e) { } catch (IOException e) {
Util.log("close() of connect socket failed"); Util.log("socket close() exception");
} }
} }
} }

View File

@ -1,5 +1,7 @@
package com.inputstick.api.hid; package com.inputstick.api.hid;
import android.util.SparseArray;
public class HIDKeycodes { public class HIDKeycodes {
public static final byte NONE = 0x00; public static final byte NONE = 0x00;
@ -13,6 +15,7 @@ public class HIDKeycodes {
public static final byte ALT_RIGHT = 0x40; public static final byte ALT_RIGHT = 0x40;
public static final byte GUI_RIGHT = (byte)0x80; public static final byte GUI_RIGHT = (byte)0x80;
public static final byte KEY_ENTER = 0x28; public static final byte KEY_ENTER = 0x28;
public static final byte KEY_ESCAPE = 0x29; public static final byte KEY_ESCAPE = 0x29;
public static final byte KEY_BACKSPACE = 0x2A; public static final byte KEY_BACKSPACE = 0x2A;
@ -79,6 +82,8 @@ public class HIDKeycodes {
public static final byte KEY_NUM_0 = 0x62; public static final byte KEY_NUM_0 = 0x62;
public static final byte KEY_NUM_DOT = 0x63; public static final byte KEY_NUM_DOT = 0x63;
public static final byte KEY_BACKSLASH_NON_US = 0x64;
public static final byte KEY_A = 0x04; public static final byte KEY_A = 0x04;
public static final byte KEY_B = 0x05; public static final byte KEY_B = 0x05;
public static final byte KEY_C = 0x06; public static final byte KEY_C = 0x06;
@ -126,6 +131,133 @@ public class HIDKeycodes {
public static final SparseArray<String> modifiersMap;
static
{
modifiersMap = new SparseArray<String>();
modifiersMap.put(CTRL_LEFT, "Left Ctrl");
modifiersMap.put(SHIFT_LEFT, "Left Shift");
modifiersMap.put(ALT_LEFT, "Left Alt");
modifiersMap.put(GUI_LEFT, "Left GUI");
modifiersMap.put(CTRL_RIGHT, "Right Ctrl");
modifiersMap.put(SHIFT_RIGHT, "Right Shift");
modifiersMap.put(ALT_RIGHT, "Right Alt");
modifiersMap.put(GUI_RIGHT, "Right GUI");
}
public static final SparseArray<String> keyMap;
static
{
keyMap = new SparseArray<String>();
keyMap.put(0, "None");
keyMap.put(KEY_ENTER, "Enter");
keyMap.put(KEY_ESCAPE , "Esc");
keyMap.put(KEY_BACKSPACE , "Backspace");
keyMap.put(KEY_TAB , "Tab");
keyMap.put(KEY_SPACEBAR , "Space");
keyMap.put(KEY_CAPS_LOCK , "CapsLock");
keyMap.put(KEY_1 , "1");
keyMap.put(KEY_2 , "2");
keyMap.put(KEY_3 , "3");
keyMap.put(KEY_4 , "4");
keyMap.put(KEY_5 , "5");
keyMap.put(KEY_6 , "6");
keyMap.put(KEY_7 , "7");
keyMap.put(KEY_8 , "8");
keyMap.put(KEY_9 , "9");
keyMap.put(KEY_0 , "0");
keyMap.put(KEY_F1 , "F1");
keyMap.put(KEY_F2 , "F2");
keyMap.put(KEY_F3 , "F3");
keyMap.put(KEY_F4 , "F4");
keyMap.put(KEY_F5 , "F5");
keyMap.put(KEY_F6 , "F6");
keyMap.put(KEY_F7 , "F7");
keyMap.put(KEY_F8 , "F8");
keyMap.put(KEY_F9 , "F9");
keyMap.put(KEY_F10 , "F10");
keyMap.put(KEY_F11 , "F11");
keyMap.put(KEY_F12 , "F12");
keyMap.put(KEY_PRINT_SCREEN , "Print Scrn");
keyMap.put(KEY_SCROLL_LOCK , "ScrollLock");
keyMap.put(KEY_PASUE , "Pause Break");
keyMap.put(KEY_INSERT , "Insert");
keyMap.put(KEY_HOME , "Home");
keyMap.put(KEY_PAGE_UP , "PageUp");
keyMap.put(KEY_DELETE , "Delete");
keyMap.put(KEY_END , "End");
keyMap.put(KEY_PAGE_DOWN , "PageDown");
keyMap.put(KEY_ARROW_RIGHT , "Right Arrow");
keyMap.put(KEY_ARROW_LEFT , "Left Arrow");
keyMap.put(KEY_ARROW_DOWN , "Down Arrow");
keyMap.put(KEY_ARROW_UP , "Up Arrow");
keyMap.put(KEY_NUM_LOCK , "NumLock");
keyMap.put(KEY_NUM_BACKSLASH , "Num /");
keyMap.put(KEY_NUM_STAR , "Num *");
keyMap.put(KEY_NUM_MINUS , "Num -");
keyMap.put(KEY_NUM_PLUS , "Num +");
keyMap.put(KEY_NUM_ENTER , "Num Enter");
keyMap.put(KEY_NUM_1 , "Num 1");
keyMap.put(KEY_NUM_2 , "Num 2");
keyMap.put(KEY_NUM_3 , "Num 3");
keyMap.put(KEY_NUM_4 , "Num 4");
keyMap.put(KEY_NUM_5 , "Num 5");
keyMap.put(KEY_NUM_6 , "Num 6");
keyMap.put(KEY_NUM_7 , "Num 7");
keyMap.put(KEY_NUM_8 , "Num 8");
keyMap.put(KEY_NUM_9 , "Num 9");
keyMap.put(KEY_NUM_0 , "Num 0");
keyMap.put(KEY_NUM_DOT , "Num .");
keyMap.put(KEY_A , "A");
keyMap.put(KEY_B , "B");
keyMap.put(KEY_C , "C");
keyMap.put(KEY_D , "D");
keyMap.put(KEY_E , "E");
keyMap.put(KEY_F , "F");
keyMap.put(KEY_G , "G");
keyMap.put(KEY_H , "H");
keyMap.put(KEY_I , "I");
keyMap.put(KEY_J , "J");
keyMap.put(KEY_K , "K");
keyMap.put(KEY_L , "L");
keyMap.put(KEY_M , "M");
keyMap.put(KEY_N , "N");
keyMap.put(KEY_O , "O");
keyMap.put(KEY_P , "P");
keyMap.put(KEY_Q , "Q");
keyMap.put(KEY_R , "R");
keyMap.put(KEY_S , "S");
keyMap.put(KEY_T , "T");
keyMap.put(KEY_U , "U");
keyMap.put(KEY_V , "V");
keyMap.put(KEY_W , "W");
keyMap.put(KEY_X , "X");
keyMap.put(KEY_Y , "Y");
keyMap.put(KEY_Z , "Z");
keyMap.put(KEY_MINUS , "-");
keyMap.put(KEY_EQUALS , "=");
keyMap.put(KEY_LEFT_BRACKET , "[");
keyMap.put(KEY_RIGHT_BRACKET , "]");
keyMap.put(KEY_BACKSLASH , "\\");
//keyMap.put(KEY_GRAVE , "`");
keyMap.put(KEY_SEMICOLON , ";");
keyMap.put(KEY_APOSTROPHE , "'");
keyMap.put(KEY_GRAVE , "`");
keyMap.put(KEY_COMA , ",");
keyMap.put(KEY_DOT , ".");
keyMap.put(KEY_SLASH , "/");
keyMap.put(KEY_APPLICATION , "Application");
}
public static final int[] ASCIItoHID = { public static final int[] ASCIItoHID = {
0, //000 0, //000
0, //001 0, //001
@ -257,6 +389,14 @@ public class HIDKeycodes {
0 //127 just in case... 0 //127 just in case...
}; };
public static char getChar(byte keyCode) {
for (int i = 0; i < ASCIItoHID.length; i++) {
if (ASCIItoHID[i] == keyCode) {
return (char)i;
}
}
return 0;
}
public static byte getKeyCode(char c) { public static byte getKeyCode(char c) {
return (byte)ASCIItoHID[c]; //TODO range return (byte)ASCIItoHID[c]; //TODO range
@ -266,7 +406,31 @@ public class HIDKeycodes {
return ASCIItoHID[c]; //TODO range return ASCIItoHID[c]; //TODO range
} }
public static String modifiersToString(byte modifiers) {
String result = "None";
boolean first = true;
byte mod;
for (int i = 0; i < 8; i++) {
mod = (byte)(CTRL_LEFT << i);
if ((modifiers & mod) != 0) {
if ( !first) {
result += ", ";
} else {
result = "";
}
first = false;
result += modifiersMap.get(mod);
}
}
return result;
}
public static String keyToString(byte key) {
String result = keyMap.get(key);
if (result == null) {
result = "Unknown";
}
return result;
}
} }

View File

@ -1,39 +1,71 @@
package com.inputstick.api.hid; package com.inputstick.api.hid;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector; import java.util.Vector;
import com.inputstick.api.ConnectionManager; import com.inputstick.api.ConnectionManager;
import com.inputstick.api.HIDInfo;
import com.inputstick.api.OnEmptyBufferListener;
import com.inputstick.api.Packet; import com.inputstick.api.Packet;
import com.inputstick.api.basic.InputStickHID;
public class HIDTransactionQueue { public class HIDTransactionQueue {
public static final int KEYBOARD = 1;
public static final int MOUSE = 2;
public static final int CONSUMER = 3;
private static final int BUFFER_SIZE = 32; private static final int BUFFER_SIZE = 32;
private static final int BT_DELAY = 50; //additional delay for BT overhead
private static final int MAX_PACKETS_PER_UPDATE = 10;
private static final int MAX_IMMEDIATE_PACKETS = 3;
private final Vector<HIDTransaction> queue; private final Vector<HIDTransaction> queue;
private final ConnectionManager mConnectionManager; private final ConnectionManager mConnectionManager;
private final byte cmd; private final byte cmd;
private boolean ready; private boolean ready;
private int mInterfaceType;
private boolean mustNotify;
private Vector<OnEmptyBufferListener> mBufferEmptyListeners = new Vector<OnEmptyBufferListener>();
private Timer t;
private boolean timerCancelled;
private boolean sentAhead;
private long lastTime; private long lastTime;
private int lastReports; private long minNextTime;
private int lastReports;
public HIDTransactionQueue(int type, ConnectionManager connectionManager) { // >= FW 0.93
private boolean bufferInitDone;
private boolean constantUpdateMode;
private int bufferFreeSpace;
private int immediatePacketsLeft;
//private int reportsSentSinceLastUpdate;
private int packetsSentSinceLastUpdate;
public HIDTransactionQueue(int interfaceType, ConnectionManager connectionManager) {
constantUpdateMode = false;
bufferFreeSpace = BUFFER_SIZE;
queue = new Vector<HIDTransaction>(); queue = new Vector<HIDTransaction>();
mConnectionManager = connectionManager; mConnectionManager = connectionManager;
ready = false; ready = false;
switch (type) { sentAhead = false;
case KEYBOARD: minNextTime = 0;
mustNotify = false;
mInterfaceType = interfaceType;
switch (interfaceType) {
case InputStickHID.INTERFACE_KEYBOARD:
cmd = Packet.CMD_HID_DATA_KEYB; cmd = Packet.CMD_HID_DATA_KEYB;
break; break;
case MOUSE: case InputStickHID.INTERFACE_MOUSE:
cmd = Packet.CMD_HID_DATA_MOUSE; cmd = Packet.CMD_HID_DATA_MOUSE;
break; break;
case CONSUMER: case InputStickHID.INTERFACE_CONSUMER:
cmd = Packet.CMD_HID_DATA_CONSUMER; cmd = Packet.CMD_HID_DATA_CONSUMER;
break; break;
default: default:
@ -41,26 +73,32 @@ public class HIDTransactionQueue {
} }
} }
private void sendNext() { private int sendNext(int maxReports) {
HIDTransaction transaction; HIDTransaction transaction;
byte reports = 0;
ready = false;
Packet p = new Packet(false, cmd, reports);
//assume there is at least 1 element in queue //assume there is at least 1 element in queue
transaction = queue.firstElement(); transaction = queue.firstElement();
if (transaction.getReportsCount() > BUFFER_SIZE) { if (transaction.getReportsCount() > maxReports) {
//transaction too big! split // v0.92
transaction = transaction.split(BUFFER_SIZE); if (maxReports < BUFFER_SIZE) {
//don't split transactions until there is no other way left!
return 0;
}
//transaction too big to fit single packet! split
transaction = transaction.split(BUFFER_SIZE);
} else { } else {
queue.removeElementAt(0); queue.removeElementAt(0);
} }
byte reports = 0;
ready = false;
Packet p = new Packet(false, cmd, reports);
while (transaction.hasNext()) { while (transaction.hasNext()) {
p.addBytes(transaction.getNextReport()); p.addBytes(transaction.getNextReport());
reports++; reports++;
} }
//TODO add next transactions if possible
while(true) { while(true) {
if (queue.isEmpty()) { if (queue.isEmpty()) {
@ -68,7 +106,7 @@ public class HIDTransactionQueue {
} }
transaction = queue.firstElement(); transaction = queue.firstElement();
if (reports + transaction.getReportsCount() < BUFFER_SIZE) { if (reports + transaction.getReportsCount() < maxReports) {
queue.removeElementAt(0); queue.removeElementAt(0);
while (transaction.hasNext()) { while (transaction.hasNext()) {
p.addBytes(transaction.getNextReport()); p.addBytes(transaction.getNextReport());
@ -79,33 +117,183 @@ public class HIDTransactionQueue {
} }
} }
//!! total number of reports must be < 32 ! (max packet limitation)
p.modifyByte(1, reports); //set reports count p.modifyByte(1, reports); //set reports count
mConnectionManager.sendPacket(p); mConnectionManager.sendPacket(p);
lastReports = reports; lastReports = reports;
lastTime = System.currentTimeMillis(); lastTime = System.currentTimeMillis();
minNextTime = lastTime + (lastReports * 4) + BT_DELAY;
if (queue.isEmpty()) {
notifyOnLocalBufferEmpty();
}
return reports;
} }
public void addTransaction(HIDTransaction transaction) { public void addBufferEmptyListener(OnEmptyBufferListener listener) {
if (queue.isEmpty()) { if (listener != null) {
if (System.currentTimeMillis() > lastTime + (lastReports * 8 * 2/*just to be safe*/)) { if ( !mBufferEmptyListeners.contains(listener)) {
ready = true; mBufferEmptyListeners.add(listener);
} }
} }
}
public void removeBufferEmptyListener(OnEmptyBufferListener listener) {
if (listener != null) {
mBufferEmptyListeners.remove(listener);
}
}
private void notifyOnRemoteBufferEmpty() {
for (OnEmptyBufferListener listener : mBufferEmptyListeners) {
listener.onRemoteBufferEmpty(mInterfaceType);
}
}
private void notifyOnLocalBufferEmpty() {
for (OnEmptyBufferListener listener : mBufferEmptyListeners) {
listener.onRemoteBufferEmpty(mInterfaceType);
}
}
public synchronized boolean isLocalBufferEmpty() {
return queue.isEmpty();
}
public synchronized boolean isRemoteBufferEmpty() {
if ((queue.isEmpty()) && (bufferFreeSpace == BUFFER_SIZE)) {
return true;
}
if (queue.isEmpty() && ( !mustNotify)) {
return true;
} else {
return false;
}
}
public synchronized void clearBuffer() {
queue.removeAllElements();
}
public synchronized void addTransaction(HIDTransaction transaction) {
if ( !bufferInitDone) {
queue.add(transaction);
return;
}
if (constantUpdateMode) {
queue.add(transaction);
sendToBuffer(true);
return;
}
mustNotify = true;
//using sentAhead will slow down mouse. FW0.92 will solve the problems
if ((queue.isEmpty()) && (System.currentTimeMillis() > minNextTime) /*&& ( !sentAhead)*/) {
sentAhead = true;
ready = true;
}
queue.add(transaction); queue.add(transaction);
if (ready) { if (ready) {
sendNext(); sendNext(BUFFER_SIZE);
} }
}
private synchronized void timerAction() {
if ( !timerCancelled) {
if (sentAhead) {
deviceReady(null, 0); //will set sentAhead to false;
sentAhead = true; //restore value
} else {
deviceReady(null, 0);
}
}
}
public synchronized void deviceReady(HIDInfo hidInfo, int reportsSentToHost) {
//it is possible that in the meantime some packets has been sent to IS!!!
bufferInitDone = true;
if (hidInfo != null) {
if (hidInfo.isSentToHostInfoAvailable()) {
constantUpdateMode = true;
// >= FW 0.93
bufferFreeSpace += reportsSentToHost;
if ((bufferFreeSpace == BUFFER_SIZE) && (queue.isEmpty())) {
notifyOnRemoteBufferEmpty();
}
immediatePacketsLeft = MAX_IMMEDIATE_PACKETS;
//reportsSentSinceLastUpdate = 0;
packetsSentSinceLastUpdate = 0;
sendToBuffer(false);
return;
}
}
long now = System.currentTimeMillis();
//System.out.println("v90 HID update");
if (now < minNextTime) {
//set timer, just in case if deviceReady won't be called again
timerCancelled = false;
t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
timerAction();
}
}, (minNextTime - now + 1));
} else {
timerCancelled = true;
sentAhead = false;
if (!queue.isEmpty()) {
sendNext(BUFFER_SIZE);
} else {
ready = true;
//queue is empty, InputStick reported that buffer is empty, data was added since last notification
if (mustNotify) {
notifyOnRemoteBufferEmpty();
mustNotify = false;
}
}
}
}
public synchronized void sendToBuffer(boolean justAdded) {
if ((justAdded) && (immediatePacketsLeft <= 0)) {
return;
}
if ( !InputStickHID.isReady()) {
return;
}
if (queue.isEmpty()) {
return;
}
if (bufferFreeSpace <= 0) {
return;
}
if (packetsSentSinceLastUpdate >= MAX_PACKETS_PER_UPDATE) {
return;
}
int reportsSent = sendNext(bufferFreeSpace);
if (reportsSent > 0) {
if (justAdded) {
immediatePacketsLeft --;
}
bufferFreeSpace -= reportsSent;
packetsSentSinceLastUpdate ++;
}
} }
public void deviceReady() {
if (!queue.isEmpty()) {
sendNext();
} else {
ready = true;
}
}
} }

View File

@ -110,6 +110,49 @@ public class GermanLayout extends KeyboardLayout {
}; };
public static final int DEADKEYS[] = {
0x0060, 0x00b4, 0x005e
};
public static final int DEADKEY_LUT[][] = {
{ 0x00b4 , 0x0079 , 0x00fd } ,
{ 0x00b4 , 0x0061 , 0x00e1 } ,
{ 0x00b4 , 0x0065 , 0x00e9 } ,
{ 0x00b4 , 0x0075 , 0x00fa } ,
{ 0x00b4 , 0x0069 , 0x00ed } ,
{ 0x00b4 , 0x006f , 0x00f3 } ,
{ 0x00b4 , 0x0059 , 0x00dd } ,
{ 0x00b4 , 0x0041 , 0x00c1 } ,
{ 0x00b4 , 0x0045 , 0x00c9 } ,
{ 0x00b4 , 0x0055 , 0x00da } ,
{ 0x00b4 , 0x0049 , 0x00cd } ,
{ 0x00b4 , 0x004f , 0x00d3 } ,
{ 0x00b4 , 0x0020 , 0x00b4 } ,
{ 0x0060 , 0x0061 , 0x00e0 } ,
{ 0x0060 , 0x0065 , 0x00e8 } ,
{ 0x0060 , 0x0075 , 0x00f9 } ,
{ 0x0060 , 0x0069 , 0x00ec } ,
{ 0x0060 , 0x006f , 0x00f2 } ,
{ 0x0060 , 0x0041 , 0x00c0 } ,
{ 0x0060 , 0x0045 , 0x00c8 } ,
{ 0x0060 , 0x0055 , 0x00d9 } ,
{ 0x0060 , 0x0049 , 0x00cc } ,
{ 0x0060 , 0x004f , 0x00d2 } ,
{ 0x0060 , 0x0020 , 0x0060 } ,
{ 0x005e , 0x0061 , 0x00e2 } ,
{ 0x005e , 0x0065 , 0x00ea } ,
{ 0x005e , 0x0075 , 0x00fb } ,
{ 0x005e , 0x0069 , 0x00ee } ,
{ 0x005e , 0x006f , 0x00f4 } ,
{ 0x005e , 0x0041 , 0x00c2 } ,
{ 0x005e , 0x0045 , 0x00ca } ,
{ 0x005e , 0x0055 , 0x00db } ,
{ 0x005e , 0x0049 , 0x00ce } ,
{ 0x005e , 0x004f , 0x00d4 } ,
{ 0x005e , 0x0020 , 0x005e } ,
};
private static GermanLayout instance = new GermanLayout(); private static GermanLayout instance = new GermanLayout();
private GermanLayout() { private GermanLayout() {
@ -126,9 +169,14 @@ public class GermanLayout extends KeyboardLayout {
@Override @Override
public void type(String text) { public void type(String text) {
super.type(LUT, text); super.type(LUT, DEADKEY_LUT, DEADKEYS, text, (byte)0);
} }
@Override
public void type(String text, byte modifiers) {
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, modifiers);
}
@Override @Override
public char getChar(int scanCode, boolean capsLock, boolean shift, boolean altGr) { public char getChar(int scanCode, boolean capsLock, boolean shift, boolean altGr) {
return super.getChar(LUT, scanCode, capsLock, shift, altGr); return super.getChar(LUT, scanCode, capsLock, shift, altGr);
@ -137,6 +185,16 @@ public class GermanLayout extends KeyboardLayout {
@Override @Override
public String getLocaleName() { public String getLocaleName() {
return LOCALE_NAME; return LOCALE_NAME;
} }
@Override
public int[][] getDeadkeyLUT() {
return DEADKEY_LUT;
}
@Override
public int[] getDeadkeys() {
return DEADKEYS;
}
} }

View File

@ -101,7 +101,7 @@ public abstract class KeyboardLayout {
/* 0x53 */ HIDKeycodes.KEY_DELETE, /* 0x53 */ HIDKeycodes.KEY_DELETE,
/* 0x54 */ 0, /* 0x54 */ 0,
/* 0x55 */ 0, /* 0x55 */ 0,
/* 0x56 */ 0, /* 0x56 */ HIDKeycodes.KEY_BACKSLASH_NON_US, //GERMAN LAYOUT!
/* 0x57 */ HIDKeycodes.KEY_F11, /* 0x57 */ HIDKeycodes.KEY_F11,
/* 0x58 */ HIDKeycodes.KEY_F12, /* 0x58 */ HIDKeycodes.KEY_F12,
/* 0x59 */ 0, /* 0x59 */ 0,
@ -117,16 +117,19 @@ public abstract class KeyboardLayout {
public static final int LAYOUT_CODE = 0; public static final int LAYOUT_CODE = 0;
public abstract int[][] getLUT(); public abstract int[][] getLUT();
public abstract int[][] getDeadkeyLUT();
public abstract int[] getDeadkeys();
public abstract String getLocaleName(); public abstract String getLocaleName();
public abstract void type(String text); public abstract void type(String text);
public abstract void type(String text, byte modifiers);
public abstract char getChar(int scanCode, boolean capsLock, boolean shift, boolean altGr); public abstract char getChar(int scanCode, boolean capsLock, boolean shift, boolean altGr);
public void type(int[][] lut, String text) { public void type(int[][] lut, int[][] deadkeyLUT, int[] deadkeys, String text, byte modifiers) {
if (InputStickHID.getState() == ConnectionManager.STATE_READY) { if (InputStickHID.getState() == ConnectionManager.STATE_READY) {
char[] chars = text.toCharArray(); char[] chars = text.toCharArray();
HIDTransaction t; HIDTransaction t;
for (char c : chars) { for (char c : chars) {
t = getHIDTransaction(lut, c); t = getHIDTransaction(lut, deadkeyLUT, deadkeys, c, modifiers);
if (t != null) { if (t != null) {
InputStickHID.addKeyboardTransaction(t); InputStickHID.addKeyboardTransaction(t);
} }
@ -194,7 +197,7 @@ public abstract class KeyboardLayout {
} }
public static int getScanCode(int[][] lut, char c) { public static int getScanCode(int[][] lut, char c) {
for (int scanCode = 0; scanCode < 80; scanCode++) { for (int scanCode = 0; scanCode < 0x60; scanCode++) {
if (lut[scanCode][0] == -1) { if (lut[scanCode][0] == -1) {
continue; continue;
} else { } else {
@ -233,19 +236,79 @@ public abstract class KeyboardLayout {
} }
public static HIDTransaction getHIDTransaction(int[][] lut, char c) { public static boolean isDeadkey(int[] deadkeys, char c) {
if (deadkeys != null) {
for (int key : deadkeys) {
if (key == (int)c) {
return true;
}
}
}
return false;
}
public static int searchLUT(int[][] deadkeyLUT, char c, int returnIndex) {
if (deadkeyLUT != null) {
for (int i = 0; i < deadkeyLUT.length; i++) {
if (deadkeyLUT[i][2] == (int)c) {
return deadkeyLUT[i][returnIndex];
}
}
}
return -1;
}
public static int findDeadKey(int[][] deadkeyLUT, char c) {
return searchLUT(deadkeyLUT, c, 0);
}
public static int findFollowingKey(int[][] deadkeyLUT, char c) {
return searchLUT(deadkeyLUT, c, 1);
}
public static HIDTransaction getHIDTransaction(int[][] lut, int[][] deadkeyLUT, int[] deadkeys, char c, byte additionalModifierKeys) {
byte modifiers, key; byte modifiers, key;
int scanCode; int scanCode;
HIDTransaction t = new HIDTransaction(); HIDTransaction t = new HIDTransaction();
scanCode = getScanCode(lut, c); scanCode = getScanCode(lut, c);
if (scanCode > 0) { if (scanCode > 0) {
key = getKey(scanCode); key = getKey(scanCode);
modifiers = getModifiers(lut, scanCode, c); modifiers = getModifiers(lut, scanCode, c);
modifiers |= additionalModifierKeys;
t.addReport(new KeyboardReport(modifiers, (byte)0)); t.addReport(new KeyboardReport(modifiers, (byte)0));
t.addReport(new KeyboardReport(modifiers, key)); t.addReport(new KeyboardReport(modifiers, key));
t.addReport(new KeyboardReport()); t.addReport(new KeyboardReport());
//add space after deadkey!
if (isDeadkey(deadkeys, c)) {
t.addReport(new KeyboardReport((byte)0, HIDKeycodes.KEY_SPACEBAR)); //this won't work if modifiers are present!
t.addReport(new KeyboardReport());
}
} else {
//check if character can be obtained using deadkey:
int deadkey = findDeadKey(deadkeyLUT, c);
if (deadkey > 0) {
//yes it can
int following = findFollowingKey(deadkeyLUT, c);
scanCode = getScanCode(lut, (char)deadkey);
key = getKey(scanCode);
modifiers = getModifiers(lut, scanCode, (char)deadkey);
t.addReport(new KeyboardReport(modifiers, (byte)0));
t.addReport(new KeyboardReport(modifiers, key));
t.addReport(new KeyboardReport());
scanCode = getScanCode(lut, (char)following);
key = getKey(scanCode);
modifiers = getModifiers(lut, scanCode, (char)following);
t.addReport(new KeyboardReport(modifiers, (byte)0));
t.addReport(new KeyboardReport(modifiers, key));
t.addReport(new KeyboardReport());
}
} }
return t; return t;
} }
@ -260,7 +323,22 @@ public abstract class KeyboardLayout {
return RussianLayout.getInstance(); return RussianLayout.getInstance();
} else if (locale.equals(GermanLayout.getInstance().getLocaleName())) { } else if (locale.equals(GermanLayout.getInstance().getLocaleName())) {
return GermanLayout.getInstance(); return GermanLayout.getInstance();
} } else if (locale.equals(SlovakLayout.getInstance().getLocaleName())) {
return SlovakLayout.getInstance();
} else if (locale.equals(PortugueseBrazilianLayout.getInstance().getLocaleName())) {
return PortugueseBrazilianLayout.getInstance();
} else if (locale.equals(DvorakLayout.getInstance().getLocaleName())) {
return DvorakLayout.getInstance();
} else if (locale.equals(NorwegianLayout.getInstance().getLocaleName())) {
return NorwegianLayout.getInstance();
} else if (locale.equals(SwedishLayout.getInstance().getLocaleName())) {
return SwedishLayout.getInstance();
} else if (locale.equals(FrenchLayout.getInstance().getLocaleName())) {
return FrenchLayout.getInstance();
} else if (locale.equals(SpanishLayout.getInstance().getLocaleName())) {
return SpanishLayout.getInstance();
}
} }
return UnitedStatesLayout.getInstance(); return UnitedStatesLayout.getInstance();

View File

@ -111,6 +111,32 @@ public class PolishLayout extends KeyboardLayout {
}; };
public static final int DEADKEYS[] = {
0x007e
};
public static final int DEADKEY_LUT[][] = {
{ 0x007e , 0x006e , 0x0144 } ,
{ 0x007e , 0x0063 , 0x0107 } ,
{ 0x007e , 0x0078 , 0x017a } ,
{ 0x007e , 0x007a , 0x017c } ,
{ 0x007e , 0x0061 , 0x0105 } ,
{ 0x007e , 0x0073 , 0x015b } ,
{ 0x007e , 0x006c , 0x0142 } ,
{ 0x007e , 0x0065 , 0x0119 } ,
{ 0x007e , 0x006f , 0x00f3 } ,
{ 0x007e , 0x004e , 0x0143 } ,
{ 0x007e , 0x0043 , 0x0106 } ,
{ 0x007e , 0x0058 , 0x0179 } ,
{ 0x007e , 0x005a , 0x017b } ,
{ 0x007e , 0x0041 , 0x0104 } ,
{ 0x007e , 0x0053 , 0x015a } ,
{ 0x007e , 0x004c , 0x0141 } ,
{ 0x007e , 0x0045 , 0x0118 } ,
{ 0x007e , 0x004f , 0x00d3 } ,
{ 0x007e , 0x0020 , 0x007e } ,
};
private static PolishLayout instance = new PolishLayout(); private static PolishLayout instance = new PolishLayout();
private PolishLayout() { private PolishLayout() {
@ -127,7 +153,12 @@ public class PolishLayout extends KeyboardLayout {
@Override @Override
public void type(String text) { public void type(String text) {
super.type(LUT, text); super.type(LUT, DEADKEY_LUT, DEADKEYS, text, (byte)0);
}
@Override
public void type(String text, byte modifiers) {
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, modifiers);
} }
@Override @Override
@ -140,5 +171,14 @@ public class PolishLayout extends KeyboardLayout {
return LOCALE_NAME; return LOCALE_NAME;
} }
@Override
public int[][] getDeadkeyLUT() {
return DEADKEY_LUT;
}
@Override
public int[] getDeadkeys() {
return DEADKEYS;
}
} }

View File

@ -112,6 +112,9 @@ public class RussianLayout extends KeyboardLayout {
}; };
public static final int DEADKEYS[] = null;
public static final int DEADKEY_LUT[][] = null;
private static RussianLayout instance = new RussianLayout(); private static RussianLayout instance = new RussianLayout();
private RussianLayout() { private RussianLayout() {
@ -128,7 +131,12 @@ public class RussianLayout extends KeyboardLayout {
@Override @Override
public void type(String text) { public void type(String text) {
super.type(LUT, text); super.type(LUT, DEADKEY_LUT, DEADKEYS, text, (byte)0);
}
@Override
public void type(String text, byte modifiers) {
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, modifiers);
} }
@Override @Override
@ -140,5 +148,15 @@ public class RussianLayout extends KeyboardLayout {
public String getLocaleName() { public String getLocaleName() {
return LOCALE_NAME; return LOCALE_NAME;
} }
@Override
public int[][] getDeadkeyLUT() {
return DEADKEY_LUT;
}
@Override
public int[] getDeadkeys() {
return DEADKEYS;
}
} }

View File

@ -98,10 +98,10 @@ public class UnitedStatesLayout extends KeyboardLayout {
/* 50 */ { -1 , 0 , 0 , 0 , 0 , 0 } , /* 50 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
/* 51 */ { -1 , 0 , 0 , 0 , 0 , 0 } , /* 51 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
/* 52 */ { -1 , 0 , 0 , 0 , 0 , 0 } , /* 52 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
/* 53 */ { 0 , 0x002e , 0x002e , -1 , -1 , -1 } , /* 53 */ { 0 , 0x002e , 0x002e , -1 , -1 , -1 } ,
/* 54 */ { -1 , 0 , 0 , 0 , 0 , 0 } , /* 54 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
/* 55 */ { -1 , 0 , 0 , 0 , 0 , 0 } , /* 55 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
/* 56 */ { 0 , 0x005c , 0x007c , 0x001c , -1 , -1 } , /* 56 */ { 0 , 0x005c , 0x007c , 0x001c , -1 , -1 } ,
/* 57 */ { -1 , 0 , 0 , 0 , 0 , 0 } , /* 57 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
/* 58 */ { -1 , 0 , 0 , 0 , 0 , 0 } , /* 58 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
/* 59 */ { -1 , 0 , 0 , 0 , 0 , 0 } , /* 59 */ { -1 , 0 , 0 , 0 , 0 , 0 } ,
@ -114,6 +114,9 @@ public class UnitedStatesLayout extends KeyboardLayout {
}; };
public static final int DEADKEYS[] = null;
public static final int DEADKEY_LUT[][] = null;
private static UnitedStatesLayout instance = new UnitedStatesLayout(); private static UnitedStatesLayout instance = new UnitedStatesLayout();
private UnitedStatesLayout() { private UnitedStatesLayout() {
@ -130,7 +133,12 @@ public class UnitedStatesLayout extends KeyboardLayout {
@Override @Override
public void type(String text) { public void type(String text) {
super.type(LUT, text); super.type(LUT, DEADKEY_LUT, DEADKEYS, text, (byte)0);
}
@Override
public void type(String text, byte modifiers) {
super.type(LUT, DEADKEY_LUT, DEADKEYS, text, modifiers);
} }
@Override @Override
@ -141,6 +149,16 @@ public class UnitedStatesLayout extends KeyboardLayout {
@Override @Override
public String getLocaleName() { public String getLocaleName() {
return LOCALE_NAME; return LOCALE_NAME;
} }
@Override
public int[][] getDeadkeyLUT() {
return DEADKEY_LUT;
}
@Override
public int[] getDeadkeys() {
return DEADKEYS;
}
} }

View File

@ -3,9 +3,7 @@ package com.inputstick.init;
import com.inputstick.api.Packet; import com.inputstick.api.Packet;
public class BasicInitManager extends InitManager { public class BasicInitManager extends InitManager {
private boolean initDone = false;
public BasicInitManager(byte[] key) { public BasicInitManager(byte[] key) {
super(key); super(key);
@ -14,48 +12,46 @@ public class BasicInitManager extends InitManager {
@Override @Override
public void onConnected() { public void onConnected() {
/*Packet p = new Packet(false, Packet.RAW_OLD_BOOTLOADER); //compatibility /*Packet p = new Packet(false, Packet.RAW_OLD_BOOTLOADER); //compatibility with old protocol version
sendPacket(p);*/ sendPacket(p);*/
sendPacket(new Packet(true, Packet.CMD_RUN_FW)); sendPacket(new Packet(true, Packet.CMD_RUN_FW));
} }
@Override @Override
public void onData(byte[] data) { public void onData(byte[] data) {
byte cmd = data[0]; byte cmd = data[0];
byte respCode = data[1]; byte respCode = data[1];
byte param = data[1]; byte param = data[1];
if (cmd == Packet.CMD_RUN_FW) { switch (cmd) {
sendPacket(new Packet(true, Packet.CMD_GET_INFO)); case Packet.CMD_RUN_FW:
} sendPacket(new Packet(true, Packet.CMD_FW_INFO));
break;
if (cmd == Packet.CMD_GET_INFO) { case Packet.CMD_FW_INFO:
//store info onFWInfo(data, true, true, new Packet(true, Packet.CMD_INIT)); //TODO next FW: params!
sendPacket(new Packet(true, Packet.CMD_INIT)); //TODO params! break;
} case Packet.CMD_INIT:
if (respCode == Packet.RESP_OK) {
if (cmd == Packet.CMD_INIT) { initDone = true;
if (respCode == Packet.RESP_OK) { sendPacket(new Packet(true, Packet.CMD_HID_STATUS_REPORT));
initDone = true;
sendPacket(new Packet(false, Packet.CMD_HID_STATUS_REPORT));
} else {
mListener.onInitFailure(respCode);
}
}
if (cmd == Packet.CMD_HID_STATUS) {
if (initDone) {
if (param == 0x05) {
mListener.onInitReady();
} else { } else {
mListener.onInitNotReady(); mListener.onInitFailure(respCode);
} }
} break;
case Packet.CMD_INIT_AUTH:
onAuth(data, true, new Packet(true, Packet.CMD_INIT)); //TODO next FW: params!
break;
case Packet.CMD_HID_STATUS:
if (initDone) {
if (param == 0x05) {
mListener.onInitReady();
} else {
mListener.onInitNotReady();
}
}
break;
} }
} }
} }

View File

@ -1,33 +1,125 @@
package com.inputstick.init; package com.inputstick.init;
import java.util.Timer;
import java.util.TimerTask;
import com.inputstick.api.InputStickError;
import com.inputstick.api.Packet; import com.inputstick.api.Packet;
import com.inputstick.api.PacketManager; import com.inputstick.api.PacketManager;
public class InitManager { public class InitManager {
public static final int DEFAULT_INIT_TIMEOUT = 60000; //60s init timeout
protected PacketManager mPacketManager; protected PacketManager mPacketManager;
protected InitManagerListener mListener; protected InitManagerListener mListener;
protected byte[] mKey; protected byte[] mKey;
protected DeviceInfo mInfo;
protected boolean initDone;
//private Timer t;
public InitManager(byte[] key) { public InitManager(byte[] key) {
mKey = key; mKey = key;
} }
public void init(InitManagerListener listener, PacketManager packetManager) {
public DeviceInfo getDeviceInfo() {
return mInfo;
}
public boolean isEncrypted() {
return mPacketManager.isEncrypted();
}
public void init(InitManagerListener listener, PacketManager packetManager) {
mListener = listener; mListener = listener;
mPacketManager = packetManager; mPacketManager = packetManager;
initDone = false;
} }
//WRONG THREAD!
/*public void startTimeoutCountdown(int timeout) {
t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
if ( !initDone) {
mListener.onInitFailure(InputStickError.ERROR_INIT_TIMEDOUT);
}
}
}, timeout);
}*/
public void onConnected() { public void onConnected() {
mListener.onInitReady(); mListener.onInitReady();
} }
public void onData(byte[] data) { public void onData(byte[] data) {
//byte cmd = data[0];
//byte param = data[1];
} }
public void sendPacket(Packet p) { public void sendPacket(Packet p) {
mPacketManager.sendPacket(p); mPacketManager.sendPacket(p);
} }
public void onFWInfo(byte[] data, boolean authenticate, boolean enableEncryption, Packet sendNext) {
mInfo = new DeviceInfo(data);
if (authenticate) {
if (mInfo.isPasswordProtected()) {
if (mKey != null) {
//authenticate
sendPacket(mPacketManager.encPacket(enableEncryption));
} else {
mListener.onInitFailure(InputStickError.ERROR_SECURITY_NO_KEY);
}
} else {
if (mKey != null) {
//possible scenarios: FW upgrade / password removed using other device/app / tampering!
mListener.onInitFailure(InputStickError.ERROR_SECURITY_NOT_PROTECTED);
}
sendPacket(sendNext);
}
} else {
sendPacket(sendNext);
}
}
public void onAuth(byte[] data, boolean enableOutEncryption, Packet sendNext) {
byte respCode = data[1];
switch (respCode) {
case Packet.RESP_OK:
byte[] cmp = new byte[16];
//TODO check length!
System.arraycopy(data, 2, cmp, 0, 16);
if (mPacketManager.setEncryption(cmp, enableOutEncryption)) {
sendPacket(sendNext);
} else {
mListener.onInitFailure(InputStickError.ERROR_SECURITY_CHALLENGE);
}
break;
case 0x20:
mListener.onInitFailure(InputStickError.ERROR_SECURITY_INVALID_KEY);
break;
case 0x21:
mListener.onInitFailure(InputStickError.ERROR_SECURITY_NOT_PROTECTED);
break;
case Packet.RESP_UNKNOWN_CMD:
mListener.onInitFailure(InputStickError.ERROR_SECURITY_NOT_SUPPORTED);
break;
default:
mListener.onInitFailure(InputStickError.ERROR_SECURITY);
}
}
} }

View File

@ -7,11 +7,24 @@
package com.inputstick.api; package com.inputstick.api;
public final class R { public final class R {
public static final class dimen {
public static final int activity_horizontal_margin = 0x7f050000;
public static final int activity_vertical_margin = 0x7f050001;
}
public static final class drawable { public static final class drawable {
public static final int ic_launcher = 0x7f020000; public static final int ic_launcher = 0x7f020000;
} }
public static final class id {
public static final int action_settings = 0x7f0a0003;
}
public static final class menu {
public static final int install_utility = 0x7f090000;
}
public static final class string { public static final class string {
public static final int action_settings = 0x7f060002;
public static final int app_name = 0x7f060000; public static final int app_name = 0x7f060000;
public static final int hello_world = 0x7f060003;
public static final int title_activity_install_utility = 0x7f060001;
} }
public static final class style { public static final class style {
public static final int AppBaseTheme = 0x7f070000; public static final int AppBaseTheme = 0x7f070000;

View File

@ -21,6 +21,11 @@ public final class R {
(such as screen margins) for screens with more than 820dp of available width. This (such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively).
Default screen margins, per the Android Design guidelines.
Customize dimensions originally defined in res/values/dimens.xml (such as
screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
Default screen margins, per the Android Design guidelines. Default screen margins, per the Android Design guidelines.
Example customization of dimensions originally defined in res/values/dimens.xml Example customization of dimensions originally defined in res/values/dimens.xml
@ -46,23 +51,26 @@ public final class R {
public static final int fragment_main=0x7f030001; public static final int fragment_main=0x7f030001;
} }
public static final class menu { public static final class menu {
public static final int main=0x7f090000; public static final int install_utility=0x7f090000;
public static final int main=0x7f090001;
} }
public static final class string { public static final class string {
public static final int action_input_stick=0x7f060005; public static final int action_input_stick=0x7f060007;
public static final int action_settings=0x7f060001; public static final int action_settings=0x7f060002;
public static final int action_type_enter=0x7f060007; public static final int action_type_enter=0x7f060009;
public static final int action_type_tab=0x7f060006; public static final int action_type_tab=0x7f060008;
public static final int action_type_user_tab_pass_enter=0x7f060008; public static final int action_type_user_tab_pass_enter=0x7f06000a;
public static final int app_name=0x7f060000; public static final int app_name=0x7f060000;
/** Strings related to Settings /** Strings related to Settings
*/ */
public static final int configure_plugin=0x7f06000a; public static final int configure_plugin=0x7f06000c;
public static final int kp2aplugin_author=0x7f060004; public static final int hello_world=0x7f060003;
public static final int kp2aplugin_shortdesc=0x7f060003; public static final int kp2aplugin_author=0x7f060006;
public static final int kp2aplugin_title=0x7f060002; public static final int kp2aplugin_shortdesc=0x7f060005;
public static final int layout_title=0x7f06000b; public static final int kp2aplugin_title=0x7f060004;
public static final int title_activity_settings=0x7f060009; public static final int layout_title=0x7f06000d;
public static final int title_activity_install_utility=0x7f060001;
public static final int title_activity_settings=0x7f06000b;
} }
public static final class style { public static final class style {
/** /**

View File

@ -8,17 +8,31 @@
<string name="layout_title">Host keyboard layout</string> <string name="layout_title">Host keyboard layout</string>
<string-array name="layout_names"> <string-array name="layout_names">
<item>English (United States) - en-US</item> <item>English (US)</item>
<item>German - de-DE</item> <item>German</item>
<item>Polish - pl-PL</item> <item>Polish</item>
<item>Russian - ru-RU</item> <item>Russian</item>
<item>Slovak</item>
<item>Portuguese (BR)</item>
<item>Norwegian</item>
<item>Swedish</item>
<item>French</item>
<item>Spanish</item>
<item>English (Dvorak)</item>
</string-array> </string-array>
<string-array name="layout_values"> <string-array name="layout_values">
<item>en-US</item> <item>en-US</item>
<item>de-DE</item> <item>de-DE</item>
<item>pl-PL</item> <item>pl-PL</item>
<item>ru-RU</item> <item>ru-RU</item>
<item>sk-SK</item>
<item>pt-BR</item>
<item>nb-NO</item>
<item>sv-SE</item>
<item>fr-FR</item>
<item>es-ES</item>
<item>en-DV</item>
</string-array> </string-array>
</resources> </resources>