1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.backends.android; 18 19 import android.app.Dialog; 20 import android.content.Context; 21 import android.os.Handler; 22 import android.text.Editable; 23 import android.text.InputFilter; 24 import android.text.method.ArrowKeyMovementMethod; 25 import android.text.method.MovementMethod; 26 import android.util.Log; 27 import android.view.Gravity; 28 import android.view.KeyEvent; 29 import android.view.MotionEvent; 30 import android.view.View; 31 import android.view.View.OnKeyListener; 32 import android.view.View.OnTouchListener; 33 import android.view.ViewGroup; 34 import android.view.ViewTreeObserver.OnPreDrawListener; 35 import android.view.Window; 36 import android.view.WindowManager; 37 import android.view.inputmethod.EditorInfo; 38 import android.view.inputmethod.InputMethodManager; 39 import android.widget.FrameLayout; 40 import android.widget.TextView; 41 42 import com.badlogic.gdx.Input.Peripheral; 43 44 /** Responsible for showing and hiding the Android onscreen keyboard (aka softkeyboard). Uses a dialog with an invisible TextView 45 * and injects key down/up and typed events into AndroidInput. Only the delete and back keys will trigger key down/up events. 46 * Alphanumeric keys will be directly injected as key typed events which is sufficient to implement things like text fields. 47 * 48 * Since the input mechanism for softkeyboards is a bit complex, we don't directly get key events from the softkeyboard. Instead 49 * we intercept calls to the Editable of the invisible TextView which we translate into delete key events and key typed events. 50 * 51 * @author mzechner */ 52 class AndroidOnscreenKeyboard implements OnKeyListener, OnTouchListener { 53 final Context context; 54 final Handler handler; 55 final AndroidInput input; 56 Dialog dialog; 57 TextView textView; 58 59 public AndroidOnscreenKeyboard (Context context, Handler handler, AndroidInput input) { 60 this.context = context; 61 this.handler = handler; 62 this.input = input; 63 } 64 65 Dialog createDialog () { 66 textView = createView(context); 67 textView.setOnKeyListener(this); 68 FrameLayout.LayoutParams textBoxLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 69 FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM); 70 textView.setLayoutParams(textBoxLayoutParams); 71 textView.setFocusable(true); 72 textView.setFocusableInTouchMode(true); 73 textView.setImeOptions(textView.getImeOptions() | EditorInfo.IME_FLAG_NO_EXTRACT_UI); 74 75 final FrameLayout layout = new FrameLayout(context); 76 ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); 77 layout.setLayoutParams(layoutParams); 78 layout.addView(textView); 79 layout.setOnTouchListener(this); 80 81 dialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar_Fullscreen); 82 dialog.setContentView(layout); 83 return dialog; 84 } 85 86 public static TextView createView (Context context) { 87 final TextView view = new TextView(context) { 88 Editable editable = new PassThroughEditable(); 89 90 @Override 91 protected boolean getDefaultEditable () { 92 return true; 93 } 94 95 @Override 96 public Editable getEditableText () { 97 return editable; 98 } 99 100 @Override 101 protected MovementMethod getDefaultMovementMethod () { 102 return ArrowKeyMovementMethod.getInstance(); 103 } 104 105 @Override 106 public boolean onKeyDown (int keyCode, KeyEvent event) { 107 Log.d("Test", "down keycode: " + event.getKeyCode()); 108 return super.onKeyDown(keyCode, event); 109 } 110 111 @Override 112 public boolean onKeyUp (int keyCode, KeyEvent event) { 113 Log.d("Test", "up keycode: " + event.getKeyCode()); 114 return super.onKeyUp(keyCode, event); 115 } 116 }; 117 // view.setCursorVisible(false); 118 return view; 119 } 120 121 public void setVisible (boolean visible) { 122 if (visible && dialog != null) { 123 dialog.dismiss(); 124 dialog = null; 125 } 126 if (visible && dialog == null && !input.isPeripheralAvailable(Peripheral.HardwareKeyboard)) { 127 handler.post(new Runnable() { 128 @Override 129 public void run () { 130 dialog = createDialog(); 131 dialog.show(); 132 133 handler.post(new Runnable() { 134 @Override 135 public void run () { 136 dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); 137 InputMethodManager input = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); 138 if (input != null) input.showSoftInput(textView, InputMethodManager.SHOW_FORCED); 139 } 140 }); 141 142 final View content = dialog.getWindow().findViewById(Window.ID_ANDROID_CONTENT); 143 content.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { 144 int[] screenloc = new int[2]; 145 private int keyboardHeight; 146 private boolean keyboardShowing; 147 148 @Override 149 public boolean onPreDraw () { 150 content.getLocationOnScreen(screenloc); 151 keyboardHeight = Math.abs(screenloc[1]); 152 if (keyboardHeight > 0) keyboardShowing = true; 153 if (keyboardHeight == 0 && keyboardShowing) { 154 dialog.dismiss(); 155 dialog = null; 156 } 157 return true; 158 } 159 }); 160 } 161 }); 162 } else { 163 if (!visible && dialog != null) { 164 dialog.dismiss(); 165 } 166 } 167 } 168 169 public static class PassThroughEditable implements Editable { 170 171 @Override 172 public char charAt (int index) { 173 Log.d("Editable", "charAt"); 174 return 0; 175 } 176 177 @Override 178 public int length () { 179 Log.d("Editable", "length"); 180 return 0; 181 } 182 183 @Override 184 public CharSequence subSequence (int start, int end) { 185 Log.d("Editable", "subSequence"); 186 return null; 187 } 188 189 @Override 190 public void getChars (int start, int end, char[] dest, int destoff) { 191 Log.d("Editable", "getChars"); 192 } 193 194 @Override 195 public void removeSpan (Object what) { 196 Log.d("Editable", "removeSpan"); 197 } 198 199 @Override 200 public void setSpan (Object what, int start, int end, int flags) { 201 Log.d("Editable", "setSpan"); 202 } 203 204 @Override 205 public int getSpanEnd (Object tag) { 206 Log.d("Editable", "getSpanEnd"); 207 return 0; 208 } 209 210 @Override 211 public int getSpanFlags (Object tag) { 212 Log.d("Editable", "getSpanFlags"); 213 return 0; 214 } 215 216 @Override 217 public int getSpanStart (Object tag) { 218 Log.d("Editable", "getSpanStart"); 219 return 0; 220 } 221 222 @Override 223 public <T> T[] getSpans (int arg0, int arg1, Class<T> arg2) { 224 Log.d("Editable", "getSpans"); 225 return null; 226 } 227 228 @Override 229 public int nextSpanTransition (int start, int limit, Class type) { 230 Log.d("Editable", "nextSpanTransition"); 231 return 0; 232 } 233 234 @Override 235 public Editable append (CharSequence text) { 236 Log.d("Editable", "append: " + text); 237 return this; 238 } 239 240 @Override 241 public Editable append (char text) { 242 Log.d("Editable", "append: " + text); 243 return this; 244 } 245 246 @Override 247 public Editable append (CharSequence text, int start, int end) { 248 Log.d("Editable", "append: " + text); 249 return this; 250 } 251 252 @Override 253 public void clear () { 254 Log.d("Editable", "clear"); 255 } 256 257 @Override 258 public void clearSpans () { 259 Log.d("Editable", "clearSpanes"); 260 } 261 262 @Override 263 public Editable delete (int st, int en) { 264 Log.d("Editable", "delete, " + st + ", " + en); 265 return this; 266 } 267 268 @Override 269 public InputFilter[] getFilters () { 270 Log.d("Editable", "getFilters"); 271 return new InputFilter[0]; 272 } 273 274 @Override 275 public Editable insert (int where, CharSequence text) { 276 Log.d("Editable", "insert: " + text); 277 return this; 278 } 279 280 @Override 281 public Editable insert (int where, CharSequence text, int start, int end) { 282 Log.d("Editable", "insert: " + text); 283 return this; 284 } 285 286 @Override 287 public Editable replace (int st, int en, CharSequence text) { 288 Log.d("Editable", "replace: " + text); 289 return this; 290 } 291 292 @Override 293 public Editable replace (int st, int en, CharSequence source, int start, int end) { 294 Log.d("Editable", "replace: " + source); 295 return this; 296 } 297 298 @Override 299 public void setFilters (InputFilter[] filters) { 300 Log.d("Editable", "setFilters"); 301 } 302 } 303 304 @Override 305 public boolean onTouch (View view, MotionEvent e) { 306 return false; 307 } 308 309 @Override 310 public boolean onKey (View view, int keycode, KeyEvent e) { 311 return false; 312 } 313 } 314