aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2012-01-27 11:45:38 -0800
committerVladimir Chtchetkine <vchtchetkine@google.com>2012-03-01 13:11:49 -0800
commitb95755a3463e122869a8442613ca572d32e5f00a (patch)
tree677b53eddf18cd1749f95b107436e6931dc18ebe /apps
parent5879c13d7bfa88e181062178d492713e3ddbadfb (diff)
downloadsdk-b95755a3463e122869a8442613ca572d32e5f00a.zip
sdk-b95755a3463e122869a8442613ca572d32e5f00a.tar.gz
sdk-b95755a3463e122869a8442613ca572d32e5f00a.tar.bz2
Implements multitouch app supporting framebuffer transfer
Change-Id: I26061ec191e00bc9da4852080d9e2fd7c7a6fac0
Diffstat (limited to 'apps')
-rw-r--r--apps/SdkController/.gitignore2
-rwxr-xr-xapps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/Emulator.java102
-rw-r--r--apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/OnEmulatorListener.java66
-rw-r--r--apps/SdkController/SdkControllerMultitouch/AndroidManifest.xml8
-rw-r--r--apps/SdkController/SdkControllerMultitouch/res/layout/main.xml8
-rwxr-xr-xapps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/MultiTouchView.java228
-rw-r--r--apps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/SdkControllerMultitouchActivity.java210
-rwxr-xr-xapps/SdkController/SdkControllerSensor/src/com/android/tools/sdkcontroller/sdkcontrollersensor/SdkControllerSensorActivity.java4
8 files changed, 498 insertions, 130 deletions
diff --git a/apps/SdkController/.gitignore b/apps/SdkController/.gitignore
index 1ea39a4..d3a6cac 100644
--- a/apps/SdkController/.gitignore
+++ b/apps/SdkController/.gitignore
@@ -1,3 +1,5 @@
+bin
+gen
.classpath
.project
SdkControllerLib/bin/
diff --git a/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/Emulator.java b/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/Emulator.java
index 8b34642..bb5bec8 100755
--- a/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/Emulator.java
+++ b/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/Emulator.java
@@ -27,29 +27,29 @@ import android.util.Log;
/**
* Encapsulates a connection with the emulator. The connection is established
* over a TCP port forwarding enabled with 'adb forward' command.
- *
- * Communication with the emulator is performed via two socket channels connected
- * to the forwarded TCP port. One channel is a query channel that is intended
- * solely for receiving queries from the emulator. Another channel is an event
- * channel that is intended for sending notification messages (events) to the
- * emulator.
- *
+ * <p/>
+ * Communication with the emulator is performed via two socket channels
+ * connected to the forwarded TCP port. One channel is a query channel that is
+ * intended solely for receiving queries from the emulator. Another channel is
+ * an event channel that is intended for sending notification messages (events)
+ * to the emulator.
+ * <p/>
* Emulator is considered to be "connected" when both channels are connected.
* Emulator is considered to be "disconnected" when connection with any of the
* channels is lost.
- *
+ * <p/>
* Instance of this class is operational only for a single connection with the
- * emulator. Once connection is established and then lost, a new instance of this
- * class must be created to establish new connection.
- *
+ * emulator. Once connection is established and then lost, a new instance of
+ * this class must be created to establish new connection.
+ * <p/>
* Note that connection with the device over TCP port forwarding is extremely
- * fragile at the moment. For whatever reason the connection is even more fragile
- * if device uses asynchronous sockets (based on java.nio API). So, to address
- * this issue Emulator class implements two types of connections. One is using
- * synchronous sockets, and another is using asynchronous sockets. The type of
- * connection is selected when Emulator instance is created (see comments to
- * Emulator's constructor).
- *
+ * fragile at the moment. For whatever reason the connection is even more
+ * fragile if device uses asynchronous sockets (based on java.nio API). So, to
+ * address this issue Emulator class implements two types of connections. One is
+ * using synchronous sockets, and another is using asynchronous sockets. The
+ * type of connection is selected when Emulator instance is created (see
+ * comments to Emulator's constructor).
+ * <p/>
* According to the exchange protocol with the emulator, queries, responses to
* the queries, and notification messages are all zero-terminated strings.
*/
@@ -84,6 +84,8 @@ public class Emulator {
private boolean mIsConnected = false;
/** Disconnection status */
private boolean mIsDisconnected = false;
+ /** Exit I/O loop flag. */
+ private boolean mExitIoLoop = false;
/***************************************************************************
* EmulatorChannel - Base class for sync / async channels.
@@ -178,15 +180,18 @@ public class Emulator {
}
} else {
response = onQuery(query, query_param);
+ if (response.length() == 0 || response.charAt(0) == '\0') {
+ Logw("No response to the query " + query_str);
+ }
}
- if (response.length() == 0) {
- Logw("No response to query '" + query + "'. Replying with 'ko'");
- response = "ko:Protocol error.\0";
- } else if (response.charAt(response.length() - 1) != '\0') {
- Logw("Response '" + response + "' to query '" + query
- + "' does not contain zero-terminator.");
+
+ if (response.length() != 0) {
+ if (response.charAt(response.length() - 1) != '\0') {
+ Logw("Response '" + response + "' to query '" + query
+ + "' does not contain zero-terminator.");
+ }
+ sendMessage(response);
}
- sendMessage(response);
}
} // EmulatorChannel
@@ -555,6 +560,7 @@ public class Emulator {
// Register 'accept' I/O on the server socket.
mServerSocket.register(mSelector, SelectionKey.OP_ACCEPT);
+ Logv("Emulator listener is created for port " + port);
// Start I/O looper and dispatcher.
new Thread(new Runnable() {
@Override
@@ -689,7 +695,10 @@ public class Emulator {
private void runIOLooper() {
try {
Logv("Waiting on Emulator to connect...");
- while (mSelector.select() >= 0) {
+ // Check mExitIoLoop before calling 'select', and after in order to
+ // detect condition when mSelector has been waken up to exit the
+ // I/O loop.
+ while (!mExitIoLoop && mSelector.select() >= 0 && !mExitIoLoop) {
Set<SelectionKey> readyKeys = mSelector.selectedKeys();
Iterator<SelectionKey> i = readyKeys.iterator();
while (i.hasNext()) {
@@ -726,7 +735,9 @@ public class Emulator {
}
// Destroy connection on any I/O failure.
- onLostConnection();
+ if (!mExitIoLoop) {
+ onLostConnection();
+ }
}
/**
@@ -763,8 +774,8 @@ public class Emulator {
}
} else {
// TODO: Find better way to do that!
- socket.getOutputStream().write("ko:Duplicate,\0".getBytes());
Loge("Duplicate query channel.");
+ socket.getOutputStream().write("ko:Duplicate\0".getBytes());
channel.close();
return;
}
@@ -780,13 +791,14 @@ public class Emulator {
Logv("Synchronous event channel is registered.");
}
} else {
- socket.getOutputStream().write("ko:Duplicate,\0".getBytes());
Loge("Duplicate event channel.");
+ socket.getOutputStream().write("ko:Duplicate\0".getBytes());
channel.close();
return;
}
} else {
Loge("Unknown channel is connecting: " + socket_type);
+ socket.getOutputStream().write("ko:Unknown channel type\0".getBytes());
channel.close();
return;
}
@@ -814,23 +826,25 @@ public class Emulator {
mIsDisconnected = true;
}
if (first_time) {
- Logw("Connection with the emulator is lost.");
- synchronized (this) {
- // Close all channels.
+ Logw("Connection with the emulator is lost!");
+ // Close all channels, exit the I/O loop, and close the selector.
+ try {
+ if (mEventChannel != null) {
+ mEventChannel.closeChannel();
+ }
+ if (mQueryChannel != null) {
+ mQueryChannel.closeChannel();
+ }
+ if (mServerSocket != null) {
+ mServerSocket.close();
+ }
if (mSelector != null) {
- try {
- if (mEventChannel != null) {
- mEventChannel.closeChannel();
- }
- if (mQueryChannel != null) {
- mQueryChannel.closeChannel();
- }
- mServerSocket.close();
- mSelector.close();
- } catch (IOException e) {
- Loge("onLostConnection exception: " + e.getMessage());
- }
+ mExitIoLoop = true;
+ mSelector.wakeup();
+ mSelector.close();
}
+ } catch (IOException e) {
+ Loge("onLostConnection exception: " + e.getMessage());
}
// Notify the app about lost connection.
diff --git a/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/OnEmulatorListener.java b/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/OnEmulatorListener.java
index 9aa5671..8d4dffa 100644
--- a/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/OnEmulatorListener.java
+++ b/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/OnEmulatorListener.java
@@ -22,40 +22,40 @@ package com.android.tools.sdkcontroller.lib;
* setOnEmulatorListener method of the Emulator class.
*/
public interface OnEmulatorListener {
- /**
- * Called when emulator is connected. NOTE: This method is called from the I/O
- * loop, so all communication with the emulator will be "on hold" until this
- * method returns.
- */
- public void onEmulatorConnected();
+ /**
+ * Called when emulator is connected. NOTE: This method is called from the
+ * I/O loop, so all communication with the emulator will be "on hold" until
+ * this method returns.
+ */
+ public void onEmulatorConnected();
- /**
- * Called when emulator is disconnected. NOTE: This method could be called
- * from the I/O loop, in which case all communication with the emulator will
- * be "on hold" until this method returns.
- */
- public void onEmulatorDisconnected();
+ /**
+ * Called when emulator is disconnected. NOTE: This method could be called
+ * from the I/O loop, in which case all communication with the emulator will
+ * be "on hold" until this method returns.
+ */
+ public void onEmulatorDisconnected();
- /**
- * Called when a query is received from the emulator. NOTE: This method is
- * called from the I/O loop, so all communication with the emulator will be
- * "on hold" until this method returns.
- *
- * @param query Name of the query received from the emulator.
- * @param param Query parameters.
- * @return Zero-terminated reply string. String must be formatted as such:
- * "ok|ko[:reply data]"
- */
- public String onEmulatorQuery(String query, String param);
+ /**
+ * Called when a query is received from the emulator. NOTE: This method is
+ * called from the I/O loop, so all communication with the emulator will be
+ * "on hold" until this method returns.
+ *
+ * @param query Name of the query received from the emulator.
+ * @param param Query parameters.
+ * @return Zero-terminated reply string. If not an empty string is returned,
+ * it must be formatted as such: "ok|ko[:reply data]"
+ */
+ public String onEmulatorQuery(String query, String param);
- /**
- * Called when a BLOB query is received from the emulator. NOTE: This method
- * is called from the I/O loop, so all communication with the emulator will be
- * "on hold" until this method returns.
- *
- * @param array contains BLOB data for the query.
- * @return Zero-terminated reply string. String must be formatted as such:
- * "ok|ko[:reply data]"
- */
- public String onEmulatorBlobQuery(byte[] array);
+ /**
+ * Called when a BLOB query is received from the emulator. NOTE: This method
+ * is called from the I/O loop, so all communication with the emulator will
+ * be "on hold" until this method returns.
+ *
+ * @param array contains BLOB data for the query.
+ * @return Zero-terminated reply string. If not an empty string is returned,
+ * it must be formatted as such: "ok|ko[:reply data]"
+ */
+ public String onEmulatorBlobQuery(byte[] array);
}
diff --git a/apps/SdkController/SdkControllerMultitouch/AndroidManifest.xml b/apps/SdkController/SdkControllerMultitouch/AndroidManifest.xml
index 8159efc..45f5f87 100644
--- a/apps/SdkController/SdkControllerMultitouch/AndroidManifest.xml
+++ b/apps/SdkController/SdkControllerMultitouch/AndroidManifest.xml
@@ -22,6 +22,7 @@
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="7" />
+
<uses-permission android:name="android.permission.INTERNET" />
<application
@@ -29,10 +30,11 @@
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
<activity
- android:label="@string/app_name"
+ android:name=".SdkControllerMultitouchActivity"
android:configChanges="orientation|keyboardHidden|mcc|mnc|locale|touchscreen|keyboard|navigation|screenLayout|fontScale"
- android:name=".SdkControllerMultitouchActivity" >
- <intent-filter >
+ android:label="@string/app_name"
+ android:screenOrientation="portrait" >
+ <intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/apps/SdkController/SdkControllerMultitouch/res/layout/main.xml b/apps/SdkController/SdkControllerMultitouch/res/layout/main.xml
index 1371302..ed618cd 100644
--- a/apps/SdkController/SdkControllerMultitouch/res/layout/main.xml
+++ b/apps/SdkController/SdkControllerMultitouch/res/layout/main.xml
@@ -3,8 +3,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
- <ImageView android:id="@+id/imageView"
+ <com.android.tools.sdkcontroller.sdkcontrollermultitouch.MultiTouchView
+ class="com.android.tools.sdkcontroller.sdkcontrollermultitouch.MultiTouchView"
+ android:id="@+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
- </ImageView>
-</FrameLayout> \ No newline at end of file
+ </com.android.tools.sdkcontroller.sdkcontrollermultitouch.MultiTouchView>
+</FrameLayout>
diff --git a/apps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/MultiTouchView.java b/apps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/MultiTouchView.java
new file mode 100755
index 0000000..fc3f994
--- /dev/null
+++ b/apps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/MultiTouchView.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2012 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.tools.sdkcontroller.sdkcontrollermultitouch;
+
+import java.io.InputStream;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Implements a main view for the application providing multi-touch emulation.
+ */
+public class MultiTouchView extends View {
+ /** Tag for logging messages. */
+ private static final String TAG = "SdkControllerMultitouch";
+ /**
+ * Back-end bitmap. Initialized in onSizeChanged(), updated in
+ * onTouchEvent() and drawn in onDraw().
+ */
+ private Bitmap mBitmap;
+ /** Default Paint instance for drawing the bitmap. */
+ private final Paint mPaint = new Paint();
+ /** Canvas instance for this view. */
+ private Canvas mCanvas;
+ /** Emulator screen width to this view width ratio. */
+ private float mDx = 1;
+ /** Emulator screen height to this view height ratio. */
+ private float mDy = 1;
+ /**
+ * Flags whether or not image received from the emulator should be rotated.
+ * Rotation is required when display orientation state of the emulator and
+ * the device doesn't match.
+ */
+ private boolean mRotateDisplay;
+ /** Base matrix that keep emulator->device display scaling */
+ private Matrix mBaseMatrix = new Matrix();
+ /** Matrix that is used to draw emulator's screen on the device. */
+ private Matrix mDrawMatrix = new Matrix();
+
+ /**
+ * Simple constructor to use when creating a view from code.
+ *
+ * @see View#View(Context)
+ */
+ public MultiTouchView(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Constructor that is called when inflating a view from XML.
+ *
+ * @see View#View(Context, AttributeSet)
+ */
+ public MultiTouchView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /**
+ * Perform inflation from XML and apply a class-specific base style.
+ *
+ * @see View#View(Context, AttributeSet, int)
+ */
+ public MultiTouchView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // TODO Add constructor-time code here.
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ mCanvas = new Canvas(mBitmap);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ // Just draw the back-end bitmap without zooming or scaling.
+ if (mBitmap != null) {
+ canvas.drawBitmap(mBitmap, 0, 0, null);
+ }
+ }
+
+ /**
+ * Sets emulator screen width and height to this view width and height
+ * ratio.
+ *
+ * @param dx Emulator screen width to this view width ratio.
+ * @param dy Emulator screen height to this view height ratio.
+ * @param rotateDisplay Flags whether image received from the emulator
+ * should be rotated when drawn on the device.
+ */
+ public void setDxDy(float dx, float dy, boolean rotateDisplay) {
+ mDx = dx;
+ mDy = dy;
+ mRotateDisplay = rotateDisplay;
+
+ mBaseMatrix.setScale(dx, dy);
+ if (mRotateDisplay) {
+ mBaseMatrix.postRotate(90);
+ mBaseMatrix.postTranslate(getWidth(), 0);
+ }
+ }
+
+ /**
+ * Computes draw matrix for the emulator screen update.
+ *
+ * @param x Left screen coordinate of the bitmap on emulator screen.
+ * @param y Top screen coordinate of the bitmap on emulator screen.
+ */
+ private void computeDrawMatrix(int x, int y) {
+ mDrawMatrix.set(mBaseMatrix);
+ if (mRotateDisplay) {
+ mDrawMatrix.postTranslate(-y * mDy, x * mDx);
+ } else {
+ mDrawMatrix.postTranslate(x * mDx, y * mDy);
+ }
+ }
+
+ /**
+ * Draws a bitmap on the screen.
+ *
+ * @param x Left screen coordinate of the bitmap on emulator screen.
+ * @param y Top screen coordinate of the bitmap on emulator screen.
+ * @param w Width of the bitmap on the emulator screen.
+ * @param h Height of the bitmap on the emulator screen.
+ * @param colors Bitmap to draw.
+ */
+ public void drawBitmap(int x, int y, int w, int h, int[] colors) {
+ if (mCanvas != null) {
+ final Bitmap bmp = Bitmap.createBitmap(colors, 0, w, w, h, Bitmap.Config.ARGB_8888);
+
+ computeDrawMatrix(x, y);
+
+ /* Draw the bitmap and invalidate the updated region. */
+ mCanvas.drawBitmap(bmp, mDrawMatrix, mPaint);
+ invalidate();
+ }
+ }
+
+ /**
+ * Draws a JPEG bitmap on the screen.
+ *
+ * @param x Left screen coordinate of the bitmap on emulator screen.
+ * @param y Top screen coordinate of the bitmap on emulator screen.
+ * @param w Width of the bitmap on the emulator screen.
+ * @param h Height of the bitmap on the emulator screen.
+ * @param jpeg JPEG bitmap to draw.
+ */
+ public void drawJpeg(int x, int y, int w, int h, InputStream jpeg) {
+ if (mCanvas != null) {
+ final Bitmap bmp = BitmapFactory.decodeStream(jpeg);
+
+ computeDrawMatrix(x, y);
+
+ /* Draw the bitmap and invalidate the updated region. */
+ mCanvas.drawBitmap(bmp, mDrawMatrix, mPaint);
+ invalidate();
+ }
+ }
+
+ /**
+ * Constructs touch event message to be send to emulator.
+ *
+ * @param sb String builder where to construct the message.
+ * @param event Event for which to construct the message.
+ * @param ptr_index Index of the motion pointer for which to construct the
+ * message.
+ */
+ void constructEventMessage(StringBuilder sb, MotionEvent event, int ptr_index) {
+ sb.append(" pid=").append(event.getPointerId(ptr_index));
+ if (mRotateDisplay == false) {
+ sb.append(" x=").append((int) (event.getX(ptr_index) / mDx));
+ sb.append(" y=").append((int) (event.getY(ptr_index) / mDy));
+ } else {
+ sb.append(" x=").append((int) (event.getY(ptr_index) / mDy));
+ sb.append(" y=").append((int) (getWidth() - event.getX(ptr_index) / mDx));
+ }
+ // At the system level the input reader takes integers in the range
+ // 0 - 100 for the pressure.
+ int pressure = (int) (event.getPressure(ptr_index) * 100);
+ // Make sure it doesn't exceed 100...
+ if (pressure > 100) {
+ pressure = 100;
+ }
+ sb.append(" pressure=").append(pressure);
+ }
+
+ /***************************************************************************
+ * Logging wrappers
+ **************************************************************************/
+
+ private void Loge(String log) {
+ Log.e(TAG, log);
+ }
+
+ private void Logw(String log) {
+ Log.w(TAG, log);
+ }
+
+ private void Logv(String log) {
+ Log.v(TAG, log);
+ }
+}
diff --git a/apps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/SdkControllerMultitouchActivity.java b/apps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/SdkControllerMultitouchActivity.java
index 810eb80..ce4ac1d 100644
--- a/apps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/SdkControllerMultitouchActivity.java
+++ b/apps/SdkController/SdkControllerMultitouch/src/com/android/tools/sdkcontroller/sdkcontrollermultitouch/SdkControllerMultitouchActivity.java
@@ -16,39 +16,52 @@
package com.android.tools.sdkcontroller.sdkcontrollermultitouch;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
import android.app.Activity;
+import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
+import android.view.WindowManager;
import com.android.tools.sdkcontroller.lib.Emulator;
import com.android.tools.sdkcontroller.lib.Emulator.EmulatorConnectionType;
import com.android.tools.sdkcontroller.lib.OnEmulatorListener;
-import java.io.IOException;
-
/**
* Encapsulates an application that monitors multi-touch activities on a device,
* and reports them to an Android Emulator application running on the host
- * machine. This application is used to provide a realistic multi-touch emulation
- * in Android Emulator.
+ * machine. This application is used to provide a realistic multi-touch
+ * emulation in Android Emulator.
*/
public class SdkControllerMultitouchActivity extends Activity implements OnEmulatorListener {
/** Tag for logging messages. */
private static final String TAG = "SdkControllerMultitouch";
+ /** Received frame is JPEG image. */
+ private static final int FRAME_JPEG = 1;
+ /** Received frame is RGB565 bitmap. */
+ private static final int FRAME_RGB565 = 2;
+ /** Received frame is RGB888 bitmap. */
+ private static final int FRAME_RGB888 = 3;
/** TCP over USB connection to the emulator. */
private Emulator mEmulator;
/** View for this application. */
- private View mView;
+ private MultiTouchView mView;
/** Listener to touch events. */
private TouchListener mTouchListener;
- /** Multiplier for an X coordinate of a pointer. */
- private float mDx = 1;
- /** Multiplier for a Y coordinate of a pointer. */
- private float mDy = 1;
+ /** Width of the emulator's display. */
+ private int mEmulatorWidth = 0;
+ /** Height of the emulator's display. */
+ private int mEmulatorHeight = 0;
+ /** Bitmap storage. */
+ private int[] mColors;
/**
* Implements OnTouchListener interface that receives touch screen events,
@@ -56,7 +69,7 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
*/
class TouchListener implements OnTouchListener {
/**
- * Touchscreen event handler.
+ * Touch screen event handler.
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
@@ -73,19 +86,19 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
case MotionEvent.ACTION_MOVE:
sb.append("action=move");
for (int n = 0; n < event.getPointerCount(); n++) {
- constructEventMessage(sb, event, n);
+ mView.constructEventMessage(sb, event, n);
}
break;
case MotionEvent.ACTION_DOWN:
sb.append("action=down");
- constructEventMessage(sb, event, action_pid_index);
+ mView.constructEventMessage(sb, event, action_pid_index);
break;
case MotionEvent.ACTION_UP:
sb.append("action=up pid=").append(event.getPointerId(action_pid_index));
break;
case MotionEvent.ACTION_POINTER_DOWN:
sb.append("action=pdown");
- constructEventMessage(sb, event, action_pid_index);
+ mView.constructEventMessage(sb, event, action_pid_index);
break;
case MotionEvent.ACTION_POINTER_UP:
sb.append("action=pup pid=").append(event.getPointerId(action_pid_index));
@@ -99,21 +112,6 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
mEmulator.sendNotification(sb.toString() + '\0');
return true;
}
-
- /**
- * Constructs touch event message to be send to emulator.
- *<p/>
- * @param sb String builder where to construct the message.
- * @param event Event for which to construct the message.
- * @param ptr_index Index of the motion pointer for which to construct
- * the message.
- */
- private void constructEventMessage(StringBuilder sb, MotionEvent event, int ptr_index) {
- sb.append(" pid=").append(event.getPointerId(ptr_index));
- sb.append(" x=").append((int) (mDx * event.getX(ptr_index)));
- sb.append(" y=").append((int) (mDy * event.getY(ptr_index)));
- sb.append(" pressure=").append((int) event.getPressure(ptr_index));
- }
} // TouchListener
/** Called when the activity is first created. */
@@ -121,6 +119,8 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
+ mView = (MultiTouchView) findViewById(R.id.imageView);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Instantiate emulator connector.
try {
@@ -132,10 +132,39 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
}
// Create listener for touch events.
- mView = findViewById(R.id.imageView);
mTouchListener = new TouchListener();
}
+ /**
+ * Updates application's screen accordingly to the emulator screen.
+ *
+ * @param e_width Width of the emulator screen.
+ * @param e_height Height of the emulator screen.
+ */
+ private void updateDisplay(int e_width, int e_height) {
+ if (e_width != mEmulatorWidth || e_height != mEmulatorHeight) {
+ mEmulatorWidth = e_width;
+ mEmulatorHeight = e_height;
+
+ boolean rotateDisplay = false;
+ int w = mView.getWidth();
+ int h = mView.getHeight();
+ if (w > h != e_width > e_height) {
+ rotateDisplay = true;
+ int tmp = w;
+ w = h;
+ h = tmp;
+ }
+
+ float dx = (float) w / (float) e_width;
+ float dy = (float) h / (float) e_height;
+ mView.setDxDy(dx, dy, rotateDisplay);
+ Logv("Dispay updated: " + e_width + " x " + e_height +
+ " -> " + w + " x " + h + " ratio: " +
+ dx + " x " + dy);
+ }
+ }
+
/***************************************************************************
* OnEmulatorListener implementation
**************************************************************************/
@@ -147,6 +176,7 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
*/
@Override
public void onEmulatorConnected() {
+ Logv("Emulator is connected");
}
/**
@@ -154,6 +184,7 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
*/
@Override
public void onEmulatorDisconnected() {
+ Logv("Emulator is disconnected.");
// Stop listening on events, and let it cool for a sec...
onStopEvents();
try {
@@ -166,7 +197,7 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
mEmulator = new Emulator(Emulator.MULTITOUCH_PORT,
EmulatorConnectionType.SYNC_CONNECTION, this);
} catch (IOException e) {
- Loge("Exception while creating server socket: " + e.getMessage());
+ Loge("Exception while recreating server socket: " + e.getMessage());
finish();
}
}
@@ -174,11 +205,11 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
/**
* Called when a query is received from the emulator. NOTE: This method is
* called from the I/O loop.
- *<p/>
+ *
* @param query Name of the query received from the emulator. The allowed
- * queries are:
- * - 'start' - Starts delivering touch screen events to the emulator.
- * - 'stop' - Stops delivering touch screen events to the emulator.
+ * queries are: - 'start' - Starts delivering touch screen events
+ * to the emulator. - 'stop' - Stops delivering touch screen
+ * events to the emulator.
* @param param Query parameters.
* @return Zero-terminated reply string. String must be formatted as such:
* "ok|ko[:reply data]"
@@ -195,13 +226,105 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
}
}
+ /**
+ * Called when a BLOB query is received from the emulator.
+ * <p/>
+ * This query is used to deliver framebuffer updates in the emulator. The
+ * blob contains an update header, followed by the bitmap containing updated
+ * rectangle. The header is defined as MTFrameHeader structure in
+ * external/qemu/android/multitouch-port.h
+ * <p/>
+ * NOTE: This method is called from the I/O loop, so all communication with
+ * the emulator will be "on hold" until this method returns.
+ *
+ * @param array contains BLOB data for the query.
+ * @return Empty string: this query doesn't require any response.
+ */
+ @Override
+ public String onEmulatorBlobQuery(byte[] array) {
+ final ByteBuffer bb = ByteBuffer.wrap(array);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ // Read frame header.
+ final int header_size = bb.getInt();
+ final int disp_width = bb.getInt();
+ final int disp_height = bb.getInt();
+ final int x = bb.getInt();
+ final int y = bb.getInt();
+ final int w = bb.getInt();
+ final int h = bb.getInt();
+ final int bpl = bb.getInt();
+ final int bpp = bb.getInt();
+ final int format = bb.getInt();
+
+ // Update application display.
+ updateDisplay(disp_width, disp_height);
+
+ mView.post(new Runnable() {
+ @Override
+ public void run() {
+ if (format == FRAME_JPEG) {
+ /*
+ * Framebuffer is in JPEG format.
+ */
+
+ final ByteArrayInputStream jpg = new ByteArrayInputStream(bb.array());
+ // Advance input stream to JPEG image.
+ jpg.skip(header_size);
+ // Draw the image.
+ mView.drawJpeg(x, y, w, h, jpg);
+ } else {
+ /*
+ * Framebuffer is in a raw RGB format.
+ */
+
+ final int pixel_num = h * w;
+ // Advance stream to the beginning of framebuffer data.
+ bb.position(header_size);
+
+ // Make sure that mColors is large enough to contain the
+ // update bitmap.
+ if (mColors == null || mColors.length < pixel_num) {
+ mColors = new int[pixel_num];
+ }
+
+ // Convert the blob bitmap into bitmap that we will display.
+ if (format == FRAME_RGB565) {
+ for (int n = 0; n < pixel_num; n++) {
+ // Blob bitmap is in RGB565 format.
+ final int color = bb.getShort();
+ final int r = ((color & 0xf800) >> 8) | ((color & 0xf800) >> 14);
+ final int g = ((color & 0x7e0) >> 3) | ((color & 0x7e0) >> 9);
+ final int b = ((color & 0x1f) << 3) | ((color & 0x1f) >> 2);
+ mColors[n] = Color.rgb(r, g, b);
+ }
+ } else if (format == FRAME_RGB888) {
+ for (int n = 0; n < pixel_num; n++) {
+ // Blob bitmap is in RGB565 format.
+ final int r = bb.getChar();
+ final int g = bb.getChar();
+ final int b = bb.getChar();
+ mColors[n] = Color.rgb(r, g, b);
+ }
+ } else {
+ Logw("Invalid framebuffer format: " + format);
+ return;
+ }
+ mView.drawBitmap(x, y, w, h, mColors);
+ }
+ }
+ });
+
+ return "";
+ }
+
/***************************************************************************
* Emulator query handlers
**************************************************************************/
/**
* Handles 'start' query.
- *<p/>
+ *
* @return 'ok:<WidthxHeight> on success, or 'ko:<reason>' on failure. Width
* and height returned on success represent width and height of the
* application view.
@@ -210,15 +333,12 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
// Lets see if query has parameters.
int sep = param.indexOf('x');
if (sep != -1) {
- String dx = param.substring(0, sep);
- String dy = param.substring(sep + 1);
- int x = Integer.parseInt(dx);
- int y = Integer.parseInt(dy);
- mDy = (float) y / (float) mView.getHeight();
- mDx = (float) x / (float) mView.getWidth();
- Logv("Emulator: " + x + "x" + y +
- " to screen: " + mView.getWidth() + "x" + mView.getHeight() + " ratio: " +
- mDx + "x" + mDy);
+ final String dx = param.substring(0, sep);
+ final String dy = param.substring(sep + 1);
+ final int x = Integer.parseInt(dx);
+ final int y = Integer.parseInt(dy);
+
+ updateDisplay(x, y);
}
onStartEvents();
return "ok:" + mView.getWidth() + "x" + mView.getHeight() + "\0";
@@ -226,7 +346,7 @@ public class SdkControllerMultitouchActivity extends Activity implements OnEmula
/**
* Handles 'stop' query.
- *<p/>
+ *
* @return 'ok'.
*/
private String onQueryStop() {
diff --git a/apps/SdkController/SdkControllerSensor/src/com/android/tools/sdkcontroller/sdkcontrollersensor/SdkControllerSensorActivity.java b/apps/SdkController/SdkControllerSensor/src/com/android/tools/sdkcontroller/sdkcontrollersensor/SdkControllerSensorActivity.java
index 36ee4fe..1e7d970 100755
--- a/apps/SdkController/SdkControllerSensor/src/com/android/tools/sdkcontroller/sdkcontrollersensor/SdkControllerSensorActivity.java
+++ b/apps/SdkController/SdkControllerSensor/src/com/android/tools/sdkcontroller/sdkcontrollersensor/SdkControllerSensorActivity.java
@@ -460,8 +460,8 @@ public class SdkControllerSensorActivity extends Activity implements OnEmulatorL
/**
* Called when a BLOB query is received from the emulator. NOTE: This method
- * is called from the I/O loop, so all communication with the emulator will be
- * "on hold" until this method returns.
+ * is called from the I/O loop, so all communication with the emulator will
+ * be "on hold" until this method returns.
*
* @param array contains BLOB data for the query.
* @return Zero-terminated reply string. String must be formatted as such: