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 <lockwood@android.com>
diff --git a/build/sdk.atree b/build/sdk.atree
index d098400..e5da0a2 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -177,6 +177,8 @@
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 0000000..5053e7d
--- /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 0000000..daabb0c
--- /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 0000000..b1c2c2b
--- /dev/null
+++ b/samples/USB/MissileLauncher/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.missilelauncher">
+
+ <uses-feature android:name="android.hardware.usb.host" />
+ <uses-sdk android:minSdkVersion="12" />
+
+ <application>
+ <activity android:name="MissileLauncherActivity"
+ android:label="Missile Launcher"
+ android:screenOrientation="nosensor">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ <intent-filter>
+ <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+ </intent-filter>
+
+ <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+ android:resource="@xml/device_filter" />
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/USB/MissileLauncher/README.txt b/samples/USB/MissileLauncher/README.txt
new file mode 100644
index 0000000..863baad
--- /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 0000000..99a5cf4
--- /dev/null
+++ b/samples/USB/MissileLauncher/_index.html
@@ -0,0 +1,11 @@
+<p>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 <strong>Fire</strong> button will fire one missile.</p>
+
+<p>This program serves as an example of the following USB host features:</p>
+<ul>
+<li>filtering for multiple devices based on vendor and product IDs (see <code>device_filter.xml</code>)</li>
+<li>Sending control requests on endpoint zero that contain data</li>
+<li>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()}.</li>
+</p>
\ 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 0000000..3ac2523
--- /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 0000000..1e488f7
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/layout/launcher.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ >
+
+ <Button android:id="@+id/fire"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25dp"
+ android:layout_marginBottom="25dp"
+ android:layout_marginLeft="25dp"
+ android:layout_marginRight="25dp"
+ android:textSize="36sp"
+ android:text="@string/fire">
+ </Button>
+
+
+</LinearLayout>
+
+
diff --git a/samples/USB/MissileLauncher/res/values/strings.xml b/samples/USB/MissileLauncher/res/values/strings.xml
new file mode 100644
index 0000000..3f7f85d
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+
+ <string name="fire">Fire!</string>
+
+</resources>
+
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 0000000..391a7f1
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/xml/device_filter.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <!-- vendor and product ID for Dream Cheeky USB Missle Launcher -->
+ <usb-device vendor-id="2689" product-id="1793" />
+ <!-- vendor and product ID for Dream Cheeky Wireless USB Missle Launcher -->
+ <usb-device vendor-id="2689" product-id="65281" />
+</resources>
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 0000000..75e191c
--- /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 0000000..f82011d
--- /dev/null
+++ b/samples/USB/_index.html
@@ -0,0 +1 @@
+<p>A set of samples that demonstrate how to use various features of the USB APIs.</p>
\ No newline at end of file