package com.iiordanov.bVNC.input; import android.content.Context; import android.os.Handler; import android.os.SystemClock; import android.view.KeyCharacterMap; import android.view.KeyEvent; import com.iiordanov.bVNC.MetaKeyBean; import com.iiordanov.bVNC.RfbConnectable; import com.iiordanov.bVNC.RemoteCanvas; public abstract class RemoteKeyboard { public final static int SCAN_ESC = 1; public final static int SCAN_LEFTCTRL = 29; public final static int SCAN_LEFTSHIFT = 42; public final static int SCAN_RIGHTSHIFT = 54; public final static int SCAN_LEFTALT = 56; public final static int SCAN_RIGHTCTRL = 97; public final static int SCAN_RIGHTALT = 100; public final static int SCAN_DELETE = 111; public final static int SCAN_LEFTSUPER = 125; public final static int SCAN_RIGHTSUPER = 126; public final static int SCAN_F1 = 59; public final static int SCAN_F2 = 60; public final static int SCAN_F3 = 61; public final static int SCAN_F4 = 62; public final static int SCAN_F5 = 63; public final static int SCAN_F6 = 64; public final static int SCAN_F7 = 65; public final static int SCAN_F8 = 66; public final static int SCAN_F9 = 67; public final static int SCAN_F10 = 68; //public final static int SCAN_HOME = 102; //public final static int SCAN_END = 107; // Useful shortcuts for modifier masks. public final static int CTRL_MASK = KeyEvent.META_SYM_ON; public final static int SHIFT_MASK = KeyEvent.META_SHIFT_ON; public final static int ALT_MASK = KeyEvent.META_ALT_ON; public final static int SUPER_MASK = 0x8; public final static int RCTRL_MASK = KeyEvent.META_CTRL_RIGHT_ON; public final static int RSHIFT_MASK = KeyEvent.META_SHIFT_RIGHT_ON; public final static int RALT_MASK = 0x10; public final static int RSUPER_MASK = KeyEvent.META_META_RIGHT_ON; protected RemoteCanvas canvas; protected Handler handler; protected RfbConnectable rfb; protected Context context; protected KeyRepeater keyRepeater; // Variable holding the state of any pressed hardware meta keys (Ctrl, Alt...) protected int hardwareMetaState = 0; // Use camera button as meta key for right mouse button boolean cameraButtonDown = false; // Keep track when a seeming key press was the result of a menu shortcut int lastKeyDown; boolean afterMenu; // Variable holding the state of the on-screen buttons for meta keys (Ctrl, Alt...) protected int onScreenMetaState = 0; // Variable holding the state of the last metaState resulting from a button press. protected int lastDownMetaState = 0; // Variable used for BB10 workarounds boolean bb = false; RemoteKeyboard (RfbConnectable r, RemoteCanvas v, Handler h) { this.rfb = r; this.canvas = v; this.handler = h; keyRepeater = new KeyRepeater (this, h); if (android.os.Build.MODEL.contains("BlackBerry") || android.os.Build.BRAND.contains("BlackBerry") || android.os.Build.MANUFACTURER.contains("BlackBerry")) { bb = true; } } public boolean processLocalKeyEvent(int keyCode, KeyEvent evt) { return processLocalKeyEvent (keyCode, evt, 0); } public abstract boolean processLocalKeyEvent(int keyCode, KeyEvent evt, int additionalMetaState); public void repeatKeyEvent(int keyCode, KeyEvent event) { keyRepeater.start(keyCode, event); } public void stopRepeatingKeyEvent() { keyRepeater.stop(); } public abstract void sendMetaKey(MetaKeyBean meta); /** * Toggles on-screen Ctrl mask. Returns true if result is Ctrl enabled, false otherwise. * @return true if on false otherwise. */ public boolean onScreenCtrlToggle() { // If we find Ctrl on, turn it off. Otherwise, turn it on. if (onScreenMetaState == (onScreenMetaState | CTRL_MASK)) { onScreenCtrlOff(); return false; } else { onScreenMetaState = onScreenMetaState | CTRL_MASK; return true; } } /** * Turns off on-screen Ctrl. */ public void onScreenCtrlOff() { onScreenMetaState = onScreenMetaState & ~CTRL_MASK; } /** * Toggles on-screen Alt mask. Returns true if result is Alt enabled, false otherwise. * @return true if on false otherwise. */ public boolean onScreenAltToggle() { // If we find Alt on, turn it off. Otherwise, turn it on. if (onScreenMetaState == (onScreenMetaState | ALT_MASK)) { onScreenAltOff(); return false; } else { onScreenMetaState = onScreenMetaState | ALT_MASK; return true; } } /** * Turns off on-screen Alt. */ public void onScreenAltOff() { onScreenMetaState = onScreenMetaState & ~ALT_MASK; } /** * Toggles on-screen Super mask. Returns true if result is Super enabled, false otherwise. * @return true if on false otherwise. */ public boolean onScreenSuperToggle() { // If we find Super on, turn it off. Otherwise, turn it on. if (onScreenMetaState == (onScreenMetaState | SUPER_MASK)) { onScreenSuperOff(); return false; } else { onScreenMetaState = onScreenMetaState | SUPER_MASK; return true; } } /** * Turns off on-screen Super. */ public void onScreenSuperOff() { onScreenMetaState = onScreenMetaState & ~SUPER_MASK; } /** * Toggles on-screen Shift mask. Returns true if result is Shift enabled, false otherwise. * @return true if on false otherwise. */ public boolean onScreenShiftToggle() { // If we find Super on, turn it off. Otherwise, turn it on. if (onScreenMetaState == (onScreenMetaState | SHIFT_MASK)) { onScreenShiftOff(); return false; } else { onScreenMetaState = onScreenMetaState | SHIFT_MASK; return true; } } /** * Turns off on-screen Shift. */ public void onScreenShiftOff() { onScreenMetaState = onScreenMetaState & ~SHIFT_MASK; } public int getMetaState () { return onScreenMetaState|lastDownMetaState; } public void setAfterMenu(boolean value) { afterMenu = value; } public boolean getCameraButtonDown() { return cameraButtonDown; } public void clearMetaState () { onScreenMetaState = 0; } public void sendText(String s) { for (int i = 0; i < s.length(); i++) { KeyEvent event = null; char c = s.charAt(i); if (Character.isISOControl(c)) { if (c == '\n') { int keyCode = KeyEvent.KEYCODE_ENTER; processLocalKeyEvent(keyCode, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); try { Thread.sleep(10); } catch (InterruptedException e) { } processLocalKeyEvent(keyCode, new KeyEvent(KeyEvent.ACTION_UP, keyCode)); } } else { event = new KeyEvent(SystemClock.uptimeMillis(), s.substring(i, i+1), KeyCharacterMap.FULL, 0); processLocalKeyEvent(event.getKeyCode(), event); try { Thread.sleep(10); } catch (InterruptedException e) { } } } } public void sendKeySym (int keysym, int metaState) { char c = (char)XKeySymCoverter.keysym2ucs(keysym); sendUnicode(c, metaState); } /** * Tries to convert a unicode character to a KeyEvent and if successful sends with keyEvent(). * @param unicodeChar * @param metaState */ public boolean sendUnicode (char unicodeChar, int additionalMetaState) { KeyCharacterMap fullKmap = KeyCharacterMap.load(KeyCharacterMap.FULL); KeyCharacterMap virtualKmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); char[] s = new char[1]; s[0] = unicodeChar; KeyEvent[] events = fullKmap.getEvents(s); // Failing with the FULL keymap, try the VIRTUAL_KEYBOARD one. if (events == null) { events = virtualKmap.getEvents(s); } if (events != null) { for (int i = 0; i < events.length; i++) { KeyEvent evt = events[i]; processLocalKeyEvent(evt.getKeyCode(), evt, additionalMetaState); KeyEvent upEvt = new KeyEvent(KeyEvent.ACTION_UP, evt.getKeyCode()); processLocalKeyEvent(upEvt.getKeyCode(), upEvt, additionalMetaState); return true; } } else { android.util.Log.e("RemoteKeyboard", "Could not use any keymap to generate KeyEvent for unicode: " + unicodeChar); } return false; } /** * Converts event meta state to our meta state. * @param event * @return */ protected int convertEventMetaState (KeyEvent event) { return convertEventMetaState(event, event.getMetaState()); } /** * Converts event meta state to our meta state. * @param event * @return */ protected int convertEventMetaState (KeyEvent event, int eventMetaState) { int metaState = 0; int leftAltMetaStateMask = 0; // Detect whether this event is coming from a default hardware keyboard. // We have to leave KeyEvent.KEYCODE_ALT_LEFT for symbol input on a default hardware keyboard. boolean defaultHardwareKbd = (event.getScanCode() != 0 && event.getDeviceId() == 0); if (!bb && !defaultHardwareKbd) { leftAltMetaStateMask = 0x10 /*KeyEvent.META_ALT_LEFT_ON*/; } // Add shift, ctrl, alt, and super to metaState if necessary. if ((eventMetaState & 0x00000040 /*KeyEvent.META_SHIFT_LEFT_ON*/) != 0) { metaState |= SHIFT_MASK; } if ((eventMetaState & 0x00000080 /*KeyEvent.META_SHIFT_RIGHT_ON*/) != 0) { metaState |= RSHIFT_MASK; } if ((eventMetaState & 0x00002000 /*KeyEvent.META_CTRL_LEFT_ON*/) != 0) { metaState |= CTRL_MASK; } if ((eventMetaState & 0x00004000 /*KeyEvent.META_CTRL_RIGHT_ON*/) != 0) { metaState |= RCTRL_MASK; } if ((eventMetaState & leftAltMetaStateMask) !=0) { metaState |= ALT_MASK; } if ((eventMetaState & 0x00000020 /*KeyEvent.META_ALT_RIGHT_ON*/) !=0) { metaState |= RALT_MASK; } if ((eventMetaState & 0x00020000 /*KeyEvent.META_META_LEFT_ON*/) != 0) { metaState |= SUPER_MASK; } if ((eventMetaState & 0x00040000 /*KeyEvent.META_META_RIGHT_ON*/) != 0) { metaState |= RSUPER_MASK; } return metaState; } }