mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-25 22:13:04 +00:00
433 lines
20 KiB
Java
433 lines
20 KiB
Java
package com.limelight.preferences;
|
|
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.media.MediaCodecInfo;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.app.Activity;
|
|
import android.os.Handler;
|
|
import android.os.Vibrator;
|
|
import android.preference.ListPreference;
|
|
import android.preference.Preference;
|
|
import android.preference.PreferenceCategory;
|
|
import android.preference.PreferenceFragment;
|
|
import android.preference.PreferenceManager;
|
|
import android.preference.PreferenceScreen;
|
|
import android.util.Range;
|
|
import android.view.Display;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
|
|
import com.limelight.LimeLog;
|
|
import com.limelight.PcView;
|
|
import com.limelight.R;
|
|
import com.limelight.binding.video.MediaCodecHelper;
|
|
import com.limelight.utils.UiHelper;
|
|
|
|
public class StreamSettings extends Activity {
|
|
private PreferenceConfiguration previousPrefs;
|
|
|
|
void reloadSettings() {
|
|
getFragmentManager().beginTransaction().replace(
|
|
R.id.stream_settings, new SettingsFragment()
|
|
).commit();
|
|
}
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
previousPrefs = PreferenceConfiguration.readPreferences(this);
|
|
|
|
UiHelper.setLocale(this);
|
|
|
|
setContentView(R.layout.activity_stream_settings);
|
|
reloadSettings();
|
|
|
|
UiHelper.notifyNewRootView(this);
|
|
}
|
|
|
|
@Override
|
|
public void onBackPressed() {
|
|
finish();
|
|
|
|
// Check for changes that require a UI reload to take effect
|
|
PreferenceConfiguration newPrefs = PreferenceConfiguration.readPreferences(this);
|
|
if (newPrefs.smallIconMode != previousPrefs.smallIconMode ||
|
|
!newPrefs.language.equals(previousPrefs.language)) {
|
|
// Restart the PC view to apply UI changes
|
|
Intent intent = new Intent(this, PcView.class);
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
startActivity(intent, null);
|
|
}
|
|
}
|
|
|
|
public static class SettingsFragment extends PreferenceFragment {
|
|
|
|
private void setValue(String preferenceKey, String value) {
|
|
ListPreference pref = (ListPreference) findPreference(preferenceKey);
|
|
|
|
pref.setValue(value);
|
|
}
|
|
|
|
private void removeValue(String preferenceKey, String value, Runnable onMatched) {
|
|
int matchingCount = 0;
|
|
|
|
ListPreference pref = (ListPreference) findPreference(preferenceKey);
|
|
|
|
// Count the number of matching entries we'll be removing
|
|
for (CharSequence seq : pref.getEntryValues()) {
|
|
if (seq.toString().equalsIgnoreCase(value)) {
|
|
matchingCount++;
|
|
}
|
|
}
|
|
|
|
// Create the new arrays
|
|
CharSequence[] entries = new CharSequence[pref.getEntries().length-matchingCount];
|
|
CharSequence[] entryValues = new CharSequence[pref.getEntryValues().length-matchingCount];
|
|
int outIndex = 0;
|
|
for (int i = 0; i < pref.getEntryValues().length; i++) {
|
|
if (pref.getEntryValues()[i].toString().equalsIgnoreCase(value)) {
|
|
// Skip matching values
|
|
continue;
|
|
}
|
|
|
|
entries[outIndex] = pref.getEntries()[i];
|
|
entryValues[outIndex] = pref.getEntryValues()[i];
|
|
outIndex++;
|
|
}
|
|
|
|
if (pref.getValue().equalsIgnoreCase(value)) {
|
|
onMatched.run();
|
|
}
|
|
|
|
// Update the preference with the new list
|
|
pref.setEntries(entries);
|
|
pref.setEntryValues(entryValues);
|
|
}
|
|
|
|
|
|
|
|
private void resetBitrateToDefault(SharedPreferences prefs, String res, String fps) {
|
|
if (res == null) {
|
|
res = prefs.getString(PreferenceConfiguration.RESOLUTION_PREF_STRING, PreferenceConfiguration.DEFAULT_RESOLUTION);
|
|
}
|
|
if (fps == null) {
|
|
fps = prefs.getString(PreferenceConfiguration.FPS_PREF_STRING, PreferenceConfiguration.DEFAULT_FPS);
|
|
}
|
|
|
|
prefs.edit()
|
|
.putInt(PreferenceConfiguration.BITRATE_PREF_STRING,
|
|
PreferenceConfiguration.getDefaultBitrate(res, fps))
|
|
.apply();
|
|
}
|
|
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
View view = super.onCreateView(inflater, container, savedInstanceState);
|
|
UiHelper.applyStatusBarPadding(view);
|
|
return view;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
addPreferencesFromResource(R.xml.preferences);
|
|
PreferenceScreen screen = getPreferenceScreen();
|
|
|
|
// hide on-screen controls category on non touch screen devices
|
|
if (!getActivity().getPackageManager().
|
|
hasSystemFeature("android.hardware.touchscreen")) {
|
|
{
|
|
PreferenceCategory category =
|
|
(PreferenceCategory) findPreference("category_onscreen_controls");
|
|
screen.removePreference(category);
|
|
}
|
|
|
|
{
|
|
PreferenceCategory category =
|
|
(PreferenceCategory) findPreference("category_input_settings");
|
|
category.removePreference(findPreference("checkbox_touchscreen_trackpad"));
|
|
}
|
|
}
|
|
|
|
// Remove PiP mode on devices pre-Oreo or where the feature is not available (some low RAM devices)
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
|
|
!getActivity().getPackageManager().hasSystemFeature("android.software.picture_in_picture")) {
|
|
PreferenceCategory category =
|
|
(PreferenceCategory) findPreference("category_ui_settings");
|
|
category.removePreference(findPreference("checkbox_enable_pip"));
|
|
}
|
|
|
|
// Remove the vibration options if the device can't vibrate
|
|
if (!((Vibrator)getActivity().getSystemService(Context.VIBRATOR_SERVICE)).hasVibrator()) {
|
|
PreferenceCategory category =
|
|
(PreferenceCategory) findPreference("category_input_settings");
|
|
category.removePreference(findPreference("checkbox_vibrate_fallback"));
|
|
|
|
// The entire OSC category may have already been removed by the touchscreen check above
|
|
category = (PreferenceCategory) findPreference("category_onscreen_controls");
|
|
if (category != null) {
|
|
category.removePreference(findPreference("checkbox_vibrate_osc"));
|
|
}
|
|
}
|
|
|
|
int maxSupportedFps = 0;
|
|
|
|
// Hide non-supported resolution/FPS combinations
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
|
|
|
int maxSupportedResW = 0;
|
|
|
|
// Always allow resolutions that are smaller or equal to the active
|
|
// display resolution because decoders can report total non-sense to us.
|
|
// For example, a p201 device reports:
|
|
// AVC Decoder: OMX.amlogic.avc.decoder.awesome
|
|
// HEVC Decoder: OMX.amlogic.hevc.decoder.awesome
|
|
// AVC supported width range: 64 - 384
|
|
// HEVC supported width range: 64 - 544
|
|
for (Display.Mode candidate : display.getSupportedModes()) {
|
|
// Some devices report their dimensions in the portrait orientation
|
|
// where height > width. Normalize these to the conventional width > height
|
|
// arrangement before we process them.
|
|
|
|
int width = Math.max(candidate.getPhysicalWidth(), candidate.getPhysicalHeight());
|
|
int height = Math.min(candidate.getPhysicalWidth(), candidate.getPhysicalHeight());
|
|
|
|
if ((width >= 3840 || height >= 2160) && maxSupportedResW < 3840) {
|
|
maxSupportedResW = 3840;
|
|
}
|
|
else if ((width >= 2560 || height >= 1440) && maxSupportedResW < 2560) {
|
|
maxSupportedResW = 2560;
|
|
}
|
|
else if ((width >= 1920 || height >= 1080) && maxSupportedResW < 1920) {
|
|
maxSupportedResW = 1920;
|
|
}
|
|
|
|
if (candidate.getRefreshRate() > maxSupportedFps) {
|
|
maxSupportedFps = (int)candidate.getRefreshRate();
|
|
}
|
|
}
|
|
|
|
// This must be called to do runtime initialization before calling functions that evaluate
|
|
// decoder lists.
|
|
MediaCodecHelper.initialize(getContext(), GlPreferences.readPreferences(getContext()).glRenderer);
|
|
|
|
MediaCodecInfo avcDecoder = MediaCodecHelper.findProbableSafeDecoder("video/avc", -1);
|
|
MediaCodecInfo hevcDecoder = MediaCodecHelper.findProbableSafeDecoder("video/hevc", -1);
|
|
|
|
if (avcDecoder != null) {
|
|
Range<Integer> avcWidthRange = avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getSupportedWidths();
|
|
|
|
LimeLog.info("AVC supported width range: "+avcWidthRange.getLower()+" - "+avcWidthRange.getUpper());
|
|
|
|
// If 720p is not reported as supported, ignore all results from this API
|
|
if (avcWidthRange.contains(1280)) {
|
|
if (avcWidthRange.contains(3840) && maxSupportedResW < 3840) {
|
|
maxSupportedResW = 3840;
|
|
}
|
|
else if (avcWidthRange.contains(1920) && maxSupportedResW < 1920) {
|
|
maxSupportedResW = 1920;
|
|
}
|
|
else if (maxSupportedResW < 1280) {
|
|
maxSupportedResW = 1280;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hevcDecoder != null) {
|
|
Range<Integer> hevcWidthRange = hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getSupportedWidths();
|
|
|
|
LimeLog.info("HEVC supported width range: "+hevcWidthRange.getLower()+" - "+hevcWidthRange.getUpper());
|
|
|
|
// If 720p is not reported as supported, ignore all results from this API
|
|
if (hevcWidthRange.contains(1280)) {
|
|
if (hevcWidthRange.contains(3840) && maxSupportedResW < 3840) {
|
|
maxSupportedResW = 3840;
|
|
}
|
|
else if (hevcWidthRange.contains(1920) && maxSupportedResW < 1920) {
|
|
maxSupportedResW = 1920;
|
|
}
|
|
else if (maxSupportedResW < 1280) {
|
|
maxSupportedResW = 1280;
|
|
}
|
|
}
|
|
}
|
|
|
|
LimeLog.info("Maximum resolution slot: "+maxSupportedResW);
|
|
|
|
if (maxSupportedResW != 0) {
|
|
if (maxSupportedResW < 3840) {
|
|
// 4K is unsupported
|
|
removeValue(PreferenceConfiguration.RESOLUTION_PREF_STRING, PreferenceConfiguration.RES_4K, new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
|
setValue(PreferenceConfiguration.RESOLUTION_PREF_STRING, PreferenceConfiguration.RES_1440P);
|
|
resetBitrateToDefault(prefs, null, null);
|
|
}
|
|
});
|
|
}
|
|
if (maxSupportedResW < 2560) {
|
|
// 1440p is unsupported
|
|
removeValue(PreferenceConfiguration.RESOLUTION_PREF_STRING, PreferenceConfiguration.RES_1440P, new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
|
setValue(PreferenceConfiguration.RESOLUTION_PREF_STRING, PreferenceConfiguration.RES_1080P);
|
|
resetBitrateToDefault(prefs, null, null);
|
|
}
|
|
});
|
|
}
|
|
if (maxSupportedResW < 1920) {
|
|
// 1080p is unsupported
|
|
removeValue(PreferenceConfiguration.RESOLUTION_PREF_STRING, PreferenceConfiguration.RES_1080P, new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
|
setValue(PreferenceConfiguration.RESOLUTION_PREF_STRING, PreferenceConfiguration.RES_720P);
|
|
resetBitrateToDefault(prefs, null, null);
|
|
}
|
|
});
|
|
}
|
|
// Never remove 720p
|
|
}
|
|
}
|
|
|
|
if (!PreferenceConfiguration.readPreferences(this.getActivity()).unlockFps) {
|
|
// We give some extra room in case the FPS is rounded down
|
|
if (maxSupportedFps < 118) {
|
|
removeValue(PreferenceConfiguration.FPS_PREF_STRING, "120", new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
|
setValue(PreferenceConfiguration.FPS_PREF_STRING, "90");
|
|
resetBitrateToDefault(prefs, null, null);
|
|
}
|
|
});
|
|
}
|
|
if (maxSupportedFps < 88) {
|
|
// 1080p is unsupported
|
|
removeValue(PreferenceConfiguration.FPS_PREF_STRING, "90", new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
|
setValue(PreferenceConfiguration.FPS_PREF_STRING, "60");
|
|
resetBitrateToDefault(prefs, null, null);
|
|
}
|
|
});
|
|
}
|
|
// Never remove 30 FPS or 60 FPS
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
}
|
|
}, 500);
|
|
|
|
// 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) {
|
|
LimeLog.info("Excluding HDR toggle based on OS");
|
|
PreferenceCategory category =
|
|
(PreferenceCategory) findPreference("category_advanced_settings");
|
|
category.removePreference(findPreference("checkbox_enable_hdr"));
|
|
}
|
|
else {
|
|
Display display = getActivity().getWindowManager().getDefaultDisplay();
|
|
Display.HdrCapabilities hdrCaps = display.getHdrCapabilities();
|
|
|
|
// We must now ensure our display is compatible with HDR10
|
|
boolean foundHdr10 = false;
|
|
if (hdrCaps != null) {
|
|
// getHdrCapabilities() returns null on Lenovo Lenovo Mirage Solo (vega), Android 8.0
|
|
for (int hdrType : hdrCaps.getSupportedHdrTypes()) {
|
|
if (hdrType == Display.HdrCapabilities.HDR_TYPE_HDR10) {
|
|
foundHdr10 = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundHdr10) {
|
|
LimeLog.info("Excluding HDR toggle based on display capabilities");
|
|
PreferenceCategory category =
|
|
(PreferenceCategory) findPreference("category_advanced_settings");
|
|
category.removePreference(findPreference("checkbox_enable_hdr"));
|
|
}
|
|
}
|
|
|
|
// Add a listener to the FPS and resolution preference
|
|
// so the bitrate can be auto-adjusted
|
|
findPreference(PreferenceConfiguration.RESOLUTION_PREF_STRING).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
@Override
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
|
String valueStr = (String) newValue;
|
|
|
|
// Write the new bitrate value
|
|
resetBitrateToDefault(prefs, valueStr, null);
|
|
|
|
// Allow the original preference change to take place
|
|
return true;
|
|
}
|
|
});
|
|
findPreference(PreferenceConfiguration.FPS_PREF_STRING).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
@Override
|
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SettingsFragment.this.getActivity());
|
|
String valueStr = (String) newValue;
|
|
|
|
// Write the new bitrate value
|
|
resetBitrateToDefault(prefs, null, valueStr);
|
|
|
|
// Allow the original preference change to take place
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|