diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml
index 5cd135a06..9bba60dad 100644
--- a/android/.idea/gradle.xml
+++ b/android/.idea/gradle.xml
@@ -14,6 +14,7 @@
+
diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java b/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java
index 8ed4a49d1..38a2e965b 100644
--- a/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java
+++ b/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java
@@ -296,6 +296,9 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
AndroidHostInterface.getInstance().setDisplayAlignment(AndroidHostInterface.DISPLAY_ALIGNMENT_TOP_OR_LEFT);
else
AndroidHostInterface.getInstance().setDisplayAlignment(AndroidHostInterface.DISPLAY_ALIGNMENT_CENTER);
+
+ if (mTouchscreenController != null)
+ mTouchscreenController.updateOrientation();
}
private void enableFullscreenImmersive() {
@@ -411,17 +414,28 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
return;
}
- case 3: // Change Touchscreen Controller
+ case 3: // Settings
+ {
+ Intent intent = new Intent(EmulationActivity.this, SettingsActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivityForResult(intent, REQUEST_CODE_SETTINGS);
+ return;
+ }
+
+ case 4: // Change Touchscreen Controller
{
showTouchscreenControllerMenu();
return;
}
- case 4: // Settings
+ case 5: // Edit Touchscreen Controller Layout
{
- Intent intent = new Intent(EmulationActivity.this, SettingsActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivityForResult(intent, REQUEST_CODE_SETTINGS);
+ if (mTouchscreenController != null) {
+ mTouchscreenController.startLayoutEditing();
+ } else {
+ Toast.makeText(this, R.string.emulation_activity_touchscreen_controller_not_active, Toast.LENGTH_SHORT);
+ }
+
return;
}
}
diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerAxisView.java b/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerAxisView.java
index 70462031e..44436dfd3 100644
--- a/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerAxisView.java
+++ b/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerAxisView.java
@@ -6,7 +6,7 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
-public class TouchscreenControllerAxisView extends View {
+public final class TouchscreenControllerAxisView extends View {
private Drawable mBaseDrawable;
private Drawable mStickUnpressedDrawable;
private Drawable mStickPressedDrawable;
@@ -17,6 +17,7 @@ public class TouchscreenControllerAxisView extends View {
private int mDrawXPos = 0;
private int mDrawYPos = 0;
+ private String mConfigName;
private int mControllerIndex = -1;
private int mXAxisCode = -1;
private int mYAxisCode = -1;
@@ -49,6 +50,14 @@ public class TouchscreenControllerAxisView extends View {
mStickPressedDrawable.setCallback(this);
}
+ public String getConfigName() {
+ return mConfigName;
+ }
+
+ public void setConfigName(String configName) {
+ mConfigName = configName;
+ }
+
public void setControllerAxis(int controllerIndex, int xCode, int yCode) {
mControllerIndex = controllerIndex;
mXAxisCode = xCode;
diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerButtonView.java b/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerButtonView.java
index ec273f921..373e8a8ae 100644
--- a/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerButtonView.java
+++ b/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerButtonView.java
@@ -11,13 +11,14 @@ import android.view.View;
/**
* TODO: document your custom view class.
*/
-public class TouchscreenControllerButtonView extends View {
+public final class TouchscreenControllerButtonView extends View {
private Drawable mUnpressedDrawable;
private Drawable mPressedDrawable;
private boolean mPressed = false;
private boolean mHapticFeedback = false;
private int mControllerIndex = -1;
private int mButtonCode = -1;
+ private String mConfigName;
public TouchscreenControllerButtonView(Context context) {
super(context);
@@ -94,6 +95,14 @@ public class TouchscreenControllerButtonView extends View {
mButtonCode = code;
}
+ public void setConfigName(String name) {
+ mConfigName = name;
+ }
+
+ public String getConfigName() {
+ return mConfigName;
+ }
+
public void setHapticFeedback(boolean enabled) {
mHapticFeedback = enabled;
}
diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java b/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java
index c62774fd7..19b7961f0 100644
--- a/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java
+++ b/android/app/src/main/java/com/github/stenzek/duckstation/TouchscreenControllerView.java
@@ -1,13 +1,22 @@
package com.github.stenzek.duckstation;
import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.preference.PreferenceManager;
import java.util.ArrayList;
@@ -17,10 +26,18 @@ import java.util.ArrayList;
public class TouchscreenControllerView extends FrameLayout {
private int mControllerIndex;
private String mControllerType;
+ private String mViewType;
private View mMainView;
private ArrayList mButtonViews = new ArrayList<>();
private ArrayList mAxisViews = new ArrayList<>();
private boolean mHapticFeedback;
+ private String mLayoutOrientation;
+ private boolean mEditingLayout = false;
+ private View mMovingView = null;
+ private String mMovingName = null;
+ private float mMovingLastX = 0.0f;
+ private float mMovingLastY = 0.0f;
+ private ConstraintLayout mEditLayout = null;
public TouchscreenControllerView(Context context) {
super(context);
@@ -34,10 +51,101 @@ public class TouchscreenControllerView extends FrameLayout {
super(context, attrs, defStyle);
}
+ private String getConfigKeyForXTranslation(String name) {
+ return String.format("TouchscreenController/%s/%s%sXTranslation", mViewType, name, mLayoutOrientation);
+ }
+
+ private String getConfigKeyForYTranslation(String name) {
+ return String.format("TouchscreenController/%s/%s%sYTranslation", mViewType, name, mLayoutOrientation);
+ }
+
+ private void saveTranslationForButton(String name, float xTranslation, float yTranslation) {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ final SharedPreferences.Editor editor = prefs.edit();
+ editor.putFloat(getConfigKeyForXTranslation(name), xTranslation);
+ editor.putFloat(getConfigKeyForYTranslation(name), yTranslation);
+ editor.commit();
+ }
+
+ private void clearTranslationForAllButtons() {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ final SharedPreferences.Editor editor = prefs.edit();
+
+ for (TouchscreenControllerButtonView buttonView : mButtonViews) {
+ editor.remove(getConfigKeyForXTranslation(buttonView.getConfigName()));
+ editor.remove(getConfigKeyForYTranslation(buttonView.getConfigName()));
+ buttonView.setTranslationX(0.0f);
+ buttonView.setTranslationY(0.0f);
+ }
+
+ for (TouchscreenControllerAxisView axisView : mAxisViews) {
+ editor.remove(getConfigKeyForXTranslation(axisView.getConfigName()));
+ editor.remove(getConfigKeyForYTranslation(axisView.getConfigName()));
+ axisView.setTranslationX(0.0f);
+ axisView.setTranslationY(0.0f);
+ }
+
+ editor.commit();
+ requestLayout();
+ }
+
+ private void reloadButtonTranslation() {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+
+ for (TouchscreenControllerButtonView buttonView : mButtonViews) {
+ try {
+ buttonView.setTranslationX(prefs.getFloat(getConfigKeyForXTranslation(buttonView.getConfigName()), 0.0f));
+ buttonView.setTranslationY(prefs.getFloat(getConfigKeyForYTranslation(buttonView.getConfigName()), 0.0f));
+ //Log.i("TouchscreenController", String.format("Translation for %s %f %f", buttonView.getConfigName(),
+ // buttonView.getTranslationX(), buttonView.getTranslationY()));
+ } catch (ClassCastException ex) {
+
+ }
+ }
+
+ for (TouchscreenControllerAxisView axisView : mAxisViews) {
+ try {
+ axisView.setTranslationX(prefs.getFloat(getConfigKeyForXTranslation(axisView.getConfigName()), 0.0f));
+ axisView.setTranslationY(prefs.getFloat(getConfigKeyForYTranslation(axisView.getConfigName()), 0.0f));
+ } catch (ClassCastException ex) {
+
+ }
+ }
+ }
+
+ private String getOrientationString() {
+ switch (getContext().getResources().getConfiguration().orientation) {
+ case Configuration.ORIENTATION_PORTRAIT:
+ return "Portrait";
+ case Configuration.ORIENTATION_LANDSCAPE:
+ default:
+ return "Landscape";
+ }
+ }
+
+ /**
+ * Checks if the orientation of the layout has changed, and if so, reloads button translations.
+ */
+ public void updateOrientation() {
+ String newOrientation = getOrientationString();
+ if (mLayoutOrientation != null && mLayoutOrientation.equals(newOrientation))
+ return;
+
+ Log.i("TouchscreenController", "New orientation: " + newOrientation);
+ mLayoutOrientation = newOrientation;
+ reloadButtonTranslation();
+ requestLayout();
+ }
+
public void init(int controllerIndex, String controllerType, String viewType, boolean hapticFeedback) {
mControllerIndex = controllerIndex;
mControllerType = controllerType;
+ mViewType = viewType;
mHapticFeedback = hapticFeedback;
+ mLayoutOrientation = getOrientationString();
+
+ if (mEditingLayout)
+ endLayoutEditing();
mButtonViews.clear();
mAxisViews.clear();
@@ -67,31 +175,38 @@ public class TouchscreenControllerView extends FrameLayout {
return;
mMainView.setOnTouchListener((view1, event) -> {
- return handleTouchEvent(event);
+ if (mEditingLayout)
+ return handleEditingTouchEvent(event);
+ else
+ return handleTouchEvent(event);
});
- linkButton(mMainView, R.id.controller_button_up, "Up");
- linkButton(mMainView, R.id.controller_button_right, "Right");
- linkButton(mMainView, R.id.controller_button_down, "Down");
- linkButton(mMainView, R.id.controller_button_left, "Left");
- linkButton(mMainView, R.id.controller_button_l1, "L1");
- linkButton(mMainView, R.id.controller_button_l2, "L2");
- linkButton(mMainView, R.id.controller_button_select, "Select");
- linkButton(mMainView, R.id.controller_button_start, "Start");
- linkButton(mMainView, R.id.controller_button_triangle, "Triangle");
- linkButton(mMainView, R.id.controller_button_circle, "Circle");
- linkButton(mMainView, R.id.controller_button_cross, "Cross");
- linkButton(mMainView, R.id.controller_button_square, "Square");
- linkButton(mMainView, R.id.controller_button_r1, "R1");
- linkButton(mMainView, R.id.controller_button_r2, "R2");
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
- if (!linkAxis(mMainView, R.id.controller_axis_left, "Left"))
- linkAxisToButtons(mMainView, R.id.controller_axis_left, "");
+ linkButton(mMainView, R.id.controller_button_up, "UpButton", "Up");
+ linkButton(mMainView, R.id.controller_button_right, "RightButton", "Right");
+ linkButton(mMainView, R.id.controller_button_down, "DownButton", "Down");
+ linkButton(mMainView, R.id.controller_button_left, "LeftButton", "Left");
+ linkButton(mMainView, R.id.controller_button_l1, "L1Button", "L1");
+ linkButton(mMainView, R.id.controller_button_l2, "L2Button", "L2");
+ linkButton(mMainView, R.id.controller_button_select, "SelectButton", "Select");
+ linkButton(mMainView, R.id.controller_button_start, "StartButton", "Start");
+ linkButton(mMainView, R.id.controller_button_triangle, "TriangleButton", "Triangle");
+ linkButton(mMainView, R.id.controller_button_circle, "CircleButton", "Circle");
+ linkButton(mMainView, R.id.controller_button_cross, "CrossButton", "Cross");
+ linkButton(mMainView, R.id.controller_button_square, "SquareButton", "Square");
+ linkButton(mMainView, R.id.controller_button_r1, "R1Button", "R1");
+ linkButton(mMainView, R.id.controller_button_r2, "R2Button", "R2");
- linkAxis(mMainView, R.id.controller_axis_right, "Right");
+ if (!linkAxis(mMainView, R.id.controller_axis_left, "LeftAxis", "Left"))
+ linkAxisToButtons(mMainView, R.id.controller_axis_left, "LeftAxis", "");
+
+ linkAxis(mMainView, R.id.controller_axis_right, "RightAxis", "Right");
+ reloadButtonTranslation();
+ requestLayout();
}
- private void linkButton(View view, int id, String buttonName) {
+ private void linkButton(View view, int id, String configName, String buttonName) {
TouchscreenControllerButtonView buttonView = (TouchscreenControllerButtonView) view.findViewById(id);
if (buttonView == null)
return;
@@ -100,6 +215,7 @@ public class TouchscreenControllerView extends FrameLayout {
Log.i("TouchscreenController", String.format("%s -> %d", buttonName, code));
if (code >= 0) {
+ buttonView.setConfigName(configName);
buttonView.setButtonCode(mControllerIndex, code);
buttonView.setHapticFeedback(mHapticFeedback);
mButtonViews.add(buttonView);
@@ -109,7 +225,7 @@ public class TouchscreenControllerView extends FrameLayout {
}
}
- private boolean linkAxis(View view, int id, String axisName) {
+ private boolean linkAxis(View view, int id, String configName, String axisName) {
TouchscreenControllerAxisView axisView = (TouchscreenControllerAxisView) view.findViewById(id);
if (axisView == null)
return false;
@@ -120,12 +236,13 @@ public class TouchscreenControllerView extends FrameLayout {
if (xCode < 0 && yCode < 0)
return false;
+ axisView.setConfigName(configName);
axisView.setControllerAxis(mControllerIndex, xCode, yCode);
mAxisViews.add(axisView);
return true;
}
- private boolean linkAxisToButtons(View view, int id, String buttonPrefix) {
+ private boolean linkAxisToButtons(View view, int id, String configName, String buttonPrefix) {
TouchscreenControllerAxisView axisView = (TouchscreenControllerAxisView) view.findViewById(id);
if (axisView == null)
return false;
@@ -143,6 +260,111 @@ public class TouchscreenControllerView extends FrameLayout {
return true;
}
+ private int dpToPixels(float dp) {
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()));
+ }
+
+ public void startLayoutEditing() {
+ if (mEditLayout == null) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ mEditLayout = (ConstraintLayout) inflater.inflate(R.layout.layout_touchscreen_controller_edit, this, false);
+ ((Button) mEditLayout.findViewById(R.id.stop_editing)).setOnClickListener((view) -> endLayoutEditing());
+ ((Button) mEditLayout.findViewById(R.id.reset_layout)).setOnClickListener((view) -> clearTranslationForAllButtons());
+ addView(mEditLayout);
+ }
+
+ mEditingLayout = true;
+ }
+
+ public void endLayoutEditing() {
+ if (mEditLayout != null) {
+ ((ViewGroup) mMainView).removeView(mEditLayout);
+ mEditLayout = null;
+ }
+
+ mEditingLayout = false;
+ mMovingView = null;
+ mMovingName = null;
+ mMovingLastX = 0.0f;
+ mMovingLastY = 0.0f;
+ }
+
+ private boolean handleEditingTouchEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_UP: {
+ if (mMovingView != null) {
+ // save position
+ saveTranslationForButton(mMovingName, mMovingView.getTranslationX(), mMovingView.getTranslationY());
+ mMovingView = null;
+ mMovingName = null;
+ mMovingLastX = 0.0f;
+ mMovingLastY = 0.0f;
+ }
+
+ return true;
+ }
+
+ case MotionEvent.ACTION_DOWN: {
+ if (mMovingView != null) {
+ // already moving a button
+ return true;
+ }
+
+ Rect rect = new Rect();
+ final float x = event.getX();
+ final float y = event.getY();
+ for (TouchscreenControllerButtonView buttonView : mButtonViews) {
+ buttonView.getHitRect(rect);
+ if (rect.contains((int) x, (int) y)) {
+ mMovingView = buttonView;
+ mMovingName = buttonView.getConfigName();
+ mMovingLastX = x;
+ mMovingLastY = y;
+ return true;
+ }
+ }
+
+ for (TouchscreenControllerAxisView axisView : mAxisViews) {
+ axisView.getHitRect(rect);
+ if (rect.contains((int) x, (int) y)) {
+ mMovingView = axisView;
+ mMovingName = axisView.getConfigName();
+ mMovingLastX = x;
+ mMovingLastY = y;
+ return true;
+ }
+ }
+
+ // nothing..
+ return true;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ if (mMovingView == null)
+ return true;
+
+ final float x = event.getX();
+ final float y = event.getY();
+ final float dx = x - mMovingLastX;
+ final float dy = y - mMovingLastY;
+ mMovingLastX = x;
+ mMovingLastY = y;
+
+ final float posX = mMovingView.getX() + dx;
+ final float posY = mMovingView.getY() + dy;
+ //Log.d("Position", String.format("%f %f -> (%f %f) %f %f",
+ // mMovingView.getX(), mMovingView.getY(), dx, dy, posX, posY));
+ mMovingView.setX(posX);
+ mMovingView.setY(posY);
+ mMovingView.invalidate();
+ mMainView.requestLayout();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private boolean handleTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_UP: {
diff --git a/android/app/src/main/res/layout/layout_touchscreen_controller_edit.xml b/android/app/src/main/res/layout/layout_touchscreen_controller_edit.xml
new file mode 100644
index 000000000..070b334bd
--- /dev/null
+++ b/android/app/src/main/res/layout/layout_touchscreen_controller_edit.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values-it/arrays.xml b/android/app/src/main/res/values-it/arrays.xml
index 8103406fc..bc6050230 100644
--- a/android/app/src/main/res/values-it/arrays.xml
+++ b/android/app/src/main/res/values-it/arrays.xml
@@ -87,8 +87,9 @@
- Reset
- Codici Patch
- Cambia Disco
- - Cambia Controller Touchscreen
- Impostazioni
+ - Cambia Controller Touchscreen
+ - Edit Touchscreen Controller Layout
- Slot Veloce
diff --git a/android/app/src/main/res/values-nl/arrays.xml b/android/app/src/main/res/values-nl/arrays.xml
index 41232cb18..f87be4ffb 100644
--- a/android/app/src/main/res/values-nl/arrays.xml
+++ b/android/app/src/main/res/values-nl/arrays.xml
@@ -87,8 +87,9 @@
- Resetten
- Patch Codes
- Disc Veranderen
- - Touchscreen Controller Aanpassen
- Instellingen
+ - Touchscreen Controller Aanpassen
+ - Edit Touchscreen Controller Layout
- Snel Slot
diff --git a/android/app/src/main/res/values-pt-rBR/arrays.xml b/android/app/src/main/res/values-pt-rBR/arrays.xml
index 0d2afe283..312c39613 100644
--- a/android/app/src/main/res/values-pt-rBR/arrays.xml
+++ b/android/app/src/main/res/values-pt-rBR/arrays.xml
@@ -87,8 +87,9 @@
- Reiniciar
- Trapaças
- Mudar Disco
- - Mudar controle em Tela
- Configurações
+ - Mudar controle em Tela
+ - Edit Touchscreen Controller Layout
- Armazenamento Rápido
diff --git a/android/app/src/main/res/values/arrays.xml b/android/app/src/main/res/values/arrays.xml
index 1a5c053cb..264747f2d 100644
--- a/android/app/src/main/res/values/arrays.xml
+++ b/android/app/src/main/res/values/arrays.xml
@@ -163,8 +163,9 @@
- Reset
- Patch Codes
- Change Disc
- - Change Touchscreen Controller
- Settings
+ - Change Touchscreen Controller
+ - Edit Touchscreen Controller Layout
- Quick Slot
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 911456edc..0068f7c37 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -152,4 +152,7 @@
Threaded GPU Rendering
Uses a second thread for drawing graphics. Currently only available for the software renderer, but can provide a significant speed improvement, and is safe to use.
Language (restart to apply)
+ Stop Editing
+ Reset Layout
+ Touchscreen controller is not active.