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