diff --git a/.travis.yml b/.travis.yml index aaa7fc6d..e9e1dda9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,8 @@ android: components: - tools - platform-tools - - build-tools-29.0.3 - - android-29 + - build-tools-30.0.0 + - android-30 before_install: - sdkmanager --list diff --git a/app/build.gradle b/app/build.gradle index e711f73e..8ca63080 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { minSdkVersion 16 - targetSdkVersion 29 + targetSdkVersion 30 versionName "9.5.1" versionCode = 225 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0a219c23..fbbb7dae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -120,7 +120,8 @@ android:resizeableActivity="true" android:launchMode="singleTask" android:excludeFromRecents="true" - android:theme="@style/StreamTheme"> + android:theme="@style/StreamTheme" + android:preferMinimalPostProcessing="true"> diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 345e0b9a..813b8ce3 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -63,6 +63,7 @@ import android.view.Display; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; import android.view.View.OnGenericMotionListener; @@ -212,11 +213,17 @@ 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) { + if (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; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + getWindow().getAttributes().layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + } + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + getWindow().getAttributes().layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } } // Listen for events on the game surface @@ -609,29 +616,6 @@ public class Game extends Activity implements SurfaceHolder.Callback, } } - // FIXME: Remove when Android R SDK is finalized - private static void setPreferMinimalPostProcessingWithReflection(WindowManager.LayoutParams windowLayoutParams, boolean isPreferred) { - // Build.VERSION.PREVIEW_SDK_INT was added in M - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && Build.VERSION.PREVIEW_SDK_INT == 0) { - // Don't attempt this reflection unless on Android R Developer Preview - return; - } - } - else { - return; - } - - try { - Field field = windowLayoutParams.getClass().getDeclaredField("preferMinimalPostProcessing"); - field.set(windowLayoutParams, isPreferred); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - private float prepareDisplayForRendering() { Display display = getWindowManager().getDefaultDisplay(); WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes(); @@ -709,7 +693,9 @@ public class Game extends Activity implements SurfaceHolder.Callback, } // Enable HDMI ALLM (game mode) on Android R - setPreferMinimalPostProcessingWithReflection(windowLayoutParams, true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + windowLayoutParams.preferMinimalPostProcessing = true; + } // Apply the display mode change getWindow().setAttributes(windowLayoutParams); @@ -1693,6 +1679,11 @@ public class Game extends Activity implements SurfaceHolder.Callback, @Override public void surfaceCreated(SurfaceHolder holder) { surfaceCreated = true; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Tell the OS about our frame rate to allow it to adapt the display refresh rate appropriately + holder.getSurface().setFrameRate(prefConfig.fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE); + } } @Override diff --git a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java index e41fe541..15246ea4 100644 --- a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java +++ b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java @@ -318,8 +318,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer { videoFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, height); } - if (lowLatency) { - videoFormat.setInteger(MediaCodecHelper.KEY_LOW_LATENCY, 1); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && lowLatency) { + videoFormat.setInteger(MediaFormat.KEY_LOW_LATENCY, 1); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Set the Qualcomm vendor low latency extension if the Android R option is unavailable diff --git a/app/src/main/java/com/limelight/binding/video/MediaCodecHelper.java b/app/src/main/java/com/limelight/binding/video/MediaCodecHelper.java index 54c52823..a9cac570 100644 --- a/app/src/main/java/com/limelight/binding/video/MediaCodecHelper.java +++ b/app/src/main/java/com/limelight/binding/video/MediaCodecHelper.java @@ -40,10 +40,6 @@ public class MediaCodecHelper { private static final List blacklisted59FpsDecoderPrefixes; private static final List qualcommDecoderPrefixes; - // FIXME: Remove when Android R SDK is finalized - public static final String FEATURE_LowLatency = "low-latency"; - public static final String KEY_LOW_LATENCY = "low-latency"; - private static boolean isLowEndSnapdragon = false; private static boolean initialized = false; @@ -343,10 +339,9 @@ public class MediaCodecHelper { } public static boolean decoderSupportsLowLatency(MediaCodecInfo decoderInfo, String mimeType) { - // KitKat added CodecCapabilities.isFeatureSupported() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { try { - if (decoderInfo.getCapabilitiesForType(mimeType).isFeatureSupported(FEATURE_LowLatency)) { + if (decoderInfo.getCapabilitiesForType(mimeType).isFeatureSupported(CodecCapabilities.FEATURE_LowLatency)) { LimeLog.info("Low latency decoding mode supported (FEATURE_LowLatency)"); return true; } diff --git a/app/src/main/java/com/limelight/utils/HelpLauncher.java b/app/src/main/java/com/limelight/utils/HelpLauncher.java index 9d99b68a..24402ae9 100644 --- a/app/src/main/java/com/limelight/utils/HelpLauncher.java +++ b/app/src/main/java/com/limelight/utils/HelpLauncher.java @@ -3,41 +3,22 @@ package com.limelight.utils; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.net.Uri; import com.limelight.HelpActivity; public class HelpLauncher { - - private static boolean isKnownBrowser(Context context, Intent i) { - ResolveInfo resolvedActivity = context.getPackageManager().resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY); - if (resolvedActivity == null) { - // No browser - return false; - } - - String name = resolvedActivity.activityInfo.name; - if (name == null) { - return false; - } - - name = name.toLowerCase(); - return name.contains("chrome") || name.contains("firefox"); - } - private static void launchUrl(Context context, String url) { // Try to launch the default browser try { Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(url)); - // Several Android TV devices will lie and say they do have a browser - // even though the OS just shows an error dialog if we try to use it. We need to - // be a bit more clever on these devices and detect if the browser is a legitimate - // browser or just a fake error message activity. - if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK) || - isKnownBrowser(context, i)) { + // Several Android TV devices will lie and say they do have a browser even though the OS + // just shows an error dialog if we try to use it. We used to try to be clever and check + // the package name of the resolved intent, but it's not worth it anymore with Android 11's + // package visibility changes. We'll just always use the WebView on Android TV. + if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { context.startActivity(i); return; }