diff options
author | Vladimir Chtchetkine <vchtchetkine@google.com> | 2012-01-27 11:45:38 -0800 |
---|---|---|
committer | Vladimir Chtchetkine <vchtchetkine@google.com> | 2012-03-01 13:11:49 -0800 |
commit | b95755a3463e122869a8442613ca572d32e5f00a (patch) | |
tree | 677b53eddf18cd1749f95b107436e6931dc18ebe /apps/SdkController | |
parent | 5879c13d7bfa88e181062178d492713e3ddbadfb (diff) | |
download | sdk-b95755a3463e122869a8442613ca572d32e5f00a.zip sdk-b95755a3463e122869a8442613ca572d32e5f00a.tar.gz sdk-b95755a3463e122869a8442613ca572d32e5f00a.tar.bz2 |
Implements multitouch app supporting framebuffer transfer
Change-Id: I26061ec191e00bc9da4852080d9e2fd7c7a6fac0
Diffstat (limited to 'apps/SdkController')
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: |