Hi Greg,
I'll have to see about what I have to do to submit joypad inputs instead. I can see where you are coming from though - the quests can request any number of keys, whereas the joypad should only have a consistent and limited number of buttons.
I also made two improvements since yesterday:
1. hold the sword button , then move didn't move - now it does.
2. For return of the Hylian SE, at the very beginning, Link has to move fast ( with help of 'shift' key ) before the sword is obtained - otherwise, he is surrounded and can't escape the monsters.
- Karl
I'll have to see about what I have to do to submit joypad inputs instead. I can see where you are coming from though - the quests can request any number of keys, whereas the joypad should only have a consistent and limited number of buttons.
I also made two improvements since yesterday:
1. hold the sword button , then move didn't move - now it does.
2. For return of the Hylian SE, at the very beginning, Link has to move fast ( with help of 'shift' key ) before the sword is obtained - otherwise, he is surrounded and can't escape the monsters.
- Karl
Code Select
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4a57f67..7a2454a 100644
--- 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..b484518 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,100 @@ 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.widget.Toast;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
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_ESCAPE;
+import static android.view.KeyEvent.KEYCODE_SHIFT_LEFT;
+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 +114,73 @@ public class SolarusEngine extends SDLActivity {
|| "google_sdk".equals(Build.PRODUCT);
}
+ protected int apply_key_delta(int dx,int newdx,boolean hor, boolean repeat)
+ {
+ if (Math.signum(dx) == Math.signum(newdx)) { // arrow repeat for intro screen
+ if (repeat) { // don't make it too finiky. Moving the hero doesn't rely on repeat anyway.
+ if (abs(dx) == 1) {
+ SDLActivity.onNativeKeyDown( KEYCODE_SHIFT_LEFT );
+ dx += newdx;
+ } else {
+ 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 (abs(dx) > 1) { // first remove the 'fast' key
+ SDLActivity.onNativeKeyUp( KEYCODE_SHIFT_LEFT );
+ }
+ if ( (abs(dx) == 1)||(abs(newdx) > 1) ) {
+ 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 dx+newdx;
+ }
+ }
+
+ 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 +208,121 @@ 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
+
+ // don't do anything with our floating action buttons or surrounds.
+ // because then we'd get touch-down events that were actually for the FAB.
+ // - but that is handled in onTouch now...
+ onTouch(event);
+ return super.dispatchTouchEvent(event);
+ }
+
+
+ public boolean onTouch(MotionEvent event) {
+ View sv = mSurface;
+ int width = sv.getWidth();
+ int height = sv.getHeight();
+ int minw = width > height ? height:width;
+
+ 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();
+ int fullaction = event.getAction();
+ 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_POINTER_UP:
+ case MotionEvent.ACTION_UP:
+ {
+ int finger = event.findPointerIndex(pointerid);
+ if (finger >= 0) {
+ final int mypointerId = event.getPointerId(finger);
+ if (mypointerId == 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)&&(event.getY(finger) < minw/4)) { // just a tap. Anoying otherwise.
+ if (event.getX(finger) < minw/4) {
+ SDLActivity.onNativeKeyDown(KEYCODE_ESCAPE);
+ SDLActivity.onNativeKeyUp(KEYCODE_ESCAPE);
+ } else {
+ SDLActivity.onNativeKeyDown(KEYCODE_D);
+ SDLActivity.onNativeKeyUp(KEYCODE_D);
+ }
+ }
+ }
+ movecount = 0;
+ }
+ }
+ }
+ break;
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ {
+ final int mypointerIndex = (fullaction & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int mypointerId = event.getPointerId(mypointerIndex);
+ if (event.getX(mypointerIndex) < width-minw/4) { // else reserved for FABs
+ if (movecount == 0) { // e.g. the sword was activated first
+ pointerid = mypointerId;
+ 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>