/* * 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.motorola.studio.android.emulator.core.utils; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import com.motorola.studio.android.adt.SdkUtils; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.emulator.core.model.AbstractInputLogic; import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance; import com.motorola.studio.android.utilities.TelnetFrameworkAndroid; /** * This class is responsible for sending input events to the Android Emulator */ @SuppressWarnings("serial") public class TelnetAndroidInput extends AbstractInputLogic { /* * Event types */ public static final String EV_TYPE_SYN = "EV_SYN"; public static final String EV_TYPE_KEY = "EV_KEY"; public static final String EV_TYPE_ABS = "EV_ABS"; public static final String EV_TYPE_SW = "EV_SW"; public static final int EV_SW_LID = 0x00; public static final String EV_SYN_REPORT = "0"; public static final String EV_ABS_X = "ABS_X"; public static final String EV_ABS_Y = "ABS_Y"; private static final String EV_TOUCH = "BTN_TOUCH"; private final Map<Integer, String> SPECIAL_KEY_MAPPING = new HashMap<Integer, String>() { { put(16777219, "KEY_LEFT"); // left put(16777217, "KEY_UP"); // top put(16777220, "KEY_RIGHT"); // right put(16777218, "KEY_DOWN"); // bottom } }; /* * Event types */ public static final int OPHONE_EV_TYPE_SYN = 0x00; public static final int OPHONE_EV_TYPE_KEY = 0x01; public static final int OPHONE_EV_TYPE_ABS = 0x03; public static final int OPHONE_EV_TYPE_SW = 0x05; public static final int OPHONE_EV_SW_LID = 0x00; public static final int OPHONE_EV_SYN_REPORT = 0x00; public static final int OPHONE_EV_ABS_X = 0x00; public static final int OPHONE_EV_ABS_Y = 0x01; private final Map<Integer, Integer> OPHONE_SPECIAL_KEY_MAPPING = new HashMap<Integer, Integer>() { { put(16777219, 0x069); // left put(16777217, 0x067); // top put(16777220, 0x06a); // right put(16777218, 0x06c); // bottom } }; private static final int OPHONE_KEY_BACKSPACE = 0x00e; private static final int OPHONE_KEY_SPACE = 0x039; private static final int OPHONE_KEY_ENTER = 0x01c; private static final int OPHONE_EV_TOUCH = 0x14A; // mouse position x private int oldX; // mouse position y private int oldY; // telnet connection private final TelnetFrameworkAndroid telnet = new TelnetFrameworkAndroid(); /** * Open the Telnet connection and initialize the communication * * @see com.motorola.studio.android.emulator.core.model.AbstractInputLogic#init(com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance) */ @Override public void init(IAndroidEmulatorInstance instance) { super.init(instance); String deviceSerial = instance.getInstanceIdentifier(); String serial = deviceSerial.substring(deviceSerial.length() - 4, deviceSerial.length()); try { telnet.connect("localhost", Integer.parseInt(serial)); } catch (IOException e) { //Do nothing } } /** * Close Telnet connection * * @see com.motorola.studio.android.emulator.core.model.AbstractInputLogic#dispose() */ @Override public void dispose() { try { telnet.disconnect(); } catch (IOException e) { // Do nothing } } /** * Send mouse down event * * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseDown(int, int) */ public void sendMouseDown(int x, int y) { sendAndroidEvent(EV_TYPE_ABS, EV_ABS_X, x); sendAndroidEvent(EV_TYPE_ABS, EV_ABS_Y, y); if (SdkUtils.isOphoneSDK()) { sendAndroidEvent(EV_TYPE_KEY, OPHONE_EV_TOUCH, true); sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0); } else { sendAndroidEvent(EV_TYPE_KEY, EV_TOUCH, true); sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0); } oldX = x; oldY = y; } /** * Send mouse up event * * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseUp(int, int) */ public void sendMouseUp(int x, int y) { if (SdkUtils.isOphoneSDK()) { sendAndroidEvent(EV_TYPE_KEY, OPHONE_EV_TOUCH, false); sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0); } else { sendAndroidEvent(EV_TYPE_KEY, EV_TOUCH, false); sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0); } } /** * Send mouse move event * * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseMove(int, int) */ public void sendMouseMove(int x, int y) { if (oldX != x) { sendAndroidEvent(EV_TYPE_ABS, EV_ABS_X, x); oldX = x; } if (oldY != y) { sendAndroidEvent(EV_TYPE_ABS, EV_ABS_Y, y); oldY = y; } if (SdkUtils.isOphoneSDK()) { sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0); } else { sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0); } } /** * Send a generic event to the emulator * * @param type event type * @param keysym event definition * @param pressed key pressed - yes or no */ public void sendAndroidEvent(String type, String keysym, boolean pressed) { sendAndroidEvent(type, keysym, pressed ? 1 : 0); } public void sendAndroidEvent(String type, int keysym, boolean pressed) { sendAndroidEvent(type, keysym, pressed ? 1 : 0); } private void sendAndroidEvent(String type, int keysym, int i) { try { telnet.write("event send " + type + ":" + keysym + ":" + i, null); } catch (IOException e) { StudioLogger.error("Failed to send generic event to Emulator"); } } /** * Send a complete event to the Emulator * Some events, like a key press, need two events (key pressed/released) * to be executed. This method send both in order to execute the event. * * @param type event type * @param keysym event definition */ public void sendAndroidEvent(String type, String keysym) { sendAndroidEvent(type, keysym, true); sendAndroidEvent(type, keysym, false); } /** * Send a complete event to the Emulator * Some events, like a key press, need two events (key pressed/released) * to be executed. This method send both in order to execute the event. * * @param type event type * @param keysym event definition */ public void sendAndroidEvent(String type, int keysym) { sendAndroidEvent(type, keysym, true); sendAndroidEvent(type, keysym, false); } /** * Send a generic event to the emulator * * @param type event type * @param keysym event definition * @param value parameter */ private void sendAndroidEvent(String type, String keysym, int value) { try { telnet.write("event send " + type + ":" + keysym + ":" + value, null); } catch (IOException e) { StudioLogger.error("Failed to send generic event to Emulator"); } } /* (non-Javadoc) * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendKey(int, int) */ public void sendKey(int character, int keycode, Properties keyCodeMap) { /* * Check if it's a character */ if (character > 0) { String text = String.valueOf((char) character); // check it's a blank space if (text.equals(" ")) { if (SdkUtils.isOphoneSDK()) { sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_SPACE); } else { sendAndroidEvent(EV_TYPE_KEY, "KEY_SPACE"); } } // check if it's a backspace else if (text.equals("\b")) { if (SdkUtils.isOphoneSDK()) { sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_BACKSPACE); } else { sendAndroidEvent(EV_TYPE_KEY, "KEY_BACKSPACE"); } } // check if it's an enter else if (text.equals("\r")) { if (SdkUtils.isOphoneSDK()) { sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_ENTER); } else { sendAndroidEvent(EV_TYPE_KEY, "KEY_ENTER"); } } else { if (keyCodeMap != null) { String keyCode = keyCodeMap.getProperty(text.toUpperCase().trim()); if (keyCode != null) { sendAndroidEvent(EV_TYPE_KEY, keyCode); } } } } else { if (!SdkUtils.isOphoneSDK()) { String keycode_str = null; if (SPECIAL_KEY_MAPPING.containsKey(keycode)) { keycode_str = SPECIAL_KEY_MAPPING.get(keycode); } if (keycode_str != null) { sendAndroidEvent(EV_TYPE_KEY, keycode_str); } } else { if (OPHONE_SPECIAL_KEY_MAPPING.containsKey(keycode)) { keycode = OPHONE_SPECIAL_KEY_MAPPING.get(keycode); } if (keycode != -1) { sendAndroidEvent(EV_TYPE_KEY, keycode); } } } } /* (non-Javadoc) * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendClick(int, boolean) */ public void sendClick(String code, boolean pressed) { sendAndroidEvent(EV_TYPE_KEY, code, pressed ? 1 : 0); } public void sendClick(int code, boolean pressed) { sendAndroidEvent(EV_TYPE_KEY, code, pressed ? 1 : 0); } public void sendWindowScale(double zoomFactor) { try { telnet.write("window scale " + zoomFactor, null); } catch (IOException e) { StudioLogger.error("Failed to send window scale to Emulator"); } } }