Android Open Source - wifikeyboard Wi Fi Input Method






From Project

Back to project page wifikeyboard.

License

The source code is released under:

GNU General Public License

If you think the Android project wifikeyboard listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * WiFi Keyboard - Remote Keyboard for Android.
 * Copyright (C) 2011 Ivan Volosyuk//  w  w  w .j  a va 2  s . c  o m
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.volosyukivan;

import java.util.HashSet;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.hardware.input.InputManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;

import com.volosyukivan.RemoteKeyListener.Stub;

public class WiFiInputMethod extends InputMethodService {
  public static final int KEY_HOME = -1000;
  public static final int KEY_END = -1001;
  public static final int KEY_CONTROL = -1002;
  public static final int KEY_DEL = -1003;

  @Override
  public void onStartInput(EditorInfo attribute, boolean restarting) {
    super.onStartInput(attribute, restarting);

//    boolean multiline = (attribute.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
//    Log.d("ivan", "onStartInput "
//       + "actionId=" + attribute.actionId + " "
//       + "id=" + attribute.fieldId + " "
//       + "name=" + attribute.fieldName + " "
//       + "opt=" + Integer.toHexString(attribute.imeOptions) + " "
//       + "inputType=" + Integer.toHexString(attribute.inputType) + " "
//       + "priv=" + attribute.privateImeOptions
//       + "multiline=" + multiline);

    try {
      String text = getText();
      remoteKeyboard.startTextEdit(text);
    } catch (RemoteException e) {
      Debug.e("failed communicating to HttpService", e);
    } catch (NullPointerException e) {
      Debug.e("remoteKeyboard is not connected yet", e);
    }
  }

  ServiceConnection serviceConnection;
  private RemoteKeyboard remoteKeyboard;
  protected Stub keyboardListener;

  @Override
  public void onDestroy() {
//    Debug.d("WiFiInputMethod onDestroy()");
    try {
      if (remoteKeyboard != null)
        remoteKeyboard.unregisterKeyListener(keyboardListener);
    } catch (RemoteException e) {
//      Debug.d("Failed to unregister listener");
    }
    remoteKeyboard = null;
    if (serviceConnection != null)
      unbindService(serviceConnection);
    serviceConnection = null;
    super.onDestroy();
  }

  PowerManager.WakeLock wakeLock;
  HashSet<Integer> pressedKeys = new HashSet<Integer>();

  @Override
  public void onCreate() {
    super.onCreate();
    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    wakeLock = pm.newWakeLock(
        PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "wifikeyboard");
//    Debug.d("WiFiInputMethod started");
    serviceConnection = new ServiceConnection() {
      public void onServiceConnected(ComponentName name, IBinder service) {
//        Debug.d("WiFiInputMethod connected to HttpService.");
        try {
          remoteKeyboard = RemoteKeyboard.Stub.asInterface(service);
          keyboardListener = new RemoteKeyListener.Stub() {
            @Override
            public void keyEvent(int code, boolean pressed) throws RemoteException {
              // Debug.d("got key in WiFiInputMethod");
              receivedKey(code, pressed);
            }
            @Override
            public void charEvent(int code) throws RemoteException {
              // Debug.d("got key in WiFiInputMethod");
              receivedChar(code);
            }
            @Override
            public boolean setText(String text) throws RemoteException {
              return WiFiInputMethod.this.setText(text);
            }
            @Override
            public String getText() throws RemoteException {
              return WiFiInputMethod.this.getText();
            }
          };
          RemoteKeyboard.Stub.asInterface(service).registerKeyListener(keyboardListener);
        } catch (RemoteException e) {
          throw new RuntimeException(
              "WiFiInputMethod failed to connected to HttpService.", e);
        }
      }
      //@Override
      public void onServiceDisconnected(ComponentName name) {
//        Debug.d("WiFiInputMethod disconnected from HttpService.");
      }
    };
    if (this.bindService(new Intent(this, HttpService.class),
        serviceConnection, BIND_AUTO_CREATE) == false) {
      throw new RuntimeException("failed to connect to HttpService");
    }
  }

  @Override
  public boolean onEvaluateFullscreenMode() {
    return false;
  }

  void receivedChar(int code) {
    wakeLock.acquire();
    wakeLock.release();
    InputConnection conn = getCurrentInputConnection();
    if (conn == null) {
//      Debug.d("connection closed");
      return;
    }

    if (pressedKeys.contains(KEY_CONTROL)) {
      switch (code) {
      case 'a':case 'A': selectAll(conn); return;
      case 'x':case 'X': cut(conn); return;
      case 'c':case 'C': copy(conn); return;
      case 'v':case 'V': paste(conn); return;
      }
    }

    String text = null;
    if (code >= 0 && code <= 65535) {
      text = new String(new char[] { (char) code } );
    } else {
      int HI_SURROGATE_START = 0xD800;
      int LO_SURROGATE_START = 0xDC00;
      int hi = ((code >> 10) & 0x3FF) - 0x040 | HI_SURROGATE_START;
      int lo = LO_SURROGATE_START | (code & 0x3FF);
      text = new String(new char[] { (char) hi, (char) lo } );
    }
    conn.commitText(text, 1);
  }

  void receivedKey(int code, boolean pressed) {
    if (code == KeyboardHttpServer.FOCUS) {
      for (int key : pressedKeys) {
        sendKey(key, false, false);
      }
      pressedKeys.clear();
      resetModifiers();
      return;
    }
    if (pressedKeys.contains(code) == pressed) {
      if (pressed == false) return;
      // ignore autorepeat on following keys
      switch (code) {
      case KeyEvent.KEYCODE_ALT_LEFT:
      case KeyEvent.KEYCODE_SHIFT_LEFT:
      case KeyEvent.KEYCODE_HOME:
      case KeyEvent.KEYCODE_MENU: return;
      }
    }
    if (pressed) {
      pressedKeys.add(code);
      sendKey(code, pressed, false);
    } else {
      pressedKeys.remove(code);
      sendKey(code, pressed, pressedKeys.isEmpty());
    }
  }

  void resetModifiers() {
    InputConnection conn = getCurrentInputConnection();
    if (conn == null) {
      return;
    }
    conn.clearMetaKeyStates(
        KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON);
  }

  private long lastWake;

  void sendKey(int code, boolean down, boolean resetModifiers) {
    long time = System.currentTimeMillis();
    if (time - lastWake > 5000) {
      wakeLock.acquire();
      wakeLock.release();
      lastWake = time;
    }
    InputConnection conn = getCurrentInputConnection();
    if (conn == null) {
//      Debug.d("connection closed");
      return;
    }
    if (code < 0) {
      if (down == false) return;
      switch (code) {
      case KEY_HOME: keyHome(conn); break;
      case KEY_END: keyEnd(conn); break;
      case KEY_DEL: keyDel(conn); break;
      }
      return;
    }

    if (pressedKeys.contains(KEY_CONTROL)) {
      switch (code) {
        case KeyEvent.KEYCODE_DPAD_LEFT:
          if (!down) return;
          wordLeft(conn);
          return;
        case KeyEvent.KEYCODE_DPAD_RIGHT:
          if (!down) return;
          wordRight(conn);
          return;
        case KeyEvent.KEYCODE_DEL:
          if (!down) return;
          deleteWordLeft(conn);
          return;
        case KeyEvent.KEYCODE_FORWARD_DEL:
          deleteWordRight(conn);
          return;
        case KeyEvent.KEYCODE_DPAD_CENTER:
          if (!down) return;
          copy(conn);
          return;
      }
    }

    if (pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT)) {
      switch (code) {
        case KeyEvent.KEYCODE_DPAD_CENTER:
          if (!down) return;
          paste(conn);
          return;
      }
    }

    if (code == KeyEvent.KEYCODE_ENTER) {
      if (shouldSend()) {
        if (!down) return;
        Log.d("ivan", "sending submit action");
        conn.performEditorAction(EditorInfo.IME_ACTION_SEND);
        return;
      }
    }

//    if (pressedKeys.contains(KEY_CONTROL)) {
//      if (down == false) return;
//      switch (code) {
//      case KeyEvent.KEYCODE_A: selectAll(conn); break;
//      case KeyEvent.KEYCODE_X: cut(conn); break;
//      case KeyEvent.KEYCODE_C: copy(conn); break;
//      case KeyEvent.KEYCODE_V: paste(conn); break;
//      }
//      return;
//    }

    conn.sendKeyEvent(new KeyEvent(
        android.os.SystemClock.uptimeMillis(),
        android.os.SystemClock.uptimeMillis(),
        down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
            code,
            0,
            (pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT)
                ? KeyEvent.META_SHIFT_LEFT_ON : 0) +
            (pressedKeys.contains(KEY_CONTROL)? KeyEvent.META_CTRL_ON : 0) +
            (pressedKeys.contains(KeyEvent.KEYCODE_ALT_LEFT)
                ? KeyEvent.META_ALT_LEFT_ON : 0)

        ));

    if (resetModifiers) {
      conn.clearMetaKeyStates(
          KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON);
    }
  }

  private boolean shouldSend() {
    if (pressedKeys.contains(KEY_CONTROL)) {
//      Log.d("ivan", "Control pressed");
      return true;
    }
    EditorInfo editorInfo = getCurrentInputEditorInfo();
    if (editorInfo == null) {
//      Log.d("ivan", "No editor info");
      return false;
    }

    if ((editorInfo.inputType & InputType.TYPE_CLASS_TEXT) == 0) {
//      Log.d("ivan", "Not text, sending enter");
      return false;
    }


    if ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0) {
//      Log.d("ivan", "Multi-line, sending ordinary enter");
      return false;
    }

    int action = editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
    if (action == EditorInfo.IME_ACTION_NONE || action == EditorInfo.IME_ACTION_DONE) {
//      Log.d("ivan", "No useful action, sending enter");
      return false;
    }
//    Log.d("ivan", "Useful action to be performed");
    return true;
  }

  private void keyDel(InputConnection conn) {
    // if control key used -- delete word right
    if (pressedKeys.contains(KEY_CONTROL)) {
      deleteWordRight(conn);
      return;
    }

    if (pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT)) {
      cut(conn);
      return;
    }
    conn.deleteSurroundingText(0, 1);
    conn.commitText("", 0);
  }

  private void paste(InputConnection conn) {
    conn.performContextMenuAction(android.R.id.paste);
  }

  private void copy(InputConnection conn) {
    conn.performContextMenuAction(android.R.id.copy);
  }

  private void cut(InputConnection conn) {
    conn.performContextMenuAction(android.R.id.cut);
  }

  private void selectAll(InputConnection conn) {
    ExtractedText text = conn.getExtractedText(req, 0);
    try {
      conn.setSelection(0, text.text.length());
    } catch (NullPointerException e) {
      // Potentially, text or text.text can be null
    }
  }

  ExtractedTextRequest req = new ExtractedTextRequest();
  {
    req.hintMaxChars = 100000;
    req.hintMaxLines = 10000;
  }

  private void deleteWordRight(InputConnection conn) {
    ExtractedText text = conn.getExtractedText(req, 0);
    if (text == null) return;

    int end = text.selectionEnd;
    String str = text.text.toString();
    int len = str.length();

    for (; end < len; end++) {
      if (!Character.isSpace(str.charAt(end))) break;
    }
    for (; end < len; end++) {
      if (Character.isSpace(str.charAt(end))) break;
    }

    conn.deleteSurroundingText(0, end - text.selectionEnd);
  }

  private void deleteWordLeft(InputConnection conn) {
    // TODO: what is the correct word deleting policy?
    // delete until next space? until next different character type?
    ExtractedText text = conn.getExtractedText(req, 0);
    if (text == null) return;

    int end = text.selectionEnd - 1;

    String str = text.text.toString();

    for (; end >= 0; end--) {
      if (!Character.isSpace(str.charAt(end))) break;
    }
    for (; end >= 0; end--) {
      if (Character.isSpace(str.charAt(end))) break;
    }
    end++;
    conn.deleteSurroundingText(text.selectionEnd - end, 0);
  }

  private void wordRight(InputConnection conn) {
    boolean shift = pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT);
    ExtractedText text = conn.getExtractedText(req, 0);
    if (text == null) return;

    int end = text.selectionEnd;
    String str = text.text.toString();
    int len = str.length();

    for (; end < len; end++) {
      if (!Character.isSpace(str.charAt(end))) break;
    }
    for (; end < len; end++) {
      if (Character.isSpace(str.charAt(end))) break;
    }
    int start = shift ? text.selectionStart : end;
    Log.d("wifikeyboard", "start = " + start + " end = " + end);
    conn.setSelection(start, end);
  }

  private void wordLeft(InputConnection conn) {
    boolean shift = pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT);
    ExtractedText text = conn.getExtractedText(req, 0);
    if (text == null) return;

    int end = text.selectionEnd - 1;

    String str = text.text.toString();

    for (; end >= 0; end--) {
      if (!Character.isSpace(str.charAt(end))) break;
    }
    for (; end >= 0; end--) {
      if (Character.isSpace(str.charAt(end))) break;
    }
    end++;
    int start = shift ? text.selectionStart : end;
    Log.d("wifikeyboard", "start = " + start + " end = " + end);
    conn.setSelection(start, end);
  }

  private void keyEnd(InputConnection conn) {
    boolean control = pressedKeys.contains(KEY_CONTROL);
    boolean shift = pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT);
    ExtractedText text = conn.getExtractedText(req, 0);
    if (text == null) return;
    int end;
    if (control) {
      end = text.text.length();
    } else {
      end = text.text.toString().indexOf('\n', text.selectionEnd);
      if (end == -1) end = text.text.length();
    }
    int start = shift ? text.selectionStart : end;
    Log.d("wifikeyboard", "start = " + start + " end = " + end);
    conn.setSelection(start, end);
  }

  private void keyHome(InputConnection conn) {
    boolean control = pressedKeys.contains(KEY_CONTROL);
    boolean shift = pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT);
    ExtractedText text = conn.getExtractedText(req, 0);
    if (text == null) return;

    int end;
    if (control) {
      end = 0;
    } else {
      end = text.text.toString().lastIndexOf('\n', text.selectionEnd - 1);
      end++;
    }
    int start = shift ? text.selectionStart : end;
    Log.d("wifikeyboard", "start = " + start + " end = " + end);
   conn.setSelection(start, end);
  }

  boolean setText(String text) {
    // FIXME: need feedback if the input was lost
    InputConnection conn = getCurrentInputConnection();
    if (conn == null) {
//      Debug.d("connection closed");
      return false;
    }
    conn.beginBatchEdit();
    // FIXME: hack
    conn.deleteSurroundingText(100000, 100000);
    conn.commitText(text, text.length());
    conn.endBatchEdit();
    return true;
  }

  String getText() {
    String text = "";
    try {
      InputConnection conn = getCurrentInputConnection();
      ExtractedTextRequest req = new ExtractedTextRequest();
      req.hintMaxChars = 1000000;
      req.hintMaxLines = 10000;
      req.flags = 0;
      req.token = 1;
      text = conn.getExtractedText(req, 0).text.toString();
    } catch (Throwable t) {
    }
    return text;
  }
}




Java Source Code List

com.volosyukivan.Debug.java
com.volosyukivan.HttpConnection.java
com.volosyukivan.HttpServer.java
com.volosyukivan.HttpService.java
com.volosyukivan.KeyboardHttpConnection.java
com.volosyukivan.KeyboardHttpServer.java
com.volosyukivan.KeycodeConvertor.java
com.volosyukivan.WiFiInputMethod.java
com.volosyukivan.WiFiKeyboard.java
com.volosyukivan.WidgetActivity.java
com.volosyukivan.WidgetConfigure.java
com.volosyukivan.WidgetProvider.java