Roll NDK to pick std::deque patch.
[android_tools.git] / sdk / tools / apps / SdkController / src / com / android / tools / sdkcontroller / activities / MultiTouchActivity.java
blobfaba8828f1700d3a487be28678cfee444fc6a582
1 /*
2 * Copyright (C) 2012 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
17 package com.android.tools.sdkcontroller.activities;
19 import java.io.ByteArrayInputStream;
20 import java.nio.ByteBuffer;
21 import java.nio.ByteOrder;
23 import android.graphics.Color;
24 import android.os.Bundle;
25 import android.os.Message;
26 import android.util.Log;
27 import android.view.MotionEvent;
28 import android.view.View;
29 import android.view.View.OnTouchListener;
30 import android.widget.TextView;
32 import com.android.tools.sdkcontroller.R;
33 import com.android.tools.sdkcontroller.handlers.MultiTouchChannel;
34 import com.android.tools.sdkcontroller.lib.Channel;
35 import com.android.tools.sdkcontroller.lib.ProtocolConstants;
36 import com.android.tools.sdkcontroller.service.ControllerService.ControllerBinder;
37 import com.android.tools.sdkcontroller.service.ControllerService.ControllerListener;
38 import com.android.tools.sdkcontroller.utils.ApiHelper;
39 import com.android.tools.sdkcontroller.views.MultiTouchView;
41 /**
42 * Activity that controls and displays the {@link MultiTouchChannel}.
44 public class MultiTouchActivity extends BaseBindingActivity
45 implements android.os.Handler.Callback {
47 @SuppressWarnings("hiding")
48 private static String TAG = MultiTouchActivity.class.getSimpleName();
49 private static boolean DEBUG = true;
51 private volatile MultiTouchChannel mHandler;
53 private TextView mTextError;
54 private TextView mTextStatus;
55 private MultiTouchView mImageView;
56 /** Width of the emulator's display. */
57 private int mEmulatorWidth = 0;
58 /** Height of the emulator's display. */
59 private int mEmulatorHeight = 0;
60 /** Bitmap storage. */
61 private int[] mColors;
63 private final TouchListener mTouchListener = new TouchListener();
64 private final android.os.Handler mUiHandler = new android.os.Handler(this);
66 /** Called when the activity is first created. */
67 @Override
68 public void onCreate(Bundle savedInstanceState) {
69 super.onCreate(savedInstanceState);
70 setContentView(R.layout.multitouch);
71 mImageView = (MultiTouchView) findViewById(R.id.imageView);
72 mTextError = (TextView) findViewById(R.id.textError);
73 mTextStatus = (TextView) findViewById(R.id.textStatus);
74 updateStatus("Waiting for connection");
76 ApiHelper ah = ApiHelper.get();
77 ah.View_setSystemUiVisibility(mImageView, View.SYSTEM_UI_FLAG_LOW_PROFILE);
80 @Override
81 protected void onResume() {
82 if (DEBUG) Log.d(TAG, "onResume");
83 // BaseBindingActivity.onResume will bind to the service.
84 // Note: any initialization related to the service or the handler should
85 // go in onServiceConnected() since in this call the service may not be
86 // bound yet.
87 super.onResume();
88 updateError();
91 @Override
92 protected void onPause() {
93 if (DEBUG) Log.d(TAG, "onPause");
94 // BaseBindingActivity.onResume will unbind from (but not stop) the service.
95 super.onPause();
96 mImageView.setEnabled(false);
97 updateStatus("Paused");
100 // ----------
102 @Override
103 protected void onServiceConnected() {
104 if (DEBUG) Log.d(TAG, "onServiceConnected");
105 mHandler = (MultiTouchChannel) getServiceBinder().getChannel(Channel.MULTITOUCH_CHANNEL);
106 if (mHandler != null) {
107 mHandler.setViewSize(mImageView.getWidth(), mImageView.getHeight());
108 mHandler.addUiHandler(mUiHandler);
112 @Override
113 protected void onServiceDisconnected() {
114 if (DEBUG) Log.d(TAG, "onServiceDisconnected");
115 if (mHandler != null) {
116 mHandler.removeUiHandler(mUiHandler);
117 mHandler = null;
121 @Override
122 protected ControllerListener createControllerListener() {
123 return new MultiTouchControllerListener();
126 // ----------
128 private class MultiTouchControllerListener implements ControllerListener {
129 @Override
130 public void onErrorChanged() {
131 runOnUiThread(new Runnable() {
132 @Override
133 public void run() {
134 updateError();
139 @Override
140 public void onStatusChanged() {
141 runOnUiThread(new Runnable() {
142 @Override
143 public void run() {
144 ControllerBinder binder = getServiceBinder();
145 if (binder != null) {
146 boolean connected = binder.isEmuConnected();
147 mImageView.setEnabled(connected);
148 updateStatus(connected ? "Emulator connected" : "Emulator disconnected");
155 // ----------
158 * Implements OnTouchListener interface that receives touch screen events,
159 * and reports them to the emulator application.
161 class TouchListener implements OnTouchListener {
163 * Touch screen event handler.
165 @Override
166 public boolean onTouch(View v, MotionEvent event) {
167 ByteBuffer bb = null;
168 final int action = event.getAction();
169 final int action_code = action & MotionEvent.ACTION_MASK;
170 final int action_pid_index = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
171 int msg_type = 0;
172 MultiTouchChannel h = mHandler;
174 // Build message for the emulator.
175 switch (action_code) {
176 case MotionEvent.ACTION_MOVE:
177 if (h != null) {
178 bb = ByteBuffer.allocate(
179 event.getPointerCount() * ProtocolConstants.MT_EVENT_ENTRY_SIZE);
180 bb.order(h.getEndian());
181 for (int n = 0; n < event.getPointerCount(); n++) {
182 mImageView.constructEventMessage(bb, event, n);
184 msg_type = ProtocolConstants.MT_MOVE;
186 break;
187 case MotionEvent.ACTION_DOWN:
188 if (h != null) {
189 bb = ByteBuffer.allocate(ProtocolConstants.MT_EVENT_ENTRY_SIZE);
190 bb.order(h.getEndian());
191 mImageView.constructEventMessage(bb, event, action_pid_index);
192 msg_type = ProtocolConstants.MT_FISRT_DOWN;
194 break;
195 case MotionEvent.ACTION_UP:
196 if (h != null) {
197 bb = ByteBuffer.allocate(ProtocolConstants.MT_EVENT_ENTRY_SIZE);
198 bb.order(h.getEndian());
199 bb.putInt(event.getPointerId(action_pid_index));
200 msg_type = ProtocolConstants.MT_LAST_UP;
202 break;
203 case MotionEvent.ACTION_POINTER_DOWN:
204 if (h != null) {
205 bb = ByteBuffer.allocate(ProtocolConstants.MT_EVENT_ENTRY_SIZE);
206 bb.order(h.getEndian());
207 mImageView.constructEventMessage(bb, event, action_pid_index);
208 msg_type = ProtocolConstants.MT_POINTER_DOWN;
210 break;
211 case MotionEvent.ACTION_POINTER_UP:
212 if (h != null) {
213 bb = ByteBuffer.allocate(ProtocolConstants.MT_EVENT_ENTRY_SIZE);
214 bb.order(h.getEndian());
215 bb.putInt(event.getPointerId(action_pid_index));
216 msg_type = ProtocolConstants.MT_POINTER_UP;
218 break;
219 default:
220 Log.w(TAG, "Unknown action type: " + action_code);
221 return true;
224 if (DEBUG && bb != null) Log.d(TAG, bb.toString());
226 if (h != null && bb != null) {
227 h.postMessage(msg_type, bb);
229 return true;
231 } // TouchListener
233 /** Implementation of Handler.Callback */
234 @Override
235 public boolean handleMessage(Message msg) {
236 switch (msg.what) {
237 case MultiTouchChannel.EVENT_MT_START:
238 MultiTouchChannel h = mHandler;
239 if (h != null) {
240 mImageView.setEnabled(true);
241 mImageView.setOnTouchListener(mTouchListener);
243 break;
244 case MultiTouchChannel.EVENT_MT_STOP:
245 mImageView.setOnTouchListener(null);
246 break;
247 case MultiTouchChannel.EVENT_FRAME_BUFFER:
248 onFrameBuffer(((ByteBuffer) msg.obj).array());
249 mHandler.postMessage(ProtocolConstants.MT_FB_HANDLED, (byte[]) null);
250 break;
252 return true; // we consumed this message
256 * Called when a BLOB query is received from the emulator.
257 * <p/>
258 * This query is used to deliver framebuffer updates in the emulator. The
259 * blob contains an update header, followed by the bitmap containing updated
260 * rectangle. The header is defined as MTFrameHeader structure in
261 * external/qemu/android/multitouch-port.h
262 * <p/>
263 * NOTE: This method is called from the I/O loop, so all communication with
264 * the emulator will be "on hold" until this method returns.
266 * TODO ===> CHECK that we can consume that array from a different thread than the producer's.
267 * E.g. does the produce reuse the same array or does it generate a new one each time?
269 * @param array contains BLOB data for the query.
271 private void onFrameBuffer(byte[] array) {
272 final ByteBuffer bb = ByteBuffer.wrap(array);
273 bb.order(ByteOrder.LITTLE_ENDIAN);
275 // Read frame header.
276 final int header_size = bb.getInt();
277 final int disp_width = bb.getInt();
278 final int disp_height = bb.getInt();
279 final int x = bb.getInt();
280 final int y = bb.getInt();
281 final int w = bb.getInt();
282 final int h = bb.getInt();
283 final int bpl = bb.getInt();
284 final int bpp = bb.getInt();
285 final int format = bb.getInt();
287 // Update application display.
288 updateDisplay(disp_width, disp_height);
290 if (format == ProtocolConstants.MT_FRAME_JPEG) {
292 * Framebuffer is in JPEG format.
295 final ByteArrayInputStream jpg = new ByteArrayInputStream(bb.array());
296 // Advance input stream to JPEG image.
297 jpg.skip(header_size);
298 // Draw the image.
299 mImageView.drawJpeg(x, y, w, h, jpg);
300 } else {
302 * Framebuffer is in a raw RGB format.
305 final int pixel_num = h * w;
306 // Advance stream to the beginning of framebuffer data.
307 bb.position(header_size);
309 // Make sure that mColors is large enough to contain the
310 // update bitmap.
311 if (mColors == null || mColors.length < pixel_num) {
312 mColors = new int[pixel_num];
315 // Convert the blob bitmap into bitmap that we will display.
316 if (format == ProtocolConstants.MT_FRAME_RGB565) {
317 for (int n = 0; n < pixel_num; n++) {
318 // Blob bitmap is in RGB565 format.
319 final int color = bb.getShort();
320 final int r = ((color & 0xf800) >> 8) | ((color & 0xf800) >> 14);
321 final int g = ((color & 0x7e0) >> 3) | ((color & 0x7e0) >> 9);
322 final int b = ((color & 0x1f) << 3) | ((color & 0x1f) >> 2);
323 mColors[n] = Color.rgb(r, g, b);
325 } else if (format == ProtocolConstants.MT_FRAME_RGB888) {
326 for (int n = 0; n < pixel_num; n++) {
327 // Blob bitmap is in RGB565 format.
328 final int r = bb.getChar();
329 final int g = bb.getChar();
330 final int b = bb.getChar();
331 mColors[n] = Color.rgb(r, g, b);
333 } else {
334 Log.w(TAG, "Invalid framebuffer format: " + format);
335 return;
337 mImageView.drawBitmap(x, y, w, h, mColors);
342 * Updates application's screen accordingly to the emulator screen.
344 * @param e_width Width of the emulator screen.
345 * @param e_height Height of the emulator screen.
347 private void updateDisplay(int e_width, int e_height) {
348 if (e_width != mEmulatorWidth || e_height != mEmulatorHeight) {
349 mEmulatorWidth = e_width;
350 mEmulatorHeight = e_height;
352 boolean rotateDisplay = false;
353 int w = mImageView.getWidth();
354 int h = mImageView.getHeight();
355 if (w > h != e_width > e_height) {
356 rotateDisplay = true;
357 int tmp = w;
358 w = h;
359 h = tmp;
362 float dx = (float) w / (float) e_width;
363 float dy = (float) h / (float) e_height;
364 mImageView.setDxDy(dx, dy, rotateDisplay);
365 if (DEBUG) Log.d(TAG, "Dispay updated: " + e_width + " x " + e_height +
366 " -> " + w + " x " + h + " ratio: " +
367 dx + " x " + dy);
371 // ----------
373 private void updateStatus(String status) {
374 mTextStatus.setVisibility(status == null ? View.GONE : View.VISIBLE);
375 if (status != null) mTextStatus.setText(status);
378 private void updateError() {
379 ControllerBinder binder = getServiceBinder();
380 String error = binder == null ? "" : binder.getServiceError();
381 if (error == null) {
382 error = "";
385 mTextError.setVisibility(error.length() == 0 ? View.GONE : View.VISIBLE);
386 mTextError.setText(error);