mirror of
https://github.com/izzy2lost/WeeU.git
synced 2026-07-06 00:19:59 -07:00
Add support for controller on Android
This commit is contained in:
@@ -14,7 +14,10 @@
|
||||
#include "input/InputManager.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
|
||||
#include "AndroidGui/AndroidAudio.h"
|
||||
|
||||
#include "input/api/Android/AndroidControllerProvider.h"
|
||||
#include "input/api/Android/AndroidController.h"
|
||||
#include "input/ControllerFactory.h"
|
||||
#include "AndroidGui/AndroidEmulatedController.h"
|
||||
#include "JNIUtils.h"
|
||||
|
||||
extern "C"
|
||||
@@ -345,4 +348,167 @@ extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_addGamePath(JNIEnv *env, jclass clazz, jstring uri) {
|
||||
gui_addGamePath(JNIUtils::JStringToString(env, uri));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_onNativeKey(JNIEnv *env, jclass clazz, jstring device_descriptor,
|
||||
jstring device_name, jint key, jboolean is_pressed) {
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider *>(apiProvider.get());
|
||||
if (androidControllerProvider)
|
||||
androidControllerProvider->on_key_event(JNIUtils::JStringToString(env, device_descriptor),
|
||||
JNIUtils::JStringToString(env, device_name),
|
||||
key,
|
||||
is_pressed);
|
||||
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_onNativeAxis(JNIEnv *env, jclass clazz, jstring device_descriptor,
|
||||
jstring device_name, jint axis, jfloat value) {
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider *>(apiProvider.get());
|
||||
androidControllerProvider->on_axis_event(JNIUtils::JStringToString(env, device_descriptor),
|
||||
JNIUtils::JStringToString(env, device_name),
|
||||
axis,
|
||||
value);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_setControllerType(JNIEnv *env, jclass clazz, jint index,
|
||||
jint emulated_controller_type) {
|
||||
EmulatedController::Type type = static_cast<EmulatedController::Type>(emulated_controller_type);
|
||||
auto &androidEmulatedController = AndroidEmulatedController::getAndroidEmulatedController(
|
||||
index);
|
||||
if (EmulatedController::Type::VPAD <= type && type < EmulatedController::Type::MAX)
|
||||
androidEmulatedController.setType(type);
|
||||
else
|
||||
androidEmulatedController.setDisabled();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_getControllerType(JNIEnv *env, jclass clazz, jint index) {
|
||||
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(
|
||||
index).getEmulatedController();
|
||||
if (emulatedController)
|
||||
return emulatedController->type();
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_getWPADControllersCount(JNIEnv *env, jclass clazz) {
|
||||
jint wpadCount = 0;
|
||||
for (int i = 0; i < InputManager::kMaxController; i++) {
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(
|
||||
i).getEmulatedController();
|
||||
if (!emulatedController)
|
||||
continue;
|
||||
if (emulatedController->type() != EmulatedController::Type::VPAD)
|
||||
++wpadCount;
|
||||
}
|
||||
return wpadCount;
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_getVPADControllersCount(JNIEnv *env, jclass clazz) {
|
||||
jint vpadCount = 0;
|
||||
for (int i = 0; i < InputManager::kMaxController; i++) {
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(
|
||||
i).getEmulatedController();
|
||||
if (!emulatedController)
|
||||
continue;
|
||||
if (emulatedController->type() == EmulatedController::Type::VPAD)
|
||||
++vpadCount;
|
||||
}
|
||||
return vpadCount;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_isControllerDisabled(JNIEnv *env, jclass clazz, jint index) {
|
||||
return AndroidEmulatedController::getAndroidEmulatedController(index).getEmulatedController() ==
|
||||
nullptr;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_setControllerMapping(JNIEnv *env, jclass clazz,
|
||||
jstring device_descriptor,
|
||||
jstring device_name, jint index,
|
||||
jint mapping_id, jint button_id) {
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider *>(apiProvider.get());
|
||||
auto deviceName = JNIUtils::JStringToString(env, device_name);
|
||||
auto deviceDescriptor = JNIUtils::JStringToString(env, device_descriptor);
|
||||
auto controller = ControllerFactory::CreateController(InputAPI::Android, deviceDescriptor,
|
||||
deviceName);
|
||||
AndroidEmulatedController::getAndroidEmulatedController(index).setMapping(mapping_id,
|
||||
controller,
|
||||
button_id);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_getControllerMapping(JNIEnv *env, jclass clazz, jint index,
|
||||
jint mapping_id) {
|
||||
auto mapping = AndroidEmulatedController::getAndroidEmulatedController(index)
|
||||
.getMapping(mapping_id);
|
||||
return env->NewStringUTF(mapping.value_or("").c_str());
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_clearControllerMapping(JNIEnv *env, jclass clazz, jint index,
|
||||
jint mapping_id) {
|
||||
AndroidEmulatedController::getAndroidEmulatedController(index).clearMapping(mapping_id);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_getControllerMappings(JNIEnv *env, jclass clazz, jint index) {
|
||||
jclass hashMapClass = env->FindClass("java/util/HashMap");
|
||||
jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()V");
|
||||
jmethodID hashMapPut = env->GetMethodID(hashMapClass, "put",
|
||||
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
jclass integerClass = env->FindClass("java/lang/Integer");
|
||||
jmethodID integerConstructor = env->GetMethodID(integerClass, "<init>", "(I)V");
|
||||
jobject hashMapObj = env->NewObject(hashMapClass, hashMapConstructor);
|
||||
auto mappings = AndroidEmulatedController::getAndroidEmulatedController(index).getMappings();
|
||||
for (const auto &pair: mappings) {
|
||||
jint key = pair.first;
|
||||
jstring buttonName = env->NewStringUTF(pair.second.c_str());
|
||||
jobject mappingId = env->NewObject(integerClass, integerConstructor, key);
|
||||
env->CallObjectMethod(hashMapObj, hashMapPut, mappingId, buttonName);
|
||||
}
|
||||
return hashMapObj;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_onKeyEvent(JNIEnv *env, jclass clazz, jstring device_descriptor,
|
||||
jstring device_name, jint key_code,
|
||||
jboolean is_pressed) {
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider *>(apiProvider.get());
|
||||
auto deviceDescriptor = JNIUtils::JStringToString(env, device_descriptor);
|
||||
auto deviceName = JNIUtils::JStringToString(env, device_name);
|
||||
androidControllerProvider->on_key_event(deviceDescriptor, deviceName, key_code, is_pressed);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_onAxisEvent(JNIEnv *env, jclass clazz, jstring device_descriptor,
|
||||
jstring device_name, jint axis_code,
|
||||
jfloat value) {
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider *>(apiProvider.get());
|
||||
auto deviceDescriptor = JNIUtils::JStringToString(env, device_descriptor);
|
||||
auto deviceName = JNIUtils::JStringToString(env, device_name);
|
||||
androidControllerProvider->on_axis_event(deviceDescriptor, deviceName, axis_code, value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ButtonListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
public interface OnButtonClickListener {
|
||||
void onButtonClicked();
|
||||
}
|
||||
|
||||
private static class ButtonInfo {
|
||||
public ButtonInfo(String text, String description, OnButtonClickListener onButtonClickListener) {
|
||||
this.text = text;
|
||||
this.description = description;
|
||||
this.onButtonClickListener = onButtonClickListener;
|
||||
}
|
||||
|
||||
String text;
|
||||
String description;
|
||||
OnButtonClickListener onButtonClickListener;
|
||||
}
|
||||
|
||||
private final List<ButtonInfo> buttonInfoList = new ArrayList<>();
|
||||
|
||||
public void addButtonInfo(String text, String description, OnButtonClickListener onButtonClickListener) {
|
||||
buttonInfoList.add(new ButtonInfo(text, description, onButtonClickListener));
|
||||
notifyItemInserted(buttonInfoList.size());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new ButtonViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_button, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
((ButtonViewHolder) holder).bind(buttonInfoList.get(position));
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
ButtonInfo buttonInfo = buttonInfoList.get(holder.getAdapterPosition());
|
||||
if (buttonInfo.onButtonClickListener != null) {
|
||||
buttonInfo.onButtonClickListener.onButtonClicked();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return buttonInfoList.size();
|
||||
}
|
||||
|
||||
private static class ButtonViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView text;
|
||||
TextView description;
|
||||
|
||||
void bind(ButtonInfo buttonInfo) {
|
||||
text.setText(buttonInfo.text);
|
||||
description.setText(buttonInfo.description);
|
||||
}
|
||||
|
||||
public ButtonViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
text = itemView.findViewById(R.id.button_text);
|
||||
description = itemView.findViewById(R.id.button_description);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
public class ControllerHeaderItem implements ControllerItem {
|
||||
private String header;
|
||||
|
||||
public ControllerHeaderItem(String header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return TYPE_HEADER;
|
||||
}
|
||||
|
||||
public String getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public void setHeader(String header) {
|
||||
this.header = header;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
public class ControllerInputItem implements ControllerItem {
|
||||
private final int nameResourceId;
|
||||
private final int id;
|
||||
private String boundInput;
|
||||
|
||||
public ControllerInputItem(int nameResourceId, int id, String boundInput) {
|
||||
this.nameResourceId = nameResourceId;
|
||||
this.id = id;
|
||||
this.boundInput = boundInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return TYPE_INPUT;
|
||||
}
|
||||
|
||||
public int getNameResourceId() {
|
||||
return nameResourceId;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getBoundInput() {
|
||||
return boundInput;
|
||||
}
|
||||
|
||||
public void setBoundInput(String boundInput) {
|
||||
this.boundInput = boundInput;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class ControllerInputListener {
|
||||
private static boolean isController(int sources) {
|
||||
return ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK) ||
|
||||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD);
|
||||
}
|
||||
|
||||
public static boolean onKeyEvent(KeyEvent keyEvent) {
|
||||
InputDevice inputDevice = keyEvent.getDevice();
|
||||
if (isController(inputDevice.getSources())) {
|
||||
String descriptor = inputDevice.getDescriptor();
|
||||
String name = inputDevice.getName();
|
||||
NativeLibrary.onNativeKey(descriptor == null ? name : descriptor, name, keyEvent.getKeyCode(), keyEvent.getAction() == KeyEvent.ACTION_DOWN);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean onMotionEvent(MotionEvent motionEvent) {
|
||||
int actionPointerIndex = motionEvent.getActionIndex();
|
||||
int action = motionEvent.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_MOVE) {
|
||||
InputDevice inputDevice = motionEvent.getDevice();
|
||||
String descriptor = inputDevice.getDescriptor();
|
||||
String name = inputDevice.getName();
|
||||
for (InputDevice.MotionRange range : inputDevice.getMotionRanges()) {
|
||||
int axis = range.getAxis();
|
||||
float value = (motionEvent.getAxisValue(axis, actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
|
||||
NativeLibrary.onNativeAxis(descriptor == null ? name : descriptor, name, axis, value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ControllerInputsDataProvider {
|
||||
private int getButtonResourceIdName(int controllerType, int buttonId) {
|
||||
if (controllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_VPAD) {
|
||||
return switch (buttonId) {
|
||||
case NativeLibrary.VPAD_BUTTON_A -> R.string.button_a;
|
||||
case NativeLibrary.VPAD_BUTTON_B -> R.string.button_b;
|
||||
case NativeLibrary.VPAD_BUTTON_X -> R.string.button_x;
|
||||
case NativeLibrary.VPAD_BUTTON_Y -> R.string.button_y;
|
||||
case NativeLibrary.VPAD_BUTTON_L -> R.string.button_l;
|
||||
case NativeLibrary.VPAD_BUTTON_R -> R.string.button_r;
|
||||
case NativeLibrary.VPAD_BUTTON_ZL -> R.string.button_zl;
|
||||
case NativeLibrary.VPAD_BUTTON_ZR -> R.string.button_zr;
|
||||
case NativeLibrary.VPAD_BUTTON_PLUS -> R.string.button_plus;
|
||||
case NativeLibrary.VPAD_BUTTON_MINUS -> R.string.button_minus;
|
||||
case NativeLibrary.VPAD_BUTTON_UP -> R.string.button_up;
|
||||
case NativeLibrary.VPAD_BUTTON_DOWN -> R.string.button_down;
|
||||
case NativeLibrary.VPAD_BUTTON_LEFT -> R.string.button_left;
|
||||
case NativeLibrary.VPAD_BUTTON_RIGHT -> R.string.button_right;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKL -> R.string.button_stickl;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKR -> R.string.button_stickr;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKL_UP -> R.string.button_stickl_up;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKL_DOWN -> R.string.button_stickl_down;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKL_LEFT -> R.string.button_stickl_left;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKL_RIGHT -> R.string.button_stickl_right;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKR_UP -> R.string.button_stickr_up;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKR_DOWN -> R.string.button_stickr_down;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKR_LEFT -> R.string.button_stickr_left;
|
||||
case NativeLibrary.VPAD_BUTTON_STICKR_RIGHT -> R.string.button_stickr_right;
|
||||
case NativeLibrary.VPAD_BUTTON_MIC -> R.string.button_mic;
|
||||
case NativeLibrary.VPAD_BUTTON_SCREEN -> R.string.button_screen;
|
||||
case NativeLibrary.VPAD_BUTTON_HOME -> R.string.button_home;
|
||||
default ->
|
||||
throw new RuntimeException("Invalid buttonId " + buttonId + " for controllerType " + controllerType);
|
||||
};
|
||||
}
|
||||
if (controllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_PRO) {
|
||||
return switch (buttonId) {
|
||||
case NativeLibrary.PRO_BUTTON_A -> R.string.button_a;
|
||||
case NativeLibrary.PRO_BUTTON_B -> R.string.button_b;
|
||||
case NativeLibrary.PRO_BUTTON_X -> R.string.button_x;
|
||||
case NativeLibrary.PRO_BUTTON_Y -> R.string.button_y;
|
||||
case NativeLibrary.PRO_BUTTON_L -> R.string.button_l;
|
||||
case NativeLibrary.PRO_BUTTON_R -> R.string.button_r;
|
||||
case NativeLibrary.PRO_BUTTON_ZL -> R.string.button_zl;
|
||||
case NativeLibrary.PRO_BUTTON_ZR -> R.string.button_zr;
|
||||
case NativeLibrary.PRO_BUTTON_PLUS -> R.string.button_plus;
|
||||
case NativeLibrary.PRO_BUTTON_MINUS -> R.string.button_minus;
|
||||
case NativeLibrary.PRO_BUTTON_HOME -> R.string.button_home;
|
||||
case NativeLibrary.PRO_BUTTON_UP -> R.string.button_up;
|
||||
case NativeLibrary.PRO_BUTTON_DOWN -> R.string.button_down;
|
||||
case NativeLibrary.PRO_BUTTON_LEFT -> R.string.button_left;
|
||||
case NativeLibrary.PRO_BUTTON_RIGHT -> R.string.button_right;
|
||||
case NativeLibrary.PRO_BUTTON_STICKL -> R.string.button_stickl;
|
||||
case NativeLibrary.PRO_BUTTON_STICKR -> R.string.button_stickr;
|
||||
case NativeLibrary.PRO_BUTTON_STICKL_UP -> R.string.button_stickl_up;
|
||||
case NativeLibrary.PRO_BUTTON_STICKL_DOWN -> R.string.button_stickl_down;
|
||||
case NativeLibrary.PRO_BUTTON_STICKL_LEFT -> R.string.button_stickl_left;
|
||||
case NativeLibrary.PRO_BUTTON_STICKL_RIGHT -> R.string.button_stickl_right;
|
||||
case NativeLibrary.PRO_BUTTON_STICKR_UP -> R.string.button_stickr_up;
|
||||
case NativeLibrary.PRO_BUTTON_STICKR_DOWN -> R.string.button_stickr_down;
|
||||
case NativeLibrary.PRO_BUTTON_STICKR_LEFT -> R.string.button_stickr_left;
|
||||
case NativeLibrary.PRO_BUTTON_STICKR_RIGHT -> R.string.button_stickr_right;
|
||||
default ->
|
||||
throw new RuntimeException("Invalid buttonId " + buttonId + " for controllerType " + controllerType);
|
||||
};
|
||||
}
|
||||
if (controllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_CLASSIC) {
|
||||
return switch (buttonId) {
|
||||
case NativeLibrary.CLASSIC_BUTTON_A -> R.string.button_a;
|
||||
case NativeLibrary.CLASSIC_BUTTON_B -> R.string.button_b;
|
||||
case NativeLibrary.CLASSIC_BUTTON_X -> R.string.button_x;
|
||||
case NativeLibrary.CLASSIC_BUTTON_Y -> R.string.button_y;
|
||||
case NativeLibrary.CLASSIC_BUTTON_L -> R.string.button_l;
|
||||
case NativeLibrary.CLASSIC_BUTTON_R -> R.string.button_r;
|
||||
case NativeLibrary.CLASSIC_BUTTON_ZL -> R.string.button_zl;
|
||||
case NativeLibrary.CLASSIC_BUTTON_ZR -> R.string.button_zr;
|
||||
case NativeLibrary.CLASSIC_BUTTON_PLUS -> R.string.button_plus;
|
||||
case NativeLibrary.CLASSIC_BUTTON_MINUS -> R.string.button_minus;
|
||||
case NativeLibrary.CLASSIC_BUTTON_HOME -> R.string.button_home;
|
||||
case NativeLibrary.CLASSIC_BUTTON_UP -> R.string.button_up;
|
||||
case NativeLibrary.CLASSIC_BUTTON_DOWN -> R.string.button_down;
|
||||
case NativeLibrary.CLASSIC_BUTTON_LEFT -> R.string.button_left;
|
||||
case NativeLibrary.CLASSIC_BUTTON_RIGHT -> R.string.button_right;
|
||||
case NativeLibrary.CLASSIC_BUTTON_STICKL_UP -> R.string.button_stickl_up;
|
||||
case NativeLibrary.CLASSIC_BUTTON_STICKL_DOWN -> R.string.button_stickl_down;
|
||||
case NativeLibrary.CLASSIC_BUTTON_STICKL_LEFT -> R.string.button_stickl_left;
|
||||
case NativeLibrary.CLASSIC_BUTTON_STICKL_RIGHT -> R.string.button_stickl_right;
|
||||
case NativeLibrary.CLASSIC_BUTTON_STICKR_UP -> R.string.button_stickr_up;
|
||||
case NativeLibrary.CLASSIC_BUTTON_STICKR_DOWN -> R.string.button_stickr_down;
|
||||
case NativeLibrary.CLASSIC_BUTTON_STICKR_LEFT -> R.string.button_stickr_left;
|
||||
case NativeLibrary.CLASSIC_BUTTON_STICKR_RIGHT -> R.string.button_stickr_right;
|
||||
default ->
|
||||
throw new RuntimeException("Invalid buttonId " + buttonId + " for controllerType " + controllerType);
|
||||
};
|
||||
}
|
||||
if (controllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_WIIMOTE) {
|
||||
return switch (buttonId) {
|
||||
case NativeLibrary.WIIMOTE_BUTTON_A -> R.string.button_a;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_B -> R.string.button_b;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_1 -> R.string.button_1;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_2 -> R.string.button_2;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_Z -> R.string.button_nunchuck_z;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_C -> R.string.button_nunchuck_c;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_PLUS -> R.string.button_plus;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_MINUS -> R.string.button_minus;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_UP -> R.string.button_up;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_DOWN -> R.string.button_down;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_LEFT -> R.string.button_left;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_RIGHT -> R.string.button_right;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_UP -> R.string.button_nunchuck_up;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_DOWN -> R.string.button_nunchuck_down;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_LEFT -> R.string.button_nunchuck_left;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_RIGHT -> R.string.button_nunchuck_right;
|
||||
case NativeLibrary.WIIMOTE_BUTTON_HOME -> R.string.button_home;
|
||||
default ->
|
||||
throw new RuntimeException("Invalid buttonId " + buttonId + " for controllerType " + controllerType);
|
||||
};
|
||||
}
|
||||
throw new RuntimeException("Invalid controllerType " + controllerType);
|
||||
}
|
||||
|
||||
private void addControllerInputsGroup(List<ControllerItem> controllerInputItems, String groupName, int controllerType, int[] buttons, Map<Integer, String> inputs) {
|
||||
controllerInputItems.add(new ControllerHeaderItem(groupName));
|
||||
for (int button : buttons) {
|
||||
controllerInputItems.add(new ControllerInputItem(getButtonResourceIdName(controllerType, button), button, inputs.getOrDefault(button, "")));
|
||||
}
|
||||
}
|
||||
|
||||
public List<ControllerItem> getControllerInputs(int controllerType, Map<Integer, String> inputs) {
|
||||
List<ControllerItem> controllerInputItems = new ArrayList<>();
|
||||
if (controllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_VPAD) {
|
||||
addControllerInputsGroup(controllerInputItems, "Buttons", controllerType, new int[]{NativeLibrary.VPAD_BUTTON_A, NativeLibrary.VPAD_BUTTON_B, NativeLibrary.VPAD_BUTTON_X, NativeLibrary.VPAD_BUTTON_Y, NativeLibrary.VPAD_BUTTON_L, NativeLibrary.VPAD_BUTTON_R, NativeLibrary.VPAD_BUTTON_ZL, NativeLibrary.VPAD_BUTTON_ZR, NativeLibrary.VPAD_BUTTON_PLUS, NativeLibrary.VPAD_BUTTON_MINUS}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "D-Pad", controllerType, new int[]{NativeLibrary.VPAD_BUTTON_UP, NativeLibrary.VPAD_BUTTON_DOWN, NativeLibrary.VPAD_BUTTON_LEFT, NativeLibrary.VPAD_BUTTON_RIGHT}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "Left Axis", controllerType, new int[]{NativeLibrary.VPAD_BUTTON_STICKL, NativeLibrary.VPAD_BUTTON_STICKL_UP, NativeLibrary.VPAD_BUTTON_STICKL_DOWN, NativeLibrary.VPAD_BUTTON_STICKL_LEFT, NativeLibrary.VPAD_BUTTON_STICKL_RIGHT}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "Right Axis", controllerType, new int[]{NativeLibrary.VPAD_BUTTON_STICKR, NativeLibrary.VPAD_BUTTON_STICKR_UP, NativeLibrary.VPAD_BUTTON_STICKR_DOWN, NativeLibrary.VPAD_BUTTON_STICKR_LEFT, NativeLibrary.VPAD_BUTTON_STICKR_RIGHT}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "Extra", controllerType, new int[]{NativeLibrary.VPAD_BUTTON_MIC, NativeLibrary.VPAD_BUTTON_SCREEN, NativeLibrary.VPAD_BUTTON_HOME}, inputs);
|
||||
}
|
||||
if (controllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_PRO) {
|
||||
addControllerInputsGroup(controllerInputItems, "Buttons", controllerType, new int[]{NativeLibrary.PRO_BUTTON_A, NativeLibrary.PRO_BUTTON_B, NativeLibrary.PRO_BUTTON_X, NativeLibrary.PRO_BUTTON_Y, NativeLibrary.PRO_BUTTON_L, NativeLibrary.PRO_BUTTON_R, NativeLibrary.PRO_BUTTON_ZL, NativeLibrary.PRO_BUTTON_ZR, NativeLibrary.PRO_BUTTON_PLUS, NativeLibrary.PRO_BUTTON_MINUS, NativeLibrary.PRO_BUTTON_HOME}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "Left Axis", controllerType, new int[]{NativeLibrary.PRO_BUTTON_STICKL, NativeLibrary.PRO_BUTTON_STICKL_UP, NativeLibrary.PRO_BUTTON_STICKL_DOWN, NativeLibrary.PRO_BUTTON_STICKL_LEFT, NativeLibrary.PRO_BUTTON_STICKL_RIGHT}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "Right Axis", controllerType, new int[]{NativeLibrary.PRO_BUTTON_STICKR, NativeLibrary.PRO_BUTTON_STICKR_UP, NativeLibrary.PRO_BUTTON_STICKR_DOWN, NativeLibrary.PRO_BUTTON_STICKR_LEFT, NativeLibrary.PRO_BUTTON_STICKR_RIGHT}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "D-Pad", controllerType, new int[]{NativeLibrary.PRO_BUTTON_UP, NativeLibrary.PRO_BUTTON_DOWN, NativeLibrary.PRO_BUTTON_LEFT, NativeLibrary.PRO_BUTTON_RIGHT}, inputs);
|
||||
}
|
||||
if (controllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_CLASSIC) {
|
||||
addControllerInputsGroup(controllerInputItems, "Buttons", controllerType, new int[]{NativeLibrary.CLASSIC_BUTTON_A, NativeLibrary.CLASSIC_BUTTON_B, NativeLibrary.CLASSIC_BUTTON_X, NativeLibrary.CLASSIC_BUTTON_Y, NativeLibrary.CLASSIC_BUTTON_L, NativeLibrary.CLASSIC_BUTTON_R, NativeLibrary.CLASSIC_BUTTON_ZL, NativeLibrary.CLASSIC_BUTTON_ZR, NativeLibrary.CLASSIC_BUTTON_PLUS, NativeLibrary.CLASSIC_BUTTON_MINUS, NativeLibrary.CLASSIC_BUTTON_HOME}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "Left Axis", controllerType, new int[]{NativeLibrary.CLASSIC_BUTTON_STICKL_UP, NativeLibrary.CLASSIC_BUTTON_STICKL_DOWN, NativeLibrary.CLASSIC_BUTTON_STICKL_LEFT, NativeLibrary.CLASSIC_BUTTON_STICKL_RIGHT}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "Right Axis", controllerType, new int[]{NativeLibrary.CLASSIC_BUTTON_STICKR_UP, NativeLibrary.CLASSIC_BUTTON_STICKR_DOWN, NativeLibrary.CLASSIC_BUTTON_STICKR_LEFT, NativeLibrary.CLASSIC_BUTTON_STICKR_RIGHT}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "D-Pad", controllerType, new int[]{NativeLibrary.CLASSIC_BUTTON_UP, NativeLibrary.CLASSIC_BUTTON_DOWN, NativeLibrary.CLASSIC_BUTTON_LEFT, NativeLibrary.CLASSIC_BUTTON_RIGHT}, inputs);
|
||||
}
|
||||
if (controllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_WIIMOTE) {
|
||||
addControllerInputsGroup(controllerInputItems, "Buttons", controllerType, new int[]{NativeLibrary.WIIMOTE_BUTTON_A, NativeLibrary.WIIMOTE_BUTTON_B, NativeLibrary.WIIMOTE_BUTTON_1, NativeLibrary.WIIMOTE_BUTTON_2, NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_Z, NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_C, NativeLibrary.WIIMOTE_BUTTON_PLUS, NativeLibrary.WIIMOTE_BUTTON_MINUS, NativeLibrary.WIIMOTE_BUTTON_HOME}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "D-Pad", controllerType, new int[]{NativeLibrary.WIIMOTE_BUTTON_UP, NativeLibrary.WIIMOTE_BUTTON_DOWN, NativeLibrary.WIIMOTE_BUTTON_LEFT, NativeLibrary.WIIMOTE_BUTTON_RIGHT}, inputs);
|
||||
addControllerInputsGroup(controllerInputItems, "Nunchuck", controllerType, new int[]{NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_UP, NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_DOWN, NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_LEFT, NativeLibrary.WIIMOTE_BUTTON_NUNCHUCK_RIGHT}, inputs);
|
||||
}
|
||||
return controllerInputItems;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import info.cemu.Cemu.databinding.FragmentControllerInputsBinding;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ControllerInputsFragment extends Fragment {
|
||||
public static final String CONTROLLER_INDEX = "ControllerIndex";
|
||||
private int controllerIndex;
|
||||
private int controllerType;
|
||||
private final InputManager inputManager = new InputManager();
|
||||
private EmulatedControllerTypeAdapter emulatedControllerTypeAdapter;
|
||||
private ControllerListAdapter controllerListAdapter;
|
||||
|
||||
private void onTypeChanged(int controllerType) {
|
||||
if (this.controllerType == controllerType) return;
|
||||
this.controllerType = controllerType;
|
||||
NativeLibrary.setControllerType(controllerIndex, controllerType);
|
||||
emulatedControllerTypeAdapter.setCurrentControllerType(controllerType);
|
||||
controllerListAdapter.setEmulatedControllerListItems(controllerType, new HashMap<>());
|
||||
emulatedControllerTypeAdapter.setControllerTypeCounts(NativeLibrary.getVPADControllersCount(), NativeLibrary.getWPADControllersCount());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
controllerIndex = requireArguments().getInt(CONTROLLER_INDEX);
|
||||
ActionBar actionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar();
|
||||
if (actionBar != null)
|
||||
actionBar.setTitle(getString(R.string.controller_numbered, controllerIndex + 1));
|
||||
if (NativeLibrary.isControllerDisabled(controllerIndex))
|
||||
controllerType = NativeLibrary.EMULATED_CONTROLLER_TYPE_DISABLED;
|
||||
else controllerType = NativeLibrary.getControllerType(controllerIndex);
|
||||
controllerListAdapter = new ControllerListAdapter();
|
||||
emulatedControllerTypeAdapter = new EmulatedControllerTypeAdapter();
|
||||
emulatedControllerTypeAdapter.setCurrentControllerType(controllerType);
|
||||
emulatedControllerTypeAdapter.setControllerTypeCounts(NativeLibrary.getVPADControllersCount(), NativeLibrary.getWPADControllersCount());
|
||||
|
||||
FragmentControllerInputsBinding binding = FragmentControllerInputsBinding.inflate(inflater, container, false);
|
||||
|
||||
binding.emulatedControllerSpinner.setAdapter(emulatedControllerTypeAdapter);
|
||||
binding.emulatedControllerSpinner.setSelection(emulatedControllerTypeAdapter.getPosition(controllerType));
|
||||
binding.emulatedControllerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {
|
||||
onTypeChanged(emulatedControllerTypeAdapter.getItem(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
}
|
||||
});
|
||||
|
||||
controllerListAdapter.setEmulatedControllerListItems(controllerType, NativeLibrary.getControllerMappings(controllerIndex));
|
||||
controllerListAdapter.setOnInputClickListener((nameId, id, position) -> {
|
||||
MotionDialog inputDialog = new MotionDialog(getContext());
|
||||
inputDialog.setOnKeyListener((dialog, keyCode, keyEvent) -> {
|
||||
if (inputManager.mapKeyEventToMappingId(controllerIndex, id, keyEvent)) {
|
||||
controllerListAdapter.setBoundInput(position, NativeLibrary.getControllerMapping(controllerIndex, id));
|
||||
inputDialog.dismiss();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
inputDialog.setOnMotionListener(motionEvent -> {
|
||||
if (inputManager.mapMotionEventToMappingId(controllerIndex, id, motionEvent)) {
|
||||
controllerListAdapter.setBoundInput(position, NativeLibrary.getControllerMapping(controllerIndex, id));
|
||||
inputDialog.dismiss();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
inputDialog.setMessage(getString(R.string.inputBindingDialogMessage, getString(nameId)));
|
||||
inputDialog.setButton(AlertDialog.BUTTON_NEUTRAL, getString(R.string.clear), (dialogInterface, i) -> {
|
||||
NativeLibrary.clearControllerMapping(controllerIndex, id);
|
||||
controllerListAdapter.clearBoundInput(position);
|
||||
dialogInterface.dismiss();
|
||||
});
|
||||
inputDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel), (dialogInterface, i) -> dialogInterface.dismiss());
|
||||
inputDialog.setTitle(R.string.inputBindingDialogTitle);
|
||||
inputDialog.show();
|
||||
});
|
||||
binding.controllerInputsRecyclerView.setAdapter(controllerListAdapter);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
public interface ControllerItem {
|
||||
int TYPE_HEADER = 0;
|
||||
int TYPE_INPUT = 1;
|
||||
|
||||
int getType();
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ControllerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
private List<ControllerItem> controllerItemList = new ArrayList<>();
|
||||
private final ControllerInputsDataProvider controllerInputs = new ControllerInputsDataProvider();
|
||||
private OnInputClickListener onInputClickListener;
|
||||
|
||||
|
||||
public void setEmulatedControllerListItems(int controllerType, Map<Integer, String> inputsMapping) {
|
||||
controllerItemList = controllerInputs.getControllerInputs(controllerType, inputsMapping);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clearBoundInput(int position) {
|
||||
ControllerItem listItem = controllerItemList.get(position);
|
||||
if (listItem.getType() == ControllerItem.TYPE_INPUT) {
|
||||
ControllerInputItem controllerInputItem = (ControllerInputItem) listItem;
|
||||
controllerInputItem.setBoundInput("");
|
||||
controllerItemList.set(position, controllerInputItem);
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnInputClickListener {
|
||||
void onInputClicked(int nameId, int id, int position);
|
||||
}
|
||||
|
||||
void setOnInputClickListener(OnInputClickListener onInputClickListener) {
|
||||
this.onInputClickListener = onInputClickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return controllerItemList.get(position).getType();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
if (viewType == ControllerItem.TYPE_HEADER) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_controller_header, parent, false);
|
||||
return new HeaderViewHolder(view);
|
||||
} else if (viewType == ControllerItem.TYPE_INPUT) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_controller_input, parent, false);
|
||||
return new InputViewHolder(view);
|
||||
}
|
||||
throw new RuntimeException("Invalid viewType: " + viewType);
|
||||
}
|
||||
|
||||
public void setBoundInput(int inputItemPosition, String boundInput) {
|
||||
ControllerItem listItem = controllerItemList.get(inputItemPosition);
|
||||
if (listItem.getType() == ControllerItem.TYPE_INPUT) {
|
||||
ControllerInputItem controllerInputItem = (ControllerInputItem) listItem;
|
||||
controllerInputItem.setBoundInput(boundInput);
|
||||
controllerItemList.set(inputItemPosition, controllerInputItem);
|
||||
notifyItemChanged(inputItemPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
if (holder instanceof HeaderViewHolder) {
|
||||
((HeaderViewHolder) holder).bind((ControllerHeaderItem) controllerItemList.get(position));
|
||||
} else if (holder instanceof InputViewHolder inputViewHolder) {
|
||||
ControllerInputItem inputListItem = (ControllerInputItem) controllerItemList.get(position);
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (onInputClickListener != null) {
|
||||
onInputClickListener.onInputClicked(inputListItem.getNameResourceId(), inputListItem.getId(), holder.getAdapterPosition());
|
||||
}
|
||||
});
|
||||
inputViewHolder.bind(inputListItem);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return controllerItemList.size();
|
||||
}
|
||||
|
||||
public static class InputViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView inputName;
|
||||
TextView boundInput;
|
||||
|
||||
void bind(ControllerInputItem inputListItem) {
|
||||
inputName.setText(inputListItem.getNameResourceId());
|
||||
boundInput.setText(inputListItem.getBoundInput());
|
||||
}
|
||||
|
||||
public InputViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
inputName = itemView.findViewById(R.id.controller_input_name);
|
||||
boundInput = itemView.findViewById(R.id.controller_button_name);
|
||||
}
|
||||
}
|
||||
|
||||
public static class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView header;
|
||||
|
||||
void bind(ControllerHeaderItem headerListItem) {
|
||||
header.setText(headerListItem.getHeader());
|
||||
}
|
||||
|
||||
public HeaderViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
header = itemView.findViewById(R.id.textview_controller_header);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EmulatedControllerTypeAdapter extends BaseAdapter {
|
||||
private final List<Integer> controllerTypes = List.of(NativeLibrary.EMULATED_CONTROLLER_TYPE_DISABLED, NativeLibrary.EMULATED_CONTROLLER_TYPE_VPAD, NativeLibrary.EMULATED_CONTROLLER_TYPE_PRO, NativeLibrary.EMULATED_CONTROLLER_TYPE_CLASSIC, NativeLibrary.EMULATED_CONTROLLER_TYPE_WIIMOTE);
|
||||
private int vpadCount = 0;
|
||||
private int wpadCount = 0;
|
||||
private int currentControllerType = NativeLibrary.EMULATED_CONTROLLER_TYPE_DISABLED;
|
||||
|
||||
public void setCurrentControllerType(int currentControllerType) {
|
||||
this.currentControllerType = currentControllerType;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setControllerTypeCounts(int vpadCount, int wpadCount) {
|
||||
this.vpadCount = vpadCount;
|
||||
this.wpadCount = wpadCount;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int position) {
|
||||
int type = controllerTypes.get(position);
|
||||
if (type == NativeLibrary.EMULATED_CONTROLLER_TYPE_DISABLED)
|
||||
return true;
|
||||
if (type == NativeLibrary.EMULATED_CONTROLLER_TYPE_VPAD)
|
||||
return currentControllerType == NativeLibrary.EMULATED_CONTROLLER_TYPE_VPAD || vpadCount < NativeLibrary.MAX_VPAD_CONTROLLERS;
|
||||
boolean isWPAD = currentControllerType != NativeLibrary.EMULATED_CONTROLLER_TYPE_VPAD && currentControllerType != NativeLibrary.EMULATED_CONTROLLER_TYPE_DISABLED;
|
||||
return isWPAD || wpadCount < NativeLibrary.MAX_WPAD_CONTROLLERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return controllerTypes.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getItem(int position) {
|
||||
return controllerTypes.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup viewGroup) {
|
||||
int type = controllerTypes.get(position);
|
||||
if (view == null)
|
||||
view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_choice, viewGroup, false);
|
||||
TextView textView = view.findViewById(R.id.textview_choice);
|
||||
textView.setText(NativeLibrary.controllerTypeToResourceNameId(type));
|
||||
float DISABLED_ALPHA = 0.6f;
|
||||
if (!isEnabled(position))
|
||||
view.setAlpha(DISABLED_ALPHA);
|
||||
return view;
|
||||
}
|
||||
|
||||
public int getPosition(int controllerType) {
|
||||
return controllerTypes.indexOf(controllerType);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,10 @@ import androidx.core.view.WindowInsetsControllerCompat;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import info.cemu.Cemu.databinding.ActivityEmulationBinding;
|
||||
import info.cemu.Cemu.databinding.ActivityMainBinding;
|
||||
@@ -16,9 +20,24 @@ import info.cemu.Cemu.databinding.ActivityMainBinding;
|
||||
public class EmulationActivity extends AppCompatActivity {
|
||||
|
||||
public static final String GAME_TITLE_ID = "GameTitleId";
|
||||
long gameTitleId;
|
||||
ActivityEmulationBinding binding;
|
||||
EmulationFragment emulationFragment;
|
||||
private long gameTitleId;
|
||||
private ActivityEmulationBinding binding;
|
||||
private EmulationFragment emulationFragment;
|
||||
private final InputManager inputManager = new InputManager();
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
if (inputManager.onMotionEvent(event))
|
||||
return true;
|
||||
return super.onGenericMotionEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if (inputManager.onKeyEvent(event))
|
||||
return true;
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -32,7 +51,7 @@ public class EmulationActivity extends AppCompatActivity {
|
||||
setContentView(binding.getRoot());
|
||||
emulationFragment = (EmulationFragment) getSupportFragmentManager().findFragmentById(R.id.emulation_frame);
|
||||
if (emulationFragment == null) {
|
||||
emulationFragment = EmulationFragment.newInstance(gameTitleId);
|
||||
emulationFragment = new EmulationFragment(gameTitleId);
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.add(R.id.emulation_frame, emulationFragment)
|
||||
@@ -46,19 +65,18 @@ public class EmulationActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void showExitConfirmationDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Exit Confirmation");
|
||||
builder.setMessage("Are you sure you want to exit?");
|
||||
builder.setPositiveButton("Yes", (dialog, which) -> {
|
||||
finishAffinity();
|
||||
System.exit(0);
|
||||
});
|
||||
builder.setNegativeButton("No", (dialog, which) -> dialog.cancel());
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
|
||||
builder.setTitle(R.string.exit_confirmation_title)
|
||||
.setMessage(R.string.exit_confirm_message)
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
finishAffinity();
|
||||
System.exit(0);
|
||||
}).setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
void setFullscreen() {
|
||||
private void setFullscreen() {
|
||||
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||
WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(getWindow(), getWindow().getDecorView());
|
||||
controller.hide(WindowInsetsCompat.Type.systemBars());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
@@ -13,35 +14,17 @@ import androidx.fragment.app.Fragment;
|
||||
import info.cemu.Cemu.databinding.FragmentEmulationBinding;
|
||||
|
||||
public class EmulationFragment extends Fragment implements SurfaceHolder.Callback {
|
||||
FragmentEmulationBinding binding;
|
||||
|
||||
public EmulationFragment() {
|
||||
}
|
||||
|
||||
static EmulationFragment newInstance(long gameTitleId) {
|
||||
EmulationFragment emulationFragment = new EmulationFragment();
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putLong(EmulationActivity.GAME_TITLE_ID, gameTitleId);
|
||||
emulationFragment.setArguments(arguments);
|
||||
return emulationFragment;
|
||||
}
|
||||
|
||||
long gameTitleId;
|
||||
boolean isGameRunning;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Bundle arguments = getArguments();
|
||||
if (arguments != null) {
|
||||
gameTitleId = arguments.getLong(EmulationActivity.GAME_TITLE_ID);
|
||||
}
|
||||
private final long gameTitleId;
|
||||
private boolean isGameRunning;
|
||||
private SurfaceHolder mainCanvasSurfaceHolder;
|
||||
|
||||
public EmulationFragment(long gameTitleId) {
|
||||
this.gameTitleId = gameTitleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = FragmentEmulationBinding.inflate(inflater, container, false);
|
||||
FragmentEmulationBinding binding = FragmentEmulationBinding.inflate(inflater, container, false);
|
||||
SurfaceView mainCanvas = binding.mainCanvas;
|
||||
mainCanvas.getHolder().addCallback(this);
|
||||
return binding.getRoot();
|
||||
@@ -49,25 +32,29 @@ public class EmulationFragment extends Fragment implements SurfaceHolder.Callbac
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
|
||||
|
||||
mainCanvasSurfaceHolder = surfaceHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int format, int width, int height) {
|
||||
NativeLibrary.setSurface(surfaceHolder.getSurface(), true);
|
||||
NativeLibrary.setSurfaceSize(width, height, true);
|
||||
if (!isGameRunning) {
|
||||
isGameRunning = true;
|
||||
NativeLibrary.setSurface(surfaceHolder.getSurface(), true);
|
||||
NativeLibrary.initializerRenderer();
|
||||
NativeLibrary.initializeRendererSurface(true);
|
||||
NativeLibrary.startGame(gameTitleId);
|
||||
}else{
|
||||
} else {
|
||||
if (mainCanvasSurfaceHolder == surfaceHolder)
|
||||
return;
|
||||
NativeLibrary.setSurface(surfaceHolder.getSurface(), true);
|
||||
NativeLibrary.recreateRenderSurface(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
|
||||
mainCanvasSurfaceHolder = null;
|
||||
NativeLibrary.clearSurface(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class InputManager {
|
||||
private static class InvalidAxisException extends Exception {
|
||||
public InvalidAxisException(int axis) {
|
||||
super("Invalid axis " + axis);
|
||||
}
|
||||
}
|
||||
|
||||
private int getNativeAxisKey(int axis, boolean isPositive) throws InvalidAxisException {
|
||||
if (isPositive) {
|
||||
return switch (axis) {
|
||||
case MotionEvent.AXIS_X -> NativeLibrary.AXIS_X_POS;
|
||||
case MotionEvent.AXIS_Y -> NativeLibrary.AXIS_Y_POS;
|
||||
case MotionEvent.AXIS_RX, MotionEvent.AXIS_Z -> NativeLibrary.ROTATION_X_POS;
|
||||
case MotionEvent.AXIS_RY, MotionEvent.AXIS_RZ -> NativeLibrary.ROTATION_Y_POS;
|
||||
case MotionEvent.AXIS_LTRIGGER -> NativeLibrary.TRIGGER_X_POS;
|
||||
case MotionEvent.AXIS_RTRIGGER -> NativeLibrary.TRIGGER_Y_POS;
|
||||
case MotionEvent.AXIS_HAT_X -> NativeLibrary.DPAD_RIGHT;
|
||||
case MotionEvent.AXIS_HAT_Y -> NativeLibrary.DPAD_DOWN;
|
||||
default -> throw new InvalidAxisException(axis);
|
||||
};
|
||||
} else {
|
||||
return switch (axis) {
|
||||
case MotionEvent.AXIS_X -> NativeLibrary.AXIS_X_NEG;
|
||||
case MotionEvent.AXIS_Y -> NativeLibrary.AXIS_Y_NEG;
|
||||
case MotionEvent.AXIS_RX, MotionEvent.AXIS_Z -> NativeLibrary.ROTATION_X_NEG;
|
||||
case MotionEvent.AXIS_RY, MotionEvent.AXIS_RZ -> NativeLibrary.ROTATION_Y_NEG;
|
||||
case MotionEvent.AXIS_HAT_X -> NativeLibrary.DPAD_LEFT;
|
||||
case MotionEvent.AXIS_HAT_Y -> NativeLibrary.DPAD_UP;
|
||||
default -> throw new InvalidAxisException(axis);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMotionEventFromJoystick(MotionEvent event) {
|
||||
return (event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE;
|
||||
}
|
||||
|
||||
public boolean mapMotionEventToMappingId(int controllerIndex, int mappingId, MotionEvent event) {
|
||||
if (isMotionEventFromJoystick(event)) {
|
||||
InputDevice device = event.getDevice();
|
||||
float maxAbsAxisValue = 0.0f;
|
||||
int maxAxis = -1;
|
||||
int actionPointerIndex = event.getActionIndex();
|
||||
for (InputDevice.MotionRange motionRange : device.getMotionRanges()) {
|
||||
float axisValue = event.getAxisValue(motionRange.getAxis(), actionPointerIndex);
|
||||
int axis;
|
||||
try {
|
||||
axis = getNativeAxisKey(motionRange.getAxis(), axisValue > 0);
|
||||
} catch (InvalidAxisException e) {
|
||||
continue;
|
||||
}
|
||||
if (Math.abs(axisValue) > maxAbsAxisValue) {
|
||||
maxAxis = axis;
|
||||
maxAbsAxisValue = Math.abs(axisValue);
|
||||
}
|
||||
}
|
||||
float MIN_ABS_AXIS_VALUE = 0.33f;
|
||||
if (maxAbsAxisValue > MIN_ABS_AXIS_VALUE) {
|
||||
NativeLibrary.setControllerMapping(device.getDescriptor(), device.getName(), controllerIndex, mappingId, maxAxis);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isSpecialKey(KeyEvent event) {
|
||||
int keyCode = event.getKeyCode();
|
||||
return keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
|
||||
keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
|
||||
keyCode == KeyEvent.KEYCODE_CAMERA ||
|
||||
keyCode == KeyEvent.KEYCODE_ZOOM_IN ||
|
||||
keyCode == KeyEvent.KEYCODE_ZOOM_OUT;
|
||||
}
|
||||
|
||||
private boolean isController(InputDevice inputDevice) {
|
||||
|
||||
int sources = inputDevice.getSources();
|
||||
return (sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
|
||||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD);
|
||||
}
|
||||
|
||||
public boolean onKeyEvent(KeyEvent event) {
|
||||
if (isSpecialKey(event))
|
||||
return false;
|
||||
if (event.getDeviceId() < 0)
|
||||
return false;
|
||||
InputDevice device = event.getDevice();
|
||||
if (!isController(device))
|
||||
return false;
|
||||
NativeLibrary.onNativeKey(device.getDescriptor(), device.getName(), event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onMotionEvent(MotionEvent event) {
|
||||
if (!isMotionEventFromJoystick(event))
|
||||
return false;
|
||||
InputDevice device = event.getDevice();
|
||||
int actionPointerIndex = event.getActionIndex();
|
||||
for (InputDevice.MotionRange motionRange : device.getMotionRanges()) {
|
||||
float axisValue = event.getAxisValue(motionRange.getAxis(), actionPointerIndex);
|
||||
int axis = motionRange.getAxis();
|
||||
NativeLibrary.onNativeAxis(device.getDescriptor(), device.getName(), axis, axisValue);
|
||||
Log.i(getClass().getName(), "Axis: " + axis + " value: " + axisValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean mapKeyEventToMappingId(int controllerIndex, int mappingId, KeyEvent event) {
|
||||
Log.i(getClass().getName(), event.toString());
|
||||
InputDevice device = event.getDevice();
|
||||
NativeLibrary.setControllerMapping(device.getDescriptor(), device.getName(), controllerIndex, mappingId, event.getKeyCode());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import info.cemu.Cemu.databinding.FragmentInputSettingsBinding;
|
||||
|
||||
public class InputSettingsFragment extends Fragment {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
FragmentInputSettingsBinding binding = FragmentInputSettingsBinding.inflate(inflater, container, false);
|
||||
ButtonListAdapter buttonListAdapter = new ButtonListAdapter();
|
||||
for (int index = 0; index < NativeLibrary.MAX_CONTROLLERS; index++) {
|
||||
int controllerIndex = index;
|
||||
String controllerType = getString(NativeLibrary.controllerTypeToResourceNameId(NativeLibrary.getControllerType(controllerIndex)));
|
||||
buttonListAdapter.addButtonInfo(getString(R.string.controller_numbered, controllerIndex + 1), getString(R.string.emulated_controller_with_type, controllerType),
|
||||
() -> {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(ControllerInputsFragment.CONTROLLER_INDEX, controllerIndex);
|
||||
NavHostFragment.findNavController(InputSettingsFragment.this).navigate(R.id.action_inputSettingsFragment_to_controllerInputsFragment, bundle);
|
||||
});
|
||||
}
|
||||
binding.inputSettingsRecyclerView.setAdapter(buttonListAdapter);
|
||||
return binding.getRoot();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class MotionDialog extends AlertDialog {
|
||||
public static interface OnMotionListener {
|
||||
boolean onMotion(MotionEvent ev);
|
||||
}
|
||||
|
||||
private OnMotionListener onMotionListener;
|
||||
|
||||
protected MotionDialog(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public void setOnMotionListener(OnMotionListener onMotionListener) {
|
||||
this.onMotionListener = onMotionListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
|
||||
if (onMotionListener != null && onMotionListener.onMotion(event)) {
|
||||
return true;
|
||||
}
|
||||
return super.onGenericMotionEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
public class NativeLibrary {
|
||||
static {
|
||||
@@ -61,4 +63,173 @@ public class NativeLibrary {
|
||||
public static native void initializeEmulation();
|
||||
|
||||
public static native void addGamePath(String uri);
|
||||
|
||||
public static native void onNativeKey(String deviceDescriptor, String deviceName, int key, boolean isPressed);
|
||||
|
||||
public static native void onNativeAxis(String deviceDescriptor, String deviceName, int axis, float value);
|
||||
|
||||
static final int VPAD_BUTTON_NONE = 0;
|
||||
static final int VPAD_BUTTON_A = 1;
|
||||
static final int VPAD_BUTTON_B = 2;
|
||||
static final int VPAD_BUTTON_X = 3;
|
||||
static final int VPAD_BUTTON_Y = 4;
|
||||
static final int VPAD_BUTTON_L = 5;
|
||||
static final int VPAD_BUTTON_R = 6;
|
||||
static final int VPAD_BUTTON_ZL = 7;
|
||||
static final int VPAD_BUTTON_ZR = 8;
|
||||
static final int VPAD_BUTTON_PLUS = 9;
|
||||
static final int VPAD_BUTTON_MINUS = 10;
|
||||
static final int VPAD_BUTTON_UP = 11;
|
||||
static final int VPAD_BUTTON_DOWN = 12;
|
||||
static final int VPAD_BUTTON_LEFT = 13;
|
||||
static final int VPAD_BUTTON_RIGHT = 14;
|
||||
static final int VPAD_BUTTON_STICKL = 15;
|
||||
static final int VPAD_BUTTON_STICKR = 16;
|
||||
static final int VPAD_BUTTON_STICKL_UP = 17;
|
||||
static final int VPAD_BUTTON_STICKL_DOWN = 18;
|
||||
static final int VPAD_BUTTON_STICKL_LEFT = 19;
|
||||
static final int VPAD_BUTTON_STICKL_RIGHT = 20;
|
||||
static final int VPAD_BUTTON_STICKR_UP = 21;
|
||||
static final int VPAD_BUTTON_STICKR_DOWN = 22;
|
||||
static final int VPAD_BUTTON_STICKR_LEFT = 23;
|
||||
static final int VPAD_BUTTON_STICKR_RIGHT = 24;
|
||||
static final int VPAD_BUTTON_MIC = 25;
|
||||
static final int VPAD_BUTTON_SCREEN = 26;
|
||||
static final int VPAD_BUTTON_HOME = 27;
|
||||
static final int VPAD_BUTTON_MAX = 28;
|
||||
|
||||
static final int PRO_BUTTON_NONE = 0;
|
||||
static final int PRO_BUTTON_A = 1;
|
||||
static final int PRO_BUTTON_B = 2;
|
||||
static final int PRO_BUTTON_X = 3;
|
||||
static final int PRO_BUTTON_Y = 4;
|
||||
static final int PRO_BUTTON_L = 5;
|
||||
static final int PRO_BUTTON_R = 6;
|
||||
static final int PRO_BUTTON_ZL = 7;
|
||||
static final int PRO_BUTTON_ZR = 8;
|
||||
static final int PRO_BUTTON_PLUS = 9;
|
||||
static final int PRO_BUTTON_MINUS = 10;
|
||||
static final int PRO_BUTTON_HOME = 11;
|
||||
static final int PRO_BUTTON_UP = 12;
|
||||
static final int PRO_BUTTON_DOWN = 13;
|
||||
static final int PRO_BUTTON_LEFT = 14;
|
||||
static final int PRO_BUTTON_RIGHT = 15;
|
||||
static final int PRO_BUTTON_STICKL = 16;
|
||||
static final int PRO_BUTTON_STICKR = 17;
|
||||
static final int PRO_BUTTON_STICKL_UP = 18;
|
||||
static final int PRO_BUTTON_STICKL_DOWN = 19;
|
||||
static final int PRO_BUTTON_STICKL_LEFT = 20;
|
||||
static final int PRO_BUTTON_STICKL_RIGHT = 21;
|
||||
static final int PRO_BUTTON_STICKR_UP = 22;
|
||||
static final int PRO_BUTTON_STICKR_DOWN = 23;
|
||||
static final int PRO_BUTTON_STICKR_LEFT = 24;
|
||||
static final int PRO_BUTTON_STICKR_RIGHT = 25;
|
||||
static final int PRO_BUTTON_MAX = 26;
|
||||
|
||||
static final int CLASSIC_BUTTON_NONE = 0;
|
||||
static final int CLASSIC_BUTTON_A = 1;
|
||||
static final int CLASSIC_BUTTON_B = 2;
|
||||
static final int CLASSIC_BUTTON_X = 3;
|
||||
static final int CLASSIC_BUTTON_Y = 4;
|
||||
static final int CLASSIC_BUTTON_L = 5;
|
||||
static final int CLASSIC_BUTTON_R = 6;
|
||||
static final int CLASSIC_BUTTON_ZL = 7;
|
||||
static final int CLASSIC_BUTTON_ZR = 8;
|
||||
static final int CLASSIC_BUTTON_PLUS = 9;
|
||||
static final int CLASSIC_BUTTON_MINUS = 10;
|
||||
static final int CLASSIC_BUTTON_HOME = 11;
|
||||
static final int CLASSIC_BUTTON_UP = 12;
|
||||
static final int CLASSIC_BUTTON_DOWN = 13;
|
||||
static final int CLASSIC_BUTTON_LEFT = 14;
|
||||
static final int CLASSIC_BUTTON_RIGHT = 15;
|
||||
static final int CLASSIC_BUTTON_STICKL_UP = 16;
|
||||
static final int CLASSIC_BUTTON_STICKL_DOWN = 17;
|
||||
static final int CLASSIC_BUTTON_STICKL_LEFT = 18;
|
||||
static final int CLASSIC_BUTTON_STICKL_RIGHT = 19;
|
||||
static final int CLASSIC_BUTTON_STICKR_UP = 20;
|
||||
static final int CLASSIC_BUTTON_STICKR_DOWN = 21;
|
||||
static final int CLASSIC_BUTTON_STICKR_LEFT = 22;
|
||||
static final int CLASSIC_BUTTON_STICKR_RIGHT = 23;
|
||||
static final int CLASSIC_BUTTON_MAX = 24;
|
||||
|
||||
static final int WIIMOTE_BUTTON_NONE = 0;
|
||||
static final int WIIMOTE_BUTTON_A = 1;
|
||||
static final int WIIMOTE_BUTTON_B = 2;
|
||||
static final int WIIMOTE_BUTTON_1 = 3;
|
||||
static final int WIIMOTE_BUTTON_2 = 4;
|
||||
static final int WIIMOTE_BUTTON_NUNCHUCK_Z = 5;
|
||||
static final int WIIMOTE_BUTTON_NUNCHUCK_C = 6;
|
||||
static final int WIIMOTE_BUTTON_PLUS = 7;
|
||||
static final int WIIMOTE_BUTTON_MINUS = 8;
|
||||
static final int WIIMOTE_BUTTON_UP = 9;
|
||||
static final int WIIMOTE_BUTTON_DOWN = 10;
|
||||
static final int WIIMOTE_BUTTON_LEFT = 11;
|
||||
static final int WIIMOTE_BUTTON_RIGHT = 12;
|
||||
static final int WIIMOTE_BUTTON_NUNCHUCK_UP = 13;
|
||||
static final int WIIMOTE_BUTTON_NUNCHUCK_DOWN = 14;
|
||||
static final int WIIMOTE_BUTTON_NUNCHUCK_LEFT = 15;
|
||||
static final int WIIMOTE_BUTTON_NUNCHUCK_RIGHT = 16;
|
||||
static final int WIIMOTE_BUTTON_HOME = 17;
|
||||
static final int WIIMOTE_BUTTON_MAX = 18;
|
||||
|
||||
static final int EMULATED_CONTROLLER_TYPE_VPAD = 0;
|
||||
static final int EMULATED_CONTROLLER_TYPE_PRO = 1;
|
||||
static final int EMULATED_CONTROLLER_TYPE_CLASSIC = 2;
|
||||
static final int EMULATED_CONTROLLER_TYPE_WIIMOTE = 3;
|
||||
static final int EMULATED_CONTROLLER_TYPE_DISABLED = -1;
|
||||
|
||||
static final int DPAD_UP = 34;
|
||||
static final int DPAD_DOWN = 35;
|
||||
static final int DPAD_LEFT = 36;
|
||||
static final int DPAD_RIGHT = 37;
|
||||
static final int AXIS_X_POS = 38;
|
||||
static final int AXIS_Y_POS = 39;
|
||||
static final int ROTATION_X_POS = 40;
|
||||
static final int ROTATION_Y_POS = 41;
|
||||
static final int TRIGGER_X_POS = 42;
|
||||
static final int TRIGGER_Y_POS = 43;
|
||||
static final int AXIS_X_NEG = 44;
|
||||
static final int AXIS_Y_NEG = 45;
|
||||
static final int ROTATION_X_NEG = 46;
|
||||
static final int ROTATION_Y_NEG = 47;
|
||||
|
||||
static final int MAX_CONTROLLERS = 8;
|
||||
static final int MAX_VPAD_CONTROLLERS = 2;
|
||||
static final int MAX_WPAD_CONTROLLERS = 7;
|
||||
|
||||
public static int controllerTypeToResourceNameId(int type) {
|
||||
if (type == NativeLibrary.EMULATED_CONTROLLER_TYPE_DISABLED)
|
||||
return R.string.disabled;
|
||||
else if (type == NativeLibrary.EMULATED_CONTROLLER_TYPE_VPAD)
|
||||
return R.string.vpad_controller;
|
||||
else if (type == NativeLibrary.EMULATED_CONTROLLER_TYPE_PRO)
|
||||
return R.string.pro_controller;
|
||||
else if (type == NativeLibrary.EMULATED_CONTROLLER_TYPE_WIIMOTE)
|
||||
return R.string.wiimote_controller;
|
||||
else if (type == NativeLibrary.EMULATED_CONTROLLER_TYPE_CLASSIC)
|
||||
return R.string.classic_controller;
|
||||
throw new RuntimeException("Invalid controller type: " + type);
|
||||
}
|
||||
|
||||
public static native void setControllerType(int index, int emulatedControllerType);
|
||||
|
||||
public static native boolean isControllerDisabled(int index);
|
||||
|
||||
public static native int getControllerType(int index);
|
||||
|
||||
public static native int getWPADControllersCount();
|
||||
|
||||
public static native int getVPADControllersCount();
|
||||
|
||||
public static native void setControllerMapping(String deviceDescriptor, String deviceName, int index, int mappingId, int buttonId);
|
||||
|
||||
public static native void clearControllerMapping(int index, int mappingId);
|
||||
|
||||
public static native String getControllerMapping(int index, int mappingId);
|
||||
|
||||
public static native Map<Integer, String> getControllerMappings(int index);
|
||||
|
||||
public static native void onKeyEvent(String deviceDescriptor, String deviceName, int keyCode, boolean isPressed);
|
||||
|
||||
public static native void onAxisEvent(String deviceDescriptor, String deviceName, int axisCode, float value);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Objects;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.navigation.ui.AppBarConfiguration;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import info.cemu.Cemu.databinding.ActivitySettingsBinding;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private AppBarConfiguration appBarConfiguration;
|
||||
private ActivitySettingsBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivitySettingsBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setSupportActionBar(binding.toolbarSettings);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_settings);
|
||||
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
|
||||
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
|
||||
getSupportFragmentManager().popBackStack();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
return true;
|
||||
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_settings);
|
||||
return NavigationUI.navigateUp(navController, appBarConfiguration)
|
||||
|| super.onSupportNavigateUp();
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class SettingsFragment extends Fragment {
|
||||
View headerLayout = getLayoutInflater().inflate(R.layout.settings_header, null);
|
||||
binding.settingsLayout.addView(headerLayout);
|
||||
SettingsHeaderBinding headerBinding = SettingsHeaderBinding.bind(headerLayout);
|
||||
headerBinding.textviewHeaderName.setText(title);
|
||||
headerBinding.settingsTextviewHeaderName.setText(title);
|
||||
}
|
||||
|
||||
protected void addSpinner(String title, List<String> items, int initialPosition, OnItemSelectedPositionListener itemSelectedPositionListener) {
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".SettingsActivity">
|
||||
|
||||
<LinearLayout
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="?attr/actionBarSize" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
<fragment
|
||||
android:id="@+id/nav_host_fragment_content_settings"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/nav_settings_graph" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
tools:context=".ControllerInputsFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:text="@string/emulated_controller_label" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/emulated_controller_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintWidth_max="400dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/controller_inputs_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="16dp"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
</LinearLayout>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user