Home | History | Annotate | Download | only in android
      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