diff --git a/assets/License.txt b/assets/License.txt
new file mode 100644
index 00000000..c9f1a93b
--- /dev/null
+++ b/assets/License.txt
@@ -0,0 +1,4 @@
+These PNG files are copyright to Emile Belanger.
+They are permitted for use in SDLxash3D Android port, full copyright is owned by Emile Belanger.
+Other use not permitted without permission from Emile Belanger.
+emile.belanger@gmail.com
\ No newline at end of file
diff --git a/assets/arrow_down.png b/assets/arrow_down.png
new file mode 100644
index 00000000..d353daa4
Binary files /dev/null and b/assets/arrow_down.png differ
diff --git a/assets/arrow_left.png b/assets/arrow_left.png
new file mode 100644
index 00000000..4328c08c
Binary files /dev/null and b/assets/arrow_left.png differ
diff --git a/assets/arrow_right.png b/assets/arrow_right.png
new file mode 100644
index 00000000..020be062
Binary files /dev/null and b/assets/arrow_right.png differ
diff --git a/assets/arrow_up.png b/assets/arrow_up.png
new file mode 100644
index 00000000..d94cfc27
Binary files /dev/null and b/assets/arrow_up.png differ
diff --git a/assets/bstone_ps_config b/assets/bstone_ps_config
new file mode 100644
index 00000000..0005a06b
Binary files /dev/null and b/assets/bstone_ps_config differ
diff --git a/assets/crouch.png b/assets/crouch.png
new file mode 100644
index 00000000..18ad4b55
Binary files /dev/null and b/assets/crouch.png differ
diff --git a/assets/enter.png b/assets/enter.png
new file mode 100644
index 00000000..f74f327b
Binary files /dev/null and b/assets/enter.png differ
diff --git a/assets/gamma.png b/assets/gamma.png
new file mode 100644
index 00000000..7a019f68
Binary files /dev/null and b/assets/gamma.png differ
diff --git a/assets/jump.png b/assets/jump.png
new file mode 100644
index 00000000..286d683d
Binary files /dev/null and b/assets/jump.png differ
diff --git a/assets/key_+.png b/assets/key_+.png
new file mode 100644
index 00000000..0eff7e65
Binary files /dev/null and b/assets/key_+.png differ
diff --git a/assets/key_-.png b/assets/key_-.png
new file mode 100644
index 00000000..e611ec65
Binary files /dev/null and b/assets/key_-.png differ
diff --git a/assets/key_0.png b/assets/key_0.png
new file mode 100644
index 00000000..a0f6d21c
Binary files /dev/null and b/assets/key_0.png differ
diff --git a/assets/key_1.png b/assets/key_1.png
new file mode 100644
index 00000000..42f22e83
Binary files /dev/null and b/assets/key_1.png differ
diff --git a/assets/key_2.png b/assets/key_2.png
new file mode 100644
index 00000000..28005d2d
Binary files /dev/null and b/assets/key_2.png differ
diff --git a/assets/key_3.png b/assets/key_3.png
new file mode 100644
index 00000000..37eac4c5
Binary files /dev/null and b/assets/key_3.png differ
diff --git a/assets/key_4.png b/assets/key_4.png
new file mode 100644
index 00000000..9a3ee73e
Binary files /dev/null and b/assets/key_4.png differ
diff --git a/assets/key_5.png b/assets/key_5.png
new file mode 100644
index 00000000..1a8abccd
Binary files /dev/null and b/assets/key_5.png differ
diff --git a/assets/key_6.png b/assets/key_6.png
new file mode 100644
index 00000000..87d5dee1
Binary files /dev/null and b/assets/key_6.png differ
diff --git a/assets/key_7.png b/assets/key_7.png
new file mode 100644
index 00000000..06cee9e6
Binary files /dev/null and b/assets/key_7.png differ
diff --git a/assets/key_8.png b/assets/key_8.png
new file mode 100644
index 00000000..445dba34
Binary files /dev/null and b/assets/key_8.png differ
diff --git a/assets/key_9.png b/assets/key_9.png
new file mode 100644
index 00000000..bce79c2a
Binary files /dev/null and b/assets/key_9.png differ
diff --git a/assets/key_f1.png b/assets/key_f1.png
new file mode 100644
index 00000000..664ad046
Binary files /dev/null and b/assets/key_f1.png differ
diff --git a/assets/key_f10.png b/assets/key_f10.png
new file mode 100644
index 00000000..6a15825a
Binary files /dev/null and b/assets/key_f10.png differ
diff --git a/assets/keyboard.png b/assets/keyboard.png
new file mode 100644
index 00000000..130755c4
Binary files /dev/null and b/assets/keyboard.png differ
diff --git a/assets/load.png b/assets/load.png
new file mode 100644
index 00000000..95331ba9
Binary files /dev/null and b/assets/load.png differ
diff --git a/assets/look_arrow.png b/assets/look_arrow.png
new file mode 100644
index 00000000..878d054f
Binary files /dev/null and b/assets/look_arrow.png differ
diff --git a/assets/map.png b/assets/map.png
new file mode 100644
index 00000000..932fe530
Binary files /dev/null and b/assets/map.png differ
diff --git a/assets/next_weap.png b/assets/next_weap.png
new file mode 100644
index 00000000..3eb9acd2
Binary files /dev/null and b/assets/next_weap.png differ
diff --git a/assets/prev_weap.png b/assets/prev_weap.png
new file mode 100644
index 00000000..9e2fa321
Binary files /dev/null and b/assets/prev_weap.png differ
diff --git a/assets/red_cross.png b/assets/red_cross.png
new file mode 100644
index 00000000..4551068e
Binary files /dev/null and b/assets/red_cross.png differ
diff --git a/assets/save.png b/assets/save.png
new file mode 100644
index 00000000..c6d3df16
Binary files /dev/null and b/assets/save.png differ
diff --git a/assets/settings.png b/assets/settings.png
new file mode 100644
index 00000000..a66c5cd4
Binary files /dev/null and b/assets/settings.png differ
diff --git a/assets/settings_bars.png b/assets/settings_bars.png
new file mode 100644
index 00000000..463d5e4b
Binary files /dev/null and b/assets/settings_bars.png differ
diff --git a/assets/shoot.png b/assets/shoot.png
new file mode 100644
index 00000000..7ecadf2a
Binary files /dev/null and b/assets/shoot.png differ
diff --git a/assets/show_weapons.png b/assets/show_weapons.png
new file mode 100644
index 00000000..58e2ed3e
Binary files /dev/null and b/assets/show_weapons.png differ
diff --git a/assets/strafe_arrow.png b/assets/strafe_arrow.png
new file mode 100644
index 00000000..9dae7d05
Binary files /dev/null and b/assets/strafe_arrow.png differ
diff --git a/assets/use.png b/assets/use.png
new file mode 100644
index 00000000..8acffb10
Binary files /dev/null and b/assets/use.png differ
diff --git a/jni/Application.mk b/jni/Application.mk
index e481e89c..cd700236 100644
--- a/jni/Application.mk
+++ b/jni/Application.mk
@@ -1,21 +1,29 @@
-
-# Uncomment this if you're using STL in your project
-# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
-# APP_STL := stlport_static
-
-APPLICATIONMK_PATH = $(call my-dir)
-
-SDL_PATH := $(APPLICATIONMK_PATH)/src/SDL2
-
-#SDL_IMAGE_PATH := $(APPLICATIONMK_PATH)/src/SDL2_image/
-
-NANOGL_PATH := $(APPLICATIONMK_PATH)/src/NanoGL/nanogl
-
-XASH3D_PATH := $(APPLICATIONMK_PATH)/src/Xash3D/xash3d
-
-XASHXT_PATH := $(APPLICATIONMK_PATH)/src/XashXT/XashXT
-
-HLSDK_PATH := $(APPLICATIONMK_PATH)/src/HLSDK/halflife/
-
-APP_ABI := armeabi-v7a x86
-APP_MODULES := SDL2 xash menu client server NanoGL
+
+# Uncomment this if you're using STL in your project
+# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
+ APP_STL := stlport_static
+
+
+
+APPLICATIONMK_PATH = $(call my-dir)
+
+ifeq ($(EMILE),1)
+SDL_PATH := $(APPLICATIONMK_PATH)/src/SDL-mirror
+else
+SDL_PATH := $(APPLICATIONMK_PATH)/src/SDL2
+endif
+
+
+TOUCHCONTROLS_PATH := $(APPLICATIONMK_PATH)/src/MobileTouchControls
+
+
+NANOGL_PATH := $(APPLICATIONMK_PATH)/src/NanoGL/nanogl
+
+XASH3D_PATH := $(APPLICATIONMK_PATH)/src/Xash3D/xash3d
+
+XASHXT_PATH := $(APPLICATIONMK_PATH)/src/XashXT/XashXT
+
+HLSDK_PATH := $(APPLICATIONMK_PATH)/src/HLSDK/halflife/
+
+APP_ABI := armeabi-v7a x86
+APP_MODULES := SDL2 xash menu client server NanoGL
diff --git a/jni/src/HLSDK/halflife b/jni/src/HLSDK/halflife
deleted file mode 120000
index eb8bb8da..00000000
--- a/jni/src/HLSDK/halflife
+++ /dev/null
@@ -1 +0,0 @@
-/home/a1ba/projects/Xash3D/halflife
\ No newline at end of file
diff --git a/jni/src/HLSDK/halflife b/jni/src/HLSDK/halflife
new file mode 160000
index 00000000..c3b365fe
--- /dev/null
+++ b/jni/src/HLSDK/halflife
@@ -0,0 +1 @@
+Subproject commit c3b365fe0831e68199bb39e9f565ca724c7cc430
diff --git a/jni/src/NanoGL/nanogl b/jni/src/NanoGL/nanogl
deleted file mode 120000
index 4476145c..00000000
--- a/jni/src/NanoGL/nanogl
+++ /dev/null
@@ -1 +0,0 @@
-/home/a1ba/projects/Xash3D/nanogl
\ No newline at end of file
diff --git a/jni/src/NanoGL/nanogl b/jni/src/NanoGL/nanogl
new file mode 160000
index 00000000..63045ea1
--- /dev/null
+++ b/jni/src/NanoGL/nanogl
@@ -0,0 +1 @@
+Subproject commit 63045ea16759232df455140efd8d5ed67986882d
diff --git a/jni/src/Xash3D/xash3d b/jni/src/Xash3D/xash3d
deleted file mode 120000
index 0750ec38..00000000
--- a/jni/src/Xash3D/xash3d
+++ /dev/null
@@ -1 +0,0 @@
-/home/a1ba/projects/Xash3D/xash3d
\ No newline at end of file
diff --git a/jni/src/Xash3D/xash3d b/jni/src/Xash3D/xash3d
new file mode 160000
index 00000000..215d7a35
--- /dev/null
+++ b/jni/src/Xash3D/xash3d
@@ -0,0 +1 @@
+Subproject commit 215d7a35d99850dcbc38d37fb8fe9b4166fefcf3
diff --git a/jni/src/XashXT/XashXT b/jni/src/XashXT/XashXT
deleted file mode 120000
index ed3bdbe2..00000000
--- a/jni/src/XashXT/XashXT
+++ /dev/null
@@ -1 +0,0 @@
-/home/a1ba/projects/Xash3D/XashXT
\ No newline at end of file
diff --git a/jni/src/XashXT/XashXT b/jni/src/XashXT/XashXT
new file mode 160000
index 00000000..33130cad
--- /dev/null
+++ b/jni/src/XashXT/XashXT
@@ -0,0 +1 @@
+Subproject commit 33130cade1af08c7ed12479f0e0ecab489851972
diff --git a/libs/com.bda.controller.jar b/libs/com.bda.controller.jar
new file mode 100644
index 00000000..f71dbece
Binary files /dev/null and b/libs/com.bda.controller.jar differ
diff --git a/res/drawable/add.png b/res/drawable/add.png
new file mode 100644
index 00000000..3c5531d3
Binary files /dev/null and b/res/drawable/add.png differ
diff --git a/res/drawable/cog.png b/res/drawable/cog.png
new file mode 100644
index 00000000..18fe5183
Binary files /dev/null and b/res/drawable/cog.png differ
diff --git a/res/drawable/gamepad.png b/res/drawable/gamepad.png
new file mode 100644
index 00000000..9812a7ff
Binary files /dev/null and b/res/drawable/gamepad.png differ
diff --git a/res/drawable/gamepad_menu.png b/res/drawable/gamepad_menu.png
new file mode 100644
index 00000000..7943353e
Binary files /dev/null and b/res/drawable/gamepad_menu.png differ
diff --git a/res/drawable/help.png b/res/drawable/help.png
new file mode 100644
index 00000000..104a2c85
Binary files /dev/null and b/res/drawable/help.png differ
diff --git a/res/drawable/joystick.png b/res/drawable/joystick.png
new file mode 100644
index 00000000..cbfe03af
Binary files /dev/null and b/res/drawable/joystick.png differ
diff --git a/res/drawable/layout_sel_background.xml b/res/drawable/layout_sel_background.xml
new file mode 100644
index 00000000..a50c6766
--- /dev/null
+++ b/res/drawable/layout_sel_background.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/controls_listview_item.xml b/res/layout/controls_listview_item.xml
new file mode 100644
index 00000000..2bbead9d
--- /dev/null
+++ b/res/layout/controls_listview_item.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/edit_controls_listview_item.xml b/res/layout/edit_controls_listview_item.xml
new file mode 100644
index 00000000..0317d239
--- /dev/null
+++ b/res/layout/edit_controls_listview_item.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/fragment_gamepad.xml b/res/layout/fragment_gamepad.xml
new file mode 100644
index 00000000..d4ba93c2
--- /dev/null
+++ b/res/layout/fragment_gamepad.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/touch_controls_settings.xml b/res/layout/touch_controls_settings.xml
new file mode 100644
index 00000000..23ef5bf7
--- /dev/null
+++ b/res/layout/touch_controls_settings.xml
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8a8dde5d..8e943570 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1,4 +1,12 @@
Half-life
+
+
+ - None
+ - Shoot
+ - Jump
+ - Use
+
+
diff --git a/src/com/beloko/games/hl/Game.java b/src/com/beloko/games/hl/Game.java
new file mode 100644
index 00000000..cb717c72
--- /dev/null
+++ b/src/com/beloko/games/hl/Game.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2009 jeyries@yahoo.fr
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.beloko.games.hl;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import org.libsdl.app.SDLActivity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.bda.controller.Controller;
+import com.bda.controller.ControllerListener;
+import com.bda.controller.StateEvent;
+import com.beloko.idtech.AppSettings;
+import com.beloko.idtech.BestEglChooser;
+import com.beloko.idtech.CDAudioPlayer;
+import com.beloko.idtech.MyGLSurfaceView;
+import com.beloko.idtech.Utils;
+import com.beloko.libsdl.SDLLib;
+import com.beloko.touchcontrols.FPSLimit;
+import com.beloko.touchcontrols.ControlInterpreter;
+import com.beloko.touchcontrols.Settings;
+import com.beloko.touchcontrols.QuakeCustomCommands;
+import com.beloko.touchcontrols.TouchControlsSettings;
+import com.beloko.touchcontrols.Settings.IDGame;
+
+public class Game extends Activity
+implements Handler.Callback
+{
+ String LOG = "Quake2";
+
+ private ControlInterpreter controlInterp;
+
+ private final MogaControllerListener mogaListener = new MogaControllerListener();
+ Controller mogaController = null;
+
+ private String args;
+ private String gamePath;
+
+ private GameView mGLSurfaceView = null;
+ private QuakeRenderer mRenderer = new QuakeRenderer();
+ Activity act;
+
+ int surfaceWidth,surfaceHeight;
+
+ int useGL;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ act = this;
+
+ AppSettings.setGame(IDGame.Wolf3d);
+ AppSettings.reloadSettings(getApplication());
+
+ args = getIntent().getStringExtra("args");
+ gamePath = getIntent().getStringExtra("game_path");
+ useGL = getIntent().getIntExtra("use_gl", 0);
+
+ handlerUI = new Handler(this);
+
+ mogaController = Controller.getInstance(this);
+ mogaController.init();
+ mogaController.setListener(mogaListener,new Handler());
+
+ //Log.i( "Quake2", "version : " + getVersion());
+
+ // fullscreen
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ // keep screen on
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+
+
+ start_quake2();
+
+ }
+
+
+ /// Handler for asynchronous message
+ /// => showDialog
+
+ private Handler handlerUI ;
+
+ public static final int MSG_SHOW_DIALOG = 1;
+
+
+ // implements Handler.Callback
+ @Override
+ public boolean handleMessage(Message msg) {
+
+ Log.i( "Quake2", String.format("handleMessage %d %d", msg.what, msg.arg1));
+
+ switch( msg.what ){
+
+ case MSG_SHOW_DIALOG:
+ showDialog(msg.arg1);
+ break;
+
+ }
+
+ return true;
+
+ }
+
+
+ /////////////////////////////
+
+
+
+
+ public void start_quake2() {
+
+ NativeLib.loadLibraries(useGL == 1);
+
+
+ NativeLib engine = new NativeLib();
+
+ NativeLib.loadLibraries(false);
+
+ controlInterp = new ControlInterpreter(engine,Settings.IDGame.Doom,AppSettings.gamePadControlsFile,AppSettings.gamePadEnabled);
+
+ TouchControlsSettings.setup(act, engine);
+ TouchControlsSettings.loadSettings(act);
+ TouchControlsSettings.sendToQuake();
+
+ QuakeCustomCommands.setup(act, engine,getIntent().getStringExtra("main_qc"),getIntent().getStringExtra("mod_qc"));
+
+
+ // Create our Preview view and set it as the content of our
+ // Activity
+ mGLSurfaceView = new GameView(this);
+
+ NativeLib.gv = mGLSurfaceView;
+
+ //if (renderType == NativeLib.REND_SOFT) //SDL software mode uses gles2
+ if (useGL == 1)
+ mGLSurfaceView.setEGLContextClientVersion(2); // enable OpenGL 2.0
+
+
+
+ //mGLSurfaceView.setGLWrapper( new MyWrapper());
+ //mGLSurfaceView.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR | GLSurfaceView.DEBUG_LOG_GL_CALLS);
+ //setEGLConfigChooser (int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize)
+ //mGLSurfaceView.setEGLConfigChooser(8,8,8,0,16,0);
+ mGLSurfaceView.setEGLConfigChooser( new BestEglChooser(getApplicationContext()) );
+
+ mGLSurfaceView.setRenderer(mRenderer);
+
+ // This will keep the screen on, while your view is visible.
+ mGLSurfaceView.setKeepScreenOn(true);
+
+ setContentView(mGLSurfaceView);
+ mGLSurfaceView.requestFocus();
+ mGLSurfaceView.setFocusableInTouchMode(true);
+
+ }
+
+
+
+ @Override
+ protected void onPause() {
+ Log.i( "Quake2.java", "onPause" );
+ CDAudioPlayer.onPause();
+ SDLLib.onPause();
+ mogaController.onPause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+
+ Log.i( "Quake2.java", "onResume" );
+ CDAudioPlayer.onResume();
+ SDLLib.onResume();
+ mogaController.onResume();
+
+ super.onResume();
+ mGLSurfaceView.onResume();
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.i( "Quake2.java", "onDestroy" );
+ super.onDestroy();
+ mogaController.exit();
+ System.exit(0);
+ }
+
+ class MogaControllerListener implements ControllerListener {
+
+
+ @Override
+ public void onKeyEvent(com.bda.controller.KeyEvent event) {
+ //Log.d(LOG,"onKeyEvent " + event.getKeyCode());
+ controlInterp.onMogaKeyEvent(event,mogaController.getState(Controller.STATE_CURRENT_PRODUCT_VERSION));
+ }
+
+ @Override
+ public void onMotionEvent(com.bda.controller.MotionEvent event) {
+ // TODO Auto-generated method stub
+ Log.d(LOG,"onGenericMotionEvent " + event.toString());
+ controlInterp.onGenericMotionEvent(event);
+ }
+
+ @Override
+ public void onStateEvent(StateEvent event) {
+ Log.d(LOG,"onStateEvent " + event.getState());
+ }
+ }
+
+ class GameView extends MyGLSurfaceView {
+
+ /*--------------------
+ * Event handling
+ *--------------------*/
+
+
+ public GameView(Context context) {
+ super(context);
+
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return controlInterp.onGenericMotionEvent(event);
+ }
+ @Override
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ return controlInterp.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ return controlInterp.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event)
+ {
+ return controlInterp.onKeyUp(keyCode, event);
+ }
+
+ } // end of QuakeView
+
+
+
+
+ ///////////// GLSurfaceView.Renderer implementation ///////////
+
+ class QuakeRenderer implements MyGLSurfaceView.Renderer {
+
+
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ Log.d("Renderer", "onSurfaceCreated");
+ }
+
+
+ private void init( int width, int height ){
+
+ Log.i( "Quake2", "screen size : " + width + "x"+ height);
+
+ NativeLib.setScreenSize(width,height);
+
+ Utils.copyPNGAssets(getApplicationContext(),AppSettings.graphicsDir);
+
+ if (useGL == 0)
+ args = "--ren soft";
+
+ String[] args_array = Utils.creatArgs(args);
+
+ int ret = NativeLib.init(AppSettings.graphicsDir,64,args_array,useGL,gamePath);
+
+ Log.i("Quake2", "Quake2Init done");
+
+ }
+
+
+
+ //// new Renderer interface
+ int notifiedflags;
+
+ FPSLimit fpsLimit;
+
+ boolean inited = false;
+
+ public void onDrawFrame(GL10 gl) {
+
+ if (!inited)
+ {
+ SDLActivity.nativeInit();
+
+ AppSettings.setIntOption(getApplicationContext(), "max_fps", 0);
+
+ inited = true;
+ init( surfaceWidth, surfaceHeight );
+ }
+ }
+
+
+
+
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+
+
+ Log.d("Renderer", String.format("onSurfaceChanged %dx%d", width,height) );
+
+ // SDLLib.nativeInit(false);
+ // SDLLib.surfaceChanged(PixelFormat.RGBA_8888, width, height);
+
+ SDLActivity.onNativeResize(width, height,PixelFormat.RGBA_8888);
+
+ //SDLLib.surfaceChanged(PixelFormat.RGBA_8888, 320, 240);
+
+
+ controlInterp.setScreenSize(width, height);
+
+ surfaceWidth = width;
+ surfaceHeight = height;
+
+ }
+
+
+ } // end of QuakeRenderer
+
+
+}
+
+
diff --git a/src/com/beloko/games/hl/NativeLib.java b/src/com/beloko/games/hl/NativeLib.java
new file mode 100644
index 00000000..8ad46e56
--- /dev/null
+++ b/src/com/beloko/games/hl/NativeLib.java
@@ -0,0 +1,81 @@
+package com.beloko.games.hl;
+
+import com.beloko.touchcontrols.ControlInterface;
+
+public class NativeLib implements ControlInterface{
+
+
+ public static native int init(String graphics_dir,int mem,String[] args,int game,String path);
+ public static native int initTouchControls(String graphics_dir,int width,int height);
+
+ public static native void setScreenSize( int width, int height );
+
+ public static native boolean touchEvent( int action, int pid, float x, float y);
+ public static native void keypress(int down, int qkey, int unicode);
+ public static native void doAction(int state, int action);
+ public static native void analogFwd(float v);
+ public static native void analogSide(float v);
+ public static native void analogPitch(int mode,float v);
+ public static native void analogYaw(int mode,float v);
+ public static native void setTouchSettings(float alpha,float strafe,float fwd,float pitch,float yaw,int other);
+
+ public static native void quickCommand(String command);
+
+ @Override
+ public void initTouchControls_if(String pngPath,int width,int height) {
+ initTouchControls(pngPath,width,height);
+ }
+
+ @Override
+ public void quickCommand_if(String command){
+ quickCommand(command);
+ }
+
+ @Override
+ public boolean touchEvent_if(int action, int pid, float x, float y) {
+ return touchEvent( action, pid, x, y);
+ }
+ @Override
+ public void keyPress_if(int down, int qkey, int unicode) {
+ keypress(down,qkey,unicode);
+ }
+
+ @Override
+ public void doAction_if(int state, int action) {
+ doAction(state,action);
+ }
+
+ @Override
+ public void analogFwd_if(float v) {
+ analogFwd(v);
+ }
+ @Override
+ public void analogSide_if(float v) {
+ analogSide(v);
+ }
+ @Override
+ public void analogPitch_if(int mode,float v)
+ {
+ analogPitch(mode,v);
+ }
+ @Override
+ public void analogYaw_if(int mode,float v)
+ {
+ analogYaw(mode,v);
+ }
+
+ @Override
+ public void setTouchSettings_if(float alpha,float strafe, float fwd, float pitch,
+ float yaw, int other) {
+ setTouchSettings(alpha,strafe, fwd, pitch, yaw, other);
+
+ }
+
+ @Override
+ public int mapKey(int acode, int unicode) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+
+}
diff --git a/src/com/beloko/touchcontrols/ActionInput.java b/src/com/beloko/touchcontrols/ActionInput.java
new file mode 100644
index 00000000..075839a0
--- /dev/null
+++ b/src/com/beloko/touchcontrols/ActionInput.java
@@ -0,0 +1,37 @@
+package com.beloko.touchcontrols;
+
+import java.io.Serializable;
+
+import com.beloko.touchcontrols.ControlConfig.Type;
+
+
+public class ActionInput implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ public String tag;
+ public String description;
+ public boolean invert;
+ public float scale = 1; //senstivty for analog
+
+ public int source = -1;
+ public Type sourceType;
+ public boolean sourcePositive=true; //Used when using analog as a button
+
+ public int actionCode;
+ public Type actionType;
+
+ public ActionInput(String t,String n,int action,Type actiontype)
+ {
+ tag = t;
+ description = n;
+ actionCode = action;
+ actionType = actiontype;
+ }
+
+ public String toString()
+ {
+ return description + ":" + sourceType.toString() + " source: " + source + " sourcePositive: " + sourcePositive;
+ }
+}
+
diff --git a/src/com/beloko/touchcontrols/ControlConfig.java b/src/com/beloko/touchcontrols/ControlConfig.java
new file mode 100644
index 00000000..f9f31100
--- /dev/null
+++ b/src/com/beloko/touchcontrols/ControlConfig.java
@@ -0,0 +1,699 @@
+package com.beloko.touchcontrols;
+
+import in.celest.xash3d.hl.R;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.beloko.touchcontrols.Settings.IDGame;
+
+public class ControlConfig implements Serializable{
+
+
+ final String LOG = "QuakeControlConfig";
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ enum Type {ANALOG,BUTTON,MENU};
+
+ public static final int LOOK_MODE_MOUSE =0;
+ public static final int LOOK_MODE_ABSOLUTE =1;
+ public static final int LOOK_MODE_JOYSTICK =2;
+
+ public static final int ACTION_ANALOG_FWD = 0x100;
+ public static final int ACTION_ANALOG_STRAFE = 0x101;
+ public static final int ACTION_ANALOG_PITCH = 0x102;
+ public static final int ACTION_ANALOG_YAW = 0x103;
+
+ public static final int PORT_ACT_LEFT =1;
+ public static final int PORT_ACT_RIGHT =2;
+ public static final int PORT_ACT_FWD =3;
+ public static final int PORT_ACT_BACK =4;
+ public static final int PORT_ACT_LOOK_UP =5;
+ public static final int PORT_ACT_LOOK_DOWN =6;
+ public static final int PORT_ACT_MOVE_LEFT =7;
+ public static final int PORT_ACT_MOVE_RIGHT =8;
+ public static final int PORT_ACT_STRAFE =9;
+ public static final int PORT_ACT_SPEED =10;
+ public static final int PORT_ACT_USE =11;
+ public static final int PORT_ACT_JUMP =12;
+ public static final int PORT_ACT_ATTACK =13;
+ public static final int PORT_ACT_UP =14;
+ public static final int PORT_ACT_DOWN =15;
+
+ public static final int PORT_ACT_NEXT_WEP =16;
+ public static final int PORT_ACT_PREV_WEP =17;
+
+ //Quake 2
+ public static final int PORT_ACT_INVEN = 18;
+ public static final int PORT_ACT_INVUSE = 19;
+ public static final int PORT_ACT_INVDROP = 20;
+ public static final int PORT_ACT_INVPREV = 21;
+ public static final int PORT_ACT_INVNEXT = 22;
+ public static final int PORT_ACT_HELPCOMP = 23;
+
+ //Doom
+ public static final int PORT_ACT_MAP = 30;
+ public static final int PORT_ACT_MAP_UP = 31;
+ public static final int PORT_ACT_MAP_DOWN = 32;
+ public static final int PORT_ACT_MAP_LEFT = 33;
+ public static final int PORT_ACT_MAP_RIGHT = 34;
+ public static final int PORT_ACT_MAP_ZOOM_IN = 35;
+ public static final int PORT_ACT_MAP_ZOOM_OUT = 36;
+
+ //RTCW
+ public static final int PORT_ACT_ZOOM_IN = 50;
+ public static final int PORT_ACT_ALT_FIRE = 51;
+ public static final int PORT_ACT_RELOAD = 52;
+ public static final int PORT_ACT_QUICKSAVE = 53;
+ public static final int PORT_ACT_QUICKLOAD = 54;
+ public static final int PORT_ACT_KICK = 56;
+ public static final int PORT_ACT_LEAN_LEFT = 57;
+ public static final int PORT_ACT_LEAN_RIGHT = 58;
+
+ //JK2
+ //public static final int PORT_ACT_FORCE_LIGHTNING = 60;
+ //public static final int PORT_ACT_SABER_BLOCK = 62;
+ //public static final int PORT_ACT_FORCE_GRIP = 63;
+ public static final int PORT_ACT_ALT_ATTACK = 64;
+ public static final int PORT_ACT_NEXT_FORCE = 65;
+ public static final int PORT_ACT_PREV_FORCE = 66;
+ public static final int PORT_ACT_FORCE_USE = 67;
+ public static final int PORT_ACT_DATAPAD = 68;
+ public static final int PORT_ACT_FORCE_SELECT = 69;
+ public static final int PORT_ACT_WEAPON_SELECT = 70;
+ public static final int PORT_ACT_SABER_STYLE = 71;
+ public static final int PORT_ACT_FORCE_PULL = 75;
+ public static final int PORT_ACT_FORCE_MIND = 76;
+ public static final int PORT_ACT_FORCE_LIGHT = 77;
+ public static final int PORT_ACT_FORCE_HEAL = 78;
+ public static final int PORT_ACT_FORCE_GRIP = 79;
+ public static final int PORT_ACT_FORCE_SPEED = 80;
+ public static final int PORT_ACT_FORCE_PUSH = 81;
+ public static final int PORT_ACT_SABER_SEL = 87; //Just chooses weapon 1 so show/hide saber.
+
+ //Choloate
+ public static final int PORT_ACT_GAMMA = 90;
+ public static final int PORT_ACT_SHOW_WEAPONS = 91;
+ public static final int PORT_ACT_SHOW_KEYS = 92;
+ public static final int PORT_ACT_FLY_UP = 93;
+ public static final int PORT_ACT_FLY_DOWN = 94;
+
+ //Custom
+ public static final int PORT_ACT_CUSTOM_0 = 150;
+ public static final int PORT_ACT_CUSTOM_1 = 151;
+ public static final int PORT_ACT_CUSTOM_2 = 152;
+ public static final int PORT_ACT_CUSTOM_3 = 153;
+ public static final int PORT_ACT_CUSTOM_4 = 154;
+ public static final int PORT_ACT_CUSTOM_5 = 155;
+ public static final int PORT_ACT_CUSTOM_6 = 156;
+ public static final int PORT_ACT_CUSTOM_7 = 157;
+
+
+
+ //Menu
+ public static final int MENU_UP = 0x200;
+ public static final int MENU_DOWN = 0x201;
+ public static final int MENU_LEFT = 0x202;
+ public static final int MENU_RIGHT = 0x203;
+ public static final int MENU_SELECT = 0x204;
+ public static final int MENU_BACK = 0x205;
+
+
+ Context ctx;
+ TextView infoTextView;
+
+ String filename;
+
+ boolean ignoreDirectionFromJoystick;
+
+ public ControlConfig(String file,IDGame game)
+ {
+ actions.add(new ActionInput("analog_move_fwd","Forward/Back",ACTION_ANALOG_FWD,Type.ANALOG));
+ actions.add(new ActionInput("analog_move_strafe","Strafe",ACTION_ANALOG_STRAFE,Type.ANALOG));
+
+
+ if (game != IDGame.Wolf3d)
+ actions.add(new ActionInput("analog_look_pitch","Look Up/Look Down",ACTION_ANALOG_PITCH,Type.ANALOG));
+
+ actions.add(new ActionInput("analog_look_yaw","Look Left/Look Right",ACTION_ANALOG_YAW,Type.ANALOG));
+
+ actions.add(new ActionInput("attack","Attack",PORT_ACT_ATTACK,Type.BUTTON));
+
+ if ((game == IDGame.Doom) || (game == IDGame.Wolf3d)|| (game == IDGame.Hexen)|| (game == IDGame.Strife)|| (game == IDGame.Heretic))
+ actions.add(new ActionInput("use","Use/Open",PORT_ACT_USE,Type.BUTTON));
+
+ if (game == IDGame.RTCW)
+ {
+ actions.add(new ActionInput("use","Use/Open",PORT_ACT_USE,Type.BUTTON));
+ actions.add(new ActionInput("reload","Reload",PORT_ACT_RELOAD,Type.BUTTON));
+ actions.add(new ActionInput("alt_fire","Alt Weapon",PORT_ACT_ALT_FIRE,Type.BUTTON));
+ actions.add(new ActionInput("binocular","Binocuar",PORT_ACT_ZOOM_IN,Type.BUTTON));
+ actions.add(new ActionInput("quick_kick","Kick",PORT_ACT_KICK,Type.BUTTON));
+ actions.add(new ActionInput("lean_left","Lean Left",PORT_ACT_LEAN_LEFT,Type.BUTTON));
+ actions.add(new ActionInput("lean_right","Lean Right",PORT_ACT_LEAN_RIGHT,Type.BUTTON));
+ }
+
+ if (game == IDGame.Quake3)
+ {
+ actions.add(new ActionInput("zoomin","Zoom in/out",PORT_ACT_ZOOM_IN,Type.BUTTON));
+ actions.add(new ActionInput("custom_0","Custom F1",PORT_ACT_CUSTOM_0,Type.BUTTON));
+ actions.add(new ActionInput("custom_1","Custom F2",PORT_ACT_CUSTOM_1,Type.BUTTON));
+ actions.add(new ActionInput("custom_2","Custom F3",PORT_ACT_CUSTOM_2,Type.BUTTON));
+ actions.add(new ActionInput("custom_3","Custom F4",PORT_ACT_CUSTOM_3,Type.BUTTON));
+ }
+
+ if ((game == IDGame.JK2) || (game == IDGame.JK3))
+ {
+ actions.add(new ActionInput("attack_alt","Alt Attack",PORT_ACT_ALT_ATTACK,Type.BUTTON));
+ actions.add(new ActionInput("use_force","Use Force",PORT_ACT_FORCE_USE,Type.BUTTON));
+ actions.add(new ActionInput("saber_style","Saber Style",PORT_ACT_SABER_STYLE,Type.BUTTON));
+ actions.add(new ActionInput("saber_show_hide","Saber Sheath/Unsheath",PORT_ACT_SABER_SEL,Type.BUTTON));
+ actions.add(new ActionInput("use","Use/Open",PORT_ACT_USE,Type.BUTTON));
+ }
+
+ if ((game != IDGame.Doom) && (game != IDGame.Wolf3d))
+ actions.add(new ActionInput("jump","Jump",PORT_ACT_JUMP,Type.BUTTON));
+
+ if ((game == IDGame.Quake2) || (game == IDGame.Quake3)|| (game == IDGame.Hexen2)|| (game == IDGame.RTCW)|| (game == IDGame.JK2) || (game == IDGame.JK3))
+ actions.add(new ActionInput("crouch","Crouch",PORT_ACT_DOWN,Type.BUTTON));
+
+ //Add GZDoom specific actions
+ if (game == IDGame.Doom)
+ {
+ actions.add(new ActionInput("attack_alt","Alt Attack (GZ)",PORT_ACT_ALT_ATTACK,Type.BUTTON));
+ actions.add(new ActionInput("jump","Jump (GZ)",PORT_ACT_JUMP,Type.BUTTON));
+ actions.add(new ActionInput("crouch","Crouch (GZ)",PORT_ACT_DOWN,Type.BUTTON));
+ actions.add(new ActionInput("custom_0","Custom A (GZ)",PORT_ACT_CUSTOM_0,Type.BUTTON));
+ actions.add(new ActionInput("custom_1","Custom B (GZ)",PORT_ACT_CUSTOM_1,Type.BUTTON));
+ actions.add(new ActionInput("custom_2","Custom C (GZ)",PORT_ACT_CUSTOM_2,Type.BUTTON));
+ actions.add(new ActionInput("custom_3","Custom D (GZ)",PORT_ACT_CUSTOM_3,Type.BUTTON));
+ actions.add(new ActionInput("custom_4","Custom E (GZ)",PORT_ACT_CUSTOM_4,Type.BUTTON));
+ actions.add(new ActionInput("custom_5","Custom F (GZ)",PORT_ACT_CUSTOM_5,Type.BUTTON));
+ actions.add(new ActionInput("quick_save","Quick Save (GZ)",PORT_ACT_QUICKSAVE,Type.BUTTON));
+ actions.add(new ActionInput("quick_load","Quick Load (GZ)",PORT_ACT_QUICKLOAD,Type.BUTTON));
+
+
+ }
+
+ actions.add(new ActionInput("fwd","Move Forward",PORT_ACT_FWD,Type.BUTTON));
+ actions.add(new ActionInput("back","Move Backwards",PORT_ACT_BACK,Type.BUTTON));
+ actions.add(new ActionInput("left","Strafe Left",PORT_ACT_MOVE_LEFT,Type.BUTTON));
+ actions.add(new ActionInput("right","Strafe Right",PORT_ACT_MOVE_RIGHT,Type.BUTTON));
+
+ if ((game != IDGame.Doom) && (game != IDGame.Wolf3d))
+ {
+ actions.add(new ActionInput("look_up","Look Up",PORT_ACT_LOOK_UP,Type.BUTTON));
+ actions.add(new ActionInput("look_down","Look Down",PORT_ACT_LOOK_DOWN,Type.BUTTON));
+ }
+
+ actions.add(new ActionInput("look_left","Look Left",PORT_ACT_LEFT,Type.BUTTON));
+ actions.add(new ActionInput("look_right","Look Right",PORT_ACT_RIGHT,Type.BUTTON));
+
+ if ((game != IDGame.Wolf3d) && (game != IDGame.JK2) || (game != IDGame.JK3))
+ {
+ actions.add(new ActionInput("strafe_on","Strafe On",PORT_ACT_STRAFE,Type.BUTTON));
+ actions.add(new ActionInput("speed","Run On",PORT_ACT_SPEED,Type.BUTTON));
+ }
+ actions.add(new ActionInput("next_weapon","Next Weapon",PORT_ACT_NEXT_WEP,Type.BUTTON));
+ actions.add(new ActionInput("prev_weapon","Previous Weapon",PORT_ACT_PREV_WEP,Type.BUTTON));
+
+ if ((game == IDGame.JK2)|| (game == IDGame.JK3))
+ {
+ actions.add(new ActionInput("next_force","Next Force",PORT_ACT_NEXT_FORCE,Type.BUTTON));
+ actions.add(new ActionInput("prev_force","Previous Force",PORT_ACT_PREV_FORCE,Type.BUTTON));
+ actions.add(new ActionInput("force_pull","Force Pull",PORT_ACT_FORCE_PULL,Type.BUTTON));
+ actions.add(new ActionInput("force_push","Force Push",PORT_ACT_FORCE_PUSH,Type.BUTTON));
+ actions.add(new ActionInput("force_speed","Force Speed",PORT_ACT_FORCE_SPEED,Type.BUTTON));
+ actions.add(new ActionInput("force_heal","Force Heal",PORT_ACT_FORCE_HEAL,Type.BUTTON));
+ actions.add(new ActionInput("force_mind","Force Mind",PORT_ACT_FORCE_MIND,Type.BUTTON));
+ actions.add(new ActionInput("force_grip","Force Grip",PORT_ACT_FORCE_GRIP,Type.BUTTON));
+ actions.add(new ActionInput("force_lightning","Force Lightning",PORT_ACT_FORCE_LIGHT,Type.BUTTON));
+
+ }
+
+ if ((game == IDGame.Quake2) || (game == IDGame.Hexen2)|| (game == IDGame.RTCW))
+ {
+ actions.add(new ActionInput("help_comp","Show Objectives",PORT_ACT_HELPCOMP,Type.BUTTON));
+ actions.add(new ActionInput("inv_show","Show Inventory",PORT_ACT_INVEN,Type.BUTTON));
+ actions.add(new ActionInput("inv_use","Use Item",PORT_ACT_INVUSE,Type.BUTTON));
+ actions.add(new ActionInput("inv_next","Next Item",PORT_ACT_INVNEXT,Type.BUTTON));
+ actions.add(new ActionInput("inv_prev","Prev Item",PORT_ACT_INVPREV,Type.BUTTON));
+ }
+
+ if (game == IDGame.JK2)
+ {
+ actions.add(new ActionInput("help_comp","Show Data Pad",PORT_ACT_DATAPAD,Type.BUTTON));
+ actions.add(new ActionInput("inv_use","Use Item",PORT_ACT_INVUSE,Type.BUTTON));
+ actions.add(new ActionInput("inv_next","Next Item",PORT_ACT_INVNEXT,Type.BUTTON));
+ actions.add(new ActionInput("inv_prev","Prev Item",PORT_ACT_INVPREV,Type.BUTTON));
+ }
+
+ if (game == IDGame.Hexen)
+ {
+ actions.add(new ActionInput("inv_use","Use Item",PORT_ACT_INVUSE,Type.BUTTON));
+ actions.add(new ActionInput("inv_next","Next Item",PORT_ACT_INVNEXT,Type.BUTTON));
+ actions.add(new ActionInput("inv_prev","Prev Item",PORT_ACT_INVPREV,Type.BUTTON));
+ actions.add(new ActionInput("fly_up","Fly Up",PORT_ACT_FLY_UP,Type.BUTTON));
+ actions.add(new ActionInput("fly_down","Fly Down",PORT_ACT_FLY_DOWN,Type.BUTTON));
+
+ }
+
+ if (game == IDGame.Strife)
+ {
+ actions.add(new ActionInput("inv_use","Use Item",PORT_ACT_INVUSE,Type.BUTTON));
+ actions.add(new ActionInput("inv_drop","Drop Item",PORT_ACT_INVDROP,Type.BUTTON));
+ actions.add(new ActionInput("inv_next","Next Item",PORT_ACT_INVNEXT,Type.BUTTON));
+ actions.add(new ActionInput("inv_prev","Prev Item",PORT_ACT_INVPREV,Type.BUTTON));
+ actions.add(new ActionInput("show_weap","Show Stats/Weapons",PORT_ACT_SHOW_WEAPONS,Type.BUTTON));
+ actions.add(new ActionInput("show_keys","Show Keys",PORT_ACT_SHOW_KEYS,Type.BUTTON));
+
+ }
+
+ if (game == IDGame.Heretic)
+ {
+ actions.add(new ActionInput("inv_use","Use Item",PORT_ACT_INVUSE,Type.BUTTON));
+ actions.add(new ActionInput("inv_next","Next Item",PORT_ACT_INVNEXT,Type.BUTTON));
+ actions.add(new ActionInput("inv_prev","Prev Item",PORT_ACT_INVPREV,Type.BUTTON));
+ actions.add(new ActionInput("fly_up","Fly Up",PORT_ACT_FLY_UP,Type.BUTTON));
+ actions.add(new ActionInput("fly_down","Fly Down",PORT_ACT_FLY_DOWN,Type.BUTTON));
+ }
+
+ if (game == IDGame.Quake3)
+ {
+ actions.add(new ActionInput("inv_use","Use Item",PORT_ACT_USE,Type.BUTTON));
+ }
+
+ if (game == IDGame.Doom)
+ {
+ actions.add(new ActionInput("map_show","Show Automap",PORT_ACT_MAP,Type.BUTTON));
+ actions.add(new ActionInput("map_up","Automap Up",PORT_ACT_MAP_UP,Type.BUTTON));
+ actions.add(new ActionInput("map_down","Automap Down",PORT_ACT_MAP_DOWN,Type.BUTTON));
+ actions.add(new ActionInput("map_left","Automap Left",PORT_ACT_MAP_LEFT,Type.BUTTON));
+ actions.add(new ActionInput("map_right","Automap Right",PORT_ACT_MAP_RIGHT,Type.BUTTON));
+ actions.add(new ActionInput("map_zoomin","Automap Zoomin",PORT_ACT_MAP_ZOOM_IN,Type.BUTTON));
+ actions.add(new ActionInput("map_zoomout","Automap Zoomout",PORT_ACT_MAP_ZOOM_OUT,Type.BUTTON));
+ }
+
+ if ((game == IDGame.RTCW) || (game == IDGame.JK2) || (game == IDGame.JK3))
+ {
+ actions.add(new ActionInput("quick_save","Quick Save",PORT_ACT_QUICKSAVE,Type.BUTTON));
+ actions.add(new ActionInput("quick_load","Quick Load",PORT_ACT_QUICKLOAD,Type.BUTTON));
+ }
+
+ if ((game == IDGame.Doom) || (game == IDGame.Heretic) || (game == IDGame.Hexen)
+ || (game == IDGame.Strife)|| (game == IDGame.Quake)|| (game == IDGame.Quake2)
+ || (game == IDGame.Hexen2)
+ || (game == IDGame.JK2) || (game == IDGame.JK3))
+ {
+ actions.add(new ActionInput("menu_up","Menu Up",MENU_UP,Type.MENU));
+ actions.add(new ActionInput("menu_down","Menu Down",MENU_DOWN,Type.MENU));
+ actions.add(new ActionInput("menu_left","Menu Left",MENU_LEFT,Type.MENU));
+ actions.add(new ActionInput("menu_right","Menu Right",MENU_RIGHT,Type.MENU));
+ actions.add(new ActionInput("menu_select","Menu Select",MENU_SELECT,Type.MENU));
+ actions.add(new ActionInput("menu_back","Menu Back",MENU_BACK,Type.MENU));
+ }
+ filename = file;
+ }
+
+ public void setTextView(Context c,TextView tv)
+ {
+ ctx = c;
+ infoTextView = tv;
+ }
+
+ void saveControls(File file) throws IOException
+ {
+ if (Settings.DEBUG) Log.d(LOG,"saveControls, file = " + file.toString());
+
+ FileOutputStream fos = null;
+ ObjectOutputStream out = null;
+
+ fos = new FileOutputStream(file);
+ out = new ObjectOutputStream(fos);
+ out.writeObject(actions);
+ out.close();
+ }
+
+ public void loadControls() throws IOException, ClassNotFoundException
+ {
+ loadControls(new File(filename));
+ }
+
+ public void loadControls(File file) throws IOException, ClassNotFoundException
+ {
+ if (Settings.DEBUG) Log.d(LOG,"loadControls, file = " + file.toString());
+
+ InputStream fis = null;
+ ObjectInputStream in = null;
+
+
+ fis = new FileInputStream(file);
+
+ in = new ObjectInputStream(fis);
+ ArrayList cd = (ArrayList )in.readObject();
+ if (Settings.DEBUG) Log.d(LOG,"loadControls, file loaded OK");
+ in.close();
+
+ for (ActionInput d: cd)
+ {
+ for (ActionInput a: actions)
+ {
+ if (d.tag.contentEquals(a.tag))
+ {
+ a.invert = d.invert;
+ a.source = d.source;
+ a.sourceType = d.sourceType;
+ a.sourcePositive = d.sourcePositive;
+ a.scale = d.scale;
+ if (a.scale == 0) a.scale = 1;
+ }
+ }
+ }
+
+ //Now check no buttons are also assigned to analog, if it is, clear the buttons
+ //This is because n00bs keep assigning movment analog AND buttons!
+ for (ActionInput a: actions)
+ {
+ if ((a.source != -1) && (a.sourceType == Type.ANALOG) && (a.actionType == Type.BUTTON))
+ {
+ for (ActionInput a_check: actions)
+ {
+ if ((a_check.sourceType == Type.ANALOG) && (a_check.actionType == Type.ANALOG))
+ {
+ if (a.source == a_check.source)
+ {
+ a.source = -1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ fis.close();
+ }
+
+
+ void updated()
+ {
+ try {
+ saveControls(new File (filename));
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ ArrayList actions = new ArrayList();
+
+ ActionInput actionMontor=null;
+
+ boolean monitoring = false;
+
+ public boolean showExtraOptions(Activity act,int pos)
+ {
+ final ActionInput in = actions.get(pos);
+
+ if (in.actionType == Type.ANALOG)
+ {
+ Dialog dialog = new Dialog(act);
+ dialog.setTitle("Axis Sensitivity Setting");
+ dialog.setCancelable(true);
+
+ final LinearLayout l = new LinearLayout(act);
+ l.setOrientation(LinearLayout.VERTICAL);
+
+ final SeekBar sb = new SeekBar(act);
+ l.addView(sb);
+
+
+ sb.setMax(100);
+ sb.setProgress((int)(in.scale * 50));
+
+
+
+ final CheckBox invert = new CheckBox(act);
+ invert.setText("Invert");
+ invert.setChecked(in.invert);
+
+ l.addView(invert);
+
+ dialog.setOnDismissListener(new OnDismissListener() {
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ in.scale = (float)sb.getProgress()/(float)50;
+ in.invert = invert.isChecked();
+ updated();
+ }
+ });
+
+ dialog.setContentView(l);
+
+ dialog.show();
+ return true;
+ }
+ return false;
+ }
+
+ public void startMonitor(Activity act,int pos)
+ {
+ actionMontor = actions.get(pos);
+ monitoring = true;
+
+ if (actionMontor.actionType == Type.ANALOG)
+ infoTextView.setText("Move Stick for: " + actionMontor.description);
+ else
+ infoTextView.setText("Press Button for: " + actionMontor.description);
+
+ infoTextView.setTextColor(ctx.getResources().getColor(android.R.color.holo_green_light));
+ }
+
+ int[] axisTest = {
+ /*
+ MotionEvent.AXIS_GENERIC_1,
+ MotionEvent.AXIS_GENERIC_2,
+ MotionEvent.AXIS_GENERIC_3,
+ MotionEvent.AXIS_GENERIC_4,
+ MotionEvent.AXIS_GENERIC_5,
+ MotionEvent.AXIS_GENERIC_6,
+ MotionEvent.AXIS_GENERIC_7,
+ MotionEvent.AXIS_GENERIC_8,
+ MotionEvent.AXIS_GENERIC_9,
+ MotionEvent.AXIS_GENERIC_10,
+ MotionEvent.AXIS_GENERIC_11,
+ MotionEvent.AXIS_GENERIC_12,
+ MotionEvent.AXIS_GENERIC_13,
+ MotionEvent.AXIS_GENERIC_14,
+ MotionEvent.AXIS_GENERIC_15,
+ MotionEvent.AXIS_GENERIC_16,
+ */
+ MotionEvent.AXIS_HAT_X,
+ MotionEvent.AXIS_HAT_Y,
+ MotionEvent.AXIS_LTRIGGER,
+ MotionEvent.AXIS_RTRIGGER,
+ MotionEvent.AXIS_RUDDER,
+ MotionEvent.AXIS_RX,
+ MotionEvent.AXIS_RY,
+ MotionEvent.AXIS_RZ,
+ MotionEvent.AXIS_THROTTLE,
+ MotionEvent.AXIS_X,
+ MotionEvent.AXIS_Y,
+ MotionEvent.AXIS_Z,
+ MotionEvent.AXIS_BRAKE,
+ MotionEvent.AXIS_GAS,
+ };
+
+ public boolean onGenericMotionEvent(GenericAxisValues event)
+ {
+ Log.d(LOG,"onGenericMotionEvent");
+ if (monitoring)
+ {
+ if (actionMontor != null)
+ {
+ for (int a: axisTest)
+ {
+ if (Math.abs(event.getAxisValue(a)) > 0.6)
+ {
+ actionMontor.source = a;
+ actionMontor.sourceType = Type.ANALOG;
+ //Used for button actions
+ if (event.getAxisValue(a) > 0)
+ actionMontor.sourcePositive = true;
+ else
+ actionMontor.sourcePositive = false;
+
+ monitoring = false;
+
+ if (Settings.DEBUG) Log.d(LOG,actionMontor.description + " = Analog (" + actionMontor.source + ")");
+
+ infoTextView.setText("Select Action");
+ infoTextView.setTextColor(ctx.getResources().getColor(android.R.color.holo_blue_light));
+
+ updated();
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isMonitoring()
+ {
+ return monitoring;
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ Log.d(LOG,"onKeyDown " + keyCode);
+
+ if (monitoring)
+ {
+ if (keyCode == KeyEvent.KEYCODE_BACK) //Cancel and clear button assignment
+ {
+ actionMontor.source = -1;
+ actionMontor.sourceType = Type.BUTTON;
+ monitoring = false;
+ infoTextView.setText("CANCELED");
+ infoTextView.setTextColor(ctx.getResources().getColor(android.R.color.holo_red_light));
+
+ updated();
+ return true;
+ }
+ else
+ {
+
+ if (actionMontor != null)
+ {
+ if (actionMontor.actionType != Type.ANALOG)
+ {
+ actionMontor.source = keyCode;
+ actionMontor.sourceType = Type.BUTTON;
+ monitoring = false;
+
+ infoTextView.setText("Select Action");
+ infoTextView.setTextColor(ctx.getResources().getColor(android.R.color.holo_blue_light));
+
+ updated();
+ return true;
+ }
+ }
+ }
+ }
+
+
+ return false;
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event)
+ {
+ return false;
+ }
+
+
+ public int getSize()
+ {
+ return actions.size();
+ }
+
+ public View getView(final Activity ctx,final int nbr)
+ {
+
+ View view = ctx.getLayoutInflater().inflate(R.layout.controls_listview_item, null);
+ ImageView image = (ImageView)view.findViewById(R.id.imageView);
+ TextView name = (TextView)view.findViewById(R.id.name_textview);
+ TextView binding = (TextView)view.findViewById(R.id.binding_textview);
+ ImageView setting_image = (ImageView)view.findViewById(R.id.settings_imageview);
+
+ ActionInput ai = actions.get(nbr);
+
+ if ((ai.actionType == Type.BUTTON) || (ai.actionType == Type.MENU))
+ {
+
+ if (ai.sourceType == Type.ANALOG)
+ binding.setText(MotionEvent.axisToString(ai.source));
+ else
+ binding.setText(KeyEvent.keyCodeToString(ai.source));
+
+ setting_image.setVisibility(View.GONE);
+
+ if ( (ai.actionType == Type.MENU))
+ {
+ name.setTextColor(0xFF00aeef); //BLUEY
+ image.setImageResource(R.drawable.gamepad_menu);
+ }
+ else
+ {
+ image.setImageResource(R.drawable.gamepad);
+ }
+ }
+ else if (ai.actionType == Type.ANALOG)
+ {
+ binding.setText(MotionEvent.axisToString(ai.source));
+ setting_image.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ showExtraOptions(ctx,nbr);
+ }
+ });
+ name.setTextColor(0xFFf7941d); //ORANGE
+ }
+
+ /*
+ if (ai.actionType == Type.BUTTON)
+ {
+ image.setImageResource(R.drawable.gamepad);
+ if (ai.sourceType == Type.ANALOG)
+ binding.setText(MotionEvent.axisToString(ai.source));
+ else
+ binding.setText(KeyEvent.keyCodeToString(ai.source));
+
+ setting_image.setVisibility(View.GONE);
+ }
+ else //Analog
+ {
+ binding.setText(MotionEvent.axisToString(ai.source));
+ setting_image.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ showExtraOptions(ctx,nbr);
+ }
+ });
+ name.setTextColor(0xFFf7941d);
+ }
+*/
+ name.setText(ai.description);
+
+ return view;
+ }
+}
diff --git a/src/com/beloko/touchcontrols/ControlInterface.java b/src/com/beloko/touchcontrols/ControlInterface.java
new file mode 100644
index 00000000..bddee7bc
--- /dev/null
+++ b/src/com/beloko/touchcontrols/ControlInterface.java
@@ -0,0 +1,22 @@
+package com.beloko.touchcontrols;
+
+import android.view.KeyEvent;
+
+public interface ControlInterface {
+
+ public void initTouchControls_if(String pngPath,int width,int height);
+
+ public boolean touchEvent_if( int action, int pid, float x, float y);
+ public void keyPress_if(int down, int qkey, int unicode);
+ public void doAction_if(int state, int action);
+ public void analogFwd_if(float v);
+ public void analogSide_if(float v);
+ public void analogPitch_if(int mode,float v);
+ public void analogYaw_if(int mode,float v);
+ public void setTouchSettings_if(float alpha,float strafe,float fwd,float pitch,float yaw,int other);
+
+ public void quickCommand_if(String command);
+
+ public int mapKey(int acode,int unicode);
+}
+
\ No newline at end of file
diff --git a/src/com/beloko/touchcontrols/ControlInterpreter.java b/src/com/beloko/touchcontrols/ControlInterpreter.java
new file mode 100644
index 00000000..4db4d00a
--- /dev/null
+++ b/src/com/beloko/touchcontrols/ControlInterpreter.java
@@ -0,0 +1,304 @@
+package com.beloko.touchcontrols;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import com.bda.controller.Controller;
+import com.beloko.touchcontrols.ControlConfig.Type;
+import com.beloko.touchcontrols.Settings.IDGame;
+
+public class ControlInterpreter {
+
+ String LOG = "QuakeControlInterpreter";
+
+ ControlInterface quakeIf;
+ ControlConfig config;
+
+ boolean gamePadEnabled;
+
+ float screenWidth, screenHeight;
+
+ HashMap analogButtonState = new HashMap(); //Saves current state of analog buttons so all sent each time
+
+ public ControlInterpreter(ControlInterface qif,IDGame game,String controlfile,boolean ctrlEn)
+ {
+ Log.d("QuakeControlInterpreter", "file = " + controlfile);
+
+ gamePadEnabled = ctrlEn;
+
+ config = new ControlConfig(controlfile,game);
+ try {
+ config.loadControls();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ //e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ // TODO Auto-generated catch block
+ //e.printStackTrace();
+ }
+
+ for (ActionInput ai: config.actions)
+ {
+ if ((ai.sourceType == Type.ANALOG) && ((ai.actionType == Type.MENU) || (ai.actionType == Type.BUTTON)))
+ {
+ analogButtonState.put(ai.actionCode, false);
+ }
+ }
+
+ quakeIf = qif;
+ }
+
+ public void setScreenSize(int w,int h)
+ {
+ screenWidth = w;
+ screenHeight = h;
+ }
+
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ int action = event.getAction();
+ int actionCode = action & MotionEvent.ACTION_MASK;
+
+ if (actionCode == MotionEvent.ACTION_MOVE)
+ {
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+
+ float x = event.getX(i)/screenWidth;
+ float y = event.getY(i)/screenHeight;
+ int pid = event.getPointerId(i);
+ quakeIf.touchEvent_if(3, pid, x, y);
+ }
+ }
+ else if (actionCode == MotionEvent.ACTION_DOWN)
+ {
+ float x = event.getX()/screenWidth;
+ float y = event.getY()/screenHeight;
+ quakeIf.touchEvent_if(1, 0, x, y);
+ }
+ else if (actionCode == MotionEvent.ACTION_POINTER_DOWN)
+ {
+ int index = event.getActionIndex();
+ if (index != -1)
+ {
+ float x = event.getX(index)/screenWidth;
+ float y = event.getY(index)/screenHeight;
+ int pid = event.getPointerId(index);
+ quakeIf.touchEvent_if(1, pid, x, y);
+ }
+ }
+ else if (actionCode == MotionEvent.ACTION_POINTER_UP)
+ {
+ int index = event.getActionIndex();
+ if (index != -1)
+ {
+
+ float x = event.getX(index)/screenWidth;
+ float y = event.getY(index)/screenHeight;
+ int pid = event.getPointerId(index);
+ quakeIf.touchEvent_if(2, pid, x, y);
+ }
+ }
+ else if (actionCode == MotionEvent.ACTION_UP)
+ {
+ float x = event.getX()/screenWidth;
+ float y = event.getY()/screenHeight;
+ int index = event.getActionIndex();
+ int pid = event.getPointerId(index);
+
+ quakeIf.touchEvent_if(2, pid, x, y);
+ }
+
+ return true;
+ }
+
+
+ public void onMogaKeyEvent(com.bda.controller.KeyEvent event,int pad_version)
+ {
+ int keycode = event.getKeyCode();
+
+ if (pad_version == Controller.ACTION_VERSION_MOGA)
+ {
+ //Log.d(LOG,"removed");
+ if ((keycode == com.bda.controller.KeyEvent.KEYCODE_DPAD_DOWN) ||
+ (keycode == com.bda.controller.KeyEvent.KEYCODE_DPAD_UP) ||
+ (keycode == com.bda.controller.KeyEvent.KEYCODE_DPAD_LEFT) ||
+ (keycode == com.bda.controller.KeyEvent.KEYCODE_DPAD_RIGHT))
+ return;
+ }
+
+ if (event.getAction() == com.bda.controller.KeyEvent.ACTION_DOWN)
+ onKeyDown(keycode, null);
+ else if (event.getAction() == com.bda.controller.KeyEvent.ACTION_UP)
+ onKeyUp(keycode, null);
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ boolean used = false;;
+ if (gamePadEnabled)
+ {
+ for (ActionInput ai: config.actions)
+ {
+ if (((ai.sourceType == Type.BUTTON)||(ai.sourceType == Type.MENU)) && (ai.source == keyCode))
+ {
+ quakeIf.doAction_if(1, ai.actionCode);
+ Log.d(LOG,"key down intercept");
+ used = true;
+ }
+ }
+ }
+
+ if (used)
+ return true;
+
+
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || //If these were mapped it would have already returned
+ (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN))
+ return false;
+ else
+ {
+ int uc = 0;
+ if (event !=null)
+ uc = event.getUnicodeChar();
+ quakeIf.keyPress_if(1, quakeIf.mapKey(keyCode, uc), uc);
+ return true;
+ }
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event)
+ {
+ boolean used = false;
+
+ if (gamePadEnabled)
+ {
+ for (ActionInput ai: config.actions)
+ {
+ if (((ai.sourceType == Type.BUTTON) || (ai.sourceType == Type.MENU)) && (ai.source == keyCode))
+ {
+ quakeIf.doAction_if(0, ai.actionCode);
+ used = true;
+ }
+ }
+ }
+
+ if (used)
+ return true;
+
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || //If these were mapped it would have already returned
+ (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN))
+ return false;
+ else
+ {
+ int uc = 0;
+ if (event !=null)
+ uc = event.getUnicodeChar();
+ quakeIf.keyPress_if(0, quakeIf.mapKey(keyCode, uc), uc);
+ return true;
+ }
+
+ }
+
+ float deadRegion = 0.2f;
+ private float analogCalibrate(float v)
+ {
+ if ((v < deadRegion) && (v > -deadRegion))
+ return 0;
+ else
+ {
+ if (v > 0)
+ return(v-deadRegion) / (1-deadRegion);
+ else
+ return(v+deadRegion) / (1-deadRegion);
+ //return v;
+ }
+ }
+
+ GenericAxisValues genericAxisValues = new GenericAxisValues();
+
+
+ //This is for normal Android motioon event
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ genericAxisValues.setAndroidValues(event);
+ return onGenericMotionEvent(genericAxisValues);
+ }
+
+ //This is for Moga event
+ public boolean onGenericMotionEvent(com.bda.controller.MotionEvent event) {
+ genericAxisValues.setMogaValues(event);
+ return onGenericMotionEvent(genericAxisValues);
+ }
+
+ public boolean onGenericMotionEvent(GenericAxisValues event) {
+ if (Settings.DEBUG) Log.d(LOG,"onGenericMotionEvent" );
+
+ boolean used = false;
+ if (gamePadEnabled)
+ {
+ for (ActionInput ai: config.actions)
+ {
+ if ((ai.sourceType == Type.ANALOG) && (ai.source != -1))
+ {
+ int invert;
+ invert = ai.invert?-1:1;
+ if (ai.actionCode == ControlConfig.ACTION_ANALOG_PITCH)
+ quakeIf.analogPitch_if(ControlConfig.LOOK_MODE_JOYSTICK, analogCalibrate(event.getAxisValue(ai.source)) * invert * ai.scale);
+ else if (ai.actionCode == ControlConfig.ACTION_ANALOG_YAW)
+ quakeIf.analogYaw_if(ControlConfig.LOOK_MODE_JOYSTICK, -analogCalibrate(event.getAxisValue(ai.source)) * invert * ai.scale);
+ else if (ai.actionCode == ControlConfig.ACTION_ANALOG_FWD)
+ quakeIf.analogFwd_if(-analogCalibrate(event.getAxisValue(ai.source)) * invert * ai.scale);
+ else if (ai.actionCode == ControlConfig.ACTION_ANALOG_STRAFE)
+ quakeIf.analogSide_if(analogCalibrate(event.getAxisValue(ai.source)) * invert * ai.scale);
+ else //Must be using analog as a button
+ {
+ if (Settings.DEBUG) Log.d(LOG,"Analog as button" );
+
+ if (Settings.DEBUG) Log.d(LOG,ai.toString());
+
+ if (((ai.sourcePositive) && (event.getAxisValue(ai.source)) > 0.5) ||
+ ((!ai.sourcePositive) && (event.getAxisValue(ai.source)) < -0.5) )
+ {
+ if (!analogButtonState.get(ai.actionCode)) //Check internal state, only send if different
+ {
+ quakeIf.doAction_if(1, ai.actionCode); //press
+ analogButtonState.put(ai.actionCode, true);
+ }
+ }
+ else
+ {
+ if (analogButtonState.get(ai.actionCode)) //Check internal state, only send if different
+ {
+ quakeIf.doAction_if(0, ai.actionCode); //un-press
+ analogButtonState.put(ai.actionCode, false);
+ }
+ }
+
+ }
+ used = true;
+ }
+ /*
+ //Menu buttons
+ if ((ai.sourceType == Type.ANALOG) && (ai.actionType == Type.MENU) && (ai.source != -1))
+ {
+ if (GD.DEBUG) Log.d(LOG,"Analog as MENU button" );
+ if (GD.DEBUG) Log.d(LOG,ai.toString());
+ if (((ai.sourcePositive) && (event.getAxisValue(ai.source)) > 0.5) ||
+ ((!ai.sourcePositive) && (event.getAxisValue(ai.source)) < -0.5) )
+ quakeIf.doAction_if(1, ai.actionCode); //press
+ else
+ quakeIf.doAction_if(0, ai.actionCode); //un-press
+ }
+ */
+ }
+
+ }
+
+
+ return used;
+
+ }
+}
diff --git a/src/com/beloko/touchcontrols/GamePadFragment.java b/src/com/beloko/touchcontrols/GamePadFragment.java
new file mode 100644
index 00000000..e7a4b177
--- /dev/null
+++ b/src/com/beloko/touchcontrols/GamePadFragment.java
@@ -0,0 +1,289 @@
+package com.beloko.touchcontrols;
+
+import in.celest.xash3d.hl.R;
+
+import java.io.IOException;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.bda.controller.Controller;
+import com.bda.controller.ControllerListener;
+import com.bda.controller.StateEvent;
+
+public class GamePadFragment extends Fragment{
+ final String LOG = "GamePadFragment";
+
+ ListView listView;
+ ControlListAdapter adapter;
+
+ TextView info;
+
+ ControlConfig config;
+
+ GenericAxisValues genericAxisValues = new GenericAxisValues();
+
+ Controller mogaController = null;
+ final MogaControllerListener mListener = new MogaControllerListener();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ config = new ControlConfig(Settings.gamePadControlsFile,Settings.game);
+
+ try {
+ config.loadControls();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ //e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ // TODO Auto-generated catch block
+ //e.printStackTrace();
+ }
+
+
+ mogaController = Controller.getInstance(getActivity());
+ mogaController.init();
+ mogaController.setListener(mListener,new Handler());
+ }
+
+
+ boolean isHidden = true;
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ isHidden = hidden;
+ super.onHiddenChanged(hidden);
+ }
+
+
+
+ @Override
+ public void onPause()
+ {
+ super.onPause();
+ mogaController.onPause();
+ }
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+ mogaController.onResume();
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ super.onDestroy();
+ mogaController.exit();
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View mainView = inflater.inflate(R.layout.fragment_gamepad, null);
+
+
+ CheckBox enableCb = (CheckBox)mainView.findViewById(R.id.gamepad_enable_checkbox);
+ enableCb.setChecked(Settings.gamePadEnabled);
+
+ enableCb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Settings.setBoolOption(getActivity(), "gamepad_enabled", isChecked);
+ Settings.gamePadEnabled = isChecked;
+ setListViewEnabled(Settings.gamePadEnabled);
+
+ }
+ });
+
+
+ CheckBox hideCtrlCb = (CheckBox)mainView.findViewById(R.id.gamepad_hide_touch_checkbox);
+ hideCtrlCb.setChecked(Settings.hideTouchControls);
+
+ hideCtrlCb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Settings.setBoolOption(getActivity(), "hide_touch_controls", isChecked);
+ Settings.hideTouchControls = isChecked;
+ }
+ });
+
+
+ Button help = (Button)mainView.findViewById(R.id.gamepad_help_button);
+ help.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ //NoticeDialog.show(getActivity(),"Gamepad Help", R.raw.gamepad);
+ }
+ });
+
+ listView = (ListView)mainView.findViewById(R.id.gamepad_listview);
+ adapter = new ControlListAdapter(getActivity());
+ listView.setAdapter(adapter);
+
+ setListViewEnabled(Settings.gamePadEnabled);
+
+
+ listView.setSelector(R.drawable.layout_sel_background);
+ listView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView> arg0, View v, int pos,
+ long id) {
+ config.startMonitor(getActivity(), pos);
+ }
+ });
+
+ listView.setOnItemLongClickListener(new OnItemLongClickListener() {
+
+ @Override
+ public boolean onItemLongClick(AdapterView> arg0, View v, int pos,
+ long id) {
+ return config.showExtraOptions(getActivity(), pos);
+ }
+ });
+
+ adapter.notifyDataSetChanged();
+
+ info = (TextView)mainView.findViewById(R.id.gamepad_info_textview);
+ info.setText("Select Action");
+ info.setTextColor(getResources().getColor(android.R.color.holo_blue_light));
+
+ config.setTextView(getActivity(),info);
+
+ return mainView;
+ }
+
+ private void setListViewEnabled(boolean v)
+ {
+
+ listView.setEnabled(v);
+ if (v)
+ {
+ listView.setAlpha(1);
+ }
+ else
+ {
+ listView.setAlpha(0.3f);
+ //listView.setBackgroundColor(Color.GRAY);
+ }
+ }
+
+ public boolean onGenericMotionEvent(MotionEvent event)
+ {
+ genericAxisValues.setAndroidValues(event);
+
+ if (config.onGenericMotionEvent(genericAxisValues))
+ adapter.notifyDataSetChanged();
+
+ //return config.isMonitoring(); //This does not work, mouse appears anyway
+ return !isHidden; //If gamepas tab visible always steal
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ if (config.onKeyDown(keyCode, event))
+ {
+ adapter.notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event)
+ {
+ if(config.onKeyUp(keyCode, event))
+ {
+ adapter.notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ class ControlListAdapter extends BaseAdapter{
+ private Activity context;
+
+ public ControlListAdapter(Activity context){
+ this.context=context;
+
+ }
+ public void add(String string){
+
+ }
+ public int getCount() {
+ return config.getSize();
+ }
+
+ public Object getItem(int arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public long getItemId(int arg0) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+
+ public View getView (int position, View convertView, ViewGroup list) {
+ View v = config.getView(getActivity(), position);
+ return v;
+ }
+
+ }
+
+
+ class MogaControllerListener implements ControllerListener {
+
+
+ @Override
+ public void onKeyEvent(com.bda.controller.KeyEvent event) {
+ //Log.d(LOG,"onKeyEvent " + event.getKeyCode());
+
+ if (event.getAction() == com.bda.controller.KeyEvent.ACTION_DOWN)
+ onKeyDown(event.getKeyCode(),null);
+ else if (event.getAction() == com.bda.controller.KeyEvent.ACTION_UP)
+ onKeyUp(event.getKeyCode(),null);
+ }
+
+ @Override
+ public void onMotionEvent(com.bda.controller.MotionEvent event) {
+ //Log.d(LOG,"onGenericMotionEvent " + event.toString());
+
+ genericAxisValues.setMogaValues(event);
+
+ if (config.onGenericMotionEvent(genericAxisValues))
+ adapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onStateEvent(StateEvent event) {
+ Log.d(LOG,"onStateEvent " + event.getState());
+ }
+ }
+
+}
diff --git a/src/com/beloko/touchcontrols/GenericAxisValues.java b/src/com/beloko/touchcontrols/GenericAxisValues.java
new file mode 100644
index 00000000..ee6baa03
--- /dev/null
+++ b/src/com/beloko/touchcontrols/GenericAxisValues.java
@@ -0,0 +1,29 @@
+package com.beloko.touchcontrols;
+
+import android.view.MotionEvent;
+
+public class GenericAxisValues {
+ float[] values = new float[64];
+
+ public float getAxisValue(int a)
+ {
+ return values[a];
+ }
+
+ public void setAxisValue(int a,float v)
+ {
+ values[a] = v;
+ }
+
+ public void setAndroidValues(MotionEvent event){
+ for (int n=0;n<64;n++)
+ values[n] = event.getAxisValue(n);
+ }
+
+ public void setMogaValues(com.bda.controller.MotionEvent event){
+ values[MotionEvent.AXIS_X] = event.getAxisValue(MotionEvent.AXIS_X);
+ values[MotionEvent.AXIS_Y] = event.getAxisValue(MotionEvent.AXIS_Y);
+ values[MotionEvent.AXIS_Z] = event.getAxisValue(MotionEvent.AXIS_Z);
+ values[MotionEvent.AXIS_RZ] = event.getAxisValue(MotionEvent.AXIS_RZ);
+ }
+}
diff --git a/src/com/beloko/touchcontrols/QuickCommand.java b/src/com/beloko/touchcontrols/QuickCommand.java
new file mode 100644
index 00000000..4965d1ef
--- /dev/null
+++ b/src/com/beloko/touchcontrols/QuickCommand.java
@@ -0,0 +1,36 @@
+package com.beloko.touchcontrols;
+
+import java.io.Serializable;
+
+public class QuickCommand implements Serializable{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ String title;
+ String command;
+
+ QuickCommand(String title, String command)
+ {
+ this.title = title;
+ this.command = command;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public void setCommand(String command) {
+ this.command = command;
+ }
+
+}
diff --git a/src/com/beloko/touchcontrols/Settings.java b/src/com/beloko/touchcontrols/Settings.java
new file mode 100644
index 00000000..08e317d2
--- /dev/null
+++ b/src/com/beloko/touchcontrols/Settings.java
@@ -0,0 +1,144 @@
+package com.beloko.touchcontrols;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.AssetManager;
+import android.util.Log;
+
+
+public class Settings {
+ public static boolean DEBUG = false;
+
+ public static String gamePadControlsFile = "gamepadSettings.dat";
+ public static String graphicsDir = "";
+
+ public static boolean gamePadEnabled;
+ public static boolean hideTouchControls;
+
+ public enum IDGame{Quake,Quake2,Doom,Duke3d,Quake3,Hexen2,RTCW,Wolf3d,JK2,JK3,Heretic,Hexen,Strife,AVP,Shadow,Gish,Descent1,Descent2,Homeworld,BlakeStone,Noah,Doom3};
+
+ public static IDGame game;
+
+
+ public static float getFloatOption(Context ctx,String name, float def)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ return settings.getFloat(name, def);
+ }
+
+ public static void setFloatOption(Context ctx,String name, float value)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putFloat(name, value);
+ editor.commit();
+ }
+
+ public static boolean getBoolOption(Context ctx,String name, boolean def)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ return settings.getBoolean(name, def);
+ }
+
+ public static void setBoolOption(Context ctx,String name, boolean value)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putBoolean(name, value);
+ editor.commit();
+ }
+
+ public static int getIntOption(Context ctx,String name, int def)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ return settings.getInt(name, def);
+ }
+
+ public static void setIntOption(Context ctx,String name, int value)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(name, value);
+ editor.commit();
+ }
+
+ public static long getLongOption(Context ctx,String name, long def)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ return settings.getLong(name, def);
+ }
+
+ public static void setLongOption(Context ctx,String name, long value)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putLong(name, value);
+ editor.commit();
+ }
+
+ public static String getStringOption(Context ctx,String name, String def)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ return settings.getString(name, def);
+ }
+
+ public static void setStringOption(Context ctx,String name, String value)
+ {
+ SharedPreferences settings = ctx.getSharedPreferences("OPTIONS", Context.MODE_MULTI_PROCESS);
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putString(name, value);
+ editor.commit();
+ }
+
+ static public void copyFile(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[1024];
+ int read;
+ while((read = in.read(buffer)) != -1){
+ out.write(buffer, 0, read);
+ }
+ out.close();
+ }
+
+ static public void copyPNGAssets(Context ctx,String dir,String prefix) {
+
+ if (prefix == null)
+ prefix = "";
+
+ File d = new File(dir);
+ if (!d.exists())
+ d.mkdirs();
+
+ AssetManager assetManager = ctx.getAssets();
+ String[] files = null;
+ try {
+ files = assetManager.list("");
+ } catch (IOException e) {
+ Log.e("tag", "Failed to get asset file list.", e);
+ }
+ for(String filename : files) {
+ if (filename.endsWith("png") && filename.startsWith(prefix)){
+ InputStream in = null;
+ OutputStream out = null;
+ //Log.d("test","file = " + filename);
+ try {
+ in = assetManager.open(filename);
+ out = new FileOutputStream(dir + "/" + filename.substring(prefix.length()));
+ copyFile(in, out);
+ in.close();
+ in = null;
+ out.flush();
+ out.close();
+ out = null;
+ } catch(IOException e) {
+ Log.e("tag", "Failed to copy asset file: " + filename, e);
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/beloko/touchcontrols/ShowKeyboard.java b/src/com/beloko/touchcontrols/ShowKeyboard.java
new file mode 100644
index 00000000..c5ea09e6
--- /dev/null
+++ b/src/com/beloko/touchcontrols/ShowKeyboard.java
@@ -0,0 +1,68 @@
+package com.beloko.touchcontrols;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+public class ShowKeyboard {
+ static Activity activity;
+ static View view;;
+
+ public static void setup(Activity a,View v)
+ {
+ activity = a;
+ view = v;
+ }
+
+
+ public static void toggleKeyboard()
+ {
+ Log.d("ShowKeyboard","toggleKeyboard");
+
+ InputMethodManager im = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (im != null)
+ {
+ Log.d("ShowKeyboard","toggleKeyboard...");
+ im.toggleSoftInput(0, 0);
+ }
+ }
+
+ public static void showKeyboard(int show)
+ {
+ Log.d("ShowKeyboard","showKeyboard " + show);
+
+ InputMethodManager im = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (im != null)
+ {
+ if (show == 0)
+ {
+ im.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
+ }
+ if (show == 1)
+ if (!im.isAcceptingText())
+ toggleKeyboard();
+ if (show == 2)
+ toggleKeyboard();
+ }
+
+ /*
+ InputMethodManager imm = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (show == 1)
+ imm.showSoftInput(view, InputMethodManager.SHOW_FORCED);
+ else
+ imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+ */
+ }
+
+ public static boolean hasHardwareKeyboard()
+ {
+ if(activity == null)
+ return false;
+
+ return activity.getApplicationContext().getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY;
+ }
+
+}
diff --git a/src/com/beloko/touchcontrols/TouchControlsEditing.java b/src/com/beloko/touchcontrols/TouchControlsEditing.java
new file mode 100644
index 00000000..0c19bce5
--- /dev/null
+++ b/src/com/beloko/touchcontrols/TouchControlsEditing.java
@@ -0,0 +1,145 @@
+package com.beloko.touchcontrols;
+
+import in.celest.xash3d.hl.R;
+import android.app.Activity;
+import android.app.Dialog;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.BaseAdapter;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+public class TouchControlsEditing {
+
+ static String TAG = "TouchControlsEditing";
+
+ static class ControlInfo
+ {
+ String tag;
+ String image;
+ boolean enabled;
+ boolean hidden;
+ }
+
+ static ListAdapter adapter;
+
+ static Activity activity;
+
+ public static native void JNIGetControlInfo(int pos,ControlInfo info);
+
+ public static native int JNIGetNbrControls();
+
+ public static native void JNISetHidden(int pos, boolean hidden);
+
+ public static void setup(Activity a)
+ {
+ activity = a;
+ }
+
+ public static void show()
+ {
+ show(activity);
+ }
+
+ public static void show(Activity act)
+ {
+ Log.d(TAG,"showSettings");
+
+ if (act != null)
+ activity = act;
+
+ activity.runOnUiThread(new Runnable(){
+ public void run() {
+ final Dialog dialog = new Dialog(activity);
+ ListView listView = new ListView(activity);
+
+ dialog.setContentView(listView);
+ dialog.setTitle("Add/remove buttons");
+ dialog.setCancelable(true);
+
+ adapter = new ListAdapter(activity);
+ listView.setAdapter(adapter);
+
+ dialog.getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ dialog.show();
+ }
+ });
+
+ }
+
+ static class ListAdapter extends BaseAdapter{
+ private Activity context;
+
+ public ListAdapter(Activity context){
+ this.context=context;
+
+ }
+ public void add(String string){
+
+ }
+ public int getCount() {
+ return TouchControlsEditing.JNIGetNbrControls();
+ }
+
+ public Object getItem(int arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public long getItemId(int arg0) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+
+ public View getView (int position, View convertView, ViewGroup list) {
+ //if (convertView == null) dont reuse view otherwise check change get called
+ convertView = activity.getLayoutInflater().inflate(R.layout.edit_controls_listview_item, null);
+
+ final int my_pos = position;
+
+ ImageView image = (ImageView)convertView.findViewById(R.id.imageView);
+ TextView name = (TextView)convertView.findViewById(R.id.name_textview);
+ ToggleButton hidden = (ToggleButton)convertView.findViewById(R.id.hidden_switch);
+
+
+ TouchControlsEditing.ControlInfo ci = new TouchControlsEditing.ControlInfo();
+ TouchControlsEditing.JNIGetControlInfo(position, ci);
+
+ name.setText(ci.tag);
+ hidden.setChecked(!ci.hidden);
+ hidden.setTag(new Integer(position));
+
+ hidden.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Integer pos = (Integer)buttonView.getTag();
+
+ TouchControlsEditing.JNISetHidden(pos, !isChecked);
+ adapter.notifyDataSetChanged();
+ }
+ });
+
+ String png = activity.getFilesDir() + "/" + ci.image + ".png";
+ Log.d(TAG,"png = " + png);
+ BitmapDrawable bm = new BitmapDrawable(png);
+
+ image.setImageDrawable(bm);
+ return convertView;
+ }
+
+ }
+
+
+}
diff --git a/src/com/beloko/touchcontrols/TouchControlsSettings.java b/src/com/beloko/touchcontrols/TouchControlsSettings.java
new file mode 100644
index 00000000..c68c977e
--- /dev/null
+++ b/src/com/beloko/touchcontrols/TouchControlsSettings.java
@@ -0,0 +1,265 @@
+package com.beloko.touchcontrols;
+
+
+import in.celest.xash3d.hl.R;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+
+
+public class TouchControlsSettings {
+
+ static Activity activity;
+ static ControlInterface quakeIf;
+
+
+ static int alpha,fwdSens,strafeSens,pitchSens,yawSens;
+
+ static boolean mouseMode,showWeaponCycle,showSticks,enableWeaponWheel;
+ static boolean invertLook,precisionShoot;
+
+ static int doubleTapMove,doubleTapLook;
+
+ public static void setup(Activity a,ControlInterface qif)
+ {
+ activity = a;
+ quakeIf = qif;
+ }
+
+ public static void showSettings()
+ {
+ Log.d("settings","showSettings");
+
+ activity.runOnUiThread(new Runnable(){
+ public void run() {
+ final Dialog dialog = new Dialog(activity);
+ dialog.setContentView(R.layout.touch_controls_settings);
+ dialog.setTitle("Touch Control Sensitivity Settings");
+ dialog.setCancelable(true);
+
+ final SeekBar alphaSeek = (SeekBar)dialog.findViewById(R.id.alpha_seekbar);
+ final SeekBar fwdSeek = (SeekBar)dialog.findViewById(R.id.fwd_seekbar);
+ final SeekBar strafeSeek = (SeekBar)dialog.findViewById(R.id.strafe_seekbar);
+ final SeekBar pitchSeek = (SeekBar)dialog.findViewById(R.id.pitch_seekbar);
+ final SeekBar yawSeek = (SeekBar)dialog.findViewById(R.id.yaw_seekbar);
+
+ final CheckBox mouseModeCheck = (CheckBox)dialog.findViewById(R.id.mouse_turn_checkbox);
+ final CheckBox showWeaponCycleCheckBox = (CheckBox)dialog.findViewById(R.id.show_next_weapon_checkbox);
+ final CheckBox invertLookCheckBox = (CheckBox)dialog.findViewById(R.id.invert_loop_checkbox);
+ final CheckBox precisionShootCheckBox = (CheckBox)dialog.findViewById(R.id.precision_shoot_checkbox);
+ final CheckBox showSticksCheckBox = (CheckBox)dialog.findViewById(R.id.show_sticks_checkbox);
+ final CheckBox enableWeaponWheelCheckBox = (CheckBox)dialog.findViewById(R.id.enable_weapon_wheel_checkbox);
+
+ /*
+ //Hide controls for lookup/down
+ if (Settings.game == IDGame.Doom)
+ {
+ //pitchSeek.setVisibility(View.GONE);
+ invertLookCheckBox.setVisibility(View.GONE);
+ }
+ */
+ Button add_rem_button = (Button)dialog.findViewById(R.id.add_remove_button);
+ add_rem_button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ TouchControlsEditing.show(activity);
+ }
+ });
+
+ alphaSeek.setProgress(alpha);
+ fwdSeek.setProgress(fwdSens);
+ strafeSeek.setProgress(strafeSens);
+ pitchSeek.setProgress(pitchSens);
+ yawSeek.setProgress(yawSens);
+
+ mouseModeCheck.setChecked(mouseMode);
+ showWeaponCycleCheckBox.setChecked(showWeaponCycle);
+ invertLookCheckBox.setChecked(invertLook);
+ precisionShootCheckBox.setChecked(precisionShoot);
+ showSticksCheckBox.setChecked(showSticks);
+ enableWeaponWheelCheckBox.setChecked(enableWeaponWheel);
+
+ Spinner move_spinner = (Spinner) dialog.findViewById(R.id.move_dbl_tap_spinner);
+ ArrayAdapter adapterm;
+
+ adapterm = ArrayAdapter.createFromResource(activity,
+ R.array.double_tap_actions, android.R.layout.simple_spinner_item);
+
+ adapterm.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ move_spinner.setAdapter(adapterm);
+ move_spinner.setSelection(doubleTapMove);
+
+ move_spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view,
+ int pos, long id) {
+ doubleTapMove = pos;
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+
+ Spinner look_spinner = (Spinner) dialog.findViewById(R.id.look_dbl_tap_spinner);
+ ArrayAdapter adapterl = ArrayAdapter.createFromResource(activity,
+ R.array.double_tap_actions, android.R.layout.simple_spinner_item);
+ adapterl.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ look_spinner.setAdapter(adapterl);
+
+ look_spinner.setSelection(doubleTapLook);
+
+ look_spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view,
+ int pos, long id) {
+ doubleTapLook = pos;
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+
+ dialog.setOnDismissListener(new OnDismissListener() {
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ alpha = alphaSeek.getProgress();
+ fwdSens = fwdSeek.getProgress();
+ strafeSens = strafeSeek.getProgress();
+ pitchSens = pitchSeek.getProgress();
+ yawSens = yawSeek.getProgress();
+
+ mouseMode = mouseModeCheck.isChecked();
+ showWeaponCycle = showWeaponCycleCheckBox.isChecked();
+ invertLook = invertLookCheckBox.isChecked();
+ precisionShoot = precisionShootCheckBox.isChecked();
+ showSticks = showSticksCheckBox.isChecked();
+ enableWeaponWheel = enableWeaponWheelCheckBox.isChecked();
+
+ saveSettings(activity);
+ sendToQuake();
+ }
+ });
+
+ Button save = (Button)dialog.findViewById(R.id.save_button);
+ save.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ alpha = alphaSeek.getProgress();
+ fwdSens = fwdSeek.getProgress();
+ strafeSens = strafeSeek.getProgress();
+ pitchSens = pitchSeek.getProgress();
+ yawSens = yawSeek.getProgress();
+
+ mouseMode = mouseModeCheck.isChecked();
+ showWeaponCycle = showWeaponCycleCheckBox.isChecked();
+ invertLook = invertLookCheckBox.isChecked();
+ precisionShoot = precisionShootCheckBox.isChecked();
+ showSticks = showSticksCheckBox.isChecked();
+ enableWeaponWheel = enableWeaponWheelCheckBox.isChecked();
+
+ saveSettings(activity);
+ sendToQuake();
+ dialog.dismiss();
+ }
+ });
+
+ Button cancel = (Button)dialog.findViewById(R.id.cancel_button);
+ cancel.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dialog.dismiss();
+ }
+ });
+
+ dialog.show();
+ }
+ });
+
+ }
+
+
+
+ public static void sendToQuake()
+ {
+
+ int other = 0;
+ other += showWeaponCycle?0x1:0;
+ other += mouseMode?0x2:0;
+ other += invertLook?0x4:0;
+ other += precisionShoot?0x8:0;
+
+ other += (doubleTapMove << 4) & 0xF0;
+ other += (doubleTapLook << 8) & 0xF00;
+
+ other += showSticks?0x1000:0;
+ other += enableWeaponWheel?0x2000:0;
+
+ other += Settings.hideTouchControls?0x80000000:0;
+
+ quakeIf.setTouchSettings_if(
+ (float)alpha/(float)100,
+ (strafeSens)/(float)50,
+ (fwdSens)/(float)50,
+ (pitchSens)/(float)50,
+ (yawSens)/(float)50,
+ other);
+ }
+
+ public static void loadSettings(Context ctx)
+ {
+ alpha = Settings.getIntOption(ctx, "alpha", 50);
+ fwdSens = Settings.getIntOption(ctx, "fwdSens", 50);
+ strafeSens = Settings.getIntOption(ctx, "strafeSens", 50);
+ pitchSens = Settings.getIntOption(ctx, "pitchSens", 50);
+ yawSens = Settings.getIntOption(ctx, "yawSens", 50);
+
+ showWeaponCycle = Settings.getBoolOption(ctx, "show_weapon_cycle", true);
+ mouseMode = Settings.getBoolOption(ctx, "mouse_mode", true);
+ invertLook = Settings.getBoolOption(ctx, "invert_look", false);
+ precisionShoot = Settings.getBoolOption(ctx, "precision_shoot", false);
+ showSticks = Settings.getBoolOption(ctx, "show_sticks", false);
+ enableWeaponWheel = Settings.getBoolOption(ctx, "enable_ww", true);
+
+ doubleTapMove = Settings.getIntOption(ctx, "double_tap_move", 0);
+ doubleTapLook = Settings.getIntOption(ctx, "double_tap_look", 0);
+ }
+
+ public static void saveSettings(Context ctx)
+ {
+ Settings.setIntOption(ctx, "alpha", alpha);
+ Settings.setIntOption(ctx, "fwdSens", fwdSens);
+ Settings.setIntOption(ctx, "strafeSens", strafeSens);
+ Settings.setIntOption(ctx, "pitchSens", pitchSens);
+ Settings.setIntOption(ctx, "yawSens", yawSens);
+
+ Settings.setBoolOption(ctx, "show_weapon_cycle", showWeaponCycle);
+ Settings.setBoolOption(ctx, "invert_look", invertLook);
+ Settings.setBoolOption(ctx, "precision_shoot", precisionShoot);
+ Settings.setBoolOption(ctx, "show_sticks", showSticks);
+ Settings.setBoolOption(ctx, "enable_ww", enableWeaponWheel);
+
+ Settings.setIntOption(ctx, "double_tap_move", doubleTapMove);
+ Settings.setIntOption(ctx, "double_tap_look", doubleTapLook);
+
+ }
+}
diff --git a/src/org/libsdl/app/SDLActivity.java b/src/org/libsdl/app/SDLActivity.java
index 61a34c5a..f2dbff0b 100644
--- a/src/org/libsdl/app/SDLActivity.java
+++ b/src/org/libsdl/app/SDLActivity.java
@@ -1,26 +1,62 @@
-
package org.libsdl.app;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import android.app.*;
-import android.content.*;
-import android.view.*;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsoluteLayout;
-import android.os.*;
-import android.util.Log;
-import android.graphics.*;
-import android.media.*;
-import android.hardware.*;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.beloko.games.hl.NativeLib;
+import com.beloko.touchcontrols.ControlInterpreter;
+import com.beloko.touchcontrols.Settings;
+import com.beloko.touchcontrols.TouchControlsSettings;
/**
SDL Activity
@@ -31,6 +67,13 @@ public class SDLActivity extends Activity {
// Keep track of the paused state
public static boolean mIsPaused, mIsSurfaceReady, mHasFocus;
public static boolean mExitCalledFromJava;
+
+ /** If shared libraries (e.g. SDL or the native application) could not be loaded. */
+ public static boolean mBrokenLibraries;
+
+ // If we want to separate mouse and touch events.
+ // This is only toggled in native code when a hint is set!
+ public static boolean mSeparateMouseAndTouch;
// Main components
protected static SDLActivity mSingleton;
@@ -41,22 +84,50 @@ public class SDLActivity extends Activity {
// This is what SDL runs in. It invokes SDL_main(), eventually
protected static Thread mSDLThread;
-
+
// Audio
protected static AudioTrack mAudioTrack;
- // Load the .so
- static {
+ //Touch control interp
+ public static ControlInterpreter controlInterp;
- System.loadLibrary("SDL2");
- //System.loadLibrary("SDL2_image");
- //System.loadLibrary("SDL2_mixer");
- //System.loadLibrary("SDL2_net");
- //System.loadLibrary("SDL2_ttf");
- System.loadLibrary("xash");
+ /**
+ * This method is called by SDL before loading the native shared libraries.
+ * It can be overridden to provide names of shared libraries to be loaded.
+ * The default implementation returns the defaults. It never returns null.
+ * An array returned by a new implementation must at least contain "SDL2".
+ * Also keep in mind that the order the libraries are loaded may matter.
+ * @return names of shared libraries to be loaded (e.g. "SDL2", "main").
+ */
+ protected String[] getLibraries() {
+ return new String[] {
+ "SDL2",
+ // "SDL2_image",
+ // "SDL2_mixer",
+ // "SDL2_net",
+ // "SDL2_ttf",
+ "touchcontrols",
+ "xash"
+ };
}
-
-
+
+ // Load the .so
+ public void loadLibraries() {
+ for (String lib : getLibraries()) {
+ System.loadLibrary(lib);
+ }
+ }
+
+ /**
+ * This method is called by SDL before starting the native application thread.
+ * It can be overridden to provide the arguments after the application name.
+ * The default implementation returns an empty array. It never returns null.
+ * @return arguments for the native application.
+ */
+ protected String[] getArguments() {
+ return new String[0];
+ }
+
public static void initialize() {
// The static nature of the singleton and Android quirkyness force us to initialize everything here
// Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
@@ -68,6 +139,7 @@ public class SDLActivity extends Activity {
mSDLThread = null;
mAudioTrack = null;
mExitCalledFromJava = false;
+ mBrokenLibraries = false;
mIsPaused = false;
mIsSurfaceReady = false;
mHasFocus = true;
@@ -76,17 +148,54 @@ public class SDLActivity extends Activity {
// Setup
@Override
protected void onCreate(Bundle savedInstanceState) {
+ Log.v("SDL", "Device: " + android.os.Build.DEVICE);
+ Log.v("SDL", "Model: " + android.os.Build.MODEL);
Log.v("SDL", "onCreate():" + mSingleton);
super.onCreate(savedInstanceState);
-
+
SDLActivity.initialize();
-
// So we can call stuff from static callbacks
mSingleton = this;
+ // Load shared libraries
+ String errorMsgBrokenLib = "";
+ try {
+ loadLibraries();
+ } catch(UnsatisfiedLinkError e) {
+ System.err.println(e.getMessage());
+ mBrokenLibraries = true;
+ errorMsgBrokenLib = e.getMessage();
+ } catch(Exception e) {
+ System.err.println(e.getMessage());
+ mBrokenLibraries = true;
+ errorMsgBrokenLib = e.getMessage();
+ }
+
+ if (mBrokenLibraries)
+ {
+ AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
+ dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
+ + System.getProperty("line.separator")
+ + System.getProperty("line.separator")
+ + "Error: " + errorMsgBrokenLib);
+ dlgAlert.setTitle("SDL Error");
+ dlgAlert.setPositiveButton("Exit",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog,int id) {
+ // if this button is clicked, close current activity
+ SDLActivity.mSingleton.finish();
+ }
+ });
+ dlgAlert.setCancelable(false);
+ dlgAlert.create().show();
+
+ return;
+ }
+
// Set up the surface
mSurface = new SDLSurface(getApplication());
-
+
if(Build.VERSION.SDK_INT >= 12) {
mJoystickHandler = new SDLJoystickHandler_API12();
}
@@ -105,6 +214,11 @@ public class SDLActivity extends Activity {
protected void onPause() {
Log.v("SDL", "onPause()");
super.onPause();
+
+ if (SDLActivity.mBrokenLibraries) {
+ return;
+ }
+
SDLActivity.handlePause();
}
@@ -112,6 +226,11 @@ public class SDLActivity extends Activity {
protected void onResume() {
Log.v("SDL", "onResume()");
super.onResume();
+
+ if (SDLActivity.mBrokenLibraries) {
+ return;
+ }
+
SDLActivity.handleResume();
}
@@ -121,6 +240,10 @@ public class SDLActivity extends Activity {
super.onWindowFocusChanged(hasFocus);
Log.v("SDL", "onWindowFocusChanged(): " + hasFocus);
+ if (SDLActivity.mBrokenLibraries) {
+ return;
+ }
+
SDLActivity.mHasFocus = hasFocus;
if (hasFocus) {
SDLActivity.handleResume();
@@ -131,12 +254,25 @@ public class SDLActivity extends Activity {
public void onLowMemory() {
Log.v("SDL", "onLowMemory()");
super.onLowMemory();
+
+ if (SDLActivity.mBrokenLibraries) {
+ return;
+ }
+
SDLActivity.nativeLowMemory();
}
@Override
protected void onDestroy() {
Log.v("SDL", "onDestroy()");
+
+ if (SDLActivity.mBrokenLibraries) {
+ super.onDestroy();
+ // Reset everything in case the user re opens the app
+ SDLActivity.initialize();
+ return;
+ }
+
// Send a quit message to the application
SDLActivity.mExitCalledFromJava = true;
SDLActivity.nativeQuit();
@@ -152,7 +288,7 @@ public class SDLActivity extends Activity {
//Log.v("SDL", "Finished waiting for SDL thread");
}
-
+
super.onDestroy();
// Reset everything in case the user re opens the app
SDLActivity.initialize();
@@ -160,9 +296,16 @@ public class SDLActivity extends Activity {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
+
+ if (SDLActivity.mBrokenLibraries) {
+ return false;
+ }
+
int keyCode = event.getKeyCode();
// Ignore certain special keys so they're handled by Android
- if (keyCode == KeyEvent.KEYCODE_CAMERA ||
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
+ keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
+ keyCode == KeyEvent.KEYCODE_CAMERA ||
keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */
keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */
) {
@@ -191,15 +334,14 @@ public class SDLActivity extends Activity {
if (SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady && SDLActivity.mHasFocus) {
SDLActivity.mIsPaused = false;
SDLActivity.nativeResume();
- mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+ mSurface.handleResume();
}
}
-
+
/* The native thread has finished */
public static void handleNativeExit() {
SDLActivity.mSDLThread = null;
mSingleton.finish();
- System.exit(0);
}
@@ -207,6 +349,7 @@ public class SDLActivity extends Activity {
static final int COMMAND_CHANGE_TITLE = 1;
static final int COMMAND_UNUSED = 2;
static final int COMMAND_TEXTEDIT_HIDE = 3;
+ static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
protected static final int COMMAND_USER = 0x8000;
@@ -251,7 +394,18 @@ public class SDLActivity extends Activity {
imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
}
break;
-
+ case COMMAND_SET_KEEP_SCREEN_ON:
+ {
+ Window window = ((Activity) context).getWindow();
+ if (window != null) {
+ if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ } else {
+ window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+ break;
+ }
default:
if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) {
Log.e(TAG, "error handling message, command is " + msg.arg1);
@@ -272,12 +426,12 @@ public class SDLActivity extends Activity {
}
// C functions we call
- public static native void nativeInit();
+ public static native int nativeInit(Object arguments);
public static native void nativeLowMemory();
public static native void nativeQuit();
public static native void nativePause();
public static native void nativeResume();
- public static native void onNativeResize(int x, int y, int format);
+ public static native void onNativeResize(int x, int y, int format, float rate);
public static native int onNativePadDown(int device_id, int keycode);
public static native int onNativePadUp(int device_id, int keycode);
public static native void onNativeJoy(int device_id, int axis,
@@ -287,36 +441,51 @@ public class SDLActivity extends Activity {
public static native void onNativeKeyDown(int keycode);
public static native void onNativeKeyUp(int keycode);
public static native void onNativeKeyboardFocusLost();
+ public static native void onNativeMouse(int button, int action, float x, float y);
public static native void onNativeTouch(int touchDevId, int pointerFingerId,
- int action, float x,
+ int action, float x,
float y, float p);
public static native void onNativeAccel(float x, float y, float z);
public static native void onNativeSurfaceChanged();
public static native void onNativeSurfaceDestroyed();
public static native void nativeFlipBuffers();
- public static native int nativeAddJoystick(int device_id, String name,
- int is_accelerometer, int nbuttons,
+ public static native int nativeAddJoystick(int device_id, String name,
+ int is_accelerometer, int nbuttons,
int naxes, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
-
+ public static native String nativeGetHint(String name);
+
+ /**
+ * This method is called by SDL using JNI.
+ */
public static void flipBuffers() {
SDLActivity.nativeFlipBuffers();
}
+ /**
+ * This method is called by SDL using JNI.
+ */
public static boolean setActivityTitle(String title) {
// Called from SDLMain() thread and can't directly affect the view
return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
}
+ /**
+ * This method is called by SDL using JNI.
+ */
public static boolean sendMessage(int command, int param) {
return mSingleton.sendCommand(command, Integer.valueOf(param));
}
+ /**
+ * This method is called by SDL using JNI.
+ */
public static Context getContext() {
return mSingleton;
}
/**
+ * This method is called by SDL using JNI.
* @return result of getSystemService(name) but executed on UI thread.
*/
public Object getSystemServiceFromUiThread(final String name) {
@@ -382,50 +551,63 @@ public class SDLActivity extends Activity {
}
}
+ /**
+ * This method is called by SDL using JNI.
+ */
public static boolean showTextInput(int x, int y, int w, int h) {
// Transfer the task to the main thread as a Runnable
return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
}
-
+
+ /**
+ * This method is called by SDL using JNI.
+ */
public static Surface getNativeSurface() {
return SDLActivity.mSurface.getNativeSurface();
}
// Audio
+
+ /**
+ * This method is called by SDL using JNI.
+ */
public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
-
+
Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
-
+
// Let the user pick a larger buffer if they really want -- but ye
// gods they probably shouldn't, the minimums are horrifyingly high
// latency already
desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
-
+
if (mAudioTrack == null) {
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
-
+
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
-
+
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Log.e("SDL", "Failed during initialization of Audio Track");
mAudioTrack = null;
return -1;
}
-
+
mAudioTrack.play();
}
-
+
Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
-
+
return 0;
}
-
+
+ /**
+ * This method is called by SDL using JNI.
+ */
public static void audioWriteShortBuffer(short[] buffer) {
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
@@ -443,7 +625,10 @@ public class SDLActivity extends Activity {
}
}
}
-
+
+ /**
+ * This method is called by SDL using JNI.
+ */
public static void audioWriteByteBuffer(byte[] buffer) {
for (int i = 0; i < buffer.length; ) {
int result = mAudioTrack.write(buffer, i, buffer.length - i);
@@ -462,6 +647,9 @@ public class SDLActivity extends Activity {
}
}
+ /**
+ * This method is called by SDL using JNI.
+ */
public static void audioQuit() {
if (mAudioTrack != null) {
mAudioTrack.stop();
@@ -472,6 +660,7 @@ public class SDLActivity extends Activity {
// Input
/**
+ * This method is called by SDL using JNI.
* @return an array which may be empty but is never null.
*/
public static int[] inputGetInputDeviceIds(int sources) {
@@ -486,18 +675,274 @@ public class SDLActivity extends Activity {
}
return Arrays.copyOf(filtered, used);
}
-
+
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
public static boolean handleJoystickMotionEvent(MotionEvent event) {
return mJoystickHandler.handleMotionEvent(event);
}
-
+
+ /**
+ * This method is called by SDL using JNI.
+ */
public static void pollInputDevices() {
if (SDLActivity.mSDLThread != null) {
mJoystickHandler.pollInputDevices();
}
}
-
+
+ // APK extension files support
+
+ /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
+ private Object expansionFile;
+
+ /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
+ private Method expansionFileMethod;
+
+ /**
+ * This method is called by SDL using JNI.
+ */
+ public InputStream openAPKExtensionInputStream(String fileName) throws IOException {
+ // Get a ZipResourceFile representing a merger of both the main and patch files
+ if (expansionFile == null) {
+ Integer mainVersion = Integer.valueOf(nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"));
+ Integer patchVersion = Integer.valueOf(nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"));
+
+ try {
+ // To avoid direct dependency on Google APK extension library that is
+ // not a part of Android SDK we access it using reflection
+ expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
+ .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
+ .invoke(null, this, mainVersion, patchVersion);
+
+ expansionFileMethod = expansionFile.getClass()
+ .getMethod("getInputStream", String.class);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ expansionFile = null;
+ expansionFileMethod = null;
+ }
+ }
+
+ // Get an input stream for a known file inside the expansion file ZIPs
+ InputStream fileStream;
+ try {
+ fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ fileStream = null;
+ }
+
+ if (fileStream == null) {
+ throw new IOException();
+ }
+
+ return fileStream;
+ }
+
+ // Messagebox
+
+ /** Result of current messagebox. Also used for blocking the calling thread. */
+ protected final int[] messageboxSelection = new int[1];
+
+ /** Id of current dialog. */
+ protected int dialogs = 0;
+
+ /**
+ * This method is called by SDL using JNI.
+ * Shows the messagebox from UI thread and block calling thread.
+ * buttonFlags, buttonIds and buttonTexts must have same length.
+ * @param buttonFlags array containing flags for every button.
+ * @param buttonIds array containing id for every button.
+ * @param buttonTexts array containing text for every button.
+ * @param colors null for default or array of length 5 containing colors.
+ * @return button id or -1.
+ */
+ public int messageboxShowMessageBox(
+ final int flags,
+ final String title,
+ final String message,
+ final int[] buttonFlags,
+ final int[] buttonIds,
+ final String[] buttonTexts,
+ final int[] colors) {
+
+ messageboxSelection[0] = -1;
+
+ // sanity checks
+
+ if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) {
+ return -1; // implementation broken
+ }
+
+ // collect arguments for Dialog
+
+ final Bundle args = new Bundle();
+ args.putInt("flags", flags);
+ args.putString("title", title);
+ args.putString("message", message);
+ args.putIntArray("buttonFlags", buttonFlags);
+ args.putIntArray("buttonIds", buttonIds);
+ args.putStringArray("buttonTexts", buttonTexts);
+ args.putIntArray("colors", colors);
+
+ // trigger Dialog creation on UI thread
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ showDialog(dialogs++, args);
+ }
+ });
+
+ // block the calling thread
+
+ synchronized (messageboxSelection) {
+ try {
+ messageboxSelection.wait();
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ return -1;
+ }
+ }
+
+ // return selected value
+
+ return messageboxSelection[0];
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int ignore, Bundle args) {
+
+ // TODO set values from "flags" to messagebox dialog
+
+ // get colors
+
+ int[] colors = args.getIntArray("colors");
+ int backgroundColor;
+ int textColor;
+ int buttonBorderColor;
+ int buttonBackgroundColor;
+ int buttonSelectedColor;
+ if (colors != null) {
+ int i = -1;
+ backgroundColor = colors[++i];
+ textColor = colors[++i];
+ buttonBorderColor = colors[++i];
+ buttonBackgroundColor = colors[++i];
+ buttonSelectedColor = colors[++i];
+ } else {
+ backgroundColor = Color.TRANSPARENT;
+ textColor = Color.TRANSPARENT;
+ buttonBorderColor = Color.TRANSPARENT;
+ buttonBackgroundColor = Color.TRANSPARENT;
+ buttonSelectedColor = Color.TRANSPARENT;
+ }
+
+ // create dialog with title and a listener to wake up calling thread
+
+ final Dialog dialog = new Dialog(this);
+ dialog.setTitle(args.getString("title"));
+ dialog.setCancelable(false);
+ dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface unused) {
+ synchronized (messageboxSelection) {
+ messageboxSelection.notify();
+ }
+ }
+ });
+
+ // create text
+
+ TextView message = new TextView(this);
+ message.setGravity(Gravity.CENTER);
+ message.setText(args.getString("message"));
+ if (textColor != Color.TRANSPARENT) {
+ message.setTextColor(textColor);
+ }
+
+ // create buttons
+
+ int[] buttonFlags = args.getIntArray("buttonFlags");
+ int[] buttonIds = args.getIntArray("buttonIds");
+ String[] buttonTexts = args.getStringArray("buttonTexts");
+
+ final SparseArray