From cd39282253ec8a27eae1d580fbaa91a8f3c7c5ff Mon Sep 17 00:00:00 2001
From: Mike Lockwood
Date: Mon, 2 May 2011 14:31:47 -0400
Subject: [PATCH] Add USB MissleLauncher sample program
This is sample code that controls a USB missle launcher using USB host APIs
Change-Id: Ic4201faccc4562bf114c70d50b0cab6a00d28a98
Signed-off-by: Mike Lockwood
---
build/sdk.atree | 2 +
samples/USB/Android.mk | 1 +
samples/USB/MissileLauncher/Android.mk | 12 +
.../USB/MissileLauncher/AndroidManifest.xml | 25 ++
samples/USB/MissileLauncher/README.txt | 13 +
samples/USB/MissileLauncher/_index.html | 11 +
.../USB/MissileLauncher/default.properties | 11 +
.../MissileLauncher/res/layout/launcher.xml | 37 +++
.../MissileLauncher/res/values/strings.xml | 22 ++
.../MissileLauncher/res/xml/device_filter.xml | 21 ++
.../MissileLauncherActivity.java | 249 ++++++++++++++++++
samples/USB/_index.html | 1 +
12 files changed, 405 insertions(+)
create mode 100644 samples/USB/Android.mk
create mode 100644 samples/USB/MissileLauncher/Android.mk
create mode 100644 samples/USB/MissileLauncher/AndroidManifest.xml
create mode 100644 samples/USB/MissileLauncher/README.txt
create mode 100644 samples/USB/MissileLauncher/_index.html
create mode 100644 samples/USB/MissileLauncher/default.properties
create mode 100644 samples/USB/MissileLauncher/res/layout/launcher.xml
create mode 100644 samples/USB/MissileLauncher/res/values/strings.xml
create mode 100644 samples/USB/MissileLauncher/res/xml/device_filter.xml
create mode 100644 samples/USB/MissileLauncher/src/com/android/missilelauncher/MissileLauncherActivity.java
create mode 100644 samples/USB/_index.html
diff --git a/build/sdk.atree b/build/sdk.atree
index d098400dd..e5da0a208 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -177,6 +177,8 @@ development/samples/Spinner samples/${PLATFORM_NAME}/Spinner
development/samples/SpinnerTest samples/${PLATFORM_NAME}/SpinnerTest
development/samples/TicTacToeLib samples/${PLATFORM_NAME}/TicTacToeLib
development/samples/TicTacToeMain samples/${PLATFORM_NAME}/TicTacToeMain
+development/samples/USB/MissileLauncher samples/${PLATFORM_NAME}/USB/MissileLauncher
+development/samples/USB/AdbTest samples/${PLATFORM_NAME}/USB/AdbTest
development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService
development/samples/WeatherListWidget samples/${PLATFORM_NAME}/WeatherListWidget
development/apps/WidgetPreview samples/${PLATFORM_NAME}/WidgetPreview
diff --git a/samples/USB/Android.mk b/samples/USB/Android.mk
new file mode 100644
index 000000000..5053e7d64
--- /dev/null
+++ b/samples/USB/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/samples/USB/MissileLauncher/Android.mk b/samples/USB/MissileLauncher/Android.mk
new file mode 100644
index 000000000..daabb0c3a
--- /dev/null
+++ b/samples/USB/MissileLauncher/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := MissileLauncher
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/samples/USB/MissileLauncher/AndroidManifest.xml b/samples/USB/MissileLauncher/AndroidManifest.xml
new file mode 100644
index 000000000..b1c2c2b8b
--- /dev/null
+++ b/samples/USB/MissileLauncher/AndroidManifest.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/USB/MissileLauncher/README.txt b/samples/USB/MissileLauncher/README.txt
new file mode 100644
index 000000000..863baad7f
--- /dev/null
+++ b/samples/USB/MissileLauncher/README.txt
@@ -0,0 +1,13 @@
+MissileLauncher is a simple program that controls Dream Cheeky USB missile launchers.
+You control the left/right/up/down orientation of the launcher using the accelerometer.
+Tilt the tablet to change the direction of the launcher.
+Pressing the "Fire" button will fire one missile.
+
+This program serves as an example of the following USB host features:
+
+- filtering for multiple devices based on vendor and product IDs (see device_filter.xml)
+
+- Sending control requests on endpoint zero that contain data
+
+- Receiving packets on an interrupt endpoint using a thread that calls
+ UsbRequest.queue and UsbDeviceConnection.requestWait()
diff --git a/samples/USB/MissileLauncher/_index.html b/samples/USB/MissileLauncher/_index.html
new file mode 100644
index 000000000..99a5cf454
--- /dev/null
+++ b/samples/USB/MissileLauncher/_index.html
@@ -0,0 +1,11 @@
+
MissileLauncher is a simple program that controls Dream Cheeky USB missile launchers.
+You control the left/right/up/down orientation of the launcher using the accelerometer.
+Tilt the tablet to change the direction of the launcher. Pressing the Fire button will fire one missile.
+
+
This program serves as an example of the following USB host features:
+
+
filtering for multiple devices based on vendor and product IDs (see device_filter.xml)
+
Sending control requests on endpoint zero that contain data
+
Receiving packets on an interrupt endpoint using a thread that calls
+ {@link android.hardware.usb.UsbRequest#queue queue()} and {@link android.hardware.usb.UsbDeviceConnection#requestWait requestWait()}.
+
\ No newline at end of file
diff --git a/samples/USB/MissileLauncher/default.properties b/samples/USB/MissileLauncher/default.properties
new file mode 100644
index 000000000..3ac252342
--- /dev/null
+++ b/samples/USB/MissileLauncher/default.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-12
diff --git a/samples/USB/MissileLauncher/res/layout/launcher.xml b/samples/USB/MissileLauncher/res/layout/launcher.xml
new file mode 100644
index 000000000..1e488f72f
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/layout/launcher.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/USB/MissileLauncher/res/values/strings.xml b/samples/USB/MissileLauncher/res/values/strings.xml
new file mode 100644
index 000000000..3f7f85df3
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ Fire!
+
+
+
diff --git a/samples/USB/MissileLauncher/res/xml/device_filter.xml b/samples/USB/MissileLauncher/res/xml/device_filter.xml
new file mode 100644
index 000000000..391a7f19b
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/xml/device_filter.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/samples/USB/MissileLauncher/src/com/android/missilelauncher/MissileLauncherActivity.java b/samples/USB/MissileLauncher/src/com/android/missilelauncher/MissileLauncherActivity.java
new file mode 100644
index 000000000..75e191c84
--- /dev/null
+++ b/samples/USB/MissileLauncher/src/com/android/missilelauncher/MissileLauncherActivity.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.missilelauncher;
+
+import java.nio.ByteBuffer;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbRequest;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+public class MissileLauncherActivity extends Activity
+ implements View.OnClickListener, Runnable {
+
+ private static final String TAG = "MissileLauncherActivity";
+
+ private Button mFire;
+ private UsbManager mUsbManager;
+ private UsbDevice mDevice;
+ private UsbDeviceConnection mConnection;
+ private UsbEndpoint mEndpointIntr;
+ private SensorManager mSensorManager;
+ private Sensor mGravitySensor;
+
+ // USB control commands
+ private static final int COMMAND_UP = 1;
+ private static final int COMMAND_DOWN = 2;
+ private static final int COMMAND_RIGHT = 4;
+ private static final int COMMAND_LEFT = 8;
+ private static final int COMMAND_FIRE = 16;
+ private static final int COMMAND_STOP = 32;
+ private static final int COMMAND_STATUS = 64;
+
+ // constants for accelerometer orientation
+ private static final int TILT_LEFT = 1;
+ private static final int TILT_RIGHT = 2;
+ private static final int TILT_UP = 4;
+ private static final int TILT_DOWN = 8;
+ private static final double THRESHOLD = 5.0;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.launcher);
+ mFire = (Button)findViewById(R.id.fire);
+ mFire.setOnClickListener(this);
+
+ mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+
+ mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
+ mGravitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mSensorManager.unregisterListener(mGravityListener);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mSensorManager.registerListener(mGravityListener, mGravitySensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+
+ Intent intent = getIntent();
+ Log.d(TAG, "intent: " + intent);
+ String action = intent.getAction();
+
+ UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
+ setDevice(device);
+ } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+ if (mDevice != null && mDevice.equals(device)) {
+ setDevice(null);
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ private void setDevice(UsbDevice device) {
+ Log.d(TAG, "setDevice " + device);
+ if (device.getInterfaceCount() != 1) {
+ Log.e(TAG, "could not find interface");
+ return;
+ }
+ UsbInterface intf = device.getInterface(0);
+ // device should have one endpoint
+ if (intf.getEndpointCount() != 1) {
+ Log.e(TAG, "could not find endpoint");
+ return;
+ }
+ // endpoint should be of type interrupt
+ UsbEndpoint ep = intf.getEndpoint(0);
+ if (ep.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) {
+ Log.e(TAG, "endpoint is not interrupt type");
+ return;
+ }
+ mDevice = device;
+ mEndpointIntr = ep;
+ if (device != null) {
+ UsbDeviceConnection connection = mUsbManager.openDevice(device);
+ if (connection != null && connection.claimInterface(intf, true)) {
+ Log.d(TAG, "open SUCCESS");
+ mConnection = connection;
+ Thread thread = new Thread(this);
+ thread.start();
+
+ } else {
+ Log.d(TAG, "open FAIL");
+ mConnection = null;
+ }
+ }
+ }
+
+ private void sendCommand(int control) {
+ synchronized (this) {
+ if (control != COMMAND_STATUS) {
+ Log.d(TAG, "sendMove " + control);
+ }
+ if (mConnection != null) {
+ byte[] message = new byte[1];
+ message[0] = (byte)control;
+ // Send command via a control request on endpoint zero
+ mConnection.controlTransfer(0x21, 0x9, 0x200, 0, message, message.length, 0);
+ }
+ }
+ }
+
+ public void onClick(View v) {
+ if (v == mFire) {
+ sendCommand(COMMAND_FIRE);
+ }
+ }
+
+ private int mLastValue = 0;
+
+ SensorEventListener mGravityListener = new SensorEventListener() {
+ public void onSensorChanged(SensorEvent event) {
+
+ // compute current tilt
+ int value = 0;
+ if (event.values[0] < -THRESHOLD) {
+ value += TILT_LEFT;
+ } else if (event.values[0] > THRESHOLD) {
+ value += TILT_RIGHT;
+ }
+ if (event.values[1] < -THRESHOLD) {
+ value += TILT_UP;
+ } else if (event.values[1] > THRESHOLD) {
+ value += TILT_DOWN;
+ }
+
+ if (value != mLastValue) {
+ mLastValue = value;
+ // send motion command if the tilt changed
+ switch (value) {
+ case TILT_LEFT:
+ sendCommand(COMMAND_LEFT);
+ break;
+ case TILT_RIGHT:
+ sendCommand(COMMAND_RIGHT);
+ break;
+ case TILT_UP:
+ sendCommand(COMMAND_UP);
+ break;
+ case TILT_DOWN:
+ sendCommand(COMMAND_DOWN);
+ break;
+ default:
+ sendCommand(COMMAND_STOP);
+ break;
+ }
+ }
+ }
+
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // ignore
+ }
+ };
+
+ @Override
+ public void run() {
+ ByteBuffer buffer = ByteBuffer.allocate(1);
+ UsbRequest request = new UsbRequest();
+ request.initialize(mConnection, mEndpointIntr);
+ byte status = -1;
+ while (true) {
+ // queue a request on the interrupt endpoint
+ request.queue(buffer, 1);
+ // send poll status command
+ sendCommand(COMMAND_STATUS);
+ // wait for status event
+ if (mConnection.requestWait() == request) {
+ byte newStatus = buffer.get(0);
+ if (newStatus != status) {
+ Log.d(TAG, "got status " + newStatus);
+ status = newStatus;
+ if ((status & COMMAND_FIRE) != 0) {
+ // stop firing
+ sendCommand(COMMAND_STOP);
+ }
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ } else {
+ Log.e(TAG, "requestWait failed, exiting");
+ break;
+ }
+ }
+ }
+}
+
+
diff --git a/samples/USB/_index.html b/samples/USB/_index.html
new file mode 100644
index 000000000..f82011de2
--- /dev/null
+++ b/samples/USB/_index.html
@@ -0,0 +1 @@
+
A set of samples that demonstrate how to use various features of the USB APIs.