Compare commits

..

8 Commits

Author SHA1 Message Date
Cameron Gutman bfe6929642 Version 7.0 2019-02-16 19:44:45 -08:00
Cameron Gutman 50d45011a8 Add device vibration and other fixes 2019-02-16 19:13:01 -08:00
Cameron Gutman 2f7087d6d3 Stop vibration on stream end 2019-02-16 18:05:08 -08:00
Cameron Gutman 92b71588d0 Implement rumble on Android InputDevice 2019-02-16 17:56:34 -08:00
Cameron Gutman 4f3d018764 Fix OSC colliding with player 1 2019-02-16 17:29:05 -08:00
Cameron Gutman a22e33eeb9 Add rumble support for the in-app Xbox driver 2019-02-16 17:03:10 -08:00
Cameron Gutman 6a939e7495 Don't display the termination dialog for intended terminations 2019-02-10 02:28:11 -08:00
Cameron Gutman f8ba7cf190 Update common with SOPS fixes 2019-02-09 20:59:59 -08:00
15 changed files with 237 additions and 43 deletions
+2 -2
View File
@@ -8,8 +8,8 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 28
versionName "6.2" versionName "7.0"
versionCode = 186 versionCode = 187
} }
flavorDimensions "root" flavorDimensions "root"
+1
View File
@@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+20 -3
View File
@@ -72,6 +72,7 @@ import java.io.ByteArrayInputStream;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Locale;
public class Game extends Activity implements SurfaceHolder.Callback, public class Game extends Activity implements SurfaceHolder.Callback,
@@ -424,7 +425,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (prefConfig.onscreenController) { if (prefConfig.onscreenController) {
// create virtual onscreen controller // create virtual onscreen controller
virtualController = new VirtualController(conn, virtualController = new VirtualController(controllerHandler,
(FrameLayout)streamView.getParent(), (FrameLayout)streamView.getParent(),
this); this);
virtualController.refreshLayout(); virtualController.refreshLayout();
@@ -1267,6 +1268,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (connecting || connected) { if (connecting || connected) {
connecting = connected = false; connecting = connected = false;
controllerHandler.stop();
// Stop may take a few hundred ms to do some network I/O to tell // Stop may take a few hundred ms to do some network I/O to tell
// the server we're going away and clean up. Let it run in a separate // the server we're going away and clean up. Let it run in a separate
// thread to keep things smooth for the UI. Inside moonlight-common, // thread to keep things smooth for the UI. Inside moonlight-common,
@@ -1322,8 +1325,15 @@ public class Game extends Activity implements SurfaceHolder.Callback,
LimeLog.severe("Connection terminated: " + errorCode); LimeLog.severe("Connection terminated: " + errorCode);
stopConnection(); stopConnection();
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_terminated_title), // Display the error dialog if it was an unexpected termination.
getResources().getString(R.string.conn_terminated_msg), true); // Otherwise, just finish the activity immediately.
if (errorCode != 0) {
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_terminated_title),
getResources().getString(R.string.conn_terminated_msg), true);
}
else {
finish();
}
} }
} }
}); });
@@ -1377,6 +1387,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
} }
} }
@Override
public void rumble(short controllerNumber, short lowFreqMotor, short highFreqMotor) {
LimeLog.info(String.format((Locale)null, "Rumble on gamepad %d: %04x %04x", controllerNumber, lowFreqMotor, highFreqMotor));
controllerHandler.handleRumble(controllerNumber, lowFreqMotor, highFreqMotor);
}
@Override @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (!surfaceCreated) { if (!surfaceCreated) {
@@ -4,8 +4,11 @@ import android.content.Context;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager; import android.hardware.usb.UsbManager;
import android.media.AudioAttributes;
import android.os.Build; import android.os.Build;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.InputEvent; import android.view.InputEvent;
@@ -14,6 +17,7 @@ import android.view.MotionEvent;
import android.widget.Toast; import android.widget.Toast;
import com.limelight.LimeLog; import com.limelight.LimeLog;
import com.limelight.binding.input.driver.AbstractController;
import com.limelight.binding.input.driver.UsbDriverListener; import com.limelight.binding.input.driver.UsbDriverListener;
import com.limelight.binding.input.driver.UsbDriverService; import com.limelight.binding.input.driver.UsbDriverService;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
@@ -51,6 +55,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private final double stickDeadzone; private final double stickDeadzone;
private final InputDeviceContext defaultContext = new InputDeviceContext(); private final InputDeviceContext defaultContext = new InputDeviceContext();
private final GameGestures gestures; private final GameGestures gestures;
private final Vibrator deviceVibrator;
private boolean hasGameController; private boolean hasGameController;
private final PreferenceConfiguration prefConfig; private final PreferenceConfiguration prefConfig;
@@ -61,6 +66,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
this.conn = conn; this.conn = conn;
this.gestures = gestures; this.gestures = gestures;
this.prefConfig = prefConfig; this.prefConfig = prefConfig;
this.deviceVibrator = (Vibrator) activityContext.getSystemService(Context.VIBRATOR_SERVICE);
// HACK: For now we're hardcoding a 10% deadzone. Some deadzone // HACK: For now we're hardcoding a 10% deadzone. Some deadzone
// is required for controller batching support to work. // is required for controller batching support to work.
@@ -151,6 +157,18 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
onInputDeviceAdded(deviceId); onInputDeviceAdded(deviceId);
} }
public void stop() {
for (int i = 0; i < inputDeviceContexts.size(); i++) {
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
if (deviceContext.vibrator != null) {
deviceContext.vibrator.cancel();
}
}
deviceVibrator.cancel();
}
private static boolean hasJoystickAxes(InputDevice device) { private static boolean hasJoystickAxes(InputDevice device) {
return (device.getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && return (device.getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK &&
getMotionRangeForJoystickAxis(device, MotionEvent.AXIS_X) != null && getMotionRangeForJoystickAxis(device, MotionEvent.AXIS_X) != null &&
@@ -206,6 +224,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
} }
if (PreferenceConfiguration.readPreferences(context).onscreenController) {
LimeLog.info("Counting OSC gamepad");
mask |= 1;
}
LimeLog.info("Enumerated "+count+" gamepads"); LimeLog.info("Enumerated "+count+" gamepads");
return mask; return mask;
} }
@@ -298,10 +321,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.assignedControllerNumber = true; context.assignedControllerNumber = true;
} }
private UsbDeviceContext createUsbDeviceContextForDevice(int deviceId) { private UsbDeviceContext createUsbDeviceContextForDevice(AbstractController device) {
UsbDeviceContext context = new UsbDeviceContext(); UsbDeviceContext context = new UsbDeviceContext();
context.id = deviceId; context.id = device.getControllerId();
context.device = device;
context.leftStickDeadzoneRadius = (float) stickDeadzone; context.leftStickDeadzoneRadius = (float) stickDeadzone;
context.rightStickDeadzoneRadius = (float) stickDeadzone; context.rightStickDeadzoneRadius = (float) stickDeadzone;
@@ -393,6 +417,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.name = devName; context.name = devName;
context.id = dev.getId(); context.id = dev.getId();
if (dev.getVibrator().hasVibrator()) {
context.vibrator = dev.getVibrator();
}
context.leftStickXAxis = MotionEvent.AXIS_X; context.leftStickXAxis = MotionEvent.AXIS_X;
context.leftStickYAxis = MotionEvent.AXIS_Y; context.leftStickYAxis = MotionEvent.AXIS_Y;
if (getMotionRangeForJoystickAxis(dev, context.leftStickXAxis) != null && if (getMotionRangeForJoystickAxis(dev, context.leftStickXAxis) != null &&
@@ -614,7 +642,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private short getActiveControllerMask() { private short getActiveControllerMask() {
if (prefConfig.multiController) { if (prefConfig.multiController) {
return (short)(currentControllers | initialControllers); return (short)(currentControllers | initialControllers | (prefConfig.onscreenController ? 1 : 0));
} }
else { else {
// Only Player 1 is active with multi-controller disabled // Only Player 1 is active with multi-controller disabled
@@ -1043,6 +1071,83 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
} }
private void rumbleVibrator(Vibrator vibrator, short lowFreqMotor, short highFreqMotor) {
if (lowFreqMotor == 0 && highFreqMotor == 0) {
// This case is easy - just cancel and get out
vibrator.cancel();
return;
}
// Since we can only use a single amplitude value, compute the desired amplitude
// by taking 75% of the big motor and 25% of the small motor.
// NB: This value is now 0-255 as required by VibrationEffect.
short lowFreqMotorMSB = (short)((lowFreqMotor >> 8) & 0xFF);
short highFreqMotorMSB = (short)((lowFreqMotor >> 8) & 0xFF);
int simulatedAmplitude = (int)((lowFreqMotorMSB * 0.75) + (highFreqMotorMSB * 0.25));
// Attempt to use amplitude-based control if we're on Oreo and the device
// supports amplitude-based vibration control.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (vibrator.hasAmplitudeControl()) {
VibrationEffect effect = VibrationEffect.createOneShot(60000, simulatedAmplitude);
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.build();
vibrator.vibrate(effect, audioAttributes);
return;
}
}
// If we reach this point, we don't have amplitude controls available, so
// we must emulate it by PWMing the vibration. Ick.
long pwmPeriod = 20;
long onTime = (long)((simulatedAmplitude / 255.0) * pwmPeriod);
long offTime = pwmPeriod - onTime;
vibrator.vibrate(new long[]{0, onTime, offTime}, 0);
}
public void handleRumble(short controllerNumber, short lowFreqMotor, short highFreqMotor) {
boolean foundMatchingDevice = false;
boolean vibrated = false;
for (int i = 0; i < inputDeviceContexts.size(); i++) {
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
if (deviceContext.controllerNumber == controllerNumber) {
foundMatchingDevice = true;
if (deviceContext.vibrator != null) {
vibrated = true;
rumbleVibrator(deviceContext.vibrator, lowFreqMotor, highFreqMotor);
}
}
}
for (int i = 0; i < usbDeviceContexts.size(); i++) {
UsbDeviceContext deviceContext = usbDeviceContexts.valueAt(i);
if (deviceContext.controllerNumber == controllerNumber) {
foundMatchingDevice = vibrated = true;
deviceContext.device.rumble((short)lowFreqMotor, (short)highFreqMotor);
}
}
// We may decide to rumble the device for player 1
if (controllerNumber == 0) {
// If we didn't find a matching device, it must be the on-screen
// controls that triggered the rumble. Vibrate the device if
// the user has requested that behavior.
if (!foundMatchingDevice && prefConfig.onscreenController && !prefConfig.onlyL3R3 && prefConfig.vibrateOsc) {
rumbleVibrator(deviceVibrator, lowFreqMotor, highFreqMotor);
}
else if (foundMatchingDevice && !vibrated && prefConfig.vibrateFallbackToDevice) {
// We found a device to vibrate but it didn't have rumble support. The user
// has requested us to vibrate the device in this case.
rumbleVibrator(deviceVibrator, lowFreqMotor, highFreqMotor);
}
}
}
public boolean handleButtonUp(KeyEvent event) { public boolean handleButtonUp(KeyEvent event) {
InputDeviceContext context = getContextForEvent(event); InputDeviceContext context = getContextForEvent(event);
if (context == null) { if (context == null) {
@@ -1294,12 +1399,30 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return true; return true;
} }
public void reportOscState(short buttonFlags,
short leftStickX, short leftStickY,
short rightStickX, short rightStickY,
byte leftTrigger, byte rightTrigger) {
defaultContext.leftStickX = leftStickX;
defaultContext.leftStickY = leftStickY;
defaultContext.rightStickX = rightStickX;
defaultContext.rightStickY = rightStickY;
defaultContext.leftTrigger = leftTrigger;
defaultContext.rightTrigger = rightTrigger;
defaultContext.inputMap = buttonFlags;
sendControllerInputPacket(defaultContext);
}
@Override @Override
public void reportControllerState(int controllerId, short buttonFlags, public void reportControllerState(int controllerId, short buttonFlags,
float leftStickX, float leftStickY, float leftStickX, float leftStickY,
float rightStickX, float rightStickY, float rightStickX, float rightStickY,
float leftTrigger, float rightTrigger) { float leftTrigger, float rightTrigger) {
UsbDeviceContext context = usbDeviceContexts.get(controllerId); GenericControllerContext context = usbDeviceContexts.get(controllerId);
if (context == null) { if (context == null) {
return; return;
} }
@@ -1334,19 +1457,19 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
} }
@Override @Override
public void deviceRemoved(int controllerId) { public void deviceRemoved(AbstractController controller) {
UsbDeviceContext context = usbDeviceContexts.get(controllerId); UsbDeviceContext context = usbDeviceContexts.get(controller.getControllerId());
if (context != null) { if (context != null) {
LimeLog.info("Removed controller: "+controllerId); LimeLog.info("Removed controller: "+controller.getControllerId());
releaseControllerNumber(context); releaseControllerNumber(context);
usbDeviceContexts.remove(controllerId); usbDeviceContexts.remove(controller.getControllerId());
} }
} }
@Override @Override
public void deviceAdded(int controllerId) { public void deviceAdded(AbstractController controller) {
UsbDeviceContext context = createUsbDeviceContextForDevice(controllerId); UsbDeviceContext context = createUsbDeviceContextForDevice(controller);
usbDeviceContexts.put(controllerId, context); usbDeviceContexts.put(controller.getControllerId(), context);
} }
class GenericControllerContext { class GenericControllerContext {
@@ -1375,6 +1498,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
class InputDeviceContext extends GenericControllerContext { class InputDeviceContext extends GenericControllerContext {
public String name; public String name;
public Vibrator vibrator;
public int leftStickXAxis = -1; public int leftStickXAxis = -1;
public int leftStickYAxis = -1; public int leftStickYAxis = -1;
@@ -1412,5 +1536,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public long startDownTime = 0; public long startDownTime = 0;
} }
class UsbDeviceContext extends GenericControllerContext {} class UsbDeviceContext extends GenericControllerContext {
public AbstractController device;
}
} }
@@ -37,11 +37,13 @@ public abstract class AbstractController {
this.listener = listener; this.listener = listener;
} }
public abstract void rumble(short lowFreqMotor, short highFreqMotor);
protected void notifyDeviceRemoved() { protected void notifyDeviceRemoved() {
listener.deviceRemoved(deviceId); listener.deviceRemoved(this);
} }
protected void notifyDeviceAdded() { protected void notifyDeviceAdded() {
listener.deviceAdded(deviceId); listener.deviceAdded(this);
} }
} }
@@ -131,6 +131,9 @@ public abstract class AbstractXboxController extends AbstractController {
stopped = true; stopped = true;
// Cancel any rumble effects
rumble((short)0, (short)0);
// Stop the input thread // Stop the input thread
if (inputThread != null) { if (inputThread != null) {
inputThread.interrupt(); inputThread.interrupt();
@@ -6,6 +6,6 @@ public interface UsbDriverListener {
float rightStickX, float rightStickY, float rightStickX, float rightStickY,
float leftTrigger, float rightTrigger); float leftTrigger, float rightTrigger);
void deviceRemoved(int controllerId); void deviceRemoved(AbstractController controller);
void deviceAdded(int controllerId); void deviceAdded(AbstractController controller);
} }
@@ -47,26 +47,21 @@ public class UsbDriverService extends Service implements UsbDriverListener {
} }
@Override @Override
public void deviceRemoved(int controllerId) { public void deviceRemoved(AbstractController controller) {
// Remove the the controller from our list (if not removed already) // Remove the the controller from our list (if not removed already)
for (AbstractController controller : controllers) { controllers.remove(controller);
if (controller.getControllerId() == controllerId) {
controllers.remove(controller);
break;
}
}
// Call through to the client's listener // Call through to the client's listener
if (listener != null) { if (listener != null) {
listener.deviceRemoved(controllerId); listener.deviceRemoved(controller);
} }
} }
@Override @Override
public void deviceAdded(int controllerId) { public void deviceAdded(AbstractController controller) {
// Call through to the client's listener // Call through to the client's listener
if (listener != null) { if (listener != null) {
listener.deviceAdded(controllerId); listener.deviceAdded(controller);
} }
} }
@@ -113,7 +108,7 @@ public class UsbDriverService extends Service implements UsbDriverListener {
// Report all controllerMap that already exist // Report all controllerMap that already exist
if (listener != null) { if (listener != null) {
for (AbstractController controller : controllers) { for (AbstractController controller : controllers) {
listener.deviceAdded(controller.getControllerId()); listener.deviceAdded(controller);
} }
} }
} }
@@ -139,4 +139,17 @@ public class Xbox360Controller extends AbstractXboxController {
// No need to fail init if the LED command fails // No need to fail init if the LED command fails
return true; return true;
} }
@Override
public void rumble(short lowFreqMotor, short highFreqMotor) {
byte[] data = {
0x00, 0x08, 0x00,
(byte)(lowFreqMotor >> 8), (byte)(highFreqMotor >> 8),
0x00, 0x00, 0x00
};
int res = connection.bulkTransfer(outEndpt, data, data.length, 100);
if (res != data.length) {
LimeLog.warning("Rumble transfer failed: "+res);
}
}
} }
@@ -48,6 +48,7 @@ public class XboxOneController extends AbstractXboxController {
new InitPacket(0x24c6, 0x543a, RUMBLE_INIT2), new InitPacket(0x24c6, 0x543a, RUMBLE_INIT2),
}; };
private byte seqNum = 0;
public XboxOneController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) { public XboxOneController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
super(device, connection, deviceId, listener); super(device, connection, deviceId, listener);
@@ -134,8 +135,6 @@ public class XboxOneController extends AbstractXboxController {
@Override @Override
protected boolean doInit() { protected boolean doInit() {
byte seqNum = 0;
// Send all applicable init packets // Send all applicable init packets
for (InitPacket pkt : INIT_PKTS) { for (InitPacket pkt : INIT_PKTS) {
if (pkt.vendorId != 0 && device.getVendorId() != pkt.vendorId) { if (pkt.vendorId != 0 && device.getVendorId() != pkt.vendorId) {
@@ -162,6 +161,20 @@ public class XboxOneController extends AbstractXboxController {
return true; return true;
} }
@Override
public void rumble(short lowFreqMotor, short highFreqMotor) {
byte[] data = {
0x09, 0x00, seqNum++, 0x09, 0x00,
0x0F, 0x00, 0x00,
(byte)(lowFreqMotor >> 9), (byte)(highFreqMotor >> 9),
(byte)0xFF, 0x00, (byte)0xFF
};
int res = connection.bulkTransfer(outEndpt, data, data.length, 100);
if (res != data.length) {
LimeLog.warning("Rumble transfer failed: "+res);
}
}
private static class InitPacket { private static class InitPacket {
final int vendorId; final int vendorId;
final int productId; final int productId;
@@ -13,6 +13,7 @@ import android.widget.RelativeLayout;
import android.widget.Toast; import android.widget.Toast;
import com.limelight.R; import com.limelight.R;
import com.limelight.binding.input.ControllerHandler;
import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnection;
import java.util.ArrayList; import java.util.ArrayList;
@@ -38,7 +39,7 @@ public class VirtualController {
private static final boolean _PRINT_DEBUG_INFORMATION = false; private static final boolean _PRINT_DEBUG_INFORMATION = false;
private NvConnection connection = null; private ControllerHandler controllerHandler;
private Context context = null; private Context context = null;
private FrameLayout frame_layout = null; private FrameLayout frame_layout = null;
@@ -53,8 +54,8 @@ public class VirtualController {
private List<VirtualControllerElement> elements = new ArrayList<>(); private List<VirtualControllerElement> elements = new ArrayList<>();
public VirtualController(final NvConnection conn, FrameLayout layout, final Context context) { public VirtualController(final ControllerHandler controllerHandler, FrameLayout layout, final Context context) {
this.connection = conn; this.controllerHandler = controllerHandler;
this.frame_layout = layout; this.frame_layout = layout;
this.context = context; this.context = context;
@@ -173,15 +174,15 @@ public class VirtualController {
_DBG("LEFT STICK X: " + inputContext.leftStickX + " Y: " + inputContext.leftStickY); _DBG("LEFT STICK X: " + inputContext.leftStickX + " Y: " + inputContext.leftStickY);
_DBG("RIGHT STICK X: " + inputContext.rightStickX + " Y: " + inputContext.rightStickY); _DBG("RIGHT STICK X: " + inputContext.rightStickX + " Y: " + inputContext.rightStickY);
if (connection != null) { if (controllerHandler != null) {
connection.sendControllerInput( controllerHandler.reportOscState(
inputContext.inputMap, inputContext.inputMap,
inputContext.leftTrigger,
inputContext.rightTrigger,
inputContext.leftStickX, inputContext.leftStickX,
inputContext.leftStickY, inputContext.leftStickY,
inputContext.rightStickX, inputContext.rightStickX,
inputContext.rightStickY inputContext.rightStickY,
inputContext.leftTrigger,
inputContext.rightTrigger
); );
} }
} }
@@ -35,6 +35,8 @@ public class PreferenceConfiguration {
private static final String MOUSE_EMULATION_STRING = "checkbox_mouse_emulation"; private static final String MOUSE_EMULATION_STRING = "checkbox_mouse_emulation";
private static final String MOUSE_NAV_BUTTONS_STRING = "checkbox_mouse_nav_buttons"; private static final String MOUSE_NAV_BUTTONS_STRING = "checkbox_mouse_nav_buttons";
static final String UNLOCK_FPS_STRING = "checkbox_unlock_fps"; static final String UNLOCK_FPS_STRING = "checkbox_unlock_fps";
private static final String VIBRATE_OSC_PREF_STRING = "checkbox_vibrate_osc";
private static final String VIBRATE_FALLBACK_PREF_STRING = "checkbox_vibrate_fallback";
static final String DEFAULT_RESOLUTION = "720p"; static final String DEFAULT_RESOLUTION = "720p";
static final String DEFAULT_FPS = "60"; static final String DEFAULT_FPS = "60";
@@ -58,6 +60,8 @@ public class PreferenceConfiguration {
private static final boolean DEFAULT_MOUSE_EMULATION = true; private static final boolean DEFAULT_MOUSE_EMULATION = true;
private static final boolean DEFAULT_MOUSE_NAV_BUTTONS = false; private static final boolean DEFAULT_MOUSE_NAV_BUTTONS = false;
private static final boolean DEFAULT_UNLOCK_FPS = false; private static final boolean DEFAULT_UNLOCK_FPS = false;
private static final boolean DEFAULT_VIBRATE_OSC = true;
private static final boolean DEFAULT_VIBRATE_FALLBACK = false;
public static final int FORCE_H265_ON = -1; public static final int FORCE_H265_ON = -1;
public static final int AUTOSELECT_H265 = 0; public static final int AUTOSELECT_H265 = 0;
@@ -79,6 +83,8 @@ public class PreferenceConfiguration {
public boolean mouseEmulation; public boolean mouseEmulation;
public boolean mouseNavButtons; public boolean mouseNavButtons;
public boolean unlockFps; public boolean unlockFps;
public boolean vibrateOsc;
public boolean vibrateFallbackToDevice;
private static int getHeightFromResolutionString(String resString) { private static int getHeightFromResolutionString(String resString) {
if (resString.equalsIgnoreCase("360p")) { if (resString.equalsIgnoreCase("360p")) {
@@ -329,6 +335,8 @@ public class PreferenceConfiguration {
config.mouseEmulation = prefs.getBoolean(MOUSE_EMULATION_STRING, DEFAULT_MOUSE_EMULATION); config.mouseEmulation = prefs.getBoolean(MOUSE_EMULATION_STRING, DEFAULT_MOUSE_EMULATION);
config.mouseNavButtons = prefs.getBoolean(MOUSE_NAV_BUTTONS_STRING, DEFAULT_MOUSE_NAV_BUTTONS); config.mouseNavButtons = prefs.getBoolean(MOUSE_NAV_BUTTONS_STRING, DEFAULT_MOUSE_NAV_BUTTONS);
config.unlockFps = prefs.getBoolean(UNLOCK_FPS_STRING, DEFAULT_UNLOCK_FPS); config.unlockFps = prefs.getBoolean(UNLOCK_FPS_STRING, DEFAULT_UNLOCK_FPS);
config.vibrateOsc = prefs.getBoolean(VIBRATE_OSC_PREF_STRING, DEFAULT_VIBRATE_OSC);
config.vibrateFallbackToDevice = prefs.getBoolean(VIBRATE_FALLBACK_PREF_STRING, DEFAULT_VIBRATE_FALLBACK);
return config; return config;
} }
+4
View File
@@ -136,6 +136,8 @@
<string name="category_input_settings">Input Settings</string> <string name="category_input_settings">Input Settings</string>
<string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string> <string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string>
<string name="summary_checkbox_multi_controller">Unchecking this option forces a gamepad to always be present</string> <string name="summary_checkbox_multi_controller">Unchecking this option forces a gamepad to always be present</string>
<string name="title_checkbox_vibrate_fallback">Emulate rumble support with vibration</string>
<string name="summary_checkbox_vibrate_fallback">Vibrates your device to emulate rumble if your gamepad does not support it</string>
<string name="title_seekbar_deadzone">Adjust analog stick deadzone</string> <string name="title_seekbar_deadzone">Adjust analog stick deadzone</string>
<string name="suffix_seekbar_deadzone">%</string> <string name="suffix_seekbar_deadzone">%</string>
<string name="title_checkbox_xb1_driver">Xbox 360/One controller driver</string> <string name="title_checkbox_xb1_driver">Xbox 360/One controller driver</string>
@@ -150,6 +152,8 @@
<string name="category_on_screen_controls_settings">On-screen Controls Settings</string> <string name="category_on_screen_controls_settings">On-screen Controls Settings</string>
<string name="title_checkbox_show_onscreen_controls">Show on-screen controls</string> <string name="title_checkbox_show_onscreen_controls">Show on-screen controls</string>
<string name="summary_checkbox_show_onscreen_controls">Show virtual controller overlay on touchscreen</string> <string name="summary_checkbox_show_onscreen_controls">Show virtual controller overlay on touchscreen</string>
<string name="title_checkbox_vibrate_osc">Enable vibration</string>
<string name="summary_checkbox_vibrate_osc">Vibrates your device to emulate rumble for the on-screen controls</string>
<string name="title_only_l3r3">Only show L3 and R3</string> <string name="title_only_l3r3">Only show L3 and R3</string>
<string name="summary_only_l3r3">Hide all virtual buttons except L3 and R3</string> <string name="summary_only_l3r3">Hide all virtual buttons except L3 and R3</string>
<string name="title_reset_osc">Clear saved on-screen controls layout</string> <string name="title_reset_osc">Clear saved on-screen controls layout</string>
+11
View File
@@ -82,6 +82,11 @@
android:title="@string/title_checkbox_mouse_emulation" android:title="@string/title_checkbox_mouse_emulation"
android:summary="@string/summary_checkbox_mouse_emulation" android:summary="@string/summary_checkbox_mouse_emulation"
android:defaultValue="true" /> android:defaultValue="true" />
<CheckBoxPreference
android:key="checkbox_vibrate_fallback"
android:title="@string/title_checkbox_vibrate_fallback"
android:summary="@string/summary_checkbox_vibrate_fallback"
android:defaultValue="false" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/category_on_screen_controls_settings" <PreferenceCategory android:title="@string/category_on_screen_controls_settings"
android:key="category_onscreen_controls"> android:key="category_onscreen_controls">
@@ -90,6 +95,12 @@
android:key="checkbox_show_onscreen_controls" android:key="checkbox_show_onscreen_controls"
android:summary="@string/summary_checkbox_show_onscreen_controls" android:summary="@string/summary_checkbox_show_onscreen_controls"
android:title="@string/title_checkbox_show_onscreen_controls" /> android:title="@string/title_checkbox_show_onscreen_controls" />
<CheckBoxPreference
android:key="checkbox_vibrate_osc"
android:dependency="checkbox_show_onscreen_controls"
android:title="@string/title_checkbox_vibrate_osc"
android:summary="@string/summary_checkbox_vibrate_osc"
android:defaultValue="true" />
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="false" android:defaultValue="false"
android:dependency="checkbox_show_onscreen_controls" android:dependency="checkbox_show_onscreen_controls"