|
|
|
|
@ -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<TouchscreenControllerButtonView> mButtonViews = new ArrayList<>();
|
|
|
|
|
private ArrayList<TouchscreenControllerAxisView> 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: {
|
|
|
|
|
|