InputMethodServiceを使ってIMを作るメモ
ネタ帳からの大体そのままな引用(要は雑って事)。以前、Androidのソフトウェアキーボードを作って、PC側とbluetoothを経由してスマフォ上のテキスト入力をPCから入力させるとかっていうのをやってた。とりあえず書いておこうかと
方式
bluetooth RFCOMMなプロファイルを利用してPC側からスマフォ側にbluetooth経由でテキストを送信。それをアクティブなIMで受け取って入力を反映させるっていうだけ
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="net.kinjouj.app.bluetooth_keyboard"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<service android:name=".BluetoothKeyboardService" android:label="@string/app_name" android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter>
<action android:name="android.view.InputMethod" />
</intent-filter>
<meta-data android:name="android.view.im" android:resource="@xml/inputmethod" />
</service>
</application>
</manifest>
res/layout/input.xml
<?xml version="1.0" encoding="utf-8"?>
<android.inputmethodservice.KeyboardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/keyboard"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
後々必要になるので
res/xml/inputmethod.xml
<?xml version="1.0" ?>
<input-method
xmlns:android="http://schemas.android.com/apk/res/android" />
設定要件はR.styleable#InputMethodを参照
res/xml/keyboard.xml
ソフトウェアキーボードなレイアウトを定義
<?xml version="1.0" ?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android">
<Row android:rowEdgeFlags="top" android:keyHeight="100px">
<Key
android:codes="-5"
android:keyIcon="@*android:drawable/sym_keyboard_delete"
android:keyWidth="50%p"
android:keyEdgeFlags="right"
android:isRepeatable="true" />
<Key
android:codes="10"
android:keyIcon="@*android:drawable/sym_keyboard_return"
android:keyWidth="50%p"
android:keyEdgeFlags="right"/>
</Row>
</Keyboard>
codesで指定されたコードはキーをタッチしたい等な場合に引数に指定される(primaryCodeとか)。又、設定要件は http://developer.android.com/reference/android/inputmethodservice/Keyboard.html を参照
まぁ設定ファイル系はこれだけ。あとはサービスクラスを書いたりとか
BluetoothKeyboardService.java
package net.kinjouj.app.bluetooth_keyboard;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.KeyboardView;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import static android.view.KeyEvent.*;
public class BluetoothKeyboardService extends InputMethodService implements KeyboardView.OnKeyboardActionListener {
private final String TAG = getClass().getSimpleName();
private BluetoothServerSocket server;
private BluetoothSocket socket;
private KeyboardView kv;
@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "onCreate");
}
@Override
public void onInitializeInterface() {
super.onInitializeInterface();
Log.v(TAG,"onInitializeInterface");
}
@Override
public View onCreateInputView() {
kv = (KeyboardView)getLayoutInflater().inflate(R.layout.input, null);
kv.setKeyboard(new Keyboard(this, R.xml.keyboard));
kv.setOnKeyboardActionListener(this);
return kv;
}
@Override
public View onCreateCandidatesView() {
return super.onCreateCandidatesView();
}
@Override
public void onStartInput(EditorInfo attribute, boolean restarting) {
super.onStartInput(attribute, restarting);
Log.v(TAG, "onStartInput");
if(server == null) {
startBluetooth();
}
}
@Override
public void onFinishInput() {
super.onFinishInput();
if(kv != null) {
kv.closing();
}
}
public void onKey(int primaryCode, int[] keyCodes) {
}
public void onPress(int primaryCode) {
Log.v(TAG, "onPress");
Log.v(TAG, "primaryCode: " + primaryCode);
InputConnection conn = getCurrentInputConnection();
switch(primaryCode) {
case 10:
//shutdownBluetooth();
conn.sendKeyEvent(new KeyEvent(ACTION_DOWN, KEYCODE_ENTER));
conn.sendKeyEvent(new KeyEvent(ACTION_UP, KEYCODE_ENTER));
break;
case -5:
// deleteキーを押した場合の動作
break;
}
}
public void onRelease(int primaryCode) {
}
public void onText(CharSequence text) {
}
public void swipeDown() {
}
public void swipeLeft() {
}
public void swipeRight() {
}
public void swipeUp() {
}
private void startBluetooth() {
try {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
server = adapter.listenUsingRfcommWithServiceRecord(
"RFCOMM Service",
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
);
new Thread("server") {
@Override
public void run() {
while(true) {
try {
socket = server.accept();
if(socket != null) {
InputStream is = null;
try {
is = socket.getInputStream();
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(is));
String str = null;
while((str = br.readLine()) != null) {
InputConnection conn = getCurrentInputConnection();
if(conn != null) {
if(!TextUtils.isEmpty(str)) {
conn.commitText(str, 0);
}
}
}
} catch(IOException e) {
e.printStackTrace();
} finally {
if(br != null) {
br.close();
}
}
} catch(IOException e) {
e.printStackTrace();
break;
}
}
} catch(IOException e) {
e.printStackTrace();
break;
}
}
}
}.start();
} catch(Exception e) {
e.printStackTrace();
}
}
private void shutdownBluetooth() {
try {
if(socket != null) {
socket.close();
socket = null;
}
} catch(IOException e) {
e.printStackTrace();
}
}
}
キーボード入力が開始される(ソフトウェアキーボードが起動される?)際にbluetoothの接続を行なってRFCOMMプロファイルで要求を待ち受ける。でテキストな要求が来た際にはソフトウェアキーボードに対してテキストをコミットする。まぁそんだけ
動かしてみる
アプリを入れるとキーボード設定で
っていうように表示されるようになる。でチェックを入れておかないと使えるようにならないのでチェックしておく
あとはテキストを入力するフォーカスで入力方式でBluetoothKeyboardを選択すると
って言うように出る。あとはPC側から入力を反映させる適当なスクリプトを書く
from bluetooth import find_service,RFCOMM,BluetoothSocket,BluetoothError
host = None
port = 0
for service in find_service():
if service["protocol"] == "RFCOMM" and service["name"] == "RFCOMM Service":
host = service["host"]
port = service["port"]
if host is not None:
sock = BluetoothSocket(RFCOMM)
sock.connect((host, port))
print "Connected: %s:%d" % (host, port)
while True:
data = raw_input("message: ")
if data is not None and len(data) > 0:
try:
sock.send("%sn" % data)
except BluetoothError, e:
break
sock.close()
っていう感じでやればPC側からスマフォ側にbluetoothを経由してIMでキャッチしてテキスト入力をさせたりとかっていう事案もまぁ出来るっちゃ出来る