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 java.util.ArrayList; 20 import java.util.Arrays; 21 22 import android.app.Activity; 23 import android.app.AlertDialog; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.DialogInterface.OnCancelListener; 27 import android.hardware.Sensor; 28 import android.hardware.SensorEvent; 29 import android.hardware.SensorEventListener; 30 import android.hardware.SensorManager; 31 import android.os.Build; 32 import android.os.Handler; 33 import android.os.Vibrator; 34 import android.service.wallpaper.WallpaperService.Engine; 35 import android.view.MotionEvent; 36 import android.view.Surface; 37 import android.view.View; 38 import android.view.WindowManager; 39 import android.view.View.OnKeyListener; 40 import android.view.View.OnTouchListener; 41 import android.view.inputmethod.InputMethodManager; 42 import android.widget.EditText; 43 44 import com.badlogic.gdx.Application; 45 import com.badlogic.gdx.Gdx; 46 import com.badlogic.gdx.Graphics.DisplayMode; 47 import com.badlogic.gdx.Input; 48 import com.badlogic.gdx.Input.TextInputListener; 49 import com.badlogic.gdx.InputProcessor; 50 import com.badlogic.gdx.backends.android.AndroidLiveWallpaperService.AndroidWallpaperEngine; 51 import com.badlogic.gdx.utils.IntSet; 52 import com.badlogic.gdx.utils.Pool; 53 54 /** An implementation of the {@link Input} interface for Android. 55 * 56 * @author mzechner */ 57 /** @author jshapcot */ 58 public class AndroidInput implements Input, OnKeyListener, OnTouchListener { 59 static class KeyEvent { 60 static final int KEY_DOWN = 0; 61 static final int KEY_UP = 1; 62 static final int KEY_TYPED = 2; 63 64 long timeStamp; 65 int type; 66 int keyCode; 67 char keyChar; 68 } 69 70 static class TouchEvent { 71 static final int TOUCH_DOWN = 0; 72 static final int TOUCH_UP = 1; 73 static final int TOUCH_DRAGGED = 2; 74 static final int TOUCH_SCROLLED = 3; 75 static final int TOUCH_MOVED = 4; 76 77 long timeStamp; 78 int type; 79 int x; 80 int y; 81 int scrollAmount; 82 int button; 83 int pointer; 84 } 85 86 Pool<KeyEvent> usedKeyEvents = new Pool<KeyEvent>(16, 1000) { 87 protected KeyEvent newObject () { 88 return new KeyEvent(); 89 } 90 }; 91 92 Pool<TouchEvent> usedTouchEvents = new Pool<TouchEvent>(16, 1000) { 93 protected TouchEvent newObject () { 94 return new TouchEvent(); 95 } 96 }; 97 98 public static final int NUM_TOUCHES = 20; 99 public static final int SUPPORTED_KEYS = 260; 100 101 ArrayList<OnKeyListener> keyListeners = new ArrayList(); 102 ArrayList<KeyEvent> keyEvents = new ArrayList(); 103 ArrayList<TouchEvent> touchEvents = new ArrayList(); 104 int[] touchX = new int[NUM_TOUCHES]; 105 int[] touchY = new int[NUM_TOUCHES]; 106 int[] deltaX = new int[NUM_TOUCHES]; 107 int[] deltaY = new int[NUM_TOUCHES]; 108 boolean[] touched = new boolean[NUM_TOUCHES]; 109 int[] button = new int[NUM_TOUCHES]; 110 int[] realId = new int[NUM_TOUCHES]; 111 final boolean hasMultitouch; 112 private int keyCount = 0; 113 private boolean[] keys = new boolean[SUPPORTED_KEYS]; 114 private boolean keyJustPressed = false; 115 private boolean[] justPressedKeys = new boolean[SUPPORTED_KEYS]; 116 private SensorManager manager; 117 public boolean accelerometerAvailable = false; 118 private final float[] accelerometerValues = new float[3]; 119 public boolean gyroscopeAvailable = false; 120 private final float[] gyroscopeValues = new float[3]; 121 private String text = null; 122 private TextInputListener textListener = null; 123 private Handler handle; 124 final Application app; 125 final Context context; 126 private final AndroidTouchHandler touchHandler; 127 private int sleepTime = 0; 128 private boolean catchBack = false; 129 private boolean catchMenu = false; 130 protected final Vibrator vibrator; 131 private boolean compassAvailable = false; 132 boolean keyboardAvailable; 133 private final float[] magneticFieldValues = new float[3]; 134 private float azimuth = 0; 135 private float pitch = 0; 136 private float roll = 0; 137 private float inclination = 0; 138 private boolean justTouched = false; 139 private InputProcessor processor; 140 private final AndroidApplicationConfiguration config; 141 private final Orientation nativeOrientation; 142 private long currentEventTimeStamp = System.nanoTime(); 143 private final AndroidOnscreenKeyboard onscreenKeyboard; 144 145 private SensorEventListener accelerometerListener; 146 private SensorEventListener gyroscopeListener; 147 private SensorEventListener compassListener; 148 149 public AndroidInput (Application activity, Context context, Object view, AndroidApplicationConfiguration config) { 150 // we hook into View, for LWPs we call onTouch below directly from 151 // within the AndroidLivewallpaperEngine#onTouchEvent() method. 152 if (view instanceof View) { 153 View v = (View)view; 154 v.setOnKeyListener(this); 155 v.setOnTouchListener(this); 156 v.setFocusable(true); 157 v.setFocusableInTouchMode(true); 158 v.requestFocus(); 159 } 160 this.config = config; 161 this.onscreenKeyboard = new AndroidOnscreenKeyboard(context, new Handler(), this); 162 163 for (int i = 0; i < realId.length; i++) 164 realId[i] = -1; 165 handle = new Handler(); 166 this.app = activity; 167 this.context = context; 168 this.sleepTime = config.touchSleepTime; 169 touchHandler = new AndroidMultiTouchHandler(); 170 hasMultitouch = touchHandler.supportsMultitouch(context); 171 172 vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); 173 174 int rotation = getRotation(); 175 DisplayMode mode = app.getGraphics().getDisplayMode(); 176 if (((rotation == 0 || rotation == 180) && (mode.width >= mode.height)) 177 || ((rotation == 90 || rotation == 270) && (mode.width <= mode.height))) { 178 nativeOrientation = Orientation.Landscape; 179 } else { 180 nativeOrientation = Orientation.Portrait; 181 } 182 } 183 184 @Override 185 public float getAccelerometerX () { 186 return accelerometerValues[0]; 187 } 188 189 @Override 190 public float getAccelerometerY () { 191 return accelerometerValues[1]; 192 } 193 194 @Override 195 public float getAccelerometerZ () { 196 return accelerometerValues[2]; 197 } 198 199 @Override 200 public float getGyroscopeX () { 201 return gyroscopeValues[0]; 202 } 203 204 @Override 205 public float getGyroscopeY () { 206 return gyroscopeValues[1]; 207 } 208 209 @Override 210 public float getGyroscopeZ () { 211 return gyroscopeValues[2]; 212 } 213 214 @Override 215 public void getTextInput (final TextInputListener listener, final String title, final String text, final String hint) { 216 handle.post(new Runnable() { 217 public void run () { 218 AlertDialog.Builder alert = new AlertDialog.Builder(context); 219 alert.setTitle(title); 220 final EditText input = new EditText(context); 221 input.setHint(hint); 222 input.setText(text); 223 input.setSingleLine(); 224 alert.setView(input); 225 alert.setPositiveButton(context.getString(android.R.string.ok), new DialogInterface.OnClickListener() { 226 public void onClick (DialogInterface dialog, int whichButton) { 227 Gdx.app.postRunnable(new Runnable() { 228 @Override 229 public void run () { 230 listener.input(input.getText().toString()); 231 } 232 }); 233 } 234 }); 235 alert.setNegativeButton(context.getString(android.R.string.cancel), new DialogInterface.OnClickListener() { 236 public void onClick (DialogInterface dialog, int whichButton) { 237 Gdx.app.postRunnable(new Runnable() { 238 @Override 239 public void run () { 240 listener.canceled(); 241 } 242 }); 243 } 244 }); 245 alert.setOnCancelListener(new OnCancelListener() { 246 @Override 247 public void onCancel (DialogInterface arg0) { 248 Gdx.app.postRunnable(new Runnable() { 249 @Override 250 public void run () { 251 listener.canceled(); 252 } 253 }); 254 } 255 }); 256 alert.show(); 257 } 258 }); 259 } 260 261 @Override 262 public int getX () { 263 synchronized (this) { 264 return touchX[0]; 265 } 266 } 267 268 @Override 269 public int getY () { 270 synchronized (this) { 271 return touchY[0]; 272 } 273 } 274 275 @Override 276 public int getX (int pointer) { 277 synchronized (this) { 278 return touchX[pointer]; 279 } 280 } 281 282 @Override 283 public int getY (int pointer) { 284 synchronized (this) { 285 return touchY[pointer]; 286 } 287 } 288 289 public boolean isTouched (int pointer) { 290 synchronized (this) { 291 return touched[pointer]; 292 } 293 } 294 295 @Override 296 public synchronized boolean isKeyPressed (int key) { 297 if (key == Input.Keys.ANY_KEY) { 298 return keyCount > 0; 299 } 300 if (key < 0 || key >= SUPPORTED_KEYS) { 301 return false; 302 } 303 return keys[key]; 304 } 305 306 @Override 307 public synchronized boolean isKeyJustPressed (int key) { 308 if (key == Input.Keys.ANY_KEY) { 309 return keyJustPressed; 310 } 311 if (key < 0 || key >= SUPPORTED_KEYS) { 312 return false; 313 } 314 return justPressedKeys[key]; 315 } 316 317 @Override 318 public boolean isTouched () { 319 synchronized (this) { 320 if (hasMultitouch) { 321 for (int pointer = 0; pointer < NUM_TOUCHES; pointer++) { 322 if (touched[pointer]) { 323 return true; 324 } 325 } 326 } 327 return touched[0]; 328 } 329 } 330 331 public void setInputProcessor (InputProcessor processor) { 332 synchronized (this) { 333 this.processor = processor; 334 } 335 } 336 337 void processEvents () { 338 synchronized (this) { 339 justTouched = false; 340 if (keyJustPressed) { 341 keyJustPressed = false; 342 for (int i = 0; i < justPressedKeys.length; i++) { 343 justPressedKeys[i] = false; 344 } 345 } 346 347 if (processor != null) { 348 final InputProcessor processor = this.processor; 349 350 int len = keyEvents.size(); 351 for (int i = 0; i < len; i++) { 352 KeyEvent e = keyEvents.get(i); 353 currentEventTimeStamp = e.timeStamp; 354 switch (e.type) { 355 case KeyEvent.KEY_DOWN: 356 processor.keyDown(e.keyCode); 357 keyJustPressed = true; 358 justPressedKeys[e.keyCode] = true; 359 break; 360 case KeyEvent.KEY_UP: 361 processor.keyUp(e.keyCode); 362 break; 363 case KeyEvent.KEY_TYPED: 364 processor.keyTyped(e.keyChar); 365 } 366 usedKeyEvents.free(e); 367 } 368 369 len = touchEvents.size(); 370 for (int i = 0; i < len; i++) { 371 TouchEvent e = touchEvents.get(i); 372 currentEventTimeStamp = e.timeStamp; 373 switch (e.type) { 374 case TouchEvent.TOUCH_DOWN: 375 processor.touchDown(e.x, e.y, e.pointer, e.button); 376 justTouched = true; 377 break; 378 case TouchEvent.TOUCH_UP: 379 processor.touchUp(e.x, e.y, e.pointer, e.button); 380 break; 381 case TouchEvent.TOUCH_DRAGGED: 382 processor.touchDragged(e.x, e.y, e.pointer); 383 break; 384 case TouchEvent.TOUCH_MOVED: 385 processor.mouseMoved(e.x, e.y); 386 break; 387 case TouchEvent.TOUCH_SCROLLED: 388 processor.scrolled(e.scrollAmount); 389 } 390 usedTouchEvents.free(e); 391 } 392 } else { 393 int len = touchEvents.size(); 394 for (int i = 0; i < len; i++) { 395 TouchEvent e = touchEvents.get(i); 396 if (e.type == TouchEvent.TOUCH_DOWN) justTouched = true; 397 usedTouchEvents.free(e); 398 } 399 400 len = keyEvents.size(); 401 for (int i = 0; i < len; i++) { 402 usedKeyEvents.free(keyEvents.get(i)); 403 } 404 } 405 406 if (touchEvents.size() == 0) { 407 for (int i = 0; i < deltaX.length; i++) { 408 deltaX[0] = 0; 409 deltaY[0] = 0; 410 } 411 } 412 413 keyEvents.clear(); 414 touchEvents.clear(); 415 } 416 } 417 418 boolean requestFocus = true; 419 420 @Override 421 public boolean onTouch (View view, MotionEvent event) { 422 if (requestFocus && view != null) { 423 view.setFocusableInTouchMode(true); 424 view.requestFocus(); 425 requestFocus = false; 426 } 427 428 // synchronized in handler.postTouchEvent() 429 touchHandler.onTouch(event, this); 430 431 if (sleepTime != 0) { 432 try { 433 Thread.sleep(sleepTime); 434 } catch (InterruptedException e) { 435 } 436 } 437 return true; 438 } 439 440 /** Called in {@link AndroidLiveWallpaperService} on tap 441 * @param x 442 * @param y */ 443 public void onTap (int x, int y) { 444 postTap(x, y); 445 } 446 447 /** Called in {@link AndroidLiveWallpaperService} on drop 448 * @param x 449 * @param y */ 450 public void onDrop (int x, int y) { 451 postTap(x, y); 452 } 453 454 protected void postTap (int x, int y) { 455 synchronized (this) { 456 TouchEvent event = usedTouchEvents.obtain(); 457 event.timeStamp = System.nanoTime(); 458 event.pointer = 0; 459 event.x = x; 460 event.y = y; 461 event.type = TouchEvent.TOUCH_DOWN; 462 touchEvents.add(event); 463 464 event = usedTouchEvents.obtain(); 465 event.timeStamp = System.nanoTime(); 466 event.pointer = 0; 467 event.x = x; 468 event.y = y; 469 event.type = TouchEvent.TOUCH_UP; 470 touchEvents.add(event); 471 } 472 Gdx.app.getGraphics().requestRendering(); 473 } 474 475 @Override 476 public boolean onKey (View v, int keyCode, android.view.KeyEvent e) { 477 for (int i = 0, n = keyListeners.size(); i < n; i++) 478 if (keyListeners.get(i).onKey(v, keyCode, e)) return true; 479 480 synchronized (this) { 481 KeyEvent event = null; 482 483 if (e.getKeyCode() == android.view.KeyEvent.KEYCODE_UNKNOWN && e.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) { 484 String chars = e.getCharacters(); 485 for (int i = 0; i < chars.length(); i++) { 486 event = usedKeyEvents.obtain(); 487 event.timeStamp = System.nanoTime(); 488 event.keyCode = 0; 489 event.keyChar = chars.charAt(i); 490 event.type = KeyEvent.KEY_TYPED; 491 keyEvents.add(event); 492 } 493 return false; 494 } 495 496 char character = (char)e.getUnicodeChar(); 497 // Android doesn't report a unicode char for back space. hrm... 498 if (keyCode == 67) character = '\b'; 499 if (e.getKeyCode() < 0 || e.getKeyCode() >= SUPPORTED_KEYS) { 500 return false; 501 } 502 503 switch (e.getAction()) { 504 case android.view.KeyEvent.ACTION_DOWN: 505 event = usedKeyEvents.obtain(); 506 event.timeStamp = System.nanoTime(); 507 event.keyChar = 0; 508 event.keyCode = e.getKeyCode(); 509 event.type = KeyEvent.KEY_DOWN; 510 511 // Xperia hack for circle key. gah... 512 if (keyCode == android.view.KeyEvent.KEYCODE_BACK && e.isAltPressed()) { 513 keyCode = Keys.BUTTON_CIRCLE; 514 event.keyCode = keyCode; 515 } 516 517 keyEvents.add(event); 518 if (!keys[event.keyCode]) { 519 keyCount++; 520 keys[event.keyCode] = true; 521 } 522 break; 523 case android.view.KeyEvent.ACTION_UP: 524 long timeStamp = System.nanoTime(); 525 event = usedKeyEvents.obtain(); 526 event.timeStamp = timeStamp; 527 event.keyChar = 0; 528 event.keyCode = e.getKeyCode(); 529 event.type = KeyEvent.KEY_UP; 530 // Xperia hack for circle key. gah... 531 if (keyCode == android.view.KeyEvent.KEYCODE_BACK && e.isAltPressed()) { 532 keyCode = Keys.BUTTON_CIRCLE; 533 event.keyCode = keyCode; 534 } 535 keyEvents.add(event); 536 537 event = usedKeyEvents.obtain(); 538 event.timeStamp = timeStamp; 539 event.keyChar = character; 540 event.keyCode = 0; 541 event.type = KeyEvent.KEY_TYPED; 542 keyEvents.add(event); 543 544 if (keyCode == Keys.BUTTON_CIRCLE) { 545 if (keys[Keys.BUTTON_CIRCLE]) { 546 keyCount--; 547 keys[Keys.BUTTON_CIRCLE] = false; 548 } 549 } else { 550 if (keys[e.getKeyCode()]) { 551 keyCount--; 552 keys[e.getKeyCode()] = false; 553 } 554 } 555 } 556 app.getGraphics().requestRendering(); 557 } 558 559 // circle button on Xperia Play shouldn't need catchBack == true 560 if (keyCode == Keys.BUTTON_CIRCLE) return true; 561 if (catchBack && keyCode == android.view.KeyEvent.KEYCODE_BACK) return true; 562 if (catchMenu && keyCode == android.view.KeyEvent.KEYCODE_MENU) return true; 563 return false; 564 } 565 566 @Override 567 public void setOnscreenKeyboardVisible (final boolean visible) { 568 handle.post(new Runnable() { 569 public void run () { 570 InputMethodManager manager = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); 571 if (visible) { 572 View view = ((AndroidGraphics)app.getGraphics()).getView(); 573 view.setFocusable(true); 574 view.setFocusableInTouchMode(true); 575 manager.showSoftInput(((AndroidGraphics)app.getGraphics()).getView(), 0); 576 } else { 577 manager.hideSoftInputFromWindow(((AndroidGraphics)app.getGraphics()).getView().getWindowToken(), 0); 578 } 579 } 580 }); 581 } 582 583 @Override 584 public void setCatchBackKey (boolean catchBack) { 585 this.catchBack = catchBack; 586 } 587 588 @Override 589 public boolean isCatchBackKey() { 590 return catchBack; 591 } 592 593 @Override 594 public void setCatchMenuKey (boolean catchMenu) { 595 this.catchMenu = catchMenu; 596 } 597 598 @Override 599 public boolean isCatchMenuKey () { 600 return catchMenu; 601 } 602 603 @Override 604 public void vibrate (int milliseconds) { 605 vibrator.vibrate(milliseconds); 606 } 607 608 @Override 609 public void vibrate (long[] pattern, int repeat) { 610 vibrator.vibrate(pattern, repeat); 611 } 612 613 @Override 614 public void cancelVibrate () { 615 vibrator.cancel(); 616 } 617 618 @Override 619 public boolean justTouched () { 620 return justTouched; 621 } 622 623 @Override 624 public boolean isButtonPressed (int button) { 625 synchronized (this) { 626 if (hasMultitouch) { 627 for (int pointer = 0; pointer < NUM_TOUCHES; pointer++) { 628 if (touched[pointer] && (this.button[pointer] == button)) { 629 return true; 630 } 631 } 632 } 633 return (touched[0] && (this.button[0] == button)); 634 } 635 } 636 637 final float[] R = new float[9]; 638 final float[] orientation = new float[3]; 639 640 private void updateOrientation () { 641 if (SensorManager.getRotationMatrix(R, null, accelerometerValues, magneticFieldValues)) { 642 SensorManager.getOrientation(R, orientation); 643 azimuth = (float)Math.toDegrees(orientation[0]); 644 pitch = (float)Math.toDegrees(orientation[1]); 645 roll = (float)Math.toDegrees(orientation[2]); 646 } 647 } 648 649 /** Returns the rotation matrix describing the devices rotation as per <a href= 650 * "http://developer.android.com/reference/android/hardware/SensorManager.html#getRotationMatrix(float[], float[], float[], float[])" 651 * >SensorManager#getRotationMatrix(float[], float[], float[], float[])</a>. Does not manipulate the matrix if the platform 652 * does not have an accelerometer. 653 * @param matrix */ 654 public void getRotationMatrix (float[] matrix) { 655 SensorManager.getRotationMatrix(matrix, null, accelerometerValues, magneticFieldValues); 656 } 657 658 @Override 659 public float getAzimuth () { 660 if (!compassAvailable) return 0; 661 662 updateOrientation(); 663 return azimuth; 664 } 665 666 @Override 667 public float getPitch () { 668 if (!compassAvailable) return 0; 669 670 updateOrientation(); 671 return pitch; 672 } 673 674 @Override 675 public float getRoll () { 676 if (!compassAvailable) return 0; 677 678 updateOrientation(); 679 return roll; 680 } 681 682 void registerSensorListeners () { 683 if (config.useAccelerometer) { 684 manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); 685 if (manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() == 0) { 686 accelerometerAvailable = false; 687 } else { 688 Sensor accelerometer = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0); 689 accelerometerListener = new SensorListener(this.nativeOrientation, this.accelerometerValues, this.magneticFieldValues, this.gyroscopeValues); 690 accelerometerAvailable = manager.registerListener(accelerometerListener, accelerometer, 691 SensorManager.SENSOR_DELAY_GAME); 692 } 693 } else 694 accelerometerAvailable = false; 695 696 if (config.useGyroscope) { 697 manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); 698 if (manager.getSensorList(Sensor.TYPE_GYROSCOPE).size() == 0) { 699 gyroscopeAvailable = false; 700 } else { 701 Sensor gyroscope = manager.getSensorList(Sensor.TYPE_GYROSCOPE).get(0); 702 gyroscopeListener = new SensorListener(this.nativeOrientation, this.gyroscopeValues, this.magneticFieldValues, this.gyroscopeValues); 703 gyroscopeAvailable = manager.registerListener(gyroscopeListener, gyroscope, 704 SensorManager.SENSOR_DELAY_GAME); 705 } 706 } else 707 gyroscopeAvailable = false; 708 709 if (config.useCompass) { 710 if (manager == null) manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); 711 Sensor sensor = manager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 712 if (sensor != null) { 713 compassAvailable = accelerometerAvailable; 714 if (compassAvailable) { 715 compassListener = new SensorListener(this.nativeOrientation, this.accelerometerValues, this.magneticFieldValues, this.gyroscopeValues); 716 compassAvailable = manager.registerListener(compassListener, sensor, SensorManager.SENSOR_DELAY_GAME); 717 } 718 } else { 719 compassAvailable = false; 720 } 721 } else 722 compassAvailable = false; 723 Gdx.app.log("AndroidInput", "sensor listener setup"); 724 } 725 726 void unregisterSensorListeners () { 727 if (manager != null) { 728 if (accelerometerListener != null) { 729 manager.unregisterListener(accelerometerListener); 730 accelerometerListener = null; 731 } 732 if (gyroscopeListener != null) { 733 manager.unregisterListener(gyroscopeListener); 734 gyroscopeListener = null; 735 } 736 if (compassListener != null) { 737 manager.unregisterListener(compassListener); 738 compassListener = null; 739 } 740 manager = null; 741 } 742 Gdx.app.log("AndroidInput", "sensor listener tear down"); 743 } 744 745 @Override 746 public InputProcessor getInputProcessor () { 747 return this.processor; 748 } 749 750 @Override 751 public boolean isPeripheralAvailable (Peripheral peripheral) { 752 if (peripheral == Peripheral.Accelerometer) return accelerometerAvailable; 753 if (peripheral == Peripheral.Gyroscope) return gyroscopeAvailable; 754 if (peripheral == Peripheral.Compass) return compassAvailable; 755 if (peripheral == Peripheral.HardwareKeyboard) return keyboardAvailable; 756 if (peripheral == Peripheral.OnscreenKeyboard) return true; 757 if (peripheral == Peripheral.Vibrator) 758 return (Build.VERSION.SDK_INT >= 11 && vibrator != null) ? vibrator.hasVibrator() : vibrator != null; 759 if (peripheral == Peripheral.MultitouchScreen) return hasMultitouch; 760 return false; 761 } 762 763 public int getFreePointerIndex () { 764 int len = realId.length; 765 for (int i = 0; i < len; i++) { 766 if (realId[i] == -1) return i; 767 } 768 769 realId = resize(realId); 770 touchX = resize(touchX); 771 touchY = resize(touchY); 772 deltaX = resize(deltaX); 773 deltaY = resize(deltaY); 774 touched = resize(touched); 775 button = resize(button); 776 777 return len; 778 } 779 780 private int[] resize (int[] orig) { 781 int[] tmp = new int[orig.length + 2]; 782 System.arraycopy(orig, 0, tmp, 0, orig.length); 783 return tmp; 784 } 785 786 private boolean[] resize (boolean[] orig) { 787 boolean[] tmp = new boolean[orig.length + 2]; 788 System.arraycopy(orig, 0, tmp, 0, orig.length); 789 return tmp; 790 } 791 792 public int lookUpPointerIndex (int pointerId) { 793 int len = realId.length; 794 for (int i = 0; i < len; i++) { 795 if (realId[i] == pointerId) return i; 796 } 797 798 StringBuffer buf = new StringBuffer(); 799 for (int i = 0; i < len; i++) { 800 buf.append(i + ":" + realId[i] + " "); 801 } 802 Gdx.app.log("AndroidInput", "Pointer ID lookup failed: " + pointerId + ", " + buf.toString()); 803 return -1; 804 } 805 806 @Override 807 public int getRotation () { 808 int orientation = 0; 809 810 if (context instanceof Activity) { 811 orientation = ((Activity)context).getWindowManager().getDefaultDisplay().getRotation(); 812 } else { 813 orientation = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); 814 } 815 816 switch (orientation) { 817 case Surface.ROTATION_0: 818 return 0; 819 case Surface.ROTATION_90: 820 return 90; 821 case Surface.ROTATION_180: 822 return 180; 823 case Surface.ROTATION_270: 824 return 270; 825 default: 826 return 0; 827 } 828 } 829 830 @Override 831 public Orientation getNativeOrientation () { 832 return nativeOrientation; 833 } 834 835 @Override 836 public void setCursorCatched (boolean catched) { 837 } 838 839 @Override 840 public boolean isCursorCatched () { 841 return false; 842 } 843 844 @Override 845 public int getDeltaX () { 846 return deltaX[0]; 847 } 848 849 @Override 850 public int getDeltaX (int pointer) { 851 return deltaX[pointer]; 852 } 853 854 @Override 855 public int getDeltaY () { 856 return deltaY[0]; 857 } 858 859 @Override 860 public int getDeltaY (int pointer) { 861 return deltaY[pointer]; 862 } 863 864 @Override 865 public void setCursorPosition (int x, int y) { 866 } 867 868 @Override 869 public long getCurrentEventTime () { 870 return currentEventTimeStamp; 871 } 872 873 public void addKeyListener (OnKeyListener listener) { 874 keyListeners.add(listener); 875 } 876 877 public void onPause () { 878 unregisterSensorListeners(); 879 880 // erase pointer ids. this sucks donkeyballs... 881 Arrays.fill(realId, -1); 882 883 // erase touched state. this also sucks donkeyballs... 884 Arrays.fill(touched, false); 885 } 886 887 public void onResume () { 888 registerSensorListeners(); 889 } 890 891 /** Our implementation of SensorEventListener. Because Android doesn't like it when we register more than one Sensor to a single 892 * SensorEventListener, we add one of these for each Sensor. Could use an anonymous class, but I don't see any harm in 893 * explicitly defining it here. Correct me if I am wrong. */ 894 private class SensorListener implements SensorEventListener { 895 final float[] accelerometerValues; 896 final float[] magneticFieldValues; 897 final Orientation nativeOrientation; 898 final float[] gyroscopeValues; 899 900 SensorListener (Orientation nativeOrientation, float[] accelerometerValues, float[] magneticFieldValues, float[] gyroscopeValues) { 901 this.accelerometerValues = accelerometerValues; 902 this.magneticFieldValues = magneticFieldValues; 903 this.nativeOrientation = nativeOrientation; 904 this.gyroscopeValues = gyroscopeValues; 905 } 906 907 @Override 908 public void onAccuracyChanged (Sensor arg0, int arg1) { 909 910 } 911 912 @Override 913 public void onSensorChanged (SensorEvent event) { 914 if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { 915 if (nativeOrientation == Orientation.Portrait) { 916 System.arraycopy(event.values, 0, accelerometerValues, 0, accelerometerValues.length); 917 } else { 918 accelerometerValues[0] = event.values[1]; 919 accelerometerValues[1] = -event.values[0]; 920 accelerometerValues[2] = event.values[2]; 921 } 922 } 923 if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { 924 System.arraycopy(event.values, 0, magneticFieldValues, 0, magneticFieldValues.length); 925 } 926 if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { 927 if (nativeOrientation == Orientation.Portrait) { 928 System.arraycopy(event.values, 0, gyroscopeValues, 0, gyroscopeValues.length); 929 } else { 930 gyroscopeValues[0] = event.values[1]; 931 gyroscopeValues[1] = -event.values[0]; 932 gyroscopeValues[2] = event.values[2]; 933 } 934 } 935 } 936 } 937 } 938