/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.backends.android;
import android.app.Dialog;
import android.content.Context;
import android.os.Handler;
import android.text.Editable;
import android.text.InputFilter;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.badlogic.gdx.Input.Peripheral;
/** Responsible for showing and hiding the Android onscreen keyboard (aka softkeyboard). Uses a dialog with an invisible TextView
* and injects key down/up and typed events into AndroidInput. Only the delete and back keys will trigger key down/up events.
* Alphanumeric keys will be directly injected as key typed events which is sufficient to implement things like text fields.
*
* Since the input mechanism for softkeyboards is a bit complex, we don't directly get key events from the softkeyboard. Instead
* we intercept calls to the Editable of the invisible TextView which we translate into delete key events and key typed events.
*
* @author mzechner */
class AndroidOnscreenKeyboard implements OnKeyListener, OnTouchListener {
final Context context;
final Handler handler;
final AndroidInput input;
Dialog dialog;
TextView textView;
public AndroidOnscreenKeyboard (Context context, Handler handler, AndroidInput input) {
this.context = context;
this.handler = handler;
this.input = input;
}
Dialog createDialog () {
textView = createView(context);
textView.setOnKeyListener(this);
FrameLayout.LayoutParams textBoxLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM);
textView.setLayoutParams(textBoxLayoutParams);
textView.setFocusable(true);
textView.setFocusableInTouchMode(true);
textView.setImeOptions(textView.getImeOptions() | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
final FrameLayout layout = new FrameLayout(context);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
layout.setLayoutParams(layoutParams);
layout.addView(textView);
layout.setOnTouchListener(this);
dialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar_Fullscreen);
dialog.setContentView(layout);
return dialog;
}
public static TextView createView (Context context) {
final TextView view = new TextView(context) {
Editable editable = new PassThroughEditable();
@Override
protected boolean getDefaultEditable () {
return true;
}
@Override
public Editable getEditableText () {
return editable;
}
@Override
protected MovementMethod getDefaultMovementMethod () {
return ArrowKeyMovementMethod.getInstance();
}
@Override
public boolean onKeyDown (int keyCode, KeyEvent event) {
Log.d("Test", "down keycode: " + event.getKeyCode());
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp (int keyCode, KeyEvent event) {
Log.d("Test", "up keycode: " + event.getKeyCode());
return super.onKeyUp(keyCode, event);
}
};
// view.setCursorVisible(false);
return view;
}
public void setVisible (boolean visible) {
if (visible && dialog != null) {
dialog.dismiss();
dialog = null;
}
if (visible && dialog == null && !input.isPeripheralAvailable(Peripheral.HardwareKeyboard)) {
handler.post(new Runnable() {
@Override
public void run () {
dialog = createDialog();
dialog.show();
handler.post(new Runnable() {
@Override
public void run () {
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
InputMethodManager input = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (input != null) input.showSoftInput(textView, InputMethodManager.SHOW_FORCED);
}
});
final View content = dialog.getWindow().findViewById(Window.ID_ANDROID_CONTENT);
content.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
int[] screenloc = new int[2];
private int keyboardHeight;
private boolean keyboardShowing;
@Override
public boolean onPreDraw () {
content.getLocationOnScreen(screenloc);
keyboardHeight = Math.abs(screenloc[1]);
if (keyboardHeight > 0) keyboardShowing = true;
if (keyboardHeight == 0 && keyboardShowing) {
dialog.dismiss();
dialog = null;
}
return true;
}
});
}
});
} else {
if (!visible && dialog != null) {
dialog.dismiss();
}
}
}
public static class PassThroughEditable implements Editable {
@Override
public char charAt (int index) {
Log.d("Editable", "charAt");
return 0;
}
@Override
public int length () {
Log.d("Editable", "length");
return 0;
}
@Override
public CharSequence subSequence (int start, int end) {
Log.d("Editable", "subSequence");
return null;
}
@Override
public void getChars (int start, int end, char[] dest, int destoff) {
Log.d("Editable", "getChars");
}
@Override
public void removeSpan (Object what) {
Log.d("Editable", "removeSpan");
}
@Override
public void setSpan (Object what, int start, int end, int flags) {
Log.d("Editable", "setSpan");
}
@Override
public int getSpanEnd (Object tag) {
Log.d("Editable", "getSpanEnd");
return 0;
}
@Override
public int getSpanFlags (Object tag) {
Log.d("Editable", "getSpanFlags");
return 0;
}
@Override
public int getSpanStart (Object tag) {
Log.d("Editable", "getSpanStart");
return 0;
}
@Override
public <T> T[] getSpans (int arg0, int arg1, Class<T> arg2) {
Log.d("Editable", "getSpans");
return null;
}
@Override
public int nextSpanTransition (int start, int limit, Class type) {
Log.d("Editable", "nextSpanTransition");
return 0;
}
@Override
public Editable append (CharSequence text) {
Log.d("Editable", "append: " + text);
return this;
}
@Override
public Editable append (char text) {
Log.d("Editable", "append: " + text);
return this;
}
@Override
public Editable append (CharSequence text, int start, int end) {
Log.d("Editable", "append: " + text);
return this;
}
@Override
public void clear () {
Log.d("Editable", "clear");
}
@Override
public void clearSpans () {
Log.d("Editable", "clearSpanes");
}
@Override
public Editable delete (int st, int en) {
Log.d("Editable", "delete, " + st + ", " + en);
return this;
}
@Override
public InputFilter[] getFilters () {
Log.d("Editable", "getFilters");
return new InputFilter[0];
}
@Override
public Editable insert (int where, CharSequence text) {
Log.d("Editable", "insert: " + text);
return this;
}
@Override
public Editable insert (int where, CharSequence text, int start, int end) {
Log.d("Editable", "insert: " + text);
return this;
}
@Override
public Editable replace (int st, int en, CharSequence text) {
Log.d("Editable", "replace: " + text);
return this;
}
@Override
public Editable replace (int st, int en, CharSequence source, int start, int end) {
Log.d("Editable", "replace: " + source);
return this;
}
@Override
public void setFilters (InputFilter[] filters) {
Log.d("Editable", "setFilters");
}
}
@Override
public boolean onTouch (View view, MotionEvent e) {
return false;
}
@Override
public boolean onKey (View view, int keycode, KeyEvent e) {
return false;
}
}