diff options
| author | JosJuice <josjuice@gmail.com> | 2022-01-23 21:47:57 +0100 |
|---|---|---|
| committer | JosJuice <josjuice@gmail.com> | 2023-03-03 22:28:24 +0100 |
| commit | 2c529b9db12a60eb3640975f6ea4802237e118fa (patch) | |
| tree | 4f88817c854ef49828cfab134bb5cdf8186c7306 /Source/Android/app/src/main/java/org | |
| parent | dd8976f18dd5fb0c44fb40f3a24803f6cc5009b2 (diff) | |
Android: Add new input mapping implementation
Diffstat (limited to 'Source/Android/app/src/main/java/org')
19 files changed, 375 insertions, 479 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java deleted file mode 100644 index a111155181..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.dialogs; - -import android.content.Context; -import android.view.InputDevice; -import android.view.KeyEvent; -import android.view.MotionEvent; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; -import org.dolphinemu.dolphinemu.utils.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * {@link AlertDialog} derivative that listens for - * motion events from controllers and joysticks. - */ -public final class MotionAlertDialog extends AlertDialog -{ - // The selected input preference - private final InputBindingSetting setting; - private final ArrayList<Float> mPreviousValues = new ArrayList<>(); - private int mPrevDeviceId = 0; - private boolean mWaitingForEvent = true; - private SettingsAdapter mAdapter; - - /** - * Constructor - * - * @param context The current {@link Context}. - * @param setting The Preference to show this dialog for. - */ - public MotionAlertDialog(Context context, InputBindingSetting setting, SettingsAdapter adapter) - { - super(context); - - this.setting = setting; - mAdapter = adapter; - } - - public boolean onKeyEvent(int keyCode, KeyEvent event) - { - Log.debug("[MotionAlertDialog] Received key event: " + event.getAction()); - if (event.getAction() == KeyEvent.ACTION_UP) - { - if (true) - { - setting.onKeyInput(mAdapter.getSettings(), event); - dismiss(); - } - // Even if we ignore the key, we still consume it. Thus return true regardless. - return true; - } - return false; - } - - @Override - public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) - { - // Intended for devices with no touchscreen or mouse - if (keyCode == KeyEvent.KEYCODE_BACK) - { - setting.clearValue(mAdapter.getSettings()); - dismiss(); - return true; - } - return super.onKeyLongPress(keyCode, event); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) - { - // Handle this key if we care about it, otherwise pass it down the framework - return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event); - } - - @Override - public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) - { - // Handle this event if we care about it, otherwise pass it down the framework - return onMotionEvent(event) || super.dispatchGenericMotionEvent(event); - } - - private boolean onMotionEvent(MotionEvent event) - { - if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) - return false; - if (event.getAction() != MotionEvent.ACTION_MOVE) - return false; - - InputDevice input = event.getDevice(); - - List<InputDevice.MotionRange> motionRanges = input.getMotionRanges(); - - if (input.getId() != mPrevDeviceId) - { - mPreviousValues.clear(); - } - mPrevDeviceId = input.getId(); - boolean firstEvent = mPreviousValues.isEmpty(); - - int numMovedAxis = 0; - float axisMoveValue = 0.0f; - InputDevice.MotionRange lastMovedRange = null; - char lastMovedDir = '?'; - if (mWaitingForEvent) - { - for (int i = 0; i < motionRanges.size(); i++) - { - InputDevice.MotionRange range = motionRanges.get(i); - int axis = range.getAxis(); - float value = event.getAxisValue(axis); - if (firstEvent) - { - mPreviousValues.add(value); - } - else - { - float previousValue = mPreviousValues.get(i); - - // Only handle the axes that are not neutral (more than 0.5) - // but ignore any axis that has a constant value (e.g. always 1) - if (Math.abs(value) > 0.5f && value != previousValue) - { - // It is common to have multiple axes with the same physical input. For example, - // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. - // To handle this, we ignore an axis motion that's the exact same as a motion - // we already saw. This way, we ignore axes with two names, but catch the case - // where a joystick is moved in two directions. - // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html - if (value != axisMoveValue) - { - axisMoveValue = value; - numMovedAxis++; - lastMovedRange = range; - lastMovedDir = value < 0.0f ? '-' : '+'; - } - } - // Special case for d-pads (axis value jumps between 0 and 1 without any values - // in between). Without this, the user would need to press the d-pad twice - // due to the first press being caught by the "if (firstEvent)" case further up. - else if (Math.abs(value) < 0.25f && Math.abs(previousValue) > 0.75f) - { - numMovedAxis++; - lastMovedRange = range; - lastMovedDir = previousValue < 0.0f ? '-' : '+'; - } - } - - mPreviousValues.set(i, value); - } - - // If only one axis moved, that's the winner. - if (numMovedAxis == 1) - { - mWaitingForEvent = false; - setting.onMotionInput(mAdapter.getSettings(), input, lastMovedRange, lastMovedDir); - dismiss(); - } - } - return true; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java new file mode 100644 index 0000000000..cb136a908d --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +public final class MappingCommon +{ + private MappingCommon() + { + } + + /** + * Waits until the user presses one or more inputs or until a timeout, + * then returns the pressed inputs. + * + * When this is being called, a separate thread must be calling ControllerInterface's + * dispatchKeyEvent and dispatchGenericMotionEvent, otherwise no inputs will be registered. + * + * @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 void save(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java new file mode 100644 index 0000000000..6033e17e37 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::Control. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class Control +{ + @Keep + private final long mPointer; + + @Keep + private Control(long pointer) + { + mPointer = pointer; + } + + public native String getUiName(); + + public native ControlReference getControlReference(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java new file mode 100644 index 0000000000..ddfce9a737 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::ControlGroup. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class ControlGroup +{ + @Keep + private final long mPointer; + + @Keep + private ControlGroup(long pointer) + { + mPointer = pointer; + } + + public native String getUiName(); + + public native int getControlCount(); + + public native Control getControl(int i); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java new file mode 100644 index 0000000000..fd01cc1705 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; +import androidx.annotation.Nullable; + +/** + * Represents a C++ ControlReference. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class ControlReference +{ + @Keep + private final long mPointer; + + @Keep + private ControlReference(long pointer) + { + mPointer = pointer; + } + + public native double getState(); + + public native String getExpression(); + + /** + * Sets the expression for this control reference. + * + * @param expr The new expression + * @return null on success, a human-readable error on failure + */ + @Nullable + public native String setExpression(String expr); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java new file mode 100644 index 0000000000..b0fbd0fd92 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::EmulatedController. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class EmulatedController +{ + @Keep + private final long mPointer; + + @Keep + private EmulatedController(long pointer) + { + mPointer = pointer; + } + + public native int getGroupCount(); + + public native ControlGroup getGroup(int index); + + public native void updateSingleControlReference(ControlReference controlReference); + + public static native EmulatedController getGcPad(int controllerIndex); + + public static native EmulatedController getWiimote(int controllerIndex); + + public static native EmulatedController getWiimoteAttachment(int controllerIndex, + int attachmentIndex); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java new file mode 100644 index 0000000000..8888e2430b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.view; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.Control; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; + +public final class InputMappingControlSetting extends SettingsItem +{ + private final ControlReference mControlReference; + private final EmulatedController mController; + + public InputMappingControlSetting(Control control, EmulatedController controller) + { + super(control.getUiName(), ""); + mControlReference = control.getControlReference(); + mController = controller; + } + + public String getValue() + { + return mControlReference.getExpression(); + } + + public void setValue(String expr) + { + mControlReference.setExpression(expr); + mController.updateSingleControlReference(mControlReference); + } + + public void clearValue() + { + setValue(""); + } + + @Override + public int getType() + { + return TYPE_INPUT_MAPPING_CONTROL; + } + + @Override + public AbstractSetting getSetting() + { + return null; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java new file mode 100644 index 0000000000..f36d9bc478 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.ui; + +import android.app.Activity; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; +import org.dolphinemu.dolphinemu.features.input.model.MappingCommon; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; + +/** + * {@link AlertDialog} derivative that listens for + * motion events from controllers and joysticks. + */ +public final class MotionAlertDialog extends AlertDialog +{ + private final Activity mActivity; + private final InputMappingControlSetting mSetting; + private boolean mRunning = false; + + /** + * Constructor + * + * @param activity The current {@link Activity}. + * @param setting The setting to show this dialog for. + */ + public MotionAlertDialog(Activity activity, InputMappingControlSetting setting) + { + super(activity); + + mActivity = activity; + mSetting = setting; + } + + @Override + protected void onStart() + { + super.onStart(); + + mRunning = true; + new Thread(() -> + { + String result = MappingCommon.detectInput(); + mActivity.runOnUiThread(() -> + { + if (mRunning) + { + mSetting.setValue(result); + dismiss(); + } + }); + }).start(); + } + + @Override + protected void onStop() + { + super.onStop(); + mRunning = false; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) + { + ControllerInterface.dispatchKeyEvent(event); + + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isLongPress()) + { + // Special case: Let the user cancel by long-pressing Back (intended for non-touch devices) + mSetting.clearValue(); + dismiss(); + } + + return true; + } + + @Override + public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) + { + if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) + { + // Special case: Let the user cancel by touching an on-screen button + return super.dispatchGenericMotionEvent(event); + } + + ControllerInterface.dispatchGenericMotionEvent(event); + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java index b61fcc458b..72ddcb3fd3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java @@ -1,46 +1,38 @@ // SPDX-License-Identifier: GPL-2.0-or-later -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; +package org.dolphinemu.dolphinemu.features.input.ui.viewholder; -import android.content.Context; -import android.content.SharedPreferences; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.preference.PreferenceManager; import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; +import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder; -public final class InputBindingSettingViewHolder extends SettingViewHolder +public final class InputMappingControlSettingViewHolder extends SettingViewHolder { - private InputBindingSetting mItem; - - private final Context mContext; + private InputMappingControlSetting mItem; private final ListItemSettingBinding mBinding; - public InputBindingSettingViewHolder(@NonNull ListItemSettingBinding binding, - SettingsAdapter adapter, Context context) + public InputMappingControlSettingViewHolder(@NonNull ListItemSettingBinding binding, + SettingsAdapter adapter) { super(binding.getRoot(), adapter); mBinding = binding; - mContext = context; } @Override public void bind(SettingsItem item) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - - mItem = (InputBindingSetting) item; + mItem = (InputMappingControlSetting) item; mBinding.textSettingName.setText(mItem.getName()); - mBinding.textSettingDescription - .setText(sharedPreferences.getString(mItem.getKey() + mItem.getGameId(), "")); + mBinding.textSettingDescription.setText(mItem.getValue()); setStyle(mBinding.textSettingName, mItem); } @@ -54,7 +46,7 @@ public final class InputBindingSettingViewHolder extends SettingViewHolder return; } - getAdapter().onInputBindingClick(mItem, getBindingAdapterPosition()); + getAdapter().onInputMappingClick(mItem, getBindingAdapterPosition()); setStyle(mBinding.textSettingName, mItem); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java index 7d3d5a5b19..9a94048838 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java @@ -8,6 +8,7 @@ import android.widget.Toast; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.input.model.MappingCommon; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.services.GameFileCacheManager; @@ -170,6 +171,8 @@ public class Settings implements Closeable SettingsFile.saveFile(entry.getKey(), entry.getValue(), view); } + MappingCommon.save(); + NativeConfig.save(NativeConfig.LAYER_BASE); if (!NativeLibrary.IsRunning()) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java index 14c5f49021..b65f45837b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java @@ -13,6 +13,11 @@ public class HeaderSetting extends SettingsItem super(context, titleId, descriptionId); } + public HeaderSetting(CharSequence title, CharSequence description) + { + super(title, description); + } + @Override public int getType() { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java deleted file mode 100644 index 175986d3ba..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; -import android.content.SharedPreferences; -import android.view.InputDevice; -import android.view.KeyEvent; - -import androidx.preference.PreferenceManager; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public class InputBindingSetting extends SettingsItem -{ - private String mFile; - private String mSection; - private String mKey; - - private String mGameId; - - public InputBindingSetting(Context context, String file, String section, String key, int titleId, - String gameId) - { - super(context, titleId, 0); - mFile = file; - mSection = section; - mKey = key; - mGameId = gameId; - } - - public String getKey() - { - return mKey; - } - - public String getValue(Settings settings) - { - return settings.getSection(mFile, mSection).getString(mKey, ""); - } - - /** - * Saves the provided key input setting both to the INI file (so native code can use it) and as - * an Android preference (so it persists correctly and is human-readable.) - * - * @param keyEvent KeyEvent of this key press. - */ - public void onKeyInput(Settings settings, KeyEvent keyEvent) - { - InputDevice device = keyEvent.getDevice(); - String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode(); - String uiString = device.getName() + ": Button " + keyEvent.getKeyCode(); - setValue(settings, bindStr, uiString); - } - - /** - * Saves the provided motion input setting both to the INI file (so native code can use it) and as - * an Android preference (so it persists correctly and is human-readable.) - * - * @param device InputDevice from which the input event originated. - * @param motionRange MotionRange of the movement - * @param axisDir Either '-' or '+' - */ - public void onMotionInput(Settings settings, InputDevice device, - InputDevice.MotionRange motionRange, char axisDir) - { - String bindStr = - "Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir; - String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir; - setValue(settings, bindStr, uiString); - } - - public void setValue(Settings settings, String bind, String ui) - { - SharedPreferences - preferences = - PreferenceManager.getDefaultSharedPreferences(DolphinApplication.getAppContext()); - SharedPreferences.Editor editor = preferences.edit(); - editor.putString(mKey + mGameId, ui); - editor.apply(); - - settings.getSection(mFile, mSection).setString(mKey, bind); - } - - public void clearValue(Settings settings) - { - setValue(settings, "", ""); - } - - @Override - public int getType() - { - return TYPE_INPUT_BINDING; - } - - public String getGameId() - { - return mGameId; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java deleted file mode 100644 index 6c3e11e17b..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; -import android.os.Vibrator; -import android.view.InputDevice; -import android.view.KeyEvent; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.utils.Rumble; - -public class RumbleBindingSetting extends InputBindingSetting -{ - public RumbleBindingSetting(Context context, String file, String section, String key, int titleId, - String gameId) - { - super(context, file, section, key, titleId, gameId); - } - - /** - * Just need the device when saving rumble. - */ - @Override - public void onKeyInput(Settings settings, KeyEvent keyEvent) - { - saveRumble(settings, keyEvent.getDevice()); - } - - /** - * Just need the device when saving rumble. - */ - @Override - public void onMotionInput(Settings settings, InputDevice device, - InputDevice.MotionRange motionRange, char axisDir) - { - saveRumble(settings, device); - } - - private void saveRumble(Settings settings, InputDevice device) - { - Vibrator vibrator = device.getVibrator(); - if (vibrator != null && vibrator.hasVibrator()) - { - setValue(settings, device.getDescriptor(), device.getName()); - Rumble.doRumble(vibrator); - } - else - { - setValue(settings, "", - DolphinApplication.getAppContext().getString(R.string.rumble_not_found)); - } - } - - @Override - public int getType() - { - return TYPE_RUMBLE_BINDING; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java index 7b41c8e3f8..9c7b89c12f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java @@ -21,9 +21,8 @@ public abstract class SettingsItem public static final int TYPE_SINGLE_CHOICE = 2; public static final int TYPE_SLIDER = 3; public static final int TYPE_SUBMENU = 4; - public static final int TYPE_INPUT_BINDING = 5; + public static final int TYPE_INPUT_MAPPING_CONTROL = 5; public static final int TYPE_STRING_SINGLE_CHOICE = 6; - public static final int TYPE_RUMBLE_BINDING = 7; public static final int TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8; public static final int TYPE_FILE_PICKER = 9; public static final int TYPE_RUN_RUNNABLE = 10; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java index 6fc30a381b..1d4fe28af9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java @@ -31,14 +31,14 @@ public enum MenuTag GCPAD_2("gcpad", 1), GCPAD_3("gcpad", 2), GCPAD_4("gcpad", 3), - WIIMOTE_1("wiimote", 4), - WIIMOTE_2("wiimote", 5), - WIIMOTE_3("wiimote", 6), - WIIMOTE_4("wiimote", 7), - WIIMOTE_EXTENSION_1("wiimote_extension", 4), - WIIMOTE_EXTENSION_2("wiimote_extension", 5), - WIIMOTE_EXTENSION_3("wiimote_extension", 6), - WIIMOTE_EXTENSION_4("wiimote_extension", 7); + WIIMOTE_1("wiimote", 0), + WIIMOTE_2("wiimote", 1), + WIIMOTE_3("wiimote", 2), + WIIMOTE_4("wiimote", 3), + WIIMOTE_EXTENSION_1("wiimote_extension", 0), + WIIMOTE_EXTENSION_2("wiimote_extension", 1), + WIIMOTE_EXTENSION_3("wiimote_extension", 2), + WIIMOTE_EXTENSION_4("wiimote_extension", 3); private String tag; private int subType = -1; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index a7f028afe3..8fe3fe4602 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -37,15 +37,15 @@ import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding; -import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog; +import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; +import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions; @@ -57,9 +57,7 @@ import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.DateTimeSetting import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputBindingSettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputStringSettingViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RumbleBindingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RunRunnableViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder; @@ -124,13 +122,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde case SettingsItem.TYPE_SUBMENU: return new SubmenuViewHolder(ListItemSubmenuBinding.inflate(inflater), this); - case SettingsItem.TYPE_INPUT_BINDING: - return new InputBindingSettingViewHolder(ListItemSettingBinding.inflate(inflater), this, - mContext); - - case SettingsItem.TYPE_RUMBLE_BINDING: - return new RumbleBindingViewHolder(ListItemSettingBinding.inflate(inflater), this, - mContext); + case SettingsItem.TYPE_INPUT_MAPPING_CONTROL: + return new InputMappingControlSettingViewHolder(ListItemSettingBinding.inflate(inflater), + this); case SettingsItem.TYPE_FILE_PICKER: return new FilePickerViewHolder(ListItemSettingBinding.inflate(inflater), this); @@ -314,9 +308,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde mView.loadSubMenu(item.getMenuKey()); } - public void onInputBindingClick(final InputBindingSetting item, final int position) + public void onInputMappingClick(final InputMappingControlSetting item, final int position) { - final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item, this); + final MotionAlertDialog dialog = new MotionAlertDialog(mView.getActivity(), item); Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round); @ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay( @@ -326,13 +320,11 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde dialog.getWindow().setBackgroundDrawable(background); dialog.setTitle(R.string.input_binding); - dialog.setMessage(String.format(mContext.getString( - item instanceof RumbleBindingSetting ? - R.string.input_rumble_description : R.string.input_binding_description), + dialog.setMessage(String.format(mContext.getString(R.string.input_binding_description), item.getName())); dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this); dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear), - (dialogInterface, i) -> item.clearValue(getSettings())); + (dialogInterface, i) -> item.clearValue()); dialog.setOnDismissListener(dialog1 -> { notifyItemChanged(position); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java index 00699e3a0c..bb73093656 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java @@ -67,14 +67,14 @@ public final class SettingsFragment extends Fragment implements SettingsFragment titles.put(MenuTag.GCPAD_2, R.string.controller_1); titles.put(MenuTag.GCPAD_3, R.string.controller_2); titles.put(MenuTag.GCPAD_4, R.string.controller_3); - titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_4); - titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_5); - titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_6); - titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_7); - titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_4); - titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_5); - titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_6); - titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_7); + titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_0); + titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_1); + titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_2); + titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_3); + titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_0); + titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_1); + titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_2); + titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_3); } private FragmentSettingsBinding mBinding; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 9d1a783979..01f4b52b71 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -14,6 +14,9 @@ import androidx.appcompat.app.AppCompatActivity; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.UserDataActivity; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; +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; import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting; @@ -795,14 +798,14 @@ public final class SettingsFragmentPresenter private void addWiimoteSettings(ArrayList<SettingsItem> sl) { - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_4, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(4))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_5, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(5))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_6, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(6))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_7, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(7))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_0, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(0))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_1, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(1))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_2, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(2))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_3, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(3))); } private void addGraphicsSettings(ArrayList<SettingsItem> sl) @@ -1073,7 +1076,7 @@ public final class SettingsFragmentPresenter { if (gcPadType == 6) // Emulated { - // TODO + addControllerSettings(sl, EmulatedController.getGcPad(gcPadNumber)); } else if (gcPadType == 12) // Adapter { @@ -1086,13 +1089,30 @@ public final class SettingsFragmentPresenter private void addWiimoteSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber) { - // TODO + addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber)); } private void addExtensionTypeSettings(ArrayList<SettingsItem> sl, int wiimoteNumber, - int extentionType) + int extensionType) { - // TODO + addControllerSettings(sl, + EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType)); + } + + private void addControllerSettings(ArrayList<SettingsItem> sl, EmulatedController controller) + { + int groupCount = controller.getGroupCount(); + for (int i = 0; i < groupCount; i++) + { + ControlGroup group = controller.getGroup(i); + sl.add(new HeaderSetting(group.getUiName(), "")); + + int controlCount = group.getControlCount(); + for (int j = 0; j < controlCount; j++) + { + sl.add(new InputMappingControlSetting(group.getControl(j), controller)); + } + } } private static int getLogVerbosityEntries() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java deleted file mode 100644 index 3b7a6b0f75..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.content.Context; -import android.content.SharedPreferences; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.PreferenceManager; - -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public class RumbleBindingViewHolder extends SettingViewHolder -{ - private RumbleBindingSetting mItem; - - private final Context mContext; - - private final ListItemSettingBinding mBinding; - - public RumbleBindingViewHolder(@NonNull ListItemSettingBinding binding, SettingsAdapter adapter, - Context context) - { - super(binding.getRoot(), adapter); - mBinding = binding; - mContext = context; - } - - @Override - public void bind(SettingsItem item) - { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - - mItem = (RumbleBindingSetting) item; - - mBinding.textSettingName.setText(item.getName()); - mBinding.textSettingDescription - .setText(sharedPreferences.getString(mItem.getKey() + mItem.getGameId(), "")); - - setStyle(mBinding.textSettingName, mItem); - } - - @Override - public void onClick(View clicked) - { - if (!mItem.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - getAdapter().onInputBindingClick(mItem, getBindingAdapterPosition()); - - setStyle(mBinding.textSettingName, mItem); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } -} |
