Hi Greg - I didn't "simply forget" because I never knew that requirement in the first place.
Getting the quest to work means that touch events have to be translated into keys for hero actions and movements.
I decided that movement should be just moving your finger on the display, a tap brings up the menu ( i.e. generates 'd' ), and have some floating action buttons for the rest ( space,'c','x','v' ).
Floating action buttons go against the fullscreen schema it seems ( maybe not - it's my first time to do anything with Android programming whatsoever ), so I changed the fullscreen theme. There still is that title-bar - maybe some real expert can fix it.
Then I can play mercuris chess. And maybe get past the 'lighting 5 lamps' in Solarus dx if I can tweak the movement to be intuitive enough.
It would be really cool to have some interaction between the engine and the android interface, so that e.g. the 'x' FAB shows the 'cat food' icon when I select 'cat food', but at least I can play without keyboard now.
- Karl
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -45,7 +45,7 @@
<activity android:name=".SolarusEngine"
android:configChanges="orientation|screenSize"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+ android:theme="@style/AppFullScreenTheme">
</activity>
<activity
android:name=".Solarus"
diff --git a/app/src/main/java/org/solarus_games/solarus/SolarusEngine.java b/app/src/main/java/org/solarus_games/solarus/SolarusEngine.java
index 4fa3d91..ca46037 100644
--- a/app/src/main/java/org/solarus_games/solarus/SolarusEngine.java
+++ b/app/src/main/java/org/solarus_games/solarus/SolarusEngine.java
@@ -2,29 +2,99 @@ package org.solarus_games.solarus;
import android.app.Activity;
import android.content.Intent;
+import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.support.design.widget.FloatingActionButton;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.PixelCopy;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
import android.widget.Toast;
import org.libsdl.app.SDLActivity;
+import static android.view.KeyEvent.KEYCODE_C;
+import static android.view.KeyEvent.KEYCODE_D;
+import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
+import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
+import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
+import static android.view.KeyEvent.KEYCODE_DPAD_UP;
+import static android.view.KeyEvent.KEYCODE_SPACE;
+import static android.view.KeyEvent.KEYCODE_V;
+import static android.view.KeyEvent.KEYCODE_X;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static java.lang.Math.abs;
+
public class SolarusEngine extends SDLActivity {
public Quest quest;
final private String TAG = "SolarusEngine";
public Bitmap screenCapture;
+
+ protected static float touch_xy[]; // last tracked position of touch
+ protected static int g_delta[]; // computed binary delta for simulated cursor key press
+ protected static int pointerid;
+ protected static int movecount; // number of events in this touch sequence
+ protected static long move_time;
+
+
+ FloatingActionButton createButton(final int keycode, int offset, int color, final int imageId)
+ {
+ FloatingActionButton floatingActionButton1 = new FloatingActionButton(this);
+ RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ layoutParams.setMargins(32, 32, 32, 32 + 128*offset);
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,RelativeLayout.TRUE);
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,RelativeLayout.TRUE);
+
+ floatingActionButton1.setLayoutParams(layoutParams);
+ floatingActionButton1.setBackgroundTintList(ColorStateList.valueOf(color));
+ floatingActionButton1.setImageResource(imageId);
+ floatingActionButton1.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ int action = event.getActionMasked();
+ switch(action) {
+ case MotionEvent.ACTION_UP:
+ SDLActivity.onNativeKeyUp(keycode);
+ break;
+ case MotionEvent.ACTION_DOWN:
+ SDLActivity.onNativeKeyDown(keycode);
+ break;
+ }
+ return true;
+ }
+ });
+
+ mLayout.addView(floatingActionButton1);
+ return floatingActionButton1;
+ }
+
+
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
quest = Quest.fromPath(getIntent().getExtras().getString("quest_path"));
SolarusApp.setCurrentQuest(this);
+ {
+ FloatingActionButton floatingActionButton1 = createButton(KEYCODE_SPACE,0, Color.parseColor("#00c0ff"),
+ android.R.drawable.ic_menu_agenda);
+ FloatingActionButton floatingActionButton2 = createButton(KEYCODE_C,1,Color.parseColor("#90ff90"),
+ android.R.drawable.ic_menu_compass);
+ FloatingActionButton floatingActionButton3 = createButton(KEYCODE_X,2,Color.parseColor("#999999"),
+ android.R.drawable.ic_menu_mylocation);
+ FloatingActionButton floatingActionButton4 = createButton(KEYCODE_V,3,Color.parseColor("#999999"),
+ android.R.drawable.ic_menu_add);
+ }
}
@Override
@@ -43,6 +113,63 @@ public class SolarusEngine extends SDLActivity {
|| "google_sdk".equals(Build.PRODUCT);
}
+ protected int apply_key_delta(int dx,int newdx,boolean hor, boolean repeat)
+ {
+ if (dx == newdx) { // arrow repeat for intro screen
+ if (repeat) { // don't make it too finiky. Moving the hero doesn't rely on repeat anyway.
+ if (newdx < 0) {
+ SDLActivity.onNativeKeyUp( hor ? KEYCODE_DPAD_LEFT : KEYCODE_DPAD_UP);
+ SDLActivity.onNativeKeyDown( hor ? KEYCODE_DPAD_LEFT : KEYCODE_DPAD_UP);
+ } else if (newdx > 0) {
+ SDLActivity.onNativeKeyUp( hor ? KEYCODE_DPAD_RIGHT : KEYCODE_DPAD_DOWN);
+ SDLActivity.onNativeKeyDown( hor ? KEYCODE_DPAD_RIGHT : KEYCODE_DPAD_DOWN);
+ }
+ }
+ return dx;
+ }
+ if (newdx == 0) { return dx; }
+ if (dx == 0) {
+ if (newdx < 0) {
+ movecount += 1;
+ SDLActivity.onNativeKeyDown( hor ? KEYCODE_DPAD_LEFT : KEYCODE_DPAD_UP);
+ } else if (newdx > 0) {
+ movecount += 1;
+ SDLActivity.onNativeKeyDown( hor ? KEYCODE_DPAD_RIGHT : KEYCODE_DPAD_DOWN);
+ }
+ return newdx;
+ } else { // dx,newdx are opposite
+ if (dx < 0) {
+ SDLActivity.onNativeKeyUp( hor ? KEYCODE_DPAD_LEFT : KEYCODE_DPAD_UP);
+ } else if (dx > 0) {
+ SDLActivity.onNativeKeyUp( hor ? KEYCODE_DPAD_RIGHT : KEYCODE_DPAD_DOWN);
+ }
+ return 0;
+ }
+ }
+
+ protected int[] GetBinaryDelta(float[] mdelta)
+ {
+ int res[] = new int[2];
+ final int limit = 5;
+ res[0] = 0;
+ res[1] = 0;
+ if ( (abs(mdelta[0]) < limit)&& (abs(mdelta[1]) < limit) ) {
+ return res;
+ }
+ if (abs(mdelta[0]) < 0.8 * abs(mdelta[1])){
+ res[0] = 0;
+ res[1] = mdelta[1] > 0 ? 1:-1;
+ } else if (abs(mdelta[1]) < (0.8 * abs(mdelta[0]))) {
+ res[0] = mdelta[0] > 0 ? 1:-1;
+ res[1] = 0;
+ } else {
+ res[0] = mdelta[0] > 0 ? 1:-1;
+ res[1] = mdelta[1] > 0 ? 1:-1;
+ }
+ return res;
+ }
+
+
private void bringMainActivityToFront() {
Intent i = new Intent(getApplicationContext(), Solarus.class);
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
@@ -70,6 +197,120 @@ public class SolarusEngine extends SDLActivity {
return super.dispatchKeyEvent(event);
}
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ // turn certain touch events into keys so hero can move
+ View sv = mSurface;
+ int width = sv.getWidth();
+ int height = sv.getHeight();
+ int minw = width > height ? height:width;
+ if (event.getX() < width-minw/4) { // don't do anything with our floating action buttons or surrounds.
+ onTouch(event);
+ }
+ return super.dispatchTouchEvent(event);
+ }
+
+
+ public boolean onTouch(MotionEvent event) {
+ if (touch_xy == null) {
+ touch_xy = new float[2];
+ }
+ if (g_delta == null) {
+ g_delta = new int[2];
+ }
+ // should translate to keystrokes in sensible manner.
+ int action = event.getActionMasked();
+ switch(action) {
+ case ACTION_MOVE:
+ if (movecount > 0) {
+ int finger = event.findPointerIndex(pointerid);
+ float delta[] = new float[2];
+ delta[0] = event.getX(finger) - touch_xy[0];
+ delta[1] = event.getY(finger) - touch_xy[1];
+ int ndelta[] = GetBinaryDelta(delta);
+ long new_time = event.getEventTime();
+ long timedelta = new_time-move_time;
+ boolean dorepeat = false;
+ boolean largemove = (abs(delta[0])+abs(delta[1]) > 50);
+ boolean smallmove = (abs(delta[0])+abs(delta[1]) > 10);
+ if ((timedelta > 200)|| largemove) { // restart
+ dorepeat = true;
+ move_time = new_time;
+ }
+ if (smallmove || dorepeat) {
+ g_delta[0] = apply_key_delta(g_delta[0], ndelta[0], true, dorepeat);
+ g_delta[1] = apply_key_delta(g_delta[1], ndelta[1], false, dorepeat);
+ if ((ndelta[0] != 0)||(ndelta[1] != 0)) { // otherwise not moved enough.
+ touch_xy[0] = event.getX(finger);
+ touch_xy[1] = event.getY(finger);
+ }
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ {
+ int finger = event.findPointerIndex(pointerid);
+ float delta[] = new float[2];
+ delta[0] = event.getX(finger) - touch_xy[0];
+ delta[1] = event.getY(finger) - touch_xy[1];
+ if ((delta[0] > 1) || (delta[1] > 1)) {
+ int ndelta[] = GetBinaryDelta(delta);
+ g_delta[0] = apply_key_delta(g_delta[0], ndelta[0], true , true);
+ g_delta[1] = apply_key_delta(g_delta[1], ndelta[1], false, true);
+ }
+
+ // up no move, if move first should apply that.
+ g_delta[0] = apply_key_delta(g_delta[0], -g_delta[0], true, true);
+ g_delta[1] = apply_key_delta(g_delta[1], -g_delta[1], false, true);
+ if (movecount == 1) {
+ long new_time = event.getEventTime();
+ long timedelta = new_time-move_time;
+ if (timedelta < 200) { // just a tap. Anoying otherwise.
+ SDLActivity.onNativeKeyDown(KEYCODE_D);
+ SDLActivity.onNativeKeyUp(KEYCODE_D);
+ }
+ }
+ movecount = 0;
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ {
+ final int mypointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int mypointerId = event.getPointerId(mypointerIndex);
+ if (mypointerId != pointerid){ // emit 'menu' keycode
+ SDLActivity.onNativeKeyDown(KEYCODE_D);
+ SDLActivity.onNativeKeyUp(KEYCODE_D);
+ }
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ {
+ final int mypointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int mypointerId = event.getPointerId(mypointerIndex);
+ if (mypointerId == pointerid){
+ // the main one going up !
+ // some other finger is still resting on the surface.
+ g_delta[0] = apply_key_delta(g_delta[0], -g_delta[0], true, true);
+ g_delta[1] = apply_key_delta(g_delta[1], -g_delta[1], false, true);
+ movecount = 0;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_DOWN:
+ pointerid = event.getPointerId(0); // down is the first finger down
+ move_time = event.getEventTime();
+
+ touch_xy[0] = event.getX(0);
+ touch_xy[1] = event.getY(0);
+ g_delta[0] = 0;
+ g_delta[1] = 0;
+ movecount = 1; // if new pointerID == pointerid.
+ break;
+ }
+ return true;
+ }
+
+
public void exit() {
SDLActivity.mExitCalledFromJava = true;
SDLActivity.nativeQuit();
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index c322a03..24b25c2 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -17,6 +17,13 @@
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
+ <style name="AppFullScreenTheme" parent="Theme.AppCompat.Light.NoActionBar">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
<!-- Texts -->
<style name="paragraph">
<item name="android:textSize">@dimen/text_size</item>