Android: Add controller rumble support
Android can be funky with controller vibration. Of the three controlers I have that contain a vibrator(PS3, Xbox360, 2017 Shield controller), only the Xbox360 controller registered as having a vibrator. So YYMV depending on the driver support of the device.
This commit is contained in:
parent
dd922660c9
commit
3499a416e7
|
@ -7,15 +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;
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
import org.dolphinemu.dolphinemu.utils.Log;
|
||||||
|
import org.dolphinemu.dolphinemu.utils.Rumble;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
@ -245,22 +241,7 @@ public final class NativeLibrary
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(emulationActivity)
|
Rumble.checkRumble(padID, state);
|
||||||
.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);
|
||||||
|
|
|
@ -50,6 +50,7 @@ import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
|
||||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||||
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
|
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
|
||||||
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
|
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
|
||||||
|
import org.dolphinemu.dolphinemu.utils.Rumble;
|
||||||
import org.dolphinemu.dolphinemu.utils.TvUtil;
|
import org.dolphinemu.dolphinemu.utils.TvUtil;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
@ -277,6 +278,7 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
|
|
||||||
Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
||||||
Java_WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
Java_WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
||||||
|
Rumble.initRumble(this);
|
||||||
|
|
||||||
setContentView(R.layout.activity_emulation);
|
setContentView(R.layout.activity_emulation);
|
||||||
|
|
||||||
|
@ -361,6 +363,13 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION);
|
mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop()
|
||||||
|
{
|
||||||
|
super.onStop();
|
||||||
|
Rumble.clear();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed()
|
public void onBackPressed()
|
||||||
{
|
{
|
||||||
|
@ -693,6 +702,7 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
final SharedPreferences.Editor editor = mPreferences.edit();
|
final SharedPreferences.Editor editor = mPreferences.edit();
|
||||||
editor.putBoolean("phoneRumble", state);
|
editor.putBoolean("phoneRumble", state);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
|
Rumble.setPhoneVibrator(state, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -950,6 +960,11 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean deviceHasTouchScreen()
|
||||||
|
{
|
||||||
|
return mDeviceHasTouchScreen;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSelectedTitle()
|
public String getSelectedTitle()
|
||||||
{
|
{
|
||||||
return mSelectedTitle;
|
return mSelectedTitle;
|
||||||
|
|
|
@ -3,14 +3,18 @@ package org.dolphinemu.dolphinemu.dialogs;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
|
||||||
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
|
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
import org.dolphinemu.dolphinemu.utils.Log;
|
||||||
|
import org.dolphinemu.dolphinemu.utils.Rumble;
|
||||||
import org.dolphinemu.dolphinemu.utils.TvUtil;
|
import org.dolphinemu.dolphinemu.utils.TvUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -49,7 +53,8 @@ public final class MotionAlertDialog extends AlertDialog
|
||||||
case KeyEvent.ACTION_UP:
|
case KeyEvent.ACTION_UP:
|
||||||
if (!ControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode))
|
if (!ControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode))
|
||||||
{
|
{
|
||||||
saveKeyInput(event);
|
setting.onKeyInput(event);
|
||||||
|
dismiss();
|
||||||
}
|
}
|
||||||
// Even if we ignore the key, we still consume it. Thus return true regardless.
|
// Even if we ignore the key, we still consume it. Thus return true regardless.
|
||||||
return true;
|
return true;
|
||||||
|
@ -63,13 +68,11 @@ public final class MotionAlertDialog extends AlertDialog
|
||||||
public boolean onKeyLongPress(int keyCode, KeyEvent event)
|
public boolean onKeyLongPress(int keyCode, KeyEvent event)
|
||||||
{
|
{
|
||||||
// Option to clear by long back is only needed on the TV interface
|
// Option to clear by long back is only needed on the TV interface
|
||||||
if (TvUtil.isLeanback(getContext()))
|
if (TvUtil.isLeanback(getContext()) && keyCode == KeyEvent.KEYCODE_BACK)
|
||||||
{
|
{
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK)
|
setting.clearValue();
|
||||||
{
|
dismiss();
|
||||||
clearBinding();
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return super.onKeyLongPress(keyCode, event);
|
return super.onKeyLongPress(keyCode, event);
|
||||||
}
|
}
|
||||||
|
@ -162,69 +165,10 @@ public final class MotionAlertDialog extends AlertDialog
|
||||||
if (numMovedAxis == 1)
|
if (numMovedAxis == 1)
|
||||||
{
|
{
|
||||||
mWaitingForEvent = false;
|
mWaitingForEvent = false;
|
||||||
saveMotionInput(input, lastMovedRange, lastMovedDir);
|
setting.onMotionInput(input, lastMovedRange, lastMovedDir);
|
||||||
|
dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
private void saveKeyInput(KeyEvent keyEvent)
|
|
||||||
{
|
|
||||||
InputDevice device = keyEvent.getDevice();
|
|
||||||
String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode();
|
|
||||||
String uiString = device.getName() + ": Button " + keyEvent.getKeyCode();
|
|
||||||
|
|
||||||
saveInput(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 '+'
|
|
||||||
*/
|
|
||||||
private void saveMotionInput(InputDevice device, InputDevice.MotionRange motionRange,
|
|
||||||
char axisDir)
|
|
||||||
{
|
|
||||||
String bindStr =
|
|
||||||
"Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir;
|
|
||||||
String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir;
|
|
||||||
|
|
||||||
saveInput(bindStr, uiString);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the input string to settings and SharedPreferences, then dismiss this Dialog.
|
|
||||||
*/
|
|
||||||
private void saveInput(String bind, String ui)
|
|
||||||
{
|
|
||||||
setting.setValue(bind);
|
|
||||||
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
|
||||||
SharedPreferences.Editor editor = preferences.edit();
|
|
||||||
|
|
||||||
editor.putString(setting.getKey(), ui);
|
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearBinding()
|
|
||||||
{
|
|
||||||
setting.setValue("");
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
|
||||||
SharedPreferences.Editor editor = preferences.edit();
|
|
||||||
editor.remove(setting.getKey());
|
|
||||||
editor.apply();
|
|
||||||
dismiss();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
package org.dolphinemu.dolphinemu.features.settings.model.view;
|
package org.dolphinemu.dolphinemu.features.settings.model.view;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.DolphinApplication;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
|
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
||||||
|
|
||||||
public final class InputBindingSetting extends SettingsItem
|
public class InputBindingSetting extends SettingsItem
|
||||||
{
|
{
|
||||||
public InputBindingSetting(String key, String section, int titleId, Setting setting)
|
public InputBindingSetting(String key, String section, int titleId, Setting setting)
|
||||||
{
|
{
|
||||||
|
@ -21,6 +27,37 @@ public final class InputBindingSetting extends SettingsItem
|
||||||
return setting.getValue();
|
return setting.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(KeyEvent keyEvent)
|
||||||
|
{
|
||||||
|
InputDevice device = keyEvent.getDevice();
|
||||||
|
String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode();
|
||||||
|
String uiString = device.getName() + ": Button " + keyEvent.getKeyCode();
|
||||||
|
setValue(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(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(bindStr, uiString);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a value to the backing string. If that string was previously null,
|
* Write a value to the backing string. If that string was previously null,
|
||||||
* initializes a new one and returns it, so it can be added to the Hashmap.
|
* initializes a new one and returns it, so it can be added to the Hashmap.
|
||||||
|
@ -28,8 +65,15 @@ public final class InputBindingSetting extends SettingsItem
|
||||||
* @param bind The input that will be bound
|
* @param bind The input that will be bound
|
||||||
* @return null if overwritten successfully; otherwise, a newly created StringSetting.
|
* @return null if overwritten successfully; otherwise, a newly created StringSetting.
|
||||||
*/
|
*/
|
||||||
public StringSetting setValue(String bind)
|
public StringSetting setValue(String bind, String ui)
|
||||||
{
|
{
|
||||||
|
SharedPreferences
|
||||||
|
preferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(DolphinApplication.getAppContext());
|
||||||
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
editor.putString(getKey(), ui);
|
||||||
|
editor.apply();
|
||||||
|
|
||||||
if (getSetting() == null)
|
if (getSetting() == null)
|
||||||
{
|
{
|
||||||
StringSetting setting = new StringSetting(getKey(), getSection(), bind);
|
StringSetting setting = new StringSetting(getKey(), getSection(), bind);
|
||||||
|
@ -44,6 +88,11 @@ public final class InputBindingSetting extends SettingsItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearValue()
|
||||||
|
{
|
||||||
|
setValue("", "");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getType()
|
public int getType()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package org.dolphinemu.dolphinemu.features.settings.model.view;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
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.Setting;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
||||||
|
import org.dolphinemu.dolphinemu.utils.Rumble;
|
||||||
|
|
||||||
|
public class RumbleBindingSetting extends InputBindingSetting
|
||||||
|
{
|
||||||
|
|
||||||
|
public RumbleBindingSetting(String key, String section, int titleId, Setting setting)
|
||||||
|
{
|
||||||
|
super(key, section, titleId, setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue()
|
||||||
|
{
|
||||||
|
if (getSetting() == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringSetting setting = (StringSetting) getSetting();
|
||||||
|
return setting.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just need the device when saving rumble.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onKeyInput(KeyEvent keyEvent)
|
||||||
|
{
|
||||||
|
saveRumble(keyEvent.getDevice());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just need the device when saving rumble.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onMotionInput(InputDevice device,
|
||||||
|
InputDevice.MotionRange motionRange,
|
||||||
|
char axisDir)
|
||||||
|
{
|
||||||
|
saveRumble(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveRumble(InputDevice device)
|
||||||
|
{
|
||||||
|
Vibrator vibrator = device.getVibrator();
|
||||||
|
if (vibrator != null && vibrator.hasVibrator())
|
||||||
|
{
|
||||||
|
setValue(device.getDescriptor(), device.getName());
|
||||||
|
Rumble.doRumble(vibrator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setValue("",
|
||||||
|
DolphinApplication.getAppContext().getString(R.string.rumble_not_found));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getType()
|
||||||
|
{
|
||||||
|
return TYPE_RUMBLE_BINDING;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ public abstract class SettingsItem
|
||||||
public static final int TYPE_SUBMENU = 4;
|
public static final int TYPE_SUBMENU = 4;
|
||||||
public static final int TYPE_INPUT_BINDING = 5;
|
public static final int TYPE_INPUT_BINDING = 5;
|
||||||
public static final int TYPE_STRING_SINGLE_CHOICE = 6;
|
public static final int TYPE_STRING_SINGLE_CHOICE = 6;
|
||||||
|
public static final int TYPE_RUMBLE_BINDING = 7;
|
||||||
|
|
||||||
private String mKey;
|
private String mKey;
|
||||||
private String mSection;
|
private String mSection;
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.Settings;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.CheckBoxSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.CheckBoxSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
|
||||||
|
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.SettingsItem;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting;
|
||||||
|
@ -29,6 +30,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.CheckBoxSettingViewHolder;
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.CheckBoxSettingViewHolder;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder;
|
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.InputBindingSettingViewHolder;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RumbleBindingViewHolder;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder;
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder;
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder;
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder;
|
||||||
|
@ -90,6 +92,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
||||||
view = inflater.inflate(R.layout.list_item_setting, parent, false);
|
view = inflater.inflate(R.layout.list_item_setting, parent, false);
|
||||||
return new InputBindingSettingViewHolder(view, this, mContext);
|
return new InputBindingSettingViewHolder(view, this, mContext);
|
||||||
|
|
||||||
|
case SettingsItem.TYPE_RUMBLE_BINDING:
|
||||||
|
view = inflater.inflate(R.layout.list_item_setting, parent, false);
|
||||||
|
return new RumbleBindingViewHolder(view, this, mContext);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Log.error("[SettingsAdapter] Invalid view type: " + viewType);
|
Log.error("[SettingsAdapter] Invalid view type: " + viewType);
|
||||||
return null;
|
return null;
|
||||||
|
@ -216,19 +222,17 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
||||||
{
|
{
|
||||||
final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item);
|
final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item);
|
||||||
dialog.setTitle(R.string.input_binding);
|
dialog.setTitle(R.string.input_binding);
|
||||||
dialog.setMessage(String.format(mContext.getString(R.string.input_binding_description),
|
dialog.setMessage(String.format(mContext.getString(
|
||||||
|
item instanceof RumbleBindingSetting ?
|
||||||
|
R.string.input_rumble_description : R.string.input_binding_description),
|
||||||
mContext.getString(item.getNameId())));
|
mContext.getString(item.getNameId())));
|
||||||
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
|
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
|
||||||
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
|
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
|
||||||
(dialogInterface, i) ->
|
(dialogInterface, i) ->
|
||||||
{
|
{
|
||||||
item.setValue("");
|
SharedPreferences preferences =
|
||||||
|
|
||||||
SharedPreferences sharedPreferences =
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(mContext);
|
PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
item.clearValue();
|
||||||
editor.remove(item.getKey());
|
|
||||||
editor.apply();
|
|
||||||
});
|
});
|
||||||
dialog.setOnDismissListener(dialog1 ->
|
dialog.setOnDismissListener(dialog1 ->
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.CheckBoxSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.CheckBoxSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
|
||||||
|
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.SettingsItem;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting;
|
||||||
|
@ -632,6 +633,8 @@ public final class SettingsFragmentPresenter
|
||||||
bindingsSection.getSetting(SettingsFile.KEY_GCBIND_DPAD_LEFT + gcPadNumber);
|
bindingsSection.getSetting(SettingsFile.KEY_GCBIND_DPAD_LEFT + gcPadNumber);
|
||||||
Setting bindDPadRight =
|
Setting bindDPadRight =
|
||||||
bindingsSection.getSetting(SettingsFile.KEY_GCBIND_DPAD_RIGHT + gcPadNumber);
|
bindingsSection.getSetting(SettingsFile.KEY_GCBIND_DPAD_RIGHT + gcPadNumber);
|
||||||
|
Setting gcEmuRumble =
|
||||||
|
bindingsSection.getSetting(SettingsFile.KEY_EMU_RUMBLE + gcPadNumber);
|
||||||
|
|
||||||
sl.add(new HeaderSetting(null, null, R.string.generic_buttons, 0));
|
sl.add(new HeaderSetting(null, null, R.string.generic_buttons, 0));
|
||||||
sl.add(new InputBindingSetting(SettingsFile.KEY_GCBIND_A + gcPadNumber,
|
sl.add(new InputBindingSetting(SettingsFile.KEY_GCBIND_A + gcPadNumber,
|
||||||
|
@ -682,6 +685,11 @@ public final class SettingsFragmentPresenter
|
||||||
Settings.SECTION_BINDINGS, R.string.generic_left, bindDPadLeft));
|
Settings.SECTION_BINDINGS, R.string.generic_left, bindDPadLeft));
|
||||||
sl.add(new InputBindingSetting(SettingsFile.KEY_GCBIND_DPAD_RIGHT + gcPadNumber,
|
sl.add(new InputBindingSetting(SettingsFile.KEY_GCBIND_DPAD_RIGHT + gcPadNumber,
|
||||||
Settings.SECTION_BINDINGS, R.string.generic_right, bindDPadRight));
|
Settings.SECTION_BINDINGS, R.string.generic_right, bindDPadRight));
|
||||||
|
|
||||||
|
|
||||||
|
sl.add(new HeaderSetting(null, null, R.string.emulation_control_rumble, 0));
|
||||||
|
sl.add(new RumbleBindingSetting(SettingsFile.KEY_EMU_RUMBLE + gcPadNumber,
|
||||||
|
Settings.SECTION_BINDINGS, R.string.emulation_control_rumble, gcEmuRumble));
|
||||||
}
|
}
|
||||||
else // Adapter
|
else // Adapter
|
||||||
{
|
{
|
||||||
|
@ -761,6 +769,8 @@ public final class SettingsFragmentPresenter
|
||||||
bindingsSection.getSetting(SettingsFile.KEY_WIIBIND_DPAD_LEFT + wiimoteNumber);
|
bindingsSection.getSetting(SettingsFile.KEY_WIIBIND_DPAD_LEFT + wiimoteNumber);
|
||||||
Setting bindDPadRight =
|
Setting bindDPadRight =
|
||||||
bindingsSection.getSetting(SettingsFile.KEY_WIIBIND_DPAD_RIGHT + wiimoteNumber);
|
bindingsSection.getSetting(SettingsFile.KEY_WIIBIND_DPAD_RIGHT + wiimoteNumber);
|
||||||
|
Setting wiiEmuRumble =
|
||||||
|
bindingsSection.getSetting(SettingsFile.KEY_EMU_RUMBLE + wiimoteNumber);
|
||||||
|
|
||||||
sl.add(new SingleChoiceSetting(SettingsFile.KEY_WIIMOTE_EXTENSION,
|
sl.add(new SingleChoiceSetting(SettingsFile.KEY_WIIMOTE_EXTENSION,
|
||||||
Settings.SECTION_WIIMOTE + (wiimoteNumber - 3), R.string.wiimote_extensions,
|
Settings.SECTION_WIIMOTE + (wiimoteNumber - 3), R.string.wiimote_extensions,
|
||||||
|
@ -843,6 +853,11 @@ public final class SettingsFragmentPresenter
|
||||||
Settings.SECTION_BINDINGS, R.string.generic_left, bindDPadLeft));
|
Settings.SECTION_BINDINGS, R.string.generic_left, bindDPadLeft));
|
||||||
sl.add(new InputBindingSetting(SettingsFile.KEY_WIIBIND_DPAD_RIGHT + wiimoteNumber,
|
sl.add(new InputBindingSetting(SettingsFile.KEY_WIIBIND_DPAD_RIGHT + wiimoteNumber,
|
||||||
Settings.SECTION_BINDINGS, R.string.generic_right, bindDPadRight));
|
Settings.SECTION_BINDINGS, R.string.generic_right, bindDPadRight));
|
||||||
|
|
||||||
|
|
||||||
|
sl.add(new HeaderSetting(null, null, R.string.emulation_control_rumble, 0));
|
||||||
|
sl.add(new RumbleBindingSetting(SettingsFile.KEY_EMU_RUMBLE + wiimoteNumber,
|
||||||
|
Settings.SECTION_BINDINGS, R.string.emulation_control_rumble, wiiEmuRumble));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addExtensionTypeSettings(ArrayList<SettingsItem> sl, int wiimoteNumber,
|
private void addExtensionTypeSettings(ArrayList<SettingsItem> sl, int wiimoteNumber,
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.R;
|
||||||
|
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 TextView mTextSettingName;
|
||||||
|
private TextView mTextSettingDescription;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
public RumbleBindingViewHolder(View itemView, SettingsAdapter adapter, Context context)
|
||||||
|
{
|
||||||
|
super(itemView, adapter);
|
||||||
|
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void findViews(View root)
|
||||||
|
{
|
||||||
|
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
|
||||||
|
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bind(SettingsItem item)
|
||||||
|
{
|
||||||
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||||
|
|
||||||
|
mItem = (RumbleBindingSetting) item;
|
||||||
|
|
||||||
|
mTextSettingName.setText(item.getNameId());
|
||||||
|
mTextSettingDescription.setText(sharedPreferences.getString(mItem.getKey(), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View clicked)
|
||||||
|
{
|
||||||
|
getAdapter().onInputBindingClick(mItem, getAdapterPosition());
|
||||||
|
}
|
||||||
|
}
|
|
@ -115,6 +115,8 @@ public final class SettingsFile
|
||||||
public static final String KEY_GCADAPTER_RUMBLE = "AdapterRumble";
|
public static final String KEY_GCADAPTER_RUMBLE = "AdapterRumble";
|
||||||
public static final String KEY_GCADAPTER_BONGOS = "SimulateKonga";
|
public static final String KEY_GCADAPTER_BONGOS = "SimulateKonga";
|
||||||
|
|
||||||
|
public static final String KEY_EMU_RUMBLE = "EmuRumble";
|
||||||
|
|
||||||
public static final String KEY_WIIMOTE_TYPE = "Source";
|
public static final String KEY_WIIMOTE_TYPE = "Source";
|
||||||
public static final String KEY_WIIMOTE_EXTENSION = "Extension";
|
public static final String KEY_WIIMOTE_EXTENSION = "Extension";
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
package org.dolphinemu.dolphinemu.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class Rumble
|
||||||
|
{
|
||||||
|
private static Vibrator phoneVibrator;
|
||||||
|
private static SparseArray<Vibrator> emuVibrators;
|
||||||
|
|
||||||
|
public static void initRumble(EmulationActivity activity)
|
||||||
|
{
|
||||||
|
if (activity.deviceHasTouchScreen() &&
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
.getBoolean("phoneRumble", true))
|
||||||
|
{
|
||||||
|
setPhoneVibrator(true, activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
emuVibrators = new SparseArray<>();
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
StringSetting deviceName =
|
||||||
|
(StringSetting) activity.getSettings().getSection(Settings.SECTION_BINDINGS)
|
||||||
|
.getSetting(SettingsFile.KEY_EMU_RUMBLE + i);
|
||||||
|
if (deviceName != null && !deviceName.getValue().isEmpty())
|
||||||
|
{
|
||||||
|
for (int id : InputDevice.getDeviceIds())
|
||||||
|
{
|
||||||
|
InputDevice device = InputDevice.getDevice(id);
|
||||||
|
if (deviceName.getValue().equals(device.getDescriptor()))
|
||||||
|
{
|
||||||
|
Vibrator vib = device.getVibrator();
|
||||||
|
if (vib != null && vib.hasVibrator())
|
||||||
|
emuVibrators.put(i, vib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPhoneVibrator(boolean set, EmulationActivity activity)
|
||||||
|
{
|
||||||
|
if (set)
|
||||||
|
{
|
||||||
|
Vibrator vib = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
if (vib != null && vib.hasVibrator())
|
||||||
|
phoneVibrator = vib;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
phoneVibrator = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear()
|
||||||
|
{
|
||||||
|
phoneVibrator = null;
|
||||||
|
emuVibrators.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkRumble(int padId, double state)
|
||||||
|
{
|
||||||
|
if (phoneVibrator != null)
|
||||||
|
doRumble(phoneVibrator);
|
||||||
|
|
||||||
|
if (emuVibrators.get(padId) != null)
|
||||||
|
doRumble(emuVibrators.get(padId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void doRumble(Vibrator vib)
|
||||||
|
{
|
||||||
|
// Check again that it exists and can vibrate
|
||||||
|
if (vib != null && vib.hasVibrator())
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
|
{
|
||||||
|
vib.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vib.vibrate(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
<string name="input_binding">Input Binding</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>
|
<string name="input_binding_description">Press or move an input to bind it to %1$s.</string>
|
||||||
|
<string name="input_rumble_description">Press or move any input to set rumble.</string>
|
||||||
|
|
||||||
<!-- Generic buttons (Shared with lots of stuff) -->
|
<!-- Generic buttons (Shared with lots of stuff) -->
|
||||||
<string name="generic_buttons">Buttons</string>
|
<string name="generic_buttons">Buttons</string>
|
||||||
|
@ -291,6 +292,9 @@
|
||||||
<string name="header_wiimote_general">General</string>
|
<string name="header_wiimote_general">General</string>
|
||||||
<string name="header_controllers">Controllers</string>
|
<string name="header_controllers">Controllers</string>
|
||||||
|
|
||||||
|
<!-- Rumble -->
|
||||||
|
<string name="rumble_not_found">Device rumble not found</string>
|
||||||
|
|
||||||
<string name="write_permission_needed">You need to allow write access to external storage for the emulator to work</string>
|
<string name="write_permission_needed">You need to allow write access to external storage for the emulator to work</string>
|
||||||
<string name="load_settings">Loading Settings...</string>
|
<string name="load_settings">Loading Settings...</string>
|
||||||
<string name="emulation_change_disc">Change Disc</string>
|
<string name="emulation_change_disc">Change Disc</string>
|
||||||
|
|
Loading…
Reference in New Issue