Compare commits

..

66 Commits

Author SHA1 Message Date
Cameron Gutman 56b814e877 Bump versioncode 2016-10-21 16:22:07 -07:00
Cameron Gutman 628ccd39d6 Fix default context getting picked up falsely during gamepad removal 2016-10-21 16:19:05 -07:00
Cameron Gutman 59db3f9b62 Fix Xbox button behavior quirks on Xbox One S controller 2016-10-21 15:57:00 -07:00
Cameron Gutman 416f922b56 Fix triggers stuck at 50% after controller reconnect 2016-10-21 14:54:21 -07:00
Cameron Gutman b52a86e6cc Fix app grid isRunning icon not displaying 2016-10-21 14:04:31 -07:00
Cameron Gutman e523b5069e Version 4.7.3 2016-10-21 13:48:03 -07:00
Cameron Gutman e8ae8d9807 Manually set pairing state after pairing 2016-10-21 13:47:03 -07:00
Cameron Gutman 64e56a861d Ignore case when sorting apps and PCs 2016-10-21 13:44:59 -07:00
Cameron Gutman c1bcd09c9b Disable launcher shortcuts pending further work 2016-10-21 12:52:37 -07:00
Cameron Gutman 574258804f Update to AS 2.2.2 and SDK 25 2016-10-21 12:50:04 -07:00
Cameron Gutman 21ea3d8a2b Fix 3rd party Xbox controller d-pads 2016-10-21 12:38:55 -07:00
Cameron Gutman 6de4288a85 Fix running app state on GFE 3.1 2016-10-21 12:28:15 -07:00
Cameron Gutman a107b5e652 Add launcher shortcuts and fix duplicate pairing error 2016-10-20 13:09:24 -07:00
Cameron Gutman b02db2c182 Fix JNI build warnings with modern NDKs 2016-10-19 20:47:23 -07:00
Cameron Gutman f8a04cda7a Version 4.7.2 2016-10-05 18:53:36 -07:00
Cameron Gutman 226e8edefc Update common jar to 55f0114 2016-10-05 18:49:57 -07:00
Cameron Gutman 9b90b30a1f Update build tools and version code 2016-09-28 22:31:55 -07:00
Cameron Gutman 2ed245b25a Add some extra text for GFE 3.0, since GameStream isn't on by default 2016-09-28 22:30:05 -07:00
Cameron Gutman 4b769839d0 Update common jar to 3c56730 2016-09-24 20:57:19 -07:00
Cameron Gutman 239dd1d5a1 Only display box art progress bar if a network load is required 2016-09-20 12:11:06 -07:00
Cameron Gutman 37509cce9b Update common jar to a76df84 2016-09-20 11:29:13 -07:00
Cameron Gutman 227c71549b Migrate project and NDK build to Android Studio 2.2 2016-09-20 11:26:56 -07:00
Cameron Gutman a10d8334f3 Update Gradle 2016-08-26 22:02:51 -07:00
Cameron Gutman 0fc61e52dd Fix Lint error in translation 2016-08-13 19:24:49 -07:00
Cameron Gutman 5e44c33bb6 Version 4.7 2016-08-13 19:22:29 -07:00
Cameron Gutman df3655e958 Add support for Xbox One S controller over USB 2016-08-13 19:21:49 -07:00
Cameron Gutman fe43e13145 Set larger dimensions for vector drawables so the generated PNGs are larger 2016-08-13 19:18:42 -07:00
Cameron Gutman acd3aad8d9 Add support for mouse emulation with a gamepad 2016-08-13 18:52:39 -07:00
Cameron Gutman 811b4b4f22 Better center overlays on PC view 2016-08-13 17:48:30 -07:00
Cameron Gutman 7db3b9f401 Use Material icons 2016-08-13 16:45:42 -07:00
Cameron Gutman a5a099cf43 Update common jar with 4K fix 2016-08-13 14:51:07 -07:00
Cameron Gutman ba605643bb Switch to indeterminate progress bars 2016-08-13 14:42:03 -07:00
Cameron Gutman 96e98c1abb Update translations 2016-08-13 12:32:49 -07:00
Cameron Gutman 5de6f6ae2b Fix build with Dutch translation 2016-08-13 12:29:08 -07:00
Cameron Gutman 0685722773 Merge branch 'master' of github.com:moonlight-stream/moonlight-android 2016-08-13 12:20:54 -07:00
Cameron Gutman 29df3b2859 Merge branch 'master' of https://github.com/halluci/moonlight-android 2016-08-13 12:14:37 -07:00
DragonSpirit fc6f859ced Russian Translation Added (#146) 2016-08-13 12:10:02 -07:00
Subject 6b21a5416f Dutch Translation Added (#118)
* Halfway through string translation

* Fixed up translation -- Ready for pull request
2016-08-13 12:09:39 -07:00
jeid64 74e7c8bbf1 Add basic building instructions to README (#227)
* Add basic building instructions to README

Added instructions to build APK for developers. Included getting submodules and installing NDK.

* Spelling fixes. Added ndk.dir
2016-08-13 12:07:20 -07:00
Cameron Gutman 757075b16a Add support for Xbox One S controller connected via Bluetooth 2016-08-13 12:01:52 -07:00
Cameron Gutman e8903c4d48 Update build tools to 24.0.1 2016-08-13 11:45:38 -07:00
halluci 98262d16ee Japanese translation added:updated 2016-07-17 22:01:49 +09:00
halluci 339506cf10 Japanese translation added 2016-07-17 17:22:30 +09:00
Cameron Gutman 63bd5df09b Prefer Evdev over N native capture since Evdev can capture over the system UI 2016-07-13 23:12:29 -07:00
Cameron Gutman 32af2d0831 Increment version code 2016-06-26 14:05:38 -07:00
Cameron Gutman 242b03d4b5 Add gradle.properties for Dex In-Process 2016-06-20 21:33:39 -07:00
Cameron Gutman 87a62666ac Prefer Shield capture provider over Android N 2016-06-20 20:43:59 -07:00
Cameron Gutman 2dcf5486da Revert "Display the running app first on the app grid"
This reverts commit 36f8cc02cb.
2016-06-20 20:30:47 -07:00
Cameron Gutman 60d3d8b3ae Version to 4.6 2016-06-18 15:17:15 -07:00
Cameron Gutman e9141d65fe Improve reliability of missing root detection 2016-06-18 14:54:53 -07:00
Cameron Gutman aae591daec Improve multi-window experience on N 2016-06-18 14:52:20 -07:00
Cameron Gutman a5ca8a7472 Add a hack to avoid crashing when the app window divider is dragged off of the screen on N multi-window 2016-06-18 14:40:42 -07:00
Cameron Gutman 36f8cc02cb Display the running app first on the app grid 2016-06-18 13:38:37 -07:00
Cameron Gutman 55b9645651 Fix minor Lint issues 2016-06-18 12:38:43 -07:00
Cameron Gutman d30ecbed5b Update gradle 2016-06-18 11:40:55 -07:00
Cameron Gutman 0bbd27f04c Update common jar 2016-06-18 11:37:00 -07:00
Cameron Gutman 3c53fb7403 Update target SDK to 24 2016-06-18 11:19:04 -07:00
Cameron Gutman 7a81950819 Enable sustained performance mode on N+ when streaming 2016-06-18 11:17:34 -07:00
Cameron Gutman 74f212c702 Add Android N mouse capture support 2016-06-18 11:15:53 -07:00
Cameron Gutman 36be943854 Add support for more Xbox controller models 2016-06-13 22:28:48 -05:00
Cameron Gutman 26a4fc75a5 Add handling for the ADT-1 controller 2016-06-13 21:28:54 -05:00
Cameron Gutman a5ec5fc265 Select the optimal display mode before streaming 2016-06-13 21:23:00 -05:00
Cameron Gutman 541ac44be4 Add an unified input capture interface 2016-06-13 20:33:43 -05:00
Cameron Gutman 117b555fcd Fix wiki link 2016-05-30 12:22:54 -05:00
Cameron Gutman a10cd04441 Clarify wording in H.265 settings 2016-05-29 16:39:20 -05:00
Cameron Gutman 53dccbde2a Repeat key down events are needed for proper key repeating 2016-05-29 15:52:18 -05:00
69 changed files with 1503 additions and 402 deletions
+2 -1
View File
@@ -32,4 +32,5 @@ Thumbs.db
build/
# Compiled JNI libraries folder
**/jniLibs
**/jniLibs
app/.externalNativeBuild/
+7 -1
View File
@@ -8,7 +8,7 @@ whether in your own home or over the internet.
[Moonlight-pc](https://github.com/moonlight-stream/moonlight-pc) is also currently in development for Windows, OS X and Linux. Versions for [iOS](https://github.com/moonlight-stream/moonlight-ios) and [Windows and Windows Phone](https://github.com/moonlight-stream/moonlight-windows) are also in development.
Check our [wiki](https://github.com/moonlight-stream/moonlight-android/wiki) for more detailed information or a troubleshooting guide.
Check our [wiki](https://github.com/moonlight-stream/moonlight-docs/wiki) for more detailed information or a troubleshooting guide.
##Features
@@ -46,6 +46,12 @@ This project is being actively developed at [XDA Developers](http://forum.xda-de
2. Write code
3. Send Pull Requests
##Building
* Install Android Studio and the Android NDK
* Run git submodule update --init --recursive from within moonlight-android/
* In moonlight-android/, create a file called local.properties. Add an ndk.dir= property to the local.properties file and set it equal to your NDK directory.
* Build the APK using Android Studio
##Authors
* [Cameron Gutman](https://github.com/cgutman)
+53 -20
View File
@@ -12,10 +12,7 @@
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleNonRootDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileNonRootDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleNonRootDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileNonRootDebugAndroidTestSources" />
<afterSyncTasks>
<task>generateNonRootDebugAndroidTestSources</task>
<task>generateNonRootDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
@@ -25,16 +22,26 @@
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
<facet type="native-android-gradle" name="Native-Android-Gradle">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="nonRootDebug" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/nonRoot/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/nonRoot/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/nonRoot/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/jnienet/enet" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/jnienet" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/evdev_reader" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/nv_opus_dec" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/nonRoot/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/nonRoot/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/nonRoot/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/nonRoot/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/nonRoot/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/nonRoot/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/nonRoot/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/res" type="java-resource" />
@@ -42,71 +49,97 @@
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/nonRoot/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/nonRoot/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndkBuild" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="bcprov-jdk15on-1.52" level="project" />
<orderEntry type="library" exported="" name="bcpkix-jdk15on-1.52" level="project" />
+14 -30
View File
@@ -4,24 +4,30 @@ import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
minSdkVersion 16
targetSdkVersion 23
targetSdkVersion 25
versionName "4.5.10"
versionCode = 101
versionName "4.7.3"
versionCode = 108
}
productFlavors {
root {
applicationId "com.limelight.root"
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64"
}
}
nonRoot {
applicationId "com.limelight"
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64"
}
}
}
@@ -43,32 +49,10 @@ android {
exclude 'META-INF/BCKEY.DSA'
}
sourceSets.main.jni.srcDirs = []
//noinspection GroovyAssignabilityCheck,GroovyAssignabilityCheck
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkDir = properties.getProperty('ndk.dir')
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "$ndkDir\\ndk-build.cmd",
'NDK_PROJECT_PATH=build/intermediates/ndk',
'NDK_LIBS_OUT=src/main/jniLibs',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
externalNativeBuild {
ndkBuild {
path "src/main/jni/Android.mk"
}
else {
commandLine "$ndkDir/ndk-build",
'NDK_PROJECT_PATH=build/intermediates/ndk',
'NDK_LIBS_OUT=src/main/jniLibs',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
}
Binary file not shown.
+1 -1
View File
@@ -37,7 +37,7 @@
</activity>
<activity
android:name=".AppView"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|fontScale|uiMode" >
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.limelight.PcView" />
+75 -29
View File
@@ -11,12 +11,14 @@ import com.limelight.grid.AppGridAdapter;
import com.limelight.nvstream.http.ComputerDetails;
import com.limelight.nvstream.http.NvApp;
import com.limelight.nvstream.http.NvHTTP;
import com.limelight.nvstream.http.PairingManager;
import com.limelight.preferences.PreferenceConfiguration;
import com.limelight.ui.AdapterFragment;
import com.limelight.ui.AdapterFragmentCallbacks;
import com.limelight.utils.CacheHelper;
import com.limelight.utils.Dialog;
import com.limelight.utils.ServerHelper;
import com.limelight.utils.ShortcutHelper;
import com.limelight.utils.SpinnerDialog;
import com.limelight.utils.UiHelper;
@@ -43,14 +45,16 @@ import android.widget.AdapterView.AdapterContextMenuInfo;
public class AppView extends Activity implements AdapterFragmentCallbacks {
private AppGridAdapter appGridAdapter;
private String uuidString;
private ShortcutHelper shortcutHelper;
private ComputerDetails computer;
private ComputerManagerService.ApplistPoller poller;
private SpinnerDialog blockingLoadSpinner;
private SpinnerDialog blockingLoadSpinner, blockingServerinfoSpinner;
private String lastRawApplist;
private int lastRunningAppId;
private boolean suspendGridUpdates;
private boolean inForeground;
private boolean launchedFromShortcut;
private final static int START_OR_RESUME_ID = 1;
private final static int QUIT_ID = 2;
@@ -59,6 +63,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
public final static String NAME_EXTRA = "Name";
public final static String UUID_EXTRA = "UUID";
public final static String SHORTCUT_EXTRA = "Shortcut";
private ComputerManagerService.ComputerManagerBinder managerBinder;
private final ServiceConnection serviceConnection = new ServiceConnection() {
@@ -90,12 +95,15 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
return;
}
// Load the app grid with cached data (if possible).
// This must be done _before_ startComputerUpdates()
// so the initial serverinfo response can update the running
// icon.
populateAppGridWithCache();
// Start updates
startComputerUpdates();
// Load the app grid with cached data (if possible)
populateAppGridWithCache();
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -132,7 +140,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
managerBinder.startPolling(new ComputerManagerListener() {
@Override
public void notifyComputerUpdated(ComputerDetails details) {
public void notifyComputerUpdated(final ComputerDetails details) {
// Do nothing if updates are suspended
if (suspendGridUpdates) {
return;
@@ -157,6 +165,48 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
return;
}
// Close immediately if the PC is no longer paired
if (details.state == ComputerDetails.State.ONLINE && details.pairState != PairingManager.PairState.PAIRED) {
AppView.this.runOnUiThread(new Runnable() {
@Override
public void run() {
// Disable shortcuts referencing this PC for now
shortcutHelper.disableShortcut(details.uuid.toString(),
getResources().getString(R.string.scut_not_paired));
// Display a toast to the user and quit the activity
Toast.makeText(AppView.this, getResources().getText(R.string.scut_not_paired), Toast.LENGTH_SHORT).show();
finish();
}
});
return;
}
if (launchedFromShortcut) {
if (details.state == ComputerDetails.State.ONLINE) {
if (blockingServerinfoSpinner != null) {
blockingServerinfoSpinner.dismiss();
blockingServerinfoSpinner = null;
}
if (details.runningGameId != 0) {
AppView.this.runOnUiThread(new Runnable() {
@Override
public void run() {
// We have to finish this activity here otherwise we'll get into a loop
// when the user hits back
finish();
// When launched from shortcut, resume the running game
ServerHelper.doStart(AppView.this, new NvApp("app", details.runningGameId), computer, managerBinder);
}
});
return;
}
}
}
// App list is the same or empty
if (details.rawAppList == null || details.rawAppList.equals(lastRawApplist)) {
@@ -175,6 +225,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
try {
updateUiWithAppList(NvHTTP.getAppListByReader(new StringReader(details.rawAppList)));
updateUiWithServerinfo(details);
if (blockingLoadSpinner != null) {
blockingLoadSpinner.dismiss();
@@ -208,6 +259,8 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
shortcutHelper = new ShortcutHelper(this);
String locale = PreferenceConfiguration.readPreferences(this).language;
if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) {
Configuration config = new Configuration(getResources().getConfiguration());
@@ -221,6 +274,15 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
uuidString = getIntent().getStringExtra(UUID_EXTRA);
launchedFromShortcut = getIntent().getBooleanExtra(SHORTCUT_EXTRA, false);
if (launchedFromShortcut) {
// Display blocking loading spinner
blockingLoadSpinner = SpinnerDialog.displayDialog(this, getResources().getString(R.string.conn_establishing_title),
getResources().getString(R.string.applist_connect_msg), true);
}
shortcutHelper.reportShortcutUsed(uuidString);
String labelText = getResources().getString(R.string.title_applist)+" "+getIntent().getStringExtra(NAME_EXTRA);
TextView label = (TextView) findViewById(R.id.appListText);
setTitle(labelText);
@@ -282,27 +344,14 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
stopComputerUpdates();
}
private int getRunningAppId() {
int runningAppId = -1;
for (int i = 0; i < appGridAdapter.getCount(); i++) {
AppObject app = (AppObject) appGridAdapter.getItem(i);
if (app.app.getIsRunning()) {
runningAppId = app.app.getAppId();
break;
}
}
return runningAppId;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
AppObject selectedApp = (AppObject) appGridAdapter.getItem(info.position);
int runningAppId = getRunningAppId();
if (runningAppId != -1) {
if (runningAppId == selectedApp.app.getAppId()) {
if (lastRunningAppId != 0) {
if (lastRunningAppId == selectedApp.app.getAppId()) {
menu.add(Menu.NONE, START_OR_RESUME_ID, 1, getResources().getString(R.string.applist_menu_resume));
menu.add(Menu.NONE, QUIT_ID, 2, getResources().getString(R.string.applist_menu_quit));
}
@@ -378,19 +427,19 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
AppObject existingApp = (AppObject) appGridAdapter.getItem(i);
// There can only be one or zero apps running.
if (existingApp.app.getIsRunning() &&
if (existingApp.isRunning &&
existingApp.app.getAppId() == details.runningGameId) {
// This app was running and still is, so we're done now
return;
}
else if (existingApp.app.getAppId() == details.runningGameId) {
// This app wasn't running but now is
existingApp.app.setIsRunning(true);
existingApp.isRunning = true;
updated = true;
}
else if (existingApp.app.getIsRunning()) {
else if (existingApp.isRunning) {
// This app was running but now isn't
existingApp.app.setIsRunning(false);
existingApp.isRunning = false;
updated = true;
}
else {
@@ -420,10 +469,6 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
AppObject existingApp = (AppObject) appGridAdapter.getItem(i);
if (existingApp.app.getAppId() == app.getAppId()) {
// Found the app; update its properties
if (existingApp.app.getIsRunning() != app.getIsRunning()) {
existingApp.app.setIsRunning(app.getIsRunning());
updated = true;
}
if (!existingApp.app.getAppName().equals(app.getAppName())) {
existingApp.app.setAppName(app.getAppName());
updated = true;
@@ -493,7 +538,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
AppObject app = (AppObject) appGridAdapter.getItem(pos);
// Only open the context menu if something is running, otherwise start it
if (getRunningAppId() != -1) {
if (lastRunningAppId != 0) {
openContextMenu(arg1);
} else {
ServerHelper.doStart(AppView.this, app.app, computer, managerBinder);
@@ -506,6 +551,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
public class AppObject {
public final NvApp app;
public boolean isRunning;
public AppObject(NvApp app) {
if (app == null) {
+121 -64
View File
@@ -4,10 +4,10 @@ package com.limelight;
import com.limelight.binding.PlatformBinding;
import com.limelight.binding.input.ControllerHandler;
import com.limelight.binding.input.KeyboardTranslator;
import com.limelight.binding.input.NvMouseHelper;
import com.limelight.binding.input.capture.InputCaptureManager;
import com.limelight.binding.input.capture.InputCaptureProvider;
import com.limelight.binding.input.TouchContext;
import com.limelight.binding.input.driver.UsbDriverService;
import com.limelight.binding.input.evdev.EvdevHandler;
import com.limelight.binding.input.evdev.EvdevListener;
import com.limelight.binding.input.virtual_controller.VirtualController;
import com.limelight.binding.video.EnhancedDecoderRenderer;
@@ -39,6 +39,7 @@ import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -48,12 +49,10 @@ import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnGenericMotionListener;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -92,7 +91,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private boolean connecting = false;
private boolean connected = false;
private EvdevHandler evdevHandler;
private InputCaptureProvider inputCaptureProvider;
private int modifierFlags = 0;
private boolean grabbedInput = true;
private boolean grabComboDown = false;
@@ -242,40 +241,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Initialize the connection
conn = new NvConnection(host, uniqueId, Game.this, config, PlatformBinding.getCryptoProvider(this));
keybTranslator = new KeyboardTranslator(conn);
controllerHandler = new ControllerHandler(conn, this, prefConfig.multiController, prefConfig.deadzonePercentage);
controllerHandler = new ControllerHandler(this, conn, this, prefConfig.multiController, prefConfig.deadzonePercentage);
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
inputManager.registerInputDeviceListener(controllerHandler, null);
boolean aspectRatioMatch = false;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
// On KitKat and later (where we can use the whole screen via immersive mode), we'll
// calculate whether we need to scale by aspect ratio or not. If not, we'll use
// setFixedSize so we can handle 4K properly. The only known devices that have
// >= 4K screens have exactly 4K screens, so we'll be able to hit this good path
// on these devices. On Marshmallow, we can start changing to 4K manually but no
// 4K devices run 6.0 at the moment.
Display display = getWindowManager().getDefaultDisplay();
Point screenSize = new Point(0, 0);
display.getSize(screenSize);
double screenAspectRatio = ((double)screenSize.y) / screenSize.x;
double streamAspectRatio = ((double)prefConfig.height) / prefConfig.width;
if (Math.abs(screenAspectRatio - streamAspectRatio) < 0.001) {
LimeLog.info("Stream has compatible aspect ratio with output display");
aspectRatioMatch = true;
}
}
SurfaceHolder sh = streamView.getHolder();
if (prefConfig.stretchVideo || aspectRatioMatch) {
// Set the surface to the size of the video
sh.setFixedSize(prefConfig.width, prefConfig.height);
}
else {
// Set the surface to scale based on the aspect ratio of the stream
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
}
// Set to the optimal mode for streaming
prepareDisplayForRendering();
// Initialize touch contexts
for (int i = 0; i < touchContextMap.length; i++) {
@@ -284,12 +256,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
streamView);
}
if (LimelightBuildProps.ROOT_BUILD) {
// Start watching for raw input
evdevHandler = new EvdevHandler(this, this);
evdevHandler.start();
// Use sustained performance mode on N+ to ensure consistent
// CPU availability
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
getWindow().setSustainedPerformanceMode(true);
}
inputCaptureProvider = InputCaptureManager.getInputCaptureProvider(this, this);
if (prefConfig.onscreenController) {
// create virtual onscreen controller
virtualController = new VirtualController(conn,
@@ -305,7 +279,96 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
// The connection will be started when the surface gets created
sh.addCallback(this);
streamView.getHolder().addCallback(this);
}
private void prepareDisplayForRendering() {
Display display = getWindowManager().getDefaultDisplay();
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
// On M, we can explicitly set the optimal display mode
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Display.Mode bestMode = display.getMode();
for (Display.Mode candidate : display.getSupportedModes()) {
boolean refreshRateOk = candidate.getRefreshRate() >= bestMode.getRefreshRate() &&
candidate.getRefreshRate() < 63;
boolean resolutionOk = candidate.getPhysicalWidth() >= bestMode.getPhysicalWidth() &&
candidate.getPhysicalHeight() >= bestMode.getPhysicalHeight() &&
candidate.getPhysicalWidth() <= 4096;
LimeLog.info("Examining display mode: "+candidate.getPhysicalWidth()+"x"+
candidate.getPhysicalHeight()+"x"+candidate.getRefreshRate());
// On non-4K streams, we force the resolution to never change
if (prefConfig.width < 3840) {
if (display.getMode().getPhysicalWidth() != candidate.getPhysicalWidth() ||
display.getMode().getPhysicalHeight() != candidate.getPhysicalHeight()) {
continue;
}
}
// Make sure the refresh rate doesn't regress
if (!refreshRateOk) {
continue;
}
// Make sure the resolution doesn't regress
if (!resolutionOk) {
continue;
}
bestMode = candidate;
}
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
windowLayoutParams.preferredDisplayModeId = bestMode.getModeId();
}
// On L, we can at least tell the OS that we want 60 Hz
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
float bestRefreshRate = display.getRefreshRate();
for (float candidate : display.getSupportedRefreshRates()) {
if (candidate > bestRefreshRate && candidate < 63) {
LimeLog.info("Examining refresh rate: "+candidate);
bestRefreshRate = candidate;
}
}
LimeLog.info("Selected refresh rate: "+bestRefreshRate);
windowLayoutParams.preferredRefreshRate = bestRefreshRate;
}
// Apply the display mode change
getWindow().setAttributes(windowLayoutParams);
// From 4.4 to 5.1 we can't ask for a 4K display mode, so we'll
// need to hint the OS to provide one.
boolean aspectRatioMatch = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
// On KitKat and later (where we can use the whole screen via immersive mode), we'll
// calculate whether we need to scale by aspect ratio or not. If not, we'll use
// setFixedSize so we can handle 4K properly. The only known devices that have
// >= 4K screens have exactly 4K screens, so we'll be able to hit this good path
// on these devices. On Marshmallow, we can start changing to 4K manually but no
// 4K devices run 6.0 at the moment.
Point screenSize = new Point(0, 0);
display.getSize(screenSize);
double screenAspectRatio = ((double)screenSize.y) / screenSize.x;
double streamAspectRatio = ((double)prefConfig.height) / prefConfig.width;
if (Math.abs(screenAspectRatio - streamAspectRatio) < 0.001) {
LimeLog.info("Stream has compatible aspect ratio with output display");
aspectRatioMatch = true;
}
}
if (prefConfig.stretchVideo || aspectRatioMatch) {
// Set the surface to the size of the video
streamView.getHolder().setFixedSize(prefConfig.width, prefConfig.height);
}
else {
// Set the surface to scale based on the aspect ratio of the stream
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
}
}
private void checkDataConnection()
@@ -406,16 +469,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public void run() {
if (grabbedInput) {
NvMouseHelper.setCursorVisibility(Game.this, true);
if (evdevHandler != null) {
evdevHandler.ungrabAll();
}
inputCaptureProvider.disableCapture();
}
else {
NvMouseHelper.setCursorVisibility(Game.this, false);
if (evdevHandler != null) {
evdevHandler.regrabAll();
}
inputCaptureProvider.enableCapture();
}
grabbedInput = !grabbedInput;
@@ -522,11 +579,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
return true;
}
// Eat repeat down events
if (event.getRepeatCount() > 0) {
return true;
}
// Pass through keyboard input if we're not grabbing
if (!grabbedInput) {
return super.onKeyDown(keyCode, event);
@@ -643,10 +695,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
}
// Get relative axis values if we can
if (NvMouseHelper.eventHasRelativeMouseAxes(event)) {
if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) {
// Send the deltas straight from the motion event
conn.sendMouseMove((short)NvMouseHelper.getRelativeAxisX(event),
(short)NvMouseHelper.getRelativeAxisY(event));
conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event),
(short) inputCaptureProvider.getRelativeAxisY(event));
// We have to also update the position Android thinks the cursor is at
// in order to avoid jumping when we stop moving or click.
@@ -823,14 +875,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
conn.stop();
}
// Close the Evdev reader to allow use of captured input devices
if (evdevHandler != null) {
evdevHandler.stop();
evdevHandler = null;
}
// Enable cursor visibility again
NvMouseHelper.setCursorVisibility(this, true);
inputCaptureProvider.disableCapture();
// Destroy the capture provider
inputCaptureProvider.destroy();
}
@Override
@@ -876,7 +925,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
// Hide the mouse cursor now. Doing it before
// dismissing the spinner seems to be undone
// when the spinner gets displayed.
NvMouseHelper.setCursorVisibility(Game.this, false);
inputCaptureProvider.enableCapture();
}
});
@@ -922,6 +971,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (connected) {
// HACK: Android is supposed to let you return from this function
// before throwing a fit if you access the surface again. Unfortunately,
// MediaCodec often tries to access the destroyed surface and triggers
// an IllegalStateException. To workaround this, we will invoke
// the DecoderRenderer's stop function ourselves, so it will hopefully
// happen early enough to not trigger the bug
decoderRenderer.stop();
stopConnection();
}
}
+16 -2
View File
@@ -24,6 +24,7 @@ import com.limelight.ui.AdapterFragment;
import com.limelight.ui.AdapterFragmentCallbacks;
import com.limelight.utils.Dialog;
import com.limelight.utils.ServerHelper;
import com.limelight.utils.ShortcutHelper;
import com.limelight.utils.UiHelper;
import android.app.Activity;
@@ -52,6 +53,7 @@ import android.widget.AdapterView.AdapterContextMenuInfo;
public class PcView extends Activity implements AdapterFragmentCallbacks {
private RelativeLayout noPcFoundLayout;
private PcGridAdapter pcGridAdapter;
private ShortcutHelper shortcutHelper;
private ComputerManagerService.ComputerManagerBinder managerBinder;
private boolean freezeUpdates, runningPolling, inForeground;
private final ServiceConnection serviceConnection = new ServiceConnection() {
@@ -142,6 +144,8 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
shortcutHelper = new ShortcutHelper(this);
String locale = PreferenceConfiguration.readPreferences(this).language;
if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) {
Configuration config = new Configuration(getResources().getConfiguration());
@@ -299,7 +303,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
// Stop updates and wait while pairing
stopComputerUpdates(true);
InetAddress addr = null;
InetAddress addr;
if (computer.reachability == ComputerDetails.Reachability.LOCAL) {
addr = computer.localIp;
}
@@ -334,6 +338,9 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
else if (pairState == PairingManager.PairState.FAILED) {
message = getResources().getString(R.string.pair_fail);
}
else if (pairState == PairingManager.PairState.ALREADY_IN_PROGRESS) {
message = getResources().getString(R.string.pair_already_in_progress);
}
else if (pairState == PairingManager.PairState.PAIRED) {
// Just navigate to the app view without displaying a toast
message = null;
@@ -366,6 +373,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
if (toastSuccess) {
// Open the app list after a successful pairing attempt
computer.pairState = PairState.PAIRED;
doAppList(computer);
}
else {
@@ -429,7 +437,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
NvHTTP httpConn;
String message;
try {
InetAddress addr = null;
InetAddress addr;
if (computer.reachability == ComputerDetails.Reachability.LOCAL) {
addr = computer.localIp;
}
@@ -486,6 +494,8 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
return;
}
shortcutHelper.createAppViewShortcut(computer.uuid.toString(), computer);
Intent i = new Intent(this, AppView.class);
i.putExtra(AppView.NAME_EXTRA, computer.name);
i.putExtra(AppView.UUID_EXTRA, computer.uuid.toString());
@@ -558,6 +568,10 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
ComputerObject computer = (ComputerObject) pcGridAdapter.getItem(i);
if (details.equals(computer.details)) {
// Disable or delete shortcuts referencing this PC
shortcutHelper.disableShortcut(details.uuid.toString(),
getResources().getString(R.string.scut_deleted_pc));
pcGridAdapter.removeComputer(computer);
pcGridAdapter.notifyDataSetChanged();
@@ -1,24 +1,31 @@
package com.limelight.binding.input;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.SystemClock;
import android.util.SparseArray;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.Toast;
import com.limelight.LimeLog;
import com.limelight.binding.input.driver.UsbDriverListener;
import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket;
import com.limelight.nvstream.input.MouseButtonPacket;
import com.limelight.ui.GameGestures;
import com.limelight.utils.Vector2d;
import java.util.Timer;
import java.util.TimerTask;
public class ControllerHandler implements InputManager.InputDeviceListener, UsbDriverListener {
private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100;
private static final int START_DOWN_TIME_KEYB_MS = 750;
private static final int START_DOWN_TIME_MOUSE_MODE_MS = 750;
private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 25;
@@ -34,6 +41,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private final SparseArray<UsbDeviceContext> usbDeviceContexts = new SparseArray<>();
private final NvConnection conn;
private final Context activityContext;
private final double stickDeadzone;
private final InputDeviceContext defaultContext = new InputDeviceContext();
private final GameGestures gestures;
@@ -42,7 +50,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
private final boolean multiControllerEnabled;
private short currentControllers;
public ControllerHandler(NvConnection conn, GameGestures gestures, boolean multiControllerEnabled, int deadzonePercentage) {
public ControllerHandler(Context activityContext, NvConnection conn, GameGestures gestures, boolean multiControllerEnabled, int deadzonePercentage) {
this.activityContext = activityContext;
this.conn = conn;
this.gestures = gestures;
this.multiControllerEnabled = multiControllerEnabled;
@@ -342,6 +351,17 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
}
}
// The ADT-1 controller needs a similar fixup to the ASUS Gamepad
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
// The device name provided is just "Gamepad" which is pretty useless, so we
// use VID/PID instead
if (dev.getVendorId() == 0x18d1 && dev.getProductId() == 0x2c40) {
context.backIsStart = true;
context.modeIsSelect = true;
context.triggerDeadzone = 0.30f;
}
}
if (devName != null) {
// For the Nexus Player (and probably other ATV devices), we should
// use the back button as start since it doesn't have a start/menu button
@@ -384,6 +404,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
context.isServal = true;
context.ignoreBack = true;
}
// The Xbox One S Bluetooth controller has some mappings that need fixing up
else if (devName.equals("Xbox Wireless Controller")) {
context.isXboxBtController = true;
}
}
LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius);
@@ -392,21 +416,27 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return context;
}
private InputDeviceContext getContextForDevice(InputDevice dev) {
private InputDeviceContext getContextForEvent(InputEvent event) {
// Unknown devices use the default context
if (dev == null) {
if (event.getDeviceId() == 0) {
return defaultContext;
}
else if (event.getDevice() == null) {
// During device removal, sometimes we can get events after the
// input device has been destroyed. In this case we'll see a
// != 0 device ID but no device attached.
return null;
}
// Return the existing context if it exists
InputDeviceContext context = inputDeviceContexts.get(dev.getId());
InputDeviceContext context = inputDeviceContexts.get(event.getDeviceId());
if (context != null) {
return context;
}
// Otherwise create a new context
context = createInputDeviceContextForDevice(dev);
inputDeviceContexts.put(dev.getId(), context);
context = createInputDeviceContextForDevice(event.getDevice());
inputDeviceContexts.put(event.getDeviceId(), context);
return context;
}
@@ -451,7 +481,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
// device before we send it.
for (int i = 0; i < inputDeviceContexts.size(); i++) {
GenericControllerContext context = inputDeviceContexts.valueAt(i);
if (context.assignedControllerNumber && context.controllerNumber == controllerNumber) {
if (context.assignedControllerNumber &&
context.controllerNumber == controllerNumber &&
context.mouseEmulationActive == originalContext.mouseEmulationActive) {
inputMap |= context.inputMap;
leftTrigger |= maxByMagnitude(leftTrigger, context.leftTrigger);
rightTrigger |= maxByMagnitude(rightTrigger, context.rightTrigger);
@@ -463,7 +495,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
}
for (int i = 0; i < usbDeviceContexts.size(); i++) {
GenericControllerContext context = usbDeviceContexts.valueAt(i);
if (context.assignedControllerNumber && context.controllerNumber == controllerNumber) {
if (context.assignedControllerNumber &&
context.controllerNumber == controllerNumber &&
context.mouseEmulationActive == originalContext.mouseEmulationActive) {
inputMap |= context.inputMap;
leftTrigger |= maxByMagnitude(leftTrigger, context.leftTrigger);
rightTrigger |= maxByMagnitude(rightTrigger, context.rightTrigger);
@@ -483,10 +517,40 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
rightStickY |= maxByMagnitude(rightStickY, defaultContext.rightStickY);
}
conn.sendControllerInput(controllerNumber, inputMap,
leftTrigger, rightTrigger,
leftStickX, leftStickY,
rightStickX, rightStickY);
if (originalContext.mouseEmulationActive) {
int changedMask = inputMap ^ originalContext.mouseEmulationLastInputMap;
boolean aDown = (inputMap & ControllerPacket.A_FLAG) != 0;
boolean bDown = (inputMap & ControllerPacket.B_FLAG) != 0;
originalContext.mouseEmulationLastInputMap = inputMap;
if ((changedMask & ControllerPacket.A_FLAG) != 0) {
if (aDown) {
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_LEFT);
}
else {
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
}
}
if ((changedMask & ControllerPacket.B_FLAG) != 0) {
if (bDown) {
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
}
else {
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
}
}
conn.sendControllerInput(controllerNumber,
(short)0, (byte)0, (byte)0, (short)0, (short)0, (short)0, (short)0);
}
else {
conn.sendControllerInput(controllerNumber, inputMap,
leftTrigger, rightTrigger,
leftStickX, leftStickY,
rightStickX, rightStickY);
}
}
// Return a valid keycode, 0 to consume, or -1 to not consume the event
@@ -547,6 +611,35 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return KeyEvent.KEYCODE_BUTTON_START;
}
}
else if (context.isXboxBtController) {
switch (event.getScanCode()) {
case 306:
return KeyEvent.KEYCODE_BUTTON_X;
case 307:
return KeyEvent.KEYCODE_BUTTON_Y;
case 308:
return KeyEvent.KEYCODE_BUTTON_L1;
case 309:
return KeyEvent.KEYCODE_BUTTON_R1;
case 310:
return KeyEvent.KEYCODE_BUTTON_SELECT;
case 311:
return KeyEvent.KEYCODE_BUTTON_START;
case 312:
return KeyEvent.KEYCODE_BUTTON_THUMBL;
case 313:
return KeyEvent.KEYCODE_BUTTON_THUMBR;
case 139:
return KeyEvent.KEYCODE_BUTTON_MODE;
default:
// Other buttons are mapped correctly
}
// The Xbox button is sent as MENU
if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) {
return KeyEvent.KEYCODE_BUTTON_MODE;
}
}
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
switch (event.getKeyCode()) {
@@ -561,7 +654,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
}
else if (context.hatXAxis == -1 &&
context.hatYAxis == -1 &&
context.isXboxController &&
/* FIXME: There's no good way to know for sure if xpad is bound
to this device, so we won't use the name to validate if these
scancodes should be mapped to DPAD
context.isXboxController &&
*/
event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
// If there's not a proper Xbox controller mapping, we'll translate the raw d-pad
// scan codes into proper key codes
@@ -651,9 +749,23 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
}
if (context.leftTriggerAxis != -1 && context.rightTriggerAxis != -1) {
// Android sends an initial 0 value for trigger axes even if the trigger
// should be negative when idle. After the first touch, the axes will go back
// to normal behavior, so ignore triggersIdleNegative for each trigger until
// first touch.
if (lt != 0) {
context.leftTriggerUsed = true;
}
if (rt != 0) {
context.rightTriggerUsed = true;
}
if (context.triggersIdleNegative) {
lt = (lt + 1) / 2;
rt = (rt + 1) / 2;
if (context.leftTriggerUsed) {
lt = (lt + 1) / 2;
}
if (context.rightTriggerUsed) {
rt = (rt + 1) / 2;
}
}
if (lt <= context.triggerDeadzone) {
@@ -689,7 +801,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
}
public boolean handleMotionEvent(MotionEvent event) {
InputDeviceContext context = getContextForDevice(event.getDevice());
InputDeviceContext context = getContextForEvent(event);
if (context == null) {
return true;
}
float lsX = 0, lsY = 0, rsX = 0, rsY = 0, rt = 0, lt = 0, hatX = 0, hatY = 0;
// We purposefully ignore the historical values in the motion event as it makes
@@ -720,8 +836,51 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
return true;
}
private short scaleRawStickAxis(float stickValue) {
return (short)Math.pow(stickValue, 3);
}
private void sendEmulatedMouseEvent(short x, short y) {
Vector2d vector = new Vector2d();
vector.initialize(x, y);
vector.scalarMultiply(1 / 32766.0f);
vector.scalarMultiply(4);
if (vector.getMagnitude() > 0) {
// Move faster as the stick is pressed further from center
vector.scalarMultiply(Math.pow(vector.getMagnitude(), 2));
if (vector.getMagnitude() >= 1) {
conn.sendMouseMove((short)vector.getX(), (short)-vector.getY());
}
}
}
private void toggleMouseEmulation(final GenericControllerContext context) {
if (context.mouseEmulationTimer != null) {
context.mouseEmulationTimer.cancel();
context.mouseEmulationTimer = null;
}
context.mouseEmulationActive = !context.mouseEmulationActive;
Toast.makeText(activityContext, "Mouse emulation is: " + (context.mouseEmulationActive ? "ON" : "OFF"), Toast.LENGTH_SHORT).show();
if (context.mouseEmulationActive) {
context.mouseEmulationTimer = new Timer();
context.mouseEmulationTimer.schedule(new TimerTask() {
@Override
public void run() {
// Send mouse movement events from analog sticks
sendEmulatedMouseEvent(context.leftStickX, context.leftStickY);
sendEmulatedMouseEvent(context.rightStickX, context.rightStickY);
}
}, 50, 50);
}
}
public boolean handleButtonUp(KeyEvent event) {
InputDeviceContext context = getContextForDevice(event.getDevice());
InputDeviceContext context = getContextForEvent(event);
if (context == null) {
return true;
}
int keyCode = handleRemapping(context, event);
if (keyCode == 0) {
@@ -746,10 +905,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
break;
case KeyEvent.KEYCODE_BUTTON_START:
case KeyEvent.KEYCODE_MENU:
if (SystemClock.uptimeMillis() - context.startDownTime > ControllerHandler.START_DOWN_TIME_KEYB_MS) {
// FIXME: The stock keyboard doesn't have controller focus so isn't usable. I'm not enabling this shortcut
// until we have a custom keyboard or some other fix
//gestures.showKeyboard();
// Sometimes we'll get a spurious key up event on controller disconnect.
// Make sure it's real by checking that the key is actually down before taking
// any action.
if ((context.inputMap & ControllerPacket.PLAY_FLAG) != 0 &&
SystemClock.uptimeMillis() - context.startDownTime > ControllerHandler.START_DOWN_TIME_MOUSE_MODE_MS) {
toggleMouseEmulation(context);
}
context.inputMap &= ~ControllerPacket.PLAY_FLAG;
break;
@@ -846,7 +1007,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
}
public boolean handleButtonDown(KeyEvent event) {
InputDeviceContext context = getContextForDevice(event.getDevice());
InputDeviceContext context = getContextForEvent(event);
if (context == null) {
return true;
}
int keyCode = handleRemapping(context, event);
if (keyCode == 0) {
@@ -1016,6 +1180,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public short rightStickY = 0x0000;
public short leftStickX = 0x0000;
public short leftStickY = 0x0000;
public boolean mouseEmulationActive;
public Timer mouseEmulationTimer;
public short mouseEmulationLastInputMap;
}
class InputDeviceContext extends GenericControllerContext {
@@ -1030,12 +1198,14 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
public int leftTriggerAxis = -1;
public int rightTriggerAxis = -1;
public boolean triggersIdleNegative;
public boolean leftTriggerUsed, rightTriggerUsed;
public int hatXAxis = -1;
public int hatYAxis = -1;
public boolean isDualShock4;
public boolean isXboxController;
public boolean isXboxBtController;
public boolean isServal;
public boolean backIsStart;
public boolean modeIsSelect;
@@ -0,0 +1,59 @@
package com.limelight.binding.input.capture;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.View;
import android.view.ViewGroup;
@TargetApi(Build.VERSION_CODES.N)
public class AndroidCaptureProvider extends InputCaptureProvider {
private ViewGroup rootViewGroup;
private Context context;
public AndroidCaptureProvider(Activity activity) {
this.context = activity;
this.rootViewGroup = (ViewGroup) activity.getWindow().getDecorView();
}
public static boolean isCaptureProviderSupported() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
}
private void setPointerIconOnAllViews(PointerIcon icon) {
for (int i = 0; i < rootViewGroup.getChildCount(); i++) {
View view = rootViewGroup.getChildAt(i);
view.setPointerIcon(icon);
}
rootViewGroup.setPointerIcon(icon);
}
@Override
public void enableCapture() {
setPointerIconOnAllViews(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
}
@Override
public void disableCapture() {
setPointerIconOnAllViews(null);
}
@Override
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X) != 0 ||
event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y) != 0;
}
@Override
public float getRelativeAxisX(MotionEvent event) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
}
@Override
public float getRelativeAxisY(MotionEvent event) {
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
}
}
@@ -0,0 +1,31 @@
package com.limelight.binding.input.capture;
import android.app.Activity;
import com.limelight.LimeLog;
import com.limelight.binding.input.evdev.EvdevCaptureProvider;
import com.limelight.binding.input.evdev.EvdevListener;
public class InputCaptureManager {
public static InputCaptureProvider getInputCaptureProvider(Activity activity, EvdevListener rootListener) {
// Shield capture is preferred because it can capture when the cursor is over
// the system UI. Android N native capture can only capture over views owned
// by the application.
if (ShieldCaptureProvider.isCaptureProviderSupported()) {
LimeLog.info("Using NVIDIA mouse capture extension");
return new ShieldCaptureProvider(activity);
}
else if (EvdevCaptureProvider.isCaptureProviderSupported()) {
LimeLog.info("Using Evdev mouse capture");
return new EvdevCaptureProvider(activity, rootListener);
}
else if (AndroidCaptureProvider.isCaptureProviderSupported()) {
LimeLog.info("Using Android N+ native mouse capture");
return new AndroidCaptureProvider(activity);
}
else {
LimeLog.info("Mouse capture not available");
return new NullCaptureProvider();
}
}
}
@@ -0,0 +1,21 @@
package com.limelight.binding.input.capture;
import android.view.MotionEvent;
public abstract class InputCaptureProvider {
public abstract void enableCapture();
public abstract void disableCapture();
public void destroy() {}
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
return false;
}
public float getRelativeAxisX(MotionEvent event) {
return 0;
}
public float getRelativeAxisY(MotionEvent event) {
return 0;
}
}
@@ -0,0 +1,10 @@
package com.limelight.binding.input.capture;
public class NullCaptureProvider extends InputCaptureProvider {
@Override
public void enableCapture() {}
@Override
public void disableCapture() {}
}
@@ -1,12 +1,10 @@
package com.limelight.binding.input;
package com.limelight.binding.input.capture;
import android.content.Context;
import android.hardware.input.InputManager;
import android.view.MotionEvent;
import com.limelight.LimeLog;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -18,12 +16,14 @@ import java.lang.reflect.Method;
//
// http://docs.nvidia.com/gameworks/index.html#technologies/mobile/game_controller_handling_mouse.htm
public class NvMouseHelper {
public class ShieldCaptureProvider extends InputCaptureProvider {
private static boolean nvExtensionSupported;
private static Method methodSetCursorVisibility;
private static int AXIS_RELATIVE_X;
private static int AXIS_RELATIVE_Y;
private Context context;
static {
try {
methodSetCursorVisibility = InputManager.class.getMethod("setCursorVisibility", boolean.class);
@@ -36,16 +36,19 @@ public class NvMouseHelper {
nvExtensionSupported = true;
} catch (Exception e) {
LimeLog.info("NvMouseHelper not supported");
nvExtensionSupported = false;
}
}
public static boolean setCursorVisibility(Context context, boolean visible) {
if (!nvExtensionSupported) {
return false;
}
public ShieldCaptureProvider(Context context) {
this.context = context;
}
public static boolean isCaptureProviderSupported() {
return nvExtensionSupported;
}
private boolean setCursorVisibility(boolean visible) {
try {
methodSetCursorVisibility.invoke(context.getSystemService(Context.INPUT_SERVICE), visible);
return true;
@@ -58,19 +61,29 @@ public class NvMouseHelper {
return false;
}
public static boolean eventHasRelativeMouseAxes(MotionEvent event) {
if (!nvExtensionSupported) {
return false;
}
@Override
public void enableCapture() {
setCursorVisibility(false);
}
@Override
public void disableCapture() {
setCursorVisibility(true);
}
@Override
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
return event.getAxisValue(AXIS_RELATIVE_X) != 0 ||
event.getAxisValue(AXIS_RELATIVE_Y) != 0;
}
public static float getRelativeAxisX(MotionEvent event) {
@Override
public float getRelativeAxisX(MotionEvent event) {
return event.getAxisValue(AXIS_RELATIVE_X);
}
public static float getRelativeAxisY(MotionEvent event) {
@Override
public float getRelativeAxisY(MotionEvent event) {
return event.getAxisValue(AXIS_RELATIVE_Y);
}
}
@@ -1,13 +1,5 @@
package com.limelight.binding.input.driver;
import android.hardware.usb.UsbEndpoint;
import com.limelight.LimeLog;
import com.limelight.binding.video.MediaCodecHelper;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public abstract class AbstractController {
private final int deviceId;
@@ -72,14 +72,14 @@ public class UsbDriverService extends Service implements UsbDriverListener {
// Initial attachment broadcast
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
// Continue the state machine
handleUsbDeviceState(device);
}
// Subsequent permission dialog completion intent
else if (action.equals(ACTION_USB_PERMISSION)) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
// If we got this far, we've already found we're able to handle this device
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
@@ -10,56 +10,38 @@ import com.limelight.nvstream.input.ControllerPacket;
import java.nio.ByteBuffer;
public class Xbox360Controller extends AbstractXboxController {
private static final int XB360_IFACE_SUBCLASS = 93;
private static final int XB360_IFACE_PROTOCOL = 1; // Wired only
// This list is taken from the Xpad driver in the Linux kernel.
// I've excluded the devices that aren't "controllers" in the traditional sense, but
// if people really want to use their dancepads or fight sticks with Moonlight, I can
// put them in.
private static final DeviceIdTuple[] supportedDeviceTuples = {
new DeviceIdTuple(0x045e, 0x028e, "Microsoft X-Box 360 pad"),
new DeviceIdTuple(0x044f, 0xb326, "Thrustmaster Gamepad GP XID"),
new DeviceIdTuple(0x046d, 0xc21d, "Logitech Gamepad F310"),
new DeviceIdTuple(0x046d, 0xc21e, "Logitech Gamepad F510"),
new DeviceIdTuple(0x046d, 0xc21f, "Logitech Gamepad F710"),
new DeviceIdTuple(0x046d, 0xc242, "Logitech Chillstream Controller"),
new DeviceIdTuple(0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller"),
new DeviceIdTuple(0x0738, 0x4726, "Mad Catz Xbox 360 Controller"),
new DeviceIdTuple(0x0738, 0xb726, "Mad Catz Xbox controller - MW2"),
new DeviceIdTuple(0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad"),
new DeviceIdTuple(0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360"),
new DeviceIdTuple(0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360"),
new DeviceIdTuple(0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360"),
new DeviceIdTuple(0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller"),
new DeviceIdTuple(0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360"),
new DeviceIdTuple(0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360"),
new DeviceIdTuple(0x0e6f, 0x0301, "Logic3 Controller"),
new DeviceIdTuple(0x0e6f, 0x0401, "Logic3 Controller"),
new DeviceIdTuple(0x12ab, 0x0301, "PDP AFTERGLOW AX.1"),
new DeviceIdTuple(0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller"),
new DeviceIdTuple(0x1532, 0x0037, "Razer Sabertooth"),
new DeviceIdTuple(0x15e4, 0x3f00, "Power A Mini Pro Elite"),
new DeviceIdTuple(0x15e4, 0x3f0a, "Xbox Airflo wired controller"),
new DeviceIdTuple(0x15e4, 0x3f10, "Batarang Xbox 360 controller"),
new DeviceIdTuple(0x162e, 0xbeef, "Joytech Neo-Se Take2"),
new DeviceIdTuple(0x1689, 0xfd00, "Razer Onza Tournament Edition"),
new DeviceIdTuple(0x1689, 0xfd01, "Razer Onza Classic Edition"),
new DeviceIdTuple(0x24c6, 0x5d04, "Razer Sabertooth"),
new DeviceIdTuple(0x1bad, 0xf016, "Mad Catz Xbox 360 Controller"),
new DeviceIdTuple(0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)"),
new DeviceIdTuple(0x1bad, 0xf900, "Harmonix Xbox 360 Controller"),
new DeviceIdTuple(0x1bad, 0xf901, "Gamestop Xbox 360 Controller"),
new DeviceIdTuple(0x1bad, 0xf903, "Tron Xbox 360 controller"),
new DeviceIdTuple(0x24c6, 0x5300, "PowerA MINI PROEX Controller"),
new DeviceIdTuple(0x24c6, 0x5303, "Xbox Airflo wired controller"),
private static final int[] SUPPORTED_VENDORS = {
0x044f, // Thrustmaster
0x045e, // Microsoft
0x046d, // Logitech
0x0738, // Mad Catz
0x0e6f, // Unknown
0x12ab, // Unknown
0x1430, // RedOctane
0x146b, // BigBen
0x1bad, // Harmonix
0x0f0d, // Hori
0x1689, // Razer Onza
0x24c6, // PowerA
0x1532, // Razer Sabertooth
0x15e4, // Numark
0x162e, // Joytech
};
public static boolean canClaimDevice(UsbDevice device) {
for (DeviceIdTuple tuple : supportedDeviceTuples) {
if (device.getVendorId() == tuple.vid && device.getProductId() == tuple.pid) {
LimeLog.info("XB360 can claim device: " + tuple.name);
for (int supportedVid : SUPPORTED_VENDORS) {
if (device.getVendorId() == supportedVid &&
device.getInterfaceCount() >= 1 &&
device.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
device.getInterface(0).getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
device.getInterface(0).getInterfaceProtocol() == XB360_IFACE_PROTOCOL) {
return true;
}
}
return false;
}
@@ -151,16 +133,4 @@ public class Xbox360Controller extends AbstractXboxController {
// No need to fail init if the LED command fails
return true;
}
private static class DeviceIdTuple {
public final int vid;
public final int pid;
public final String name;
public DeviceIdTuple(int vid, int pid, String name) {
this.vid = vid;
this.pid = pid;
this.name = name;
}
}
}
@@ -3,22 +3,25 @@ package com.limelight.binding.input.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import com.limelight.LimeLog;
import com.limelight.binding.video.MediaCodecHelper;
import com.limelight.nvstream.input.ControllerPacket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class XboxOneController extends AbstractXboxController {
private static final int MICROSOFT_VID = 0x045e;
private static final int XB1_IFACE_SUBCLASS = 71;
private static final int XB1_IFACE_PROTOCOL = 208;
private static final int[] SUPPORTED_VENDORS = {
0x045e, // Microsoft
0x0738, // Mad Catz
0x0e6f, // Unknown
0x0f0d, // Hori
0x24c6, // PowerA
};
// FIXME: odata_serial
private static final byte[] XB1_INIT_DATA = {0x05, 0x20, 0x00, 0x01, 0x00};
@@ -59,6 +62,12 @@ public class XboxOneController extends AbstractXboxController {
rightStickY = ~buffer.getShort() / 32767.0f;
}
private void ackModeReport(byte seqNum) {
byte[] payload = {0x01, 0x20, seqNum, 0x09, 0x00, 0x07, 0x20, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00};
connection.bulkTransfer(outEndpt, payload, payload.length, 3000);
}
@Override
protected boolean handleRead(ByteBuffer buffer) {
switch (buffer.get())
@@ -69,7 +78,15 @@ public class XboxOneController extends AbstractXboxController {
return true;
case 0x07:
buffer.position(buffer.position() + 3);
// The Xbox One S controller needs acks for mode reports otherwise
// it retransmits them forever.
if (buffer.get() == 0x30) {
ackModeReport(buffer.get());
buffer.position(buffer.position() + 1);
}
else {
buffer.position(buffer.position() + 2);
}
setButtonFlag(ControllerPacket.SPECIAL_BUTTON_FLAG, buffer.get() & 0x01);
return true;
}
@@ -78,11 +95,17 @@ public class XboxOneController extends AbstractXboxController {
}
public static boolean canClaimDevice(UsbDevice device) {
return device.getVendorId() == MICROSOFT_VID &&
device.getInterfaceCount() >= 1 &&
device.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
device.getInterface(0).getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
device.getInterface(0).getInterfaceProtocol() == XB1_IFACE_PROTOCOL;
for (int supportedVid : SUPPORTED_VENDORS) {
if (device.getVendorId() == supportedVid &&
device.getInterfaceCount() >= 1 &&
device.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
device.getInterface(0).getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
device.getInterface(0).getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
return true;
}
}
return false;
}
@Override
@@ -4,6 +4,8 @@ import android.app.Activity;
import android.widget.Toast;
import com.limelight.LimeLog;
import com.limelight.LimelightBuildProps;
import com.limelight.binding.input.capture.InputCaptureProvider;
import java.io.DataOutputStream;
import java.io.File;
@@ -13,7 +15,7 @@ import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class EvdevHandler {
public class EvdevCaptureProvider extends InputCaptureProvider {
private final EvdevListener listener;
private final String libraryPath;
@@ -25,6 +27,7 @@ public class EvdevHandler {
private ServerSocket servSock;
private Socket evdevSock;
private Activity activity;
private boolean started = false;
private static final byte UNGRAB_REQUEST = 1;
private static final byte REGRAB_REQUEST = 2;
@@ -51,12 +54,7 @@ public class EvdevHandler {
try {
su = builder.start();
} catch (IOException e) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "This device is not rooted - Mouse capture is unavailable", Toast.LENGTH_LONG).show();
}
});
reportDeviceNotRooted();
e.printStackTrace();
return;
}
@@ -66,6 +64,7 @@ public class EvdevHandler {
try {
suOut.writeChars(libraryPath+File.separatorChar+"libevdev_reader.so "+servSock.getLocalPort()+"\n");
} catch (IOException e) {
reportDeviceNotRooted();
e.printStackTrace();
return;
}
@@ -163,24 +162,48 @@ public class EvdevHandler {
}
};
public EvdevHandler(Activity activity, EvdevListener listener) {
public EvdevCaptureProvider(Activity activity, EvdevListener listener) {
this.listener = listener;
this.activity = activity;
this.libraryPath = activity.getApplicationInfo().nativeLibraryDir;
}
public void regrabAll() {
if (!shutdown && evdevOut != null) {
try {
evdevOut.write(REGRAB_REQUEST);
} catch (IOException e) {
e.printStackTrace();
public static boolean isCaptureProviderSupported() {
return LimelightBuildProps.ROOT_BUILD;
}
private void reportDeviceNotRooted() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "This device is not rooted - Mouse capture is unavailable", Toast.LENGTH_LONG).show();
}
});
}
@Override
public void enableCapture() {
if (!started) {
// Start the handler thread if it's our first time
// capturing
handlerThread.start();
started = true;
}
else {
// Send a request to regrab if we're already capturing
if (!shutdown && evdevOut != null) {
try {
evdevOut.write(REGRAB_REQUEST);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void ungrabAll() {
if (!shutdown && evdevOut != null) {
@Override
public void disableCapture() {
if (started && !shutdown && evdevOut != null) {
try {
evdevOut.write(UNGRAB_REQUEST);
} catch (IOException e) {
@@ -189,15 +212,16 @@ public class EvdevHandler {
}
}
public void start() {
handlerThread.start();
}
public void stop() {
@Override
public void destroy() {
// We need to stop the process in this context otherwise
// we could get stuck waiting on output from the process
// in order to terminate it.
if (!started) {
return;
}
shutdown = true;
handlerThread.interrupt();
@@ -52,7 +52,7 @@ public class DigitalButton extends VirtualControllerElement {
}
}
private List<DigitalButtonListener> listeners = new ArrayList<DigitalButtonListener>();
private List<DigitalButtonListener> listeners = new ArrayList<>();
private String text = "";
private int icon = -1;
private long timerLongClickTimeout = 3000;
@@ -20,7 +20,7 @@ public class DigitalPad extends VirtualControllerElement {
public final static int DIGITAL_PAD_DIRECTION_UP = 2;
public final static int DIGITAL_PAD_DIRECTION_RIGHT = 4;
public final static int DIGITAL_PAD_DIRECTION_DOWN = 8;
List<DigitalPadListener> listeners = new ArrayList<DigitalPadListener>();
List<DigitalPadListener> listeners = new ArrayList<>();
private static final int DPAD_MARGIN = 5;
@@ -48,7 +48,7 @@ public class VirtualController {
private RelativeLayout.LayoutParams layoutParamsButtonConfigure = null;
private Button buttonConfigure = null;
private List<VirtualControllerElement> elements = new ArrayList<VirtualControllerElement>();
private List<VirtualControllerElement> elements = new ArrayList<>();
public VirtualController(final NvConnection conn, FrameLayout layout, final Context context) {
this.connection = conn;
@@ -60,7 +60,7 @@ public class VirtualController {
frame_layout.addView(relative_layout);
buttonConfigure = new Button(context);
buttonConfigure.setBackgroundResource(R.drawable.settings);
buttonConfigure.setBackgroundResource(R.drawable.ic_settings);
buttonConfigure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -475,8 +475,8 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
} catch (InterruptedException ignored) { }
}
// Stop the decoder
videoDecoder.stop();
// We could stop the decoder here, but it seems to cause some problems
// so we'll just let release take care of it.
}
@Override
@@ -34,7 +34,7 @@ public class MediaCodecHelper {
private static final List<String> whitelistedHevcDecoders;
static {
directSubmitPrefixes = new LinkedList<String>();
directSubmitPrefixes = new LinkedList<>();
// These decoders have low enough input buffer latency that they
// can be directly invoked from the receive thread
@@ -48,11 +48,11 @@ public class MediaCodecHelper {
}
static {
preferredDecoders = new LinkedList<String>();
preferredDecoders = new LinkedList<>();
}
static {
blacklistedDecoderPrefixes = new LinkedList<String>();
blacklistedDecoderPrefixes = new LinkedList<>();
// Software decoders that don't support H264 high profile
blacklistedDecoderPrefixes.add("omx.google");
@@ -65,21 +65,21 @@ public class MediaCodecHelper {
}
static {
spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<String>();
spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<>();
spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
spsFixupBitstreamFixupDecoderPrefixes.add("omx.brcm");
baselineProfileHackPrefixes = new LinkedList<String>();
baselineProfileHackPrefixes = new LinkedList<>();
baselineProfileHackPrefixes.add("omx.intel");
whitelistedAdaptiveResolutionPrefixes = new LinkedList<String>();
whitelistedAdaptiveResolutionPrefixes = new LinkedList<>();
whitelistedAdaptiveResolutionPrefixes.add("omx.nvidia");
whitelistedAdaptiveResolutionPrefixes.add("omx.qcom");
whitelistedAdaptiveResolutionPrefixes.add("omx.sec");
whitelistedAdaptiveResolutionPrefixes.add("omx.TI");
constrainedHighProfilePrefixes = new LinkedList<String>();
constrainedHighProfilePrefixes = new LinkedList<>();
constrainedHighProfilePrefixes.add("omx.intel");
}
@@ -218,7 +218,7 @@ public class MediaCodecHelper {
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private static LinkedList<MediaCodecInfo> getMediaCodecList() {
LinkedList<MediaCodecInfo> infoList = new LinkedList<MediaCodecInfo>();
LinkedList<MediaCodecInfo> infoList = new LinkedList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
@@ -376,7 +376,7 @@ public class MediaCodecHelper {
break;
cpuInfo.append((char)ch);
}
return cpuInfo.toString();
} finally {
br.close();
@@ -68,7 +68,7 @@ public class ComputerDatabaseManager {
public List<ComputerDetails> getAllComputers() {
Cursor c = computerDb.rawQuery("SELECT * FROM "+COMPUTER_TABLE_NAME, null);
LinkedList<ComputerDetails> computerList = new LinkedList<ComputerDetails>();
LinkedList<ComputerDetails> computerList = new LinkedList<>();
while (c.moveToNext()) {
ComputerDetails details = new ComputerDetails();
@@ -45,7 +45,7 @@ public class ComputerManagerService extends Service {
private final AtomicInteger dbRefCount = new AtomicInteger(0);
private IdentityManager idManager;
private final LinkedList<PollingTuple> pollingTuples = new LinkedList<PollingTuple>();
private final LinkedList<PollingTuple> pollingTuples = new LinkedList<>();
private ComputerManagerListener listener = null;
private final AtomicInteger activePolls = new AtomicInteger(0);
private boolean pollingActive = false;
@@ -3,6 +3,7 @@ package com.limelight.grid;
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.limelight.AppView;
@@ -14,8 +15,6 @@ import com.limelight.grid.assets.MemoryAssetLoader;
import com.limelight.grid.assets.NetworkAssetLoader;
import com.limelight.nvstream.http.ComputerDetails;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Comparator;
@@ -28,7 +27,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
private final CachedAppAssetLoader loader;
public AppGridAdapter(Activity activity, boolean listMode, boolean small, ComputerDetails computer, String uniqueId) {
super(activity, listMode ? R.layout.simple_row : (small ? R.layout.app_grid_item_small : R.layout.app_grid_item), R.drawable.image_loading);
super(activity, listMode ? R.layout.simple_row : (small ? R.layout.app_grid_item_small : R.layout.app_grid_item));
int dpi = activity.getResources().getDisplayMetrics().densityDpi;
int dp;
@@ -53,9 +52,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
this.loader = new CachedAppAssetLoader(computer, scalingDivisor,
new NetworkAssetLoader(context, uniqueId),
new MemoryAssetLoader(),
new DiskAssetLoader(context.getCacheDir()),
BitmapFactory.decodeResource(activity.getResources(),
R.drawable.image_loading, options));
new DiskAssetLoader(context.getCacheDir()));
}
public void cancelQueuedOperations() {
@@ -68,7 +65,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
Collections.sort(itemList, new Comparator<AppView.AppObject>() {
@Override
public int compare(AppView.AppObject lhs, AppView.AppObject rhs) {
return lhs.app.getAppName().compareTo(rhs.app.getAppName());
return lhs.app.getAppName().toLowerCase().compareTo(rhs.app.getAppName().toLowerCase());
}
});
}
@@ -86,9 +83,10 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
itemList.remove(app);
}
public boolean populateImageView(ImageView imgView, AppView.AppObject obj) {
@Override
public boolean populateImageView(ImageView imgView, ProgressBar prgView, AppView.AppObject obj) {
// Let the cached asset loader handle it
loader.populateImageView(obj.app, imgView);
loader.populateImageView(obj.app, imgView, prgView);
return true;
}
@@ -103,9 +101,9 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
@Override
public boolean populateOverlayView(ImageView overlayView, AppView.AppObject obj) {
if (obj.app.getIsRunning()) {
if (obj.isRunning) {
// Show the play button overlay
overlayView.setImageResource(R.drawable.play);
overlayView.setImageResource(R.drawable.ic_play);
return true;
}
@@ -6,6 +6,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.limelight.R;
@@ -14,15 +15,13 @@ import java.util.ArrayList;
public abstract class GenericGridAdapter<T> extends BaseAdapter {
protected final Context context;
protected final int defaultImageRes;
protected final int layoutId;
protected final ArrayList<T> itemList = new ArrayList<T>();
protected final ArrayList<T> itemList = new ArrayList<>();
protected final LayoutInflater inflater;
public GenericGridAdapter(Context context, int layoutId, int defaultImageRes) {
public GenericGridAdapter(Context context, int layoutId) {
this.context = context;
this.layoutId = layoutId;
this.defaultImageRes = defaultImageRes;
this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@@ -46,7 +45,7 @@ public abstract class GenericGridAdapter<T> extends BaseAdapter {
return i;
}
public abstract boolean populateImageView(ImageView imgView, T obj);
public abstract boolean populateImageView(ImageView imgView, ProgressBar prgView, T obj);
public abstract boolean populateTextView(TextView txtView, T obj);
public abstract boolean populateOverlayView(ImageView overlayView, T obj);
@@ -59,10 +58,11 @@ public abstract class GenericGridAdapter<T> extends BaseAdapter {
ImageView imgView = (ImageView) convertView.findViewById(R.id.grid_image);
ImageView overlayView = (ImageView) convertView.findViewById(R.id.grid_overlay);
TextView txtView = (TextView) convertView.findViewById(R.id.grid_text);
ProgressBar prgView = (ProgressBar) convertView.findViewById(R.id.grid_spinner);
if (imgView != null) {
if (!populateImageView(imgView, itemList.get(i))) {
imgView.setImageResource(defaultImageRes);
if (!populateImageView(imgView, prgView, itemList.get(i))) {
imgView.setImageBitmap(null);
}
}
if (!populateTextView(txtView, itemList.get(i))) {
@@ -1,7 +1,9 @@
package com.limelight.grid;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.limelight.PcView;
@@ -14,7 +16,7 @@ import java.util.Comparator;
public class PcGridAdapter extends GenericGridAdapter<PcView.ComputerObject> {
public PcGridAdapter(Context context, boolean listMode, boolean small) {
super(context, listMode ? R.layout.simple_row : (small ? R.layout.pc_grid_item_small : R.layout.pc_grid_item), R.drawable.computer);
super(context, listMode ? R.layout.simple_row : (small ? R.layout.pc_grid_item_small : R.layout.pc_grid_item));
}
public void addComputer(PcView.ComputerObject computer) {
@@ -26,7 +28,7 @@ public class PcGridAdapter extends GenericGridAdapter<PcView.ComputerObject> {
Collections.sort(itemList, new Comparator<PcView.ComputerObject>() {
@Override
public int compare(PcView.ComputerObject lhs, PcView.ComputerObject rhs) {
return lhs.details.name.compareTo(rhs.details.name);
return lhs.details.name.toLowerCase().compareTo(rhs.details.name.toLowerCase());
}
});
}
@@ -36,21 +38,28 @@ public class PcGridAdapter extends GenericGridAdapter<PcView.ComputerObject> {
}
@Override
public boolean populateImageView(ImageView imgView, PcView.ComputerObject obj) {
if (obj.details.reachability != ComputerDetails.Reachability.OFFLINE) {
public boolean populateImageView(ImageView imgView, ProgressBar prgView, PcView.ComputerObject obj) {
if (obj.details.state == ComputerDetails.State.ONLINE) {
imgView.setAlpha(1.0f);
}
else {
imgView.setAlpha(0.4f);
}
// Return false to use the default drawable
return false;
if (obj.details.reachability == ComputerDetails.Reachability.UNKNOWN) {
prgView.setVisibility(View.VISIBLE);
}
else {
prgView.setVisibility(View.INVISIBLE);
}
imgView.setImageResource(R.drawable.ic_computer);
return true;
}
@Override
public boolean populateTextView(TextView txtView, PcView.ComputerObject obj) {
if (obj.details.reachability != ComputerDetails.Reachability.OFFLINE) {
if (obj.details.state == ComputerDetails.State.ONLINE) {
txtView.setAlpha(1.0f);
}
else {
@@ -63,13 +72,11 @@ public class PcGridAdapter extends GenericGridAdapter<PcView.ComputerObject> {
@Override
public boolean populateOverlayView(ImageView overlayView, PcView.ComputerObject obj) {
if (obj.details.reachability == ComputerDetails.Reachability.UNKNOWN) {
// Still refreshing this PC so display the overlay
overlayView.setImageResource(R.drawable.image_loading);
if (obj.details.state == ComputerDetails.State.OFFLINE) {
overlayView.setImageResource(R.drawable.ic_pc_offline);
overlayView.setAlpha(0.4f);
return true;
}
// No overlay
return false;
}
}
@@ -5,7 +5,9 @@ import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import com.limelight.nvstream.http.ComputerDetails;
import com.limelight.nvstream.http.NvApp;
@@ -53,13 +55,13 @@ public class CachedAppAssetLoader {
public CachedAppAssetLoader(ComputerDetails computer, double scalingDivider,
NetworkAssetLoader networkLoader, MemoryAssetLoader memoryLoader,
DiskAssetLoader diskLoader, Bitmap placeholderBitmap) {
DiskAssetLoader diskLoader) {
this.computer = computer;
this.scalingDivider = scalingDivider;
this.networkLoader = networkLoader;
this.memoryLoader = memoryLoader;
this.diskLoader = diskLoader;
this.placeholderBitmap = placeholderBitmap;
this.placeholderBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
}
public void cancelBackgroundLoads() {
@@ -130,12 +132,14 @@ public class CachedAppAssetLoader {
private class LoaderTask extends AsyncTask<LoaderTuple, Void, Bitmap> {
private final WeakReference<ImageView> imageViewRef;
private final WeakReference<ProgressBar> progressViewRef;
private final boolean diskOnly;
private LoaderTuple tuple;
public LoaderTask(ImageView imageView, boolean diskOnly) {
this.imageViewRef = new WeakReference<ImageView>(imageView);
public LoaderTask(ImageView imageView, ProgressBar prgView, boolean diskOnly) {
this.imageViewRef = new WeakReference<>(imageView);
this.progressViewRef = new WeakReference<>(prgView);
this.diskOnly = diskOnly;
}
@@ -143,8 +147,8 @@ public class CachedAppAssetLoader {
protected Bitmap doInBackground(LoaderTuple... params) {
tuple = params[0];
// Check whether it has been cancelled or the image view is gone
if (isCancelled() || imageViewRef.get() == null) {
// Check whether it has been cancelled or the views are gone
if (isCancelled() || imageViewRef.get() == null || progressViewRef.get() == null) {
return null;
}
@@ -177,11 +181,17 @@ public class CachedAppAssetLoader {
// If the current loader task for this view isn't us, do nothing
final ImageView imageView = imageViewRef.get();
final ProgressBar prgView = progressViewRef.get();
if (getLoaderTask(imageView) == this) {
// Now display the progress bar since we have to hit the network
if (prgView != null) {
prgView.setVisibility(View.VISIBLE);
}
// Set off another loader task on the network executor
LoaderTask task = new LoaderTask(imageView, false);
LoaderTask task = new LoaderTask(imageView, prgView, false);
AsyncDrawable asyncDrawable = new AsyncDrawable(imageView.getResources(), placeholderBitmap, task);
imageView.setAlpha(1.0f);
imageView.setVisibility(View.VISIBLE);
imageView.setImageDrawable(asyncDrawable);
task.executeOnExecutor(networkExecutor, tuple);
}
@@ -195,14 +205,20 @@ public class CachedAppAssetLoader {
}
final ImageView imageView = imageViewRef.get();
final ProgressBar prgView = progressViewRef.get();
if (getLoaderTask(imageView) == this) {
// Set the bitmap
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
}
// Hide the progress bar
if (prgView != null) {
prgView.setVisibility(View.INVISIBLE);
}
// Show the view
imageView.setAlpha(1.0f);
imageView.setVisibility(View.VISIBLE);
}
}
}
@@ -213,7 +229,7 @@ public class CachedAppAssetLoader {
public AsyncDrawable(Resources res, Bitmap bitmap,
LoaderTask loaderTask) {
super(res, bitmap);
loaderTaskReference = new WeakReference<LoaderTask>(loaderTask);
loaderTaskReference = new WeakReference<>(loaderTask);
}
public LoaderTask getLoaderTask() {
@@ -280,34 +296,38 @@ public class CachedAppAssetLoader {
});
}
public void populateImageView(NvApp app, ImageView view) {
public boolean populateImageView(NvApp app, ImageView imgView, ProgressBar prgView) {
LoaderTuple tuple = new LoaderTuple(computer, app);
// If there's already a task in progress for this view,
// cancel it. If the task is already loading the same image,
// we return and let that load finish.
if (!cancelPendingLoad(tuple, view)) {
return;
if (!cancelPendingLoad(tuple, imgView)) {
return true;
}
// Hide the progress bar always on initial load
prgView.setVisibility(View.INVISIBLE);
// First, try the memory cache in the current context
Bitmap bmp = memoryLoader.loadBitmapFromCache(tuple);
if (bmp != null) {
// Show the bitmap immediately
view.setAlpha(1.0f);
view.setImageBitmap(bmp);
return;
imgView.setVisibility(View.VISIBLE);
imgView.setImageBitmap(bmp);
return true;
}
// If it's not in memory, create an async task to load it. This task will be attached
// via AsyncDrawable to this view.
final LoaderTask task = new LoaderTask(view, true);
final AsyncDrawable asyncDrawable = new AsyncDrawable(view.getResources(), placeholderBitmap, task);
view.setAlpha(0.0f);
view.setImageDrawable(asyncDrawable);
final LoaderTask task = new LoaderTask(imgView, prgView, true);
final AsyncDrawable asyncDrawable = new AsyncDrawable(imgView.getResources(), placeholderBitmap, task);
imgView.setVisibility(View.INVISIBLE);
imgView.setImageDrawable(asyncDrawable);
// Run the task on our foreground executor
task.executeOnExecutor(foregroundExecutor, tuple);
return false;
}
public class LoaderTuple {
@@ -7,7 +7,6 @@ import com.limelight.LimeLog;
import com.limelight.utils.CacheHelper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -29,7 +29,7 @@ import android.widget.Toast;
public class AddComputerManually extends Activity {
private TextView hostText;
private ComputerManagerService.ComputerManagerBinder managerBinder;
private final LinkedBlockingQueue<String> computersToAdd = new LinkedBlockingQueue<String>();
private final LinkedBlockingQueue<String> computersToAdd = new LinkedBlockingQueue<>();
private Thread addThread;
private final ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, final IBinder binder) {
@@ -3,6 +3,7 @@ package com.limelight.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Build;
import android.preference.PreferenceManager;
@@ -100,7 +101,7 @@ public class PreferenceConfiguration {
}
// Use small mode on anything smaller than a 7" tablet
return context.getResources().getConfiguration().screenWidthDp < 600;
return context.getResources().getConfiguration().smallestScreenWidthDp < 500;
}
public static int getDefaultBitrate(Context context) {
@@ -2,7 +2,6 @@ package com.limelight.preferences;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.app.Activity;
@@ -14,7 +14,7 @@ public class Dialog implements Runnable {
private AlertDialog alert;
private static final ArrayList<Dialog> rundownDialogs = new ArrayList<Dialog>();
private static final ArrayList<Dialog> rundownDialogs = new ArrayList<>();
private Dialog(Activity activity, String title, String message, boolean endAfterDismiss)
{
@@ -0,0 +1,115 @@
package com.limelight.utils;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.os.Build;
import com.limelight.AppView;
import com.limelight.R;
import com.limelight.nvstream.http.ComputerDetails;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class ShortcutHelper {
private final ShortcutManager sm;
private final Context context;
public ShortcutHelper(Context context) {
this.context = context;
if (Build.VERSION.SDK_INT >= 25) {
sm = context.getSystemService(ShortcutManager.class);
}
else {
sm = null;
}
}
@TargetApi(25)
private void reapShortcutsForDynamicAdd() {
List<ShortcutInfo> dynamicShortcuts = sm.getDynamicShortcuts();
while (dynamicShortcuts.size() >= sm.getMaxShortcutCountPerActivity()) {
ShortcutInfo maxRankShortcut = dynamicShortcuts.get(0);
for (ShortcutInfo scut : dynamicShortcuts) {
if (maxRankShortcut.getRank() < scut.getRank()) {
maxRankShortcut = scut;
}
}
sm.removeDynamicShortcuts(Arrays.asList(maxRankShortcut.getId()));
}
}
@TargetApi(25)
private List<ShortcutInfo> getAllShortcuts() {
LinkedList<ShortcutInfo> list = new LinkedList<>();
list.addAll(sm.getDynamicShortcuts());
list.addAll(sm.getPinnedShortcuts());
return list;
}
@TargetApi(25)
private ShortcutInfo getInfoForId(String id) {
List<ShortcutInfo> shortcuts = getAllShortcuts();
for (ShortcutInfo info : shortcuts) {
if (info.getId().equals(id)) {
return info;
}
}
return null;
}
public void reportShortcutUsed(String id) {
if (Build.VERSION.SDK_INT >= 25) {
ShortcutInfo sinfo = getInfoForId(id);
if (sinfo != null) {
sm.reportShortcutUsed(id);
}
}
}
public void createAppViewShortcut(String id, ComputerDetails details) {
if (Build.VERSION.SDK_INT >= 25) {
Intent i = new Intent(context, AppView.class);
i.putExtra(AppView.NAME_EXTRA, details.name);
i.putExtra(AppView.UUID_EXTRA, details.uuid.toString());
i.putExtra(AppView.SHORTCUT_EXTRA, true);
i.setAction(Intent.ACTION_DEFAULT);
ShortcutInfo sinfo = new ShortcutInfo.Builder(context, id)
.setIntent(i)
.setShortLabel(details.name)
.setLongLabel(details.name)
.build();
ShortcutInfo existingSinfo = getInfoForId(id);
if (existingSinfo != null) {
// Update in place
sm.updateShortcuts(Arrays.asList(sinfo));
sm.enableShortcuts(Arrays.asList(id));
}
else {
// Reap shortcuts to make space for this new one
reapShortcutsForDynamicAdd();
// Add the new shortcut
//TODO: Testing and proper icon - sm.addDynamicShortcuts(Arrays.asList(sinfo));
}
}
}
public void disableShortcut(String id, CharSequence reason) {
if (Build.VERSION.SDK_INT >= 25) {
ShortcutInfo sinfo = getInfoForId(id);
if (sinfo != null) {
sm.disableShortcuts(Arrays.asList(id), reason);
}
}
}
}
@@ -15,7 +15,7 @@ public class SpinnerDialog implements Runnable,OnCancelListener {
private ProgressDialog progress;
private final boolean finish;
private static final ArrayList<SpinnerDialog> rundownDialogs = new ArrayList<SpinnerDialog>();
private static final ArrayList<SpinnerDialog> rundownDialogs = new ArrayList<>();
private SpinnerDialog(Activity activity, String title, String message, boolean finish)
{
@@ -15,6 +15,8 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <android/log.h>
#define EVDEV_MAX_EVENT_SIZE 24
@@ -19,7 +19,8 @@ Java_com_limelight_nvstream_av_audio_OpusDecoder_init(JNIEnv *env, jobject this,
ChannelCount = channelCount;
jni_mapping_data = (*env)->GetByteArrayElements(env, mapping, 0);
ret = nv_opus_init(sampleRate, channelCount, streams, coupledStreams, jni_mapping_data);
ret = nv_opus_init(sampleRate, channelCount, streams, coupledStreams,
(const unsigned char*)jni_mapping_data);
(*env)->ReleaseByteArrayElements(env, mapping, jni_mapping_data, JNI_ABORT);
return ret;
@@ -49,7 +50,8 @@ Java_com_limelight_nvstream_av_audio_OpusDecoder_decode(
if (indata != NULL) {
jni_input_data = (*env)->GetByteArrayElements(env, indata, 0);
ret = nv_opus_decode(&jni_input_data[inoff], inlen, (jshort*)jni_pcm_data, SamplesPerChannel);
ret = nv_opus_decode((unsigned char*)&jni_input_data[inoff], inlen,
(jshort*)jni_pcm_data, SamplesPerChannel);
// The input data isn't changed so it can be safely aborted
(*env)->ReleaseByteArrayElements(env, indata, jni_input_data, JNI_ABORT);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

+9
View File
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"
android:fillColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M21,2L3,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7v2L8,20v2h8v-2h-2v-2h7c1.1,0 2,-0.9 2,-2L23,4c0,-1.1 -0.9,-2 -2,-2zM21,16L3,16L3,4h18v12z"
android:fillColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4 -1.5,0 -2.89,0.19 -4.15,0.48L18.18,13.8 23.64,7zM17.04,15.22L3.27,1.44 2,2.72l2.05,2.06C1.91,5.76 0.59,6.82 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01 3.9,-4.86 3.32,3.32 1.27,-1.27 -3.46,-3.46z"
android:fillColor="#FFFFFF"/>
</vector>
+9
View File
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,16.5v-9l6,4.5 -6,4.5z"
android:fillColor="#FFFFFF"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"
android:fillColor="#FFFFFF"/>
</vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

@@ -15,18 +15,18 @@
android:layout_centerInParent="true"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true">
<ImageView
<ProgressBar
android:id="@+id/pcs_loading"
android:layout_width="75dp"
android:layout_height="75dp"
android:src="@drawable/image_loading"/>
android:indeterminate="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/pcs_loading"
android:layout_toEndOf="@+id/pcs_loading"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_centerVertical="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center"
@@ -55,7 +55,7 @@
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="@drawable/settings"
android:src="@drawable/ic_settings"
style="?android:attr/borderlessButtonStyle"/>
<ImageButton
@@ -68,7 +68,7 @@
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:src="@drawable/add_computer"
android:src="@drawable/ic_add"
style="?android:attr/borderlessButtonStyle"/>
</RelativeLayout>
@@ -15,18 +15,18 @@
android:layout_centerInParent="true"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true">
<ImageView
<ProgressBar
android:id="@+id/pcs_loading"
android:layout_width="75dp"
android:layout_height="75dp"
android:src="@drawable/image_loading"/>
android:indeterminate="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/pcs_loading"
android:layout_toEndOf="@+id/pcs_loading"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_centerVertical="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center"
@@ -54,7 +54,7 @@
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@drawable/settings"
android:src="@drawable/ic_settings"
style="?android:attr/borderlessButtonStyle"/>
<ImageButton
@@ -66,7 +66,7 @@
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:src="@drawable/add_computer"
android:src="@drawable/ic_add"
style="?android:attr/borderlessButtonStyle"/>
</RelativeLayout>
@@ -8,6 +8,14 @@
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/grid_spinner"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_width="75dp"
android:layout_height="75dp"
android:indeterminate="true">
</ProgressBar>
<ImageView
android:id="@+id/grid_image"
android:cropToPadding="false"
@@ -8,6 +8,14 @@
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/grid_spinner"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_width="50dp"
android:layout_height="50dp"
android:indeterminate="true">
</ProgressBar>
<ImageView
android:id="@+id/grid_image"
android:cropToPadding="false"
+13 -8
View File
@@ -10,23 +10,28 @@
<ImageView
android:id="@+id/grid_image"
android:layout_centerHorizontal="true"
android:layout_width="150dp"
android:layout_height="100dp">
android:layout_width="125dp"
android:layout_height="125dp">
</ImageView>
<ImageView
android:id="@+id/grid_overlay"
android:layout_marginTop="15dp"
android:layout_marginLeft="65dp"
android:layout_marginStart="65dp"
android:layout_marginRight="20dp"
android:layout_marginEnd="20dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="28dp"
android:layout_width="50dp"
android:layout_height="50dp">
</ImageView>
<ProgressBar
android:id="@+id/grid_spinner"
android:layout_centerHorizontal="true"
android:layout_marginTop="28dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:indeterminate="true">
</ProgressBar>
</RelativeLayout>
<TextView
android:id="@+id/grid_text"
android:layout_width="150dp"
android:layout_width="125dp"
android:layout_height="wrap_content"
android:layout_below="@id/grid_image_layout"
android:layout_marginTop="10dp"
+13 -8
View File
@@ -10,23 +10,28 @@
<ImageView
android:id="@+id/grid_image"
android:layout_centerHorizontal="true"
android:layout_width="100dp"
android:layout_height="67dp">
android:layout_width="75dp"
android:layout_height="75dp">
</ImageView>
<ImageView
android:id="@+id/grid_overlay"
android:layout_marginTop="10dp"
android:layout_marginLeft="42dp"
android:layout_marginStart="42dp"
android:layout_marginRight="13dp"
android:layout_marginEnd="13dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:layout_width="33dp"
android:layout_height="33dp">
</ImageView>
<ProgressBar
android:id="@+id/grid_spinner"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:layout_width="33dp"
android:layout_height="33dp"
android:indeterminate="true">
</ProgressBar>
</RelativeLayout>
<TextView
android:id="@+id/grid_text"
android:layout_width="100dp"
android:layout_width="75dp"
android:layout_height="wrap_content"
android:layout_below="@id/grid_image_layout"
android:layout_marginTop="10dp"
-9
View File
@@ -1,14 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="resolution_names">
<item>720p - 30 FPS</item>
<item>720p - 60 FPS</item>
<item>1080p - 30 FPS</item>
<item>1080p - 60 FPS</item>
<item>4K - 30 FPS</item>
<item>4K - 60 FPS</item>
</string-array>
<string-array name="decoder_names">
<item>Scegli decoder automaticamente</item>
<item>Forza decoder software</item>
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="decoder_names">
<item>デコーダを自動選択</item>
<item>ソフトウェアデコードを強制</item>
<item>ハードウェアデコードを強制</item>
</string-array>
<string-array name="video_format_names">
<item>安定している場合にH.265を有効化</item>
<item>H.265を強制的に有効化(クラッシュする可能性があります)</item>
<item>H.265を無効化</item>
</string-array>
</resources>
+127
View File
@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- PC view menu entries -->
<string name="pcview_menu_app_list">ゲームリストを表示</string>
<string name="pcview_menu_pair_pc">コンピュータとペアリング</string>
<string name="pcview_menu_unpair_pc">ペアリングを解除r</string>
<string name="pcview_menu_send_wol">Wake-On-LANリクエストを送信する</string>
<string name="pcview_menu_delete_pc">コンピュータをリストから削除する</string>
<!-- Pair messages -->
<string name="pairing">ペアリング中</string>
<string name="pair_pc_offline">コンピュータはオフラインです</string>
<string name="pair_pc_ingame">現在このコンピュータはゲームを実行中のため、ペアリングする前にゲームを閉じる必要があります</string>
<string name="pair_pairing_title">ペアリング</string>
<string name="pair_pairing_msg">ペアリングしたいコンピュータのPINコードを入力してください:</string>
<string name="pair_incorrect_pin">PINコードが間違っています</string>
<string name="pair_fail">ペアリングに失敗しました</string>
<!-- WOL messages -->
<string name="wol_pc_online">コンピュータはオンラインです</string>
<string name="wol_no_mac">GFEがMACアドレスを送信しなかったため、コンピュータを起動することができません</string>
<string name="wol_waking_pc">コンピュータを起動中</string>
<string name="wol_waking_msg">コンピュータが起動するまで数秒かかる可能性があります。Wake-On-LANが正しく構成されているか確認してください</string>
<string name="wol_fail">Wake-On-LANパケットの送信に失敗しました</string>
<!-- Unpair messages -->
<string name="unpairing">ペアリングの解除中</string>
<string name="unpair_success">ペアリングの解除に成功しました</string>
<string name="unpair_fail">ペアリングの解除に失敗しました</string>
<string name="unpair_error">このデバイスはペアリングされませんでした</string>
<!-- Errors -->
<string name="error_pc_offline">コンピュータはオフラインです</string>
<string name="error_manager_not_running">ComputerManagerサービスが動作していません。しばらく待つか、Moonlightを再起動してください</string>
<string name="error_unknown_host">ホストが解決できません</string>
<string name="error_404">GFEがHTTP 404を返しました。コンピュータでサポートされているGPUが動作しているか確認してください。リモートデスクトップソフトウェアでもこのエラーが発生する可能性があります。コンピュータを再起動するか、GFEを再インストールしてください</string>
<!-- Start application messages -->
<string name="conn_establishing_title">接続を確立</string>
<string name="conn_establishing_msg">接続を開始</string>
<string name="conn_metered">警告: 現在使用中のネットワークは従量制課金接続です</string>
<string name="conn_client_latency"> デコードの平均待ち時間:</string>
<string name="conn_client_latency_hw">ハードウェアデコーダーの待ち時間</string>
<string name="conn_hardware_latency">ハードウェアデコーダーの平均待ち時間:</string>
<string name="conn_starting">開始</string>
<string name="conn_error_title">接続エラー</string>
<string name="conn_error_msg">開始できません</string>
<string name="conn_terminated_title">接続を終了</string>
<string name="conn_terminated_msg">接続は終了しました</string>
<!-- General strings -->
<string name="ip_hint">コンピュータのIPアドレス</string>
<string name="searching_pc">コンピュータを検索中</string>
<string name="yes">はい</string>
<string name="no">いいえ</string>
<string name="lost_connection">コンピュータとの接続が失われました</string>
<!-- AppList activity -->
<string name="title_applist">ゲーム</string>
<string name="applist_menu_resume">セッションを続ける</string>
<string name="applist_menu_quit">セッションを終了する</string>
<string name="applist_menu_quit_and_start">現在のゲームを終了して新しいゲームを始める</string>
<string name="applist_menu_cancel">キャンセル</string>
<string name="applist_refresh_title">ゲームリスト</string>
<string name="applist_refresh_msg">ゲームリストを更新中</string>
<string name="applist_refresh_error_title">エラー</string>
<string name="applist_refresh_error_msg">ゲームリストの取得に失敗しました</string>
<string name="applist_quit_app">終了中</string>
<string name="applist_quit_success">ゲームを終了しました</string>
<string name="applist_quit_fail">ゲームが終了できませんでした</string>
<string name="applist_quit_confirmation">本当にゲームを終了しますか? 保存されていないデータは破棄されます</string>
<!-- Add computer manually activity -->
<string name="title_add_pc">手動でコンピュータを追加する</string>
<string name="msg_add_pc">コンピュータに接続中</string>
<string name="addpc_fail">指定されたコンピュータに接続できませんでした。ファイアーウォールの設定を確認してください</string>
<string name="addpc_success">コンピュータの追加に成功しました</string>
<string name="addpc_unknown_host">コンピュータのアドレスが解決できません。コンピュータのアドレスを確認してください</string>
<string name="addpc_enter_ip">IPアドレスを入力してください</string>
<!-- Preferences -->
<string name="category_basic_settings">基本的な設定</string>
<string name="title_resolution_list">解像度とフレームレート</string>
<string name="summary_resolution_list">品質が高いほどラグとクラッシュが発生しやすくなります</string>
<string name="title_seekbar_bitrate">映像のビットレート</string>
<string name="summary_seekbar_bitrate">ビットレートを低くすればカクつきが抑制され、高くすれば画質が向上します</string>
<string name="suffix_seekbar_bitrate">Mbps</string>
<string name="title_checkbox_stretch_video">映像を全画面に拡大</string>
<string name="title_checkbox_disable_warnings">警告を無効化</string>
<string name="summary_checkbox_disable_warnings">ストリーミング中に画面に警告メッセージを表示しない</string>
<string name="category_audio_settings">音声</string>
<string name="title_checkbox_51_surround">5.1chサラウンド</string>
<string name="summary_checkbox_51_surround">音声に問題が生じる場合はチェックを外してください。バージョン2.7以降のGFEが必要です</string>
<string name="category_gamepad_settings">ゲームコントローラ</string>
<string name="title_checkbox_multi_controller">複数のゲームコントローラ</string>
<string name="summary_checkbox_multi_controller">チェックを外すと、全てのゲームコントローラが単一の物として認識されます</string>
<string name="title_seekbar_deadzone">アナログゲームコントローラのデッドゾーン</string>
<string name="suffix_seekbar_deadzone">%</string>
<string name="title_checkbox_xb1_driver">Xbox 360/Oneコントローラ</string>
<string name="summary_checkbox_xb1_driver">XboxゲームコントローラをサポートしないデバイスでXboxゲームコントローラ用のドライバを有効にします</string>
<string name="category_on_screen_controls_settings">オンスクリーンコントローラ</string>
<string name="title_checkbox_show_onscreen_controls">オンスクリーンコントローラ</string>
<string name="summary_checkbox_show_onscreen_controls">タッチスクリーン上にオンスクリーンコントローラを表示します</string>
<string name="category_ui_settings">インターフェース</string>
<string name="title_language_list">言語</string>
<string name="summary_language_list">Moonlightで使用する言語</string>
<string name="title_checkbox_list_mode">リストメニュー</string>
<string name="summary_checkbox_list_mode">ゲームをグリッドではなく、リストで表示します</string>
<string name="title_checkbox_small_icon_mode">小さなアイコン</string>
<string name="summary_checkbox_small_icon_mode">グリッドで小さなアイコンを使用します</string>
<string name="category_host_settings">ホスト</string>
<string name="title_checkbox_enable_sops">ゲーム設定の最適化</string>
<string name="summary_checkbox_enable_sops">GFEがゲームの設定を最適化します</string>
<string name="title_checkbox_host_audio">コンピュータで音声を再生</string>
<string name="summary_checkbox_host_audio">音声をコンピュータとこのデバイスの両方で再生します</string>
<string name="category_advanced_settings">高度な設定</string>
<string name="title_video_format">H.265</string>
<string name="summary_video_format">H.265は動画に必要な帯域幅を圧縮します。この機能にはなるべく新しいデバイスが必要です</string>
</resources>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="decoder_names">
<item>Selecteer Decoder Automatisch</item>
<item>Forceer Software Decoderen</item>
<item>Forceer Hardware Decoderen</item>
</string-array>
</resources>
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- PC view menu entries -->
<string name="pcview_menu_app_list">Toon Spel Lijst</string>
<string name="pcview_menu_pair_pc">Koppel met PC</string>
<string name="pcview_menu_unpair_pc">Ontkoppen</string>
<string name="pcview_menu_send_wol">Stuur Wake-On-LAN aanvraag</string>
<string name="pcview_menu_delete_pc">Verwijder PC</string>
<!-- Pair messages -->
<string name="pairing">Koppelen…</string>
<string name="pair_pc_offline">Computer is onbereikbaar</string>
<string name="pair_pc_ingame">Computer is op dit moment in een spel. Sluit de spel voordat je koppelt.</string>
<string name="pair_pairing_title">Koppelen</string>
<string name="pair_pairing_msg">Voer de volgende PIN in op de doel PC:</string>
<string name="pair_incorrect_pin">Ongeldige PIN</string>
<string name="pair_fail">Koppelen mislukt</string>
<!-- WOL messages -->
<string name="wol_pc_online">Computer is online</string>
<string name="wol_no_mac">Het is niet mogelijk om de PC uit slaapstand te halen, omdat GFE geen MAC adres heeft verstuurd.</string>
<string name="wol_waking_pc">PC uit slaapstand halen…</string>
<string name="wol_waking_msg"> Het ken even duren voordat de PC reageert.
Als dit niet gebeurt dan moet je de Wake-On-LAN instellingen controleren.
</string>
<string name="wol_fail">Wake-On-LAN packets versturen mislukt</string>
<!-- Unpair messages -->
<string name="unpairing">Ontkoppelen…</string>
<string name="unpair_success">Ontkoppeling succesvol</string>
<string name="unpair_fail">Ontkoppeling mislukt</string>
<string name="unpair_error">Apparaat niet gekoppeld</string>
<!-- Errors -->
<string name="error_pc_offline">Computer is offline</string>
<string name="error_manager_not_running">The ComputerManager service is niet gestart. Wacht een ogenblik of herstart de app.</string>
<string name="error_unknown_host">Host achterhalen mislukt.</string>
<string name="error_404">GFE geeft een HTTP 404 fout. Kijk na of de PC een ondersteunde GPU heeft.
Remote desktop software kan soms ook voor deze fout zorgen. Probeer de computer opnieuw op te starten of GFE te herinstalleren.
</string>
<!-- Start application messages -->
<string name="conn_establishing_title">Verbinding maken</string>
<string name="conn_establishing_msg">Verbinding starten</string>
<string name="conn_metered">Waarschuwing: Dit internet verbinding bevat een datalimiet!</string>
<string name="conn_client_latency">Gemiddelde frame decoding reactietijd:</string>
<string name="conn_client_latency_hw">hardware decoder reactietijd:</string>
<string name="conn_hardware_latency">Gemiddelde hardware decoding reactietijd:</string>
<string name="conn_starting">Starten</string>
<string name="conn_error_title">Verbindingsprobleem</string>
<string name="conn_error_msg">Starten mislukt</string>
<string name="conn_terminated_title">Verbinding beëindigd</string>
<string name="conn_terminated_msg">De verbinding is beëindigd</string>
<!-- General strings -->
<string name="ip_hint">IP adres van GeForce PC</string>
<string name="searching_pc">Zoeken naar PCs…</string>
<string name="yes">Ja</string>
<string name="no">Nee</string>
<string name="lost_connection">Verbinding met PC verloren</string>
<!-- AppList activity -->
<string name="title_applist">Apps op</string>
<string name="applist_menu_resume">Hervat Sessie</string>
<string name="applist_menu_quit">Stop Sessie</string>
<string name="applist_menu_quit_and_start">Stop Huidige Spel en Start</string>
<string name="applist_menu_cancel">Annuleer</string>
<string name="applist_refresh_title">App Lijst</string>
<string name="applist_refresh_msg">Apps Verversen…</string>
<string name="applist_refresh_error_title">Fout</string>
<string name="applist_refresh_error_msg">App lijst ophalen mislukt</string>
<string name="applist_quit_app">Stoppen</string>
<string name="applist_quit_success">Succesvol gestopt.</string>
<string name="applist_quit_fail">Stoppen mislukt</string>
<string name="applist_quit_confirmation">Weet je het zeker dat je de app wilt sluiten? Alle niet-opgeslagen gegevens gaan verloren.</string>
<!-- Add computer manually activity -->
<string name="title_add_pc">Voeg handmatig PC toe</string>
<string name="msg_add_pc">Verbinding maken met PC…</string>
<string name="addpc_fail">Verbinden mislukt. Controleer of de benodigde poorten openstaan in de firewall.</string>
<string name="addpc_success">Computer toevoegen succesvol</string>
<string name="addpc_unknown_host">Adres achterhalen is mislukt. Controleer het ingevoerde adres op typfouten.</string>
<string name="addpc_enter_ip">IP-adres invoeren is verplicht.</string>
<!-- Preferences -->
<string name="category_basic_settings">Algemene Instellingen</string>
<string name="title_resolution_list">Selecteer resolutie en FPS doel</string>
<string name="summary_resolution_list">Instellingen te hoog zetten kunnen crashes en haperingen veroorzaken.</string>
<string name="title_seekbar_bitrate">Selecteer doel video bitsnelheid</string>
<string name="summary_seekbar_bitrate">Verlaag bitsnelheid om haperingen te verminderen. Verhoog de bitsnelheid voor een betere videokwaliteit.</string>
<string name="suffix_seekbar_bitrate">Mbps</string>
<string name="title_checkbox_stretch_video">Rek video uit tot volledig scherm</string>
<string name="title_checkbox_disable_warnings">Verberg waarschuwingsberichten</string>
<string name="summary_checkbox_disable_warnings">Verberg on-screen verbindingswaarschuwingen tijdens het streamen</string>
<string name="category_gamepad_settings">Gamepad Instellingen</string>
<string name="title_checkbox_multi_controller">Multi-gamepad support</string>
<string name="summary_checkbox_multi_controller">Wanneer uitgevinkt, alle controllers verschijnen als één.</string>
<string name="title_seekbar_deadzone">Pas analoge dodezone aan.</string>
<string name="suffix_seekbar_deadzone">%</string>
<string name="title_checkbox_xb1_driver">Xbox One controller stuurprogramma</string>
<string name="summary_checkbox_xb1_driver">Gebruikt de ingebouwde USB stuurprogramma voor apparaten zonder Xbox One controller ondersteuning.</string>
<string name="category_ui_settings">UI Installingen</string>
<string name="title_language_list">Taal</string>
<string name="summary_language_list">Taal te gebruiken in Moonlight</string>
<string name="title_checkbox_list_mode">Gebruik lijsten in plaats van kolommen</string>
<string name="summary_checkbox_list_mode">Display apps and PCs in lists instead of grids</string>
<string name="title_checkbox_small_icon_mode">Gebruik kleine iconen</string>
<string name="summary_checkbox_small_icon_mode">Gebruik kleine iconen in kolom onderdelen zodat meer items tegelijk zichtbaar worden.</string>
<string name="category_host_settings">Host Instellingen</string>
<string name="title_checkbox_enable_sops">Optimaliseer spel instellingen</string>
<string name="summary_checkbox_enable_sops">Sta GFE toe om spel instellingen te veranderen voor een optimale stream</string>
<string name="title_checkbox_host_audio">Speel audio af op PC</string>
<string name="summary_checkbox_host_audio">Speel audio af op de computer en op dit apparaat</string>
<string name="category_advanced_settings">Geavanceerde Instellingen</string>
</resources>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="decoder_names">
<item>Автоматический выбор декодера</item>
<item>Принудительное программное декодирование</item>
<item>Принудительное аппаратное декодирование</item>
</string-array>
<string-array name="video_format_names">
<item>Использовать H.265 только если безопасно</item>
<item>Всегда использовать H.265 если доступно</item>
<item>Никогда не использовать H.265</item>
</string-array>
</resources>
+127
View File
@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- PC view menu entries -->
<string name="pcview_menu_app_list">Посмотреть список игр</string>
<string name="pcview_menu_pair_pc">Создать пару с PC</string>
<string name="pcview_menu_unpair_pc">Разорвать пару</string>
<string name="pcview_menu_send_wol">Отправить Wake-On-LAN запрос</string>
<string name="pcview_menu_delete_pc">Удалить PC</string>
<!-- Pair messages -->
<string name="pairing">Создание пары…</string>
<string name="pair_pc_offline">Компьютер выключен или находится не в сети</string>
<string name="pair_pc_ingame">Компьютер в данный момент находится в игре. Вы должны закрыть игру перед создание пары.</string>
<string name="pair_pairing_title">Создание пары</string>
<string name="pair_pairing_msg">Пожалуйста введите этот PIN на PC:</string>
<string name="pair_incorrect_pin">Неправильный PIN</string>
<string name="pair_fail">Создание пары не удалось</string>
<!-- WOL messages -->
<string name="wol_pc_online">Компьютер в сети</string>
<string name="wol_no_mac">Невозможно разбудить PC потому что GFE не отправило MAC адрес</string>
<string name="wol_waking_pc">Пробуждение PC…</string>
<string name="wol_waking_msg">Пробуждение PC может занять несколько секунд.
Если этого не происходит, удостоверьтесь что Wake-On-LAN настроен правильно.
</string>
<string name="wol_fail">Ошибка при отправке Wake-On-LAN пакетов</string>
<!-- Unpair messages -->
<string name="unpairing">Разрыв пары…</string>
<string name="unpair_success">Разрыв пары закончился успешно.</string>
<string name="unpair_fail">Разрыв пары не удался</string>
<string name="unpair_error">Устройство не было спарено</string>
<!-- Errors -->
<string name="error_pc_offline">Компьютер выключен или находится не в сети</string>
<string name="error_manager_not_running">Сервис ComputerManager не запущен. Пожалуйста, подождите несколько секунд или перезапустите приложение.</string>
<string name="error_unknown_host">Не удалось найти хост</string>
<string name="error_404">GFE вернул ошибку HTTP 404. Убедитесь что ваш PC is испольщует поддерживаемый GPU.
Использование программ для удалённого доступа также можнт вызывать эту ошибку. Попробуйте перезагрузить компьютер или переустановить GFE.
</string>
<!-- Start application messages -->
<string name="conn_establishing_title">Создание соединения.</string>
<string name="conn_establishing_msg">Подключение</string>
<string name="conn_metered">Внимание: Происходит измерение вашего сетевого соединения!</string>
<string name="conn_client_latency">Средняя задержка декодирования кадра: </string>
<string name="conn_client_latency_hw">задержка аппаратного декодирования:</string>
<string name="conn_hardware_latency">Средняя задержка апаратного декодирования:</string>
<string name="conn_starting">Запуск</string>
<string name="conn_error_title">Ошибка соединения</string>
<string name="conn_error_msg">Запуск не удался</string>
<string name="conn_terminated_title">Соединение прекращено</string>
<string name="conn_terminated_msg">Подключение было прервано</string>
<!-- General strings -->
<string name="ip_hint">IP адресс компьютера с GeForce</string>
<string name="searching_pc">Поиск компьютеров…</string>
<string name="yes">Да</string>
<string name="no">Нет</string>
<string name="lost_connection">Потеряно соединение с PC</string>
<!-- AppList activity -->
<string name="title_applist">Приложения на</string>
<string name="applist_menu_resume">Возобновить сессию</string>
<string name="applist_menu_quit">Выйти из сессии</string>
<string name="applist_menu_quit_and_start">Выйти из текущей игры и запустить</string>
<string name="applist_menu_cancel">Отмена</string>
<string name="applist_refresh_title">Список приложений</string>
<string name="applist_refresh_msg">Обновление приложений…</string>
<string name="applist_refresh_error_title">Ошибка</string>
<string name="applist_refresh_error_msg">Ошибка при получении списка приложений</string>
<string name="applist_quit_app">Выход из</string>
<string name="applist_quit_success">Выход произошёл успешно из</string>
<string name="applist_quit_fail">Ошибка при выходе</string>
<string name="applist_quit_confirmation">Вы уверены, что хотите выйти из запущенного приложения? Все несохраненные данные будут потеряны.</string>
<!-- Add computer manually activity -->
<string name="title_add_pc">Добавление PC вручную</string>
<string name="msg_add_pc">Соединение с PC…</string>
<string name="addpc_fail">Не удалось подключиться к выбранному компьютеру. Удостоверьтесь, что необходимые порты разрешены в настройках брандмауэра.</string>
<string name="addpc_success">Компьютер добавлен успешно.</string>
<string name="addpc_unknown_host">Не удалось найти PC по указанному адресу. Убедитесь, что вы не совершили ошибок во время его написания.</string>
<string name="addpc_enter_ip">Вы должны ввести IP адрес</string>
<!-- Preferences -->
<string name="category_basic_settings">Базовые Настройки</string>
<string name="title_resolution_list">Выберите разрешение и частоту кадров.</string>
<string name="summary_resolution_list">Выбор слишком высокого значеня для своего устройства может вызвать тормоза или вылеты.</string>
<string name="title_seekbar_bitrate">Выберите битрейт видео.</string>
<string name="summary_seekbar_bitrate">Низкий битрейт уменьшит зависания. Увеличение битрейта улучшит качество изображения.</string>
<string name="suffix_seekbar_bitrate">Mbps</string>
<string name="title_checkbox_stretch_video">Растягивать видео на весь экран</string>
<string name="title_checkbox_disable_warnings">Отключить сообщения с предупреждениями</string>
<string name="summary_checkbox_disable_warnings">Выключить экранные предупреждения о соединении во время стрима.</string>
<string name="category_audio_settings">Аудио Настройки</string>
<string name="title_checkbox_51_surround">Включить объёмный звук 5.1</string>
<string name="summary_checkbox_51_surround">Отключите, если появляются аудио проблемы. Требуется GFE 2.7 или выше.</string>
<string name="category_gamepad_settings">Настройки Гемпада</string>
<string name="title_checkbox_multi_controller">Поддержка нескольких контроллеров</string>
<string name="summary_checkbox_multi_controller">Когда отключена, все контроллеры определяются как один. </string>
<string name="title_seekbar_deadzone">Регулировать мертвую зону аналогового стика.</string>
<string name="suffix_seekbar_deadzone">%</string>
<string name="title_checkbox_xb1_driver">Драйвер контроллера от Xbox One</string>
<string name="summary_checkbox_xb1_driver">Включить встроенный USB драйвер для устройств без встроенной поддержки контроллера от Xbox One.</string>
<string name="category_ui_settings">Настройки Интерфейса</string>
<string name="title_language_list">Язык</string>
<string name="summary_language_list">Язык, который будет использоваться в Moonlight</string>
<string name="title_checkbox_list_mode">Использовать списки вместо сеток.</string>
<string name="summary_checkbox_list_mode">Выводить приложения и компьютеры списком, вместо использования сетки.</string>
<string name="title_checkbox_small_icon_mode">Использовать маленькие иконки</string>
<string name="summary_checkbox_small_icon_mode">Использовать маленькие иконки в сетки для увеличения числа элементов, отображаемых на экране.</string>
<string name="category_host_settings">Настройки Хоста</string>
<string name="title_checkbox_enable_sops">Оптимизировать игровые настройки</string>
<string name="summary_checkbox_enable_sops">Разрешить GFE изменять настройки игр для оптимальной потоковой передачи</string>
<string name="title_checkbox_host_audio">Проигрывать звук на PC</string>
<string name="summary_checkbox_host_audio">Проигрывать звук на компьютере и текущем устройстве.</string>
<string name="category_advanced_settings">Расширенные Настройки</string>
<string name="title_video_format">Изменить настройки H.265</string>
<string name="summary_video_format">H.265 снижает требования к пропускной способности, но требует очень свежих устройств.</string>
</resources>
+8 -2
View File
@@ -21,11 +21,17 @@
<item>Default</item>
<item>English</item>
<item>Italiano</item>
<item>Japanese</item>
<item>Русский</item>
<item>Nederlands</item>
</string-array>
<string-array name="language_values" translatable="false">
<item>default</item>
<item>en</item>
<item>it</item>
<item>ja</item>
<item>ru</item>
<item>nl</item>
</string-array>
<string-array name="decoder_names">
@@ -40,8 +46,8 @@
</string-array>
<string-array name="video_format_names">
<item>Use H.265 only if safe</item>
<item>Always use H.265 if available</item>
<item>Use H.265 only if stable</item>
<item>Always use H.265 (may crash)</item>
<item>Never use H.265</item>
</string-array>
<string-array name="video_format_values" translatable="false">
+7 -1
View File
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Shortcut strings -->
<string name="scut_deleted_pc">PC deleted</string>
<string name="scut_not_paired">PC not paired</string>
<!-- PC view menu entries -->
<string name="pcview_menu_app_list">View Game List</string>
@@ -16,6 +19,7 @@
<string name="pair_pairing_msg">Please enter the following PIN on the target PC:</string>
<string name="pair_incorrect_pin">Incorrect PIN</string>
<string name="pair_fail">Pairing failed</string>
<string name="pair_already_in_progress">Pairing already in progress</string>
<!-- WOL messages -->
<string name="wol_pc_online">Computer is online</string>
@@ -55,13 +59,15 @@
<!-- General strings -->
<string name="ip_hint">IP address of GeForce PC</string>
<string name="searching_pc">Searching for PCs</string>
<string name="searching_pc">Searching for PCs with GameStream running…\n\n
Ensure GameStream is enabled in the GeForce Experience SHIELD settings.</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="lost_connection">Lost connection to PC</string>
<!-- AppList activity -->
<string name="title_applist">Apps on</string>
<string name="applist_connect_msg">Connecting to PC…</string>
<string name="applist_menu_resume">Resume Session</string>
<string name="applist_menu_quit">Quit Session</string>
<string name="applist_menu_quit_and_start">Quit Current Game and Start</string>
+1 -1
View File
@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.android.tools.build:gradle:2.2.2'
}
}
+19
View File
@@ -0,0 +1,19 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Settings specified in this file will override any Gradle settings
# configured through the IDE.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx3072m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
+2 -2
View File
@@ -1,6 +1,6 @@
#Sat Feb 06 16:21:20 EST 2016
#Fri Aug 26 22:01:33 PDT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
+1 -1
View File
@@ -13,7 +13,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>