/**
* MainUsbActivity is a GameActivity that monitors a USB Accessory
* device and uses the input from that device to control the game.
*/
package com.examples.accessory.controller;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainUsbActivity extends GameActivity implements Runnable {
private static final String ACTION_USB_PERMISSION = "com.examples.accessory.controller.action.USB_PERMISSION";
private UsbManager mUsbManager;
private PendingIntent mPermissionIntent;
private boolean mPermissionRequestPending;
UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;
private static final int MESSAGE_SWITCH = 1;
private static final int MESSAGE_JOY = 4;
private static final int MESSAGE_VIBE = 5;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(mUsbReceiver, filter);
setContentView(R.layout.main);
enableControls(false);
}
@Override
public void onResume() {
super.onResume();
Intent intent = getIntent();
if (mInputStream != null && mOutputStream != null) {
return;
}
UsbAccessory[] accessories = mUsbManager.getAccessoryList();
UsbAccessory accessory = (accessories == null ? null : accessories[0]);
if (accessory != null) {
if (mUsbManager.hasPermission(accessory)) {
openAccessory(accessory);
} else {
synchronized (mUsbReceiver) {
if (!mPermissionRequestPending) {
mUsbManager.requestPermission(accessory,
mPermissionIntent);
mPermissionRequestPending = true;
}
}
}
} else {
Log.d(TAG, "mAccessory is null");
}
}
@Override
public void onPause() {
super.onPause();
closeAccessory();
}
@Override
public void onDestroy() {
unregisterReceiver(mUsbReceiver);
super.onDestroy();
}
@Override
protected boolean isControllerConnected() {
return (mAccessory != null);
}
@Override
protected void hideControls() {
setContentView(R.layout.no_device);
super.hideControls();
}
@Override
protected void sendVibeControl(boolean longDuration) {
byte[] command = {0x02,
longDuration ? (byte)0x64 : (byte)0x32,
0x00};
Message msg = Message.obtain(null, MESSAGE_VIBE, command);
mHandler.sendMessage(msg);
}
private void openAccessory(UsbAccessory accessory) {
mFileDescriptor = mUsbManager.openAccessory(accessory);
if (mFileDescriptor != null) {
mAccessory = accessory;
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryController");
thread.start();
Log.d(TAG, "accessory opened");
enableControls(true);
} else {
Log.d(TAG, "accessory open fail");
}
}
private void closeAccessory() {
enableControls(false);
try {
if (mFileDescriptor != null) {
mFileDescriptor.close();
}
} catch (IOException e) {
} finally {
mFileDescriptor = null;
mAccessory = null;
}
}
/*
* This receiver monitors for the event of a user granting permission to use
* the attached accessory. If the user has checked to always allow, this will
* be generated following attachment without further user interaction.
*/
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
openAccessory(accessory);
} else {
Log.d(TAG, "permission denied for accessory "+ accessory);
}
mPermissionRequestPending = false;
}
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null && accessory.equals(mAccessory)) {
closeAccessory();
}
}
}
};
/*
* Runnable block that will poll the accessory data stream
* for regular updates, posting each message it finds to a
* Handler. This is run on a spawned background thread.
*/
public void run() {
int ret = 0;
byte[] buffer = new byte[16384];
int i;
while (ret >= 0) {
try {
ret = mInputStream.read(buffer);
} catch (IOException e) {
break;
}
i = 0;
while (i < ret) {
int len = ret - i;
switch (buffer[i]) {
case 0x1:
if (len >= 3) {
Message m = Message.obtain(mHandler, MESSAGE_SWITCH);
m.obj = new SwitchMsg(buffer[i + 1], buffer[i + 2]);
mHandler.sendMessage(m);
}
i += 3;
break;
case 0x6:
if (len >= 3) {
Message m = Message.obtain(mHandler, MESSAGE_JOY);
m.obj = new JoyMsg(buffer[i + 1], buffer[i + 2]);
mHandler.sendMessage(m);
}
i += 3;
break;
default:
Log.d(TAG, "unknown msg: " + buffer[i]);
i = len;
break;
}
}
}
}
/*
* This Handler receives messages from the polling thread and
* injects them into the GameActivity methods on the main thread.
*/
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_SWITCH:
SwitchMsg o = (SwitchMsg) msg.obj;
handleSwitchMessage(o);
break;
case MESSAGE_JOY:
JoyMsg j = (JoyMsg) msg.obj;
handleJoyMessage(j);
break;
case MESSAGE_VIBE:
try {
byte[] v = (byte[]) msg.obj;
mOutputStream.write(v);
mOutputStream.flush();
} catch (IOException e) {
Log.w("AccessoryController", "Error writing vibe output");
}
break;
}
}
};
}