summaryrefslogtreecommitdiff
path: root/src/us/minak/IMEGestureOverlayView.java
blob: 35fe1ebe7bc7dc34adacdee08b78886dc6b13242 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package us.minak;

import android.content.Context;
import android.gesture.Gesture;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.gesture.Prediction;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.KeyCharacterMap;
import android.view.MotionEvent;
import android.view.inputmethod.InputConnection;

import java.util.List;

public class IMEGestureOverlayView extends GestureOverlayView implements OnGesturePerformedListener {
	private static final double SCORE_TRESHOLD = 3.0;
	private final GestureLibrary mGestureLibrary;
	private InputConnectionGetter icGetter = new InputConnectionGetter.NullGetter();
	private final IMEModifiers modifiers = new IMEModifiers();
	private final KeyCharacterMap charMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
	float x = -1, y = -1;
	int meta = 0;

	// cache for repeated calls
	InputConnection ic = null;

	public IMEGestureOverlayView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mGestureLibrary = SettingsUtil.getGestureLibrary(context);
		mGestureLibrary.load();
		addOnGesturePerformedListener(this);
	}

	public void setInputConnectionGetter(InputConnectionGetter icGetter) {
		this.icGetter = icGetter;
	}

	private void sendKeyEvent(KeyEvent keyEvent) {
		if (ic != null) {
			ic.sendKeyEvent(new KeyEvent(
					keyEvent.getDownTime(),
					keyEvent.getEventTime(),
					keyEvent.getAction(),
					keyEvent.getKeyCode(),
					keyEvent.getRepeatCount(),
					keyEvent.getMetaState() | meta));
		}
	}

	/**
	 * This function is pretty strongly based on the code in
	 * Samsung's "Penboard" whitepaper.
	 */
	@Override
	public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
		final List<Prediction> predictions = mGestureLibrary.recognize(gesture);
		Prediction bestPrediction = null;
		if (!predictions.isEmpty()) {
			bestPrediction = predictions.get(0);
		}

		ic = icGetter.getCurrentInputConnection();
		if (ic != null) {
			if (bestPrediction != null) {
				if (bestPrediction.score > SCORE_TRESHOLD) {
					for (KeyEvent keyEvent : charMap.getEvents(bestPrediction.name.toCharArray()))
						sendKeyEvent(keyEvent);
				} else {
					clear(false);
				}
			}
			for (IMEModifier modifier : modifiers.getSelection()) {
				sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, modifier.keycode));
			}
		}
		modifiers.clearSelection();
		meta = 0;
		invalidate();
		x = y = -1;
	}

	@Override
	public void onDraw(Canvas canvas) {
		float d = Math.min(canvas.getWidth(), canvas.getHeight());
		modifiers.draw(canvas, d/2, d/2, d*.47F);
	}

	@Override
	public boolean onTouchEvent (MotionEvent event) {
		if (x < 0 && y < 0 && event.getActionMasked() == MotionEvent.ACTION_DOWN) {
			x = event.getX();
			y = event.getY();
			modifiers.setSelectionPoint(x, y);
			invalidate();

			ic = icGetter.getCurrentInputConnection();
			if (ic != null) {
				for (IMEModifier modifier : modifiers.getSelection()) {
					sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, modifier.keycode));
					meta |= modifier.metamask;
				}
				return true;
			}
		}
		return false;
	}
}