Compare commits

...

21 Commits

Author SHA1 Message Date
Cameron Gutman 8853bf0670 Bump version to 4.5.8 2016-05-07 21:25:11 -04:00
Cameron Gutman 71fa3a824b Update gradle 2016-05-07 21:20:05 -04:00
Cameron Gutman 56fd50834c Update common jar with the RTP queue changes 2016-05-07 21:19:54 -04:00
Cameron Gutman 48ba812cf6 When combining analog inputs, use the one with the highest magnitude 2016-05-07 21:19:02 -04:00
Cameron Gutman 019dc6d45f Display a warning at stream start if root access is unavailable 2016-05-07 20:59:17 -04:00
Cameron Gutman cbcb784a79 Blacklist Tegra X1's HEVC decoder until the correct SPS fixups are in place 2016-05-07 20:53:45 -04:00
Cameron Gutman 39fa0258ad Force the Archos Gamepad 2's controller buttons as controller 0 2016-04-23 22:23:43 -04:00
Cameron Gutman d0dd5bfa8c Combine all controllers with the same controller number before sending controller input 2016-04-23 22:23:01 -04:00
Cameron Gutman b948c47618 Increment patch level again 2016-04-22 00:15:21 -04:00
Cameron Gutman 18cae8ac53 Use common jar from the android branch (da297b5a89c2b645573f231af3e47752f27fbc79) to fix API 19 issues 2016-04-21 13:33:08 -04:00
Cameron Gutman 0576231dfc Update patch level to 4.5.7.1 2016-04-20 13:52:53 -04:00
Cameron Gutman 6ad35a83dd Update common jar with fix for < API 19 2016-04-20 13:42:57 -04:00
Cameron Gutman 33d4dfc745 Revert "Prevent the small-mode default from changing between portrait and landscape orientations"
This reverts commit 7c1eb80d62.
2016-04-20 13:34:24 -04:00
Cameron Gutman f3bf63a668 Increment app version 2016-04-19 20:49:40 -04:00
Cameron Gutman 2dbb7395a4 Restart the app view activity when configuration changes are made that could require the grid to be resized. This is much simpler than handling all of the fine edge cases here. 2016-04-19 20:38:05 -04:00
Cameron Gutman 7c1eb80d62 Prevent the small-mode default from changing between portrait and landscape orientations 2016-04-19 20:36:04 -04:00
Cameron Gutman f2bf093691 Update Gradle 2016-04-19 19:51:00 -04:00
Cameron Gutman 2f002bfa4a Fix being stuck in small-icon mode after resizing to minimum size on Android N 2016-04-19 19:36:10 -04:00
Cameron Gutman 4a19038d54 Update common jar to fix crashes in jnienet 2016-04-19 19:18:13 -04:00
Cameron Gutman 15fb3dd92c Fix mouse scaling to scale by stream view size rather than screen size for better behavior on N and in general 2016-04-19 19:13:57 -04:00
Cameron Gutman e0982d3961 Fix video stream aspect ratio scaling in multi-window mode on Android N 2016-04-19 18:40:45 -04:00
13 changed files with 211 additions and 69 deletions
+3 -3
View File
@@ -5,14 +5,14 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
buildToolsVersion "23.0.3"
defaultConfig {
minSdkVersion 16
targetSdkVersion 23
versionName "4.5.6"
versionCode = 95
versionName "4.5.8"
versionCode = 99
}
productFlavors {
Binary file not shown.
+1 -1
View File
@@ -37,7 +37,7 @@
</activity>
<activity
android:name=".AppView"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection" >
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|fontScale|uiMode" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.limelight.PcView" />
+19 -3
View File
@@ -96,9 +96,25 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
// Load the app grid with cached data (if possible)
populateAppGridWithCache();
getFragmentManager().beginTransaction()
.replace(R.id.appFragmentContainer, new AdapterFragment())
.commitAllowingStateLoss();
runOnUiThread(new Runnable() {
@Override
public void run() {
if (isFinishing() || isChangingConfigurations()) {
return;
}
// Despite my best efforts to catch all conditions that could
// cause the activity to be destroyed when we try to commit
// I haven't been able to, so we have this try-catch block.
try {
getFragmentManager().beginTransaction()
.replace(R.id.appFragmentContainer, new AdapterFragment())
.commitAllowingStateLoss();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
});
}
}.start();
}
+18 -43
View File
@@ -22,6 +22,7 @@ import com.limelight.nvstream.input.KeyboardPacket;
import com.limelight.nvstream.input.MouseButtonPacket;
import com.limelight.preferences.PreferenceConfiguration;
import com.limelight.ui.GameGestures;
import com.limelight.ui.StreamView;
import com.limelight.utils.Dialog;
import com.limelight.utils.SpinnerDialog;
@@ -74,8 +75,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private final TouchContext[] touchContextMap = new TouchContext[2];
private long threeFingerDownTime = 0;
private static final double REFERENCE_HORIZ_RES = 1280;
private static final double REFERENCE_VERT_RES = 720;
private static final int REFERENCE_HORIZ_RES = 1280;
private static final int REFERENCE_VERT_RES = 720;
private static final int THREE_FINGER_TAP_THRESHOLD = 300;
@@ -84,19 +85,18 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private KeyboardTranslator keybTranslator;
private PreferenceConfiguration prefConfig;
private final Point screenSize = new Point(0, 0);
private NvConnection conn;
private SpinnerDialog spinner;
private boolean displayedFailureDialog = false;
private boolean connecting = false;
private boolean connected = false;
private boolean deferredSurfaceResize = false;
private EvdevHandler evdevHandler;
private int modifierFlags = 0;
private boolean grabbedInput = true;
private boolean grabComboDown = false;
private StreamView streamView;
private EnhancedDecoderRenderer decoderRenderer;
@@ -175,13 +175,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
drFlags |= VideoDecoderRenderer.FLAG_FILL_SCREEN;
}
Display display = getWindowManager().getDefaultDisplay();
display.getSize(screenSize);
// Listen for events on the game surface
SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceView);
sv.setOnGenericMotionListener(this);
sv.setOnTouchListener(this);
streamView = (StreamView) findViewById(R.id.surfaceView);
streamView.setOnGenericMotionListener(this);
streamView.setOnTouchListener(this);
// Warn the user if they're on a metered connection
checkDataConnection();
@@ -258,6 +255,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// >= 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) {
@@ -266,20 +267,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
}
SurfaceHolder sh = sv.getHolder();
SurfaceHolder sh = streamView.getHolder();
if (prefConfig.stretchVideo || aspectRatioMatch) {
// Set the surface to the size of the video
sh.setFixedSize(prefConfig.width, prefConfig.height);
}
else {
deferredSurfaceResize = true;
// Set the surface to scale based on the aspect ratio of the stream
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
}
// Initialize touch contexts
for (int i = 0; i < touchContextMap.length; i++) {
touchContextMap[i] = new TouchContext(conn, i,
(REFERENCE_HORIZ_RES / (double)screenSize.x),
(REFERENCE_VERT_RES / (double)screenSize.y));
REFERENCE_HORIZ_RES, REFERENCE_VERT_RES,
streamView);
}
if (LimelightBuildProps.ROOT_BUILD) {
@@ -306,26 +308,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
sh.addCallback(this);
}
private void resizeSurfaceWithAspectRatio(SurfaceView sv, double vidWidth, double vidHeight)
{
// Get the visible width of the activity
double visibleWidth = getWindow().getDecorView().getWidth();
ViewGroup.LayoutParams lp = sv.getLayoutParams();
// Calculate the new size of the SurfaceView
lp.width = (int) visibleWidth;
lp.height = (int) ((vidHeight / vidWidth) * visibleWidth);
// Apply the size change
sv.setLayoutParams(lp);
// refresh virtual controller layout
if (virtualController != null) {
virtualController.refreshLayout();
}
}
private void checkDataConnection()
{
ConnectivityManager mgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -802,8 +784,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Scale the deltas if the device resolution is different
// than the stream resolution
deltaX = (int)Math.round((double)deltaX * (REFERENCE_HORIZ_RES / (double)screenSize.x));
deltaY = (int)Math.round((double)deltaY * (REFERENCE_VERT_RES / (double)screenSize.y));
deltaX = (int)Math.round((double)deltaX * (REFERENCE_HORIZ_RES / (double)streamView.getWidth()));
deltaY = (int)Math.round((double)deltaY * (REFERENCE_VERT_RES / (double)streamView.getHeight()));
conn.sendMouseMove((short)deltaX, (short)deltaY);
}
@@ -932,13 +914,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if (!connected && !connecting) {
connecting = true;
// Resize the surface to match the aspect ratio of the video
// This must be done after the surface is created.
if (deferredSurfaceResize) {
resizeSurfaceWithAspectRatio((SurfaceView) findViewById(R.id.surfaceView),
prefConfig.width, prefConfig.height);
}
conn.start(PlatformBinding.getDeviceName(), holder, drFlags,
PlatformBinding.getAudioRenderer(), decoderRenderer);
}
@@ -82,6 +82,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
defaultContext.leftTriggerAxis = MotionEvent.AXIS_BRAKE;
defaultContext.rightTriggerAxis = MotionEvent.AXIS_GAS;
defaultContext.controllerNumber = (short) 0;
defaultContext.assignedControllerNumber = true;
}
private static InputDevice.MotionRange getMotionRangeForJoystickAxis(InputDevice dev, int axis) {
@@ -146,8 +147,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
InputDeviceContext devContext = (InputDeviceContext) context;
LimeLog.info(devContext.name+" ("+context.id+") needs a controller number assigned");
if (devContext.name != null && devContext.name.contains("gpio-keys")) {
// This is the back button on Shield portable consoles
if (devContext.name != null &&
(devContext.name.contains("gpio-keys") || // This is the back button on Shield portable consoles
devContext.name.contains("joy_key"))) { // These are the gamepad buttons on the Archos Gamepad 2
LimeLog.info("Built-in buttons hardcoded as controller 0");
context.controllerNumber = 0;
}
@@ -398,12 +400,82 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return context;
}
private void sendControllerInputPacket(GenericControllerContext context) {
assignControllerNumberIfNeeded(context);
conn.sendControllerInput(context.controllerNumber, context.inputMap,
context.leftTrigger, context.rightTrigger,
context.leftStickX, context.leftStickY,
context.rightStickX, context.rightStickY);
private byte maxByMagnitude(byte a, byte b) {
int absA = Math.abs(a);
int absB = Math.abs(b);
if (absA > absB) {
return a;
}
else {
return b;
}
}
private short maxByMagnitude(short a, short b) {
int absA = Math.abs(a);
int absB = Math.abs(b);
if (absA > absB) {
return a;
}
else {
return b;
}
}
private void sendControllerInputPacket(GenericControllerContext originalContext) {
assignControllerNumberIfNeeded(originalContext);
// Take the context's controller number and fuse all inputs with the same number
short controllerNumber = originalContext.controllerNumber;
short inputMap = 0;
byte leftTrigger = 0;
byte rightTrigger = 0;
short leftStickX = 0;
short leftStickY = 0;
short rightStickX = 0;
short rightStickY = 0;
// In order to properly handle controllers that are split into multiple devices,
// we must aggregate all controllers with the same controller number into a single
// device before we send it.
for (int i = 0; i < inputDeviceContexts.size(); i++) {
GenericControllerContext context = inputDeviceContexts.valueAt(i);
if (context.assignedControllerNumber && context.controllerNumber == controllerNumber) {
inputMap |= context.inputMap;
leftTrigger |= maxByMagnitude(leftTrigger, context.leftTrigger);
rightTrigger |= maxByMagnitude(rightTrigger, context.rightTrigger);
leftStickX |= maxByMagnitude(leftStickX, context.leftStickX);
leftStickY |= maxByMagnitude(leftStickY, context.leftStickY);
rightStickX |= maxByMagnitude(rightStickX, context.rightStickX);
rightStickY |= maxByMagnitude(rightStickY, context.rightStickY);
}
}
for (int i = 0; i < usbDeviceContexts.size(); i++) {
GenericControllerContext context = usbDeviceContexts.valueAt(i);
if (context.assignedControllerNumber && context.controllerNumber == controllerNumber) {
inputMap |= context.inputMap;
leftTrigger |= maxByMagnitude(leftTrigger, context.leftTrigger);
rightTrigger |= maxByMagnitude(rightTrigger, context.rightTrigger);
leftStickX |= maxByMagnitude(leftStickX, context.leftStickX);
leftStickY |= maxByMagnitude(leftStickY, context.leftStickY);
rightStickX |= maxByMagnitude(rightStickX, context.rightStickX);
rightStickY |= maxByMagnitude(rightStickY, context.rightStickY);
}
}
if (defaultContext.controllerNumber == controllerNumber) {
inputMap |= defaultContext.inputMap;
leftTrigger |= maxByMagnitude(leftTrigger, defaultContext.leftTrigger);
rightTrigger |= maxByMagnitude(rightTrigger, defaultContext.rightTrigger);
leftStickX |= maxByMagnitude(leftStickX, defaultContext.leftStickX);
leftStickY |= maxByMagnitude(leftStickY, defaultContext.leftStickY);
rightStickX |= maxByMagnitude(rightStickX, defaultContext.rightStickX);
rightStickY |= maxByMagnitude(rightStickY, defaultContext.rightStickY);
}
conn.sendControllerInput(controllerNumber, inputMap,
leftTrigger, rightTrigger,
leftStickX, leftStickY,
rightStickX, rightStickY);
}
// Return a valid keycode, 0 to consume, or -1 to not consume the event
@@ -1,5 +1,7 @@
package com.limelight.binding.input;
import android.view.View;
import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.MouseButtonPacket;
@@ -17,23 +19,27 @@ public class TouchContext {
private boolean confirmedDrag;
private Timer dragTimer;
private double distanceMoved;
private double xFactor, yFactor;
private final NvConnection conn;
private final int actionIndex;
private final double xFactor;
private final double yFactor;
private final int referenceWidth;
private final int referenceHeight;
private final View targetView;
private static final int TAP_MOVEMENT_THRESHOLD = 20;
private static final int TAP_DISTANCE_THRESHOLD = 25;
private static final int TAP_TIME_THRESHOLD = 250;
private static final int DRAG_TIME_THRESHOLD = 650;
public TouchContext(NvConnection conn, int actionIndex, double xFactor, double yFactor)
public TouchContext(NvConnection conn, int actionIndex,
int referenceWidth, int referenceHeight, View view)
{
this.conn = conn;
this.actionIndex = actionIndex;
this.xFactor = xFactor;
this.yFactor = yFactor;
this.referenceWidth = referenceWidth;
this.referenceHeight = referenceHeight;
this.targetView = view;
}
public int getActionIndex()
@@ -68,6 +74,10 @@ public class TouchContext {
public boolean touchDownEvent(int eventX, int eventY)
{
// Get the view dimensions to scale inputs on this touch
xFactor = referenceWidth / (double)targetView.getWidth();
yFactor = referenceHeight / (double)targetView.getHeight();
originalTouchX = lastTouchX = eventX;
originalTouchY = lastTouchY = eventY;
originalTouchTime = System.currentTimeMillis();
@@ -1,6 +1,7 @@
package com.limelight.binding.input.evdev;
import android.content.Context;
import android.app.Activity;
import android.widget.Toast;
import com.limelight.LimeLog;
@@ -23,6 +24,7 @@ public class EvdevHandler {
private Process su;
private ServerSocket servSock;
private Socket evdevSock;
private Activity activity;
private static final byte UNGRAB_REQUEST = 1;
private static final byte REGRAB_REQUEST = 2;
@@ -49,6 +51,12 @@ public class EvdevHandler {
try {
su = builder.start();
} catch (IOException e) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "This device is not rooted - Mouse capture is unavailable", Toast.LENGTH_LONG).show();
}
});
e.printStackTrace();
return;
}
@@ -155,9 +163,10 @@ public class EvdevHandler {
}
};
public EvdevHandler(Context context, EvdevListener listener) {
public EvdevHandler(Activity activity, EvdevListener listener) {
this.listener = listener;
this.libraryPath = context.getApplicationInfo().nativeLibraryDir;
this.activity = activity;
this.libraryPath = activity.getApplicationInfo().nativeLibraryDir;
}
public void regrabAll() {
@@ -57,6 +57,11 @@ public class MediaCodecHelper {
// Software decoders that don't support H264 high profile
blacklistedDecoderPrefixes.add("omx.google");
blacklistedDecoderPrefixes.add("AVCDecoder");
// Without bitstream fixups, we perform horribly on NVIDIA's HEVC
// decoder. While not strictly necessary, I'm going to fully blacklist this
// one to avoid users getting inaccurate impressions of Tegra X1/Moonlight performance.
blacklistedDecoderPrefixes.add("OMX.Nvidia.h265.decode");
}
static {
@@ -100,7 +100,7 @@ public class PreferenceConfiguration {
}
// Use small mode on anything smaller than a 7" tablet
return context.getResources().getConfiguration().smallestScreenWidthDp < 600;
return context.getResources().getConfiguration().screenWidthDp < 600;
}
public static int getDefaultBitrate(Context context) {
@@ -0,0 +1,55 @@
package com.limelight.ui;
import android.annotation.TargetApi;
import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceView;
public class StreamView extends SurfaceView {
private double desiredAspectRatio;
public void setDesiredAspectRatio(double aspectRatio) {
this.desiredAspectRatio = aspectRatio;
}
public StreamView(Context context) {
super(context);
}
public StreamView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StreamView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(21)
public StreamView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// If no fixed aspect ratio has been provided, simply use the default onMeasure() behavior
if (desiredAspectRatio == 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
// Based on code from: https://www.buzzingandroid.com/2012/11/easy-measuring-of-custom-views-with-specific-aspect-ratio/
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int measuredHeight, measuredWidth;
if (widthSize > heightSize * desiredAspectRatio) {
measuredHeight = heightSize;
measuredWidth = (int)(measuredHeight * desiredAspectRatio);
} else {
measuredWidth = widthSize;
measuredHeight = (int)(measuredWidth / desiredAspectRatio);
}
setMeasuredDimension(measuredWidth, measuredHeight);
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
android:layout_height="match_parent"
tools:context=".Game" >
<SurfaceView
<com.limelight.ui.StreamView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
+1 -1
View File
@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0-alpha4'
classpath 'com.android.tools.build:gradle:2.1.0'
}
}