Revamp the special key system to match v2 spirit:

- Fix special keys triggerring up to 16 times !
- Fix special keys not being properly toggleable
- Moved special key handling into the ControlButton.
- Removed the need for a listener.
- Toggling the mouse doesn't affect the button text anymore.
This commit is contained in:
SerpentSpirale 2021-08-24 15:25:10 +02:00 committed by ArtDev
parent 28fa92f1cb
commit c435db640a
8 changed files with 94 additions and 179 deletions

View file

@ -32,7 +32,7 @@ import org.lwjgl.glfw.*;
public class BaseMainActivity extends LoggableActivity { public class BaseMainActivity extends LoggableActivity {
public static volatile ClipboardManager GLOBAL_CLIPBOARD; public static volatile ClipboardManager GLOBAL_CLIPBOARD;
public TouchCharInput touchCharInput; public static TouchCharInput touchCharInput;
volatile public static boolean isInputStackCall; volatile public static boolean isInputStackCall;
@ -84,8 +84,8 @@ public class BaseMainActivity extends LoggableActivity {
public boolean hiddenTextIgnoreUpdate = true; public boolean hiddenTextIgnoreUpdate = true;
private boolean isVirtualMouseEnabled; private static boolean isVirtualMouseEnabled;
private LinearLayout touchPad; private static LinearLayout touchPad;
private ImageView mousePointer; private ImageView mousePointer;
private MinecraftAccount mProfile; private MinecraftAccount mProfile;
@ -867,12 +867,14 @@ public class BaseMainActivity extends LoggableActivity {
this.mousePointer.setY(y); this.mousePointer.setY(y);
} }
public void toggleMouse(View view) { public static void toggleMouse(Context ctx) {
if (CallbackBridge.isGrabbing()) return; if (CallbackBridge.isGrabbing()) return;
isVirtualMouseEnabled = !isVirtualMouseEnabled; isVirtualMouseEnabled = !isVirtualMouseEnabled;
touchPad.setVisibility(isVirtualMouseEnabled ? View.VISIBLE : View.GONE); touchPad.setVisibility(isVirtualMouseEnabled ? View.VISIBLE : View.GONE);
((Button) view).setText(isVirtualMouseEnabled ? R.string.control_mouseon: R.string.control_mouseoff); Toast.makeText(ctx,
isVirtualMouseEnabled ? R.string.control_mouseon : R.string.control_mouseoff,
Toast.LENGTH_SHORT).show();
} }
public static void dialogForceClose(Context ctx) { public static void dialogForceClose(Context ctx) {
@ -895,6 +897,12 @@ public class BaseMainActivity extends LoggableActivity {
sendKeyPress(LWJGLGLFWKeycode.GLFW_KEY_ESCAPE); sendKeyPress(LWJGLGLFWKeycode.GLFW_KEY_ESCAPE);
} }
public static void switchKeyboardState() {
if(touchCharInput != null) touchCharInput.switchKeyboardState();
}
protected void setRightOverride(boolean val) { protected void setRightOverride(boolean val) {
this.rightOverride = val; this.rightOverride = val;

View file

@ -3,142 +3,54 @@ package net.kdt.pojavlaunch;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.*; import android.os.*;
import android.view.*;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.material.navigation.NavigationView;
import net.kdt.pojavlaunch.customcontrols.*; import net.kdt.pojavlaunch.customcontrols.*;
import net.kdt.pojavlaunch.customcontrols.buttons.ControlButton;
import net.kdt.pojavlaunch.prefs.*; import net.kdt.pojavlaunch.prefs.*;
import net.kdt.pojavlaunch.utils.MCOptionUtils; import net.kdt.pojavlaunch.utils.MCOptionUtils;
import org.lwjgl.glfw.*;
import java.io.*; import java.io.*;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF;
public class MainActivity extends BaseMainActivity { public class MainActivity extends BaseMainActivity {
public ControlLayout mControlLayout; public static ControlLayout mControlLayout;
private View.OnClickListener mClickListener;
private View.OnTouchListener mTouchListener;
private FileObserver fileObserver; private FileObserver fileObserver;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
initLayout(R.layout.main_with_customctrl); initLayout(R.layout.main_with_customctrl);
super.ingameControlsEditorListener = new NavigationView.OnNavigationItemSelectedListener() { super.ingameControlsEditorListener = menuItem -> {
@Override switch (menuItem.getItemId()) {
public boolean onNavigationItemSelected(MenuItem menuItem) { case R.id.menu_ctrl_load:
switch (menuItem.getItemId()) { CustomControlsActivity.load(mControlLayout);
case R.id.menu_ctrl_load: break;
CustomControlsActivity.load(mControlLayout); case R.id.menu_ctrl_add:
break; mControlLayout.addControlButton(new ControlData("New"));
case R.id.menu_ctrl_add: break;
mControlLayout.addControlButton(new ControlData("New")); case R.id.menu_ctrl_add_drawer:
break; mControlLayout.addDrawer(new ControlDrawerData());
case R.id.menu_ctrl_add_drawer: break;
mControlLayout.addDrawer(new ControlDrawerData()); case R.id.menu_ctrl_selectdefault:
break; CustomControlsActivity.dialogSelectDefaultCtrl(mControlLayout);
case R.id.menu_ctrl_selectdefault: break;
CustomControlsActivity.dialogSelectDefaultCtrl(mControlLayout); case R.id.menu_ctrl_save:
break; CustomControlsActivity.save(true,mControlLayout);
case R.id.menu_ctrl_save: break;
CustomControlsActivity.save(true,mControlLayout);
break;
}
//Toast.makeText(MainActivity.this, menuItem.getTitle() + ":" + menuItem.getItemId(), Toast.LENGTH_SHORT).show();
return true;
} }
//Toast.makeText(MainActivity.this, menuItem.getTitle() + ":" + menuItem.getItemId(), Toast.LENGTH_SHORT).show();
return true;
}; };
mClickListener = new View.OnClickListener(){
@Override
public void onClick(View view) {
if (view instanceof ControlButton) {
ControlButton button = (ControlButton) view;
for(int keycode : button.getProperties().keycodes){
switch (keycode) {
case ControlData.SPECIALBTN_KEYBOARD:
touchCharInput.switchKeyboardState();
break;
case ControlData.SPECIALBTN_TOGGLECTRL:
mControlLayout.toggleControlVisible();
break;
case ControlData.SPECIALBTN_VIRTUALMOUSE:
toggleMouse(button);
break;
}
}
}
}
};
mTouchListener = new View.OnTouchListener(){
@Override
public boolean onTouch(View view, MotionEvent e) {
boolean isDown;
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: // 0
case MotionEvent.ACTION_POINTER_DOWN: // 5
isDown = true;
break;
case MotionEvent.ACTION_UP: // 1
case MotionEvent.ACTION_CANCEL: // 3
case MotionEvent.ACTION_POINTER_UP: // 6
isDown = false;
break;
default:
return false;
}
if (view instanceof ControlButton) {
ControlButton button = (ControlButton) view;
for(int keycode : button.getProperties().keycodes) {
switch (keycode) {
case ControlData.SPECIALBTN_MOUSEPRI:
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT, isDown);
break;
case ControlData.SPECIALBTN_MOUSEMID:
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_MIDDLE, isDown);
break;
case ControlData.SPECIALBTN_MOUSESEC:
if (CallbackBridge.isGrabbing()) {
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown);
} else {
CallbackBridge.putMouseEventWithCoords(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown ? 1 : 0, CallbackBridge.mouseX, CallbackBridge.mouseY);
setRightOverride(isDown);
}
break;
case ControlData.SPECIALBTN_SCROLLDOWN:
if (!isDown) CallbackBridge.sendScroll(0, 1d);
break;
case ControlData.SPECIALBTN_SCROLLUP:
if (!isDown) CallbackBridge.sendScroll(0, -1d);
break;
}
}
}
return false;
}
};
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
fileObserver = new FileObserver(new File(Tools.DIR_GAME_NEW + "/options.txt"), FileObserver.MODIFY) { fileObserver = new FileObserver(new File(Tools.DIR_GAME_NEW + "/options.txt"), FileObserver.MODIFY) {
@Override @Override
public void onEvent(int i, @Nullable String s) { public void onEvent(int i, @Nullable String s) {
//FIXME Make sure the multithreading nature of this event doesn't cause any problems ?
MCOptionUtils.load(); MCOptionUtils.load();
getMcScale(); getMcScale();
} }
@ -147,7 +59,6 @@ public class MainActivity extends BaseMainActivity {
fileObserver = new FileObserver(Tools.DIR_GAME_NEW + "/options.txt", FileObserver.MODIFY) { fileObserver = new FileObserver(Tools.DIR_GAME_NEW + "/options.txt", FileObserver.MODIFY) {
@Override @Override
public void onEvent(int i, @Nullable String s) { public void onEvent(int i, @Nullable String s) {
//FIXME Make sure the multithreading nature of this event doesn't cause any problems ?
MCOptionUtils.load(); MCOptionUtils.load();
getMcScale(); getMcScale();
} }
@ -156,19 +67,6 @@ public class MainActivity extends BaseMainActivity {
fileObserver.startWatching(); fileObserver.startWatching();
ControlData[] specialButtons = ControlData.getSpecialButtons();
specialButtons[0].specialButtonListener
= specialButtons[1].specialButtonListener
= specialButtons[4].specialButtonListener
= mClickListener;
specialButtons[2].specialButtonListener
= specialButtons[3].specialButtonListener
= specialButtons[5].specialButtonListener
= specialButtons[6].specialButtonListener
= specialButtons[7].specialButtonListener
= mTouchListener;
mControlLayout = findViewById(R.id.main_control_layout); mControlLayout = findViewById(R.id.main_control_layout);
mControlLayout.setModifiable(false); mControlLayout.setModifiable(false);
try { try {

View file

@ -81,8 +81,6 @@ public class ControlData implements Cloneable
public float cornerRadius; //0-100% public float cornerRadius; //0-100%
public boolean isSwipeable; public boolean isSwipeable;
public Object specialButtonListener;
public ControlData() { public ControlData() {
this("button"); this("button");
} }
@ -142,7 +140,6 @@ public class ControlData implements Cloneable
this.strokeColor = strokeColor; this.strokeColor = strokeColor;
this.strokeWidth = strokeWidth; this.strokeWidth = strokeWidth;
this.cornerRadius = cornerRadius; this.cornerRadius = cornerRadius;
update();
} }
public void execute(boolean isDown) { public void execute(boolean isDown) {
@ -176,14 +173,6 @@ public class ControlData implements Cloneable
// Calculate, because the dynamic position contains some math equations // Calculate, because the dynamic position contains some math equations
return calculate(insertedPos); return calculate(insertedPos);
} }
public void update() {
if(SPECIAL_BUTTONS != null)
for(int keycode : keycodes)
for (ControlData data : getSpecialButtons())
if (keycode == data.keycodes[0])
specialButtonListener = data.specialButtonListener;
}
private static float calculate(String math) { private static float calculate(String math) {
return (float) new ExpressionBuilder(math) return (float) new ExpressionBuilder(math)

View file

@ -7,7 +7,7 @@ import com.google.gson.*;
import java.io.*; import java.io.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import net.kdt.pojavlaunch.*; import net.kdt.pojavlaunch.*;
import net.kdt.pojavlaunch.customcontrols.buttons.ControlButton; import net.kdt.pojavlaunch.customcontrols.buttons.ControlButton;
@ -16,11 +16,6 @@ import net.kdt.pojavlaunch.customcontrols.buttons.ControlSubButton;
import net.kdt.pojavlaunch.customcontrols.handleview.HandleView; import net.kdt.pojavlaunch.customcontrols.handleview.HandleView;
import net.kdt.pojavlaunch.prefs.*; import net.kdt.pojavlaunch.prefs.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.lwjgl.glfw.*;
public class ControlLayout extends FrameLayout public class ControlLayout extends FrameLayout
{ {
protected CustomControls mLayout; protected CustomControls mLayout;
@ -260,7 +255,7 @@ public class ControlLayout extends FrameLayout
//Check if the action is cancelling, reset the lastControl button associated to the view //Check if the action is cancelling, reset the lastControl button associated to the view
if(ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL){ if(ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL){
if(lastControlButton != null) lastControlButton.sendKeyPresses(ev,false); if(lastControlButton != null) lastControlButton.sendKeyPresses(false);
mapTable.put(v, null); mapTable.put(v, null);
return true; return true;
} }
@ -276,7 +271,7 @@ public class ControlLayout extends FrameLayout
} }
//Release last keys //Release last keys
if (lastControlButton != null) lastControlButton.sendKeyPresses(ev,false); if (lastControlButton != null) lastControlButton.sendKeyPresses(false);
mapTable.put(v, null); mapTable.put(v, null);
//Look for another SWIPEABLE button //Look for another SWIPEABLE button
@ -288,7 +283,7 @@ public class ControlLayout extends FrameLayout
//Press the new key //Press the new key
if(!button.equals(lastControlButton)){ if(!button.equals(lastControlButton)){
button.sendKeyPresses(ev,true); button.sendKeyPresses(true);
mapTable.put(v, button); mapTable.put(v, button);
} }

View file

@ -66,7 +66,7 @@ public class CustomControls {
} }
public void save(String path) throws IOException { public void save(String path) throws IOException {
//Current version is the V2 so the version as to be marked as 2 ! //Current version is the V2.3 so the version as to be marked as 3 !
version = 3; version = 3;
Tools.write(path, Tools.GLOBAL_GSON.toJson(this)); Tools.write(path, Tools.GLOBAL_GSON.toJson(this));

View file

@ -121,7 +121,6 @@ public class LayoutConverter {
if(button.getBoolean("holdAlt")) { keycodes[next_idx] = LWJGLGLFWKeycode.GLFW_KEY_LEFT_ALT; next_idx++; } if(button.getBoolean("holdAlt")) { keycodes[next_idx] = LWJGLGLFWKeycode.GLFW_KEY_LEFT_ALT; next_idx++; }
keycodes[next_idx] = button.getInt("keycode"); keycodes[next_idx] = button.getInt("keycode");
n_button.keycodes = keycodes; n_button.keycodes = keycodes;
n_button.update();
empty.mControlDataList.add(n_button); empty.mControlDataList.add(n_button);
} }
empty.scaledAt = (float)oldLayoutJson.getDouble("scaledAt"); empty.scaledAt = (float)oldLayoutJson.getDouble("scaledAt");

View file

@ -20,6 +20,7 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import org.lwjgl.glfw.*; import org.lwjgl.glfw.*;
import static net.kdt.pojavlaunch.BaseMainActivity.sendMouseButton;
import static net.kdt.pojavlaunch.LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN; import static net.kdt.pojavlaunch.LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN;
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_BUTTONSIZE; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_BUTTONSIZE;
@ -79,17 +80,12 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
//Visibility //Visibility
properties.isHideable = !properties.containsKeycode(ControlData.SPECIALBTN_TOGGLECTRL) && !properties.containsKeycode(ControlData.SPECIALBTN_VIRTUALMOUSE); properties.isHideable = !properties.containsKeycode(ControlData.SPECIALBTN_TOGGLECTRL) && !properties.containsKeycode(ControlData.SPECIALBTN_VIRTUALMOUSE);
properties.update();
return properties; return properties;
} }
public void setProperties(ControlData properties, boolean changePos) { public void setProperties(ControlData properties, boolean changePos) {
mProperties = properties; mProperties = properties;
properties.update();
// com.android.internal.R.string.delete
// android.R.string.
setText(properties.name); setText(properties.name);
if (changePos) { if (changePos) {
@ -97,17 +93,6 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
setY(properties.insertDynamicPos(mProperties.dynamicY)); setY(properties.insertDynamicPos(mProperties.dynamicY));
} }
if (properties.specialButtonListener == null) {
// A non-special button or inside custom controls screen so skip listener
} else if (properties.specialButtonListener instanceof View.OnClickListener) {
setOnClickListener((View.OnClickListener) properties.specialButtonListener);
} else if (properties.specialButtonListener instanceof View.OnTouchListener) {
setOnTouchListener((View.OnTouchListener) properties.specialButtonListener);
} else {
throw new IllegalArgumentException("Field " + ControlData.class.getName() + ".specialButtonListener must be View.OnClickListener or View.OnTouchListener, but was " +
properties.specialButtonListener.getClass().getName());
}
setLayoutParams(new FrameLayout.LayoutParams((int) properties.getWidth(), (int) properties.getHeight() )); setLayoutParams(new FrameLayout.LayoutParams((int) properties.getWidth(), (int) properties.getHeight() ));
} }
@ -140,7 +125,6 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
setBackground(); setBackground();
// Re-calculate position // Re-calculate position
mProperties.update();
if(!mProperties.isDynamicBtn){ if(!mProperties.isDynamicBtn){
setX(getX()); setX(getX());
setY(getY()); setY(getY());
@ -269,7 +253,7 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
if(mProperties.isSwipeable && !isPointerOutOfBounds){ if(mProperties.isSwipeable && !isPointerOutOfBounds){
//Remove keys //Remove keys
if(!triggerToggle(event)) { if(!triggerToggle(event)) {
sendKeyPresses(event,false); sendKeyPresses(false);
} }
} }
isPointerOutOfBounds = true; isPointerOutOfBounds = true;
@ -282,7 +266,7 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
((ControlLayout) getParent()).onTouch(this, event); ((ControlLayout) getParent()).onTouch(this, event);
//RE-press the button //RE-press the button
if(mProperties.isSwipeable && !mProperties.isToggle){ if(mProperties.isSwipeable && !mProperties.isToggle){
sendKeyPresses(event,true); sendKeyPresses(true);
} }
} }
isPointerOutOfBounds = false; isPointerOutOfBounds = false;
@ -291,7 +275,7 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
case MotionEvent.ACTION_DOWN: // 0 case MotionEvent.ACTION_DOWN: // 0
case MotionEvent.ACTION_POINTER_DOWN: // 5 case MotionEvent.ACTION_POINTER_DOWN: // 5
if(!mProperties.isToggle){ if(!mProperties.isToggle){
sendKeyPresses(event,true); sendKeyPresses(true);
} }
break; break;
@ -306,7 +290,7 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
isPointerOutOfBounds = false; isPointerOutOfBounds = false;
if(!triggerToggle(event)) { if(!triggerToggle(event)) {
sendKeyPresses(event,false); sendKeyPresses(false);
} }
break; break;
@ -465,21 +449,63 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
if(mProperties.isToggle){ if(mProperties.isToggle){
isToggled = !isToggled; isToggled = !isToggled;
invalidate(); invalidate();
sendKeyPresses(event, isToggled); sendKeyPresses(isToggled);
return true; return true;
} }
return false; return false;
} }
public void sendKeyPresses(MotionEvent event, boolean isDown){ public void sendKeyPresses(boolean isDown){
for(int keycode : mProperties.keycodes){ for(int keycode : mProperties.keycodes){
if(keycode >= GLFW_KEY_UNKNOWN){ if(keycode >= GLFW_KEY_UNKNOWN){
MainActivity.sendKeyPress(keycode, CallbackBridge.getCurrentMods(), isDown); MainActivity.sendKeyPress(keycode, CallbackBridge.getCurrentMods(), isDown);
CallbackBridge.setModifiers(keycode, isDown); CallbackBridge.setModifiers(keycode, isDown);
}else{ }else{
super.onTouchEvent(event); sendSpecialKey(keycode, isDown);
} }
} }
} }
private void sendSpecialKey(int keycode, boolean isDown){
switch (keycode) {
case ControlData.SPECIALBTN_KEYBOARD:
if(isDown)BaseMainActivity.switchKeyboardState();
break;
case ControlData.SPECIALBTN_TOGGLECTRL:
if(isDown)MainActivity.mControlLayout.toggleControlVisible();
break;
case ControlData.SPECIALBTN_VIRTUALMOUSE:
if(isDown)BaseMainActivity.toggleMouse(getContext());
break;
case ControlData.SPECIALBTN_MOUSEPRI:
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT, isDown);
break;
case ControlData.SPECIALBTN_MOUSEMID:
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_MIDDLE, isDown);
break;
case ControlData.SPECIALBTN_MOUSESEC:
if (CallbackBridge.isGrabbing()) {
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown);
} else {
CallbackBridge.putMouseEventWithCoords(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown , CallbackBridge.mouseX, CallbackBridge.mouseY);
//setRightOverride(isDown);
}
break;
case ControlData.SPECIALBTN_SCROLLDOWN:
if (!isDown) CallbackBridge.sendScroll(0, 1d);
break;
case ControlData.SPECIALBTN_SCROLLUP:
if (!isDown) CallbackBridge.sendScroll(0, -1d);
break;
}
}
} }

View file

@ -29,18 +29,18 @@ public class CallbackBridge {
} }
@Override @Override
public void run() { public void run() {
putMouseEventWithCoords(button, 1, x, y); putMouseEventWithCoords(button, true, x, y);
try { Thread.sleep(40); } catch (InterruptedException e) {} try { Thread.sleep(40); } catch (InterruptedException e) {}
putMouseEventWithCoords(button, 0, x, y); putMouseEventWithCoords(button, false, x, y);
} }
} }
public static void putMouseEventWithCoords(int button, int x, int y /* , int dz, long nanos */) { public static void putMouseEventWithCoords(int button, int x, int y /* , int dz, long nanos */) {
new Thread(new PusherRunnable(button,x,y)).run(); new Thread(new PusherRunnable(button,x,y)).run();
} }
public static void putMouseEventWithCoords(int button, int state, int x, int y /* , int dz, long nanos */) { public static void putMouseEventWithCoords(int button, boolean isDown, int x, int y /* , int dz, long nanos */) {
sendCursorPos(x, y); sendCursorPos(x, y);
sendMouseKeycode(button, CallbackBridge.getCurrentMods(), state == 1); sendMouseKeycode(button, CallbackBridge.getCurrentMods(), isDown);
} }
private static boolean threadAttached; private static boolean threadAttached;