@@ -21,6 +21,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.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.SingleChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting;
@@ -29,6 +30,7 @@
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.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.SingleChoiceViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder;
@@ -90,6 +92,10 @@ public SettingViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
view = inflater.inflate(R.layout.list_item_setting, parent, false);
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:
Log.error("[SettingsAdapter] Invalid view type: " + viewType);
return null;
@@ -216,19 +222,17 @@ public void onInputBindingClick(final InputBindingSetting item, final int positi
{
final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item);
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())));
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
(dialogInterface, i) ->
{
item.setValue("");

SharedPreferences sharedPreferences =
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(item.getKey());
editor.apply();
item.clearValue();
});
dialog.setOnDismissListener(dialog1 ->
{
@@ -14,6 +14,7 @@
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.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.SingleChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting;
@@ -632,6 +633,8 @@ private void addGcPadSubSettings(ArrayList<SettingsItem> sl, int gcPadNumber, in
bindingsSection.getSetting(SettingsFile.KEY_GCBIND_DPAD_LEFT + gcPadNumber);
Setting bindDPadRight =
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 InputBindingSetting(SettingsFile.KEY_GCBIND_A + gcPadNumber,
@@ -682,6 +685,11 @@ private void addGcPadSubSettings(ArrayList<SettingsItem> sl, int gcPadNumber, in
Settings.SECTION_BINDINGS, R.string.generic_left, bindDPadLeft));
sl.add(new InputBindingSetting(SettingsFile.KEY_GCBIND_DPAD_RIGHT + gcPadNumber,
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
{
@@ -761,6 +769,8 @@ private void addWiimoteSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber
bindingsSection.getSetting(SettingsFile.KEY_WIIBIND_DPAD_LEFT + wiimoteNumber);
Setting bindDPadRight =
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,
Settings.SECTION_WIIMOTE + (wiimoteNumber - 3), R.string.wiimote_extensions,
@@ -843,6 +853,11 @@ private void addWiimoteSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber
Settings.SECTION_BINDINGS, R.string.generic_left, bindDPadLeft));
sl.add(new InputBindingSetting(SettingsFile.KEY_WIIBIND_DPAD_RIGHT + wiimoteNumber,
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,
@@ -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 static final String KEY_GCADAPTER_RUMBLE = "AdapterRumble";
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_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_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) -->
<string name="generic_buttons">Buttons</string>
@@ -291,6 +292,9 @@
<string name="header_wiimote_general">General</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="load_settings">Loading Settings...</string>
<string name="emulation_change_disc">Change Disc</string>