An incredibly easy to use, lightweight and widely compatible library to streamline the implementation of gamepads for android applications !
This library addresses 3 big problems when using the official documentation regarding gamepad integration:
- Multiple input entry points for the same physical input (KEYCODE_DPAD_UP vs AXIS_HAT_Y)
- Axis and keycode may be swapped or wrongly mapped, even on popular gamepads
- Motion Events require to check all axis manually, to verify a value has actually changed.
First, add the dependency inside the build.gradle
file of your app module:
implementation 'com.github.Mathias-Boulay:android_gamepad_remapper:master-SNAPSHOT'
If not done so already, you need to add the Jitpack repository to the root build.gradle
file:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Clone this repository, and fiddle around with the demo project to see how it's made. Also available as a release !
The managed integration passes down all of the work to the library. Compared to the manual integration, it takes care of the following:
- Automatically show the remapping UI, per gamepad.
- Auto load/save of the remapping data
How to add a managed integration
Consider the following code block, which integrates the entire lib into the activity which needs to support gamepad input.
class MyActivity extends Activity implements GamepadHandler {
// The RemapperView.Builder object allows you to set which buttons to remap
private RemapperManager inputManager = new RemapperManager(this, new RemapperView.Builder(null)
.remapDpad(true)
.remapLeftJoystick(true)
.remapRightJoystick(true)
.remapLeftTrigger(true)
.remapRightTrigger(true));
@Override // Redirect KeyEvents to the remapper if one is available
public boolean dispatchKeyEvent(KeyEvent event) {
return inputManager.handleKeyEventInput(this, event, this) || super.dispatchKeyEvent(event);
}
@Override // Redirect MotionEvents to the remapper if one is available
public boolean dispatchGenericMotionEvent(MotionEvent event) {
return inputManager.handleMotionEventInput(this, event, this) || super.dispatchGenericMotionEvent(event);
}
@Override // Implement the GamepadHandler interface
public void handleGamepadInput(int code, float value){
// TODO Your code to take care of the gamepad input.
}
}
The Activity
implements GamepadHandler
method: handleGamepadInput
.
See the full documentation on how to implement it for managed instances.
With that, you're done integrating the gamepad !
When performing a manual integration, you have to take care of the following things:
- Displaying the UI at the right moment
- Saving and loading of all gamepad mappings
Manual integration
To display the remapping UI to the user, use the RemapperView.Builder
object to build the RemapperView
:
new RemapperView.Builder(
new RemapperView.Listener() {
@Override
public void onRemapDone(Remapper remapper) {
// This method is called when the user finished remapping
// Here, you can save the remapper instance into a file and grab a reference to it.
}
})
.remapDpad(true)
.remapLeftJoystick(true)
.remapRightJoystick(true)
.remapLeftTrigger(true)
.remapRightTrigger(true)
.build(this);
Once the remapping is done, you get a Remapper
instance passed through the RemapperView.Listener
interface.
Note: The full array of remappable controls is available on the documentation below.
Once the remapping is done, we can make use on the Remapper
object.
Inside your activity supporting the gamepad:
- override 2 functions to intercept controller's
KeyEvent
andMotionEvent
- Implement the
GamepadHandler
interface, which handles standardized and mapped input
class MyActivity extends Activity implements GamepadHandler{
private Remapper mRemapper;
...
@Override // Redirect KeyEvents to the remapper if one is available
public boolean dispatchKeyEvent(KeyEvent event) {
if(remapper == null) return super.dispatchKeyEvent(event);
return remapper.handleKeyEventInput(event, this);
}
@Override // Redirect MotionEvents to the remapper if one is available
public boolean dispatchGenericMotionEvent(MotionEvent event) {
if(remapper == null) return super.onGenericMotionEvent(event);
return remapper.handleMotionEventInput(event, this);
}
@Override // Implement the GamepadHandler interface
public void handleGamepadInput(int code, float value){
// TODO Your code to take care of the gamepad input.
}
}
Lazier people might want to use the Managed integration. Consult the FULL DOCUMENTATION for details.
Click here to see it in all its glory !
Class able to map inputs from one way or another, used to normalize inputs.
/**
* Load the Remapper data from the shared preferences
* @param context A context object, necessary to fetch SharedPreferences
*/
public Remapper(Context context);
/**
* If the event is a valid Gamepad event, call the GamepadHandler method.
* @param event The current MotionEvent
* @param handler The handler, through which remapped inputs will be passed.
* @return Whether the input was handled or not.
*/
public boolean handleMotionEventInput(MotionEvent event, GamepadHandler handler);
/**
* If the event is a valid Gamepad event, call the GamepadHandler method
* @param event The current KeyEvent
* @param handler The handler, through which remapped inputs will be passed.
* @return Whether the input was handled or not.
*/
public boolean handleKeyEventInput(KeyEvent event, GamepadHandler handler);
/**
* Saves the remapper data inside its own shared preference file
* @param context A context object, necessary to fetch SharedPreferences
*/
public void save(Context context);
/** Wipes the saved gamepad maps from the data. A reload of the data is needed for the input manager to notice */
public static void wipePreferences(Context context);
/** @param listener The listener to which the Remapper object is passed after remapping */
public Builder(RemapperView.Listener listener);
/** @param enabled Enable the remapping of said button. Default is false. */
public Builder remapLeftJoystick(boolean enabled);
public Builder remapRightJoystick(boolean enabled);
public Builder remapLeftJoystickButton(boolean enabled);
public Builder remapRightJoystickButton(boolean enabled);
public Builder remapDpad(boolean enabled);
public Builder remapLeftShoulder(boolean enabled);
public Builder remapRightShoulder(boolean enabled);
public Builder remapLeftTrigger(boolean enabled);
public Builder remapRightTrigger(boolean enabled);
public Builder remapA(boolean enabled);
public Builder remapX(boolean enabled);
public Builder remapY(boolean enabled);
public Builder remapB(boolean enabled);
public Builder remapStart(boolean enabled);
public Builder remapSelect(boolean enabled);
/** Set the listener, replacing the one set by the constructor */
public Builder setRemapListener(RemapperView.Listener listener);
/**
* Build and display the remapping dialog with all the parameters set previously
* @param context A context object referring to the current window
*/
public void build(Context context);
Manager class to streamline even more the integration of gamepads
It auto handles displaying the mapper view and handling events.
Note that the compatibility with a manual integration at the same time is limited
/**
* @param context A context for SharedPreferences. The Manager attempts to fetch an existing remapper.
* @param builder Builder with all the params set in. Note that the listener is going to be overridden.
*/
public RemapperManager(Context context, RemapperView.Builder builder);
/**
* If the event is a valid Gamepad event and a remapper is available, call the GamepadHandler method
* Will automatically ask to remap if no remapper is available
* @param event The current MotionEvent
* @param handler The handler, through which remapped inputs will be passed.
* @return Whether the input was handled or not.
*/
public boolean handleMotionEventInput(Context context, MotionEvent event, GamepadHandler handler);
/**
* If the event is a valid Gamepad event and a remapper is available, call the GamepadHandler method
* Will automatically ask to remap if no remapper is available
* @param event The current KeyEvent
* @param handler The handler, through which remapped inputs will be passed.
* @return Whether the input was handled or not.
*/
public boolean handleKeyEventInput(Context context, KeyEvent event, GamepadHandler handler);
/**
* Function handling all gamepad actions: For mapped buttons, the value is guaranteed to have changed.
* Either a keycode, one of:
* KEYCODE_BUTTON_A, KEYCODE_BUTTON_B, KEYCODE_BUTTON_X, KEYCODE_BUTTON_Y,
* KEYCODE_BUTTON_R1, KEYCODE_BUTTON_L1, KEYCODE_BUTTON_START, KEYCODE_BUTTON_SELECT,
* KEYCODE_BUTTON_THUMBL, KEYCODE_BUTTON_THUMBR
* Either an axis, one of:
* AXIS_HAT_X, AXIS_HAT_Y, AXIS_X, AXIS_Y, AXIS_Z, AXIS_RZ, AXIS_RTRIGGER, AXIS_LTRIGGER
* Note: The code may be different if the gamepad is not fully remapped.
*
* @param value For keycodes, 0 for released state, 1 for pressed state.
* For Axis, the value of the axis. Varies between 0/1 or -1/1 depending on the axis.
*/
public void handleGamepadInput(int code, float value);
This project is licensed under the LGPLv3, which allows you to make commercial of software using this library. Only improvements/modifications done to this very library (and technically the demo project) need to be open sourced !
Thanks to thoseawesomeguys for the bitmap graphics !