mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-10-20 07:24:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32af2d0831 | ||
|
|
242b03d4b5 | ||
|
|
87a62666ac | ||
|
|
2dcf5486da | ||
|
|
60d3d8b3ae | ||
|
|
e9141d65fe | ||
|
|
aae591daec | ||
|
|
a5ca8a7472 | ||
|
|
36f8cc02cb | ||
|
|
55b9645651 | ||
|
|
d30ecbed5b | ||
|
|
0bbd27f04c | ||
|
|
3c53fb7403 | ||
|
|
7a81950819 | ||
|
|
74f212c702 | ||
|
|
36be943854 | ||
|
|
26a4fc75a5 | ||
|
|
a5ec5fc265 | ||
|
|
541ac44be4 | ||
|
|
117b555fcd | ||
|
|
a10cd04441 | ||
|
|
53dccbde2a | ||
|
|
56625dfe4b | ||
|
|
2eab5a3b7b |
No files matched your search
@@ -8,7 +8,7 @@ whether in your own home or over the internet.
|
|||||||
|
|
||||||
[Moonlight-pc](https://github.com/moonlight-stream/moonlight-pc) is also currently in development for Windows, OS X and Linux. Versions for [iOS](https://github.com/moonlight-stream/moonlight-ios) and [Windows and Windows Phone](https://github.com/moonlight-stream/moonlight-windows) are also in development.
|
[Moonlight-pc](https://github.com/moonlight-stream/moonlight-pc) is also currently in development for Windows, OS X and Linux. Versions for [iOS](https://github.com/moonlight-stream/moonlight-ios) and [Windows and Windows Phone](https://github.com/moonlight-stream/moonlight-windows) are also in development.
|
||||||
|
|
||||||
Check our [wiki](https://github.com/moonlight-stream/moonlight-android/wiki) for more detailed information or a troubleshooting guide.
|
Check our [wiki](https://github.com/moonlight-stream/moonlight-docs/wiki) for more detailed information or a troubleshooting guide.
|
||||||
|
|
||||||
##Features
|
##Features
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -4,15 +4,15 @@ import org.apache.tools.ant.taskdefs.condition.Os
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 23
|
compileSdkVersion 24
|
||||||
buildToolsVersion "23.0.3"
|
buildToolsVersion "24.0.0"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 23
|
targetSdkVersion 24
|
||||||
|
|
||||||
versionName "4.5.9"
|
versionName "4.6"
|
||||||
versionCode = 100
|
versionCode = 103
|
||||||
}
|
}
|
||||||
|
|
||||||
productFlavors {
|
productFlavors {
|
||||||
|
|||||||
Binary file not shown.
@@ -37,7 +37,7 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".AppView"
|
android:name=".AppView"
|
||||||
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|fontScale|uiMode" >
|
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection" >
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="com.limelight.PcView" />
|
android:value="com.limelight.PcView" />
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ package com.limelight;
|
|||||||
import com.limelight.binding.PlatformBinding;
|
import com.limelight.binding.PlatformBinding;
|
||||||
import com.limelight.binding.input.ControllerHandler;
|
import com.limelight.binding.input.ControllerHandler;
|
||||||
import com.limelight.binding.input.KeyboardTranslator;
|
import com.limelight.binding.input.KeyboardTranslator;
|
||||||
import com.limelight.binding.input.NvMouseHelper;
|
import com.limelight.binding.input.capture.InputCaptureManager;
|
||||||
|
import com.limelight.binding.input.capture.InputCaptureProvider;
|
||||||
import com.limelight.binding.input.TouchContext;
|
import com.limelight.binding.input.TouchContext;
|
||||||
import com.limelight.binding.input.driver.UsbDriverService;
|
import com.limelight.binding.input.driver.UsbDriverService;
|
||||||
import com.limelight.binding.input.evdev.EvdevHandler;
|
|
||||||
import com.limelight.binding.input.evdev.EvdevListener;
|
import com.limelight.binding.input.evdev.EvdevListener;
|
||||||
import com.limelight.binding.input.virtual_controller.VirtualController;
|
import com.limelight.binding.input.virtual_controller.VirtualController;
|
||||||
import com.limelight.binding.video.EnhancedDecoderRenderer;
|
import com.limelight.binding.video.EnhancedDecoderRenderer;
|
||||||
@@ -39,6 +39,7 @@ import android.hardware.input.InputManager;
|
|||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -48,12 +49,10 @@ import android.view.InputDevice;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.SurfaceView;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnGenericMotionListener;
|
import android.view.View.OnGenericMotionListener;
|
||||||
import android.view.View.OnSystemUiVisibilityChangeListener;
|
import android.view.View.OnSystemUiVisibilityChangeListener;
|
||||||
import android.view.View.OnTouchListener;
|
import android.view.View.OnTouchListener;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
@@ -92,7 +91,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
private boolean connecting = false;
|
private boolean connecting = false;
|
||||||
private boolean connected = false;
|
private boolean connected = false;
|
||||||
|
|
||||||
private EvdevHandler evdevHandler;
|
private InputCaptureProvider inputCaptureProvider;
|
||||||
private int modifierFlags = 0;
|
private int modifierFlags = 0;
|
||||||
private boolean grabbedInput = true;
|
private boolean grabbedInput = true;
|
||||||
private boolean grabComboDown = false;
|
private boolean grabComboDown = false;
|
||||||
@@ -247,35 +246,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||||
inputManager.registerInputDeviceListener(controllerHandler, null);
|
inputManager.registerInputDeviceListener(controllerHandler, null);
|
||||||
|
|
||||||
boolean aspectRatioMatch = false;
|
// Set to the optimal mode for streaming
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
prepareDisplayForRendering();
|
||||||
// On KitKat and later (where we can use the whole screen via immersive mode), we'll
|
|
||||||
// calculate whether we need to scale by aspect ratio or not. If not, we'll use
|
|
||||||
// setFixedSize so we can handle 4K properly. The only known devices that have
|
|
||||||
// >= 4K screens have exactly 4K screens, so we'll be able to hit this good path
|
|
||||||
// on these devices. On Marshmallow, we can start changing to 4K manually but no
|
|
||||||
// 4K devices run 6.0 at the moment.
|
|
||||||
Display display = getWindowManager().getDefaultDisplay();
|
|
||||||
Point screenSize = new Point(0, 0);
|
|
||||||
display.getSize(screenSize);
|
|
||||||
|
|
||||||
double screenAspectRatio = ((double)screenSize.y) / screenSize.x;
|
|
||||||
double streamAspectRatio = ((double)prefConfig.height) / prefConfig.width;
|
|
||||||
if (Math.abs(screenAspectRatio - streamAspectRatio) < 0.001) {
|
|
||||||
LimeLog.info("Stream has compatible aspect ratio with output display");
|
|
||||||
aspectRatioMatch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SurfaceHolder sh = streamView.getHolder();
|
|
||||||
if (prefConfig.stretchVideo || aspectRatioMatch) {
|
|
||||||
// Set the surface to the size of the video
|
|
||||||
sh.setFixedSize(prefConfig.width, prefConfig.height);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Set the surface to scale based on the aspect ratio of the stream
|
|
||||||
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize touch contexts
|
// Initialize touch contexts
|
||||||
for (int i = 0; i < touchContextMap.length; i++) {
|
for (int i = 0; i < touchContextMap.length; i++) {
|
||||||
@@ -284,12 +256,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
streamView);
|
streamView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LimelightBuildProps.ROOT_BUILD) {
|
// Use sustained performance mode on N+ to ensure consistent
|
||||||
// Start watching for raw input
|
// CPU availability
|
||||||
evdevHandler = new EvdevHandler(this, this);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
evdevHandler.start();
|
getWindow().setSustainedPerformanceMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputCaptureProvider = InputCaptureManager.getInputCaptureProvider(this, this);
|
||||||
|
|
||||||
if (prefConfig.onscreenController) {
|
if (prefConfig.onscreenController) {
|
||||||
// create virtual onscreen controller
|
// create virtual onscreen controller
|
||||||
virtualController = new VirtualController(conn,
|
virtualController = new VirtualController(conn,
|
||||||
@@ -305,7 +279,96 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The connection will be started when the surface gets created
|
// The connection will be started when the surface gets created
|
||||||
sh.addCallback(this);
|
streamView.getHolder().addCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareDisplayForRendering() {
|
||||||
|
Display display = getWindowManager().getDefaultDisplay();
|
||||||
|
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
|
||||||
|
|
||||||
|
// On M, we can explicitly set the optimal display mode
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
Display.Mode bestMode = display.getMode();
|
||||||
|
for (Display.Mode candidate : display.getSupportedModes()) {
|
||||||
|
boolean refreshRateOk = candidate.getRefreshRate() >= bestMode.getRefreshRate() &&
|
||||||
|
candidate.getRefreshRate() < 63;
|
||||||
|
boolean resolutionOk = candidate.getPhysicalWidth() >= bestMode.getPhysicalWidth() &&
|
||||||
|
candidate.getPhysicalHeight() >= bestMode.getPhysicalHeight() &&
|
||||||
|
candidate.getPhysicalWidth() <= 4096;
|
||||||
|
|
||||||
|
LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+
|
||||||
|
candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate());
|
||||||
|
|
||||||
|
// On non-4K streams, we force the resolution to never change
|
||||||
|
if (prefConfig.width < 3840) {
|
||||||
|
if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() ||
|
||||||
|
display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the refresh rate doesn't regress
|
||||||
|
if (!refreshRateOk) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the resolution doesn't regress
|
||||||
|
if (!resolutionOk) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bestMode = candidate;
|
||||||
|
}
|
||||||
|
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
|
||||||
|
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
|
||||||
|
windowLayoutParams.preferredDisplayModeId = bestMode.getModeId();
|
||||||
|
}
|
||||||
|
// On L, we can at least tell the OS that we want 60 Hz
|
||||||
|
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
float bestRefreshRate = display.getRefreshRate();
|
||||||
|
for (float candidate : display.getSupportedRefreshRates()) {
|
||||||
|
if (candidate > bestRefreshRate && candidate < 63) {
|
||||||
|
LimeLog.info("Examining refresh rate: "+candidate);
|
||||||
|
bestRefreshRate = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LimeLog.info("Selected refresh rate: "+bestRefreshRate);
|
||||||
|
windowLayoutParams.preferredRefreshRate = bestRefreshRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the display mode change
|
||||||
|
getWindow().setAttributes(windowLayoutParams);
|
||||||
|
|
||||||
|
// From 4.4 to 5.1 we can't ask for a 4K display mode, so we'll
|
||||||
|
// need to hint the OS to provide one.
|
||||||
|
boolean aspectRatioMatch = false;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
|
||||||
|
Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||||
|
// On KitKat and later (where we can use the whole screen via immersive mode), we'll
|
||||||
|
// calculate whether we need to scale by aspect ratio or not. If not, we'll use
|
||||||
|
// setFixedSize so we can handle 4K properly. The only known devices that have
|
||||||
|
// >= 4K screens have exactly 4K screens, so we'll be able to hit this good path
|
||||||
|
// on these devices. On Marshmallow, we can start changing to 4K manually but no
|
||||||
|
// 4K devices run 6.0 at the moment.
|
||||||
|
Point screenSize = new Point(0, 0);
|
||||||
|
display.getSize(screenSize);
|
||||||
|
|
||||||
|
double screenAspectRatio = ((double)screenSize.y) / screenSize.x;
|
||||||
|
double streamAspectRatio = ((double)prefConfig.height) / prefConfig.width;
|
||||||
|
if (Math.abs(screenAspectRatio - streamAspectRatio) < 0.001) {
|
||||||
|
LimeLog.info("Stream has compatible aspect ratio with output display");
|
||||||
|
aspectRatioMatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefConfig.stretchVideo || aspectRatioMatch) {
|
||||||
|
// Set the surface to the size of the video
|
||||||
|
streamView.getHolder().setFixedSize(prefConfig.width, prefConfig.height);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Set the surface to scale based on the aspect ratio of the stream
|
||||||
|
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDataConnection()
|
private void checkDataConnection()
|
||||||
@@ -406,16 +469,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (grabbedInput) {
|
if (grabbedInput) {
|
||||||
NvMouseHelper.setCursorVisibility(Game.this, true);
|
inputCaptureProvider.disableCapture();
|
||||||
if (evdevHandler != null) {
|
|
||||||
evdevHandler.ungrabAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
NvMouseHelper.setCursorVisibility(Game.this, false);
|
inputCaptureProvider.enableCapture();
|
||||||
if (evdevHandler != null) {
|
|
||||||
evdevHandler.regrabAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
grabbedInput = !grabbedInput;
|
grabbedInput = !grabbedInput;
|
||||||
@@ -522,11 +579,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eat repeat down events
|
|
||||||
if (event.getRepeatCount() > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass through keyboard input if we're not grabbing
|
// Pass through keyboard input if we're not grabbing
|
||||||
if (!grabbedInput) {
|
if (!grabbedInput) {
|
||||||
return super.onKeyDown(keyCode, event);
|
return super.onKeyDown(keyCode, event);
|
||||||
@@ -643,10 +695,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get relative axis values if we can
|
// Get relative axis values if we can
|
||||||
if (NvMouseHelper.eventHasRelativeMouseAxes(event)) {
|
if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) {
|
||||||
// Send the deltas straight from the motion event
|
// Send the deltas straight from the motion event
|
||||||
conn.sendMouseMove((short)NvMouseHelper.getRelativeAxisX(event),
|
conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event),
|
||||||
(short)NvMouseHelper.getRelativeAxisY(event));
|
(short) inputCaptureProvider.getRelativeAxisY(event));
|
||||||
|
|
||||||
// We have to also update the position Android thinks the cursor is at
|
// We have to also update the position Android thinks the cursor is at
|
||||||
// in order to avoid jumping when we stop moving or click.
|
// in order to avoid jumping when we stop moving or click.
|
||||||
@@ -823,14 +875,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
conn.stop();
|
conn.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the Evdev reader to allow use of captured input devices
|
|
||||||
if (evdevHandler != null) {
|
|
||||||
evdevHandler.stop();
|
|
||||||
evdevHandler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable cursor visibility again
|
// Enable cursor visibility again
|
||||||
NvMouseHelper.setCursorVisibility(this, true);
|
inputCaptureProvider.disableCapture();
|
||||||
|
|
||||||
|
// Destroy the capture provider
|
||||||
|
inputCaptureProvider.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -876,7 +925,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Hide the mouse cursor now. Doing it before
|
// Hide the mouse cursor now. Doing it before
|
||||||
// dismissing the spinner seems to be undone
|
// dismissing the spinner seems to be undone
|
||||||
// when the spinner gets displayed.
|
// when the spinner gets displayed.
|
||||||
NvMouseHelper.setCursorVisibility(Game.this, false);
|
inputCaptureProvider.enableCapture();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -922,6 +971,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
|
// HACK: Android is supposed to let you return from this function
|
||||||
|
// before throwing a fit if you access the surface again. Unfortunately,
|
||||||
|
// MediaCodec often tries to access the destroyed surface and triggers
|
||||||
|
// an IllegalStateException. To workaround this, we will invoke
|
||||||
|
// the DecoderRenderer's stop function ourselves, so it will hopefully
|
||||||
|
// happen early enough to not trigger the bug
|
||||||
|
decoderRenderer.stop();
|
||||||
|
|
||||||
stopConnection();
|
stopConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
// Stop updates and wait while pairing
|
// Stop updates and wait while pairing
|
||||||
stopComputerUpdates(true);
|
stopComputerUpdates(true);
|
||||||
|
|
||||||
InetAddress addr = null;
|
InetAddress addr;
|
||||||
if (computer.reachability == ComputerDetails.Reachability.LOCAL) {
|
if (computer.reachability == ComputerDetails.Reachability.LOCAL) {
|
||||||
addr = computer.localIp;
|
addr = computer.localIp;
|
||||||
}
|
}
|
||||||
@@ -429,7 +429,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
NvHTTP httpConn;
|
NvHTTP httpConn;
|
||||||
String message;
|
String message;
|
||||||
try {
|
try {
|
||||||
InetAddress addr = null;
|
InetAddress addr;
|
||||||
if (computer.reachability == ComputerDetails.Reachability.LOCAL) {
|
if (computer.reachability == ComputerDetails.Reachability.LOCAL) {
|
||||||
addr = computer.localIp;
|
addr = computer.localIp;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,6 +342,17 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The ADT-1 controller needs a similar fixup to the ASUS Gamepad
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
||||||
|
// The device name provided is just "Gamepad" which is pretty useless, so we
|
||||||
|
// use VID/PID instead
|
||||||
|
if (dev.getVendorId() == 0x18d1 && dev.getProductId() == 0x2c40) {
|
||||||
|
context.backIsStart = true;
|
||||||
|
context.modeIsSelect = true;
|
||||||
|
context.triggerDeadzone = 0.30f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (devName != null) {
|
if (devName != null) {
|
||||||
// For the Nexus Player (and probably other ATV devices), we should
|
// For the Nexus Player (and probably other ATV devices), we should
|
||||||
// use the back button as start since it doesn't have a start/menu button
|
// use the back button as start since it doesn't have a start/menu button
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.PointerIcon;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.N)
|
||||||
|
public class AndroidCaptureProvider extends InputCaptureProvider {
|
||||||
|
private ViewGroup rootViewGroup;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public AndroidCaptureProvider(Activity activity) {
|
||||||
|
this.context = activity;
|
||||||
|
this.rootViewGroup = (ViewGroup) activity.getWindow().getDecorView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCaptureProviderSupported() {
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPointerIconOnAllViews(PointerIcon icon) {
|
||||||
|
for (int i = 0; i < rootViewGroup.getChildCount(); i++) {
|
||||||
|
View view = rootViewGroup.getChildAt(i);
|
||||||
|
view.setPointerIcon(icon);
|
||||||
|
}
|
||||||
|
rootViewGroup.setPointerIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enableCapture() {
|
||||||
|
setPointerIconOnAllViews(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disableCapture() {
|
||||||
|
setPointerIconOnAllViews(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X) != 0 ||
|
||||||
|
event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getRelativeAxisX(MotionEvent event) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getRelativeAxisY(MotionEvent event) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import com.limelight.LimeLog;
|
||||||
|
import com.limelight.binding.input.evdev.EvdevCaptureProvider;
|
||||||
|
import com.limelight.binding.input.evdev.EvdevListener;
|
||||||
|
|
||||||
|
public class InputCaptureManager {
|
||||||
|
public static InputCaptureProvider getInputCaptureProvider(Activity activity, EvdevListener rootListener) {
|
||||||
|
// Shield capture is preferred because it can capture when the cursor is over
|
||||||
|
// the system UI. Android N native capture can only capture over views owned
|
||||||
|
// by the application.
|
||||||
|
if (ShieldCaptureProvider.isCaptureProviderSupported()) {
|
||||||
|
LimeLog.info("Using NVIDIA mouse capture extension");
|
||||||
|
return new ShieldCaptureProvider(activity);
|
||||||
|
}
|
||||||
|
else if (AndroidCaptureProvider.isCaptureProviderSupported()) {
|
||||||
|
LimeLog.info("Using Android N+ native mouse capture");
|
||||||
|
return new AndroidCaptureProvider(activity);
|
||||||
|
}
|
||||||
|
else if (EvdevCaptureProvider.isCaptureProviderSupported()) {
|
||||||
|
LimeLog.info("Using Evdev mouse capture");
|
||||||
|
return new EvdevCaptureProvider(activity, rootListener);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LimeLog.info("Mouse capture not available");
|
||||||
|
return new NullCaptureProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
public abstract class InputCaptureProvider {
|
||||||
|
public abstract void enableCapture();
|
||||||
|
public abstract void disableCapture();
|
||||||
|
public void destroy() {}
|
||||||
|
|
||||||
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRelativeAxisX(MotionEvent event) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRelativeAxisY(MotionEvent event) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
|
||||||
|
public class NullCaptureProvider extends InputCaptureProvider {
|
||||||
|
@Override
|
||||||
|
public void enableCapture() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disableCapture() {}
|
||||||
|
}
|
||||||
+28
-15
@@ -1,12 +1,10 @@
|
|||||||
package com.limelight.binding.input;
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.input.InputManager;
|
import android.hardware.input.InputManager;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -18,12 +16,14 @@ import java.lang.reflect.Method;
|
|||||||
//
|
//
|
||||||
// http://docs.nvidia.com/gameworks/index.html#technologies/mobile/game_controller_handling_mouse.htm
|
// http://docs.nvidia.com/gameworks/index.html#technologies/mobile/game_controller_handling_mouse.htm
|
||||||
|
|
||||||
public class NvMouseHelper {
|
public class ShieldCaptureProvider extends InputCaptureProvider {
|
||||||
private static boolean nvExtensionSupported;
|
private static boolean nvExtensionSupported;
|
||||||
private static Method methodSetCursorVisibility;
|
private static Method methodSetCursorVisibility;
|
||||||
private static int AXIS_RELATIVE_X;
|
private static int AXIS_RELATIVE_X;
|
||||||
private static int AXIS_RELATIVE_Y;
|
private static int AXIS_RELATIVE_Y;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
methodSetCursorVisibility = InputManager.class.getMethod("setCursorVisibility", boolean.class);
|
methodSetCursorVisibility = InputManager.class.getMethod("setCursorVisibility", boolean.class);
|
||||||
@@ -36,16 +36,19 @@ public class NvMouseHelper {
|
|||||||
|
|
||||||
nvExtensionSupported = true;
|
nvExtensionSupported = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LimeLog.info("NvMouseHelper not supported");
|
|
||||||
nvExtensionSupported = false;
|
nvExtensionSupported = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean setCursorVisibility(Context context, boolean visible) {
|
public ShieldCaptureProvider(Context context) {
|
||||||
if (!nvExtensionSupported) {
|
this.context = context;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public static boolean isCaptureProviderSupported() {
|
||||||
|
return nvExtensionSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean setCursorVisibility(boolean visible) {
|
||||||
try {
|
try {
|
||||||
methodSetCursorVisibility.invoke(context.getSystemService(Context.INPUT_SERVICE), visible);
|
methodSetCursorVisibility.invoke(context.getSystemService(Context.INPUT_SERVICE), visible);
|
||||||
return true;
|
return true;
|
||||||
@@ -58,19 +61,29 @@ public class NvMouseHelper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
@Override
|
||||||
if (!nvExtensionSupported) {
|
public void enableCapture() {
|
||||||
return false;
|
setCursorVisibility(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disableCapture() {
|
||||||
|
setCursorVisibility(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
return event.getAxisValue(AXIS_RELATIVE_X) != 0 ||
|
return event.getAxisValue(AXIS_RELATIVE_X) != 0 ||
|
||||||
event.getAxisValue(AXIS_RELATIVE_Y) != 0;
|
event.getAxisValue(AXIS_RELATIVE_Y) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getRelativeAxisX(MotionEvent event) {
|
@Override
|
||||||
|
public float getRelativeAxisX(MotionEvent event) {
|
||||||
return event.getAxisValue(AXIS_RELATIVE_X);
|
return event.getAxisValue(AXIS_RELATIVE_X);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getRelativeAxisY(MotionEvent event) {
|
@Override
|
||||||
|
public float getRelativeAxisY(MotionEvent event) {
|
||||||
return event.getAxisValue(AXIS_RELATIVE_Y);
|
return event.getAxisValue(AXIS_RELATIVE_Y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,5 @@
|
|||||||
package com.limelight.binding.input.driver;
|
package com.limelight.binding.input.driver;
|
||||||
|
|
||||||
import android.hardware.usb.UsbEndpoint;
|
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
|
||||||
import com.limelight.binding.video.MediaCodecHelper;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
|
|
||||||
public abstract class AbstractController {
|
public abstract class AbstractController {
|
||||||
|
|
||||||
private final int deviceId;
|
private final int deviceId;
|
||||||
|
|||||||
@@ -72,14 +72,14 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
|||||||
|
|
||||||
// Initial attachment broadcast
|
// Initial attachment broadcast
|
||||||
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
|
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
|
||||||
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
|
||||||
// Continue the state machine
|
// Continue the state machine
|
||||||
handleUsbDeviceState(device);
|
handleUsbDeviceState(device);
|
||||||
}
|
}
|
||||||
// Subsequent permission dialog completion intent
|
// Subsequent permission dialog completion intent
|
||||||
else if (action.equals(ACTION_USB_PERMISSION)) {
|
else if (action.equals(ACTION_USB_PERMISSION)) {
|
||||||
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
|
||||||
// If we got this far, we've already found we're able to handle this device
|
// If we got this far, we've already found we're able to handle this device
|
||||||
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
|
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
|
||||||
|
|||||||
@@ -10,56 +10,38 @@ import com.limelight.nvstream.input.ControllerPacket;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
public class Xbox360Controller extends AbstractXboxController {
|
public class Xbox360Controller extends AbstractXboxController {
|
||||||
|
private static final int XB360_IFACE_SUBCLASS = 93;
|
||||||
|
private static final int XB360_IFACE_PROTOCOL = 1; // Wired only
|
||||||
|
|
||||||
// This list is taken from the Xpad driver in the Linux kernel.
|
private static final int[] SUPPORTED_VENDORS = {
|
||||||
// I've excluded the devices that aren't "controllers" in the traditional sense, but
|
0x044f, // Thrustmaster
|
||||||
// if people really want to use their dancepads or fight sticks with Moonlight, I can
|
0x045e, // Microsoft
|
||||||
// put them in.
|
0x046d, // Logitech
|
||||||
private static final DeviceIdTuple[] supportedDeviceTuples = {
|
0x0738, // Mad Catz
|
||||||
new DeviceIdTuple(0x045e, 0x028e, "Microsoft X-Box 360 pad"),
|
0x0e6f, // Unknown
|
||||||
new DeviceIdTuple(0x044f, 0xb326, "Thrustmaster Gamepad GP XID"),
|
0x12ab, // Unknown
|
||||||
new DeviceIdTuple(0x046d, 0xc21d, "Logitech Gamepad F310"),
|
0x1430, // RedOctane
|
||||||
new DeviceIdTuple(0x046d, 0xc21e, "Logitech Gamepad F510"),
|
0x146b, // BigBen
|
||||||
new DeviceIdTuple(0x046d, 0xc21f, "Logitech Gamepad F710"),
|
0x1bad, // Harmonix
|
||||||
new DeviceIdTuple(0x046d, 0xc242, "Logitech Chillstream Controller"),
|
0x0f0d, // Hori
|
||||||
new DeviceIdTuple(0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller"),
|
0x1689, // Razer Onza
|
||||||
new DeviceIdTuple(0x0738, 0x4726, "Mad Catz Xbox 360 Controller"),
|
0x24c6, // PowerA
|
||||||
new DeviceIdTuple(0x0738, 0xb726, "Mad Catz Xbox controller - MW2"),
|
0x1532, // Razer Sabertooth
|
||||||
new DeviceIdTuple(0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad"),
|
0x15e4, // Numark
|
||||||
new DeviceIdTuple(0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360"),
|
0x162e, // Joytech
|
||||||
new DeviceIdTuple(0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360"),
|
|
||||||
new DeviceIdTuple(0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360"),
|
|
||||||
new DeviceIdTuple(0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller"),
|
|
||||||
new DeviceIdTuple(0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360"),
|
|
||||||
new DeviceIdTuple(0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360"),
|
|
||||||
new DeviceIdTuple(0x0e6f, 0x0301, "Logic3 Controller"),
|
|
||||||
new DeviceIdTuple(0x0e6f, 0x0401, "Logic3 Controller"),
|
|
||||||
new DeviceIdTuple(0x12ab, 0x0301, "PDP AFTERGLOW AX.1"),
|
|
||||||
new DeviceIdTuple(0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller"),
|
|
||||||
new DeviceIdTuple(0x1532, 0x0037, "Razer Sabertooth"),
|
|
||||||
new DeviceIdTuple(0x15e4, 0x3f00, "Power A Mini Pro Elite"),
|
|
||||||
new DeviceIdTuple(0x15e4, 0x3f0a, "Xbox Airflo wired controller"),
|
|
||||||
new DeviceIdTuple(0x15e4, 0x3f10, "Batarang Xbox 360 controller"),
|
|
||||||
new DeviceIdTuple(0x162e, 0xbeef, "Joytech Neo-Se Take2"),
|
|
||||||
new DeviceIdTuple(0x1689, 0xfd00, "Razer Onza Tournament Edition"),
|
|
||||||
new DeviceIdTuple(0x1689, 0xfd01, "Razer Onza Classic Edition"),
|
|
||||||
new DeviceIdTuple(0x24c6, 0x5d04, "Razer Sabertooth"),
|
|
||||||
new DeviceIdTuple(0x1bad, 0xf016, "Mad Catz Xbox 360 Controller"),
|
|
||||||
new DeviceIdTuple(0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)"),
|
|
||||||
new DeviceIdTuple(0x1bad, 0xf900, "Harmonix Xbox 360 Controller"),
|
|
||||||
new DeviceIdTuple(0x1bad, 0xf901, "Gamestop Xbox 360 Controller"),
|
|
||||||
new DeviceIdTuple(0x1bad, 0xf903, "Tron Xbox 360 controller"),
|
|
||||||
new DeviceIdTuple(0x24c6, 0x5300, "PowerA MINI PROEX Controller"),
|
|
||||||
new DeviceIdTuple(0x24c6, 0x5303, "Xbox Airflo wired controller"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static boolean canClaimDevice(UsbDevice device) {
|
public static boolean canClaimDevice(UsbDevice device) {
|
||||||
for (DeviceIdTuple tuple : supportedDeviceTuples) {
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
if (device.getVendorId() == tuple.vid && device.getProductId() == tuple.pid) {
|
if (device.getVendorId() == supportedVid &&
|
||||||
LimeLog.info("XB360 can claim device: " + tuple.name);
|
device.getInterfaceCount() >= 1 &&
|
||||||
|
device.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
|
device.getInterface(0).getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
|
||||||
|
device.getInterface(0).getInterfaceProtocol() == XB360_IFACE_PROTOCOL) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,16 +133,4 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DeviceIdTuple {
|
|
||||||
public final int vid;
|
|
||||||
public final int pid;
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
public DeviceIdTuple(int vid, int pid, String name) {
|
|
||||||
this.vid = vid;
|
|
||||||
this.pid = pid;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,22 +3,25 @@ package com.limelight.binding.input.driver;
|
|||||||
import android.hardware.usb.UsbConstants;
|
import android.hardware.usb.UsbConstants;
|
||||||
import android.hardware.usb.UsbDevice;
|
import android.hardware.usb.UsbDevice;
|
||||||
import android.hardware.usb.UsbDeviceConnection;
|
import android.hardware.usb.UsbDeviceConnection;
|
||||||
import android.hardware.usb.UsbEndpoint;
|
|
||||||
import android.hardware.usb.UsbInterface;
|
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
import com.limelight.binding.video.MediaCodecHelper;
|
|
||||||
import com.limelight.nvstream.input.ControllerPacket;
|
import com.limelight.nvstream.input.ControllerPacket;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
|
||||||
|
|
||||||
public class XboxOneController extends AbstractXboxController {
|
public class XboxOneController extends AbstractXboxController {
|
||||||
|
|
||||||
private static final int MICROSOFT_VID = 0x045e;
|
|
||||||
private static final int XB1_IFACE_SUBCLASS = 71;
|
private static final int XB1_IFACE_SUBCLASS = 71;
|
||||||
private static final int XB1_IFACE_PROTOCOL = 208;
|
private static final int XB1_IFACE_PROTOCOL = 208;
|
||||||
|
|
||||||
|
private static final int[] SUPPORTED_VENDORS = {
|
||||||
|
0x045e, // Microsoft
|
||||||
|
0x0738, // Mad Catz
|
||||||
|
0x0e6f, // Unknown
|
||||||
|
0x0f0d, // Hori
|
||||||
|
0x24c6, // PowerA
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME: odata_serial
|
// FIXME: odata_serial
|
||||||
private static final byte[] XB1_INIT_DATA = {0x05, 0x20, 0x00, 0x01, 0x00};
|
private static final byte[] XB1_INIT_DATA = {0x05, 0x20, 0x00, 0x01, 0x00};
|
||||||
|
|
||||||
@@ -78,11 +81,17 @@ public class XboxOneController extends AbstractXboxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean canClaimDevice(UsbDevice device) {
|
public static boolean canClaimDevice(UsbDevice device) {
|
||||||
return device.getVendorId() == MICROSOFT_VID &&
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
device.getInterfaceCount() >= 1 &&
|
if (device.getVendorId() == supportedVid &&
|
||||||
device.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
device.getInterfaceCount() >= 1 &&
|
||||||
device.getInterface(0).getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
device.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
device.getInterface(0).getInterfaceProtocol() == XB1_IFACE_PROTOCOL;
|
device.getInterface(0).getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
||||||
|
device.getInterface(0).getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+45
-21
@@ -4,6 +4,8 @@ import android.app.Activity;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
|
import com.limelight.LimelightBuildProps;
|
||||||
|
import com.limelight.binding.input.capture.InputCaptureProvider;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -13,7 +15,7 @@ import java.io.OutputStream;
|
|||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
public class EvdevHandler {
|
public class EvdevCaptureProvider extends InputCaptureProvider {
|
||||||
|
|
||||||
private final EvdevListener listener;
|
private final EvdevListener listener;
|
||||||
private final String libraryPath;
|
private final String libraryPath;
|
||||||
@@ -25,6 +27,7 @@ public class EvdevHandler {
|
|||||||
private ServerSocket servSock;
|
private ServerSocket servSock;
|
||||||
private Socket evdevSock;
|
private Socket evdevSock;
|
||||||
private Activity activity;
|
private Activity activity;
|
||||||
|
private boolean started = false;
|
||||||
|
|
||||||
private static final byte UNGRAB_REQUEST = 1;
|
private static final byte UNGRAB_REQUEST = 1;
|
||||||
private static final byte REGRAB_REQUEST = 2;
|
private static final byte REGRAB_REQUEST = 2;
|
||||||
@@ -51,12 +54,7 @@ public class EvdevHandler {
|
|||||||
try {
|
try {
|
||||||
su = builder.start();
|
su = builder.start();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
activity.runOnUiThread(new Runnable() {
|
reportDeviceNotRooted();
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(activity, "This device is not rooted - Mouse capture is unavailable", Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -66,6 +64,7 @@ public class EvdevHandler {
|
|||||||
try {
|
try {
|
||||||
suOut.writeChars(libraryPath+File.separatorChar+"libevdev_reader.so "+servSock.getLocalPort()+"\n");
|
suOut.writeChars(libraryPath+File.separatorChar+"libevdev_reader.so "+servSock.getLocalPort()+"\n");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
reportDeviceNotRooted();
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -163,24 +162,48 @@ public class EvdevHandler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public EvdevHandler(Activity activity, EvdevListener listener) {
|
public EvdevCaptureProvider(Activity activity, EvdevListener listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.libraryPath = activity.getApplicationInfo().nativeLibraryDir;
|
this.libraryPath = activity.getApplicationInfo().nativeLibraryDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void regrabAll() {
|
public static boolean isCaptureProviderSupported() {
|
||||||
if (!shutdown && evdevOut != null) {
|
return LimelightBuildProps.ROOT_BUILD;
|
||||||
try {
|
}
|
||||||
evdevOut.write(REGRAB_REQUEST);
|
|
||||||
} catch (IOException e) {
|
private void reportDeviceNotRooted() {
|
||||||
e.printStackTrace();
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(activity, "This device is not rooted - Mouse capture is unavailable", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enableCapture() {
|
||||||
|
if (!started) {
|
||||||
|
// Start the handler thread if it's our first time
|
||||||
|
// capturing
|
||||||
|
handlerThread.start();
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Send a request to regrab if we're already capturing
|
||||||
|
if (!shutdown && evdevOut != null) {
|
||||||
|
try {
|
||||||
|
evdevOut.write(REGRAB_REQUEST);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ungrabAll() {
|
@Override
|
||||||
if (!shutdown && evdevOut != null) {
|
public void disableCapture() {
|
||||||
|
if (started && !shutdown && evdevOut != null) {
|
||||||
try {
|
try {
|
||||||
evdevOut.write(UNGRAB_REQUEST);
|
evdevOut.write(UNGRAB_REQUEST);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -189,15 +212,16 @@ public class EvdevHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
@Override
|
||||||
handlerThread.start();
|
public void destroy() {
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
|
||||||
// We need to stop the process in this context otherwise
|
// We need to stop the process in this context otherwise
|
||||||
// we could get stuck waiting on output from the process
|
// we could get stuck waiting on output from the process
|
||||||
// in order to terminate it.
|
// in order to terminate it.
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
shutdown = true;
|
shutdown = true;
|
||||||
handlerThread.interrupt();
|
handlerThread.interrupt();
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ public class DigitalButton extends VirtualControllerElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DigitalButtonListener> listeners = new ArrayList<DigitalButtonListener>();
|
private List<DigitalButtonListener> listeners = new ArrayList<>();
|
||||||
private String text = "";
|
private String text = "";
|
||||||
private int icon = -1;
|
private int icon = -1;
|
||||||
private long timerLongClickTimeout = 3000;
|
private long timerLongClickTimeout = 3000;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class DigitalPad extends VirtualControllerElement {
|
|||||||
public final static int DIGITAL_PAD_DIRECTION_UP = 2;
|
public final static int DIGITAL_PAD_DIRECTION_UP = 2;
|
||||||
public final static int DIGITAL_PAD_DIRECTION_RIGHT = 4;
|
public final static int DIGITAL_PAD_DIRECTION_RIGHT = 4;
|
||||||
public final static int DIGITAL_PAD_DIRECTION_DOWN = 8;
|
public final static int DIGITAL_PAD_DIRECTION_DOWN = 8;
|
||||||
List<DigitalPadListener> listeners = new ArrayList<DigitalPadListener>();
|
List<DigitalPadListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
private static final int DPAD_MARGIN = 5;
|
private static final int DPAD_MARGIN = 5;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ public class VirtualController {
|
|||||||
private RelativeLayout.LayoutParams layoutParamsButtonConfigure = null;
|
private RelativeLayout.LayoutParams layoutParamsButtonConfigure = null;
|
||||||
private Button buttonConfigure = null;
|
private Button buttonConfigure = null;
|
||||||
|
|
||||||
private List<VirtualControllerElement> elements = new ArrayList<VirtualControllerElement>();
|
private List<VirtualControllerElement> elements = new ArrayList<>();
|
||||||
|
|
||||||
public VirtualController(final NvConnection conn, FrameLayout layout, final Context context) {
|
public VirtualController(final NvConnection conn, FrameLayout layout, final Context context) {
|
||||||
this.connection = conn;
|
this.connection = conn;
|
||||||
|
|||||||
@@ -475,8 +475,8 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
} catch (InterruptedException ignored) { }
|
} catch (InterruptedException ignored) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the decoder
|
// We could stop the decoder here, but it seems to cause some problems
|
||||||
videoDecoder.stop();
|
// so we'll just let release take care of it.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class MediaCodecHelper {
|
|||||||
private static final List<String> whitelistedHevcDecoders;
|
private static final List<String> whitelistedHevcDecoders;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
directSubmitPrefixes = new LinkedList<String>();
|
directSubmitPrefixes = new LinkedList<>();
|
||||||
|
|
||||||
// These decoders have low enough input buffer latency that they
|
// These decoders have low enough input buffer latency that they
|
||||||
// can be directly invoked from the receive thread
|
// can be directly invoked from the receive thread
|
||||||
@@ -48,11 +48,11 @@ public class MediaCodecHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
preferredDecoders = new LinkedList<String>();
|
preferredDecoders = new LinkedList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
blacklistedDecoderPrefixes = new LinkedList<String>();
|
blacklistedDecoderPrefixes = new LinkedList<>();
|
||||||
|
|
||||||
// Software decoders that don't support H264 high profile
|
// Software decoders that don't support H264 high profile
|
||||||
blacklistedDecoderPrefixes.add("omx.google");
|
blacklistedDecoderPrefixes.add("omx.google");
|
||||||
@@ -65,21 +65,21 @@ public class MediaCodecHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<String>();
|
spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<>();
|
||||||
spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
|
spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
|
||||||
spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
|
spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
|
||||||
spsFixupBitstreamFixupDecoderPrefixes.add("omx.brcm");
|
spsFixupBitstreamFixupDecoderPrefixes.add("omx.brcm");
|
||||||
|
|
||||||
baselineProfileHackPrefixes = new LinkedList<String>();
|
baselineProfileHackPrefixes = new LinkedList<>();
|
||||||
baselineProfileHackPrefixes.add("omx.intel");
|
baselineProfileHackPrefixes.add("omx.intel");
|
||||||
|
|
||||||
whitelistedAdaptiveResolutionPrefixes = new LinkedList<String>();
|
whitelistedAdaptiveResolutionPrefixes = new LinkedList<>();
|
||||||
whitelistedAdaptiveResolutionPrefixes.add("omx.nvidia");
|
whitelistedAdaptiveResolutionPrefixes.add("omx.nvidia");
|
||||||
whitelistedAdaptiveResolutionPrefixes.add("omx.qcom");
|
whitelistedAdaptiveResolutionPrefixes.add("omx.qcom");
|
||||||
whitelistedAdaptiveResolutionPrefixes.add("omx.sec");
|
whitelistedAdaptiveResolutionPrefixes.add("omx.sec");
|
||||||
whitelistedAdaptiveResolutionPrefixes.add("omx.TI");
|
whitelistedAdaptiveResolutionPrefixes.add("omx.TI");
|
||||||
|
|
||||||
constrainedHighProfilePrefixes = new LinkedList<String>();
|
constrainedHighProfilePrefixes = new LinkedList<>();
|
||||||
constrainedHighProfilePrefixes.add("omx.intel");
|
constrainedHighProfilePrefixes.add("omx.intel");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ public class MediaCodecHelper {
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
private static LinkedList<MediaCodecInfo> getMediaCodecList() {
|
private static LinkedList<MediaCodecInfo> getMediaCodecList() {
|
||||||
LinkedList<MediaCodecInfo> infoList = new LinkedList<MediaCodecInfo>();
|
LinkedList<MediaCodecInfo> infoList = new LinkedList<>();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
||||||
@@ -376,7 +376,7 @@ public class MediaCodecHelper {
|
|||||||
break;
|
break;
|
||||||
cpuInfo.append((char)ch);
|
cpuInfo.append((char)ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cpuInfo.toString();
|
return cpuInfo.toString();
|
||||||
} finally {
|
} finally {
|
||||||
br.close();
|
br.close();
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class ComputerDatabaseManager {
|
|||||||
|
|
||||||
public List<ComputerDetails> getAllComputers() {
|
public List<ComputerDetails> getAllComputers() {
|
||||||
Cursor c = computerDb.rawQuery("SELECT * FROM "+COMPUTER_TABLE_NAME, null);
|
Cursor c = computerDb.rawQuery("SELECT * FROM "+COMPUTER_TABLE_NAME, null);
|
||||||
LinkedList<ComputerDetails> computerList = new LinkedList<ComputerDetails>();
|
LinkedList<ComputerDetails> computerList = new LinkedList<>();
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
ComputerDetails details = new ComputerDetails();
|
ComputerDetails details = new ComputerDetails();
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class ComputerManagerService extends Service {
|
|||||||
private final AtomicInteger dbRefCount = new AtomicInteger(0);
|
private final AtomicInteger dbRefCount = new AtomicInteger(0);
|
||||||
|
|
||||||
private IdentityManager idManager;
|
private IdentityManager idManager;
|
||||||
private final LinkedList<PollingTuple> pollingTuples = new LinkedList<PollingTuple>();
|
private final LinkedList<PollingTuple> pollingTuples = new LinkedList<>();
|
||||||
private ComputerManagerListener listener = null;
|
private ComputerManagerListener listener = null;
|
||||||
private final AtomicInteger activePolls = new AtomicInteger(0);
|
private final AtomicInteger activePolls = new AtomicInteger(0);
|
||||||
private boolean pollingActive = false;
|
private boolean pollingActive = false;
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import com.limelight.grid.assets.MemoryAssetLoader;
|
|||||||
import com.limelight.grid.assets.NetworkAssetLoader;
|
import com.limelight.grid.assets.NetworkAssetLoader;
|
||||||
import com.limelight.nvstream.http.ComputerDetails;
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public abstract class GenericGridAdapter<T> extends BaseAdapter {
|
|||||||
protected final Context context;
|
protected final Context context;
|
||||||
protected final int defaultImageRes;
|
protected final int defaultImageRes;
|
||||||
protected final int layoutId;
|
protected final int layoutId;
|
||||||
protected final ArrayList<T> itemList = new ArrayList<T>();
|
protected final ArrayList<T> itemList = new ArrayList<>();
|
||||||
protected final LayoutInflater inflater;
|
protected final LayoutInflater inflater;
|
||||||
|
|
||||||
public GenericGridAdapter(Context context, int layoutId, int defaultImageRes) {
|
public GenericGridAdapter(Context context, int layoutId, int defaultImageRes) {
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ public class CachedAppAssetLoader {
|
|||||||
private LoaderTuple tuple;
|
private LoaderTuple tuple;
|
||||||
|
|
||||||
public LoaderTask(ImageView imageView, boolean diskOnly) {
|
public LoaderTask(ImageView imageView, boolean diskOnly) {
|
||||||
this.imageViewRef = new WeakReference<ImageView>(imageView);
|
this.imageViewRef = new WeakReference<>(imageView);
|
||||||
this.diskOnly = diskOnly;
|
this.diskOnly = diskOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ public class CachedAppAssetLoader {
|
|||||||
public AsyncDrawable(Resources res, Bitmap bitmap,
|
public AsyncDrawable(Resources res, Bitmap bitmap,
|
||||||
LoaderTask loaderTask) {
|
LoaderTask loaderTask) {
|
||||||
super(res, bitmap);
|
super(res, bitmap);
|
||||||
loaderTaskReference = new WeakReference<LoaderTask>(loaderTask);
|
loaderTaskReference = new WeakReference<>(loaderTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoaderTask getLoaderTask() {
|
public LoaderTask getLoaderTask() {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import com.limelight.LimeLog;
|
|||||||
import com.limelight.utils.CacheHelper;
|
import com.limelight.utils.CacheHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import android.widget.Toast;
|
|||||||
public class AddComputerManually extends Activity {
|
public class AddComputerManually extends Activity {
|
||||||
private TextView hostText;
|
private TextView hostText;
|
||||||
private ComputerManagerService.ComputerManagerBinder managerBinder;
|
private ComputerManagerService.ComputerManagerBinder managerBinder;
|
||||||
private final LinkedBlockingQueue<String> computersToAdd = new LinkedBlockingQueue<String>();
|
private final LinkedBlockingQueue<String> computersToAdd = new LinkedBlockingQueue<>();
|
||||||
private Thread addThread;
|
private Thread addThread;
|
||||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
public void onServiceConnected(ComponentName className, final IBinder binder) {
|
public void onServiceConnected(ComponentName className, final IBinder binder) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.limelight.preferences;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ public class PreferenceConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use small mode on anything smaller than a 7" tablet
|
// Use small mode on anything smaller than a 7" tablet
|
||||||
return context.getResources().getConfiguration().screenWidthDp < 600;
|
return context.getResources().getConfiguration().smallestScreenWidthDp < 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getDefaultBitrate(Context context) {
|
public static int getDefaultBitrate(Context context) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.limelight.preferences;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class Dialog implements Runnable {
|
|||||||
|
|
||||||
private AlertDialog alert;
|
private AlertDialog alert;
|
||||||
|
|
||||||
private static final ArrayList<Dialog> rundownDialogs = new ArrayList<Dialog>();
|
private static final ArrayList<Dialog> rundownDialogs = new ArrayList<>();
|
||||||
|
|
||||||
private Dialog(Activity activity, String title, String message, boolean endAfterDismiss)
|
private Dialog(Activity activity, String title, String message, boolean endAfterDismiss)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class SpinnerDialog implements Runnable,OnCancelListener {
|
|||||||
private ProgressDialog progress;
|
private ProgressDialog progress;
|
||||||
private final boolean finish;
|
private final boolean finish;
|
||||||
|
|
||||||
private static final ArrayList<SpinnerDialog> rundownDialogs = new ArrayList<SpinnerDialog>();
|
private static final ArrayList<SpinnerDialog> rundownDialogs = new ArrayList<>();
|
||||||
|
|
||||||
private SpinnerDialog(Activity activity, String title, String message, boolean finish)
|
private SpinnerDialog(Activity activity, String title, String message, boolean finish)
|
||||||
{
|
{
|
||||||
|
|||||||
Submodule app/src/main/jni/jnienet/enet updated: 8b24595dbd...7546b505c1.
@@ -40,8 +40,8 @@
|
|||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="video_format_names">
|
<string-array name="video_format_names">
|
||||||
<item>Use H.265 only if safe</item>
|
<item>Use H.265 only if stable</item>
|
||||||
<item>Always use H.265 if available</item>
|
<item>Always use H.265 (may crash)</item>
|
||||||
<item>Never use H.265</item>
|
<item>Never use H.265</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="video_format_values" translatable="false">
|
<string-array name="video_format_values" translatable="false">
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.1.0'
|
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Settings specified in this file will override any Gradle settings
|
||||||
|
# configured through the IDE.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||||
|
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
org.gradle.jvmargs=-Xmx3072m
|
||||||
|
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
Reference in new issue
Block a user