1 /* 2 * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game 3 * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com> 4 * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com> 5 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 */ 21 22 package org.hedgewars.hedgeroid; 23 24 import java.io.IOException; 25 import java.io.UnsupportedEncodingException; 26 import java.net.ConnectException; 27 28 import javax.microedition.khronos.egl.EGL10; 29 import javax.microedition.khronos.egl.EGLConfig; 30 import javax.microedition.khronos.egl.EGLContext; 31 import javax.microedition.khronos.egl.EGLDisplay; 32 import javax.microedition.khronos.egl.EGLSurface; 33 34 import org.hedgewars.hedgeroid.Datastructures.GameConfig; 35 import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; 36 import org.hedgewars.hedgeroid.netplay.Netplay; 37 import org.hedgewars.hedgeroid.util.FileUtils; 38 39 import android.app.Activity; 40 import android.content.Context; 41 import android.graphics.Canvas; 42 import android.graphics.PixelFormat; 43 import android.hardware.Sensor; 44 import android.hardware.SensorEvent; 45 import android.hardware.SensorEventListener; 46 import android.hardware.SensorManager; 47 import android.media.AudioFormat; 48 import android.media.AudioManager; 49 import android.media.AudioTrack; 50 import android.os.Bundle; 51 import android.util.Base64; 52 import android.util.DisplayMetrics; 53 import android.util.Log; 54 import android.view.KeyEvent; 55 import android.view.MotionEvent; 56 import android.view.SurfaceHolder; 57 import android.view.SurfaceView; 58 import android.view.View; 59 60 61 /** 62 SDL Activity 63 */ 64 public class SDLActivity extends Activity { 65 /** 66 * Set startConfig to the desired config when starting this activity. This avoids having to parcel all 67 * the config objects into the Intent. Not particularly elegant, but it's actually a recommended 68 * way to do this (http://developer.android.com/guide/faq/framework.html#3) 69 */ 70 public static volatile GameConfig startConfig; 71 public static volatile boolean startNetgame; 72 73 // Main components 74 public static SDLActivity mSingleton; 75 private static SDLSurface mSurface; 76 private static Thread mSDLThread; 77 78 // Audio 79 private static Thread mAudioThread; 80 private static AudioTrack mAudioTrack; 81 82 // EGL private objects 83 private static EGLContext mEGLContext; 84 private static EGLSurface mEGLSurface; 85 private static EGLDisplay mEGLDisplay; 86 private static EGLConfig mEGLConfig; 87 private static int mGLMajor, mGLMinor; 88 89 // Load the .so 90 static { 91 System.loadLibrary("SDL"); 92 System.loadLibrary("main"); 93 } 94 95 // Setup onCreate(Bundle savedInstanceState)96 protected void onCreate(Bundle savedInstanceState) { 97 super.onCreate(savedInstanceState); 98 99 // So we can call stuff from static callbacks 100 mSingleton = this; 101 102 // Set up the surface 103 mSurface = new SDLSurface(getApplication(), startConfig, startNetgame); 104 startConfig = null; 105 setContentView(mSurface); 106 } 107 108 // Events onPause()109 protected void onPause() { 110 Log.v("SDL", "onPause()"); 111 super.onPause(); 112 113 if(mEGLDisplay != null && mEGLContext != null){ 114 EGL10 egl = (EGL10)EGLContext.getEGL(); 115 egl.eglDestroyContext(mEGLDisplay, mEGLContext); 116 mEGLDisplay = null; 117 mEGLContext = null; 118 } 119 120 SDLActivity.nativePause(); 121 } 122 onResume()123 protected void onResume() { 124 Log.v("SDL", "onResume()"); 125 super.onResume(); 126 } 127 onDestroy()128 protected void onDestroy() { 129 super.onDestroy(); 130 Log.v("SDL", "onDestroy()"); 131 // Send a quit message to the application 132 SDLActivity.nativeQuit(); 133 // Now wait for the SDL thread to quit 134 if (mSDLThread != null) { 135 try { 136 mSDLThread.join(); 137 } catch(Exception e) { 138 Log.w("SDL", "Problem stopping thread: " + e); 139 } 140 mSDLThread = null; 141 } 142 mSingleton = null; 143 } 144 synchronizedNativeInit(String...args)145 public static void synchronizedNativeInit(String...args) { 146 synchronized(PascalExports.engineMutex) { 147 nativeInit(args); 148 } 149 } 150 151 // C functions we call nativeInit(String...args)152 private static native void nativeInit(String...args); nativeQuit()153 public static native void nativeQuit(); nativePause()154 public static native void nativePause(); nativeResume()155 public static native void nativeResume(); onNativeResize(int x, int y, int format)156 public static native void onNativeResize(int x, int y, int format); onNativeKeyDown(int keycode)157 public static native void onNativeKeyDown(int keycode); onNativeKeyUp(int keycode)158 public static native void onNativeKeyUp(int keycode); onNativeTouch(int touchDevId, int pointerFingerId, int action, float x, float y, float p)159 public static native void onNativeTouch(int touchDevId, int pointerFingerId, 160 int action, float x, 161 float y, float p); onNativeAccel(float x, float y, float z)162 public static native void onNativeAccel(float x, float y, float z); nativeRunAudioThread()163 public static native void nativeRunAudioThread(); 164 165 166 // Java functions called from C 167 createGLContext(int majorVersion, int minorVersion)168 public static boolean createGLContext(int majorVersion, int minorVersion) { 169 return initEGL(majorVersion, minorVersion); 170 } 171 flipBuffers()172 public static void flipBuffers() { 173 flipEGL(); 174 } 175 setActivityTitle(final String title)176 public static void setActivityTitle(final String title) { 177 // Called from SDLMain() thread and can't directly affect the view 178 mSingleton.runOnUiThread(new Runnable() { 179 public void run() { 180 mSingleton.setTitle(title); 181 } 182 }); 183 } 184 getContext()185 public static Context getContext() { 186 return mSingleton; 187 } 188 startApp(final int width, final int height, GameConfig config, boolean netgame)189 public static void startApp(final int width, final int height, GameConfig config, boolean netgame) { 190 // Start up the C app thread 191 if (mSDLThread == null) { 192 mSDLThread = new Thread(new SDLMain(width, height, config, netgame)); 193 mSDLThread.start(); 194 } else { 195 SDLActivity.nativeResume(); 196 } 197 } 198 199 // EGL functions initEGL(int majorVersion, int minorVersion)200 public static boolean initEGL(int majorVersion, int minorVersion) { 201 if (SDLActivity.mEGLDisplay == null) { 202 try { 203 EGL10 egl = (EGL10)EGLContext.getEGL(); 204 205 EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 206 207 int[] version = new int[2]; 208 egl.eglInitialize(dpy, version); 209 210 int EGL_OPENGL_ES_BIT = 1; 211 int EGL_OPENGL_ES2_BIT = 4; 212 int renderableType = 0; 213 if (majorVersion == 2) { 214 renderableType = EGL_OPENGL_ES2_BIT; 215 } else if (majorVersion == 1) { 216 renderableType = EGL_OPENGL_ES_BIT; 217 } 218 int[] configSpec = { 219 EGL10.EGL_RENDERABLE_TYPE, renderableType, 220 EGL10.EGL_NONE 221 }; 222 EGLConfig[] configs = new EGLConfig[1]; 223 int[] num_config = new int[1]; 224 if (!egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config) || num_config[0] == 0) { 225 Log.e("SDL", "No EGL config available"); 226 return false; 227 } 228 EGLConfig config = configs[0]; 229 230 SDLActivity.mEGLDisplay = dpy; 231 SDLActivity.mEGLConfig = config; 232 SDLActivity.mGLMajor = majorVersion; 233 SDLActivity.mGLMinor = minorVersion; 234 235 SDLActivity.createEGLSurface(); 236 } catch(Exception e) { 237 Log.v("SDL", e + ""); 238 for (StackTraceElement s : e.getStackTrace()) { 239 Log.v("SDL", s.toString()); 240 } 241 } 242 } 243 else SDLActivity.createEGLSurface(); 244 245 return true; 246 } 247 createEGLContext()248 public static boolean createEGLContext() { 249 EGL10 egl = (EGL10)EGLContext.getEGL(); 250 int EGL_CONTEXT_CLIENT_VERSION=0x3098; 251 int contextAttrs[] = new int[] { EGL_CONTEXT_CLIENT_VERSION, SDLActivity.mGLMajor, EGL10.EGL_NONE }; 252 SDLActivity.mEGLContext = egl.eglCreateContext(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, EGL10.EGL_NO_CONTEXT, contextAttrs); 253 if (SDLActivity.mEGLContext == EGL10.EGL_NO_CONTEXT) { 254 Log.e("SDL", "Couldn't create context"); 255 return false; 256 } 257 return true; 258 } 259 createEGLSurface()260 public static boolean createEGLSurface() { 261 if (SDLActivity.mEGLDisplay != null && SDLActivity.mEGLConfig != null) { 262 EGL10 egl = (EGL10)EGLContext.getEGL(); 263 if (SDLActivity.mEGLContext == null) createEGLContext(); 264 265 Log.v("SDL", "Creating new EGL Surface"); 266 EGLSurface surface = egl.eglCreateWindowSurface(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, SDLActivity.mSurface, null); 267 if (surface == EGL10.EGL_NO_SURFACE) { 268 Log.e("SDL", "Couldn't create surface"); 269 return false; 270 } 271 272 if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) { 273 Log.e("SDL", "Old EGL Context doesnt work, trying with a new one"); 274 createEGLContext(); 275 if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) { 276 Log.e("SDL", "Failed making EGL Context current"); 277 return false; 278 } 279 } 280 SDLActivity.mEGLSurface = surface; 281 return true; 282 } 283 return false; 284 } 285 286 // EGL buffer flip flipEGL()287 public static void flipEGL() { 288 try { 289 EGL10 egl = (EGL10)EGLContext.getEGL(); 290 291 egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, null); 292 293 // drawing here 294 295 egl.eglWaitGL(); 296 297 egl.eglSwapBuffers(SDLActivity.mEGLDisplay, SDLActivity.mEGLSurface); 298 299 300 } catch(Exception e) { 301 Log.v("SDL", "flipEGL(): " + e); 302 for (StackTraceElement s : e.getStackTrace()) { 303 Log.v("SDL", s.toString()); 304 } 305 } 306 } 307 308 // Audio 309 private static Object buf; 310 audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames)311 public static Object audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { 312 int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; 313 int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; 314 int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); 315 316 Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); 317 318 // Let the user pick a larger buffer if they really want -- but ye 319 // gods they probably shouldn't, the minimums are horrifyingly high 320 // latency already 321 desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); 322 323 mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, 324 channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); 325 326 audioStartThread(); 327 328 Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); 329 330 if (is16Bit) { 331 buf = new short[desiredFrames * (isStereo ? 2 : 1)]; 332 } else { 333 buf = new byte[desiredFrames * (isStereo ? 2 : 1)]; 334 } 335 return buf; 336 } 337 audioStartThread()338 public static void audioStartThread() { 339 mAudioThread = new Thread(new Runnable() { 340 public void run() { 341 mAudioTrack.play(); 342 nativeRunAudioThread(); 343 } 344 }); 345 346 // I'd take REALTIME if I could get it! 347 mAudioThread.setPriority(Thread.MAX_PRIORITY); 348 mAudioThread.start(); 349 } 350 audioWriteShortBuffer(short[] buffer)351 public static void audioWriteShortBuffer(short[] buffer) { 352 for (int i = 0; i < buffer.length; ) { 353 int result = mAudioTrack.write(buffer, i, buffer.length - i); 354 if (result > 0) { 355 i += result; 356 } else if (result == 0) { 357 try { 358 Thread.sleep(1); 359 } catch(InterruptedException e) { 360 // Nom nom 361 } 362 } else { 363 Log.w("SDL", "SDL audio: error return from write(short)"); 364 return; 365 } 366 } 367 } 368 audioWriteByteBuffer(byte[] buffer)369 public static void audioWriteByteBuffer(byte[] buffer) { 370 for (int i = 0; i < buffer.length; ) { 371 int result = mAudioTrack.write(buffer, i, buffer.length - i); 372 if (result > 0) { 373 i += result; 374 } else if (result == 0) { 375 try { 376 Thread.sleep(1); 377 } catch(InterruptedException e) { 378 // Nom nom 379 } 380 } else { 381 Log.w("SDL", "SDL audio: error return from write(short)"); 382 return; 383 } 384 } 385 } 386 audioQuit()387 public static void audioQuit() { 388 if (mAudioThread != null) { 389 try { 390 mAudioThread.join(); 391 } catch(Exception e) { 392 Log.v("SDL", "Problem stopping audio thread: " + e); 393 } 394 mAudioThread = null; 395 396 //Log.v("SDL", "Finished waiting for audio thread"); 397 } 398 399 if (mAudioTrack != null) { 400 mAudioTrack.stop(); 401 mAudioTrack = null; 402 } 403 } 404 getDensity()405 public static int getDensity(){ 406 DisplayMetrics dm = SDLActivity.getContext().getResources().getDisplayMetrics(); 407 return dm.densityDpi; 408 } 409 } 410 411 /** 412 Simple nativeInit() runnable 413 */ 414 class SDLMain implements Runnable { 415 public static final String TAG = "SDLMain"; 416 417 public static final int RQ_LOWRES = 0x00000001; // use half land array 418 public static final int RQ_BLURRY_LAND = 0x00000002; // downscaled terrain 419 public static final int RQ_NO_BACKGROUND = 0x00000004; // don't draw background 420 public static final int RQ_SIMPLE_ROPE = 0x00000008; // avoid drawing rope 421 public static final int RQ_2D_WATER = 0x00000010; // disabe 3D water effect 422 public static final int RQ_SIMPLE_EXPLOSIONS = 0x00000020; // no fancy explosion effects 423 public static final int RQ_NO_FLAKES = 0x00000040; // no flakes 424 public static final int RQ_NO_MENU_ANIM = 0x00000080; // ammomenu appears with no animation 425 public static final int RQ_NO_DROPLETS = 0x00000100; // no droplets 426 public static final int RQ_NO_CLAMPING = 0x00000200; // don't clamp textures 427 public static final int RQ_NO_TOOLTIPS = 0x00000400; // tooltips are not drawn 428 public static final int RQ_NO_VSYNC = 0x00000800; // don't sync on vblank 429 430 private final int surfaceWidth, surfaceHeight; 431 private final String playerName; 432 private final GameConfig config; 433 private final boolean netgame; 434 SDLMain(int width, int height, GameConfig config, boolean netgame)435 public SDLMain(int width, int height, GameConfig config, boolean netgame) { 436 surfaceWidth = width; 437 surfaceHeight = height; 438 if(netgame) { 439 playerName = Netplay.getAppInstance(SDLActivity.getContext().getApplicationContext()).getPlayerName(); 440 } else { 441 playerName = "Player"; 442 } 443 this.config = config; 444 this.netgame = netgame; 445 } 446 run()447 public void run() { 448 //Set up the IPC socket server to communicate with the engine 449 GameConnection gameConn; 450 String path; 451 try { 452 if(netgame) { 453 Netplay netplay = Netplay.getAppInstance(SDLActivity.mSingleton.getApplicationContext()); 454 gameConn = GameConnection.forNetgame(config, netplay); 455 } else { 456 gameConn = GameConnection.forLocalGame(config); 457 } 458 459 path = FileUtils.getDataPathFile(SDLActivity.mSingleton).getAbsolutePath(); 460 Log.d(TAG, "Starting engine"); 461 // Runs SDL_main() with added parameters 462 try { 463 String pPort = String.valueOf(gameConn.port); 464 String pWidth = String.valueOf(surfaceWidth); 465 String pHeight = String.valueOf(surfaceHeight); 466 String pQuality = Integer.toString(RQ_NO_FLAKES|RQ_NO_DROPLETS|RQ_SIMPLE_EXPLOSIONS); 467 String pPlayerName = Base64.encodeToString(playerName.getBytes("UTF-8"), 0); 468 SDLActivity.synchronizedNativeInit(new String[] { pPort, pWidth, pHeight, pQuality, "en.txt", pPlayerName, "1", "1", "1", path, "" }); 469 } catch (UnsupportedEncodingException e) { 470 throw new AssertionError(e); // never happens 471 } 472 Log.d(TAG, "Engine stopped"); 473 } catch(ConnectException e) { 474 Log.e(TAG, "Error starting IPC connection"); 475 } catch (IOException e) { 476 Log.e(TAG, "Missing SDCard"); 477 } 478 SDLActivity.mSingleton.runOnUiThread(new Runnable() { public void run() { 479 if(SDLActivity.mSingleton != null) { 480 SDLActivity.mSingleton.finish(); 481 } 482 }}); 483 } 484 } 485 486 487 /** 488 SDLSurface. This is what we draw on, so we need to know when it's created 489 in order to do anything useful. 490 491 Because of this, that's where we set up the SDL thread 492 */ 493 class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, 494 View.OnKeyListener, View.OnTouchListener, SensorEventListener { 495 496 private GameConfig config; 497 private boolean netgame; 498 499 // Sensors 500 private static SensorManager mSensorManager; 501 502 // Startup SDLSurface(Context context, GameConfig _config, boolean netgame)503 public SDLSurface(Context context, GameConfig _config, boolean netgame) { 504 super(context); 505 getHolder().addCallback(this); 506 507 setFocusable(true); 508 setFocusableInTouchMode(true); 509 requestFocus(); 510 setOnKeyListener(this); 511 setOnTouchListener(this); 512 513 mSensorManager = (SensorManager)context.getSystemService("sensor"); 514 config = _config; 515 this.netgame = netgame; 516 } 517 518 // Called when we have a valid drawing surface surfaceCreated(SurfaceHolder holder)519 public void surfaceCreated(SurfaceHolder holder) { 520 Log.v("SDL", "surfaceCreated()"); 521 holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); 522 SDLActivity.createEGLSurface(); 523 // enableSensor(Sensor.TYPE_ACCELEROMETER, true); 524 } 525 526 // Called when we lose the surface surfaceDestroyed(SurfaceHolder holder)527 public void surfaceDestroyed(SurfaceHolder holder) { 528 Log.v("SDL", "surfaceDestroyed()"); 529 SDLActivity.nativePause(); 530 // enableSensor(Sensor.TYPE_ACCELEROMETER, false); 531 } 532 533 // Called when the surface is resized surfaceChanged(SurfaceHolder holder, int format, int width, int height)534 public void surfaceChanged(SurfaceHolder holder, 535 int format, int width, int height) { 536 Log.v("SDL", "surfaceChanged()"); 537 538 int sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 by default 539 switch (format) { 540 case PixelFormat.A_8: 541 Log.v("SDL", "pixel format A_8"); 542 break; 543 case PixelFormat.LA_88: 544 Log.v("SDL", "pixel format LA_88"); 545 break; 546 case PixelFormat.L_8: 547 Log.v("SDL", "pixel format L_8"); 548 break; 549 case PixelFormat.RGBA_4444: 550 Log.v("SDL", "pixel format RGBA_4444"); 551 sdlFormat = 0x85421002; // SDL_PIXELFORMAT_RGBA4444 552 break; 553 case PixelFormat.RGBA_5551: 554 Log.v("SDL", "pixel format RGBA_5551"); 555 sdlFormat = 0x85441002; // SDL_PIXELFORMAT_RGBA5551 556 break; 557 case PixelFormat.RGBA_8888: 558 Log.v("SDL", "pixel format RGBA_8888"); 559 sdlFormat = 0x86462004; // SDL_PIXELFORMAT_RGBA8888 560 break; 561 case PixelFormat.RGBX_8888: 562 Log.v("SDL", "pixel format RGBX_8888"); 563 sdlFormat = 0x86262004; // SDL_PIXELFORMAT_RGBX8888 564 break; 565 case PixelFormat.RGB_332: 566 Log.v("SDL", "pixel format RGB_332"); 567 sdlFormat = 0x84110801; // SDL_PIXELFORMAT_RGB332 568 break; 569 case PixelFormat.RGB_565: 570 Log.v("SDL", "pixel format RGB_565"); 571 sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 572 break; 573 case PixelFormat.RGB_888: 574 Log.v("SDL", "pixel format RGB_888"); 575 // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? 576 sdlFormat = 0x86161804; // SDL_PIXELFORMAT_RGB888 577 break; 578 default: 579 Log.v("SDL", "pixel format unknown " + format); 580 break; 581 } 582 SDLActivity.onNativeResize(width, height, sdlFormat); 583 Log.v("SDL", "Window size:" + width + "x"+height); 584 585 SDLActivity.startApp(width, height, config, netgame); 586 } 587 588 // unused onDraw(Canvas canvas)589 public void onDraw(Canvas canvas) {} 590 591 592 593 594 // Key events onKey(View v, int keyCode, KeyEvent event)595 public boolean onKey(View v, int keyCode, KeyEvent event) { 596 switch(keyCode){ 597 case KeyEvent.KEYCODE_BACK: 598 Log.d("SDL", "KEYCODE_BACK"); 599 SDLActivity.nativeQuit(); 600 return true; 601 case KeyEvent.KEYCODE_VOLUME_DOWN: 602 case KeyEvent.KEYCODE_VOLUME_UP: 603 case KeyEvent.KEYCODE_VOLUME_MUTE: 604 return false; 605 } 606 if (event.getAction() == KeyEvent.ACTION_DOWN) { 607 //Log.v("SDL", "key down: " + keyCode); 608 SDLActivity.onNativeKeyDown(keyCode); 609 return true; 610 } 611 else if (event.getAction() == KeyEvent.ACTION_UP) { 612 //Log.v("SDL", "key up: " + keyCode); 613 SDLActivity.onNativeKeyUp(keyCode); 614 return true; 615 } 616 617 return false; 618 } 619 620 // Touch events onTouch(View v, MotionEvent event)621 public boolean onTouch(View v, MotionEvent event) { 622 final int action = event.getAction() & MotionEvent.ACTION_MASK; 623 final int actionPointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 624 625 if (action == MotionEvent.ACTION_MOVE) { 626 // TODO send motion to every pointer if its position has 627 // changed since prev event. 628 for (int i = 0; i < event.getPointerCount(); i++) { 629 sendNativeTouch(event, action, i); 630 } 631 } else { 632 sendNativeTouch(event, action, actionPointerIndex); 633 } 634 return true; 635 } 636 sendNativeTouch(MotionEvent event, int action, int pointerIndex)637 private static void sendNativeTouch(MotionEvent event, int action, int pointerIndex) { 638 int touchDevId = event.getDeviceId(); 639 int pointerFingerId = event.getPointerId(pointerIndex); 640 float x = event.getX(pointerIndex); 641 float y = event.getY(pointerIndex); 642 float pressure = event.getPressure(pointerIndex); 643 SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, pressure); 644 } 645 646 // Sensor events enableSensor(int sensortype, boolean enabled)647 public void enableSensor(int sensortype, boolean enabled) { 648 // TODO: This uses getDefaultSensor - what if we have >1 accels? 649 if (enabled) { 650 mSensorManager.registerListener(this, 651 mSensorManager.getDefaultSensor(sensortype), 652 SensorManager.SENSOR_DELAY_GAME, null); 653 } else { 654 mSensorManager.unregisterListener(this, 655 mSensorManager.getDefaultSensor(sensortype)); 656 } 657 } 658 onAccuracyChanged(Sensor sensor, int accuracy)659 public void onAccuracyChanged(Sensor sensor, int accuracy) { 660 // TODO 661 } 662 onSensorChanged(SensorEvent event)663 public void onSensorChanged(SensorEvent event) { 664 if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { 665 SDLActivity.onNativeAccel(event.values[0] / SensorManager.GRAVITY_EARTH, 666 event.values[1] / SensorManager.GRAVITY_EARTH, 667 event.values[2] / SensorManager.GRAVITY_EARTH); 668 } 669 } 670 } 671 672