fteqw/engine/droid/src/com/fteqw/FTENativeActivity.java

480 lines
16 KiB
Java

package com.fteqw;
import android.view.inputmethod.InputMethodManager;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.InputDevice;
import android.view.WindowManager;
public class FTENativeActivity extends android.app.Activity implements android.view.SurfaceHolder.Callback2, android.view.ViewTreeObserver.OnGlobalLayoutListener
{
//Native functions and stuff
private native boolean startup(String externalDataPath, String libraryPath);
private native void openfile(String url);
private native void surfacechange(boolean teardown, boolean restart, android.view.SurfaceHolder holder, android.view.Surface surface);
private native void shutdown();
private static native void keypress(int devid, boolean down, int androidkey, int unicode);
private static native void mousepress(int devid, int buttonbits);
private static native void motion(int devid, int action, float x, float y, float z, float size);
private static native boolean wantrelative();
private static native void axis(int devid, int axisid, float value);
// private static native void oncreate(String bindir, String basedir, byte[] savedstate);
static
{
System.loadLibrary("ftedroid"); //registers the methods properly.
}
static class NativeContentView extends android.view.View
{
FTENativeActivity mActivity;
public NativeContentView(android.content.Context context)
{
super(context);
}
public NativeContentView(android.content.Context context, android.util.AttributeSet attrs)
{
super(context, attrs);
}
}
private NativeContentView mNativeContentView;
//SurfaceHolder.Callback2 methods
public void surfaceRedrawNeeded(android.view.SurfaceHolder holder)
{ //we constantly redraw.
}
public void surfaceCreated(android.view.SurfaceHolder holder)
{
// surfacechange(false, true, holder.getSurface());
}
public void surfaceChanged(android.view.SurfaceHolder holder, int format, int width, int height)
{
surfacechange(true, true, holder, holder.getSurface());
}
public void surfaceDestroyed(android.view.SurfaceHolder holder)
{
surfacechange(true, false, null, null);
}
//OnGlobalLayoutListener methods
public void onGlobalLayout()
{
/* mNativeContentView.getLocationInWindow(mLocation);
int w = mNativeContentView.getWidth();
int h = mNativeContentView.getHeight();
if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY || w != mLastContentWidth || h != mLastContentHeight)
{
mLastContentX = mLocation[0];
mLastContentY = mLocation[1];
mLastContentWidth = w;
mLastContentHeight = h;
if (!mDestroyed) {
onContentRectChangedNative(mNativeHandle, mLastContentX,
mLastContentY, mLastContentWidth, mLastContentHeight);
}
}
*/ }
//Activity methods
@Override
protected void onCreate(android.os.Bundle savedInstanceState)
{
getWindow().takeSurface(this);
getWindow().setFormat(android.graphics.PixelFormat.RGB_565);
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
mNativeContentView = new NativeContentView(this);
mNativeContentView.mActivity = this;
setContentView(mNativeContentView);
mNativeContentView.requestFocus();
mNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this);
// byte[] nativeSavedState = savedInstanceState != null
// ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;
startup(getAbsolutePath(getExternalFilesDir(null)), getNativeLibraryDirectory());
handleIntent(getIntent());
// if (Build.VERSION.SDK_INT >= 19)
// {
// int flags = 0;
// flags |= 4096/*SYSTEM_UI_FLAG_IMMERSIVE_STICKY, api 19*/;
// flags |= 4/*SYSTEM_UI_FLAG_FULLSCREEN, api 16*/;
// flags |= 2/*SYSTEM_UI_FLAG_HIDE_NAVIGATION, api 14*/;
// mNativeContentView.setSystemUiVisibility(flags); /*api 11*/
// }
super.onCreate(savedInstanceState);
}
//random helpers
private void handleIntent(android.content.Intent intent)
{
String s = intent.getScheme();
if (s=="content")
{
android.database.Cursor cursor = this.getContentResolver().query(intent.getData(), null, null, null, null);
cursor.moveToFirst();
String myloc = cursor.getString(0);
cursor.close();
}
else
openfile(intent.getDataString());
}
private static String getAbsolutePath(java.io.File file)
{
return (file != null) ? file.getAbsolutePath() : null;
}
public String getNativeLibraryDirectory()
{
android.content.Context context = getApplicationContext();
int sdk_level = android.os.Build.VERSION.SDK_INT;
if (sdk_level >= android.os.Build.VERSION_CODES.GINGERBREAD)
{
try
{
String secondary = (String) android.content.pm.ApplicationInfo.class.getField("nativeLibraryDir").get(context.getApplicationInfo());
return secondary;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
if (sdk_level >= android.os.Build.VERSION_CODES.DONUT)
return context.getApplicationInfo().dataDir + "/lib";
return "/data/data/" + context.getPackageName() + "/lib";
}
//called by C code on errors / quitting.
public void showMessageAndQuit(String errormessage)
{
final android.app.Activity act = this;
final String errormsg = errormessage;
if (errormsg.equals(""))
{ //just quit
finish();
System.exit(0);
}
else runOnUiThread(new Runnable()
{ //show an error message, then quit.
public void run()
{
// act.getView().setVisibility(android.view.View.GONE);
android.app.AlertDialog ad = new android.app.AlertDialog.Builder(act).create();
ad.setTitle("Fatal Error");
ad.setMessage(errormsg);
ad.setCancelable(false);
ad.setButton("Ok", new android.content.DialogInterface.OnClickListener()
{
public void onClick(android.content.DialogInterface dialog, int which)
{
finish();
System.exit(0);
}
});
ad.show();
}
});
}
public void updateScreenKeepOn(final boolean keepon)
{
final android.app.Activity act = this;
runOnUiThread(new Runnable()
{
public void run()
{
if (keepon)
act.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
else
act.getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
});
}
//called by C code to set orientation.
public void updateOrientation(String orientation)
{
final String ors = orientation;
runOnUiThread(new Runnable()
{
public void run()
{
int ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
if (ors.equalsIgnoreCase("unspecified"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
else if (ors.equalsIgnoreCase("landscape"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
else if (ors.equalsIgnoreCase("portrait"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
else if (ors.equalsIgnoreCase("user"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
else if (ors.equalsIgnoreCase("behind"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
else if (ors.equalsIgnoreCase("sensor"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
else if (ors.equalsIgnoreCase("nosensor"))
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
//the following are api level 9+
else if (ors.equalsIgnoreCase("sensorlandscape"))
ori = 6;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
else if (ors.equalsIgnoreCase("sensorportrait"))
ori = 7;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
else if (ors.equalsIgnoreCase("reverselandscape"))
ori = 8;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
else if (ors.equalsIgnoreCase("reverseportrait"))
ori = 9;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
else if (ors.equalsIgnoreCase("fullsensor"))
ori = 10;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
//and the default, because specifying it again is always useless.
else
ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
android.util.Log.i("FTEDroid", "Orientation changed to " + ori + " (" + ors + ").");
setRequestedOrientation(ori);
}
});
};
//keyboard stuff, called from C.
public void showKeyboard(int softkeyflags)
{ //needed because the ndk's ANativeActivity_showSoftInput is defective
final android.app.Activity act = this;
final int flags = softkeyflags;
runOnUiThread(new Runnable()
{
public void run()
{
if (flags != 0)
{
InputMethodManager imm = (InputMethodManager)getSystemService(android.content.Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(act.getWindow().getDecorView(), InputMethodManager.SHOW_FORCED);
}
else
{
InputMethodManager imm = (InputMethodManager)getSystemService(android.content.Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(act.getWindow().getDecorView().getWindowToken(), 0);
}
}
});
}
@Override
public boolean dispatchKeyEvent(KeyEvent event)
{ //needed because AKeyEvent_getUnicode is missing completely.
int act = event.getAction();
if (act == KeyEvent.ACTION_DOWN)
{
int metastate = event.getMetaState();
int unichar = event.getUnicodeChar(metastate);
if (unichar == 0)
unichar = event.getUnicodeChar();
if (unichar == 0)
unichar = event.getDisplayLabel();
keypress(event.getDeviceId(), true, event.getKeyCode(), unichar);
return true;
}
else if (act == KeyEvent.ACTION_UP)
{
keypress(event.getDeviceId(), false, event.getKeyCode(), 0);
return true;
}
else
android.util.Log.i("FTEDroid", "other type of event");
//ignore ACTION_MULTIPLE or whatever it is, apparently its deprecated anyway.
return super.dispatchKeyEvent(event);
}
private static boolean canrelative;
private static int AXIS_RELATIVE_X;//MotionEvent 24
private static int AXIS_RELATIVE_Y;//MotionEvent 24
private static java.lang.reflect.Method MotionEvent_getAxisValueP; //MotionEvent 12
private static int SOURCE_MOUSE = 0x00002002; //InputDevice 9
//private static int SOURCE_STYLUS = 0x00004002; //InputDevice 14
//private static int SOURCE_STYLUS = 0x00004002; //InputDevice 14
private static int SOURCE_MOUSE_RELATIVE = 0x00020004; //InputDevice 26
private static boolean canbuttons;
private static java.lang.reflect.Method MotionEvent_getButtonState; //MotionEvent 14
private static boolean canjoystick;
private static java.lang.reflect.Method MotionEvent_getAxisValueJ;
private static java.lang.reflect.Method InputDevice_getMotionRange;
private static int SOURCE_JOYSTICK = 0x01000010; //InputDevice 12
private static int SOURCE_GAMEPAD = 0x00000401; //InputDevice 12
private static int AXIS_X;
private static int AXIS_Y;
private static int AXIS_LTRIGGER;
private static int AXIS_Z;
private static int AXIS_RZ;
private static int AXIS_RTRIGGER;
static
{
//if (android.os.Build.VERSION.SDK_INT >= 12)
try
{
MotionEvent_getAxisValueP = MotionEvent.class.getMethod("getAxisValue", int.class, int.class); //api12
java.lang.reflect.Field relX = MotionEvent.class.getField("AXIS_RELATIVE_X"); //api24ish
java.lang.reflect.Field relY = MotionEvent.class.getField("AXIS_RELATIVE_Y"); //api24ish
AXIS_RELATIVE_X = (Integer)relX.get(null);
AXIS_RELATIVE_Y = (Integer)relY.get(null);
// SOURCE_MOUSE = (Integer)InputDevice.class.getField("SOURCE_MOUSE").get(null);
// SOURCE_MOUSE_RELATIVE = (Integer)InputDevice.class.getField("SOURCE_MOUSE_RELATIVE").get(null);
canrelative = true; //yay, no exceptions.
android.util.Log.i("FTEDroid", "relative mouse supported");
MotionEvent_getButtonState = MotionEvent.class.getMethod("getButtonState"); //api14ish
canbuttons = true;
android.util.Log.i("FTEDroid", "mouse buttons supported");
} catch(Exception e) {
canrelative = false;
android.util.Log.i("FTEDroid", "relative mouse not supported");
}
try
{
MotionEvent_getAxisValueJ = MotionEvent.class.getMethod("getAxisValue", int.class); //api12
InputDevice_getMotionRange = InputDevice.class.getMethod("getMotionRange", int.class); //api12
AXIS_X = (Integer)MotionEvent.class.getField("AXIS_X").get(null);
AXIS_Y = (Integer)MotionEvent.class.getField("AXIS_Y").get(null);
AXIS_LTRIGGER = (Integer)MotionEvent.class.getField("AXIS_LTRIGGER").get(null);
AXIS_Z = (Integer)MotionEvent.class.getField("AXIS_Z").get(null);
AXIS_RZ = (Integer)MotionEvent.class.getField("AXIS_RZ").get(null);
AXIS_RTRIGGER = (Integer)MotionEvent.class.getField("AXIS_RTRIGGER").get(null);
// SOURCE_JOYSTICK = (Integer)InputDevice.class.getField("SOURCE_JOYSTICK").get(null);
// SOURCE_GAMEPAD = (Integer)InputDevice.class.getField("SOURCE_GAMEPAD").get(null);
canjoystick = true;
android.util.Log.i("FTEDroid", "gamepad supported");
} catch(Exception e) {
canjoystick = false;
android.util.Log.i("FTEDroid", "gamepad not supported");
}
}
private static void handleJoystickAxis(MotionEvent event, InputDevice dev, int aaxis, int qaxis)
{
try
{
final InputDevice.MotionRange range = (InputDevice.MotionRange)InputDevice_getMotionRange.invoke(dev, aaxis, event.getSource());
if (range != null)
{
final float flat = range.getFlat();
float v = (Float)MotionEvent_getAxisValueJ.invoke(event, aaxis, 0);
if (Math.abs(v) < flat)
v = 0; //read as 0 if its within the deadzone.
axis(event.getDeviceId(), qaxis, v);
}
}
catch(Exception e)
{
}
}
private boolean motionEvent(MotionEvent event)
{
int id;
float x, y, size;
final int act = event.getAction();
final int src = event.getSource();
//handle gamepad axis
if ((event.getSource() & (SOURCE_GAMEPAD|SOURCE_JOYSTICK))!=0 && event.getAction() == MotionEvent.ACTION_MOVE)
{
InputDevice dev = event.getDevice();
handleJoystickAxis(event, dev, AXIS_X, 0);
handleJoystickAxis(event, dev, AXIS_Y, 1);
handleJoystickAxis(event, dev, AXIS_LTRIGGER, 2);
handleJoystickAxis(event, dev, AXIS_Z, 3);
handleJoystickAxis(event, dev, AXIS_RZ, 4);
handleJoystickAxis(event, dev, AXIS_RTRIGGER, 5);
return true;
}
final int pointerCount = event.getPointerCount();
int i;
for (i = 0; i < pointerCount; i++)
{
if (canrelative && src == SOURCE_MOUSE && wantrelative())
{
try
{
x = (Float)MotionEvent_getAxisValueP.invoke(event, AXIS_RELATIVE_X, i);
y = (Float)MotionEvent_getAxisValueP.invoke(event, AXIS_RELATIVE_Y, i);
motion(event.getPointerId(i), 1, x, y, 0, event.getSize(i));
}
catch(Exception e)
{
android.util.Log.i("FTEDroid", "exception using relative mouse");
canrelative=false;
}
}
else
{
motion(event.getPointerId(i), 0, event.getX(i), event.getY(i), 0, event.getSize(i));
}
}
switch(act & event.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
id = ((act&event.ACTION_POINTER_ID_MASK) >> event.ACTION_POINTER_ID_SHIFT);
x = event.getX(id);
y = event.getY(id);
size = event.getSize(id);
id = event.getPointerId(id);
if (canbuttons && src == SOURCE_MOUSE)
{
try {mousepress(id, (Integer)MotionEvent_getButtonState.invoke(event));}
catch(Exception e){}
}
else
motion(id, 2, x, y, 0, size);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
id = ((act&event.ACTION_POINTER_ID_MASK) >> event.ACTION_POINTER_ID_SHIFT);
x = event.getX(id);
y = event.getY(id);
size = event.getSize(id);
id = event.getPointerId(id);
if (canbuttons && event.getSource() == SOURCE_MOUSE)
{
try {mousepress(id, (Integer)MotionEvent_getButtonState.invoke(event));}
catch(Exception e){}
}
else
motion(id, 3, x, y, 0, size);
break;
case MotionEvent.ACTION_MOVE:
break;
default:
return false;
}
return true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event)
{ //works when mouse is pressed...
return motionEvent(event);
}
// @Override
public boolean dispatchGenericMotionEvent(MotionEvent event)
{ //works even when mouse is not pressed
return motionEvent(event);
}
//launching stuff
private static native int unicodeKeyPress(int unicode);
@Override
protected void onNewIntent(android.content.Intent intent)
{
handleIntent(intent);
super.onNewIntent(intent);
}
}