2014-06-01 01:06:58 -04:00
package com.inputstick.api.basic ;
2014-09-29 15:29:52 -04:00
import java.util.Timer ;
import java.util.TimerTask ;
2014-06-01 01:06:58 -04:00
import java.util.Vector ;
2014-09-29 15:29:52 -04:00
import android.app.AlertDialog ;
2014-06-01 01:06:58 -04:00
import android.app.Application ;
2014-09-29 15:29:52 -04:00
import android.content.Context ;
import android.content.DialogInterface ;
import android.content.Intent ;
import android.net.Uri ;
2014-06-01 01:06:58 -04:00
import com.inputstick.api.BTConnectionManager ;
import com.inputstick.api.ConnectionManager ;
import com.inputstick.api.HIDInfo ;
import com.inputstick.api.IPCConnectionManager ;
import com.inputstick.api.InputStickDataListener ;
2014-09-29 15:29:52 -04:00
import com.inputstick.api.InputStickError ;
2014-06-01 01:06:58 -04:00
import com.inputstick.api.InputStickStateListener ;
2014-09-29 15:29:52 -04:00
import com.inputstick.api.OnEmptyBufferListener ;
2014-06-01 01:06:58 -04:00
import com.inputstick.api.Packet ;
2014-09-29 15:29:52 -04:00
import com.inputstick.api.Util ;
2015-06-23 11:25:27 -04:00
import com.inputstick.api.hid.HIDReport ;
2014-06-01 01:06:58 -04:00
import com.inputstick.api.hid.HIDTransaction ;
import com.inputstick.api.hid.HIDTransactionQueue ;
2015-06-23 11:25:27 -04:00
import com.inputstick.init.BasicInitManager ;
import com.inputstick.init.DeviceInfo ;
2014-06-01 01:06:58 -04:00
import com.inputstick.init.InitManager ;
public class InputStickHID implements InputStickStateListener , InputStickDataListener {
2014-09-29 15:29:52 -04:00
public static final int INTERFACE_KEYBOARD = 0 ;
public static final int INTERFACE_CONSUMER = 1 ;
public static final int INTERFACE_MOUSE = 2 ;
2014-06-01 01:06:58 -04:00
2014-09-29 15:29:52 -04:00
//private static final String mTag = "InputStickBasic";
2014-06-01 01:06:58 -04:00
private static ConnectionManager mConnectionManager ;
private static Vector < InputStickStateListener > mStateListeners = new Vector < InputStickStateListener > ( ) ;
2015-06-23 11:25:27 -04:00
protected static Vector < OnEmptyBufferListener > mBufferEmptyListeners = new Vector < OnEmptyBufferListener > ( ) ;
2014-09-29 15:29:52 -04:00
2014-06-01 01:06:58 -04:00
private static InputStickHID instance = new InputStickHID ( ) ;
2014-09-29 15:29:52 -04:00
private static HIDInfo mHIDInfo ;
2015-06-23 11:25:27 -04:00
private static DeviceInfo mDeviceInfo ;
2014-06-01 01:06:58 -04:00
private static HIDTransactionQueue keyboardQueue ;
private static HIDTransactionQueue mouseQueue ;
private static HIDTransactionQueue consumerQueue ;
2015-06-23 11:25:27 -04:00
2014-09-29 15:29:52 -04:00
// >= FW 0.93
private static Timer t1 ;
private static boolean constantUpdateMode ;
2015-06-23 11:25:27 -04:00
private static int mKeyboardReportMultiplier ; //enables "slow" typing by multiplying HID reports
2014-06-01 01:06:58 -04:00
private InputStickHID ( ) {
}
public static InputStickHID getInstance ( ) {
return instance ;
}
private static void init ( ) {
2014-09-29 15:29:52 -04:00
mHIDInfo = new HIDInfo ( ) ;
constantUpdateMode = false ;
keyboardQueue = new HIDTransactionQueue ( INTERFACE_KEYBOARD , mConnectionManager ) ;
mouseQueue = new HIDTransactionQueue ( INTERFACE_MOUSE , mConnectionManager ) ;
consumerQueue = new HIDTransactionQueue ( INTERFACE_CONSUMER , mConnectionManager ) ;
2014-06-01 01:06:58 -04:00
mConnectionManager . addStateListener ( instance ) ;
mConnectionManager . addDataListener ( instance ) ;
mConnectionManager . connect ( ) ;
}
2015-06-23 11:25:27 -04:00
//direct Bluetooth connection, custom InitManager (use BT2.0)
//mac - Bluetooth MAC address
//key - use null if InputStick is not password protected, otherwise provide 16byte key: MD5(password)
2014-06-01 01:06:58 -04:00
public static void connect ( Application app , String mac , byte [ ] key , InitManager initManager ) {
2015-06-23 11:25:27 -04:00
connect ( app , mac , key , initManager , false ) ;
2014-06-01 01:06:58 -04:00
}
2015-06-23 11:25:27 -04:00
//direct Bluetooth connection, custom InitManager
//is40 - use true if target device is BluetoothLowEnergy (4.0) type
//mac - Bluetooth MAC address
//key - use null if InputStick is not password protected, otherwise provide 16byte key: MD5(password)
//is40 - use true if target device is BluetoothLowEnergy (4.0) type
public static void connect ( Application app , String mac , byte [ ] key , InitManager initManager , boolean isBT40 ) {
mConnectionManager = new BTConnectionManager ( initManager , app , mac , key , isBT40 ) ;
init ( ) ;
}
2014-06-01 01:06:58 -04:00
//direct Bluetooth connection
2015-06-23 11:25:27 -04:00
//key - use null if InputStick is not password protected, otherwise provide 16byte key: MD5(password)
//is40 - use true if target device is BluetoothLowEnergy (4.0) type
public static void connect ( Application app , String mac , byte [ ] key , boolean isBT40 ) {
mConnectionManager = new BTConnectionManager ( new BasicInitManager ( key ) , app , mac , key , isBT40 ) ;
2014-06-01 01:06:58 -04:00
init ( ) ;
}
2015-06-23 11:25:27 -04:00
//direct Bluetooth connection (use BT2.0)
//key - use null if InputStick is not password protected, otherwise provide 16byte key: MD5(password)
public static void connect ( Application app , String mac , byte [ ] key ) {
connect ( app , mac , key , false ) ;
}
//use InputStickUtility to connect with InputStick
2014-06-01 01:06:58 -04:00
public static void connect ( Application app ) {
mConnectionManager = new IPCConnectionManager ( app ) ;
init ( ) ;
}
2015-06-23 11:25:27 -04:00
//closes Bluetooth connection
2014-06-01 01:06:58 -04:00
public static void disconnect ( ) {
2015-06-23 11:25:27 -04:00
if ( mConnectionManager ! = null ) {
mConnectionManager . disconnect ( ) ;
}
2014-06-01 01:06:58 -04:00
}
2015-06-23 11:25:27 -04:00
//requests USB host to resume from sleep mode (must be supported by USB host)
//note: InputStick will most likely be in STATE_CONNECTED state instead of STATE_READY
public static void wakeUpUSBHost ( ) {
if ( isConnected ( ) ) {
Packet p = new Packet ( false , Packet . CMD_USB_RESUME ) ;
InputStickHID . sendPacket ( p ) ;
mConnectionManager . sendPacket ( p ) ;
}
}
public static DeviceInfo getDeviceInfo ( ) {
if ( ( isReady ( ) ) & & ( mDeviceInfo ! = null ) ) {
return mDeviceInfo ;
} else {
return null ;
}
}
public static HIDInfo getHIDInfo ( ) {
return mHIDInfo ;
}
//returns current connection state
2014-06-01 01:06:58 -04:00
public static int getState ( ) {
if ( mConnectionManager ! = null ) {
return mConnectionManager . getState ( ) ;
} else {
return ConnectionManager . STATE_DISCONNECTED ;
}
}
2015-06-23 11:25:27 -04:00
//returns last error code
2014-09-29 15:29:52 -04:00
public static int getErrorCode ( ) {
if ( mConnectionManager ! = null ) {
return mConnectionManager . getErrorCode ( ) ;
} else {
return InputStickError . ERROR_UNKNOWN ;
}
}
2015-06-23 11:25:27 -04:00
//returns true if Bluetooth connection is established between the device and InputStick.
//note - InputStick may be not ready yet to accept keyboard/mouse data
public static boolean isConnected ( ) {
if ( ( getState ( ) = = ConnectionManager . STATE_READY ) | | ( getState ( ) = = ConnectionManager . STATE_CONNECTED ) ) {
return true ;
} else {
return false ;
}
}
2014-09-29 15:29:52 -04:00
2015-06-23 11:25:27 -04:00
//returns true if InputStick is ready for keyboard/mouse data
2014-06-01 01:06:58 -04:00
public static boolean isReady ( ) {
if ( getState ( ) = = ConnectionManager . STATE_READY ) {
return true ;
} else {
return false ;
}
}
2015-06-23 11:25:27 -04:00
//adds state listener. Listeners will be notified about change of connection state
2014-06-01 01:06:58 -04:00
public static void addStateListener ( InputStickStateListener listener ) {
if ( listener ! = null ) {
2014-09-29 15:29:52 -04:00
if ( ! mStateListeners . contains ( listener ) ) {
mStateListeners . add ( listener ) ;
}
2014-06-01 01:06:58 -04:00
}
}
2015-06-23 11:25:27 -04:00
2014-06-01 01:06:58 -04:00
public static void removeStateListener ( InputStickStateListener listener ) {
if ( listener ! = null ) {
mStateListeners . remove ( listener ) ;
}
}
2014-09-29 15:29:52 -04:00
2015-06-23 11:25:27 -04:00
//adds buffer listener. Listeners will be notified when local (application) or remote (InputStick) HID report buffer is empty
2014-09-29 15:29:52 -04:00
public static void addBufferEmptyListener ( OnEmptyBufferListener listener ) {
if ( listener ! = null ) {
2015-06-23 11:25:27 -04:00
if ( ! mBufferEmptyListeners . contains ( listener ) ) {
mBufferEmptyListeners . add ( listener ) ;
}
2014-09-29 15:29:52 -04:00
}
}
public static void removeBufferEmptyListener ( OnEmptyBufferListener listener ) {
if ( listener ! = null ) {
2015-06-23 11:25:27 -04:00
mBufferEmptyListeners . remove ( listener ) ;
}
}
public static Vector < OnEmptyBufferListener > getBufferEmptyListeners ( ) {
return mBufferEmptyListeners ;
}
//reports added to keyboard queue will be multiplied by reportMultiplier times. This allows to type text slower.
//NOTE: using high multiplier values can make transactions larger than available buffer and as a result they will be splitted!
//this can cause problem if connection is lost (stuck keys)
//TIP: remember to manually set multiplier value back to 1 when slow typing mode is no longer needed!!!
public static void setKeyboardReportMultiplier ( int reportMultiplier ) {
mKeyboardReportMultiplier = reportMultiplier ;
2014-09-29 15:29:52 -04:00
}
2014-06-01 01:06:58 -04:00
2015-06-23 11:25:27 -04:00
//adds transaction to keyboard queue. If possible, all reports form a signel transactions will be sent in a single packet
2014-06-01 01:06:58 -04:00
public static void addKeyboardTransaction ( HIDTransaction transaction ) {
2015-06-23 11:25:27 -04:00
if ( ( transaction ! = null ) & & ( keyboardQueue ! = null ) ) {
//keyboardQueue.addTransaction(transaction);
if ( mKeyboardReportMultiplier > 1 ) {
HIDTransaction multipliedTransaction = new HIDTransaction ( ) ;
HIDReport r ;
for ( int i = 0 ; i < transaction . getReportsCount ( ) ; i + + ) {
r = transaction . getHIDReportAt ( i ) ;
for ( int j = 0 ; j < mKeyboardReportMultiplier ; j + + ) {
multipliedTransaction . addReport ( r ) ;
}
}
keyboardQueue . addTransaction ( multipliedTransaction ) ;
} else {
keyboardQueue . addTransaction ( transaction ) ;
}
}
2014-06-01 01:06:58 -04:00
}
2015-06-23 11:25:27 -04:00
//adds transaction to mouse queue. If possible, all reports form a signel transactions will be sent in a single packet
2014-06-01 01:06:58 -04:00
public static void addMouseTransaction ( HIDTransaction transaction ) {
2015-06-23 11:25:27 -04:00
if ( ( transaction ! = null ) & & ( mouseQueue ! = null ) ) {
mouseQueue . addTransaction ( transaction ) ;
}
2014-06-01 01:06:58 -04:00
}
2015-06-23 11:25:27 -04:00
//adds transaction to consumer queue. If possible, all reports form a signel transactions will be sent in a single packet
2014-06-01 01:06:58 -04:00
public static void addConsumerTransaction ( HIDTransaction transaction ) {
2015-06-23 11:25:27 -04:00
if ( ( transaction ! = null ) & & ( consumerQueue ! = null ) ) {
consumerQueue . addTransaction ( transaction ) ;
}
2014-06-01 01:06:58 -04:00
}
2015-06-23 11:25:27 -04:00
//removes all reports from keyboard buffer
2014-09-29 15:29:52 -04:00
public static void clearKeyboardBuffer ( ) {
2015-06-23 11:25:27 -04:00
if ( keyboardQueue ! = null ) {
keyboardQueue . clearBuffer ( ) ;
}
2014-09-29 15:29:52 -04:00
}
2015-06-23 11:25:27 -04:00
//removes all reports from mouse buffer
2014-09-29 15:29:52 -04:00
public static void clearMouseBuffer ( ) {
2015-06-23 11:25:27 -04:00
if ( mouseQueue ! = null ) {
mouseQueue . clearBuffer ( ) ;
}
2014-09-29 15:29:52 -04:00
}
2015-06-23 11:25:27 -04:00
//removes all reports from consumer buffer
2014-09-29 15:29:52 -04:00
public static void clearConsumerBuffer ( ) {
2015-06-23 11:25:27 -04:00
if ( consumerQueue ! = null ) {
consumerQueue . clearBuffer ( ) ;
}
2014-09-29 15:29:52 -04:00
}
2015-06-23 11:25:27 -04:00
//sends packet to InputStick
2014-06-01 01:06:58 -04:00
public static boolean sendPacket ( Packet p ) {
if ( mConnectionManager ! = null ) {
mConnectionManager . sendPacket ( p ) ;
return true ;
} else {
return false ;
}
}
@Override
2014-09-29 15:29:52 -04:00
public void onStateChanged ( int state ) {
if ( ( state = = ConnectionManager . STATE_DISCONNECTED ) & & ( t1 ! = null ) ) {
t1 . cancel ( ) ;
t1 = null ;
}
2014-06-01 01:06:58 -04:00
for ( InputStickStateListener listener : mStateListeners ) {
listener . onStateChanged ( state ) ;
}
}
2014-09-29 15:29:52 -04:00
2015-06-23 11:25:27 -04:00
//returns true if local (application) keyboard report buffer is empty. It is still possible that there are reports queued in InputStick's buffer.
2014-09-29 15:29:52 -04:00
public static boolean isKeyboardLocalBufferEmpty ( ) {
2015-06-23 11:25:27 -04:00
if ( keyboardQueue ! = null ) {
return keyboardQueue . isLocalBufferEmpty ( ) ;
} else {
return true ;
}
2014-09-29 15:29:52 -04:00
}
2015-06-23 11:25:27 -04:00
//returns true if local (application) mouse report buffer is empty. It is still possible that there are reports queued in InputStick's buffer.
2014-09-29 15:29:52 -04:00
public static boolean isMouseLocalBufferEmpty ( ) {
2015-06-23 11:25:27 -04:00
if ( mouseQueue ! = null ) {
return mouseQueue . isLocalBufferEmpty ( ) ;
} else {
return true ;
}
2014-09-29 15:29:52 -04:00
}
2015-06-23 11:25:27 -04:00
//returns true if local (application) consumer report buffer is empty. It is still possible that there are reports queued in InputStick's buffer.
2014-09-29 15:29:52 -04:00
public static boolean isConsumerLocalBufferEmpty ( ) {
2015-06-23 11:25:27 -04:00
if ( consumerQueue ! = null ) {
return consumerQueue . isLocalBufferEmpty ( ) ;
} else {
return true ;
}
2014-09-29 15:29:52 -04:00
}
2015-06-23 11:25:27 -04:00
//returns true if remote (InputStick) keyboard report buffer is empty. No more keyboard reports will be send to USB host
2014-09-29 15:29:52 -04:00
public static boolean isKeyboardRemoteBufferEmpty ( ) {
2015-06-23 11:25:27 -04:00
if ( keyboardQueue ! = null ) {
return keyboardQueue . isRemoteBufferEmpty ( ) ;
} else {
return true ;
}
2014-09-29 15:29:52 -04:00
}
2015-06-23 11:25:27 -04:00
//returns true if remote (InputStick) mouse report buffer is empty. No more mouse reports will be send to USB host
2014-09-29 15:29:52 -04:00
public static boolean isMouseRemoteBufferEmpty ( ) {
2015-06-23 11:25:27 -04:00
if ( mouseQueue ! = null ) {
return mouseQueue . isRemoteBufferEmpty ( ) ;
} else {
return true ;
}
2014-09-29 15:29:52 -04:00
}
2015-06-23 11:25:27 -04:00
//returns true if remote (InputStick) consumer report buffer is empty. No more consumer reports will be send to USB host
2014-09-29 15:29:52 -04:00
public static boolean isConsumerRemoteBufferEmpty ( ) {
2015-06-23 11:25:27 -04:00
if ( consumerQueue ! = null ) {
return consumerQueue . isRemoteBufferEmpty ( ) ;
} else {
return true ;
}
2014-09-29 15:29:52 -04:00
}
2014-06-01 01:06:58 -04:00
@Override
public void onInputStickData ( byte [ ] data ) {
2015-06-23 11:25:27 -04:00
byte cmd = data [ 0 ] ;
if ( cmd = = Packet . CMD_FW_INFO ) {
mDeviceInfo = new DeviceInfo ( data ) ;
}
if ( cmd = = Packet . CMD_HID_STATUS ) {
2014-06-01 01:06:58 -04:00
mHIDInfo . update ( data ) ;
2014-09-29 15:29:52 -04:00
if ( mHIDInfo . isSentToHostInfoAvailable ( ) ) {
// >= 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 ) ;
}
2014-06-01 01:06:58 -04:00
}
InputStickKeyboard . setLEDs ( mHIDInfo . getNumLock ( ) , mHIDInfo . getCapsLock ( ) , mHIDInfo . getScrollLock ( ) ) ;
}
}
2015-06-23 11:25:27 -04:00
//returns "Download InputStickUtility" dialog, if connection attepmt resulted in error caused by InputStickUtility not being installed on the device.
//otherwise returns null
2014-09-29 15:29:52 -04:00
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 " ) ;
2015-06-23 11:25:27 -04:00
downloadDialog . setMessage ( " InputStickUtility is required to run this application. Download now? \ nNote: InputStick USB receiver hardware is also required. " ) ;
2014-09-29 15:29:52 -04:00
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 ;
}
2015-06-23 11:25:27 -04:00
}
2014-09-29 15:29:52 -04:00
2014-06-01 01:06:58 -04:00
}