Drop support for Jelly Bean and KitKat

NDK support for these is already gone
This commit is contained in:
Cameron Gutman 2024-03-17 17:43:42 -05:00
parent 6551076613
commit 17df15293f
20 changed files with 218 additions and 457 deletions

View File

@ -8,7 +8,7 @@ android {
namespace 'com.limelight'
defaultConfig {
minSdk 16
minSdk 21
targetSdk 34
versionName "12.1"

View File

@ -195,14 +195,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// If we're going to use immersive mode, we want to have
// the entire screen
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
// Listen for UI visibility events
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
@ -560,39 +558,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
if (desiredOrientation == Configuration.ORIENTATION_LANDSCAPE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
}
else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
}
else if (desiredOrientation == Configuration.ORIENTATION_PORTRAIT) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
}
else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
}
else {
// If we don't have a reason to lock to portrait or landscape, allow any orientation
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
}
else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
}
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
}
}
else {
// For regular displays, we always request landscape
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
}
else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
}
}
@ -920,7 +898,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
displayRefreshRate = bestMode.getRefreshRate();
}
// On L, we can at least tell the OS that we want a refresh rate
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
else {
float bestRefreshRate = display.getRefreshRate();
for (float candidate : display.getSupportedRefreshRates()) {
LimeLog.info("Examining refresh rate: "+candidate);
@ -944,19 +922,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Apply the refresh rate change
getWindow().setAttributes(windowLayoutParams);
}
else {
// Otherwise, the active display refresh rate is just
// whatever is currently in use.
displayRefreshRate = display.getRefreshRate();
}
// From 4.4 to 5.1 we can't ask for a 4K display mode, so we'll
// Until Marshmallow, 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
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// We'll calculate whether we need to scale by aspect ratio. 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
@ -1011,8 +982,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
Game.this.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
// Use immersive mode on 4.4+ or standard low profile on previous builds
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
else {
// Use immersive mode
Game.this.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
@ -1021,11 +992,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
else {
Game.this.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
}
};
@ -2214,13 +2180,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View view, MotionEvent event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Tell the OS not to buffer input events for us
//
// NB: This is still needed even when we call the newer requestUnbufferedDispatch()!
view.requestUnbufferedDispatch(event);
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Tell the OS not to buffer input events for us
//
// NB: This is still needed even when we call the newer requestUnbufferedDispatch()!
view.requestUnbufferedDispatch(event);
}
return handleMotionEvent(view, event);
@ -2667,14 +2631,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
hideSystemUi(2000);
}
// This flag is only set on 4.4+
else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT &&
(visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
hideSystemUi(2000);
}
// This flag is only set before 4.4+
else if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT &&
(visibility & View.SYSTEM_UI_FLAG_LOW_PROFILE) == 0) {
else if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
hideSystemUi(2000);
}
}

View File

@ -26,51 +26,41 @@ public class AndroidAudioRenderer implements AudioRenderer {
}
private AudioTrack createAudioTrack(int channelConfig, int sampleRate, int bufferSize, boolean lowLatency) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate,
channelConfig,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM);
AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME);
AudioFormat format = new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(sampleRate)
.setChannelMask(channelConfig)
.build();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// Use FLAG_LOW_LATENCY on L through N
if (lowLatency) {
attributesBuilder.setFlags(AudioAttributes.FLAG_LOW_LATENCY);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AudioTrack.Builder trackBuilder = new AudioTrack.Builder()
.setAudioFormat(format)
.setAudioAttributes(attributesBuilder.build())
.setTransferMode(AudioTrack.MODE_STREAM)
.setBufferSizeInBytes(bufferSize);
// Use PERFORMANCE_MODE_LOW_LATENCY on O and later
if (lowLatency) {
trackBuilder.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY);
}
return trackBuilder.build();
}
else {
AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME);
AudioFormat format = new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(sampleRate)
.setChannelMask(channelConfig)
.build();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// Use FLAG_LOW_LATENCY on L through N
if (lowLatency) {
attributesBuilder.setFlags(AudioAttributes.FLAG_LOW_LATENCY);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AudioTrack.Builder trackBuilder = new AudioTrack.Builder()
.setAudioFormat(format)
.setAudioAttributes(attributesBuilder.build())
.setTransferMode(AudioTrack.MODE_STREAM)
.setBufferSizeInBytes(bufferSize);
// Use PERFORMANCE_MODE_LOW_LATENCY on O and later
if (lowLatency) {
trackBuilder.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY);
}
return trackBuilder.build();
}
else {
return new AudioTrack(attributesBuilder.build(),
format,
bufferSize,
AudioTrack.MODE_STREAM,
AudioManager.AUDIO_SESSION_ID_GENERATE);
}
return new AudioTrack(attributesBuilder.build(),
format,
bufferSize,
AudioTrack.MODE_STREAM,
AudioManager.AUDIO_SESSION_ID_GENERATE);
}
}
@ -91,20 +81,10 @@ public class AndroidAudioRenderer implements AudioRenderer {
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
case 8:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// AudioFormat.CHANNEL_OUT_7POINT1_SURROUND isn't available until Android 6.0,
// yet the CHANNEL_OUT_SIDE_LEFT and CHANNEL_OUT_SIDE_RIGHT constants were added
// in 5.0, so just hardcode the constant so we can work on Lollipop.
channelConfig = 0x000018fc; // AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
}
else {
// On KitKat and lower, creation of the AudioTrack will fail if we specify
// CHANNEL_OUT_SIDE_LEFT or CHANNEL_OUT_SIDE_RIGHT. That leaves us with
// the old CHANNEL_OUT_7POINT1 which uses left-of-center and right-of-center
// speakers instead of side-left and side-right. This non-standard layout
// is probably not what the user wants, but we don't really have a choice.
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1;
}
// AudioFormat.CHANNEL_OUT_7POINT1_SURROUND isn't available until Android 6.0,
// yet the CHANNEL_OUT_SIDE_LEFT and CHANNEL_OUT_SIDE_RIGHT constants were added
// in 5.0, so just hardcode the constant so we can work on Lollipop.
channelConfig = 0x000018fc; // AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
break;
default:
LimeLog.severe("Decoder returned unhandled channel count");

View File

@ -666,7 +666,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
// back button to function for navigation.
//
// First, check if this is an internal device we're being called on.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !isExternal(dev)) {
if (!isExternal(dev)) {
InputManager im = (InputManager) activityContext.getSystemService(Context.INPUT_SERVICE);
boolean foundInternalGamepad = false;
@ -712,10 +712,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
String devName = dev.getName();
LimeLog.info("Creating controller context for device: "+devName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
LimeLog.info("Vendor ID: "+dev.getVendorId());
LimeLog.info("Product ID: "+dev.getProductId());
}
LimeLog.info("Vendor ID: " + dev.getVendorId());
LimeLog.info("Product ID: "+dev.getProductId());
LimeLog.info(dev.toString());
context.inputDevice = dev;
@ -723,15 +721,13 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.id = dev.getId();
context.external = isExternal(dev);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
context.vendorId = dev.getVendorId();
context.productId = dev.getProductId();
context.vendorId = dev.getVendorId();
context.productId = dev.getProductId();
// These aren't always present in the Android key layout files, so they won't show up
// in our normal InputDevice.hasKeys() probing.
context.hasPaddles = MoonBridge.guessControllerHasPaddles(context.vendorId, context.productId);
context.hasShare = MoonBridge.guessControllerHasShareButton(context.vendorId, context.productId);
}
// These aren't always present in the Android key layout files, so they won't show up
// in our normal InputDevice.hasKeys() probing.
context.hasPaddles = MoonBridge.guessControllerHasPaddles(context.vendorId, context.productId);
context.hasShare = MoonBridge.guessControllerHasShareButton(context.vendorId, context.productId);
// Try to use the InputDevice's associated vibrators first
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && hasQuadAmplitudeControlledRumbleVibrators(dev.getVibratorManager())) {
@ -790,11 +786,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
// Detect if the gamepad has Mode and Select buttons according to the Android key layouts.
// We do this first because other codepaths below may override these defaults.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
boolean[] buttons = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_MODE, KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_BACK, 0);
context.hasMode = buttons[0];
context.hasSelect = buttons[1] || buttons[2];
}
boolean[] buttons = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_MODE, KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_BACK, 0);
context.hasMode = buttons[0];
context.hasSelect = buttons[1] || buttons[2];
context.touchpadXRange = dev.getMotionRange(MotionEvent.AXIS_X, InputDevice.SOURCE_TOUCHPAD);
context.touchpadYRange = dev.getMotionRange(MotionEvent.AXIS_Y, InputDevice.SOURCE_TOUCHPAD);
@ -844,22 +838,15 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
InputDevice.MotionRange ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
if (rxRange != null && ryRange != null && devName != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (dev.getVendorId() == 0x054c) { // Sony
if (dev.hasKeys(KeyEvent.KEYCODE_BUTTON_C)[0]) {
LimeLog.info("Detected non-standard DualShock 4 mapping");
context.isNonStandardDualShock4 = true;
}
else {
LimeLog.info("Detected DualShock 4 (Linux standard mapping)");
context.usesLinuxGamepadStandardFaceButtons = true;
}
if (dev.getVendorId() == 0x054c) { // Sony
if (dev.hasKeys(KeyEvent.KEYCODE_BUTTON_C)[0]) {
LimeLog.info("Detected non-standard DualShock 4 mapping");
context.isNonStandardDualShock4 = true;
} else {
LimeLog.info("Detected DualShock 4 (Linux standard mapping)");
context.usesLinuxGamepadStandardFaceButtons = true;
}
}
else if (!devName.contains("Xbox") && !devName.contains("XBox") && !devName.contains("X-Box")) {
LimeLog.info("Assuming non-standard DualShock 4 mapping on < 4.4");
context.isNonStandardDualShock4 = true;
}
if (context.isNonStandardDualShock4) {
// The old DS4 driver uses RX and RY for triggers
@ -943,16 +930,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
}
// The ADT-1 controller needs a similar fixup to the ASUS Gamepad
if (Build.VERSION.SDK_INT >= 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;
context.hasSelect = true;
context.hasMode = false;
}
if (dev.getVendorId() == 0x18d1 && dev.getProductId() == 0x2c40) {
context.backIsStart = true;
context.modeIsSelect = true;
context.triggerDeadzone = 0.30f;
context.hasSelect = true;
context.hasMode = false;
}
context.ignoreBack = shouldIgnoreBack(dev);
@ -962,16 +945,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
// use the back button as start since it doesn't have a start/menu button
// on the controller
if (devName.contains("ASUS Gamepad")) {
// We can only do this check on KitKat or higher, but it doesn't matter since ATV
// is Android 5.0 anyway
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0);
if (!hasStartKey[0] && !hasStartKey[1]) {
context.backIsStart = true;
context.modeIsSelect = true;
context.hasSelect = true;
context.hasMode = false;
}
boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0);
if (!hasStartKey[0] && !hasStartKey[1]) {
context.backIsStart = true;
context.modeIsSelect = true;
context.hasSelect = true;
context.hasMode = false;
}
// The ASUS Gamepad has triggers that sit far forward and are prone to false presses
@ -1016,10 +995,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
// Thrustmaster Score A gamepad home button reports directly to android as
// KEY_HOMEPAGE event on another event channel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (dev.getVendorId() == 0x044f && dev.getProductId() == 0xb328) {
context.hasMode = false;
}
if (dev.getVendorId() == 0x044f && dev.getProductId() == 0xb328) {
context.hasMode = false;
}
LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius);
@ -2048,15 +2025,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
.build();
vibrator.vibrate(VibrationEffect.createWaveform(new long[]{0, onTime, offTime}, 0), vibrationAttributes);
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
else {
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.build();
vibrator.vibrate(new long[]{0, onTime, offTime}, 0, audioAttributes);
}
else {
vibrator.vibrate(new long[]{0, onTime, offTime}, 0);
}
}
public void handleRumble(short controllerNumber, short lowFreqMotor, short highFreqMotor) {
@ -3086,11 +3060,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
@Override
public void sendControllerArrival() {
// Below KitKat we can't get enough information to report controller details accurately
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
byte type;
switch (inputDevice.getVendorId()) {
case 0x045e: // Microsoft

View File

@ -211,28 +211,22 @@ public class UsbDriverService extends Service implements UsbDriverListener {
}
public static boolean isRecognizedInputDevice(UsbDevice device) {
// On KitKat and later, we can determine if this VID and PID combo
// matches an existing input device and defer to the built-in controller
// support in that case. Prior to KitKat, we'll always return true to be safe.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
for (int id : InputDevice.getDeviceIds()) {
InputDevice inputDev = InputDevice.getDevice(id);
if (inputDev == null) {
// Device was removed while looping
continue;
}
if (inputDev.getVendorId() == device.getVendorId() &&
inputDev.getProductId() == device.getProductId()) {
return true;
}
// Determine if this VID and PID combo matches an existing input device
// and defer to the built-in controller support in that case.
for (int id : InputDevice.getDeviceIds()) {
InputDevice inputDev = InputDevice.getDevice(id);
if (inputDev == null) {
// Device was removed while looping
continue;
}
return false;
}
else {
return true;
if (inputDev.getVendorId() == device.getVendorId() &&
inputDev.getProductId() == device.getProductId()) {
return true;
}
}
return false;
}
public static boolean kernelSupportsXboxOne() {

View File

@ -89,28 +89,26 @@ public class Xbox360WirelessDongle extends AbstractController {
public boolean start() {
int controllerIndex = 0;
// On KitKat, there is a controller number associated with input devices.
// On Android, there is a controller number associated with input devices.
// We can use this to approximate the likely controller number. This won't
// be completely accurate because there's no guarantee the order of interfaces
// matches the order that devices were enumerated by xpad, but it's probably
// better than nothing.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
for (int id : InputDevice.getDeviceIds()) {
InputDevice inputDev = InputDevice.getDevice(id);
if (inputDev == null) {
// Device was removed while looping
continue;
}
for (int id : InputDevice.getDeviceIds()) {
InputDevice inputDev = InputDevice.getDevice(id);
if (inputDev == null) {
// Device was removed while looping
continue;
}
// Newer xpad versions use a special product ID (0x02a1) for controllers
// rather than copying the product ID of the dongle itself.
if (inputDev.getVendorId() == device.getVendorId() &&
(inputDev.getProductId() == device.getProductId() ||
inputDev.getProductId() == 0x02a1) &&
inputDev.getControllerNumber() > 0) {
controllerIndex = inputDev.getControllerNumber() - 1;
break;
}
// Newer xpad versions use a special product ID (0x02a1) for controllers
// rather than copying the product ID of the dongle itself.
if (inputDev.getVendorId() == device.getVendorId() &&
(inputDev.getProductId() == device.getProductId() ||
inputDev.getProductId() == 0x02a1) &&
inputDev.getControllerNumber() > 0) {
controllerIndex = inputDev.getControllerNumber() - 1;
break;
}
}

View File

@ -471,9 +471,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, refreshRate);
}
// Adaptive playback can also be enabled by the whitelist on pre-KitKat devices
// so we don't fill these pre-KitKat
if (adaptivePlayback && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Populate keys for adaptive playback
if (adaptivePlayback) {
videoFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, initialWidth);
videoFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, initialHeight);
}

View File

@ -596,45 +596,39 @@ public class MediaCodecHelper {
public static boolean decoderSupportsFusedIdrFrame(MediaCodecInfo decoderInfo, String mimeType) {
// If adaptive playback is supported, we can submit new CSD together with a keyframe
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
if (decoderInfo.getCapabilitiesForType(mimeType).
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
{
LimeLog.info("Decoder supports fused IDR frames (FEATURE_AdaptivePlayback)");
return true;
}
} catch (Exception e) {
// Tolerate buggy codecs
e.printStackTrace();
try {
if (decoderInfo.getCapabilitiesForType(mimeType).
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback)) {
LimeLog.info("Decoder supports fused IDR frames (FEATURE_AdaptivePlayback)");
return true;
}
} catch (Exception e) {
// Tolerate buggy codecs
e.printStackTrace();
}
return false;
}
public static boolean decoderSupportsAdaptivePlayback(MediaCodecInfo decoderInfo, String mimeType) {
// Possibly enable adaptive playback on KitKat and above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (isDecoderInList(blacklistedAdaptivePlaybackPrefixes, decoderInfo.getName())) {
LimeLog.info("Decoder blacklisted for adaptive playback");
return false;
}
try {
if (decoderInfo.getCapabilitiesForType(mimeType).
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
{
// This will make getCapabilities() return that adaptive playback is supported
LimeLog.info("Adaptive playback supported (FEATURE_AdaptivePlayback)");
return true;
}
} catch (Exception e) {
// Tolerate buggy codecs
e.printStackTrace();
}
if (isDecoderInList(blacklistedAdaptivePlaybackPrefixes, decoderInfo.getName())) {
LimeLog.info("Decoder blacklisted for adaptive playback");
return false;
}
try {
if (decoderInfo.getCapabilitiesForType(mimeType).
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
{
// This will make getCapabilities() return that adaptive playback is supported
LimeLog.info("Adaptive playback supported (FEATURE_AdaptivePlayback)");
return true;
}
} catch (Exception e) {
// Tolerate buggy codecs
e.printStackTrace();
}
return false;
}
@ -709,13 +703,6 @@ public class MediaCodecHelper {
}
public static boolean decoderIsWhitelistedForHevc(MediaCodecInfo decoderInfo) {
// Google didn't have official support for HEVC (or more importantly, a CTS test) until
// Lollipop. I've seen some MediaTek devices on 4.4 crash when attempting to use HEVC,
// so I'm restricting HEVC usage to Lollipop and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return false;
}
//
// Software decoders are terrible and we never want to use them.
// We want to catch decoders like:
@ -784,17 +771,10 @@ public class MediaCodecHelper {
@SuppressLint("NewApi")
private static LinkedList<MediaCodecInfo> getMediaCodecList() {
LinkedList<MediaCodecInfo> infoList = new LinkedList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
Collections.addAll(infoList, mcl.getCodecInfos());
}
else {
for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
infoList.add(MediaCodecList.getCodecInfoAt(i));
}
}
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
Collections.addAll(infoList, mcl.getCodecInfos());
return infoList;
}

View File

@ -341,57 +341,53 @@ public class ComputerManagerService extends Service {
// Acquire the default network lock since we could be changing global process state
defaultNetworkLock.lock();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// On Lollipop or later, we can bind our process to the underlying interface
// to ensure our STUN request goes out on that interface or not at all (which is
// preferable to getting a VPN endpoint address back).
Network[] networks = connMgr.getAllNetworks();
for (Network net : networks) {
NetworkCapabilities netCaps = connMgr.getNetworkCapabilities(net);
if (netCaps != null) {
if (!netCaps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) &&
!netCaps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
// This network looks like an underlying multicast-capable transport,
// so let's guess that it's probably where our mDNS response came from.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (connMgr.bindProcessToNetwork(net)) {
boundToNetwork = true;
break;
}
}
else if (ConnectivityManager.setProcessDefaultNetwork(net)) {
// On Lollipop or later, we can bind our process to the underlying interface
// to ensure our STUN request goes out on that interface or not at all (which is
// preferable to getting a VPN endpoint address back).
Network[] networks = connMgr.getAllNetworks();
for (Network net : networks) {
NetworkCapabilities netCaps = connMgr.getNetworkCapabilities(net);
if (netCaps != null) {
if (!netCaps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) &&
!netCaps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
// This network looks like an underlying multicast-capable transport,
// so let's guess that it's probably where our mDNS response came from.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (connMgr.bindProcessToNetwork(net)) {
boundToNetwork = true;
break;
}
} else if (ConnectivityManager.setProcessDefaultNetwork(net)) {
boundToNetwork = true;
break;
}
}
}
}
}
// Perform the STUN request if we're not on a VPN or if we bound to a network
if (!activeNetworkIsVpn || boundToNetwork) {
String stunResolvedAddress = NvConnection.findExternalAddressForMdns("stun.moonlight-stream.org", 3478);
if (stunResolvedAddress != null) {
// We don't know for sure what the external port is, so we will have to guess.
// When we contact the PC (if we haven't already), it will update the port.
details.remoteAddress = new ComputerDetails.AddressTuple(stunResolvedAddress, details.guessExternalPort());
// Perform the STUN request if we're not on a VPN or if we bound to a network
if (!activeNetworkIsVpn || boundToNetwork) {
String stunResolvedAddress = NvConnection.findExternalAddressForMdns("stun.moonlight-stream.org", 3478);
if (stunResolvedAddress != null) {
// We don't know for sure what the external port is, so we will have to guess.
// When we contact the PC (if we haven't already), it will update the port.
details.remoteAddress = new ComputerDetails.AddressTuple(stunResolvedAddress, details.guessExternalPort());
}
}
}
// Unbind from the network
if (boundToNetwork) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
connMgr.bindProcessToNetwork(null);
// Unbind from the network
if (boundToNetwork) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
connMgr.bindProcessToNetwork(null);
} else {
ConnectivityManager.setProcessDefaultNetwork(null);
}
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ConnectivityManager.setProcessDefaultNetwork(null);
}
}
// Unlock the network state
if (activeNetworkIsVpn) {
defaultNetworkLock.unlock();
// Unlock the network state
if (activeNetworkIsVpn) {
defaultNetworkLock.unlock();
}
}
}

View File

@ -28,14 +28,8 @@ public class DiskAssetLoader {
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;
}
this.isLowRamDevice =
((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).isLowRamDevice();
}
public boolean checkCacheExists(CachedAppAssetLoader.LoaderTuple tuple) {

View File

@ -198,7 +198,7 @@ public class NvConnection {
}
}
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
else {
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
if (activeNetworkInfo != null) {
switch (activeNetworkInfo.getType()) {

View File

@ -403,16 +403,7 @@ public class NvHTTP {
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(new KeyManager[] { keyManager }, new TrustManager[] { trustManager }, new SecureRandom());
// TLS 1.2 is not enabled by default prior to Android 5.0, so we'll need a custom
// SSLSocketFactory in order to connect to GFE 3.20.4 which requires TLSv1.2 or later.
// We don't just always use TLSv12SocketFactory because explicitly specifying TLS versions
// prevents later TLS versions from being negotiated even if client and server otherwise
// support them.
return client.newBuilder().sslSocketFactory(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
sc.getSocketFactory() : new TLSv12SocketFactory(sc),
trustManager).build();
return client.newBuilder().sslSocketFactory(sc.getSocketFactory(), trustManager).build();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new RuntimeException(e);
}
@ -814,62 +805,4 @@ public class NvHTTP {
return true;
}
// Based on example code from https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/
private static class TLSv12SocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSv12SocketFactory(SSLContext context) {
internalSSLSocketFactory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket());
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSv12OnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSv12OnSocket(Socket socket) {
if (socket instanceof SSLSocket) {
// TLS 1.2 is not enabled by default prior to Android 5.0. We must enable it
// explicitly to ensure we can communicate with GFE 3.20.4 which blocks TLS 1.0.
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
}
return socket;
}
}
}

View File

@ -13,7 +13,6 @@ import com.limelight.R;
import static com.limelight.binding.input.virtual_controller.VirtualControllerConfigurationLoader.OSC_PREFERENCE;
public class ConfirmDeleteOscPreference extends DialogPreference {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ConfirmDeleteOscPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@ -26,7 +25,6 @@ public class ConfirmDeleteOscPreference extends DialogPreference {
super(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ConfirmDeleteOscPreference(Context context) {
super(context);
}

View File

@ -11,12 +11,10 @@ import android.provider.Settings;
import android.util.AttributeSet;
public class LanguagePreference extends ListPreference {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public LanguagePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public LanguagePreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

View File

@ -511,29 +511,15 @@ public class StreamSettings extends Activity {
// Never remove 720p
}
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// On Android 4.2 and later, we can get the true metrics via the
// getRealMetrics() function (unlike the lies that getWidth() and getHeight()
// tell to us).
else {
// We can get the true metrics via the getRealMetrics() function (unlike the lies
// that getWidth() and getHeight() tell to us).
DisplayMetrics metrics = new DisplayMetrics();
display.getRealMetrics(metrics);
int width = Math.max(metrics.widthPixels, metrics.heightPixels);
int height = Math.min(metrics.widthPixels, metrics.heightPixels);
addNativeResolutionEntries(width, height, false);
}
else {
// On Android 4.1, we have to resort to reflection to invoke hidden APIs
// to get the real screen dimensions.
try {
Method getRawHeightFunc = Display.class.getMethod("getRawHeight");
Method getRawWidthFunc = Display.class.getMethod("getRawWidth");
int width = (Integer) getRawWidthFunc.invoke(display);
int height = (Integer) getRawHeightFunc.invoke(display);
addNativeResolutionEntries(Math.max(width, height), Math.min(width, height), false);
} catch (Exception e) {
e.printStackTrace();
}
}
if (!PreferenceConfiguration.readPreferences(this.getActivity()).unlockFps) {
// We give some extra room in case the FPS is rounded down
@ -562,49 +548,29 @@ public class StreamSettings extends Activity {
}
addNativeFrameRateEntry(maxSupportedFps);
// Android L introduces proper 7.1 surround sound support. Remove the 7.1 option
// for earlier versions of Android to prevent AudioTrack initialization issues.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
LimeLog.info("Excluding 7.1 surround sound option based on OS");
removeValue(PreferenceConfiguration.AUDIO_CONFIG_PREF_STRING, "71", new Runnable() {
@Override
public void run() {
setValue(PreferenceConfiguration.AUDIO_CONFIG_PREF_STRING, "51");
}
});
}
// Android L introduces the drop duplicate behavior of releaseOutputBuffer()
// that the unlock FPS option relies on to not massively increase latency.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
LimeLog.info("Excluding unlock FPS toggle based on OS");
PreferenceCategory category =
(PreferenceCategory) findPreference("category_advanced_settings");
category.removePreference(findPreference("checkbox_unlock_fps"));
}
else {
findPreference(PreferenceConfiguration.UNLOCK_FPS_STRING).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// HACK: We need to let the preference change succeed before reinitializing to ensure
// it's reflected in the new layout.
final Handler h = new Handler();
h.postDelayed(new Runnable() {
@Override
public void run() {
// Ensure the activity is still open when this timeout expires
StreamSettings settingsActivity = (StreamSettings)SettingsFragment.this.getActivity();
if (settingsActivity != null) {
settingsActivity.reloadSettings();
}
findPreference(PreferenceConfiguration.UNLOCK_FPS_STRING).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// HACK: We need to let the preference change succeed before reinitializing to ensure
// it's reflected in the new layout.
final Handler h = new Handler();
h.postDelayed(new Runnable() {
@Override
public void run() {
// Ensure the activity is still open when this timeout expires
StreamSettings settingsActivity = (StreamSettings) SettingsFragment.this.getActivity();
if (settingsActivity != null) {
settingsActivity.reloadSettings();
}
}, 500);
}
}, 500);
// Allow the original preference change to take place
return true;
}
});
}
// Allow the original preference change to take place
return true;
}
});
// Remove HDR preference for devices below Nougat
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
@ -623,6 +589,7 @@ public class StreamSettings extends Activity {
for (int hdrType : hdrCaps.getSupportedHdrTypes()) {
if (hdrType == Display.HdrCapabilities.HDR_TYPE_HDR10) {
foundHdr10 = true;
break;
}
}
}

View File

@ -21,7 +21,6 @@ public class WebLauncherPreference extends Preference {
initialize(attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public WebLauncherPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(attrs);

View File

@ -30,6 +30,6 @@ public class AdapterFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
callbacks.receiveAbsListView((AbsListView) getView().findViewById(R.id.fragmentView));
callbacks.receiveAbsListView(getView().findViewById(R.id.fragmentView));
}
}

View File

@ -30,7 +30,6 @@ public class StreamView extends SurfaceView {
super(context, attrs, defStyleAttr);
}
@TargetApi(21)
public StreamView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}

View File

@ -20,7 +20,7 @@ public class NetHelper {
}
}
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
else {
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
if (activeNetworkInfo != null) {
return activeNetworkInfo.getType() == ConnectivityManager.TYPE_VPN;

View File

@ -1,4 +1,4 @@
# Application.mk for Moonlight
# Our minimum version is Android 4.1
APP_PLATFORM := android-16
# Our minimum version is Android 5.0
APP_PLATFORM := android-21