Android: Add rumble for phone

This currently only supports using the internal vibrate on a phone for rumble.
This commit is contained in:
zackhow 2018-09-02 16:55:22 -04:00
parent 3405c7d420
commit 126ff8dc5f
12 changed files with 126 additions and 1 deletions

View File

@ -18,6 +18,7 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA"/> <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA"/>
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/> <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/>
<uses-permission android:name="android.permission.VIBRATE" />
<application <application
android:name=".DolphinApplication" android:name=".DolphinApplication"

View File

@ -25,6 +25,7 @@ Triggers/R = `Axis 21`
Triggers/L-Analog = `Axis 20` Triggers/L-Analog = `Axis 20`
Triggers/R-Analog = `Axis 21` Triggers/R-Analog = `Axis 21`
Triggers/Threshold = 90,000000 Triggers/Threshold = 90,000000
Rumble/Motor = `Rumble 700`
[GCPad2] [GCPad2]
Device = Android/1/Touchscreen Device = Android/1/Touchscreen
Buttons/A = `Button 0` Buttons/A = `Button 0`
@ -52,6 +53,7 @@ Triggers/R = `Axis 21`
Triggers/L-Analog = `Axis 20` Triggers/L-Analog = `Axis 20`
Triggers/R-Analog = `Axis 21` Triggers/R-Analog = `Axis 21`
Triggers/Threshold = 90,000000 Triggers/Threshold = 90,000000
Rumble/Motor = `Rumble 700`
[GCPad3] [GCPad3]
Device = Android/2/Touchscreen Device = Android/2/Touchscreen
Buttons/A = `Button 0` Buttons/A = `Button 0`
@ -79,6 +81,7 @@ Triggers/R = `Axis 21`
Triggers/L-Analog = `Axis 20` Triggers/L-Analog = `Axis 20`
Triggers/R-Analog = `Axis 21` Triggers/R-Analog = `Axis 21`
Triggers/Threshold = 90,000000 Triggers/Threshold = 90,000000
Rumble/Motor = `Rumble 700`
[GCPad4] [GCPad4]
Device = Android/3/Touchscreen Device = Android/3/Touchscreen
Buttons/A = `Button 0` Buttons/A = `Button 0`
@ -106,3 +109,4 @@ Triggers/R = `Axis 21`
Triggers/L-Analog = `Axis 20` Triggers/L-Analog = `Axis 20`
Triggers/R-Analog = `Axis 21` Triggers/R-Analog = `Axis 21`
Triggers/Threshold = 90,000000 Triggers/Threshold = 90,000000
Rumble/Motor = `Rumble 700`

View File

@ -133,6 +133,7 @@ Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623` Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624` Turntable/Crossfade/Right = `Axis 624`
Source = 1 Source = 1
Rumble/Motor = `Rumble 700`
[Wiimote2] [Wiimote2]
Device = Android/5/Touchscreen Device = Android/5/Touchscreen
Buttons/A = `Button 100` Buttons/A = `Button 100`
@ -268,6 +269,7 @@ Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623` Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624` Turntable/Crossfade/Right = `Axis 624`
Source = 0 Source = 0
Rumble/Motor = `Rumble 700`
[Wiimote3] [Wiimote3]
Device = Android/6/Touchscreen Device = Android/6/Touchscreen
Buttons/A = `Button 100` Buttons/A = `Button 100`
@ -403,6 +405,7 @@ Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623` Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624` Turntable/Crossfade/Right = `Axis 624`
Source = 0 Source = 0
Rumble/Motor = `Rumble 700`
[Wiimote4] [Wiimote4]
Device = Android/7/Touchscreen Device = Android/7/Touchscreen
Buttons/A = `Button 100` Buttons/A = `Button 100`
@ -538,3 +541,4 @@ Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623` Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624` Turntable/Crossfade/Right = `Axis 624`
Source = 0 Source = 0
Rumble/Motor = `Rumble 700`

View File

@ -7,6 +7,11 @@
package org.dolphinemu.dolphinemu; package org.dolphinemu.dolphinemu;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.view.Surface; import android.view.Surface;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
@ -225,6 +230,39 @@ public final class NativeLibrary
*/ */
public static native void onGamePadMoveEvent(String Device, int Axis, float Value); public static native void onGamePadMoveEvent(String Device, int Axis, float Value);
/**
* Rumble sent from native. Currently only supports phone rumble.
*
* @param padID Ignored for now. Future use would be to pass rumble to a connected controller
* @param state Ignored for now since phone rumble can't just be 'turned' on/off
*/
public static void rumble(int padID, double state)
{
final EmulationActivity emulationActivity = sEmulationActivity.get();
if (emulationActivity == null)
{
Log.warning("[NativeLibrary] EmulationActivity is null");
return;
}
if (PreferenceManager.getDefaultSharedPreferences(emulationActivity)
.getBoolean("phoneRumble", true))
{
Vibrator vibrator = (Vibrator) emulationActivity.getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator != null && vibrator.hasVibrator())
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
}
else
{
vibrator.vibrate(100);
}
}
}
}
public static native String GetUserSetting(String gameID, String Section, String Key); public static native String GetUserSetting(String gameID, String Section, String Key);
public static native void SetUserSetting(String gameID, String Section, String Key, String Value); public static native void SetUserSetting(String gameID, String Section, String Key, String Value);

View File

@ -124,6 +124,7 @@ public final class EmulationActivity extends AppCompatActivity
public static final int MENU_ACTION_EXIT = 22; public static final int MENU_ACTION_EXIT = 22;
public static final int MENU_ACTION_CHANGE_DISC = 23; public static final int MENU_ACTION_CHANGE_DISC = 23;
public static final int MENU_ACTION_JOYSTICK_REL_CENTER = 24; public static final int MENU_ACTION_JOYSTICK_REL_CENTER = 24;
public static final int MENU_ACTION_RUMBLE = 25;
private static SparseIntArray buttonsActionsMap = new SparseIntArray(); private static SparseIntArray buttonsActionsMap = new SparseIntArray();
@ -163,6 +164,7 @@ public final class EmulationActivity extends AppCompatActivity
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
buttonsActionsMap.append(R.id.menu_emulation_joystick_rel_center, buttonsActionsMap.append(R.id.menu_emulation_joystick_rel_center,
EmulationActivity.MENU_ACTION_JOYSTICK_REL_CENTER); EmulationActivity.MENU_ACTION_JOYSTICK_REL_CENTER);
buttonsActionsMap.append(R.id.menu_emulation_rumble, EmulationActivity.MENU_ACTION_RUMBLE);
} }
public static void launch(FragmentActivity activity, GameFile gameFile, int position, public static void launch(FragmentActivity activity, GameFile gameFile, int position,
@ -473,6 +475,8 @@ public final class EmulationActivity extends AppCompatActivity
// Populate the checkbox value for joystick center on touch // Populate the checkbox value for joystick center on touch
menu.findItem(R.id.menu_emulation_joystick_rel_center) menu.findItem(R.id.menu_emulation_joystick_rel_center)
.setChecked(mPreferences.getBoolean("joystickRelCenter", true)); .setChecked(mPreferences.getBoolean("joystickRelCenter", true));
menu.findItem(R.id.menu_emulation_rumble)
.setChecked(mPreferences.getBoolean("phoneRumble", true));
return true; return true;
} }
@ -504,7 +508,11 @@ public final class EmulationActivity extends AppCompatActivity
case MENU_ACTION_JOYSTICK_REL_CENTER: case MENU_ACTION_JOYSTICK_REL_CENTER:
item.setChecked(!item.isChecked()); item.setChecked(!item.isChecked());
toggleJoystickRelCenter(item.isChecked()); toggleJoystickRelCenter(item.isChecked());
return; break;
case MENU_ACTION_RUMBLE:
item.setChecked(!item.isChecked());
toggleRumble(item.isChecked());
break;
} }
} }
@ -636,6 +644,13 @@ public final class EmulationActivity extends AppCompatActivity
editor.commit(); editor.commit();
} }
private void toggleRumble(boolean state)
{
final SharedPreferences.Editor editor = mPreferences.edit();
editor.putBoolean("phoneRumble", state);
editor.apply();
}
private void editControlsPlacement() private void editControlsPlacement()
{ {

View File

@ -98,6 +98,10 @@
android:id="@+id/menu_emulation_joystick_rel_center" android:id="@+id/menu_emulation_joystick_rel_center"
android:checkable="true" android:checkable="true"
android:title="@string/emulation_control_joystick_rel_center"/> android:title="@string/emulation_control_joystick_rel_center"/>
<item
android:id="@+id/menu_emulation_rumble"
android:checkable="true"
android:title="@string/emulation_control_rumble"/>
</menu> </menu>
</item> </item>

View File

@ -98,11 +98,16 @@
android:id="@+id/menu_emulation_joystick_rel_center" android:id="@+id/menu_emulation_joystick_rel_center"
android:checkable="true" android:checkable="true"
android:title="@string/emulation_control_joystick_rel_center"/> android:title="@string/emulation_control_joystick_rel_center"/>
<item
android:id="@+id/menu_emulation_rumble"
android:checkable="true"
android:title="@string/emulation_control_rumble"/>
</group> </group>
<item <item
android:id="@+id/menu_emulation_choose_controller" android:id="@+id/menu_emulation_choose_controller"
android:title="@string/emulation_choose_controller"/> android:title="@string/emulation_choose_controller"/>
</menu> </menu>
</item> </item>

View File

@ -271,6 +271,7 @@
<string name="emulation_toggle_all">Toggle All</string> <string name="emulation_toggle_all">Toggle All</string>
<string name="emulation_control_scale">Adjust Scale</string> <string name="emulation_control_scale">Adjust Scale</string>
<string name="emulation_control_joystick_rel_center">Relative Stick Center</string> <string name="emulation_control_joystick_rel_center">Relative Stick Center</string>
<string name="emulation_control_rumble">Rumble</string>
<string name="emulation_choose_controller">Choose Controller</string> <string name="emulation_choose_controller">Choose Controller</string>
<string name="emulation_controller_changed">You may have to reload the game after changing extensions.</string> <string name="emulation_controller_changed">You may have to reload the game after changing extensions.</string>
<string name="emulation_touch_button_help">To change the button layout, open the menu -> Configure Controls -> Edit Layout</string> <string name="emulation_touch_button_help">To change the button layout, open the menu -> Configure Controls -> Edit Layout</string>

View File

@ -52,6 +52,8 @@ std::vector<std::string> configStrings = {
"TurntableEuphoria", "TurntableLeftTLeft", "TurntableLeftTRight", "TurntableRightTLeft", "TurntableEuphoria", "TurntableLeftTLeft", "TurntableLeftTRight", "TurntableRightTLeft",
"TurntableRightTRight", "TurntableUp", "TurntableDown", "TurntableLeft", "TurntableRight", "TurntableRightTRight", "TurntableUp", "TurntableDown", "TurntableLeft", "TurntableRight",
"TurntableEffDial", "TurntableCrossLeft", "TurntableCrossRight", "TurntableEffDial", "TurntableCrossLeft", "TurntableCrossRight",
// Rumble
"Rumble",
}; };
std::vector<ButtonType> configTypes = { std::vector<ButtonType> configTypes = {
// GC // GC
@ -95,6 +97,8 @@ std::vector<ButtonType> configTypes = {
TURNTABLE_TABLE_RIGHT_RIGHT, TURNTABLE_STICK_UP, TURNTABLE_STICK_DOWN, TURNTABLE_STICK_LEFT, TURNTABLE_TABLE_RIGHT_RIGHT, TURNTABLE_STICK_UP, TURNTABLE_STICK_DOWN, TURNTABLE_STICK_LEFT,
TURNTABLE_STICK_RIGHT, TURNTABLE_EFFECT_DIAL, TURNTABLE_CROSSFADE_LEFT, TURNTABLE_STICK_RIGHT, TURNTABLE_EFFECT_DIAL, TURNTABLE_CROSSFADE_LEFT,
TURNTABLE_CROSSFADE_RIGHT, TURNTABLE_CROSSFADE_RIGHT,
// Rumble
RUMBLE,
}; };
static void AddBind(const std::string& dev, sBind* bind) static void AddBind(const std::string& dev, sBind* bind)
@ -462,4 +466,5 @@ float InputDevice::AxisValue(int padID, ButtonType axis)
else else
return _buttons[binding->second->_buttontype] == BUTTON_PRESSED ? 1.0f : 0.0f; return _buttons[binding->second->_buttontype] == BUTTON_PRESSED ? 1.0f : 0.0f;
} }
} }

View File

@ -176,6 +176,8 @@ enum ButtonType
TURNTABLE_CROSSFADE = 622, // To Be Used on Java Side TURNTABLE_CROSSFADE = 622, // To Be Used on Java Side
TURNTABLE_CROSSFADE_LEFT = 623, TURNTABLE_CROSSFADE_LEFT = 623,
TURNTABLE_CROSSFADE_RIGHT = 624, TURNTABLE_CROSSFADE_RIGHT = 624,
// Rumble
RUMBLE = 700,
}; };
enum ButtonState enum ButtonState
{ {

View File

@ -3,7 +3,9 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "InputCommon/ControllerInterface/Android/Android.h" #include "InputCommon/ControllerInterface/Android/Android.h"
#include <jni/AndroidCommon/IDCache.h>
#include <sstream> #include <sstream>
#include <thread>
#include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/ControllerInterface.h"
namespace ciface namespace ciface
@ -190,6 +192,8 @@ Touchscreen::Touchscreen(int padID) : _padID(padID)
new Axis(_padID, ButtonManager::TURNTABLE_CROSSFADE_RIGHT)); new Axis(_padID, ButtonManager::TURNTABLE_CROSSFADE_RIGHT));
AddAnalogInputs(new Axis(_padID, ButtonManager::TURNTABLE_EFFECT_DIAL), AddAnalogInputs(new Axis(_padID, ButtonManager::TURNTABLE_EFFECT_DIAL),
new Axis(_padID, ButtonManager::TURNTABLE_EFFECT_DIAL)); new Axis(_padID, ButtonManager::TURNTABLE_EFFECT_DIAL));
// Rumble
AddOutput(new Motor(_padID, ButtonManager::RUMBLE));
} }
// Buttons and stuff // Buttons and stuff
@ -215,5 +219,34 @@ ControlState Touchscreen::Axis::GetState() const
{ {
return ButtonManager::GetAxisValue(_padID, _index) * _neg; return ButtonManager::GetAxisValue(_padID, _index) * _neg;
} }
Touchscreen::Motor::~Motor()
{
}
std::string Touchscreen::Motor::GetName() const
{
std::ostringstream ss;
ss << "Rumble " << (int)_index;
return ss.str();
}
void Touchscreen::Motor::SetState(ControlState state)
{
if (state > 0)
{
std::thread(Rumble, _padID, state).detach();
}
}
void Touchscreen::Motor::Rumble(int padID, double state)
{
JNIEnv* env;
IDCache::GetJavaVM()->AttachCurrentThread(&env, nullptr);
jmethodID rumbleMethod =
env->GetStaticMethodID(IDCache::GetNativeLibraryClass(), "rumble", "(ID)V");
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), rumbleMethod, padID, state);
IDCache::GetJavaVM()->DetachCurrentThread();
}
} }
} }

View File

@ -41,6 +41,19 @@ private:
const ButtonManager::ButtonType _index; const ButtonManager::ButtonType _index;
const float _neg; const float _neg;
}; };
class Motor : public Core::Device::Output
{
public:
Motor(int padID, ButtonManager::ButtonType index) : _padID(padID), _index(index) {}
~Motor();
std::string GetName() const override;
void SetState(ControlState state) override;
private:
const int _padID;
const ButtonManager::ButtonType _index;
static void Rumble(int padID, double state);
};
public: public:
Touchscreen(int padID); Touchscreen(int padID);