Files
gio/ui/app/GioView.java
T
Elias Naur 503c417544 ui/app: (android) enable IME dummy mode
We don't have full IME-aware editor yet. Fortunately, the BaseInputConnection
has a "dummy mode" setting to allow us to receive input from the fancier keyboards
without implementing the full IME interface.

Fixes gio#7 (I hope; I tested with SwiftKey that had the same symptoms)

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2019-06-11 16:35:43 +02:00

198 lines
5.3 KiB
Java

// SPDX-License-Identifier: Unlicense OR MIT
package org.gioui;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.text.Editable;
import android.view.Choreographer;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.EditorInfo;
public class GioView extends SurfaceView implements Choreographer.FrameCallback {
private final SurfaceHolder.Callback callbacks;
private final InputMethodManager imm;
private final Handler handler;
private long nhandle;
public GioView(Context context) {
this(context, null);
}
public GioView(Context context, AttributeSet attrs) {
super(context, attrs);
handler = new Handler();
imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
setFocusable(true);
setFocusableInTouchMode(true);
callbacks = new SurfaceHolder.Callback() {
@Override public void surfaceCreated(SurfaceHolder holder) {
// Ignore; surfaceChanged is guaranteed to be called immediately after this.
}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
onSurfaceChanged(nhandle, getHolder().getSurface());
}
@Override public void surfaceDestroyed(SurfaceHolder holder) {
onSurfaceDestroyed(nhandle);
}
};
getHolder().addCallback(callbacks);
nhandle = onCreateView(this);
}
@Override public boolean onKeyDown(int keyCode, KeyEvent event) {
onKeyEvent(nhandle, keyCode, event.getUnicodeChar(), event.getEventTime());
return false;
}
@Override public boolean onTouchEvent(MotionEvent event) {
// Ask for unbuffered events. Flutter and Chrome does it
// so I assume its good for us as well.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
requestUnbufferedDispatch(event);
}
for (int j = 0; j < event.getHistorySize(); j++) {
long time = event.getHistoricalEventTime(j);
for (int i = 0; i < event.getPointerCount(); i++) {
onTouchEvent(
nhandle,
event.ACTION_MOVE,
event.getPointerId(i),
event.getToolType(i),
event.getHistoricalX(i, j),
event.getHistoricalY(i, j),
time);
}
}
int act = event.getActionMasked();
int idx = event.getActionIndex();
for (int i = 0; i < event.getPointerCount(); i++) {
int pact = event.ACTION_MOVE;
if (i == idx) {
pact = act;
}
onTouchEvent(
nhandle,
act,
event.getPointerId(i),
event.getToolType(i),
event.getX(i),
event.getY(i),
event.getEventTime());
}
return true;
}
@Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new InputConnection(this);
}
void showTextInput() {
post(new Runnable() {
@Override public void run() {
GioView.this.requestFocus();
imm.showSoftInput(GioView.this, 0);
}
});
}
void hideTextInput() {
post(new Runnable() {
@Override public void run() {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
});
}
void postFrameCallbackOnMainThread() {
handler.post(new Runnable() {
@Override public void run() {
postFrameCallback();
}
});
}
void postFrameCallback() {
Choreographer.getInstance().removeFrameCallback(this);
Choreographer.getInstance().postFrameCallback(this);
}
@Override public void doFrame(long nanos) {
onFrameCallback(nhandle, nanos);
}
int getDensity() {
return getResources().getDisplayMetrics().densityDpi;
}
float getFontScale() {
return getResources().getConfiguration().fontScale;
}
void start() {
onStartView(nhandle);
}
void stop() {
onStopView(nhandle);
}
void destroy() {
getHolder().removeCallback(callbacks);
onDestroyView(nhandle);
nhandle = 0;
}
void configurationChanged() {
onConfigurationChanged(nhandle);
}
void lowMemory() {
onLowMemory();
}
boolean backPressed() {
return onBack(nhandle);
}
static private native long onCreateView(GioView view);
static private native void onDestroyView(long handle);
static private native void onStartView(long handle);
static private native void onStopView(long handle);
static private native void onSurfaceDestroyed(long handle);
static private native void onSurfaceChanged(long handle, Surface surface);
static private native void onConfigurationChanged(long handle);
static private native void onLowMemory();
static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, long time);
static private native void onKeyEvent(long handle, int code, int character, long time);
static private native void onFrameCallback(long handle, long nanos);
static private native boolean onBack(long handle);
private static class InputConnection extends BaseInputConnection {
private final Editable editable;
InputConnection(View view) {
// Passing false enables "dummy mode", where the BaseInputConnection
// attempts to convert IME operations to key events.
super(view, false);
editable = Editable.Factory.getInstance().newEditable("");
}
@Override public Editable getEditable() {
return editable;
}
}
}