My project is to read button inputs from my car's steering wheel controls and convert them (using a Teensy 3.2 Arduino-alike) into android system actions of my choosing (e.g. Volume Up, Next Track, OK Google).
I have tried several different modes that the Teensy provides in order to solve this, initially as a keyboard emulation, followed by Joystick emulation and now finally a Raw HID device. All of these modes work flawlessly on Windows and Linux but do not work on android (I've tried on the target device running Android 4.1 and an Android 5 device, neither work).
The closest I've managed to get this working is as a RawHID Device with a small app i wrote to decode the packets and convert to system actions. This actually works...for about 2-5 button presses. Then nothing. In order to get my next 2-5 button presses i have to unplug the device and restart the program. The program halts on thisConnection.requestWait() forever. In an older version i used bulkTransfer and it has a similar effect, returning -1 and no data perpetually after 2-5 button presses.
Code for OpenConnection:
public boolean OpenConnection(UsbManager pUsbManager)
{
if(ActiveDevice == null) return false;
if(!hasPermission) return false;
if(ActiveConnection != null && ActiveEndpoint != null) return true;
if(hasAssociatedUsbDevice()) {
ActiveInterface = ActiveDevice.getInterface(InterfaceIndex);
ActiveEndpoint = ActiveInterface.getEndpoint(EndpointIndex);
ActiveConnection = pUsbManager.openDevice(ActiveDevice);
ActiveConnection.claimInterface(ActiveInterface, true);
ActiveRequest = new UsbRequest();
ActiveRequest.initialize(ActiveConnection,ActiveEndpoint);
return true;
}
return false;
}
Code for the device loop (run on a separate low priority thread)
private void deviceLoop(Config.HIDDevice pHIDDevice)
{
try
{
if (!pHIDDevice.OpenConnection(mUsbManager)) return;
ByteBuffer dataBufferIn = ByteBuffer.allocate(64);
//String activeAppName = mAppDetector.getForegroundAppName(); //TODO: Refactor, causing excessive memory alloc
String activeAppName = null;
Config.AppProfile activeProfile = pHIDDevice.getAppProfile(activeAppName);
while (!mExitDeviceThreads)
{
UsbDeviceConnection thisConnection = pHIDDevice.getActiveConnection();
if (thisConnection == null) break; //connection dropped
UsbRequest thisRequest = pHIDDevice.getActiveRequest();
if (thisRequest == null) break; //connection dropped
thisRequest.queue(dataBufferIn, dataBufferIn.capacity());
if (thisConnection.requestWait() == thisRequest)
{
byte[] dataIn = dataBufferIn.array();
for (Config.ButtonPacketMapping thisButtonMapping : pHIDDevice.getButtonPacketMappings())
{
if (thisButtonMapping.Update(dataIn))
{
for (Config.ButtonAction thisButtonAction : activeProfile.getButtonActions(thisButtonMapping.getName()))
{
if (thisButtonMapping.getLastValue() == false && thisButtonMapping.getValue() == true)
{
if (thisButtonAction.buttonAction == Config.ButtonAction.eButtonActionType.Press)
{
thisButtonAction.Set();
}
}
else if (thisButtonMapping.getLastValue() == true && thisButtonMapping.getValue() == false)
{
if (thisButtonAction.buttonAction == Config.ButtonAction.eButtonActionType.Release)
{
thisButtonAction.Set();
}
}
}
}
}
}
else
{
break; //Connection dropped or something went very wrong
}
}
}
finally
{
pHIDDevice.CloseConnection();
}
}
So my question more succinctly is:
Has anyone managed to get a Teensy Arduino to interface with Android in any way at all over USB? Is there anything wrong with my HID approach to cause this "stalling" problem?
In the end, I switched to an Arduino Pro Micro which has native USB support, and used the Project-HID library using the "Consumer Device" method. This works perfectly with any OS/Hardware combo that I've tried.
Related
we are using symbol ls4278 bluetooth scanner to integrate in android application. It is connected as keyboard and types scanned barcode in any edittext field...
After scanner OnKeyUp event is called.
public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
{
..
}
I was searching documentation and android sdk, but I can't found such one. But for LI4278 they have android sdk here : https://www.zebra.com/us/en/support-downloads/scanners/general-purpose-scanners/li4278.html
here is also documentation for sdk but LS4278 is not in supported device list.
Does anyone implemented LS4278 scanner in android devices?
The LS4278 product page is here: https://www.zebra.com/us/en/support-downloads/scanners/general-purpose-scanners/ls4278.html and lists support for the "Windows Scanner SDK" ONLY. The LS4278 was discontinued on September 24th 2012 so I am not surprised it does not have Android support. As you say, its successor, the LI4278 does have Android support. As the other answer states, if you want more control over how you receive data then I suggest trying SPP if the scanner supports it.
If it works as a bluetooth keyboard, then no support is needed. Just capture the key events, and react to the data when enter is pressed. Its just a mediocre experience and can mess with on screen keyboards and stop them from using an actual bluetooth keyboard. If the scanner supports SPP, you can pretty trivially parse the scan data out of it via bluetooth serial (I did this about 2 weeks ago).
BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
if(bta != null) {
Set<BluetoothDevice> devices = bta.getBondedDevices();
for (final BluetoothDevice device : devices) {
BluetoothClass btClass = device.getBluetoothClass();
if (btClass.getMajorDeviceClass() == 0x1f00) {
//Only look at devices which are considered uncategorized, so we don't screw up any bt headset, leyboard, mouse, etc
new DeviceThread(device).start();
}
}
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
if (state == BluetoothDevice.BOND_BONDED) {
new DeviceThread(device).start();
} else if (state == BluetoothDevice.BOND_NONE) {
DeviceThread thread = threadMap.get(device.getAddress());
if (thread != null) {
thread.interrupt();
}
}
}
}, filter);
}
private class DeviceThread extends Thread {
private BluetoothDevice device;
public DeviceThread(BluetoothDevice device) {
this.device = device;
threadMap.put(device.getAddress(), this);
}
#Override
public void run() {
try {
BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
socket.connect();
InputStream inputStream = socket.getInputStream();
while (!Thread.interrupted() && socket.isConnected()) {
inputStream.skip(5);
String data = "";
do {
int code = inputStream.read();
char character = (char) code;
data = data + character;
} while (inputStream.available() > 0);
data = data.substring(0, data.length() - 2);
if (scannerEventListener != null) {
scannerEventListener.onScan(data);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
Log.d("GABE", "Exiting thread");
}
}
This code will register for bluetooth devices being paired, then check and see if they're unknown device types (scanners don't have a device class). If so, it will start a thread to listen for that device. When its unbonded, it will interrupt that thread. On the thread it opens up a SPP connection to the device and waits for input. When it gets it, it parses the input and sends the result to a listener.
For this to work, the scanner needs to be in SPP mode. Some scanners support it, some don't, and how to set it into that mode varies (the one on my desk has a control barcode I need to scan to set the mode). Generally I would code for it to accept either type of input- hardware keyboard mode or SPP.
I want to detect in my Android application if a HDMI cable is connected.
I found a way how to do that:
private boolean isHdmiSwitchSet() {
// The file '/sys/devices/virtual/switch/hdmi/state' holds an int -- if it's 1 then an HDMI device is connected.
// An alternative file to check is '/sys/class/switch/hdmi/state' which exists instead on certain devices.
File switchFile = new File("/sys/devices/virtual/switch/hdmi/state");
if (!switchFile.exists()) {
switchFile = new File("/sys/class/switch/hdmi/state");
}
try {
Scanner switchFileScanner = new Scanner(switchFile);
int switchValue = switchFileScanner.nextInt();
switchFileScanner.close();
return switchValue > 0;
} catch (Exception e) {
return false;
}
}
The problem now ist that I want to do something if HDMI is connected but I dont want to run a threat checking every second if the boolean has flipped. Is there a better way?
I am working with samsung t365(android 4.4.4) and it is communicate with one uart quite good. But I need to use multiple (two) uart to communicate with one android device's only one port. When I use usb hub (S-LİNK SL-U602 USB 2.0) and use uart term application from play market I can see both uart by selecting ports. Question is:
Is it possible to use com ports programmatically choosing and do the work,
What might be challange (with multiple uarts)
Thank you.
Try with android-serialport-api library.
As per RS232 standard, only one device per COM port is allowed.
In FTdriver.java there is a begin method which can help to connect multi device. But, in default it is setted for first device. as you can see in comment line. It should be modified for multi connection.
reference: https://github.com/ksksue/FTDriver/blob/master/FTDriver/src/jp/ksksue/driver/serial/FTDriver.java
// Open an FTDI USB Device
public boolean begin(int baudrate) {
for (UsbDevice device : mManager.getDeviceList().values()) {
Log.i(TAG, "Devices : " + device.toString());
getPermission(device);
if (!mManager.hasPermission(device)) {
return false;
}
// TODO: support any connections(current version find a first
// device)
if (getUsbInterfaces(device)) {
break;
}
}
if (mSelectedDeviceInfo == null) {
return false;
}
if (mDevice == null) {
return false;
}
if (mDevice.getDeviceClass() == UsbConstants.USB_CLASS_COMM) {
isCDC = true;
} else {
isCDC = false;
}
mFTDIEndpointIN = new UsbEndpoint[mSelectedDeviceInfo.mNumOfChannels];
mFTDIEndpointOUT = new UsbEndpoint[mSelectedDeviceInfo.mNumOfChannels];
if (isCDC) {
if (!getCdcEndpoint()) {
return false;
}
} else {
if (!setFTDIEndpoints(mInterface,
mSelectedDeviceInfo.mNumOfChannels)) {
return false;
}
}
if (isCDC) {
initCdcAcm(mDeviceConnection, baudrate);
} else {
initFTDIChip(mDeviceConnection, baudrate);
}
Log.i(TAG, "Device Serial : " + mDeviceConnection.getSerial());
return true;
}
I have read this stackoverflow thread already and I tried using the code given in that answer to find out if I run my code on the emulator or on a real device:
import android.content.ContentResolver;
import android.provider.Settings.Secure;
...
mTextView.setText(Secure.getString(getContentResolver(), Secure.ANDROID_ID));
On my real device it returns "2bccce3...", however on the emulator it does not return null, but also a string "bd9f8..."
Ideas how to find out if emulator or real device from code would be highly appreciated
This should do it:
boolean inEmulator = false;
String brand = Build.BRAND;
if (brand.compareTo("generic") == 0)
{
inEmulator = true;
}
EDIT:
boolean inEmulator = "generic".equals(Build.BRAND.toLowerCase());
With the advent of the new Intel native emulator the above mentioned methods did not work any longer. Now I am using this code snippet which works on both Intel and ARM emulators:
if (Build.MODEL.contains("google_sdk") ||
Build.MODEL.contains("Emulator") ||
Build.MODEL.contains("Android SDK")) {
RunsInEmulator = true;
}
There's a rather old thread on Android Developers group that suggests checking the number of sensors on the device. Something like this might work:
SensorManager manager = (SensorManager) getSystemService(SENSOR_SERVICE);
if (manager.getSensorList(Sensor.TYPE_ALL).isEmpty()) {
// running on an emulator
} else {
// running on a device
}
I haven't tried this, so I have no idea how reliable the suggestion is. (Perhaps some emulators now report some sensors; perhaps some devices report no sensors. [Is there an Android toothbrush yet?]) But it can't be worse than checking for a null ANDROID_ID (which doesn't work as of 2.2).
P.S. Another thread claims that as of 2.2, the ANDROID_ID for the emulator is always "9774D56D682E549C". However, you are apparently getting some other hex string, so I don't think this is right, either.
P.P.S. Other suggestions I haven't tried are here. One that seems particularly nice (if it works) is:
if (android.os.Build.MODEL.equals(“google_sdk”)) {
// emulator
} else {
//not emulator
}
I think that the best answer is to decide why you actually care to know - and then check for whatever specific characteristic of the emulator you believe requires that your app behave differently than it would on a device.
How about this solution:
public static boolean isRunningOnEmulator()
{
boolean result=//
Build.FINGERPRINT.startsWith("generic")//
||Build.FINGERPRINT.startsWith("unknown")//
||Build.MODEL.contains("google_sdk")//
||Build.MODEL.contains("Emulator")//
||Build.MODEL.contains("Android SDK built for x86");
if(result)
return true;
result|=Build.BRAND.startsWith("generic")&&Build.DEVICE.startsWith("generic");
if(result)
return true;
result|="google_sdk".equals(Build.PRODUCT);
return result;
}
EDIT: seems I've answered this already, here:
https://stackoverflow.com/a/21505193/878126
As stated in this post, IMEI and IMSI are harcoded on the emulator:
2325 { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL }, /* request internation subscriber identification number */
2326 { "+CGSN", "000000000000000", NULL }, /* request model version */
You can easily get the value using
adb shell dumpsys iphonesubinfo
So checking the device's IMEI using TelephonyManager.getDeviceId() should be sufficient to find out, whether you're on an emulator or a real device.To be absolutely sure, you might combine it with checking the model name as stated by various other posts.
public static boolean isRunningOnEmulator(final Context inContext) {
final TelephonyManager theTelephonyManager = (TelephonyManager)inContext.getSystemService(Context.TELEPHONY_SERVICE);
final boolean hasEmulatorImei = theTelephonyManager.getDeviceId().equals("000000000000000");
final boolean hasEmulatorModelName = Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK");
return hasEmulatorImei || hasEmulatorModelName;
}
The downside to this approach is that you need a context to access this information and instantiating a TelephonyManager for every check.
This is the standard google flutter emulator check :
public boolean isEmulator() {
return (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|| Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.HARDWARE.contains("goldfish")
|| Build.HARDWARE.contains("ranchu")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| Build.PRODUCT.contains("sdk_google")
|| Build.PRODUCT.contains("google_sdk")
|| Build.PRODUCT.contains("sdk")
|| Build.PRODUCT.contains("sdk_x86")
|| Build.PRODUCT.contains("vbox86p")
|| Build.PRODUCT.contains("emulator")
|| Build.PRODUCT.contains("simulator");
}
Following one is correctly detect my emulator
if (Build.BRAND.equalsIgnoreCase("generic")) {
//"YES, I am an emulator"
} else {
//"NO, I am NOT an emulator"
}
As of writing this, nothing in this thread worked for the Bluestacks 4 emulator except trying to check for sensors. And so I checked the battery temperature using this gist. It should return 0.0 which means it does not have a battery temperature (and therefore it's an emulator).
public float getCpuTemp() {
Process process;
try {
process = Runtime.getRuntime().exec("cat sys/class/thermal/thermal_zone0/temp");
process.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = reader.readLine();
float temp = Float.parseFloat(line) / 1000.0f;
return temp;
} catch (Exception e) {
e.printStackTrace();
return 0.0f;
}
}
I'm doing bluetooth development for connecting with a PC. I've basicly used the BTChatExample and changed the UUID to the standard PC SPP-profile.
Trying to close a bluetooth application during a blocking read, by closing the BluetoothSocket will leave the Bluetooth stack in a unusable state. This can only be fixed by disabling and enabling bluetooth and restarting the application. Checking logcat, you can see that some of the internal methods are failing, leaving a open port. Any information on this?
First of all there seams to be differences on how bluetooth is implemented on N1 and HTC Legend/Desire both running 2.1, do you know anything about this?
Connecting isn't 100% reliable, sometimes I get a warning saying ~PortSystemContext init: FAILED. This leaves bluetooth unusable, and restarting is needed.
Am I right in assuming that SPP is the only profile supported for use with the APIs? That's what the docs on the BluetoothAdapter says.
I would love to discuss issues on bluetooth with a developer and iron out these bugs so that Android can have good proper Bluetooth support it deserves.
Closing a socket in one thread during a blocking read should definitely cause the read to return (by throwing IOException) and should not leave the stack in a 'bad state'. This is the behavior on Droid and Nexus.
I spoke directly to the original poster, and he had observed this problem on HTC Legend and HTC Desire. It appears like they are not implementing the API's correctly. I am raising the issue with them.
You are correct that SPP/RFCOMM is the only profile that is intended for use with the API's. SPP/RFCOMM gets you a streaming socket which is good enough for a lot of use cases.
For now I recommend BT development on Nexus One / Motorola Droid, which are considered 'reference' implementations of the Bluetooth API's.
May I suggest that you do not perform blocking read() calls unless you have first checked that there is data ready to be read by using inputstream.available() which returns an integer indicating how many bytes are waiting in the input buffer.
long timeouttime = gettimeinseconds() + 2;
String response = "";
while (gettimeinseconds() < timeouttime) {
if (inputstream.available() > 0)
response = response + inputstream.read();
} else {
Thread.sleep(100); // sleep to slow down the while() loop.
}
}
return response;
That's just pseudo code, and its oversimplified. The bottom line is that we're not performing any blocking calls (read()) unless we're sure they will return immediately without delay.
Also, I highly recommend using BufferedInputStream instead of the standard InputStream.
Anyone could solve this problem ?
I try the following code :
// Keep listening to the InputStream while connected
while (!isInterrupted)
{
try
{
//Clear buffer
buffer = new byte[1024];
// Read from the InputStream
if (mmInStream != null && mmInStream.available() > 0)
{
if (isInterrupted)
break;
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Act_Main.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
}
else
{
try
{
synchronized (this)
{
this.wait(100);
}
if (isInterrupted)
break;
}
catch(InterruptedException ex)
{
Log.e(TAG, "WAIT_EXCEPTION:"+ ex.getMessage());
}
}
}
catch(Exception ex)
{
Log.e(TAG, "disconnected", ex);
connectionLost();
break;
}
}
And I changed the isInterrupted boolean in the cancel() method. Here is my stop() method:
/**
* Stop all threads
*/
public synchronized void stop()
{
isStop = true ;
if (D)
Log.d(TAG, "stop");
if(mConnectThread != null)
{
mConnectThread.cancel();
mConnectThread = null;
}
if(mConnectedThread != null)
{
mConnectedThread.cancel();
mConnectedThread = null;
}
setState(STATE_NONE);
}