Android : use GestureDetector instead of raw touch handling

- Also simplifies general settings (now you can swipe to change emulation speed)
This commit is contained in:
Aaron Culliney 2015-03-14 15:42:02 -07:00
parent 598de8cb70
commit 51617fa620
6 changed files with 213 additions and 234 deletions

View File

@ -21,6 +21,7 @@ import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewTreeObserver;
@ -39,6 +40,10 @@ public class Apple2Activity extends Activity {
private Apple2View mView = null;
private AlertDialog mQuitDialog = null;
private GestureDetector mDetector = null;
private boolean mSwipeTogglesSpeed = true;
private boolean mDoubleTapShowsKeyboard = true;
private int mWidth = 0;
private int mHeight = 0;
private boolean mSoftKeyboardShowing = false;
@ -48,15 +53,19 @@ public class Apple2Activity extends Activity {
}
private native void nativeOnCreate(String dataDir);
private native void nativeGraphicsInitialized(int width, int height);
private native void nativeGraphicsChanged(int width, int height);
private native void nativeOnKeyDown(int keyCode, int metaState);
private native void nativeOnKeyUp(int keyCode, int metaState);
private native void nativeIncreaseCPUSpeed();
private native void nativeDecreaseCPUSpeed();
public native void nativeOnResume();
public native void nativeOnPause();
public native void nativeOnQuit();
public native boolean nativeOnTouch(int action, float x, float y);
public native void nativeReboot();
private native void nativeGraphicsInitialized(int width, int height);
private native void nativeGraphicsChanged(int width, int height);
public native void nativeRender();
private native void nativeOnKeyDown(int keyCode, int metaState);
private native void nativeOnKeyUp(int keyCode, int metaState);
// HACK NOTE 2015/02/22 : Apparently native code cannot easily access stuff in the APK ... so copy various resources
// out of the APK and into the /data/data/... for ease of access. Because this is FOSS software we don't care about
@ -146,11 +155,14 @@ public class Apple2Activity extends Activity {
Log.d(TAG, "Soft keyboard appears to be occupying screen real estate ...");
Apple2Activity.this.mSoftKeyboardShowing = true;
} else {
Log.d(TAG, "Soft keyboard appears to be gone ...");
Apple2Activity.this.mSoftKeyboardShowing = false;
}
nativeGraphicsChanged(rect.width(), h);
}
});
mDetector = new GestureDetector(this, new Apple2GestureListener());
}
@Override
@ -219,7 +231,7 @@ public class Apple2Activity extends Activity {
mView.showMainMenu();
return true;
} else if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) || (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) || (keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
return false;
return super.onKeyUp(keyCode, event);
} else {
nativeOnKeyUp(keyCode, event.getMetaState());
return true;
@ -228,8 +240,83 @@ public class Apple2Activity extends Activity {
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent...");
return true;
do {
Apple2MainMenu mainMenu = mView.getMainMenu();
if (mainMenu == null) {
break;
}
Apple2SettingsMenu settingsMenu = mainMenu.getSettingsMenu();
if (settingsMenu != null && settingsMenu.isShowing()) {
break;
}
int action = event.getActionMasked();
float x = event.getX();
float y = event.getY();
boolean nativeHandled = nativeOnTouch(action, x, y);
if (nativeHandled) {
break;
}
this.mDetector.onTouchEvent(event);
} while (false);
return super.onTouchEvent(event);
}
private class Apple2GestureListener extends GestureDetector.SimpleOnGestureListener {
private static final String TAG = "Gestures";
@Override
public boolean onDown(MotionEvent event) {
Log.d(TAG,"onDown: " + event.toString());
return true;
}
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
if (mSwipeTogglesSpeed) {
float ev1X = event1.getX();
float ev2X = event2.getX();
if (ev1X < ev2X) {
nativeIncreaseCPUSpeed();
} else {
nativeDecreaseCPUSpeed();
}
}
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
Log.d(TAG, "onSingleTapConfirmed: " + event.toString());
Apple2MainMenu mainMenu = Apple2Activity.this.mView.getMainMenu();
if (mainMenu.isShowing()) {
Log.d(TAG, "dismissing main menu");
mainMenu.dismiss();
} else if (Apple2Activity.this.isSoftKeyboardShowing()) {
Log.d(TAG, "hiding keyboard");
Apple2Activity.this.mView.toggleKeyboard();
} else {
Log.d(TAG, "showing main menu");
Apple2Activity.this.mView.showMainMenu();
}
return true;
}
@Override
public boolean onDoubleTap(MotionEvent event) {
if (mDoubleTapShowsKeyboard) {
Log.d(TAG, "onDoubleTap: " + event.toString());
if (!Apple2Activity.this.isSoftKeyboardShowing()) {
Log.d(TAG, "showing keyboard");
Apple2Activity.this.mView.toggleKeyboard();
}
}
return true;
}
}
public void graphicsInitialized(int width, int height) {
@ -274,4 +361,12 @@ public class Apple2Activity extends Activity {
}
mQuitDialog.show();
}
public void setSwipeTogglesSpeed(boolean swipeTogglesSpeed) {
mSwipeTogglesSpeed = swipeTogglesSpeed;
}
public void setDoubleTapShowsKeyboard(boolean doubleTapShowsKeyboard) {
mDoubleTapShowsKeyboard = doubleTapShowsKeyboard;
}
}

View File

@ -18,7 +18,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.TabHost;
@ -41,12 +42,31 @@ public class Apple2SettingsMenu {
LayoutInflater inflater = (LayoutInflater)mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSettingsView = inflater.inflate(R.layout.activity_settings, null, false);
ListView settingsMenuView = (ListView)mSettingsView.findViewById(R.id.joystick_settings_listview);
// General Settings
final CheckBox swipeToChangeSpeeds = (CheckBox)mSettingsView.findViewById(R.id.checkBox_speedswipe);
swipeToChangeSpeeds.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Apple2SettingsMenu.this.mActivity.setSwipeTogglesSpeed(isChecked);
}
});
final CheckBox doubleTapShowsKeyboard = (CheckBox)mSettingsView.findViewById(R.id.checkBox_doubletapkeyboard);
doubleTapShowsKeyboard.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Apple2SettingsMenu.this.mActivity.setDoubleTapShowsKeyboard(isChecked);
}
});
// Joystick Settings
ListView settingsMenuView = (ListView)mSettingsView.findViewById(R.id.joystick_settings_listview);
String[] values = new String[] {
mActivity.getResources().getString(R.string.joystick_configure),
};
ArrayAdapter<?> adapter = new ArrayAdapter<String>(mActivity, android.R.layout.simple_list_item_1, android.R.id.text1, values);
settingsMenuView.setAdapter(adapter);
settingsMenuView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@ -62,6 +82,8 @@ public class Apple2SettingsMenu {
}
});
// Tab management
TabHost tabHost = (TabHost)mSettingsView.findViewById(R.id.tabHost_settings);
tabHost.setup();
TabHost.TabSpec spec = tabHost.newTabSpec("tab_general");
@ -73,15 +95,6 @@ public class Apple2SettingsMenu {
spec.setIndicator(mActivity.getResources().getString(R.string.tab_joystick), mActivity.getResources().getDrawable(android.R.drawable.ic_menu_compass));
spec.setContent(R.id.tab_joystick);
tabHost.addTab(spec);
Button rebootButton = (Button)mSettingsView.findViewById(R.id.reboot_button);
rebootButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Apple2SettingsMenu.this.mActivity.nativeReboot();
Apple2SettingsMenu.this.dismiss();
}
});
}
public void showJoystickConfiguration() {

View File

@ -19,7 +19,6 @@ import android.content.Context;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.view.MotionEvent;
import android.view.inputmethod.InputMethodManager;
import javax.microedition.khronos.egl.EGL10;
@ -49,33 +48,20 @@ import javax.microedition.khronos.opengles.GL10;
class Apple2View extends GLSurfaceView {
private final static String TAG = "Apple2View";
private final static boolean DEBUG = false;
private final static int MENU_CANCEL_MOVE_THRESHOLD = 20;
private Apple2Activity mActivity = null;
private Apple2MainMenu mMainMenu = null;
private boolean mUltiTapEventBegin = false;
private boolean mTapEventBegin = false;
private float mSingleX = 0;
private float mSingleY = 0;
private float mUltiX = 0;
private float mUltiY = 0;
private boolean inefficient8888 = true; // HACK FIXME TODO : rewrite GL code to accommodate 565 rendering ...
public Apple2View(Apple2Activity activity) {
super(activity.getApplication());
mActivity = activity;
init(inefficient8888, 0, 0);
setup(inefficient8888, 0, 0);
}
public Apple2View(Apple2Activity activity, boolean translucent, int depth, int stencil) {
super(activity.getApplication());
mActivity = activity;
init(translucent, depth, stencil);
}
private void init(boolean translucent, int depth, int stencil) {
private void setup(boolean translucent, int depth, int stencil) {
/* By default, GLSurfaceView() creates a RGB_565 opaque surface.
* If we want a translucent one, we should change the surface's
@ -104,82 +90,10 @@ class Apple2View extends GLSurfaceView {
setRenderer(new Renderer());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mMainMenu == null) {
return false;
}
Log.d(TAG, "VIEW onTouchEvent : " + event.toString());
int action = event.getActionMasked();
if (mMainMenu.isShowing()) {
mMainMenu.dismiss();
return true;
}
// TODO : native GL touch joystick ...
switch (action) {
case (MotionEvent.ACTION_DOWN):
mSingleX = event.getX();
mSingleY = event.getY();
mTapEventBegin = true;
mUltiTapEventBegin = false;
return true;
case (MotionEvent.ACTION_MOVE):
if (mTapEventBegin) {
float thresholdX = Math.abs(event.getX() - mSingleX);
float thresholdY = Math.abs(event.getY() - mSingleY);
if (thresholdX > MENU_CANCEL_MOVE_THRESHOLD || thresholdY > MENU_CANCEL_MOVE_THRESHOLD) {
mTapEventBegin = false;
}
} else if (mUltiTapEventBegin) {
float thresholdX = Math.abs(event.getX() - mUltiX);
float thresholdY = Math.abs(event.getY() - mUltiY);
if (thresholdX > MENU_CANCEL_MOVE_THRESHOLD || thresholdY > MENU_CANCEL_MOVE_THRESHOLD) {
mUltiTapEventBegin = false;
}
}
return true;
case (MotionEvent.ACTION_POINTER_DOWN):
mUltiX = event.getX();
mUltiY = event.getY();
mTapEventBegin = false;
mUltiTapEventBegin = true;
return true;
case (MotionEvent.ACTION_POINTER_UP):
if (mUltiTapEventBegin) {
Log.d(TAG, "Toggling keyboard...");
toggleKeyboard();
}
mTapEventBegin = false;
mUltiTapEventBegin = false;
return true;
case (MotionEvent.ACTION_UP):
if (mTapEventBegin) {
showMainMenu();
}
mTapEventBegin = false;
mUltiTapEventBegin = false;
return true;
}
return super.onTouchEvent(event);
}
public void showMainMenu() {
if (mMainMenu != null) {
Apple2SettingsMenu settingsMenu = mMainMenu.getSettingsMenu();
if (!settingsMenu.isShowing()) {
if (mActivity.isSoftKeyboardShowing()) {
toggleKeyboard();
}
mMainMenu.show();
}
}

View File

@ -5,100 +5,17 @@
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:orientation="horizontal">
<TextView
android:id="@+id/cpuspeed_section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/speed_cpu"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_marginTop="4dp"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<SeekBar
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/cpuspeed_scale"
android:max="375"
android:progress="75"
android:layout_margin="4dp"
android:layout_weight=".75" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="100%"
android:id="@+id/cpuspeed_label"
android:layout_marginRight="4dp"
android:textSize="14sp" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/max"
android:id="@+id/cpuspeed_max"
android:layout_marginRight="4dp"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:orientation="horizontal">
<TextView
android:id="@+id/altspeed_section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/speed_alt"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<SeekBar
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/altspeed_scale"
android:max="375"
android:progress="375"
android:layout_margin="4dp"
android:layout_weight=".75" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="400%"
android:id="@+id/altspeed_label"
android:layout_marginRight="4dp"
android:textSize="14sp" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/max"
android:id="@+id/altspeed_max"
android:layout_marginRight="4dp"
android:text="@string/keyboard_doubletap"
android:id="@+id/checkBox_doubletapkeyboard"
android:textSize="14sp"
android:checked="true" />
</LinearLayout>
@ -117,18 +34,4 @@
android:checked="true"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="bottom|right"
android:layout_margin="4dp">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reboot"
android:id="@+id/reboot_button" />
</LinearLayout>
</LinearLayout>

View File

@ -8,7 +8,7 @@
<string name="joystick_keyB">Key Joystick 2</string>
<string name="joystick_show">Show emulated joystick</string>
<string name="keyboard">Keyboard</string>
<string name="keyboard_two_finger">2-finger-tap shows keyboard</string>
<string name="keyboard_doubletap">Double-tap shows keyboard</string>
<string name="max">Max</string>
<string name="menu_disks">Load disk image…</string>
<string name="menu_settings">Emulator settings…</string>
@ -19,7 +19,7 @@
<string name="spacer"> </string>
<string name="speed_alt">Alternate CPU Speed</string>
<string name="speed_cpu">CPU Speed</string>
<string name="speed_swipe">Swipe toggles between configured speeds</string>
<string name="speed_swipe">Left/right swiping changes emulation speed</string>
<string name="tab_general">General</string>
<string name="tab_joystick">Joystick</string>
</resources>

View File

@ -16,6 +16,34 @@
static bool nativePaused = false;
#if TESTING
static bool _run_tests(void) {
char *local_argv[] = {
"-f",
NULL
};
int local_argc = 0;
for (char **p = &local_argv[0]; *p != NULL; p++) {
++local_argc;
}
#if defined(TEST_CPU)
extern int test_cpu(int, char *[]);
test_cpu(local_argc, local_argv);
#elif defined(TEST_VM)
extern int test_vm(int, char *[]);
test_vm(local_argc, local_argv);
#elif defined(TEST_DISPLAY)
extern int test_display(int, char *[]);
test_display(local_argc, local_argv);
#elif defined(TEST_DISK)
extern int test_disk(int, char *[]);
test_disk(local_argc, local_argv);
#else
# error "OOPS, no tests specified"
#endif
}
#endif
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobject obj, jstring j_dataDir) {
const char *dataDir = (*env)->GetStringUTFChars(env, j_dataDir, 0);
data_dir = strdup(dataDir);
@ -35,38 +63,15 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsInitialized(JNIEnv
LOG("%s", "native graphicsInitialized...");
video_driver_reshape(width, height);
#if !TESTING
#if TESTING
_run_tests();
#else
static bool graphicsPreviouslyInitialized = false;
if (graphicsPreviouslyInitialized) {
video_driver_shutdown();
}
graphicsPreviouslyInitialized = true;
video_driver_init((void *)0);
#else
char *local_argv[] = {
"-f",
NULL
};
int local_argc = 0;
for (char **p = &local_argv[0]; *p != NULL; p++) {
++local_argc;
}
# if defined(TEST_CPU)
extern int test_cpu(int, char *[]);
test_cpu(local_argc, local_argv);
# elif defined(TEST_VM)
extern int test_vm(int, char *[]);
test_vm(local_argc, local_argv);
# elif defined(TEST_DISPLAY)
extern int test_display(int, char *[]);
test_display(local_argc, local_argv);
# elif defined(TEST_DISK)
extern int test_disk(int, char *[]);
test_disk(local_argc, local_argv);
# else
# error "OOPS, no tests specified"
# endif
#endif
}
@ -147,3 +152,52 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnKeyUp(JNIEnv *env, jobjec
android_keycode_to_emulator(keyCode, metaState, false);
#endif
}
jboolean Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnTouch(JNIEnv *env, jobject obj, jint action, jfloat keyCode, jfloat metaState) {
return false;
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeIncreaseCPUSpeed(JNIEnv *env, jobject obj) {
pthread_mutex_lock(&interface_mutex);
if (cpu_scale_factor > 1.0) {
cpu_scale_factor += 0.25;
} else {
cpu_scale_factor += 0.05;
}
if (cpu_scale_factor > CPU_SCALE_FASTEST) {
cpu_scale_factor = CPU_SCALE_FASTEST;
}
//video_driver_animate_speedscale();
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
pthread_mutex_unlock(&interface_mutex);
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeDecreaseCPUSpeed(JNIEnv *env, jobject obj) {
pthread_mutex_lock(&interface_mutex);
if (cpu_scale_factor > 1.0) {
cpu_scale_factor -= 0.25;
} else {
cpu_scale_factor -= 0.05;
}
if (cpu_scale_factor < CPU_SCALE_SLOWEST) {
cpu_scale_factor = CPU_SCALE_SLOWEST;
}
//video_driver_animate_speedscale();
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
pthread_mutex_unlock(&interface_mutex);
}