Compare commits

...

46 Commits

Author SHA1 Message Date
Cameron Gutman d726d939f4 Version 5.8.1-r3 2018-06-09 21:49:41 -07:00
Cameron Gutman 748085e7bb Update common to fix reconnection issue 2018-06-09 21:39:26 -07:00
Cameron Gutman d57d19174b Version 5.8.1 r2 2018-06-09 18:16:41 -07:00
Cameron Gutman efebe1828a Update common with Lint fixes 2018-06-09 18:14:39 -07:00
Cameron Gutman 06007e0597 Add a button for adding a PC manually 2018-06-09 18:14:09 -07:00
Cameron Gutman 3a868045d7 Allow the display to go off if the stream disconnects 2018-06-09 17:48:07 -07:00
Cameron Gutman e0a7ff1880 Remove in-progress toast for WOL 2018-06-09 17:23:59 -07:00
Cameron Gutman 88d43bbd40 Disable density splits until I can figure out why we're crashing 2018-06-08 01:13:00 -07:00
Cameron Gutman 30ff319b13 Version 5.8.1 2018-06-08 01:08:09 -07:00
Cameron Gutman 9a0f48b799 Add support for display cutouts on P 2018-06-08 01:05:32 -07:00
Cameron Gutman b52c8a1a8f Use ImageDecoder API on P and higher quality decodes on non-low ram devices 2018-06-08 00:46:40 -07:00
Cameron Gutman 3fde115670 Update to AGP 3.2 alpha 17 2018-06-08 00:24:39 -07:00
Cameron Gutman b6f4d8ff1e Target API 28 2018-06-08 00:23:41 -07:00
Cameron Gutman a7d85a7dd5 Update to AGP 3.2-alpha16 2018-05-29 18:40:26 -07:00
Cameron Gutman 9b238ab6c3 Version 5.8 (take 2) 2018-05-27 20:05:51 -07:00
Cameron Gutman f82ee97c05 Update common to fix channel mapping error in 5.1 high quality mode 2018-05-27 20:04:28 -07:00
Cameron Gutman 35fb96f9f4 Version 5.8 2018-05-27 18:56:03 -07:00
Cameron Gutman 37371906d5 Update common for audio and JNI library size improvements 2018-05-27 18:51:28 -07:00
Cameron Gutman 83a9539f4b Version 5.7.7 2018-05-21 19:24:26 -07:00
Cameron Gutman b214fe5301 Update to AGP 3.2-alpha15 2018-05-21 18:55:36 -07:00
Cameron Gutman 57779b4e89 Always expose gamepad 1 in single controller mode 2018-05-21 18:55:02 -07:00
Cameron Gutman 547932f8b2 Version 5.7.6 2018-05-13 22:23:22 -07:00
Cameron Gutman 762fa0fe2f Tighten ProGuard rules for BC 2018-05-12 21:25:41 -07:00
Cameron Gutman 9cedc57df2 Move the portrait activity_pc_view.xml to the default directory 2018-05-12 18:33:47 -07:00
Cameron Gutman ba81f8096a Allow software decoding in CrOS emulator 2018-05-11 19:28:30 -07:00
Cameron Gutman c4fa654166 Fix split breaking language chooser 2018-05-08 21:24:49 -07:00
Cameron Gutman 8ac440b68b Update AGP for AS 3.2 and app bundles 2018-05-08 21:22:10 -07:00
Cameron Gutman 165386b941 Update AGP and D8 2018-05-08 18:43:49 -07:00
Cameron Gutman 3a7398f321 Use ProGuard for minification 2018-05-08 18:43:23 -07:00
Cameron Gutman ebb1d0dfa2 Version 5.7.5 2018-04-21 23:48:04 -07:00
Cameron Gutman 1ca1ed5d20 Increase OSC analog stick size 2018-04-21 23:46:55 -07:00
Cameron Gutman b416bafb78 Hide OSC in PiP and scale properly in multi-window 2018-04-21 23:37:38 -07:00
Cameron Gutman 3a301b74a6 Update to D8 v1.0.23 2018-04-21 23:17:04 -07:00
Cameron Gutman 71d463f063 Avoid crashing from unexpected enterPictureInPictureMode() exceptions 2018-04-21 21:32:59 -07:00
Cameron Gutman 1fae816223 Remove the spinner threads (and battery saver option to disable them) 2018-04-21 21:29:42 -07:00
Cameron Gutman 989d6fc169 Fix for broken keyboard d-pad and Shift+Space behavior on Samsung devices 2018-04-21 16:24:23 -07:00
Cameron Gutman 381509b3a6 Properly handle joysticks that only return events for one trigger axis 2018-04-21 15:09:57 -07:00
Cameron Gutman d8ae40376e Update to AGP 3.1.1 2018-04-09 20:32:06 -07:00
Cameron Gutman 4ea93f5e68 Version 5.7.4 2018-04-08 21:21:37 -07:00
Cameron Gutman cd84c8f30e Fix grammar issue in decoder crash message 2018-04-08 21:19:49 -07:00
Cameron Gutman 8d4cdca7c3 Fix RFI disabling for KDDI/b5_jp_kdi/b5:7.0/NRD90U/1801120299534:user/release-keys and KDDI/b3_jp_kdi/b3:7.0/NRD90U/180120857f434:user/release-keys 2018-04-08 19:56:01 -07:00
Cameron Gutman c0239c36fd Update language around decoder crashes 2018-03-27 20:52:01 -07:00
Cameron Gutman 9d9f729e42 Address another buggy LGE variant (b5_jp_kdi) 2018-03-27 20:48:16 -07:00
Cameron Gutman 6c5fe18b6e Update Gradle for AS 3.1 2018-03-26 23:30:33 -07:00
Cameron Gutman 1994bf6522 Version 5.7.3.1 for Amazon 2018-03-24 23:48:04 -07:00
Cameron Gutman 31381e5664 Add Amlogic SoC to HEVC whitelist for Fire TV 3 now that maxNumReferenceFrames support has been out for a while 2018-03-24 23:47:05 -07:00
24 changed files with 379 additions and 246 deletions
+25 -6
View File
@@ -1,14 +1,15 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion '28.0.0'
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
targetSdkVersion 28
versionName "5.7.3"
versionCode = 151
versionName "5.8.1"
versionCode = 166
}
flavorDimensions "root"
@@ -45,9 +46,27 @@ android {
disable 'MissingTranslation'
}
bundle {
language {
// Avoid splitting by language, since we allow users
// to manually switch language in settings.
enableSplit = false
}
density {
// FIXME: This should not be neccessary but we get
// weird crashes due to missing drawable resources
// when this split is enabled.
enableSplit = false
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
minifyEnabled true
useProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
// To whomever is releasing/using an APK in release mode with
@@ -82,8 +101,8 @@ android {
// TL;DR: Leave the following line alone!
applicationIdSuffix ".unofficial"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt')
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+27
View File
@@ -0,0 +1,27 @@
# Don't obfuscate code
-dontobfuscate
# Our code
-keep class com.limelight.binding.input.evdev.* {*;}
# Moonlight common
-keep class com.limelight.nvstream.jni.* {*;}
# Okio
-keep class sun.misc.Unsafe {*;}
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**
# BouncyCastle
-keep class org.bouncycastle.jcajce.provider.asymmetric.* {*;}
-keep class org.bouncycastle.jcajce.provider.asymmetric.util.* {*;}
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.* {*;}
-keep class org.bouncycastle.jcajce.provider.digest.** {*;}
-keep class org.bouncycastle.jcajce.provider.symmetric.** {*;}
-keep class org.bouncycastle.jcajce.spec.* {*;}
-keep class org.bouncycastle.jce.** {*;}
-dontwarn javax.naming.**
# jMDNS
-dontwarn javax.jmdns.impl.DNSCache
+127 -77
View File
@@ -40,6 +40,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.input.InputManager;
@@ -70,7 +71,7 @@ import android.widget.Toast;
public class Game extends Activity implements SurfaceHolder.Callback,
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener,
OnSystemUiVisibilityChangeListener, GameGestures
OnSystemUiVisibilityChangeListener, GameGestures, StreamView.InputCallbacks
{
private int lastMouseX = Integer.MIN_VALUE;
private int lastMouseY = Integer.MIN_VALUE;
@@ -145,10 +146,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// We don't want a title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Full-screen and don't let the display go off
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Full-screen
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// If we're going to use immersive mode, we want to have
// the entire screen
@@ -184,11 +183,18 @@ public class Game extends Activity implements SurfaceHolder.Callback,
prefConfig = PreferenceConfiguration.readPreferences(this);
tombstonePrefs = Game.this.getSharedPreferences("DecoderTombstone", 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && prefConfig.stretchVideo) {
// Allow the activity to layout under notches if the fill-screen option
// was turned on by the user
getWindow().getAttributes().layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
// Listen for events on the game surface
streamView = findViewById(R.id.surfaceView);
streamView.setOnGenericMotionListener(this);
streamView.setOnTouchListener(this);
streamView.setInputCallbacks(this);
inputCaptureProvider = InputCaptureManager.getInputCaptureProvider(this, this);
@@ -305,8 +311,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
int gamepadMask = ControllerHandler.getAttachedControllerMask(this);
if (!prefConfig.multiController && gamepadMask != 0) {
// If any gamepads are present in non-MC mode, set only gamepad 1.
if (!prefConfig.multiController) {
// Always set gamepad 1 present for when multi-controller is
// disabled for games that don't properly support detection
// of gamepads removed and replugged at runtime.
gamepadMask = 1;
}
if (prefConfig.onscreenController) {
@@ -424,19 +432,46 @@ public class Game extends Activity implements SurfaceHolder.Callback,
streamView.getHolder().addCallback(this);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (virtualController != null) {
// Refresh layout of OSC for possible new screen size
virtualController.refreshLayout();
// Hide OSC in PiP
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (isInPictureInPictureMode()) {
virtualController.hide();
}
else {
virtualController.show();
}
}
}
}
@Override
public void onUserLeaveHint() {
super.onUserLeaveHint();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (prefConfig.enablePip && connected) {
enterPictureInPictureMode(
new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(prefConfig.width, prefConfig.height))
.setSourceRectHint(new Rect(
streamView.getLeft(), streamView.getTop(),
streamView.getRight(), streamView.getBottom()))
.build());
try {
// This has thrown all sorts of weird exceptions on Samsung devices
// running Oreo. Just eat them and close gracefully on leave, rather
// than crashing.
enterPictureInPictureMode(
new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(prefConfig.width, prefConfig.height))
.setSourceRectHint(new Rect(
streamView.getLeft(), streamView.getTop(),
streamView.getRight(), streamView.getBottom()))
.build());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@@ -788,9 +823,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return handleKeyDown(event) || super.onKeyDown(keyCode, event);
}
@Override
public boolean handleKeyDown(KeyEvent event) {
// Pass-through virtual navigation keys
if ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) {
return super.onKeyDown(keyCode, event);
return false;
}
// Handle a synthetic back button event that some Android OS versions
@@ -802,11 +842,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
boolean handled = false;
boolean detectedGamepad = event.getDevice() != null && ((event.getDevice().getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK ||
(event.getDevice().getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD);
if (detectedGamepad || (event.getDevice() == null ||
event.getDevice().getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC
)) {
if (ControllerHandler.isGameControllerDevice(event.getDevice())) {
// Always try the controller handler first, unless it's an alphanumeric keyboard device.
// Otherwise, controller handler will eat keyboard d-pad events.
handled = controllerHandler.handleButtonDown(event);
@@ -816,17 +852,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Try the keyboard handler
short translated = KeyboardTranslator.translate(event.getKeyCode());
if (translated == 0) {
return super.onKeyDown(keyCode, event);
return false;
}
// Let this method take duplicate key down events
if (handleSpecialKeys(keyCode, true)) {
if (handleSpecialKeys(event.getKeyCode(), true)) {
return true;
}
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return super.onKeyDown(keyCode, event);
return false;
}
conn.sendKeyboardInput(translated, KeyboardPacket.KEY_DOWN, getModifierState(event));
@@ -837,9 +873,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return handleKeyUp(event) || super.onKeyUp(keyCode, event);
}
@Override
public boolean handleKeyUp(KeyEvent event) {
// Pass-through virtual navigation keys
if ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) {
return super.onKeyUp(keyCode, event);
return false;
}
// Handle a synthetic back button event that some Android OS versions
@@ -850,11 +891,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
boolean handled = false;
boolean detectedGamepad = event.getDevice() != null && ((event.getDevice().getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK ||
(event.getDevice().getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD);
if (detectedGamepad || (event.getDevice() == null ||
event.getDevice().getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC
)) {
if (ControllerHandler.isGameControllerDevice(event.getDevice())) {
// Always try the controller handler first, unless it's an alphanumeric keyboard device.
// Otherwise, controller handler will eat keyboard d-pad events.
handled = controllerHandler.handleButtonUp(event);
@@ -864,16 +901,16 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Try the keyboard handler
short translated = KeyboardTranslator.translate(event.getKeyCode());
if (translated == 0) {
return super.onKeyUp(keyCode, event);
return false;
}
if (handleSpecialKeys(keyCode, false)) {
if (handleSpecialKeys(event.getKeyCode(), false)) {
return true;
}
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return super.onKeyUp(keyCode, event);
return false;
}
conn.sendKeyboardInput(translated, KeyboardPacket.KEY_UP, getModifierState(event));
@@ -1130,10 +1167,15 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
@Override
public void stageStarting(String stage) {
if (spinner != null) {
spinner.setMessage(getResources().getString(R.string.conn_starting)+" "+stage);
}
public void stageStarting(final String stage) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (spinner != null) {
spinner.setMessage(getResources().getString(R.string.conn_starting) + " " + stage);
}
}
});
}
@Override
@@ -1158,70 +1200,78 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
@Override
public void stageFailed(String stage, long errorCode) {
if (spinner != null) {
spinner.dismiss();
spinner = null;
}
public void stageFailed(final String stage, final long errorCode) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (spinner != null) {
spinner.dismiss();
spinner = null;
}
// Enable cursor visibility again
inputCaptureProvider.disableCapture();
if (!displayedFailureDialog) {
displayedFailureDialog = true;
LimeLog.severe(stage + " failed: " + errorCode);
if (!displayedFailureDialog) {
displayedFailureDialog = true;
LimeLog.severe(stage+" failed: "+errorCode);
// If video initialization failed and the surface is still valid, display extra information for the user
if (stage.contains("video") && streamView.getHolder().getSurface().isValid()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// If video initialization failed and the surface is still valid, display extra information for the user
if (stage.contains("video") && streamView.getHolder().getSurface().isValid()) {
Toast.makeText(Game.this, "Video decoder failed to initialize. Your device may not support the selected resolution.", Toast.LENGTH_LONG).show();
}
});
}
Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title),
getResources().getString(R.string.conn_error_msg)+" "+stage, true);
}
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_error_title),
getResources().getString(R.string.conn_error_msg) + " " + stage, true);
}
}
});
}
@Override
public void connectionTerminated(long errorCode) {
// Enable cursor visibility again
inputCaptureProvider.disableCapture();
public void connectionTerminated(final long errorCode) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// Let the display go to sleep now
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (!displayedFailureDialog) {
displayedFailureDialog = true;
LimeLog.severe("Connection terminated: "+errorCode);
stopConnection();
// Enable cursor visibility again
inputCaptureProvider.disableCapture();
Dialog.displayDialog(this, getResources().getString(R.string.conn_terminated_title),
getResources().getString(R.string.conn_terminated_msg), true);
}
if (!displayedFailureDialog) {
displayedFailureDialog = true;
LimeLog.severe("Connection terminated: " + errorCode);
stopConnection();
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_terminated_title),
getResources().getString(R.string.conn_terminated_msg), true);
}
}
});
}
@Override
public void connectionStarted() {
if (spinner != null) {
spinner.dismiss();
spinner = null;
}
connected = true;
connecting = false;
runOnUiThread(new Runnable() {
@Override
public void run() {
if (spinner != null) {
spinner.dismiss();
spinner = null;
}
connected = true;
connecting = false;
// Hide the mouse cursor now. Doing it before
// dismissing the spinner seems to be undone
// when the spinner gets displayed.
inputCaptureProvider.enableCapture();
// Keep the display on
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
hideSystemUi(1000);
}
});
hideSystemUi(1000);
}
@Override
@@ -452,7 +452,6 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
return;
}
Toast.makeText(PcView.this, getResources().getString(R.string.wol_waking_pc), Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@Override
public void run() {
@@ -149,6 +149,30 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
onInputDeviceAdded(deviceId);
}
private static boolean hasJoystickAxes(InputDevice device) {
return (device.getSources() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK &&
getMotionRangeForJoystickAxis(device, MotionEvent.AXIS_X) != null &&
getMotionRangeForJoystickAxis(device, MotionEvent.AXIS_Y) != null;
}
private static boolean hasGamepadButtons(InputDevice device) {
return (device.getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
}
public static boolean isGameControllerDevice(InputDevice device) {
if (device == null) {
return true;
}
if (hasJoystickAxes(device) || hasGamepadButtons(device)) {
// Has real joystick axes or gamepad buttons
return true;
}
// Otherwise, we'll try anything that claims to be a non-alphabetic keyboard
return device.getKeyboardType() != InputDevice.KEYBOARD_TYPE_ALPHABETIC;
}
public static short getAttachedControllerMask(Context context) {
int count = 0;
short mask = 0;
@@ -161,9 +185,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
continue;
}
if ((dev.getSources() & InputDevice.SOURCE_JOYSTICK) != 0 &&
getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_X) != null &&
getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_Y) != null) {
if (hasJoystickAxes(dev)) {
LimeLog.info("Counting InputDevice: "+dev.getName());
mask |= 1 << count++;
}
@@ -837,16 +859,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
// to normal behavior, so ignore triggersIdleNegative for each trigger until
// first touch.
if (lt != 0) {
context.leftTriggerUsed = true;
context.leftTriggerAxisUsed = true;
}
if (rt != 0) {
context.rightTriggerUsed = true;
context.rightTriggerAxisUsed = true;
}
if (context.triggersIdleNegative) {
if (context.leftTriggerUsed) {
if (context.leftTriggerAxisUsed) {
lt = (lt + 1) / 2;
}
if (context.rightTriggerUsed) {
if (context.rightTriggerAxisUsed) {
rt = (rt + 1) / 2;
}
}
@@ -1042,15 +1064,15 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.inputMap &= ~ControllerPacket.RS_CLK_FLAG;
break;
case KeyEvent.KEYCODE_BUTTON_L2:
if (context.leftTriggerAxis >= 0) {
// Suppress this digital event if an analog trigger is present
if (context.leftTriggerAxisUsed) {
// Suppress this digital event if an analog trigger is active
return true;
}
context.leftTrigger = 0;
break;
case KeyEvent.KEYCODE_BUTTON_R2:
if (context.rightTriggerAxis >= 0) {
// Suppress this digital event if an analog trigger is present
if (context.rightTriggerAxisUsed) {
// Suppress this digital event if an analog trigger is active
return true;
}
context.rightTrigger = 0;
@@ -1162,15 +1184,15 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.inputMap |= ControllerPacket.RS_CLK_FLAG;
break;
case KeyEvent.KEYCODE_BUTTON_L2:
if (context.leftTriggerAxis >= 0) {
// Suppress this digital event if an analog trigger is present
if (context.leftTriggerAxisUsed) {
// Suppress this digital event if an analog trigger is active
return true;
}
context.leftTrigger = (byte)0xFF;
break;
case KeyEvent.KEYCODE_BUTTON_R2:
if (context.rightTriggerAxis >= 0) {
// Suppress this digital event if an analog trigger is present
if (context.rightTriggerAxisUsed) {
// Suppress this digital event if an analog trigger is active
return true;
}
context.rightTrigger = (byte)0xFF;
@@ -1301,7 +1323,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public int leftTriggerAxis = -1;
public int rightTriggerAxis = -1;
public boolean triggersIdleNegative;
public boolean leftTriggerUsed, rightTriggerUsed;
public boolean leftTriggerAxisUsed, rightTriggerAxisUsed;
public int hatXAxis = -1;
public int hatYAxis = -1;
@@ -210,7 +210,7 @@ public class AnalogStick extends VirtualControllerElement {
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// calculate new radius sizes depending
radius_complete = getPercent(getCorrectWidth() / 2, 90);
radius_complete = getPercent(getCorrectWidth() / 2, 100);
radius_dead_zone = getPercent(getCorrectWidth() / 2, 30);
radius_analog_stick = getPercent(getCorrectWidth() / 2, 20);
@@ -86,6 +86,14 @@ public class VirtualController {
});
}
public void hide() {
relative_layout.setVisibility(View.INVISIBLE);
}
public void show() {
relative_layout.setVisibility(View.VISIBLE);
}
public void removeElements() {
for (VirtualControllerElement element : elements) {
relative_layout.removeView(element);
@@ -1,7 +1,6 @@
package com.limelight.binding.video;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Locale;
import org.jcodec.codecs.h264.H264Utils;
@@ -41,7 +40,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
private MediaCodec videoDecoder;
private Thread rendererThread;
private Thread[] spinnerThreads;
private boolean needsSpsBitstreamFixup, isExynos4;
private boolean adaptivePlayback, directSubmit;
private boolean constrainedHighProfile;
@@ -132,14 +130,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
this.consecutiveCrashCount = consecutiveCrashCount;
this.glRenderer = glRenderer;
// Disable spinner threads in battery saver mode or 4K
if (prefs.batterySaver || prefs.height >= 2160) {
spinnerThreads = new Thread[0];
}
else {
spinnerThreads = new Thread[Runtime.getRuntime().availableProcessors()];
}
avcDecoder = findAvcDecoder();
if (avcDecoder != null) {
LimeLog.info("Selected AVC decoder: "+avcDecoder.getName());
@@ -217,15 +207,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
}
public void notifyVideoForeground() {
startSpinnerThreads();
foreground = true;
}
public void notifyVideoBackground() {
// Signal the spinner threads to stop but
// don't wait for them to terminate to avoid
// delaying the state transition
signalSpinnerStop();
foreground = false;
}
@@ -462,73 +447,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
rendererThread.start();
}
private void startSpinnerThread(final int i) {
spinnerThreads[i] = new Thread() {
@Override
public void run() {
// This thread exists to keep the CPU at a higher DVFS state on devices
// where the governor scales clock speed sporadically, causing dropped frames.
//
// Run until we notice our thread has been removed from the spinner threads
// array. Even if we don't notice immediately, we'll notice soon enough.
// This will also ensure we terminate even if someone has restarted spinning
// before we realized we should stop.
while (this == spinnerThreads[i]) {
try {
Thread.sleep(0, 1);
} catch (InterruptedException e) {
break;
}
}
}
};
spinnerThreads[i].setName("Spinner-"+i);
spinnerThreads[i].setPriority(Thread.MIN_PRIORITY);
spinnerThreads[i].start();
}
private void startSpinnerThreads() {
LimeLog.info("Using "+spinnerThreads.length+" spinner threads");
for (int i = 0; i < spinnerThreads.length; i++) {
if (spinnerThreads[i] != null) {
continue;
}
startSpinnerThread(i);
}
}
private Thread[] signalSpinnerStop() {
// Capture current running threads
Thread[] runningThreads = Arrays.copyOf(spinnerThreads, spinnerThreads.length);
// Clear the spinner threads to signal their termination
for (int i = 0; i < spinnerThreads.length; i++) {
spinnerThreads[i] = null;
}
// Interrupt the threads
for (int i = 0; i < runningThreads.length; i++) {
if (runningThreads[i] != null) {
runningThreads[i].interrupt();
}
}
return runningThreads;
}
private void stopSpinnerThreads() {
// Signal and wait for the threads to stop
Thread[] runningThreads = signalSpinnerStop();
for (int i = 0; i < runningThreads.length; i++) {
if (runningThreads[i] != null) {
try {
runningThreads[i].join();
} catch (InterruptedException ignored) { }
}
}
}
private int dequeueInputBuffer() {
int index = -1;
long startTime;
@@ -570,7 +488,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
@Override
public void start() {
startRendererThread();
startSpinnerThreads();
}
// !!! May be called even if setup()/start() fails !!!
@@ -593,9 +510,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
try {
rendererThread.join();
} catch (InterruptedException ignored) { }
// Halt the spinner threads
stopSpinnerThreads();
}
@Override
@@ -71,8 +71,8 @@ public class MediaCodecHelper {
blacklistedDecoderPrefixes = new LinkedList<>();
// Blacklist software decoders that don't support H264 high profile,
// but exclude the official AOSP emulator from this restriction.
if (!Build.HARDWARE.equals("ranchu") || !Build.BRAND.equals("google")) {
// but exclude the official AOSP and CrOS emulator from this restriction.
if (!Build.HARDWARE.equals("ranchu") && !Build.HARDWARE.equals("cheets")) {
blacklistedDecoderPrefixes.add("omx.google");
blacklistedDecoderPrefixes.add("AVCDecoder");
}
@@ -135,6 +135,7 @@ public class MediaCodecHelper {
// to actually be tested. Ugh...
if (Build.MANUFACTURER.equalsIgnoreCase("Amazon")) {
whitelistedHevcDecoders.add("omx.mtk");
whitelistedHevcDecoders.add("omx.amlogic");
}
// These theoretically have good HEVC decoding capabilities (potentially better than
@@ -336,7 +337,7 @@ public class MediaCodecHelper {
// This device seems to crash constantly at 720p, so try disabling
// RFI to see if we can get that under control.
if (Build.BRAND.toLowerCase().equals("lge") && Build.PRODUCT.toLowerCase().startsWith("b3_")) {
if (Build.DEVICE.equals("b3") || Build.DEVICE.equals("b5")) {
return false;
}
@@ -1,7 +1,6 @@
package com.limelight.grid;
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -46,13 +45,10 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
}
LimeLog.info("Art scaling divisor: " + scalingDivisor);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = (int) scalingDivisor;
this.loader = new CachedAppAssetLoader(computer, scalingDivisor,
new NetworkAssetLoader(context, uniqueId),
new MemoryAssetLoader(),
new DiskAssetLoader(context.getCacheDir()));
new DiskAssetLoader(context));
}
public void cancelQueuedOperations() {
@@ -1,7 +1,11 @@
package com.limelight.grid.assets;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
import android.os.Build;
import com.limelight.LimeLog;
import com.limelight.utils.CacheHelper;
@@ -19,10 +23,19 @@ public class DiskAssetLoader {
private static final int STANDARD_ASSET_WIDTH = 300;
private static final int STANDARD_ASSET_HEIGHT = 400;
private final boolean isLowRamDevice;
private final File cacheDir;
public DiskAssetLoader(File cacheDir) {
this.cacheDir = cacheDir;
public DiskAssetLoader(Context context) {
this.cacheDir = context.getCacheDir();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
this.isLowRamDevice =
((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).isLowRamDevice();
}
else {
// Use conservative low RAM behavior on very old devices
this.isLowRamDevice = true;
}
}
public boolean checkCacheExists(CachedAppAssetLoader.LoaderTuple tuple) {
@@ -66,28 +79,55 @@ public class DiskAssetLoader {
return null;
}
// Lookup bounds of the downloaded image
BitmapFactory.Options decodeOnlyOptions = new BitmapFactory.Options();
decodeOnlyOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), decodeOnlyOptions);
if (decodeOnlyOptions.outWidth <= 0 || decodeOnlyOptions.outHeight <= 0) {
// Dimensions set to -1 on error. Return value always null.
return null;
Bitmap bmp;
// For OSes prior to P, we have to use the ugly BitmapFactory API
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
// Lookup bounds of the downloaded image
BitmapFactory.Options decodeOnlyOptions = new BitmapFactory.Options();
decodeOnlyOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), decodeOnlyOptions);
if (decodeOnlyOptions.outWidth <= 0 || decodeOnlyOptions.outHeight <= 0) {
// Dimensions set to -1 on error. Return value always null.
return null;
}
LimeLog.info("Tuple "+tuple+" has cached art of size: "+decodeOnlyOptions.outWidth+"x"+decodeOnlyOptions.outHeight);
// Load the image scaled to the appropriate size
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calculateInSampleSize(decodeOnlyOptions,
STANDARD_ASSET_WIDTH / sampleSize,
STANDARD_ASSET_HEIGHT / sampleSize);
if (isLowRamDevice) {
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inDither = true;
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
options.inPreferredConfig = Bitmap.Config.HARDWARE;
}
bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
if (bmp != null) {
LimeLog.info("Tuple "+tuple+" decoded from disk cache with sample size: "+options.inSampleSize);
}
}
LimeLog.info("Tuple "+tuple+" has cached art of size: "+decodeOnlyOptions.outWidth+"x"+decodeOnlyOptions.outHeight);
// Load the image scaled to the appropriate size
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calculateInSampleSize(decodeOnlyOptions,
STANDARD_ASSET_WIDTH / sampleSize,
STANDARD_ASSET_HEIGHT / sampleSize);
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inDither = true;
Bitmap bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
if (bmp != null) {
LimeLog.info("Tuple "+tuple+" decoded from disk cache with sample size: "+options.inSampleSize);
else {
// On P, we can get a bitmap back in one step with ImageDecoder
try {
bmp = ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), new ImageDecoder.OnHeaderDecodedListener() {
@Override
public void onHeaderDecoded(ImageDecoder imageDecoder, ImageDecoder.ImageInfo imageInfo, ImageDecoder.Source source) {
imageDecoder.setTargetSize(STANDARD_ASSET_WIDTH, STANDARD_ASSET_HEIGHT);
if (isLowRamDevice) {
imageDecoder.setMemorySizePolicy(ImageDecoder.MEMORY_POLICY_LOW_RAM);
}
}
});
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
return bmp;
@@ -24,6 +24,7 @@ import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
@@ -196,12 +197,7 @@ public class AddComputerManually extends Activity {
(keyEvent != null &&
keyEvent.getAction() == KeyEvent.ACTION_DOWN &&
keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
if (hostText.getText().length() == 0) {
Toast.makeText(AddComputerManually.this, getResources().getString(R.string.addpc_enter_ip), Toast.LENGTH_LONG).show();
return true;
}
computersToAdd.add(hostText.getText().toString().trim());
return handleDoneEvent();
}
else if (actionId == EditorInfo.IME_ACTION_PREVIOUS) {
// This is how the Fire TV dismisses the keyboard
@@ -214,8 +210,28 @@ public class AddComputerManually extends Activity {
}
});
findViewById(R.id.addPcButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
handleDoneEvent();
}
});
// Bind to the ComputerManager service
bindService(new Intent(AddComputerManually.this,
ComputerManagerService.class), serviceConnection, Service.BIND_AUTO_CREATE);
}
// Returns true if the event should be eaten
private boolean handleDoneEvent() {
String hostAddress = hostText.getText().toString().trim();
if (hostAddress.length() == 0) {
Toast.makeText(AddComputerManually.this, getResources().getString(R.string.addpc_enter_ip), Toast.LENGTH_LONG).show();
return true;
}
computersToAdd.add(hostAddress);
return false;
}
}
@@ -24,7 +24,6 @@ public class PreferenceConfiguration {
private static final String VIDEO_FORMAT_PREF_STRING = "video_format";
private static final String ONSCREEN_CONTROLLER_PREF_STRING = "checkbox_show_onscreen_controls";
private static final String ONLY_L3_R3_PREF_STRING = "checkbox_only_show_L3R3";
private static final String BATTERY_SAVER_PREF_STRING = "checkbox_battery_saver";
private static final String DISABLE_FRAME_DROP_PREF_STRING = "checkbox_disable_frame_drop";
private static final String ENABLE_HDR_PREF_STRING = "checkbox_enable_hdr";
private static final String ENABLE_PIP_PREF_STRING = "checkbox_enable_pip";
@@ -75,7 +74,6 @@ public class PreferenceConfiguration {
public boolean listMode, smallIconMode, multiController, enable51Surround, usbDriver;
public boolean onscreenController;
public boolean onlyL3R3;
public boolean batterySaver;
public boolean disableFrameDrop;
public boolean enableHdr;
public boolean enablePip;
@@ -244,7 +242,6 @@ public class PreferenceConfiguration {
config.usbDriver = prefs.getBoolean(USB_DRIVER_PREF_SRING, DEFAULT_USB_DRIVER);
config.onscreenController = prefs.getBoolean(ONSCREEN_CONTROLLER_PREF_STRING, ONSCREEN_CONTROLLER_DEFAULT);
config.onlyL3R3 = prefs.getBoolean(ONLY_L3_R3_PREF_STRING, ONLY_L3_R3_DEFAULT);
config.batterySaver = prefs.getBoolean(BATTERY_SAVER_PREF_STRING, DEFAULT_BATTERY_SAVER);
config.disableFrameDrop = prefs.getBoolean(DISABLE_FRAME_DROP_PREF_STRING, DEFAULT_DISABLE_FRAME_DROP);
config.enableHdr = prefs.getBoolean(ENABLE_HDR_PREF_STRING, DEFAULT_ENABLE_HDR);
config.enablePip = prefs.getBoolean(ENABLE_PIP_PREF_STRING, DEFAULT_ENABLE_PIP);
@@ -3,15 +3,21 @@ package com.limelight.ui;
import android.annotation.TargetApi;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.SurfaceView;
public class StreamView extends SurfaceView {
private double desiredAspectRatio;
private InputCallbacks inputCallbacks;
public void setDesiredAspectRatio(double aspectRatio) {
this.desiredAspectRatio = aspectRatio;
}
public void setInputCallbacks(InputCallbacks callbacks) {
this.inputCallbacks = callbacks;
}
public StreamView(Context context) {
super(context);
}
@@ -52,4 +58,30 @@ public class StreamView extends SurfaceView {
setMeasuredDimension(measuredWidth, measuredHeight);
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
// This callbacks allows us to override dumb IME behavior like when
// Samsung's default keyboard consumes Shift+Space. We'll process
// the input event directly if any modifier keys are down.
if (inputCallbacks != null && event.getModifiers() != 0) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (inputCallbacks.handleKeyDown(event)) {
return true;
}
}
else if (event.getAction() == KeyEvent.ACTION_UP) {
if (inputCallbacks.handleKeyUp(event)) {
return true;
}
}
}
return super.onKeyPreIme(keyCode, event);
}
public interface InputCallbacks {
boolean handleKeyUp(KeyEvent event);
boolean handleKeyDown(KeyEvent event);
}
}
@@ -7,7 +7,9 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
import android.view.View;
import android.view.WindowManager;
import com.limelight.R;
import com.limelight.preferences.PreferenceConfiguration;
@@ -57,6 +59,16 @@ public class UiHelper {
rootView.setPadding(horizontalPaddingPixels, verticalPaddingPixels,
horizontalPaddingPixels, verticalPaddingPixels);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// Allow this non-streaming activity to layout under notches.
//
// We should NOT do this for the Game activity unless
// the user specifically opts in, because it can obscure
// parts of the streaming surface.
activity.getWindow().getAttributes().layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
}
public static void showDecoderCrashDialog(Activity activity) {
@@ -22,11 +22,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/manuallyAddPcText"
android:layout_toLeftOf="@+id/addPcButton"
android:layout_toStartOf="@+id/addPcButton"
android:layout_marginTop="25dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:ems="10"
android:singleLine="true"
android:inputType="textNoSuggestions"
@@ -34,5 +34,15 @@
<requestFocus />
</EditText>
<Button
android:id="@+id/addPcButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:layout_below="@+id/manuallyAddPcText"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:text="@android:string/ok"/>
</RelativeLayout>
@@ -1,3 +1,4 @@
<!-- Portrait orientation only -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
-2
View File
@@ -110,8 +110,6 @@
<string name="title_checkbox_stretch_video">Forza video a schermo intero</string>
<string name="title_checkbox_disable_warnings">Disabilita messaggi di warning</string>
<string name="summary_checkbox_disable_warnings">Disabilita i messaggi di warning sullo schermo durante lo streaming</string>
<string name="title_checkbox_battery_saver">Risparmio batteria</string>
<string name="summary_checkbox_battery_saver">Usa meno batteria, ma può aumentare lo stuttering</string>
<string name="title_checkbox_enable_pip">Abilita modalità spettatore Picture-in-Picture</string>
<string name="summary_checkbox_enable_pip">Permette di osservare (ma non di controllare) la stream in multitasking</string>
-2
View File
@@ -142,8 +142,6 @@
<string name="message_decoding_reset">Видео декодер Вашего устройства давал сбои с выбранными настройками. Настройки трансляции были сброшены до значений по умолчанию.</string>
<string name="error_usb_prohibited">USB доступ запрещен администратором устройства. Проверьте настройки Knox или MDM.</string>
<string name="addpc_wrong_sitelocal">Адрес указан неверно. Вы должны ввести публичный IP-адрес Вашего роутера для передачи через интернет.</string>
<string name="title_checkbox_battery_saver">Экономия батареи</string>
<string name="summary_checkbox_battery_saver">Использует меньше заряда батареи, но может увеличить зависания</string>
<string name="title_checkbox_enable_pip">Включить просмотр в режиме \"Картинка в картинке\"</string>
<string name="summary_checkbox_enable_pip">Позволяет просматривать трансляцию (но не управлять ей) во время работы в других приложениях</string>
<string name="title_checkbox_usb_bind_all">Переопределить поддержку контроллеров Android</string>
+2 -4
View File
@@ -51,7 +51,7 @@
Using remote desktop software can also cause this error. Try rebooting your machine or reinstalling GFE.
</string>
<string name="title_decoding_error">Video Decoder Crashed</string>
<string name="message_decoding_error">Moonlight has crashed due to a problem with this device\'s video decoder. Try adjusting the streaming settings if the crashes continue.</string>
<string name="message_decoding_error">Moonlight has crashed due to an incompatibility with this device\'s video decoder. Ensure GeForce Experience is updated to the latest version on your PC. Try adjusting the streaming settings if the crashes continue.</string>
<string name="title_decoding_reset">Video Settings Reset</string>
<string name="message_decoding_reset">Your device\'s video decoder continues to crash at your selected streaming settings. Your streaming settings have been reset to default.</string>
<string name="error_usb_prohibited">USB access is prohibited by your device administrator. Check your Knox or MDM settings.</string>
@@ -113,8 +113,6 @@
<string name="title_checkbox_stretch_video">Stretch video to full-screen</string>
<string name="title_checkbox_disable_warnings">Disable warning messages</string>
<string name="summary_checkbox_disable_warnings">Disable on-screen connection warning messages while streaming</string>
<string name="title_checkbox_battery_saver">Battery saver</string>
<string name="summary_checkbox_battery_saver">Uses less battery, but may increase stuttering</string>
<string name="title_checkbox_enable_pip">Enable Picture-in-Picture observer mode</string>
<string name="summary_checkbox_enable_pip">Allows the stream to be viewed (but not controlled) while multitasking</string>
@@ -124,7 +122,7 @@
<string name="category_gamepad_settings">Gamepad Settings</string>
<string name="title_checkbox_multi_controller">Multiple controller support</string>
<string name="summary_checkbox_multi_controller">When unchecked, all controllers appear as one</string>
<string name="summary_checkbox_multi_controller">Uncheck for games with controller detection issues</string>
<string name="title_seekbar_deadzone">Adjust analog stick deadzone</string>
<string name="suffix_seekbar_deadzone">%</string>
<string name="title_checkbox_xb1_driver">Xbox 360/One controller driver</string>
-5
View File
@@ -24,11 +24,6 @@
android:key="checkbox_stretch_video"
android:title="@string/title_checkbox_stretch_video"
android:defaultValue="false" />
<CheckBoxPreference
android:key="checkbox_battery_saver"
android:title="@string/title_checkbox_battery_saver"
android:summary="@string/summary_checkbox_battery_saver"
android:defaultValue="false" />
<CheckBoxPreference
android:key="checkbox_enable_pip"
android:title="@string/title_checkbox_enable_pip"
+1 -1
View File
@@ -5,7 +5,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0-rc03'
classpath 'com.android.tools.build:gradle:3.2.0-alpha17'
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
#Tue Mar 20 18:12:28 PDT 2018
#Tue May 08 18:56:31 PDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip