mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-01 23:35:28 +00:00
WIP: Resolution dialog
This commit is contained in:
parent
00415aac79
commit
41592b2ef4
@ -220,7 +220,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// Enter landscape unless we're on a square screen
|
||||
setPreferredOrientationForCurrentDisplay();
|
||||
|
||||
if (prefConfig.stretchVideo || shouldIgnoreInsetsForResolution(prefConfig.width, prefConfig.height)) {
|
||||
if (prefConfig.stretchVideo || prefConfig.resolution.fullScreen) {
|
||||
// Allow the activity to layout under notches if the fill-screen option
|
||||
// was turned on by the user or it's a full-screen native resolution
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
@ -456,7 +456,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
|
||||
StreamConfiguration config = new StreamConfiguration.Builder()
|
||||
.setResolution(prefConfig.width, prefConfig.height)
|
||||
.setResolution(prefConfig.resolution.width, prefConfig.resolution.height)
|
||||
.setLaunchRefreshRate(prefConfig.fps)
|
||||
.setRefreshRate(chosenFrameRate)
|
||||
.setApp(new NvApp(appName != null ? appName : "app", appId, appSupportsHdr))
|
||||
@ -551,12 +551,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
|
||||
// For native resolution, we will lock the orientation to the one that matches the specified resolution
|
||||
if (PreferenceConfiguration.isNativeResolution(prefConfig.width, prefConfig.height)) {
|
||||
if (prefConfig.width > prefConfig.height) {
|
||||
desiredOrientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||
if (prefConfig.resolution.type == PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE) {
|
||||
if (prefConfig.resolution.portrait) {
|
||||
desiredOrientation = Configuration.ORIENTATION_PORTRAIT;
|
||||
}
|
||||
else {
|
||||
desiredOrientation = Configuration.ORIENTATION_PORTRAIT;
|
||||
desiredOrientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,7 +649,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
private PictureInPictureParams getPictureInPictureParams(boolean autoEnter) {
|
||||
PictureInPictureParams.Builder builder =
|
||||
new PictureInPictureParams.Builder()
|
||||
.setAspectRatio(new Rational(prefConfig.width, prefConfig.height))
|
||||
.setAspectRatio(new Rational(prefConfig.resolution.width, prefConfig.resolution.height))
|
||||
.setSourceRectHint(new Rect(
|
||||
streamView.getLeft(), streamView.getTop(),
|
||||
streamView.getRight(), streamView.getBottom()));
|
||||
@ -771,26 +771,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
Math.round(refreshRate) % prefConfig.fps <= 3;
|
||||
}
|
||||
|
||||
private boolean shouldIgnoreInsetsForResolution(int width, int height) {
|
||||
// Never ignore insets for non-native resolutions
|
||||
if (!PreferenceConfiguration.isNativeResolution(width, height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
for (Display.Mode candidate : display.getSupportedModes()) {
|
||||
// Ignore insets if this is an exact match for the display resolution
|
||||
if ((width == candidate.getPhysicalWidth() && height == candidate.getPhysicalHeight()) ||
|
||||
(height == candidate.getPhysicalWidth() && width == candidate.getPhysicalHeight())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean mayReduceRefreshRate() {
|
||||
return prefConfig.framePacing == PreferenceConfiguration.FRAME_PACING_CAP_FPS ||
|
||||
prefConfig.framePacing == PreferenceConfiguration.FRAME_PACING_MAX_SMOOTHNESS ||
|
||||
@ -805,7 +785,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// On M, we can explicitly set the optimal display mode
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Display.Mode bestMode = display.getMode();
|
||||
boolean isNativeResolutionStream = PreferenceConfiguration.isNativeResolution(prefConfig.width, prefConfig.height);
|
||||
boolean refreshRateIsGood = isRefreshRateGoodMatch(bestMode.getRefreshRate());
|
||||
boolean refreshRateIsEqual = isRefreshRateEqualMatch(bestMode.getRefreshRate());
|
||||
|
||||
@ -813,13 +792,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
boolean refreshRateReduced = candidate.getRefreshRate() < bestMode.getRefreshRate();
|
||||
boolean resolutionReduced = candidate.getPhysicalWidth() < bestMode.getPhysicalWidth() ||
|
||||
candidate.getPhysicalHeight() < bestMode.getPhysicalHeight();
|
||||
boolean resolutionFitsStream = candidate.getPhysicalWidth() >= prefConfig.width &&
|
||||
candidate.getPhysicalHeight() >= prefConfig.height;
|
||||
boolean resolutionFitsStream = candidate.getPhysicalWidth() >= prefConfig.resolution.width &&
|
||||
candidate.getPhysicalHeight() >= prefConfig.resolution.height;
|
||||
|
||||
LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+
|
||||
candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate());
|
||||
|
||||
if (candidate.getPhysicalWidth() > 4096 && prefConfig.width <= 4096) {
|
||||
if (candidate.getPhysicalWidth() > 4096 && prefConfig.resolution.width <= 4096) {
|
||||
// Avoid resolutions options above 4K to be safe
|
||||
continue;
|
||||
}
|
||||
@ -827,7 +806,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
// On non-4K streams, we force the resolution to never change unless it's above
|
||||
// 60 FPS, which may require a resolution reduction due to HDMI bandwidth limitations,
|
||||
// or it's a native resolution stream.
|
||||
if (prefConfig.width < 3840 && prefConfig.fps <= 60 && !isNativeResolutionStream) {
|
||||
if (prefConfig.resolution.width < 3840 && prefConfig.fps <= 60 &&
|
||||
prefConfig.resolution.type != PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE) {
|
||||
if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() ||
|
||||
display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) {
|
||||
continue;
|
||||
@ -940,7 +920,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
display.getSize(screenSize);
|
||||
|
||||
double screenAspectRatio = ((double)screenSize.y) / screenSize.x;
|
||||
double streamAspectRatio = ((double)prefConfig.height) / prefConfig.width;
|
||||
double streamAspectRatio = ((double)prefConfig.resolution.height) / prefConfig.resolution.width;
|
||||
if (Math.abs(screenAspectRatio - streamAspectRatio) < 0.001) {
|
||||
LimeLog.info("Stream has compatible aspect ratio with output display");
|
||||
aspectRatioMatch = true;
|
||||
@ -949,11 +929,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
|
||||
if (prefConfig.stretchVideo || aspectRatioMatch) {
|
||||
// Set the surface to the size of the video
|
||||
streamView.getHolder().setFixedSize(prefConfig.width, prefConfig.height);
|
||||
streamView.getHolder().setFixedSize(prefConfig.resolution.width, prefConfig.resolution.height);
|
||||
}
|
||||
else {
|
||||
// Set the surface to scale based on the aspect ratio of the stream
|
||||
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
||||
streamView.setDesiredAspectRatio((double)prefConfig.resolution.width / (double)prefConfig.resolution.height);
|
||||
}
|
||||
|
||||
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION) ||
|
||||
|
@ -134,7 +134,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private boolean decoderCanMeetPerformancePoint(MediaCodecInfo.VideoCapabilities caps, PreferenceConfiguration prefs) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
MediaCodecInfo.VideoCapabilities.PerformancePoint targetPerfPoint = new MediaCodecInfo.VideoCapabilities.PerformancePoint(prefs.width, prefs.height, prefs.fps);
|
||||
MediaCodecInfo.VideoCapabilities.PerformancePoint targetPerfPoint = new MediaCodecInfo.VideoCapabilities.PerformancePoint(prefs.resolution.width, prefs.resolution.height, prefs.fps);
|
||||
List<MediaCodecInfo.VideoCapabilities.PerformancePoint> perfPoints = caps.getSupportedPerformancePoints();
|
||||
if (perfPoints != null) {
|
||||
for (MediaCodecInfo.VideoCapabilities.PerformancePoint perfPoint : perfPoints) {
|
||||
@ -155,7 +155,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
||||
try {
|
||||
// We'll ask the decoder what it can do for us at this resolution and see if our
|
||||
// requested frame rate falls below or inside the range of achievable frame rates.
|
||||
Range<Double> fpsRange = caps.getAchievableFrameRatesFor(prefs.width, prefs.height);
|
||||
Range<Double> fpsRange = caps.getAchievableFrameRatesFor(prefs.resolution.width, prefs.resolution.height);
|
||||
if (fpsRange != null) {
|
||||
return prefs.fps <= fpsRange.getUpper();
|
||||
}
|
||||
@ -170,7 +170,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
||||
// As a last resort, we will use areSizeAndRateSupported() which is explicitly NOT a
|
||||
// performance metric, but it can work at least for the purpose of determining if
|
||||
// the codec is going to die when given a stream with the specified settings.
|
||||
return caps.areSizeAndRateSupported(prefs.width, prefs.height, prefs.fps);
|
||||
return caps.areSizeAndRateSupported(prefs.resolution.width, prefs.resolution.height, prefs.fps);
|
||||
}
|
||||
|
||||
private boolean decoderCanMeetPerformancePointWithHevcAndNotAvc(MediaCodecInfo avcDecoderInfo, MediaCodecInfo hevcDecoderInfo, PreferenceConfiguration prefs) {
|
||||
@ -211,7 +211,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
||||
LimeLog.info("Forcing HEVC enabled for HDR streaming");
|
||||
}
|
||||
// > 4K streaming also requires HEVC, so force it on there too.
|
||||
else if (prefs.width > 4096 || prefs.height > 4096) {
|
||||
else if (prefs.resolution.width > 4096 || prefs.resolution.height > 4096) {
|
||||
LimeLog.info("Forcing HEVC enabled for over 4K streaming");
|
||||
}
|
||||
// Use HEVC if the H.264 decoder is unable to meet the performance point
|
||||
@ -271,7 +271,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
|
||||
int hevcOptimalSlicesPerFrame = 0;
|
||||
if (avcDecoder != null) {
|
||||
directSubmit = MediaCodecHelper.decoderCanDirectSubmit(avcDecoder.getName());
|
||||
refFrameInvalidationAvc = MediaCodecHelper.decoderSupportsRefFrameInvalidationAvc(avcDecoder.getName(), prefs.height);
|
||||
refFrameInvalidationAvc = MediaCodecHelper.decoderSupportsRefFrameInvalidationAvc(avcDecoder.getName(), prefs.resolution.height);
|
||||
avcOptimalSlicesPerFrame = MediaCodecHelper.getDecoderOptimalSlicesPerFrame(avcDecoder.getName());
|
||||
|
||||
if (directSubmit) {
|
||||
|
@ -90,15 +90,15 @@ public class PreferenceConfiguration {
|
||||
public static final int FRAME_PACING_CAP_FPS = 2;
|
||||
public static final int FRAME_PACING_MAX_SMOOTHNESS = 3;
|
||||
|
||||
public static final String RES_360P = "640x360";
|
||||
public static final String RES_480P = "854x480";
|
||||
public static final String RES_720P = "1280x720";
|
||||
public static final String RES_1080P = "1920x1080";
|
||||
public static final String RES_1440P = "2560x1440";
|
||||
public static final String RES_4K = "3840x2160";
|
||||
public static final String RES_NATIVE = "Native";
|
||||
public static final String RES_360P = "s_640x360";
|
||||
public static final String RES_480P = "s_854x480";
|
||||
public static final String RES_720P = "s_1280x720";
|
||||
public static final String RES_1080P = "s_1920x1080";
|
||||
public static final String RES_1440P = "s_2560x1440";
|
||||
public static final String RES_4K = "s_3840x2160";
|
||||
|
||||
public int width, height, fps;
|
||||
public StreamResolution resolution;
|
||||
public int fps;
|
||||
public int bitrate;
|
||||
public int videoFormat;
|
||||
public int deadzonePercentage;
|
||||
@ -125,7 +125,9 @@ public class PreferenceConfiguration {
|
||||
public boolean enableAudioFx;
|
||||
public boolean reduceRefreshRate;
|
||||
|
||||
public static boolean isNativeResolution(int width, int height) {
|
||||
// This is used only for migration to the new type code format of resolution strings
|
||||
// It doesn't properly handle custom resolutions!
|
||||
private static boolean legacyIsNativeResolution(int width, int height) {
|
||||
// It's not a native resolution if it matches an existing resolution option
|
||||
if (width == 640 && height == 360) {
|
||||
return false;
|
||||
@ -175,6 +177,8 @@ public class PreferenceConfiguration {
|
||||
}
|
||||
|
||||
private static String convertFromLegacyResolutionString(String resString) {
|
||||
if (!resString.contains("x")) {
|
||||
// Convert from hardcoded strings to strings with dimensions embedded
|
||||
if (resString.equalsIgnoreCase("360p")) {
|
||||
return RES_360P;
|
||||
}
|
||||
@ -198,36 +202,22 @@ public class PreferenceConfiguration {
|
||||
return RES_720P;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getWidthFromResolutionString(String resString) {
|
||||
return Integer.parseInt(resString.split("x")[0]);
|
||||
else if (Character.isDigit(resString.charAt(0))) {
|
||||
// Convert to type code prefix format
|
||||
String[] dims = resString.split("x");
|
||||
int width = Integer.parseInt(dims[0]);
|
||||
int height = Integer.parseInt(dims[1]);
|
||||
return new StreamResolution(width, height,
|
||||
legacyIsNativeResolution(width, height) ? StreamResolution.ResolutionType.NATIVE : StreamResolution.ResolutionType.STANDARD)
|
||||
.toPrefString();
|
||||
}
|
||||
|
||||
private static int getHeightFromResolutionString(String resString) {
|
||||
return Integer.parseInt(resString.split("x")[1]);
|
||||
}
|
||||
|
||||
private static String getResolutionString(int width, int height) {
|
||||
switch (height) {
|
||||
case 360:
|
||||
return RES_360P;
|
||||
case 480:
|
||||
return RES_480P;
|
||||
default:
|
||||
case 720:
|
||||
return RES_720P;
|
||||
case 1080:
|
||||
return RES_1080P;
|
||||
case 1440:
|
||||
return RES_1440P;
|
||||
case 2160:
|
||||
return RES_4K;
|
||||
else {
|
||||
return resString;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getDefaultBitrate(String resString, String fpsString) {
|
||||
int width = getWidthFromResolutionString(resString);
|
||||
int height = getHeightFromResolutionString(resString);
|
||||
StreamResolution res = new StreamResolution(resString);
|
||||
int fps = Integer.parseInt(fpsString);
|
||||
|
||||
// This table prefers 16:10 resolutions because they are
|
||||
@ -238,23 +228,23 @@ public class PreferenceConfiguration {
|
||||
// This logic is shamelessly stolen from Moonlight Qt:
|
||||
// https://github.com/moonlight-stream/moonlight-qt/blob/master/app/settings/streamingpreferences.cpp
|
||||
|
||||
if (width * height <= 640 * 360) {
|
||||
if (res.width * res.height <= 640 * 360) {
|
||||
return (int)(1000 * (fps / 30.0));
|
||||
}
|
||||
else if (width * height <= 854 * 480) {
|
||||
else if (res.width * res.height <= 854 * 480) {
|
||||
return (int)(1500 * (fps / 30.0));
|
||||
}
|
||||
// This covers 1280x720 and 1280x800 too
|
||||
else if (width * height <= 1366 * 768) {
|
||||
else if (res.width * res.height <= 1366 * 768) {
|
||||
return (int)(5000 * (fps / 30.0));
|
||||
}
|
||||
else if (width * height <= 1920 * 1200) {
|
||||
else if (res.width * res.height <= 1920 * 1200) {
|
||||
return (int)(10000 * (fps / 30.0));
|
||||
}
|
||||
else if (width * height <= 2560 * 1600) {
|
||||
else if (res.width * res.height <= 2560 * 1600) {
|
||||
return (int)(20000 * (fps / 30.0));
|
||||
}
|
||||
else /* if (width * height <= 3840 * 2160) */ {
|
||||
else /* if (res.width * res.height <= 3840 * 2160) */ {
|
||||
return (int)(40000 * (fps / 30.0));
|
||||
}
|
||||
}
|
||||
@ -381,55 +371,46 @@ public class PreferenceConfiguration {
|
||||
String str = prefs.getString(LEGACY_RES_FPS_PREF_STRING, null);
|
||||
if (str != null) {
|
||||
if (str.equals("360p30")) {
|
||||
config.width = 640;
|
||||
config.height = 360;
|
||||
config.resolution = new StreamResolution(640, 360, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 30;
|
||||
}
|
||||
else if (str.equals("360p60")) {
|
||||
config.width = 640;
|
||||
config.height = 360;
|
||||
config.resolution = new StreamResolution(640, 360, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 60;
|
||||
}
|
||||
else if (str.equals("720p30")) {
|
||||
config.width = 1280;
|
||||
config.height = 720;
|
||||
config.resolution = new StreamResolution(1280, 720, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 30;
|
||||
}
|
||||
else if (str.equals("720p60")) {
|
||||
config.width = 1280;
|
||||
config.height = 720;
|
||||
config.resolution = new StreamResolution(1280, 720, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 60;
|
||||
}
|
||||
else if (str.equals("1080p30")) {
|
||||
config.width = 1920;
|
||||
config.height = 1080;
|
||||
config.resolution = new StreamResolution(1920, 1080, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 30;
|
||||
}
|
||||
else if (str.equals("1080p60")) {
|
||||
config.width = 1920;
|
||||
config.height = 1080;
|
||||
config.resolution = new StreamResolution(1920, 1080, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 60;
|
||||
}
|
||||
else if (str.equals("4K30")) {
|
||||
config.width = 3840;
|
||||
config.height = 2160;
|
||||
config.resolution = new StreamResolution(3840, 2160, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 30;
|
||||
}
|
||||
else if (str.equals("4K60")) {
|
||||
config.width = 3840;
|
||||
config.height = 2160;
|
||||
config.resolution = new StreamResolution(3840, 2160, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 60;
|
||||
}
|
||||
else {
|
||||
// Should never get here
|
||||
config.width = 1280;
|
||||
config.height = 720;
|
||||
config.resolution = new StreamResolution(1280, 720, StreamResolution.ResolutionType.STANDARD);
|
||||
config.fps = 60;
|
||||
}
|
||||
|
||||
prefs.edit()
|
||||
.remove(LEGACY_RES_FPS_PREF_STRING)
|
||||
.putString(RESOLUTION_PREF_STRING, getResolutionString(config.width, config.height))
|
||||
.putString(RESOLUTION_PREF_STRING, config.resolution.toPrefString())
|
||||
.putString(FPS_PREF_STRING, ""+config.fps)
|
||||
.apply();
|
||||
}
|
||||
@ -438,13 +419,12 @@ public class PreferenceConfiguration {
|
||||
String resStr = prefs.getString(RESOLUTION_PREF_STRING, PreferenceConfiguration.DEFAULT_RESOLUTION);
|
||||
|
||||
// Convert legacy resolution strings to the new style
|
||||
if (!resStr.contains("x")) {
|
||||
if (Character.isDigit(resStr.charAt(0)) || !resStr.contains("x")) {
|
||||
resStr = PreferenceConfiguration.convertFromLegacyResolutionString(resStr);
|
||||
prefs.edit().putString(RESOLUTION_PREF_STRING, resStr).apply();
|
||||
}
|
||||
|
||||
config.width = PreferenceConfiguration.getWidthFromResolutionString(resStr);
|
||||
config.height = PreferenceConfiguration.getHeightFromResolutionString(resStr);
|
||||
config.resolution = new StreamResolution(resStr);
|
||||
config.fps = Integer.parseInt(prefs.getString(FPS_PREF_STRING, PreferenceConfiguration.DEFAULT_FPS));
|
||||
}
|
||||
|
||||
@ -508,4 +488,112 @@ public class PreferenceConfiguration {
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public static class StreamResolution {
|
||||
public int width;
|
||||
public int height;
|
||||
public ResolutionType type;
|
||||
public boolean portrait;
|
||||
public boolean fullScreen;
|
||||
|
||||
private static final char RES_START_DELIMITER = '_';
|
||||
private static final char RES_DIM_DELIMITER = 'x';
|
||||
|
||||
private static final char TYPE_CODE_STANDARD = 's';
|
||||
private static final char TYPE_CODE_NATIVE = 'n';
|
||||
|
||||
private static final char OPTION_CODE_PORTRAIT = 'p';
|
||||
private static final char OPTION_CODE_FULL_SCREEN = 'f';
|
||||
|
||||
public enum ResolutionType {
|
||||
STANDARD,
|
||||
NATIVE
|
||||
}
|
||||
|
||||
private StreamResolution(int width, int height, ResolutionType type) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.type = type;
|
||||
|
||||
// Just guess for the options, since we don't know for sure
|
||||
this.portrait = height > width;
|
||||
this.fullScreen = false;
|
||||
}
|
||||
|
||||
public StreamResolution(int width, int height, ResolutionType type, boolean portrait, boolean fullScreen) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.type = type;
|
||||
this.portrait = portrait;
|
||||
this.fullScreen = fullScreen;
|
||||
}
|
||||
|
||||
StreamResolution(String resString) {
|
||||
// Type code is the first character in the string
|
||||
switch (resString.charAt(0)) {
|
||||
case TYPE_CODE_NATIVE:
|
||||
this.type = StreamResolution.ResolutionType.NATIVE;
|
||||
break;
|
||||
case TYPE_CODE_STANDARD:
|
||||
this.type = StreamResolution.ResolutionType.STANDARD;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid type code: " + resString.charAt(0));
|
||||
}
|
||||
|
||||
// Parse options until we reach the resolution start delimiter
|
||||
int currentIndex = 1;
|
||||
while (resString.charAt(currentIndex) != RES_START_DELIMITER) {
|
||||
switch (resString.charAt(currentIndex)) {
|
||||
case OPTION_CODE_PORTRAIT:
|
||||
portrait = true;
|
||||
break;
|
||||
case OPTION_CODE_FULL_SCREEN:
|
||||
fullScreen = true;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid option code: " + resString.charAt(currentIndex));
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
// Skip the type code before splitting
|
||||
String[] dims = resString.substring(currentIndex).split(""+RES_DIM_DELIMITER);
|
||||
this.width = Integer.parseInt(dims[0]);
|
||||
this.height = Integer.parseInt(dims[1]);
|
||||
}
|
||||
|
||||
public String toPrefString() {
|
||||
String str = "";
|
||||
|
||||
// Start with the type code
|
||||
switch (type) {
|
||||
case NATIVE:
|
||||
str += TYPE_CODE_NATIVE;
|
||||
break;
|
||||
case STANDARD:
|
||||
str += TYPE_CODE_STANDARD;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid type: "+type);
|
||||
}
|
||||
|
||||
// Now any option codes that may apply
|
||||
if (portrait) {
|
||||
str += OPTION_CODE_PORTRAIT;
|
||||
}
|
||||
if (fullScreen) {
|
||||
str += OPTION_CODE_FULL_SCREEN;
|
||||
}
|
||||
|
||||
// And finally the dimensions
|
||||
str += RES_START_DELIMITER;
|
||||
str += width;
|
||||
str += RES_DIM_DELIMITER;
|
||||
str += height;
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,19 +122,21 @@ public class StreamSettings extends Activity {
|
||||
}
|
||||
|
||||
public static class SettingsFragment extends PreferenceFragment {
|
||||
private int nativeResolutionStartIndex = Integer.MAX_VALUE;
|
||||
|
||||
private void setValue(String preferenceKey, String value) {
|
||||
ListPreference pref = (ListPreference) findPreference(preferenceKey);
|
||||
|
||||
pref.setValue(value);
|
||||
}
|
||||
|
||||
private void addNativeResolutionEntry(int nativeWidth, int nativeHeight, boolean insetsRemoved, boolean portrait) {
|
||||
private void addResolutionEntry(int width, int height, PreferenceConfiguration.StreamResolution.ResolutionType type,
|
||||
boolean insetsRemoved, boolean portrait) {
|
||||
ListPreference pref = (ListPreference) findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING);
|
||||
PreferenceConfiguration.StreamResolution newRes =
|
||||
new PreferenceConfiguration.StreamResolution(width, height, type, portrait, insetsRemoved);
|
||||
|
||||
String newName;
|
||||
|
||||
if (type == PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE) {
|
||||
if (insetsRemoved) {
|
||||
newName = getResources().getString(R.string.resolution_prefix_native_fullscreen);
|
||||
}
|
||||
@ -142,7 +144,7 @@ public class StreamSettings extends Activity {
|
||||
newName = getResources().getString(R.string.resolution_prefix_native);
|
||||
}
|
||||
|
||||
if (PreferenceConfiguration.isSquarishScreen(nativeWidth, nativeHeight)) {
|
||||
if (PreferenceConfiguration.isSquarishScreen(width, height)) {
|
||||
if (portrait) {
|
||||
newName += " " + getResources().getString(R.string.resolution_prefix_native_portrait);
|
||||
}
|
||||
@ -150,16 +152,19 @@ public class StreamSettings extends Activity {
|
||||
newName += " " + getResources().getString(R.string.resolution_prefix_native_landscape);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
newName = null;
|
||||
}
|
||||
|
||||
newName += " ("+nativeWidth+"x"+nativeHeight+")";
|
||||
|
||||
String newValue = nativeWidth+"x"+nativeHeight;
|
||||
newName += " ("+width+"x"+height+")";
|
||||
|
||||
CharSequence[] values = pref.getEntryValues();
|
||||
|
||||
// Check if the native resolution is already present
|
||||
// Check if the new resolution is already present
|
||||
for (CharSequence value : values) {
|
||||
if (newValue.equals(value.toString())) {
|
||||
PreferenceConfiguration.StreamResolution existingResolution = new PreferenceConfiguration.StreamResolution(value.toString());
|
||||
if (existingResolution.width == newRes.width && existingResolution.height == newRes.height) {
|
||||
// It is present in the default list, so don't add it again
|
||||
return;
|
||||
}
|
||||
@ -168,23 +173,23 @@ public class StreamSettings extends Activity {
|
||||
CharSequence[] newEntries = Arrays.copyOf(pref.getEntries(), pref.getEntries().length + 1);
|
||||
CharSequence[] newValues = Arrays.copyOf(values, values.length + 1);
|
||||
|
||||
// Add the new native option
|
||||
// Add the new resolution option
|
||||
newEntries[newEntries.length - 1] = newName;
|
||||
newValues[newValues.length - 1] = newValue;
|
||||
newValues[newValues.length - 1] = newRes.toPrefString();
|
||||
|
||||
pref.setEntries(newEntries);
|
||||
pref.setEntryValues(newValues);
|
||||
|
||||
if (newValues.length - 1 < nativeResolutionStartIndex) {
|
||||
nativeResolutionStartIndex = newValues.length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void addNativeResolutionEntries(int nativeWidth, int nativeHeight, boolean insetsRemoved) {
|
||||
if (PreferenceConfiguration.isSquarishScreen(nativeWidth, nativeHeight)) {
|
||||
addNativeResolutionEntry(nativeHeight, nativeWidth, insetsRemoved, true);
|
||||
addResolutionEntry(nativeHeight, nativeWidth,
|
||||
PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE,
|
||||
insetsRemoved, true);
|
||||
}
|
||||
addNativeResolutionEntry(nativeWidth, nativeHeight, insetsRemoved, false);
|
||||
addResolutionEntry(nativeWidth, nativeHeight,
|
||||
PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE,
|
||||
insetsRemoved, false);
|
||||
}
|
||||
|
||||
private void removeValue(String preferenceKey, String value, Runnable onMatched) {
|
||||
@ -608,20 +613,10 @@ public class StreamSettings extends Activity {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
||||
String valueStr = (String) newValue;
|
||||
|
||||
// Detect if this value is the native resolution option
|
||||
CharSequence[] values = ((ListPreference)preference).getEntryValues();
|
||||
boolean isNativeRes = true;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
// Look for a match prior to the start of the native resolution entries
|
||||
if (valueStr.equals(values[i].toString()) && i < nativeResolutionStartIndex) {
|
||||
isNativeRes = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PreferenceConfiguration.StreamResolution resolution = new PreferenceConfiguration.StreamResolution(valueStr);
|
||||
|
||||
// If this is native resolution, show the warning dialog
|
||||
if (isNativeRes) {
|
||||
if (resolution.type == PreferenceConfiguration.StreamResolution.ResolutionType.NATIVE) {
|
||||
Dialog.displayDialog(getActivity(),
|
||||
getResources().getString(R.string.title_native_res_dialog),
|
||||
getResources().getString(R.string.text_native_res_dialog),
|
||||
|
@ -9,14 +9,13 @@
|
||||
<item>@string/resolution_4k</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Keep this in sync with PreferenceConfiguration.isNativeResolution()! -->
|
||||
<string-array name="resolution_values" translatable="false">
|
||||
<item>640x360</item>
|
||||
<item>854x480</item>
|
||||
<item>1280x720</item>
|
||||
<item>1920x1080</item>
|
||||
<item>2560x1440</item>
|
||||
<item>3840x2160</item>
|
||||
<item>s_640x360</item>
|
||||
<item>s_854x480</item>
|
||||
<item>s_1280x720</item>
|
||||
<item>s_1920x1080</item>
|
||||
<item>s_2560x1440</item>
|
||||
<item>s_3840x2160</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="fps_names">
|
||||
|
Loading…
x
Reference in New Issue
Block a user