mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 13:27:45 -07:00
Android: Add input device selection
This commit is contained in:
parent
2113bf5e3a
commit
2b1dd52750
@ -95,6 +95,8 @@ public final class ControllerInterface
|
||||
*/
|
||||
public static native void refreshDevices();
|
||||
|
||||
public static native String[] getAllDeviceStrings();
|
||||
|
||||
@Keep
|
||||
private static void registerInputDeviceListener()
|
||||
{
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.input.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
|
||||
|
||||
public final class MappingCommon
|
||||
{
|
||||
private MappingCommon()
|
||||
@ -15,10 +19,13 @@ public final class MappingCommon
|
||||
* When this is being called, a separate thread must be calling ControllerInterface's
|
||||
* dispatchKeyEvent and dispatchGenericMotionEvent, otherwise no inputs will be registered.
|
||||
*
|
||||
* @param controller The device to detect inputs from.
|
||||
* @param allDevices Whether to also detect inputs from devices other than the specified one.
|
||||
* @return The input(s) pressed by the user in the form of an InputCommon expression,
|
||||
* or an empty string if there were no inputs.
|
||||
*/
|
||||
public static native String detectInput();
|
||||
public static native String detectInput(@NonNull EmulatedController controller,
|
||||
boolean allDevices);
|
||||
|
||||
public static native void save();
|
||||
}
|
||||
|
@ -21,6 +21,10 @@ public class EmulatedController
|
||||
mPointer = pointer;
|
||||
}
|
||||
|
||||
public native String getDefaultDevice();
|
||||
|
||||
public native void setDefaultDevice(String device);
|
||||
|
||||
public native int getGroupCount();
|
||||
|
||||
public native ControlGroup getGroup(int index);
|
||||
|
@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.input.model.view;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting;
|
||||
|
||||
public class InputDeviceSetting extends StringSingleChoiceSetting
|
||||
{
|
||||
private final EmulatedController mController;
|
||||
|
||||
public InputDeviceSetting(Context context, int titleId, int descriptionId,
|
||||
EmulatedController controller)
|
||||
{
|
||||
super(context, null, titleId, descriptionId, null, null, null);
|
||||
|
||||
mController = controller;
|
||||
|
||||
refreshChoicesAndValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectedChoice(Settings settings)
|
||||
{
|
||||
return mController.getDefaultDevice();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectedValue(Settings settings)
|
||||
{
|
||||
return mController.getDefaultDevice();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedValue(Settings settings, String newValue)
|
||||
{
|
||||
mController.setDefaultDevice(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshChoicesAndValues()
|
||||
{
|
||||
String[] devices = ControllerInterface.getAllDeviceStrings();
|
||||
|
||||
mChoices = devices;
|
||||
mValues = devices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(Settings settings)
|
||||
{
|
||||
setSelectedValue(settings, "");
|
||||
}
|
||||
}
|
@ -47,4 +47,9 @@ public final class InputMappingControlSetting extends SettingsItem
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public EmulatedController getController()
|
||||
{
|
||||
return mController;
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public final class MotionAlertDialog extends AlertDialog
|
||||
mRunning = true;
|
||||
new Thread(() ->
|
||||
{
|
||||
String result = MappingCommon.detectInput();
|
||||
String result = MappingCommon.detectInput(mSetting.getController(), false);
|
||||
mActivity.runOnUiThread(() ->
|
||||
{
|
||||
if (mRunning)
|
||||
|
@ -95,6 +95,11 @@ public abstract class SettingsItem
|
||||
return getSetting() != null;
|
||||
}
|
||||
|
||||
public boolean canClear()
|
||||
{
|
||||
return hasSetting();
|
||||
}
|
||||
|
||||
public void clear(Settings settings)
|
||||
{
|
||||
getSetting().delete(settings);
|
||||
|
@ -14,8 +14,8 @@ public class StringSingleChoiceSetting extends SettingsItem
|
||||
{
|
||||
private AbstractStringSetting mSetting;
|
||||
|
||||
private String[] mChoices;
|
||||
private String[] mValues;
|
||||
protected String[] mChoices;
|
||||
protected String[] mValues;
|
||||
private MenuTag mMenuTag;
|
||||
|
||||
public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId,
|
||||
@ -60,6 +60,19 @@ public class StringSingleChoiceSetting extends SettingsItem
|
||||
return mValues;
|
||||
}
|
||||
|
||||
public String getChoiceAt(int index)
|
||||
{
|
||||
if (mChoices == null)
|
||||
return null;
|
||||
|
||||
if (index >= 0 && index < mChoices.length)
|
||||
{
|
||||
return mChoices[index];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getValueAt(int index)
|
||||
{
|
||||
if (mValues == null)
|
||||
@ -73,6 +86,11 @@ public class StringSingleChoiceSetting extends SettingsItem
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getSelectedChoice(Settings settings)
|
||||
{
|
||||
return getChoiceAt(getSelectedValueIndex(settings));
|
||||
}
|
||||
|
||||
public String getSelectedValue(Settings settings)
|
||||
{
|
||||
return mSetting.getString(settings);
|
||||
@ -102,6 +120,10 @@ public class StringSingleChoiceSetting extends SettingsItem
|
||||
mSetting.setString(settings, selection);
|
||||
}
|
||||
|
||||
public void refreshChoicesAndValues()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType()
|
||||
{
|
||||
|
@ -253,6 +253,8 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
||||
mClickedItem = item;
|
||||
mClickedPosition = position;
|
||||
|
||||
item.refreshChoicesAndValues();
|
||||
|
||||
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
|
||||
.setTitle(item.getName())
|
||||
.setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(getSettings()),
|
||||
|
@ -22,6 +22,7 @@ import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup
|
||||
import org.dolphinemu.dolphinemu.features.input.model.ControlGroupEnabledSetting;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.view.InputDeviceSetting;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting;
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting;
|
||||
@ -1107,7 +1108,10 @@ public final class SettingsFragmentPresenter
|
||||
{
|
||||
if (gcPadType == 6) // Emulated
|
||||
{
|
||||
addControllerSettings(sl, EmulatedController.getGcPad(gcPadNumber), null);
|
||||
EmulatedController gcPad = EmulatedController.getGcPad(gcPadNumber);
|
||||
|
||||
addControllerMetaSettings(sl, gcPad);
|
||||
addControllerMappingSettings(sl, gcPad, null);
|
||||
}
|
||||
else if (gcPadType == 12) // Adapter
|
||||
{
|
||||
@ -1120,6 +1124,10 @@ public final class SettingsFragmentPresenter
|
||||
|
||||
private void addWiimoteSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
|
||||
{
|
||||
EmulatedController wiimote = EmulatedController.getWiimote(wiimoteNumber);
|
||||
|
||||
addControllerMetaSettings(sl, wiimote);
|
||||
|
||||
sl.add(new HeaderSetting(mContext, R.string.wiimote, 0));
|
||||
sl.add(new SubmenuSetting(mContext, R.string.wiimote_general,
|
||||
MenuTag.getWiimoteGeneralMenuTag(wiimoteNumber)));
|
||||
@ -1130,44 +1138,58 @@ public final class SettingsFragmentPresenter
|
||||
|
||||
// TYPE_OTHER is included here instead of in addWiimoteGeneralSubSettings so that touchscreen
|
||||
// users won't have to dig into a submenu to find the Sideways Wii Remote setting
|
||||
addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
|
||||
addControllerMappingSettings(sl, wiimote,
|
||||
new ArraySet<>(Arrays.asList(ControlGroup.TYPE_ATTACHMENTS, ControlGroup.TYPE_OTHER)));
|
||||
}
|
||||
|
||||
private void addExtensionTypeSettings(ArrayList<SettingsItem> sl, int wiimoteNumber,
|
||||
int extensionType)
|
||||
{
|
||||
addControllerSettings(sl,
|
||||
addControllerMappingSettings(sl,
|
||||
EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType), null);
|
||||
}
|
||||
|
||||
private void addWiimoteGeneralSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
|
||||
{
|
||||
addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
|
||||
addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
|
||||
Collections.singleton(ControlGroup.TYPE_BUTTONS));
|
||||
}
|
||||
|
||||
private void addWiimoteMotionSimulationSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
|
||||
{
|
||||
addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
|
||||
addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
|
||||
new ArraySet<>(Arrays.asList(ControlGroup.TYPE_FORCE, ControlGroup.TYPE_TILT,
|
||||
ControlGroup.TYPE_CURSOR, ControlGroup.TYPE_SHAKE)));
|
||||
}
|
||||
|
||||
private void addWiimoteMotionInputSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
|
||||
{
|
||||
addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
|
||||
addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
|
||||
new ArraySet<>(Arrays.asList(ControlGroup.TYPE_IMU_ACCELEROMETER,
|
||||
ControlGroup.TYPE_IMU_GYROSCOPE, ControlGroup.TYPE_IMU_CURSOR)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sl The list to place controller settings into.
|
||||
* Adds settings and actions that apply to a controller as a whole.
|
||||
* For instance, the device setting and the Clear action.
|
||||
*
|
||||
* @param sl The list to place controller settings into.
|
||||
* @param controller The controller to add settings for.
|
||||
*/
|
||||
private void addControllerMetaSettings(ArrayList<SettingsItem> sl, EmulatedController controller)
|
||||
{
|
||||
sl.add(new InputDeviceSetting(mContext, R.string.input_device, 0, controller));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds mapping settings and other control-specific settings.
|
||||
*
|
||||
* @param sl The list to place controller settings into.
|
||||
* @param controller The controller to add settings for.
|
||||
* @param groupTypeFilter If this is non-null, only groups whose types match this are considered.
|
||||
*/
|
||||
private void addControllerSettings(ArrayList<SettingsItem> sl, EmulatedController controller,
|
||||
Set<Integer> groupTypeFilter)
|
||||
private void addControllerMappingSettings(ArrayList<SettingsItem> sl,
|
||||
EmulatedController controller, Set<Integer> groupTypeFilter)
|
||||
{
|
||||
int groupCount = controller.getGroupCount();
|
||||
for (int i = 0; i < groupCount; i++)
|
||||
|
@ -83,7 +83,7 @@ public abstract class SettingViewHolder extends RecyclerView.ViewHolder
|
||||
{
|
||||
SettingsItem item = getItem();
|
||||
|
||||
if (item == null || !item.hasSetting())
|
||||
if (item == null || !item.canClear())
|
||||
return false;
|
||||
|
||||
if (!item.isEditable())
|
||||
|
@ -64,10 +64,8 @@ public final class SingleChoiceViewHolder extends SettingViewHolder
|
||||
else if (item instanceof StringSingleChoiceSetting)
|
||||
{
|
||||
StringSingleChoiceSetting setting = (StringSingleChoiceSetting) item;
|
||||
String[] choices = setting.getChoices();
|
||||
int valueIndex = setting.getSelectedValueIndex(settings);
|
||||
if (valueIndex != -1)
|
||||
mBinding.textSettingDescription.setText(choices[valueIndex]);
|
||||
String choice = setting.getSelectedChoice(settings);
|
||||
mBinding.textSettingDescription.setText(choice);
|
||||
}
|
||||
else if (item instanceof SingleChoiceSettingDynamicDescriptions)
|
||||
{
|
||||
|
@ -27,6 +27,8 @@
|
||||
<string name="wiimote_motion_input">Motion Input</string>
|
||||
<string name="wiimote_extensions">Extension</string>
|
||||
|
||||
<string name="input_device">Device</string>
|
||||
|
||||
<string name="input_binding">Input Binding</string>
|
||||
<string name="input_binding_description">Press or move an input to bind it to %1$s.</string>
|
||||
|
||||
|
@ -17,6 +17,7 @@ add_library(main SHARED
|
||||
Input/ControlReference.cpp
|
||||
Input/ControlReference.h
|
||||
Input/EmulatedController.cpp
|
||||
Input/EmulatedController.h
|
||||
Input/InputOverrider.cpp
|
||||
Input/MappingCommon.cpp
|
||||
Input/NumericSetting.cpp
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "jni/Input/ControlGroup.h"
|
||||
#include "jni/Input/ControlReference.h"
|
||||
|
||||
static ControllerEmu::EmulatedController* GetPointer(JNIEnv* env, jobject obj)
|
||||
ControllerEmu::EmulatedController* EmulatedControllerFromJava(JNIEnv* env, jobject obj)
|
||||
{
|
||||
return reinterpret_cast<ControllerEmu::EmulatedController*>(
|
||||
env->GetLongField(obj, IDCache::GetEmulatedControllerPointer()));
|
||||
@ -34,25 +34,40 @@ static jobject EmulatedControllerToJava(JNIEnv* env, ControllerEmu::EmulatedCont
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getDefaultDevice(
|
||||
JNIEnv* env, jobject obj)
|
||||
{
|
||||
return ToJString(env, EmulatedControllerFromJava(env, obj)->GetDefaultDevice().ToString());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_setDefaultDevice(
|
||||
JNIEnv* env, jobject obj, jstring j_device)
|
||||
{
|
||||
return EmulatedControllerFromJava(env, obj)->SetDefaultDevice(GetJString(env, j_device));
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroupCount(
|
||||
JNIEnv* env, jobject obj)
|
||||
{
|
||||
return static_cast<jint>(GetPointer(env, obj)->groups.size());
|
||||
return static_cast<jint>(EmulatedControllerFromJava(env, obj)->groups.size());
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroup(
|
||||
JNIEnv* env, jobject obj, jint controller_index)
|
||||
{
|
||||
return ControlGroupToJava(env, GetPointer(env, obj)->groups[controller_index].get());
|
||||
return ControlGroupToJava(env,
|
||||
EmulatedControllerFromJava(env, obj)->groups[controller_index].get());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_updateSingleControlReference(
|
||||
JNIEnv* env, jobject obj, jobject control_reference)
|
||||
{
|
||||
return GetPointer(env, obj)->UpdateSingleControlReference(
|
||||
return EmulatedControllerFromJava(env, obj)->UpdateSingleControlReference(
|
||||
g_controller_interface, ControlReferenceFromJava(env, control_reference));
|
||||
}
|
||||
|
||||
|
13
Source/Android/jni/Input/EmulatedController.h
Normal file
13
Source/Android/jni/Input/EmulatedController.h
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace ControllerEmu
|
||||
{
|
||||
class ControlReference;
|
||||
}
|
||||
|
||||
ControllerEmu::EmulatedController* EmulatedControllerFromJava(JNIEnv* env, jobject obj);
|
@ -17,6 +17,7 @@
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
#include "InputCommon/ControllerInterface/MappingCommon.h"
|
||||
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||
#include "jni/Input/EmulatedController.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -28,10 +29,19 @@ constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_detectInput(JNIEnv* env, jclass)
|
||||
Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_detectInput(
|
||||
JNIEnv* env, jclass, jobject j_emulated_controller, jboolean all_devices)
|
||||
{
|
||||
const std::vector<std::string> device_strings = g_controller_interface.GetAllDeviceStrings();
|
||||
const ciface::Core::DeviceQualifier default_device{};
|
||||
ControllerEmu::EmulatedController* emulated_controller =
|
||||
EmulatedControllerFromJava(env, j_emulated_controller);
|
||||
|
||||
const ciface::Core::DeviceQualifier default_device = emulated_controller->GetDefaultDevice();
|
||||
|
||||
std::vector<std::string> device_strings;
|
||||
if (all_devices)
|
||||
device_strings = g_controller_interface.GetAllDeviceStrings();
|
||||
else
|
||||
device_strings = {default_device.ToString()};
|
||||
|
||||
auto detections =
|
||||
g_controller_interface.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME,
|
||||
|
@ -1122,4 +1122,11 @@ Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_refreshD
|
||||
{
|
||||
g_controller_interface.RefreshDevices();
|
||||
}
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_input_model_ControllerInterface_getAllDeviceStrings(
|
||||
JNIEnv* env, jclass)
|
||||
{
|
||||
return VectorToJStringArray(env, g_controller_interface.GetAllDeviceStrings());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user