Compare commits

...

23 Commits

Author SHA1 Message Date
Cameron Gutman babfc99c35 Version 10.6 2022-07-07 23:17:08 -05:00
Cameron Gutman 1eca461cb1 Merge remote-tracking branch 'origin/weblate' 2022-07-04 17:51:36 -05:00
Cameron Gutman ebd327c7a6 Use new ShieldControllerExtensions library for Shield Controller rumble support
https://github.com/cgutman/ShieldControllerExtensions
2022-06-30 18:04:02 -05:00
Cameron Gutman 602febe876 Use onPictureInPictureRequested() to enter PiP on Android 11 2022-06-29 23:28:52 -05:00
Cameron Gutman 84fcd3ae6a Use requestMetaKeyEvent API on Samsung devices
Inspired by #1078
2022-06-28 22:07:40 -05:00
Cameron Gutman 84296c6e1c Toggle the IME with a 3 finger tap rather than only showing it 2022-06-28 21:40:59 -05:00
Jorys Paulin 6012e0ea8c Translated using Weblate (French)
Currently translated at 100.0% (219 of 219 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/fr/
2022-06-27 15:16:50 +02:00
Cameron Gutman 9c76defad0 Add workaround for Galaxy S10 devices crashing during WifiLock acquisition 2022-06-26 13:59:39 -05:00
Cameron Gutman ffd6fab35c Prevent use of proxies 2022-06-25 14:18:38 -05:00
Cameron Gutman 296f97f7ca Version 10.5 2022-06-23 23:37:19 -05:00
Artem 9cbef34f29 Translated using Weblate (Ukrainian)
Currently translated at 94.5% (207 of 219 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/uk/
2022-06-22 22:20:52 +02:00
Cameron Gutman 7a65136d29 Disable predictive back gesture support because it breaks KEYCODE_BACK on InputDevices
Partially reverts b2e605838e
2022-06-18 16:19:02 -05:00
Cameron Gutman acaebea846 Merge remote-tracking branch 'origin/weblate' 2022-06-18 15:02:06 -05:00
Cameron Gutman ce850ac12f Fix crash on Xiaomi MiPad running newer custom ROMs
AVC Decoder: OMX.Nvidia.h264.decode
HEVC Decoder: OMX.Nvidia.h265.decode
AVC supported width range: [32, 3840]
AVC achievable FPS range: [146.0, 149.0]
HEVC supported width range: [32, 528]
HEVC achievable FPS range: UNSUPPORTED!
2022-06-18 15:00:10 -05:00
Cameron Gutman a93422d3ed Handle failure to bind com.nvidia.blakepairing more robustly 2022-06-18 14:31:38 -05:00
Cameron Gutman b2e605838e Opt in for new predictive back gesture support in Android 13 2022-06-18 14:26:13 -05:00
Cameron Gutman 2e14002442 Switch to the new native per-app language preference APIs on Android 13 2022-06-18 14:19:19 -05:00
Cameron Gutman c743949df5 Don't crash if no performance data was provided for the codec using the M API 2022-06-18 10:37:16 -05:00
Cameron Gutman f207a3f6d1 Use areSizeAndRateSupported() as a last resort if no performance data is available 2022-06-18 10:35:12 -05:00
Cameron Gutman d6211605a1 Fix crash on shortcut launch if PC has no known MAC address 2022-06-18 10:23:06 -05:00
metezd 80620ed4c6 Translated using Weblate (Turkish)
Currently translated at 69.4% (152 of 219 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/tr/
2022-06-15 14:17:48 +02:00
Wen-haur Chiu 76bd0ab696 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (219 of 219 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/zh_Hant/
2022-06-15 14:17:47 +02:00
ㅤAbsurdUsername e0914df58a Translated using Weblate (Italian)
Currently translated at 100.0% (219 of 219 strings)

Translation: Moonlight Game Streaming/moonlight-android
Translate-URL: https://hosted.weblate.org/projects/moonlight/moonlight-android/it/
2022-06-15 14:17:46 +02:00
27 changed files with 347 additions and 431 deletions
+3 -2
View File
@@ -9,8 +9,8 @@ android {
minSdk 16
targetSdk 33
versionName "10.4"
versionCode = 280
versionName "10.6"
versionCode = 283
// Generate native debug symbols to allow Google Play to symbolicate our native crashes
ndk.debugSymbolLevel = 'FULL'
@@ -130,4 +130,5 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.12.13'
implementation 'com.squareup.okio:okio:1.17.5'
implementation 'org.jmdns:jmdns:3.5.7'
implementation 'com.github.cgutman:ShieldControllerExtensions:1.0'
}
+2
View File
@@ -44,6 +44,8 @@
android:roundIcon="@mipmap/ic_launcher"
android:installLocation="auto"
android:gwpAsanMode="always"
android:localeConfig="@xml/locales_config"
android:enableOnBackInvokedCallback="false"
android:theme="@style/AppTheme">
<provider
+66 -13
View File
@@ -78,6 +78,8 @@ import android.widget.TextView;
import android.widget.Toast;
import java.io.ByteArrayInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -265,14 +267,20 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Make sure Wi-Fi is fully powered up
WifiManager wifiMgr = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
highPerfWifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Moonlight High Perf Lock");
highPerfWifiLock.setReferenceCounted(false);
highPerfWifiLock.acquire();
try {
highPerfWifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Moonlight High Perf Lock");
highPerfWifiLock.setReferenceCounted(false);
highPerfWifiLock.acquire();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
lowLatencyWifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "Moonlight Low Latency Lock");
lowLatencyWifiLock.setReferenceCounted(false);
lowLatencyWifiLock.acquire();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
lowLatencyWifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "Moonlight Low Latency Lock");
lowLatencyWifiLock.setReferenceCounted(false);
lowLatencyWifiLock.acquire();
}
} catch (SecurityException e) {
// Some Samsung Galaxy S10+/S10e devices throw a SecurityException from
// WifiLock.acquire() even though we have android.permission.WAKE_LOCK in our manifest.
e.printStackTrace();
}
appName = Game.this.getIntent().getStringExtra(EXTRA_APP_NAME);
@@ -591,13 +599,42 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
}
public void setMetaKeyCaptureState(boolean enabled) {
// This uses custom APIs present on some Samsung devices to allow capture of
// meta key events while streaming.
try {
Class<?> semWindowManager = Class.forName("com.samsung.android.view.SemWindowManager");
Method getInstanceMethod = semWindowManager.getMethod("getInstance");
Object manager = getInstanceMethod.invoke(null);
if (manager != null) {
Class<?>[] parameterTypes = new Class<?>[2];
parameterTypes[0] = String.class;
parameterTypes[1] = boolean.class;
Method requestMetaKeyEventMethod = semWindowManager.getDeclaredMethod("requestMetaKeyEvent", parameterTypes);
requestMetaKeyEventMethod.invoke(manager, this.getComponentName(), enabled);
}
else {
LimeLog.warning("SemWindowManager.getInstance() returned null");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void onUserLeaveHint() {
super.onUserLeaveHint();
// PiP is only supported on Oreo and later, and we don't need to manually enter PiP on
// Android S and later.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
// Android S and later. On Android R, we will use onPictureInPictureRequested() instead.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
if (autoEnterPip) {
try {
// This has thrown all sorts of weird exceptions on Samsung devices
@@ -611,6 +648,16 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
}
@Override
@TargetApi(Build.VERSION_CODES.R)
public boolean onPictureInPictureRequested() {
// Enter PiP when requested unless we're on Android 12 which supports auto-enter.
if (autoEnterPip && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
enterPictureInPictureMode(getPictureInPictureParams(false));
}
return true;
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
@@ -1225,10 +1272,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
@Override
public void showKeyboard() {
LimeLog.info("Showing keyboard overlay");
public void toggleKeyboard() {
LimeLog.info("Toggling keyboard overlay");
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
inputManager.toggleSoftInput(0, 0);
}
// Returns true if the event was consumed
@@ -1465,7 +1512,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// All fingers up
if (SystemClock.uptimeMillis() - threeFingerDownTime < THREE_FINGER_TAP_THRESHOLD) {
// This is a 3 finger tap to bring up the keyboard
showKeyboard();
toggleKeyboard();
return true;
}
}
@@ -1690,6 +1737,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Enable cursor visibility again
inputCaptureProvider.disableCapture();
// Disable meta key capture
setMetaKeyCaptureState(false);
if (!displayedFailureDialog) {
displayedFailureDialog = true;
LimeLog.severe("Connection terminated: " + errorCode);
@@ -1801,6 +1851,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Keep the display on
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Enable meta key capture
setMetaKeyCaptureState(true);
// Update GameManager state to indicate we're in game
UiHelper.notifyStreamConnected(Game.this);
@@ -108,6 +108,7 @@ public class HelpActivity extends Activity {
}
@Override
// NOTE: This will NOT be called on Android 13+ with android:enableOnBackInvokedCallback="true"
public void onBackPressed() {
// Back goes back through the WebView history
// until no more history remains
@@ -83,7 +83,7 @@ public class ShortcutTrampoline extends Activity {
}
// Try to wake the target PC if it's offline (up to some retry limit)
if (details.state == ComputerDetails.State.OFFLINE && --wakeHostTries >= 0) {
if (details.state == ComputerDetails.State.OFFLINE && details.macAddress != null && --wakeHostTries >= 0) {
try {
// Make a best effort attempt to wake the target PC
WakeOnLanSender.sendWolPacket(computer);
@@ -25,7 +25,6 @@ import com.limelight.LimeLog;
import com.limelight.binding.input.driver.AbstractController;
import com.limelight.binding.input.driver.UsbDriverListener;
import com.limelight.binding.input.driver.UsbDriverService;
import com.limelight.binding.input.shield.ShieldControllerExtensionsHandler;
import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket;
import com.limelight.nvstream.input.MouseButtonPacket;
@@ -33,6 +32,8 @@ import com.limelight.preferences.PreferenceConfiguration;
import com.limelight.ui.GameGestures;
import com.limelight.utils.Vector2d;
import org.cgutman.shieldcontrollerextensions.SceManager;
import java.lang.reflect.InvocationTargetException;
import java.util.Timer;
import java.util.TimerTask;
@@ -62,7 +63,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private final InputDeviceContext defaultContext = new InputDeviceContext();
private final GameGestures gestures;
private final Vibrator deviceVibrator;
private final ShieldControllerExtensionsHandler shieldControllerExtensionsHandler;
private final SceManager sceManager;
private boolean hasGameController;
private final PreferenceConfiguration prefConfig;
@@ -74,7 +75,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
this.gestures = gestures;
this.prefConfig = prefConfig;
this.deviceVibrator = (Vibrator) activityContext.getSystemService(Context.VIBRATOR_SERVICE);
this.shieldControllerExtensionsHandler = new ShieldControllerExtensionsHandler(activityContext);
this.sceManager = new SceManager(activityContext);
this.sceManager.start();
int deadzonePercentage = prefConfig.deadzonePercentage;
@@ -203,7 +206,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
deviceContext.destroy();
}
shieldControllerExtensionsHandler.destroy();
sceManager.stop();
deviceVibrator.cancel();
}
@@ -1445,32 +1448,14 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
if (deviceContext.controllerNumber == controllerNumber) {
foundMatchingDevice = true;
// Cancel pending rumble repeat timer if one exists
if (deviceContext.rumbleRepeatTimer != null) {
deviceContext.rumbleRepeatTimer.cancel();
deviceContext.rumbleRepeatTimer = null;
}
// Prefer the documented Android 12 rumble API which can handle dual vibrators on PS/Xbox controllers
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && deviceContext.vibratorManager != null) {
vibrated = true;
rumbleDualVibrators(deviceContext.vibratorManager, lowFreqMotor, highFreqMotor);
}
// On Shield devices, we can use their special API to rumble Shield controllers
else if (shieldControllerExtensionsHandler.rumble(deviceContext.inputDevice, lowFreqMotor, highFreqMotor)) {
else if (sceManager.rumble(deviceContext.inputDevice, lowFreqMotor, highFreqMotor)) {
vibrated = true;
// The Shield controller can only rumble up to 1 second at a time, so we will call rumble again
// every 500 ms until the host PC gives us another rumble value.
if (lowFreqMotor != 0 || highFreqMotor != 0) {
deviceContext.rumbleRepeatTimer = new Timer("Rumble Repeat - "+deviceContext.name, true);
deviceContext.rumbleRepeatTimer.schedule(new TimerTask() {
@Override
public void run() {
shieldControllerExtensionsHandler.rumble(deviceContext.inputDevice, lowFreqMotor, highFreqMotor);
}
}, 500, 500);
}
}
// If all else fails, we have to try the old Vibrator API
else if (deviceContext.vibrator != null) {
@@ -1961,7 +1946,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public VibratorManager vibratorManager;
public Vibrator vibrator;
public InputDevice inputDevice;
public Timer rumbleRepeatTimer;
public int leftStickXAxis = -1;
public int leftStickYAxis = -1;
@@ -2013,10 +1997,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
else if (vibrator != null) {
vibrator.cancel();
}
if (rumbleRepeatTimer != null) {
rumbleRepeatTimer.cancel();
}
}
}
@@ -1,51 +0,0 @@
package com.limelight.binding.input.shield;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
public interface IExposedControllerManagerListener extends IInterface {
void onDeviceAdded(String controllerToken);
void onDeviceChanged(String controllerToken, int i);
void onDeviceRemoved(String controllerToken);
public static abstract class Stub extends Binder implements IExposedControllerManagerListener {
public Stub() {
attachInterface(this, "com.nvidia.blakepairing.IExposedControllerManagerListener");
}
@Override
public IBinder asBinder() {
return this;
}
public boolean onTransact(int code, Parcel input, Parcel output, int flags) throws RemoteException {
switch (code) {
case 1:
input.enforceInterface("com.nvidia.blakepairing.IExposedControllerManagerListener");
onDeviceAdded(input.readString());
break;
case 2:
input.enforceInterface("com.nvidia.blakepairing.IExposedControllerManagerListener");
onDeviceChanged(input.readString(), input.readInt());
break;
case 3:
input.enforceInterface("com.nvidia.blakepairing.IExposedControllerManagerListener");
onDeviceRemoved(input.readString());
break;
case 4:
case 5:
input.enforceInterface("com.nvidia.blakepairing.IExposedControllerManagerListener");
// Don't care
break;
default:
return super.onTransact(code, input, output, flags);
}
return true;
}
}
}
@@ -1,291 +0,0 @@
package com.limelight.binding.input.shield;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.hardware.input.InputManager;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.view.InputDevice;
import com.limelight.LimeLog;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class ShieldControllerExtensionsHandler implements InputManager.InputDeviceListener {
private Context context;
private IBinder binder;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
binder = iBinder;
try {
listenerId = registerListener();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
listenerId = 0;
tokenToDeviceIdMap.clear();
deviceIdToTokenMap.clear();
binder = null;
}
};
// ConcurrentHashMap handles synchronization between the Binder thread adding/removing
// entries and callers on arbitrary threads that are doing device lookups.
//
// Since these are separate maps, they can be temporarily inconsistent (only one-way
// of the two-way mapping present). This is fine for our purposes here.
private ConcurrentHashMap<String, Integer> tokenToDeviceIdMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<Integer, String> deviceIdToTokenMap = new ConcurrentHashMap<>();
private AtomicBoolean needsRefresh = new AtomicBoolean(false);
private int listenerId;
private IExposedControllerManagerListener.Stub controllerListener = new IExposedControllerManagerListener.Stub() {
@Override
public void onDeviceAdded(String controllerToken) {
try {
int inputDeviceId = getInputDeviceId(controllerToken);
LimeLog.info("Shield controller added: " + controllerToken + " -> " + inputDeviceId);
tokenToDeviceIdMap.put(controllerToken, inputDeviceId);
deviceIdToTokenMap.put(inputDeviceId, controllerToken);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onDeviceChanged(String controllerToken, int i) {
LimeLog.info("Shield controller changed: " + controllerToken + " " + i);
}
@Override
public void onDeviceRemoved(String controllerToken) {
LimeLog.info("Shield controller removed: " + controllerToken);
Integer deviceId = tokenToDeviceIdMap.remove(controllerToken);
if (deviceId != null) {
deviceIdToTokenMap.remove(deviceId);
}
}
};
public ShieldControllerExtensionsHandler(Context context) {
this.context = context;
InputManager inputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
inputManager.registerInputDeviceListener(this, null);
Intent intent = new Intent();
intent.setClassName("com.nvidia.blakepairing", "com.nvidia.blakepairing.AccessoryService");
if (!context.bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE)) {
LimeLog.info("com.nvidia.blakepairing.AccessoryService is not available on this device");
}
}
private String getControllerToken(InputDevice device) {
// Refresh device ID <-> token mappings if one of our devices was removed
if (needsRefresh.compareAndSet(true, false)) {
try {
LimeLog.info("Refreshing controller token mappings");
// We have to enumerate tokenToDeviceIdMap rather than deviceIdToTokenMap
// because we remove the deviceIdToTokenMap entry when the device goes away.
HashMap<String, Integer> newTokenToDeviceIdMap = new HashMap<>();
HashMap<Integer, String> newDeviceIdToTokenMap = new HashMap<>();
for (String existingToken : tokenToDeviceIdMap.keySet()) {
int deviceId = getInputDeviceId(existingToken);
if (deviceId != 0) {
newTokenToDeviceIdMap.put(existingToken, deviceId);
newDeviceIdToTokenMap.put(deviceId, existingToken);
}
}
tokenToDeviceIdMap.clear();
deviceIdToTokenMap.clear();
tokenToDeviceIdMap.putAll(newTokenToDeviceIdMap);
deviceIdToTokenMap.putAll(newDeviceIdToTokenMap);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return deviceIdToTokenMap.get(device.getId());
}
public boolean rumble(InputDevice device, int lowFreqMotor, int highFreqMotor) {
String controllerToken = getControllerToken(device);
if (controllerToken != null) {
try {
return rumble(controllerToken, lowFreqMotor, highFreqMotor);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return false;
}
public void destroy() {
InputManager inputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
inputManager.unregisterInputDeviceListener(this);
tokenToDeviceIdMap.clear();
deviceIdToTokenMap.clear();
if (listenerId != 0) {
try {
unregisterListener(listenerId);
} catch (RemoteException e) {
e.printStackTrace();
}
listenerId = 0;
}
if (binder != null) {
context.unbindService(serviceConnection);
binder = null;
}
}
private int registerListener() throws RemoteException {
if (binder == null) {
return 0;
}
Parcel input = Parcel.obtain();
Parcel output = Parcel.obtain();
try {
input.writeInterfaceToken("com.nvidia.blakepairing.IExposedControllerBinder");
input.writeStrongBinder(controllerListener);
binder.transact(20, input, output, 0);
output.readException();
return output.readInt();
} finally {
input.recycle();
output.recycle();
}
}
private boolean unregisterListener(int listenerId) throws RemoteException {
if (binder == null) {
return false;
}
Parcel input = Parcel.obtain();
Parcel output = Parcel.obtain();
try {
input.writeInterfaceToken("com.nvidia.blakepairing.IExposedControllerBinder");
input.writeInt(listenerId);
binder.transact(21, input, output, 0);
output.readException();
return output.readInt() != 0;
} finally {
input.recycle();
output.recycle();
}
}
private int getInputDeviceId(String controllerToken) throws RemoteException {
if (binder == null) {
return 0;
}
Parcel input = Parcel.obtain();
Parcel output = Parcel.obtain();
try {
input.writeInterfaceToken("com.nvidia.blakepairing.IExposedControllerBinder");
input.writeString(controllerToken);
binder.transact(13, input, output, 0);
output.readException();
return output.readInt();
} finally {
input.recycle();
output.recycle();
}
}
// Rumble duration maximum of 1 second
private boolean rumble(String controllerToken, int lowFreqMotor, int highFreqMotor) throws RemoteException {
if (binder == null) {
return false;
}
Parcel input = Parcel.obtain();
Parcel output = Parcel.obtain();
try {
input.writeInterfaceToken("com.nvidia.blakepairing.IExposedControllerBinder");
input.writeString(controllerToken);
input.writeInt(lowFreqMotor);
input.writeInt(highFreqMotor);
binder.transact(18, input, output, 0);
output.readException();
return output.readInt() != 0;
} finally {
input.recycle();
output.recycle();
}
}
// Rumble duration maximum of 1.5 seconds
private boolean rumbleWithDuration(String controllerToken, int lowFreqMotor, int highFreqMotor, long durationMs) throws RemoteException {
if (binder == null) {
return false;
}
Parcel input = Parcel.obtain();
Parcel output = Parcel.obtain();
try {
input.writeInterfaceToken("com.nvidia.blakepairing.IExposedControllerBinder");
input.writeString(controllerToken);
input.writeInt(lowFreqMotor);
input.writeInt(highFreqMotor);
input.writeLong(durationMs);
binder.transact(19, input, output, 0);
output.readException();
return output.readInt() != 0;
} finally {
input.recycle();
output.recycle();
}
}
@Override
public void onInputDeviceAdded(int deviceId) {}
@Override
public void onInputDeviceChanged(int deviceId) {}
@Override
public void onInputDeviceRemoved(int deviceId) {
// Remove the device ID to token mapping, but leave the token mapping to device ID
// mapping so we will re-enumerate it when we next try to rumble a controller.
if (deviceIdToTokenMap.remove(deviceId) != null) {
needsRefresh.set(true);
}
}
}
@@ -15,6 +15,7 @@ import com.limelight.nvstream.av.video.VideoDecoderRenderer;
import com.limelight.nvstream.jni.MoonBridge;
import com.limelight.preferences.PreferenceConfiguration;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
@@ -106,7 +107,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
return decoder;
}
private Boolean decoderCanMeetPerformancePoint(MediaCodecInfo.VideoCapabilities caps, PreferenceConfiguration prefs) {
@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);
List<MediaCodecInfo.VideoCapabilities.PerformancePoint> perfPoints = caps.getSupportedPerformancePoints();
@@ -130,14 +132,21 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
// 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);
return prefs.fps <= fpsRange.getUpper();
if (fpsRange != null) {
return prefs.fps <= fpsRange.getUpper();
}
// Fall-through to try the Android L API if there's no performance point data
} catch (IllegalArgumentException e) {
// Video size not supported at any frame rate
return false;
}
}
return null;
// 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);
}
private boolean decoderCanMeetPerformancePointWithHevcAndNotAvc(MediaCodecInfo avcDecoderInfo, MediaCodecInfo hevcDecoderInfo, PreferenceConfiguration prefs) {
@@ -145,15 +154,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
MediaCodecInfo.VideoCapabilities avcCaps = avcDecoderInfo.getCapabilitiesForType("video/avc").getVideoCapabilities();
MediaCodecInfo.VideoCapabilities hevcCaps = hevcDecoderInfo.getCapabilitiesForType("video/hevc").getVideoCapabilities();
Boolean avcCanMeetPP = decoderCanMeetPerformancePoint(avcCaps, prefs);
Boolean hevcCanMeetPP = decoderCanMeetPerformancePoint(hevcCaps, prefs);
// If one or both codecs lack performance data, don't do anything
if (avcCanMeetPP == null || hevcCanMeetPP == null) {
return false;
}
return !avcCanMeetPP && hevcCanMeetPP;
return !decoderCanMeetPerformancePoint(avcCaps, prefs) && decoderCanMeetPerformancePoint(hevcCaps, prefs);
}
else {
// No performance data
@@ -144,12 +144,16 @@ public class MediaCodecHelper {
// NVIDIA does partial HEVC acceleration on the Shield Tablet. I don't know
// whether the performance is good enough to use for streaming, but they're
// using the same omx.nvidia.h265.decode name as the Shield TV which has a
// fully accelerated HEVC pipeline. AFAIK, the only K1 device with this
// partially accelerated HEVC decoder is the Shield Tablet, so I'll
// check for it here. Since there are 2 models of Shield Tablet (possibly
// more with LTE), I will also exclude pre-Oreo OSes since only Shield ATV
// got an Oreo update.
if (!Build.DEVICE.equalsIgnoreCase("shieldtablet") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// fully accelerated HEVC pipeline. AFAIK, the only K1 devices with this
// partially accelerated HEVC decoder are the Shield Tablet and Xiaomi MiPad,
// so I'll check for those here.
//
// In case there are some that I missed, I will also exclude pre-Oreo OSes since
// only Shield ATV got an Oreo update and any newer Tegra devices will not ship
// with an old OS like Nougat.
if (!Build.DEVICE.equalsIgnoreCase("shieldtablet") &&
!Build.DEVICE.equalsIgnoreCase("mocha") &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
whitelistedHevcDecoders.add("omx.nvidia");
}
@@ -9,6 +9,7 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.Proxy;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
@@ -171,6 +172,7 @@ public class NvHTTP {
.hostnameVerifier(hv)
.readTimeout(0, TimeUnit.MILLISECONDS)
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
.proxy(Proxy.NO_PROXY)
.build();
httpClientWithReadTimeout = httpClient.newBuilder()
@@ -0,0 +1,51 @@
package com.limelight.preferences;
import android.annotation.TargetApi;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.preference.ListPreference;
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);
}
public LanguagePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LanguagePreference(Context context) {
super(context);
}
@Override
protected void onClick() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
try {
// Launch the Android native app locale settings page
Intent intent = new Intent(Settings.ACTION_APP_LOCALE_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + getContext().getPackageName()));
getContext().startActivity(intent, null);
return;
} catch (ActivityNotFoundException e) {
// App locale settings should be present on all Android 13 devices,
// but if not, we'll launch the old language chooser.
}
}
// If we don't have native app locale settings, launch the normal dialog
super.onClick();
}
}
@@ -319,6 +319,12 @@ public class PreferenceConfiguration {
.apply();
}
public static void completeLanguagePreferenceMigration(Context context) {
// Put our language option back to default which tells us that we've already migrated it
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().putString(LANGUAGE_PREF_STRING, DEFAULT_LANGUAGE).apply();
}
public static boolean isShieldAtvFirmwareWithBrokenHdr() {
// This particular Shield TV firmware crashes when using HDR
// https://www.nvidia.com/en-us/geforce/forums/notifications/comment/155192/
@@ -80,16 +80,20 @@ public class StreamSettings extends Activity {
}
@Override
// NOTE: This will NOT be called on Android 13+ with android:enableOnBackInvokedCallback="true"
public void onBackPressed() {
finish();
// Check for changes that require a UI reload to take effect
PreferenceConfiguration newPrefs = PreferenceConfiguration.readPreferences(this);
if (!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);
// Language changes are handled via configuration changes in Android 13+,
// so manual activity relaunching is no longer required.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
PreferenceConfiguration newPrefs = PreferenceConfiguration.readPreferences(this);
if (!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);
}
}
}
@@ -1,5 +1,5 @@
package com.limelight.ui;
public interface GameGestures {
void showKeyboard();
void toggleKeyboard();
}
@@ -4,6 +4,7 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.app.GameManager;
import android.app.GameState;
import android.app.LocaleManager;
import android.app.UiModeManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -11,6 +12,7 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.os.Build;
import android.os.LocaleList;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -64,21 +66,29 @@ public class UiHelper {
{
String locale = PreferenceConfiguration.readPreferences(activity).language;
if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) {
Configuration config = new Configuration(activity.getResources().getConfiguration());
// Some locales include both language and country which must be separated
// before calling the Locale constructor.
if (locale.contains("-"))
{
config.locale = new Locale(locale.substring(0, locale.indexOf('-')),
locale.substring(locale.indexOf('-') + 1));
}
else
{
config.locale = new Locale(locale);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// On Android 13, migrate this non-default language setting into the OS native API
LocaleManager localeManager = activity.getSystemService(LocaleManager.class);
localeManager.setApplicationLocales(LocaleList.forLanguageTags(locale));
PreferenceConfiguration.completeLanguagePreferenceMigration(activity);
}
else {
Configuration config = new Configuration(activity.getResources().getConfiguration());
activity.getResources().updateConfiguration(config, activity.getResources().getDisplayMetrics());
// Some locales include both language and country which must be separated
// before calling the Locale constructor.
if (locale.contains("-"))
{
config.locale = new Locale(locale.substring(0, locale.indexOf('-')),
locale.substring(locale.indexOf('-') + 1));
}
else
{
config.locale = new Locale(locale);
}
activity.getResources().updateConfiguration(config, activity.getResources().getDisplayMetrics());
}
}
}
+1 -1
View File
@@ -228,7 +228,7 @@
<string name="audioconf_stereo">Stéréo</string>
<string name="audioconf_51surround">Son surround 5.1</string>
<string name="audioconf_71surround">Son surround 7.1</string>
<string name="videoformat_hevcauto">Utiliser HEVC uniquement s\'il est stable</string>
<string name="videoformat_hevcauto">Automatique</string>
<string name="videoformat_hevcalways">Utilisez toujours HEVC (mais il peut planter)</string>
<string name="videoformat_hevcnever">N\'utilisez jamais HEVC</string>
<string name="title_frame_pacing">Frame-pacing vidéo</string>
+1 -1
View File
@@ -208,7 +208,7 @@
<string name="audioconf_stereo">Stereo</string>
<string name="audioconf_51surround">Surround 5.1</string>
<string name="audioconf_71surround">Surround 7.1</string>
<string name="videoformat_hevcauto">Usa HEVC solo se stabile</string>
<string name="videoformat_hevcauto">Automatico</string>
<string name="videoformat_hevcalways">Usa sempre HEVC (potrebbe essere instabile)</string>
<string name="videoformat_hevcnever">Non usare mai HEVC</string>
<string name="title_frame_pacing">Bilanciamento frame video</string>
+112
View File
@@ -61,4 +61,116 @@
<string name="nettest_text_success">Ağınız Moonlight\'ı engelliyor gibi görünmüyor. Bağlanmakta hala sorun yaşıyorsanız, bilgisayarınızın güvenlik duvarı ayarlarını kontrol edin.
\n
\nİnternet üzerinden yayın yapmaya çalışıyorsanız, Moonlight İnternet Barındırma Aracını bilgisayarınıza yükleyin ve bilgisayarınızın internet bağlantısını kontrol etmek için birlikte verilen İnternet Akış Test Cihazını çalıştırın.</string>
<string name="title_checkbox_multi_controller">Otomatik oyun kumandası varlığı algılama</string>
<string name="summary_checkbox_multi_controller">Bu seçeneğin işaretinin kaldırılması bir oyun kumandasının her zaman mevcut olmasını zorlar</string>
<string name="summary_checkbox_vibrate_fallback">Oyun kumandanız desteklemiyorsa, gürültüyü taklit etmek için cihazınızı titreştirir</string>
<string name="title_seekbar_deadzone">Analog çubuk ölü bölgesini ayarlama</string>
<string name="summary_checkbox_xb1_driver">Yerel Xbox oyun kumandası desteği olmayan cihazlar için yerleşik USB sürücüsünü etkinleştirir</string>
<string name="title_checkbox_mouse_emulation">Oyun kumandası üzerinden fare emülasyonu</string>
<string name="title_checkbox_mouse_nav_buttons">Geri ve ileri fare düğmelerini etkinleştirme</string>
<string name="category_on_screen_controls_settings">Ekran Kontrolleri Ayarları</string>
<string name="title_checkbox_show_onscreen_controls">Ekran kontrollerini göster</string>
<string name="error_manager_not_running">ComputerManager hizmeti çalışmıyor. Lütfen birkaç saniye bekleyin veya uygulamayı yeniden başlatın.</string>
<string name="error_unknown_host">Ana bilgisayar çözümlenemedi</string>
<string name="title_decoding_error">Video Kod Çözücü Çöktü</string>
<string name="message_decoding_reset">Cihazınızın video kod çözücüsü, seçtiğiniz akış ayarlarında çökmeye devam ediyor. Akış ayarlarınız varsayılana sıfırlandı.</string>
<string name="error_usb_prohibited">USB erişimi cihaz yöneticiniz tarafından yasaklanmıştır. Knox veya MDM ayarlarınızı kontrol edin.</string>
<string name="unable_to_pin_shortcut">Mevcut başlatıcınız sabitlenmiş kısayollar oluşturmaya izin vermiyor.</string>
<string name="video_decoder_init_failed">Video kod çözücü başlatılamadı. Cihazınız seçilen çözünürlüğü veya kare hızını desteklemiyor olabilir.</string>
<string name="no_video_received_error">Ana bilgisayardan video alınmadı.</string>
<string name="no_frame_received_error">Ağ bağlantınız iyi performans göstermiyor. Video bit hızı ayarınızı düşürün veya daha hızlı bir bağlantı deneyin.</string>
<string name="early_termination_error">Yayını başlatırken ana bilgisayarda bir şeyler ters gitti.
\n
\nAna bilgisayarınızda DRM korumalı herhangi bir içeriğin açık olmadığından emin olun. Ana bilgisayarınızı yeniden başlatmayı da deneyebilirsiniz.
\n
\nSorun devam ederse GPU sürücülerinizi ve GeForce Experience\'ı yeniden yüklemeyi deneyin.</string>
<string name="check_ports_msg">Bağlantı noktaları için güvenlik duvarınızı ve bağlantı noktası yönlendirme kurallarınızı kontrol edin:</string>
<string name="conn_establishing_title">Bağlantı kuruluyor</string>
<string name="conn_terminated_title">Bağlantı sonlandırıldı</string>
<string name="conn_terminated_msg">Bağlantı sonlandırıldı</string>
<string name="yes">Evet</string>
<string name="lost_connection">Bilgisayar ile bağlantı kesildi</string>
<string name="help">Yardım</string>
<string name="delete_pc_msg">Bu bilgisayarı silmek istediğinizden emin misiniz\?</string>
<string name="slow_connection_msg">Bilgisayara yavaş bağlantı
\nBit hızınızı düşürün</string>
<string name="poor_connection_msg">Bilgisayar ile zayıf bağlantı</string>
<string name="perf_overlay_streamdetails">Video akışı: %1$s %2$.2f FPS</string>
<string name="perf_overlay_decoder">Kod çözücü: %1$s</string>
<string name="perf_overlay_incomingfps">Ağdan gelen kare hızı: %1$.2f FPS</string>
<string name="perf_overlay_renderingfps">İşleme kare hızı: %1$.2f FPS</string>
<string name="perf_overlay_netdrops">Ağ bağlantınız tarafından düşen kare sayısı: %1$.2f%%</string>
<string name="perf_overlay_dectime">Ortalama kod çözme süresi: %1$.2f ms</string>
<string name="applist_menu_resume">Oturumu sürdür</string>
<string name="applist_menu_quit">Oturumu sonlandır</string>
<string name="applist_menu_quit_and_start">Mevcut oyundan çık ve başla</string>
<string name="applist_menu_cancel">İptal</string>
<string name="applist_menu_details">Detayları göster</string>
<string name="applist_menu_scut">Kısayol oluştur</string>
<string name="applist_menu_tv_channel">Kanala ekle</string>
<string name="applist_menu_hide_app">Uygulamayı gizle</string>
<string name="applist_refresh_title">Uygulama listesi</string>
<string name="applist_quit_success">Başarıyla çıkıldı</string>
<string name="applist_refresh_error_msg">Uygulama listesi alınamadı</string>
<string name="applist_quit_app">Çıkılıyor</string>
<string name="applist_quit_fail">Çıkılamadı</string>
<string name="applist_quit_confirmation">Çalışan uygulamadan çıkmak istediğinizden emin misiniz\? Kaydedilmemiş tüm veriler kaybolacaktır.</string>
<string name="applist_details_id">Uygulama kimliği:</string>
<string name="msg_add_pc">Bilgisayara bağlanıyor…</string>
<string name="addpc_success">Bilgisayar başarıyla eklendi</string>
<string name="addpc_fail">Belirtilen bilgisayara bağlanılamıyor. Gerekli bağlantı noktalarına güvenlik duvarı üzerinden izin verildiğinden emin olun.</string>
<string name="addpc_unknown_host">Bilgisayar adresi çözümlenemiyor. Adreste yazım hatası yapmadığınızdan emin olun.</string>
<string name="addpc_enter_ip">Bir IP adresi girmelisiniz</string>
<string name="category_basic_settings">Temel Ayarlar</string>
<string name="summary_resolution_list">Görüntü netliğini artırmak için artırın. Düşük uçlu cihazlarda ve daha yavaş ağlarda daha iyi performans için azaltın.</string>
<string name="title_native_res_dialog">Yerel çözünürlük uyarısı</string>
<string name="title_fps_list">Video kare hızı</string>
<string name="title_seekbar_bitrate">Video bit hızı</string>
<string name="summary_checkbox_touchscreen_trackpad">Etkinleştirilirse, dokunmatik ekran bir dokunmatik fare gibi davranır. Devre dışı bırakılırsa, dokunmatik ekran doğrudan fare imlecini kontrol eder.</string>
<string name="conn_establishing_msg">Bağlantı başlatılıyor</string>
<string name="conn_metered">Uyarı: Aktif ağ bağlantınız ölçülüdür!</string>
<string name="conn_client_latency">Ortalama kare kod çözme gecikmesi:</string>
<string name="conn_client_latency_hw">donanım kod çözücü gecikmesi:</string>
<string name="pcview_menu_send_wol">Yerel Ağda Uyandırma isteği gönder</string>
<string name="applist_refresh_msg">Uygulamalar yenileniyor…</string>
<string name="applist_refresh_error_title">Hata</string>
<string name="title_add_pc">Bilgisayarı manuel olarak Ekle</string>
<string name="title_resolution_list">Video çözünürlüğü</string>
<string name="summary_fps_list">Daha akıcı bir video akışı için artırın. Düşük uçlu cihazlarda daha iyi performans için azaltın.</string>
<string name="resolution_prefix_native">Yerel</string>
<string name="resolution_prefix_native_fullscreen">Yerel tam ekran</string>
<string name="category_audio_settings">Ses Ayarları</string>
<string name="title_audio_config_list">Çevresel ses yapılandırması</string>
<string name="summary_seekbar_deadzone">Not: Bazı oyunlar Moonlight\'ın kullanmak üzere yapılandırıldığından daha büyük bir ölü bölge uygulayabilir.</string>
<string name="suffix_seekbar_deadzone">%</string>
<string name="title_checkbox_xb1_driver">Xbox 360/One USB oyun kumandası sürücüsü</string>
<string name="title_checkbox_usb_bind_all">Yerel Xbox oyun kumandası desteğini geçersiz kılma</string>
<string name="summary_checkbox_mouse_emulation">Başlat düğmesine uzun süre basmak oyun kumandasını fare moduna geçirir</string>
<string name="summary_checkbox_mouse_nav_buttons">Bu seçeneği etkinleştirmek bazı hatalı cihazlarda sağ tıklamayı bozabilir</string>
<string name="title_checkbox_flip_face_buttons">Yüz düğmelerini çevirme</string>
<string name="title_checkbox_absolute_mouse_mode">Uzak masaüstü fare modu</string>
<string name="error_404">GFE bir HTTP 404 hatası bildirdi. Bilgisayarınızın desteklenen bir GPU çalıştırdığından emin olun. Uzak masaüstü yazılımı kullanmak da bu hataya neden olabilir. Cihazınızı yeniden başlatmayı veya GFE\'yi yeniden yüklemeyi deneyin.</string>
<string name="message_decoding_error">Moonlight, bu cihazın video kod çözücüsü ile uyumsuzluk nedeniyle çöktü. GeForce Experience\'ın bilgisayarınızdaki en son sürüme güncellendiğinden emin olun. Çökmeler devam ederse akış ayarlarını değiştirmeyi deneyin.</string>
<string name="conn_hardware_latency">Ortalama donanım kod çözme gecikmesi:</string>
<string name="title_decoding_reset">Video Ayarlarını Sıfırla</string>
<string name="searching_pc">GameStream\'in çalıştığı bilgisayarlar aranıyor...
\n
\n GeForce Experience SHIELD ayarlarında GameStream\'in etkinleştirildiğinden emin olun.</string>
<string name="applist_connect_msg">Bilgisayara bağlanıyor…</string>
<string name="no">Hayır</string>
<string name="perf_overlay_netlatency">Ortalama ağ gecikmesi: %1$d ms (varyans: %2$d ms)</string>
<string name="addpc_wrong_sitelocal">Bu adres doğru görünmüyor. İnternet üzerinden akış için yönlendiricinizin genel IP adresini kullanmanız gerekir.</string>
<string name="text_native_res_dialog">Yerel çözünürlük modları GeForce Experience tarafından resmi olarak desteklenmez, bu nedenle ana ekran çözünürlüğünüzü kendisi ayarlamaz. Oyun içindeyken manuel olarak ayarlamanız gerekecektir.
\n
\nCihazınızın çözünürlüğüne uyması için NVIDIA Denetim Masası\'nda özel bir çözünürlük oluşturmayı seçerseniz, lütfen NVIDIA\'nın olası monitör hasarı, bilgisayar kararsızlığı ve diğer olası sorunlarla ilgili uyarısını okuyup anladığınızdan emin olun.
\n
\nBilgisayarınızda özel bir çözünürlük oluşturulmasından kaynaklanan herhangi bir sorundan sorumlu değiliz.
\n
\nSon olarak, cihazınız veya bilgisayarınız doğal çözünürlükte akışı desteklemiyor olabilir. Eğer cihazınızda çalışmıyorsa, ne yazık ki şansınız yok demektir.</string>
<string name="summary_seekbar_bitrate">Daha iyi görüntü kalitesi için artırın. Daha yavaş bağlantılarda performansı artırmak için azaltın.</string>
<string name="title_checkbox_stretch_video">Videoyu tam ekrana genişlet</string>
<string name="title_checkbox_vibrate_fallback">Titreşim ile gürültü desteğini taklit etme</string>
<string name="summary_checkbox_usb_bind_all">Yerel Xbox oyun kumandası desteği mevcut olsa bile desteklenen tüm oyun kumandaları için Moonlight\'ın USB sürücüsünü kullan</string>
<string name="summary_checkbox_flip_face_buttons">Oyun kumandaları ve ekran kontrolleri için A/B ve X/Y yüz düğmelerini değiştirir</string>
<string name="summary_checkbox_absolute_mouse_mode">Bu fare hızlandırmanın uzak masaüstü kullanımı için daha doğal davranmasını sağlayabilir, ancak birçok oyunla uyumsuzdur.</string>
</resources>
+1 -1
View File
@@ -152,7 +152,7 @@
<string name="title_checkbox_vibrate_fallback">Емуляція вібровіддачі</string>
<string name="summary_checkbox_vibrate_osc">Вібрація пристрою для емуляції вібровіддачі при екранному управлінні</string>
<string name="summary_checkbox_vibrate_fallback">Вібрує пристрій для емуляції вібровіддачі, якщо під\'єднаний контролер не підтримує її</string>
<string name="summary_checkbox_mouse_nav_buttons">Включення цієї опції може привести до неправильної роботи правої клавіші миші на деяких пристроях</string>
<string name="summary_checkbox_mouse_nav_buttons">Вмикання цієї опції може привести до неправильної роботи правої клавіші миші на деяких пристроях</string>
<string name="title_checkbox_flip_face_buttons">Перевернути ґудзики</string>
<string name="summary_checkbox_flip_face_buttons">Перемикає ґудзики A/B та X/Y для контролерів та екранних елементів керування</string>
<string name="scut_pc_not_found">Пристрій не знайдено</string>
+2 -2
View File
@@ -85,7 +85,7 @@
<string name="applist_menu_quit_and_start">結束目前遊戲並開始這個遊戲</string>
<string name="applist_menu_cancel"> 取消 </string>
<string name="applist_menu_details">檢視詳細資料</string>
<string name="applist_menu_scut">建捷徑</string>
<string name="applist_menu_scut">捷徑</string>
<string name="applist_menu_tv_channel">新增至頻道</string>
<string name="applist_refresh_title">遊戲清單</string>
<string name="applist_refresh_msg">正在重新整理…</string>
@@ -223,7 +223,7 @@
<string name="audioconf_stereo">立體聲</string>
<string name="audioconf_51surround">5.1 環場音效</string>
<string name="audioconf_71surround">7.1 環場音效</string>
<string name="videoformat_hevcauto">僅在穩定時使用 HEVC</string>
<string name="videoformat_hevcauto">自動</string>
<string name="videoformat_hevcalways">強制使用 HEVC (可能會當機)</string>
<string name="videoformat_hevcnever">不使用 HEVC</string>
<string name="resolution_4k">4K</string>
+1
View File
@@ -43,6 +43,7 @@
<item>71</item>
</string-array>
<!-- Don't forget to update locales_config.xml when you modify this! -->
<string-array name="language_names" translatable="false">
<item>Default</item>
<item>English</item>
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Don't forget to update arrays.xml when you modify this file! -->
<locale android:name="en"/>
<locale android:name="it"/>
<locale android:name="ja"/>
<locale android:name="ru"/>
<locale android:name="nl"/>
<locale android:name="zh-CN"/>
<locale android:name="zh-TW"/>
<locale android:name="ko"/>
<locale android:name="es"/>
<locale android:name="fr"/>
<locale android:name="de"/>
<locale android:name="ro"/>
<locale android:name="uk"/>
<locale android:name="nb-NO"/>
<locale android:name="vi"/>
<locale android:name="hu"/>
<locale android:name="el"/>
</locale-config>
+1 -1
View File
@@ -165,7 +165,7 @@
android:title="@string/title_checkbox_enable_pip"
android:summary="@string/summary_checkbox_enable_pip"
android:defaultValue="false" />
<ListPreference
<com.limelight.preferences.LanguagePreference
android:key="list_languages"
android:title="@string/title_language_list"
android:entries="@array/language_names"
+1
View File
@@ -13,5 +13,6 @@ allprojects {
repositories {
mavenCentral()
google()
maven { url 'https://jitpack.io' }
}
}
@@ -0,0 +1,4 @@
- Implemented per-app language preferences on Android 13
- Improved automatic HEVC selection logic on older devices
- Fixed a few crashing bugs
- Updated community contributed translations from Weblate
@@ -0,0 +1,4 @@
- 3 finger tap can now dismiss the keyboard too
- Fixed crash on some Samsung devices when starting to stream
- Added meta key handling for DeX on newer Samsung devices
- Updated community contributed translations from Weblate