Switched to new style gradle Android application build process
authorSam Lantinga <slouken@libsdl.org>
Mon, 23 Oct 2017 15:23:43 -0700
changeset 116475816b27073a4
parent 11646 c875be618b90
child 11648 ed5bc0abae1b
Switched to new style gradle Android application build process
android-project/AndroidManifest.xml
android-project/ant.properties
android-project/app/app.iml
android-project/app/build.gradle
android-project/app/jni/Android.mk
android-project/app/jni/Application.mk
android-project/app/jni/src/Android.mk
android-project/app/proguard-rules.pro
android-project/app/src/main/AndroidManifest.xml
android-project/app/src/main/java/org/libsdl/app/SDL.java
android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java
android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
android-project/app/src/main/res/mipmap-hdpi/ic_launcher.png
android-project/app/src/main/res/mipmap-mdpi/ic_launcher.png
android-project/app/src/main/res/mipmap-xhdpi/ic_launcher.png
android-project/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
android-project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
android-project/app/src/main/res/values/colors.xml
android-project/app/src/main/res/values/strings.xml
android-project/app/src/main/res/values/styles.xml
android-project/build.gradle
android-project/build.properties
android-project/build.xml
android-project/default.properties
android-project/gradle.properties
android-project/gradle/wrapper/gradle-wrapper.jar
android-project/gradle/wrapper/gradle-wrapper.properties
android-project/gradlew
android-project/gradlew.bat
android-project/jni/Android.mk
android-project/jni/Application.mk
android-project/jni/src/Android.mk
android-project/jni/src/Android_static.mk
android-project/proguard-project.txt
android-project/project.properties
android-project/res/drawable-hdpi/ic_launcher.png
android-project/res/drawable-mdpi/ic_launcher.png
android-project/res/drawable-xhdpi/ic_launcher.png
android-project/res/drawable-xxhdpi/ic_launcher.png
android-project/res/layout/main.xml
android-project/res/values/strings.xml
android-project/settings.gradle
android-project/src/org/libsdl/app/SDL.java
android-project/src/org/libsdl/app/SDLActivity.java
android-project/src/org/libsdl/app/SDLAudioManager.java
android-project/src/org/libsdl/app/SDLControllerManager.java
build-scripts/androidbuild.sh
docs/README-android.md
     1.1 --- a/android-project/AndroidManifest.xml	Mon Oct 23 12:33:18 2017 -0700
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,58 +0,0 @@
     1.4 -<?xml version="1.0" encoding="utf-8"?>
     1.5 -<!-- Replace org.libsdl.app with the identifier of your game below, e.g.
     1.6 -     com.gamemaker.game
     1.7 --->
     1.8 -<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     1.9 -      package="org.libsdl.app"
    1.10 -      android:versionCode="1"
    1.11 -      android:versionName="1.0"
    1.12 -      android:installLocation="auto">
    1.13 -
    1.14 -    <!-- Android 2.3.3 -->
    1.15 -    <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="12" />
    1.16 -
    1.17 -    <!-- OpenGL ES 2.0 -->
    1.18 -    <uses-feature android:glEsVersion="0x00020000" />
    1.19 -
    1.20 -    <!-- Allow writing to external storage -->
    1.21 -    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    1.22 -    <!-- Allow access to the vibrator -->
    1.23 -    <uses-permission android:name="android.permission.VIBRATE" />
    1.24 -
    1.25 -    <!-- if you want to capture audio, uncomment this. -->
    1.26 -    <!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
    1.27 -
    1.28 -    <!-- Create a Java class extending SDLActivity and place it in a
    1.29 -         directory under src matching the package, e.g.
    1.30 -         	src/com/gamemaker/game/MyGame.java
    1.31 -
    1.32 -         then replace "SDLActivity" with the name of your class (e.g. "MyGame")
    1.33 -         in the XML below.
    1.34 -
    1.35 -         An example Java class can be found in README-android.md
    1.36 -    -->
    1.37 -    <application android:label="@string/app_name"
    1.38 -                 android:icon="@drawable/ic_launcher"
    1.39 -                 android:allowBackup="true"
    1.40 -                 android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
    1.41 -                 android:hardwareAccelerated="true" >
    1.42 -        <activity android:name="SDLActivity"
    1.43 -                  android:label="@string/app_name"
    1.44 -                  android:configChanges="keyboardHidden|orientation|screenSize"
    1.45 -                  >
    1.46 -            <intent-filter>
    1.47 -                <action android:name="android.intent.action.MAIN" />
    1.48 -                <category android:name="android.intent.category.LAUNCHER" />
    1.49 -            </intent-filter>
    1.50 -            <!-- Drop file event -->
    1.51 -            <!--
    1.52 -            <intent-filter>
    1.53 -                <action android:name="android.intent.action.VIEW" />
    1.54 -                <category android:name="android.intent.category.DEFAULT" />
    1.55 -                <data android:mimeType="*/*" />
    1.56 -            </intent-filter>
    1.57 -            -->
    1.58 -        </activity>
    1.59 -    </application>
    1.60 -
    1.61 -</manifest> 
     2.1 --- a/android-project/ant.properties	Mon Oct 23 12:33:18 2017 -0700
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,17 +0,0 @@
     2.4 -# This file is used to override default values used by the Ant build system.
     2.5 -#
     2.6 -# This file must be checked into Version Control Systems, as it is
     2.7 -# integral to the build system of your project.
     2.8 -
     2.9 -# This file is only used by the Ant script.
    2.10 -
    2.11 -# You can use this to override default values such as
    2.12 -#  'source.dir' for the location of your java source folder and
    2.13 -#  'out.dir' for the location of your output folder.
    2.14 -
    2.15 -# You can also use it define how the release builds are signed by declaring
    2.16 -# the following properties:
    2.17 -#  'key.store' for the location of your keystore and
    2.18 -#  'key.alias' for the name of the key to use.
    2.19 -# The password will be asked during the build when you use the 'release' target.
    2.20 -
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/android-project/app/app.iml	Mon Oct 23 15:23:43 2017 -0700
     3.3 @@ -0,0 +1,112 @@
     3.4 +<?xml version="1.0" encoding="UTF-8"?>
     3.5 +<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
     3.6 +  <component name="FacetManager">
     3.7 +    <facet type="android-gradle" name="Android-Gradle">
     3.8 +      <configuration>
     3.9 +        <option name="GRADLE_PROJECT_PATH" value=":app" />
    3.10 +      </configuration>
    3.11 +    </facet>
    3.12 +    <facet type="android" name="Android">
    3.13 +      <configuration>
    3.14 +        <option name="SELECTED_BUILD_VARIANT" value="debug" />
    3.15 +        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
    3.16 +        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
    3.17 +        <afterSyncTasks>
    3.18 +          <task>generateDebugSources</task>
    3.19 +        </afterSyncTasks>
    3.20 +        <option name="ALLOW_USER_CONFIGURATION" value="false" />
    3.21 +        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
    3.22 +        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
    3.23 +        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
    3.24 +        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
    3.25 +      </configuration>
    3.26 +    </facet>
    3.27 +  </component>
    3.28 +  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
    3.29 +    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
    3.30 +    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
    3.31 +    <exclude-output />
    3.32 +    <content url="file://$MODULE_DIR$">
    3.33 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
    3.34 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
    3.35 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
    3.36 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
    3.37 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
    3.38 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
    3.39 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
    3.40 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
    3.41 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
    3.42 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
    3.43 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
    3.44 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
    3.45 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
    3.46 +      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
    3.47 +      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
    3.48 +      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
    3.49 +      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
    3.50 +      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
    3.51 +      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
    3.52 +      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
    3.53 +      <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
    3.54 +      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
    3.55 +      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
    3.56 +      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
    3.57 +      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
    3.58 +      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
    3.59 +      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
    3.60 +      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
    3.61 +      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
    3.62 +      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
    3.63 +      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
    3.64 +      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
    3.65 +      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
    3.66 +      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
    3.67 +      <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
    3.68 +      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
    3.69 +      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
    3.70 +      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
    3.71 +      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
    3.72 +      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
    3.73 +      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
    3.74 +      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
    3.75 +      <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
    3.76 +      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
    3.77 +      <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
    3.78 +      <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
    3.79 +      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
    3.80 +      <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
    3.81 +      <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
    3.82 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
    3.83 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
    3.84 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
    3.85 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
    3.86 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
    3.87 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
    3.88 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
    3.89 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
    3.90 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
    3.91 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
    3.92 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
    3.93 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
    3.94 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
    3.95 +      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
    3.96 +      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
    3.97 +      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
    3.98 +    </content>
    3.99 +    <orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
   3.100 +    <orderEntry type="sourceFolder" forTests="false" />
   3.101 +    <orderEntry type="library" exported="" scope="TEST" name="espresso-core-2.2.2" level="project" />
   3.102 +    <orderEntry type="library" exported="" scope="TEST" name="runner-0.5" level="project" />
   3.103 +    <orderEntry type="library" exported="" scope="TEST" name="exposed-instrumentation-api-publish-0.5" level="project" />
   3.104 +    <orderEntry type="library" exported="" scope="TEST" name="espresso-idling-resource-2.2.2" level="project" />
   3.105 +    <orderEntry type="library" exported="" scope="TEST" name="rules-0.5" level="project" />
   3.106 +    <orderEntry type="library" exported="" scope="TEST" name="hamcrest-library-1.3" level="project" />
   3.107 +    <orderEntry type="library" exported="" scope="TEST" name="javax.annotation-api-1.2" level="project" />
   3.108 +    <orderEntry type="library" exported="" scope="TEST" name="javax.inject-1" level="project" />
   3.109 +    <orderEntry type="library" exported="" scope="TEST" name="hamcrest-integration-1.3" level="project" />
   3.110 +    <orderEntry type="library" exported="" scope="TEST" name="javawriter-2.1.1" level="project" />
   3.111 +    <orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
   3.112 +    <orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
   3.113 +    <orderEntry type="library" exported="" scope="TEST" name="jsr305-2.0.1" level="project" />
   3.114 +  </component>
   3.115 +</module>
   3.116 \ No newline at end of file
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/android-project/app/build.gradle	Mon Oct 23 15:23:43 2017 -0700
     4.3 @@ -0,0 +1,35 @@
     4.4 +apply plugin: 'com.android.application'
     4.5 +
     4.6 +android {
     4.7 +    compileSdkVersion 25
     4.8 +    buildToolsVersion "25.0.1"
     4.9 +    defaultConfig {
    4.10 +        applicationId "org.libsdl.app"
    4.11 +        minSdkVersion 10
    4.12 +        targetSdkVersion 16
    4.13 +        versionCode 1
    4.14 +        versionName "1.0"
    4.15 +        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    4.16 +    }
    4.17 +    buildTypes {
    4.18 +        release {
    4.19 +            minifyEnabled false
    4.20 +            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    4.21 +        }
    4.22 +    }
    4.23 +    sourceSets.main {
    4.24 +        jniLibs.srcDir 'libs'
    4.25 +        jni.srcDirs = [] //disable automatic ndk-build call
    4.26 +    }
    4.27 +    lintOptions {
    4.28 +        abortOnError false
    4.29 +    }
    4.30 +}
    4.31 +
    4.32 +dependencies {
    4.33 +    compile fileTree(dir: 'libs', include: ['*.jar'])
    4.34 +    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
    4.35 +        exclude group: 'com.android.support', module: 'support-annotations'
    4.36 +    })
    4.37 +    testCompile 'junit:junit:4.12'
    4.38 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/android-project/app/jni/Android.mk	Mon Oct 23 15:23:43 2017 -0700
     5.3 @@ -0,0 +1,1 @@
     5.4 +include $(call all-subdir-makefiles)
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/android-project/app/jni/Application.mk	Mon Oct 23 15:23:43 2017 -0700
     6.3 @@ -0,0 +1,9 @@
     6.4 +
     6.5 +# Uncomment this if you're using STL in your project
     6.6 +# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
     6.7 +# APP_STL := stlport_static
     6.8 +
     6.9 +APP_ABI := all
    6.10 +
    6.11 +# Min SDK level
    6.12 +APP_PLATFORM=android-10
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/android-project/app/jni/src/Android.mk	Mon Oct 23 15:23:43 2017 -0700
     7.3 @@ -0,0 +1,18 @@
     7.4 +LOCAL_PATH := $(call my-dir)
     7.5 +
     7.6 +include $(CLEAR_VARS)
     7.7 +
     7.8 +LOCAL_MODULE := main
     7.9 +
    7.10 +SDL_PATH := ../SDL
    7.11 +
    7.12 +LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include
    7.13 +
    7.14 +# Add your application source files here...
    7.15 +LOCAL_SRC_FILES := YourSourceHere.c
    7.16 +
    7.17 +LOCAL_SHARED_LIBRARIES := SDL2
    7.18 +
    7.19 +LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog
    7.20 +
    7.21 +include $(BUILD_SHARED_LIBRARY)
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/android-project/app/proguard-rules.pro	Mon Oct 23 15:23:43 2017 -0700
     8.3 @@ -0,0 +1,17 @@
     8.4 +# Add project specific ProGuard rules here.
     8.5 +# By default, the flags in this file are appended to flags specified
     8.6 +# in [sdk]/tools/proguard/proguard-android.txt
     8.7 +# You can edit the include path and order by changing the proguardFiles
     8.8 +# directive in build.gradle.
     8.9 +#
    8.10 +# For more details, see
    8.11 +#   http://developer.android.com/guide/developing/tools/proguard.html
    8.12 +
    8.13 +# Add any project specific keep options here:
    8.14 +
    8.15 +# If your project uses WebView with JS, uncomment the following
    8.16 +# and specify the fully qualified class name to the JavaScript interface
    8.17 +# class:
    8.18 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
    8.19 +#   public *;
    8.20 +#}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/android-project/app/src/main/AndroidManifest.xml	Mon Oct 23 15:23:43 2017 -0700
     9.3 @@ -0,0 +1,57 @@
     9.4 +<?xml version="1.0" encoding="utf-8"?>
     9.5 +<!-- Replace com.test.game with the identifier of your game below, e.g.
     9.6 +     com.gamemaker.game
     9.7 +-->
     9.8 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     9.9 +    package="org.libsdl.app"
    9.10 +    android:versionCode="1"
    9.11 +    android:versionName="1.0"
    9.12 +    android:installLocation="auto">
    9.13 +
    9.14 +    <!-- Android 2.3.3 -->
    9.15 +    <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="25" />
    9.16 +
    9.17 +    <!-- OpenGL ES 2.0 -->
    9.18 +    <uses-feature android:glEsVersion="0x00020000" />
    9.19 +
    9.20 +    <!-- Allow writing to external storage -->
    9.21 +    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    9.22 +    <!-- Allow access to the vibrator -->
    9.23 +    <uses-permission android:name="android.permission.VIBRATE" />
    9.24 +
    9.25 +    <!-- if you want to capture audio, uncomment this. -->
    9.26 +    <!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
    9.27 +
    9.28 +    <!-- Create a Java class extending SDLActivity and place it in a
    9.29 +         directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
    9.30 + 
    9.31 +         then replace "SDLActivity" with the name of your class (e.g. "MyGame")
    9.32 +         in the XML below.
    9.33 +
    9.34 +         An example Java class can be found in README-android.md
    9.35 +    -->
    9.36 +    <application android:label="@string/app_name"
    9.37 +        android:icon="@mipmap/ic_launcher"
    9.38 +        android:allowBackup="true"
    9.39 +        android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
    9.40 +        android:hardwareAccelerated="true" >
    9.41 +        <activity android:name="SDLActivity"
    9.42 +            android:label="@string/app_name"
    9.43 +            android:configChanges="keyboardHidden|orientation|screenSize"
    9.44 +            >
    9.45 +            <intent-filter>
    9.46 +                <action android:name="android.intent.action.MAIN" />
    9.47 +                <category android:name="android.intent.category.LAUNCHER" />
    9.48 +            </intent-filter>
    9.49 +            <!-- Drop file event -->
    9.50 +            <!--
    9.51 +            <intent-filter>
    9.52 +                <action android:name="android.intent.action.VIEW" />
    9.53 +                <category android:name="android.intent.category.DEFAULT" />
    9.54 +                <data android:mimeType="*/*" />
    9.55 +            </intent-filter>
    9.56 +            -->
    9.57 +        </activity>
    9.58 +    </application>
    9.59 +
    9.60 +</manifest>
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/android-project/app/src/main/java/org/libsdl/app/SDL.java	Mon Oct 23 15:23:43 2017 -0700
    10.3 @@ -0,0 +1,37 @@
    10.4 +package org.libsdl.app;
    10.5 +
    10.6 +import android.content.Context;
    10.7 +
    10.8 +/**
    10.9 +    SDL library initialization
   10.10 +*/
   10.11 +public class SDL {
   10.12 +
   10.13 +    // This function should be called first and sets up the native code
   10.14 +    // so it can call into the Java classes
   10.15 +    public static void setupJNI() {
   10.16 +        SDLActivity.nativeSetupJNI();
   10.17 +        SDLAudioManager.nativeSetupJNI();
   10.18 +        SDLControllerManager.nativeSetupJNI();
   10.19 +    }
   10.20 +
   10.21 +    // This function should be called each time the activity is started
   10.22 +    public static void initialize() {
   10.23 +        setContext(null);
   10.24 +
   10.25 +        SDLActivity.initialize();
   10.26 +        SDLAudioManager.initialize();
   10.27 +        SDLControllerManager.initialize();
   10.28 +    }
   10.29 +
   10.30 +    // This function stores the current activity (SDL or not)
   10.31 +    public static void setContext(Context context) {
   10.32 +        mContext = context;
   10.33 +    }
   10.34 +
   10.35 +    public static Context getContext() {
   10.36 +        return mContext;
   10.37 +    }
   10.38 +
   10.39 +    protected static Context mContext;
   10.40 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java	Mon Oct 23 15:23:43 2017 -0700
    11.3 @@ -0,0 +1,1595 @@
    11.4 +package org.libsdl.app;
    11.5 +
    11.6 +import java.io.IOException;
    11.7 +import java.io.InputStream;
    11.8 +import java.util.Arrays;
    11.9 +import java.lang.reflect.Method;
   11.10 +import java.util.Objects;
   11.11 +
   11.12 +import android.app.*;
   11.13 +import android.content.*;
   11.14 +import android.text.InputType;
   11.15 +import android.view.*;
   11.16 +import android.view.inputmethod.BaseInputConnection;
   11.17 +import android.view.inputmethod.EditorInfo;
   11.18 +import android.view.inputmethod.InputConnection;
   11.19 +import android.view.inputmethod.InputMethodManager;
   11.20 +import android.widget.RelativeLayout;
   11.21 +import android.widget.Button;
   11.22 +import android.widget.LinearLayout;
   11.23 +import android.widget.TextView;
   11.24 +import android.os.*;
   11.25 +import android.util.Log;
   11.26 +import android.util.SparseArray;
   11.27 +import android.graphics.*;
   11.28 +import android.graphics.drawable.Drawable;
   11.29 +import android.hardware.*;
   11.30 +import android.content.pm.ActivityInfo;
   11.31 +
   11.32 +/**
   11.33 +    SDL Activity
   11.34 +*/
   11.35 +public class SDLActivity extends Activity {
   11.36 +    private static final String TAG = "SDL";
   11.37 +
   11.38 +    public static boolean mIsResumedCalled, mIsSurfaceReady, mHasFocus;
   11.39 +
   11.40 +    // Handle the state of the native layer
   11.41 +    public enum NativeState {
   11.42 +           INIT, RESUMED, PAUSED
   11.43 +    }
   11.44 +
   11.45 +    public static NativeState mNextNativeState;
   11.46 +    public static NativeState mCurrentNativeState;
   11.47 +
   11.48 +    public static boolean mExitCalledFromJava;
   11.49 +
   11.50 +    /** If shared libraries (e.g. SDL or the native application) could not be loaded. */
   11.51 +    public static boolean mBrokenLibraries;
   11.52 +
   11.53 +    // If we want to separate mouse and touch events.
   11.54 +    //  This is only toggled in native code when a hint is set!
   11.55 +    public static boolean mSeparateMouseAndTouch;
   11.56 +
   11.57 +    // Main components
   11.58 +    protected static SDLActivity mSingleton;
   11.59 +    protected static SDLSurface mSurface;
   11.60 +    protected static View mTextEdit;
   11.61 +    protected static boolean mScreenKeyboardShown;
   11.62 +    protected static ViewGroup mLayout;
   11.63 +    protected static SDLClipboardHandler mClipboardHandler;
   11.64 +
   11.65 +
   11.66 +    // This is what SDL runs in. It invokes SDL_main(), eventually
   11.67 +    protected static Thread mSDLThread;
   11.68 +
   11.69 +    /**
   11.70 +     * This method returns the name of the shared object with the application entry point
   11.71 +     * It can be overridden by derived classes.
   11.72 +     */
   11.73 +    protected String getMainSharedObject() {
   11.74 +        String library;
   11.75 +        String[] libraries = SDLActivity.mSingleton.getLibraries();
   11.76 +        if (libraries.length > 0) {
   11.77 +            library = "lib" + libraries[libraries.length - 1] + ".so";
   11.78 +        } else {
   11.79 +            library = "libmain.so";
   11.80 +        }
   11.81 +        return library;
   11.82 +    }
   11.83 +
   11.84 +    /**
   11.85 +     * This method returns the name of the application entry point
   11.86 +     * It can be overridden by derived classes.
   11.87 +     */
   11.88 +    protected String getMainFunction() {
   11.89 +        return "SDL_main";
   11.90 +    }
   11.91 +
   11.92 +    /**
   11.93 +     * This method is called by SDL before loading the native shared libraries.
   11.94 +     * It can be overridden to provide names of shared libraries to be loaded.
   11.95 +     * The default implementation returns the defaults. It never returns null.
   11.96 +     * An array returned by a new implementation must at least contain "SDL2".
   11.97 +     * Also keep in mind that the order the libraries are loaded may matter.
   11.98 +     * @return names of shared libraries to be loaded (e.g. "SDL2", "main").
   11.99 +     */
  11.100 +    protected String[] getLibraries() {
  11.101 +        return new String[] {
  11.102 +            "SDL2",
  11.103 +            // "SDL2_image",
  11.104 +            // "SDL2_mixer",
  11.105 +            // "SDL2_net",
  11.106 +            // "SDL2_ttf",
  11.107 +            "main"
  11.108 +        };
  11.109 +    }
  11.110 +
  11.111 +    // Load the .so
  11.112 +    public void loadLibraries() {
  11.113 +       for (String lib : getLibraries()) {
  11.114 +          System.loadLibrary(lib);
  11.115 +       }
  11.116 +    }
  11.117 +
  11.118 +    /**
  11.119 +     * This method is called by SDL before starting the native application thread.
  11.120 +     * It can be overridden to provide the arguments after the application name.
  11.121 +     * The default implementation returns an empty array. It never returns null.
  11.122 +     * @return arguments for the native application.
  11.123 +     */
  11.124 +    protected String[] getArguments() {
  11.125 +        return new String[0];
  11.126 +    }
  11.127 +
  11.128 +    public static void initialize() {
  11.129 +        // The static nature of the singleton and Android quirkyness force us to initialize everything here
  11.130 +        // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
  11.131 +        mSingleton = null;
  11.132 +        mSurface = null;
  11.133 +        mTextEdit = null;
  11.134 +        mLayout = null;
  11.135 +        mClipboardHandler = null;
  11.136 +        mSDLThread = null;
  11.137 +        mExitCalledFromJava = false;
  11.138 +        mBrokenLibraries = false;
  11.139 +        mIsResumedCalled = false;
  11.140 +        mIsSurfaceReady = false;
  11.141 +        mHasFocus = true;
  11.142 +        mNextNativeState = NativeState.INIT;
  11.143 +        mCurrentNativeState = NativeState.INIT;
  11.144 +    }
  11.145 +
  11.146 +    // Setup
  11.147 +    @Override
  11.148 +    protected void onCreate(Bundle savedInstanceState) {
  11.149 +        Log.v(TAG, "Device: " + android.os.Build.DEVICE);
  11.150 +        Log.v(TAG, "Model: " + android.os.Build.MODEL);
  11.151 +        Log.v(TAG, "onCreate()");
  11.152 +        super.onCreate(savedInstanceState);
  11.153 +
  11.154 +        // Load shared libraries
  11.155 +        String errorMsgBrokenLib = "";
  11.156 +        try {
  11.157 +            loadLibraries();
  11.158 +        } catch(UnsatisfiedLinkError e) {
  11.159 +            System.err.println(e.getMessage());
  11.160 +            mBrokenLibraries = true;
  11.161 +            errorMsgBrokenLib = e.getMessage();
  11.162 +        } catch(Exception e) {
  11.163 +            System.err.println(e.getMessage());
  11.164 +            mBrokenLibraries = true;
  11.165 +            errorMsgBrokenLib = e.getMessage();
  11.166 +        }
  11.167 +
  11.168 +        if (mBrokenLibraries)
  11.169 +        {
  11.170 +            AlertDialog.Builder dlgAlert  = new AlertDialog.Builder(this);
  11.171 +            dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
  11.172 +                  + System.getProperty("line.separator")
  11.173 +                  + System.getProperty("line.separator")
  11.174 +                  + "Error: " + errorMsgBrokenLib);
  11.175 +            dlgAlert.setTitle("SDL Error");
  11.176 +            dlgAlert.setPositiveButton("Exit",
  11.177 +                new DialogInterface.OnClickListener() {
  11.178 +                    @Override
  11.179 +                    public void onClick(DialogInterface dialog,int id) {
  11.180 +                        // if this button is clicked, close current activity
  11.181 +                        SDLActivity.mSingleton.finish();
  11.182 +                    }
  11.183 +                });
  11.184 +           dlgAlert.setCancelable(false);
  11.185 +           dlgAlert.create().show();
  11.186 +
  11.187 +           return;
  11.188 +        }
  11.189 +
  11.190 +        // Set up JNI
  11.191 +        SDL.setupJNI();
  11.192 +
  11.193 +        // Initialize state
  11.194 +        SDL.initialize();
  11.195 +
  11.196 +        // So we can call stuff from static callbacks
  11.197 +        mSingleton = this;
  11.198 +        SDL.setContext(this);
  11.199 +
  11.200 +        if (Build.VERSION.SDK_INT >= 11) {
  11.201 +            mClipboardHandler = new SDLClipboardHandler_API11();
  11.202 +        } else {
  11.203 +            /* Before API 11, no clipboard notification (eg no SDL_CLIPBOARDUPDATE) */
  11.204 +            mClipboardHandler = new SDLClipboardHandler_Old();
  11.205 +        }
  11.206 +
  11.207 +        // Set up the surface
  11.208 +        mSurface = new SDLSurface(getApplication());
  11.209 +
  11.210 +        mLayout = new RelativeLayout(this);
  11.211 +        mLayout.addView(mSurface);
  11.212 +
  11.213 +        setContentView(mLayout);
  11.214 +
  11.215 +        // Get filename from "Open with" of another application
  11.216 +        Intent intent = getIntent();
  11.217 +        if (intent != null && intent.getData() != null) {
  11.218 +            String filename = intent.getData().getPath();
  11.219 +            if (filename != null) {
  11.220 +                Log.v(TAG, "Got filename: " + filename);
  11.221 +                SDLActivity.onNativeDropFile(filename);
  11.222 +            }
  11.223 +        }
  11.224 +    }
  11.225 +
  11.226 +    // Events
  11.227 +    @Override
  11.228 +    protected void onPause() {
  11.229 +        Log.v(TAG, "onPause()");
  11.230 +        super.onPause();
  11.231 +        mNextNativeState = NativeState.PAUSED;
  11.232 +        mIsResumedCalled = false;
  11.233 +
  11.234 +        if (SDLActivity.mBrokenLibraries) {
  11.235 +           return;
  11.236 +        }
  11.237 +
  11.238 +        SDLActivity.handleNativeState();
  11.239 +    }
  11.240 +
  11.241 +    @Override
  11.242 +    protected void onResume() {
  11.243 +        Log.v(TAG, "onResume()");
  11.244 +        super.onResume();
  11.245 +        mNextNativeState = NativeState.RESUMED;
  11.246 +        mIsResumedCalled = true;
  11.247 +
  11.248 +        if (SDLActivity.mBrokenLibraries) {
  11.249 +           return;
  11.250 +        }
  11.251 +
  11.252 +        SDLActivity.handleNativeState();
  11.253 +    }
  11.254 +
  11.255 +
  11.256 +    @Override
  11.257 +    public void onWindowFocusChanged(boolean hasFocus) {
  11.258 +        super.onWindowFocusChanged(hasFocus);
  11.259 +        Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
  11.260 +
  11.261 +        if (SDLActivity.mBrokenLibraries) {
  11.262 +           return;
  11.263 +        }
  11.264 +
  11.265 +        SDLActivity.mHasFocus = hasFocus;
  11.266 +        if (hasFocus) {
  11.267 +           mNextNativeState = NativeState.RESUMED;
  11.268 +        } else {
  11.269 +           mNextNativeState = NativeState.PAUSED;
  11.270 +        }
  11.271 +
  11.272 +        SDLActivity.handleNativeState();
  11.273 +    }
  11.274 +
  11.275 +    @Override
  11.276 +    public void onLowMemory() {
  11.277 +        Log.v(TAG, "onLowMemory()");
  11.278 +        super.onLowMemory();
  11.279 +
  11.280 +        if (SDLActivity.mBrokenLibraries) {
  11.281 +           return;
  11.282 +        }
  11.283 +
  11.284 +        SDLActivity.nativeLowMemory();
  11.285 +    }
  11.286 +
  11.287 +    @Override
  11.288 +    protected void onDestroy() {
  11.289 +        Log.v(TAG, "onDestroy()");
  11.290 +
  11.291 +        if (SDLActivity.mBrokenLibraries) {
  11.292 +           super.onDestroy();
  11.293 +           // Reset everything in case the user re opens the app
  11.294 +           SDLActivity.initialize();
  11.295 +           return;
  11.296 +        }
  11.297 +
  11.298 +        mNextNativeState = NativeState.PAUSED;
  11.299 +        SDLActivity.handleNativeState();
  11.300 +
  11.301 +        // Send a quit message to the application
  11.302 +        SDLActivity.mExitCalledFromJava = true;
  11.303 +        SDLActivity.nativeQuit();
  11.304 +
  11.305 +        // Now wait for the SDL thread to quit
  11.306 +        if (SDLActivity.mSDLThread != null) {
  11.307 +            try {
  11.308 +                SDLActivity.mSDLThread.join();
  11.309 +            } catch(Exception e) {
  11.310 +                Log.v(TAG, "Problem stopping thread: " + e);
  11.311 +            }
  11.312 +            SDLActivity.mSDLThread = null;
  11.313 +
  11.314 +            //Log.v(TAG, "Finished waiting for SDL thread");
  11.315 +        }
  11.316 +
  11.317 +        super.onDestroy();
  11.318 +
  11.319 +        // Reset everything in case the user re opens the app
  11.320 +        SDLActivity.initialize();
  11.321 +    }
  11.322 +
  11.323 +    @Override
  11.324 +    public boolean dispatchKeyEvent(KeyEvent event) {
  11.325 +
  11.326 +        if (SDLActivity.mBrokenLibraries) {
  11.327 +           return false;
  11.328 +        }
  11.329 +
  11.330 +        int keyCode = event.getKeyCode();
  11.331 +        // Ignore certain special keys so they're handled by Android
  11.332 +        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
  11.333 +            keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
  11.334 +            keyCode == KeyEvent.KEYCODE_CAMERA ||
  11.335 +            keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */
  11.336 +            keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */
  11.337 +            ) {
  11.338 +            return false;
  11.339 +        }
  11.340 +        return super.dispatchKeyEvent(event);
  11.341 +    }
  11.342 +
  11.343 +    /* Transition to next state */
  11.344 +    public static void handleNativeState() {
  11.345 +
  11.346 +        if (mNextNativeState == mCurrentNativeState) {
  11.347 +            // Already in same state, discard.
  11.348 +            return;
  11.349 +        }
  11.350 +
  11.351 +        // Try a transition to init state
  11.352 +        if (mNextNativeState == NativeState.INIT) {
  11.353 +
  11.354 +            mCurrentNativeState = mNextNativeState;
  11.355 +            return;
  11.356 +        }
  11.357 +
  11.358 +        // Try a transition to paused state
  11.359 +        if (mNextNativeState == NativeState.PAUSED) {
  11.360 +            nativePause();
  11.361 +            mSurface.handlePause();
  11.362 +            mCurrentNativeState = mNextNativeState;
  11.363 +            return;
  11.364 +        }
  11.365 +
  11.366 +        // Try a transition to resumed state
  11.367 +        if (mNextNativeState == NativeState.RESUMED) {
  11.368 +            if (mIsSurfaceReady && mHasFocus && mIsResumedCalled) {
  11.369 +                if (mSDLThread == null) {
  11.370 +                    // This is the entry point to the C app.
  11.371 +                    // Start up the C app thread and enable sensor input for the first time
  11.372 +                    // FIXME: Why aren't we enabling sensor input at start?
  11.373 +
  11.374 +                    final Thread sdlThread = new Thread(new SDLMain(), "SDLThread");
  11.375 +                    mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
  11.376 +                    sdlThread.start();
  11.377 +
  11.378 +                    // Set up a listener thread to catch when the native thread ends
  11.379 +                    mSDLThread = new Thread(new Runnable() {
  11.380 +                        @Override
  11.381 +                        public void run() {
  11.382 +                            try {
  11.383 +                                sdlThread.join();
  11.384 +                            } catch (Exception e) {
  11.385 +                                // Ignore any exception
  11.386 +                            } finally {
  11.387 +                                // Native thread has finished
  11.388 +                                if (!mExitCalledFromJava) {
  11.389 +                                    handleNativeExit();
  11.390 +                                }
  11.391 +                            }
  11.392 +                        }
  11.393 +                    }, "SDLThreadListener");
  11.394 +
  11.395 +                    mSDLThread.start();
  11.396 +                }
  11.397 +
  11.398 +                nativeResume();
  11.399 +                mSurface.handleResume();
  11.400 +                mCurrentNativeState = mNextNativeState;
  11.401 +            }
  11.402 +        }
  11.403 +    }
  11.404 +
  11.405 +    /* The native thread has finished */
  11.406 +    public static void handleNativeExit() {
  11.407 +        SDLActivity.mSDLThread = null;
  11.408 +        mSingleton.finish();
  11.409 +    }
  11.410 +
  11.411 +
  11.412 +    // Messages from the SDLMain thread
  11.413 +    static final int COMMAND_CHANGE_TITLE = 1;
  11.414 +    static final int COMMAND_UNUSED = 2;
  11.415 +    static final int COMMAND_TEXTEDIT_HIDE = 3;
  11.416 +    static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
  11.417 +
  11.418 +    protected static final int COMMAND_USER = 0x8000;
  11.419 +
  11.420 +    /**
  11.421 +     * This method is called by SDL if SDL did not handle a message itself.
  11.422 +     * This happens if a received message contains an unsupported command.
  11.423 +     * Method can be overwritten to handle Messages in a different class.
  11.424 +     * @param command the command of the message.
  11.425 +     * @param param the parameter of the message. May be null.
  11.426 +     * @return if the message was handled in overridden method.
  11.427 +     */
  11.428 +    protected boolean onUnhandledMessage(int command, Object param) {
  11.429 +        return false;
  11.430 +    }
  11.431 +
  11.432 +    /**
  11.433 +     * A Handler class for Messages from native SDL applications.
  11.434 +     * It uses current Activities as target (e.g. for the title).
  11.435 +     * static to prevent implicit references to enclosing object.
  11.436 +     */
  11.437 +    protected static class SDLCommandHandler extends Handler {
  11.438 +        @Override
  11.439 +        public void handleMessage(Message msg) {
  11.440 +            Context context = SDL.getContext();
  11.441 +            if (context == null) {
  11.442 +                Log.e(TAG, "error handling message, getContext() returned null");
  11.443 +                return;
  11.444 +            }
  11.445 +            switch (msg.arg1) {
  11.446 +            case COMMAND_CHANGE_TITLE:
  11.447 +                if (context instanceof Activity) {
  11.448 +                    ((Activity) context).setTitle((String)msg.obj);
  11.449 +                } else {
  11.450 +                    Log.e(TAG, "error handling message, getContext() returned no Activity");
  11.451 +                }
  11.452 +                break;
  11.453 +            case COMMAND_TEXTEDIT_HIDE:
  11.454 +                if (mTextEdit != null) {
  11.455 +                    // Note: On some devices setting view to GONE creates a flicker in landscape.
  11.456 +                    // Setting the View's sizes to 0 is similar to GONE but without the flicker.
  11.457 +                    // The sizes will be set to useful values when the keyboard is shown again.
  11.458 +                    mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
  11.459 +
  11.460 +                    InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
  11.461 +                    imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
  11.462 +                    
  11.463 +                    mScreenKeyboardShown = false;
  11.464 +                }
  11.465 +                break;
  11.466 +            case COMMAND_SET_KEEP_SCREEN_ON:
  11.467 +            {
  11.468 +                if (context instanceof Activity) {
  11.469 +                    Window window = ((Activity) context).getWindow();
  11.470 +                    if (window != null) {
  11.471 +                        if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
  11.472 +                            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  11.473 +                        } else {
  11.474 +                            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  11.475 +                        }
  11.476 +                    }
  11.477 +                }
  11.478 +                break;
  11.479 +            }
  11.480 +            default:
  11.481 +                if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) {
  11.482 +                    Log.e(TAG, "error handling message, command is " + msg.arg1);
  11.483 +                }
  11.484 +            }
  11.485 +        }
  11.486 +    }
  11.487 +
  11.488 +    // Handler for the messages
  11.489 +    Handler commandHandler = new SDLCommandHandler();
  11.490 +
  11.491 +    // Send a message from the SDLMain thread
  11.492 +    boolean sendCommand(int command, Object data) {
  11.493 +        Message msg = commandHandler.obtainMessage();
  11.494 +        msg.arg1 = command;
  11.495 +        msg.obj = data;
  11.496 +        return commandHandler.sendMessage(msg);
  11.497 +    }
  11.498 +
  11.499 +    // C functions we call
  11.500 +    public static native int nativeSetupJNI();
  11.501 +    public static native int nativeRunMain(String library, String function, Object arguments);
  11.502 +    public static native void nativeLowMemory();
  11.503 +    public static native void nativeQuit();
  11.504 +    public static native void nativePause();
  11.505 +    public static native void nativeResume();
  11.506 +    public static native void onNativeDropFile(String filename);
  11.507 +    public static native void onNativeResize(int x, int y, int format, float rate);
  11.508 +    public static native void onNativeKeyDown(int keycode);
  11.509 +    public static native void onNativeKeyUp(int keycode);
  11.510 +    public static native void onNativeKeyboardFocusLost();
  11.511 +    public static native void onNativeMouse(int button, int action, float x, float y);
  11.512 +    public static native void onNativeTouch(int touchDevId, int pointerFingerId,
  11.513 +                                            int action, float x,
  11.514 +                                            float y, float p);
  11.515 +    public static native void onNativeAccel(float x, float y, float z);
  11.516 +    public static native void onNativeClipboardChanged();
  11.517 +    public static native void onNativeSurfaceChanged();
  11.518 +    public static native void onNativeSurfaceDestroyed();
  11.519 +    public static native String nativeGetHint(String name);
  11.520 +
  11.521 +    /**
  11.522 +     * This method is called by SDL using JNI.
  11.523 +     */
  11.524 +    public static boolean setActivityTitle(String title) {
  11.525 +        // Called from SDLMain() thread and can't directly affect the view
  11.526 +        return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
  11.527 +    }
  11.528 +
  11.529 +    /**
  11.530 +     * This method is called by SDL using JNI.
  11.531 +     * This is a static method for JNI convenience, it calls a non-static method
  11.532 +     * so that is can be overridden  
  11.533 +     */
  11.534 +    public static void setOrientation(int w, int h, boolean resizable, String hint)
  11.535 +    {
  11.536 +        if (mSingleton != null) {
  11.537 +            mSingleton.setOrientationBis(w, h, resizable, hint);
  11.538 +        }
  11.539 +    }
  11.540 +   
  11.541 +    /**
  11.542 +     * This can be overridden
  11.543 +     */
  11.544 +    public void setOrientationBis(int w, int h, boolean resizable, String hint) 
  11.545 +    {
  11.546 +      int orientation = -1;
  11.547 +
  11.548 +      if (hint != "") {
  11.549 +         if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) {
  11.550 +            orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
  11.551 +         } else if (hint.contains("LandscapeRight")) {
  11.552 +            orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
  11.553 +         } else if (hint.contains("LandscapeLeft")) {
  11.554 +            orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
  11.555 +         } else if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) {
  11.556 +            orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
  11.557 +         } else if (hint.contains("Portrait")) {
  11.558 +            orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
  11.559 +         } else if (hint.contains("PortraitUpsideDown")) {
  11.560 +            orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
  11.561 +         }
  11.562 +      }
  11.563 +
  11.564 +      /* no valid hint */
  11.565 +      if (orientation == -1) {
  11.566 +         if (resizable) {
  11.567 +            /* no fixed orientation */
  11.568 +         } else {
  11.569 +            if (w > h) {
  11.570 +               orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
  11.571 +            } else {
  11.572 +               orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
  11.573 +            }
  11.574 +         }
  11.575 +      }
  11.576 +
  11.577 +      Log.v("SDL", "setOrientation() orientation=" + orientation + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint);
  11.578 +      if (orientation != -1) {
  11.579 +         mSingleton.setRequestedOrientation(orientation);
  11.580 +      }
  11.581 +    }
  11.582 +
  11.583 +
  11.584 +    /**
  11.585 +     * This method is called by SDL using JNI.
  11.586 +     */
  11.587 +    public static boolean isScreenKeyboardShown() 
  11.588 +    {
  11.589 +        if (mTextEdit == null) {
  11.590 +            return false;
  11.591 +        }
  11.592 +
  11.593 +        if (!mScreenKeyboardShown) {
  11.594 +            return false;
  11.595 +        }
  11.596 +
  11.597 +        InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
  11.598 +        return imm.isAcceptingText();
  11.599 +
  11.600 +    }
  11.601 +
  11.602 +    /**
  11.603 +     * This method is called by SDL using JNI.
  11.604 +     */
  11.605 +    public static boolean sendMessage(int command, int param) {
  11.606 +        if (mSingleton == null) {
  11.607 +            return false;
  11.608 +        }
  11.609 +        return mSingleton.sendCommand(command, Integer.valueOf(param));
  11.610 +    }
  11.611 +
  11.612 +    /**
  11.613 +     * This method is called by SDL using JNI.
  11.614 +     */
  11.615 +    public static Context getContext() {
  11.616 +        return SDL.getContext();
  11.617 +    }
  11.618 +
  11.619 +    static class ShowTextInputTask implements Runnable {
  11.620 +        /*
  11.621 +         * This is used to regulate the pan&scan method to have some offset from
  11.622 +         * the bottom edge of the input region and the top edge of an input
  11.623 +         * method (soft keyboard)
  11.624 +         */
  11.625 +        static final int HEIGHT_PADDING = 15;
  11.626 +
  11.627 +        public int x, y, w, h;
  11.628 +
  11.629 +        public ShowTextInputTask(int x, int y, int w, int h) {
  11.630 +            this.x = x;
  11.631 +            this.y = y;
  11.632 +            this.w = w;
  11.633 +            this.h = h;
  11.634 +        }
  11.635 +
  11.636 +        @Override
  11.637 +        public void run() {
  11.638 +            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
  11.639 +            params.leftMargin = x;
  11.640 +            params.topMargin = y;
  11.641 +
  11.642 +            if (mTextEdit == null) {
  11.643 +                mTextEdit = new DummyEdit(SDL.getContext());
  11.644 +
  11.645 +                mLayout.addView(mTextEdit, params);
  11.646 +            } else {
  11.647 +                mTextEdit.setLayoutParams(params);
  11.648 +            }
  11.649 +
  11.650 +            mTextEdit.setVisibility(View.VISIBLE);
  11.651 +            mTextEdit.requestFocus();
  11.652 +
  11.653 +            InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
  11.654 +            imm.showSoftInput(mTextEdit, 0);
  11.655 +
  11.656 +            mScreenKeyboardShown = true;
  11.657 +        }
  11.658 +    }
  11.659 +
  11.660 +    /**
  11.661 +     * This method is called by SDL using JNI.
  11.662 +     */
  11.663 +    public static boolean showTextInput(int x, int y, int w, int h) {
  11.664 +        // Transfer the task to the main thread as a Runnable
  11.665 +        return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
  11.666 +    }
  11.667 +
  11.668 +    public static boolean isTextInputEvent(KeyEvent event) {
  11.669 +      
  11.670 +        // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT
  11.671 +        if (android.os.Build.VERSION.SDK_INT >= 11) {
  11.672 +            if (event.isCtrlPressed()) {
  11.673 +                return false;
  11.674 +            }  
  11.675 +        }
  11.676 +
  11.677 +        return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
  11.678 +    }
  11.679 +
  11.680 +    /**
  11.681 +     * This method is called by SDL using JNI.
  11.682 +     */
  11.683 +    public static Surface getNativeSurface() {
  11.684 +        if (SDLActivity.mSurface == null) {
  11.685 +            return null;
  11.686 +        }
  11.687 +        return SDLActivity.mSurface.getNativeSurface();
  11.688 +    }
  11.689 +
  11.690 +    // Input
  11.691 +
  11.692 +    /**
  11.693 +     * This method is called by SDL using JNI.
  11.694 +     * @return an array which may be empty but is never null.
  11.695 +     */
  11.696 +    public static int[] inputGetInputDeviceIds(int sources) {
  11.697 +        int[] ids = InputDevice.getDeviceIds();
  11.698 +        int[] filtered = new int[ids.length];
  11.699 +        int used = 0;
  11.700 +        for (int i = 0; i < ids.length; ++i) {
  11.701 +            InputDevice device = InputDevice.getDevice(ids[i]);
  11.702 +            if ((device != null) && ((device.getSources() & sources) != 0)) {
  11.703 +                filtered[used++] = device.getId();
  11.704 +            }
  11.705 +        }
  11.706 +        return Arrays.copyOf(filtered, used);
  11.707 +    }
  11.708 +
  11.709 +    // APK expansion files support
  11.710 +
  11.711 +    /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
  11.712 +    private static Object expansionFile;
  11.713 +
  11.714 +    /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
  11.715 +    private static Method expansionFileMethod;
  11.716 +
  11.717 +    /**
  11.718 +     * This method is called by SDL using JNI.
  11.719 +     * @return an InputStream on success or null if no expansion file was used.
  11.720 +     * @throws IOException on errors. Message is set for the SDL error message.
  11.721 +     */
  11.722 +    public static InputStream openAPKExpansionInputStream(String fileName) throws IOException {
  11.723 +        // Get a ZipResourceFile representing a merger of both the main and patch files
  11.724 +        if (expansionFile == null) {
  11.725 +            String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION");
  11.726 +            if (mainHint == null) {
  11.727 +                return null; // no expansion use if no main version was set
  11.728 +            }
  11.729 +            String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION");
  11.730 +            if (patchHint == null) {
  11.731 +                return null; // no expansion use if no patch version was set
  11.732 +            }
  11.733 +
  11.734 +            Integer mainVersion;
  11.735 +            Integer patchVersion;
  11.736 +            try {
  11.737 +                mainVersion = Integer.valueOf(mainHint);
  11.738 +                patchVersion = Integer.valueOf(patchHint);
  11.739 +            } catch (NumberFormatException ex) {
  11.740 +                ex.printStackTrace();
  11.741 +                throw new IOException("No valid file versions set for APK expansion files", ex);
  11.742 +            }
  11.743 +
  11.744 +            try {
  11.745 +                // To avoid direct dependency on Google APK expansion library that is
  11.746 +                // not a part of Android SDK we access it using reflection
  11.747 +                expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
  11.748 +                    .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
  11.749 +                    .invoke(null, SDL.getContext(), mainVersion, patchVersion);
  11.750 +
  11.751 +                expansionFileMethod = expansionFile.getClass()
  11.752 +                    .getMethod("getInputStream", String.class);
  11.753 +            } catch (Exception ex) {
  11.754 +                ex.printStackTrace();
  11.755 +                expansionFile = null;
  11.756 +                expansionFileMethod = null;
  11.757 +                throw new IOException("Could not access APK expansion support library", ex);
  11.758 +            }
  11.759 +        }
  11.760 +
  11.761 +        // Get an input stream for a known file inside the expansion file ZIPs
  11.762 +        InputStream fileStream;
  11.763 +        try {
  11.764 +            fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName);
  11.765 +        } catch (Exception ex) {
  11.766 +            // calling "getInputStream" failed
  11.767 +            ex.printStackTrace();
  11.768 +            throw new IOException("Could not open stream from APK expansion file", ex);
  11.769 +        }
  11.770 +
  11.771 +        if (fileStream == null) {
  11.772 +            // calling "getInputStream" was successful but null was returned
  11.773 +            throw new IOException("Could not find path in APK expansion file");
  11.774 +        }
  11.775 +
  11.776 +        return fileStream;
  11.777 +    }
  11.778 +
  11.779 +    // Messagebox
  11.780 +
  11.781 +    /** Result of current messagebox. Also used for blocking the calling thread. */
  11.782 +    protected final int[] messageboxSelection = new int[1];
  11.783 +
  11.784 +    /** Id of current dialog. */
  11.785 +    protected int dialogs = 0;
  11.786 +
  11.787 +    /**
  11.788 +     * This method is called by SDL using JNI.
  11.789 +     * Shows the messagebox from UI thread and block calling thread.
  11.790 +     * buttonFlags, buttonIds and buttonTexts must have same length.
  11.791 +     * @param buttonFlags array containing flags for every button.
  11.792 +     * @param buttonIds array containing id for every button.
  11.793 +     * @param buttonTexts array containing text for every button.
  11.794 +     * @param colors null for default or array of length 5 containing colors.
  11.795 +     * @return button id or -1.
  11.796 +     */
  11.797 +    public int messageboxShowMessageBox(
  11.798 +            final int flags,
  11.799 +            final String title,
  11.800 +            final String message,
  11.801 +            final int[] buttonFlags,
  11.802 +            final int[] buttonIds,
  11.803 +            final String[] buttonTexts,
  11.804 +            final int[] colors) {
  11.805 +
  11.806 +        messageboxSelection[0] = -1;
  11.807 +
  11.808 +        // sanity checks
  11.809 +
  11.810 +        if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) {
  11.811 +            return -1; // implementation broken
  11.812 +        }
  11.813 +
  11.814 +        // collect arguments for Dialog
  11.815 +
  11.816 +        final Bundle args = new Bundle();
  11.817 +        args.putInt("flags", flags);
  11.818 +        args.putString("title", title);
  11.819 +        args.putString("message", message);
  11.820 +        args.putIntArray("buttonFlags", buttonFlags);
  11.821 +        args.putIntArray("buttonIds", buttonIds);
  11.822 +        args.putStringArray("buttonTexts", buttonTexts);
  11.823 +        args.putIntArray("colors", colors);
  11.824 +
  11.825 +        // trigger Dialog creation on UI thread
  11.826 +
  11.827 +        runOnUiThread(new Runnable() {
  11.828 +            @Override
  11.829 +            public void run() {
  11.830 +                showDialog(dialogs++, args);
  11.831 +            }
  11.832 +        });
  11.833 +
  11.834 +        // block the calling thread
  11.835 +
  11.836 +        synchronized (messageboxSelection) {
  11.837 +            try {
  11.838 +                messageboxSelection.wait();
  11.839 +            } catch (InterruptedException ex) {
  11.840 +                ex.printStackTrace();
  11.841 +                return -1;
  11.842 +            }
  11.843 +        }
  11.844 +
  11.845 +        // return selected value
  11.846 +
  11.847 +        return messageboxSelection[0];
  11.848 +    }
  11.849 +
  11.850 +    @Override
  11.851 +    protected Dialog onCreateDialog(int ignore, Bundle args) {
  11.852 +
  11.853 +        // TODO set values from "flags" to messagebox dialog
  11.854 +
  11.855 +        // get colors
  11.856 +
  11.857 +        int[] colors = args.getIntArray("colors");
  11.858 +        int backgroundColor;
  11.859 +        int textColor;
  11.860 +        int buttonBorderColor;
  11.861 +        int buttonBackgroundColor;
  11.862 +        int buttonSelectedColor;
  11.863 +        if (colors != null) {
  11.864 +            int i = -1;
  11.865 +            backgroundColor = colors[++i];
  11.866 +            textColor = colors[++i];
  11.867 +            buttonBorderColor = colors[++i];
  11.868 +            buttonBackgroundColor = colors[++i];
  11.869 +            buttonSelectedColor = colors[++i];
  11.870 +        } else {
  11.871 +            backgroundColor = Color.TRANSPARENT;
  11.872 +            textColor = Color.TRANSPARENT;
  11.873 +            buttonBorderColor = Color.TRANSPARENT;
  11.874 +            buttonBackgroundColor = Color.TRANSPARENT;
  11.875 +            buttonSelectedColor = Color.TRANSPARENT;
  11.876 +        }
  11.877 +
  11.878 +        // create dialog with title and a listener to wake up calling thread
  11.879 +
  11.880 +        final Dialog dialog = new Dialog(this);
  11.881 +        dialog.setTitle(args.getString("title"));
  11.882 +        dialog.setCancelable(false);
  11.883 +        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
  11.884 +            @Override
  11.885 +            public void onDismiss(DialogInterface unused) {
  11.886 +                synchronized (messageboxSelection) {
  11.887 +                    messageboxSelection.notify();
  11.888 +                }
  11.889 +            }
  11.890 +        });
  11.891 +
  11.892 +        // create text
  11.893 +
  11.894 +        TextView message = new TextView(this);
  11.895 +        message.setGravity(Gravity.CENTER);
  11.896 +        message.setText(args.getString("message"));
  11.897 +        if (textColor != Color.TRANSPARENT) {
  11.898 +            message.setTextColor(textColor);
  11.899 +        }
  11.900 +
  11.901 +        // create buttons
  11.902 +
  11.903 +        int[] buttonFlags = args.getIntArray("buttonFlags");
  11.904 +        int[] buttonIds = args.getIntArray("buttonIds");
  11.905 +        String[] buttonTexts = args.getStringArray("buttonTexts");
  11.906 +
  11.907 +        final SparseArray<Button> mapping = new SparseArray<Button>();
  11.908 +
  11.909 +        LinearLayout buttons = new LinearLayout(this);
  11.910 +        buttons.setOrientation(LinearLayout.HORIZONTAL);
  11.911 +        buttons.setGravity(Gravity.CENTER);
  11.912 +        for (int i = 0; i < buttonTexts.length; ++i) {
  11.913 +            Button button = new Button(this);
  11.914 +            final int id = buttonIds[i];
  11.915 +            button.setOnClickListener(new View.OnClickListener() {
  11.916 +                @Override
  11.917 +                public void onClick(View v) {
  11.918 +                    messageboxSelection[0] = id;
  11.919 +                    dialog.dismiss();
  11.920 +                }
  11.921 +            });
  11.922 +            if (buttonFlags[i] != 0) {
  11.923 +                // see SDL_messagebox.h
  11.924 +                if ((buttonFlags[i] & 0x00000001) != 0) {
  11.925 +                    mapping.put(KeyEvent.KEYCODE_ENTER, button);
  11.926 +                }
  11.927 +                if ((buttonFlags[i] & 0x00000002) != 0) {
  11.928 +                    mapping.put(KeyEvent.KEYCODE_ESCAPE, button); /* API 11 */
  11.929 +                }
  11.930 +            }
  11.931 +            button.setText(buttonTexts[i]);
  11.932 +            if (textColor != Color.TRANSPARENT) {
  11.933 +                button.setTextColor(textColor);
  11.934 +            }
  11.935 +            if (buttonBorderColor != Color.TRANSPARENT) {
  11.936 +                // TODO set color for border of messagebox button
  11.937 +            }
  11.938 +            if (buttonBackgroundColor != Color.TRANSPARENT) {
  11.939 +                Drawable drawable = button.getBackground();
  11.940 +                if (drawable == null) {
  11.941 +                    // setting the color this way removes the style
  11.942 +                    button.setBackgroundColor(buttonBackgroundColor);
  11.943 +                } else {
  11.944 +                    // setting the color this way keeps the style (gradient, padding, etc.)
  11.945 +                    drawable.setColorFilter(buttonBackgroundColor, PorterDuff.Mode.MULTIPLY);
  11.946 +                }
  11.947 +            }
  11.948 +            if (buttonSelectedColor != Color.TRANSPARENT) {
  11.949 +                // TODO set color for selected messagebox button
  11.950 +            }
  11.951 +            buttons.addView(button);
  11.952 +        }
  11.953 +
  11.954 +        // create content
  11.955 +
  11.956 +        LinearLayout content = new LinearLayout(this);
  11.957 +        content.setOrientation(LinearLayout.VERTICAL);
  11.958 +        content.addView(message);
  11.959 +        content.addView(buttons);
  11.960 +        if (backgroundColor != Color.TRANSPARENT) {
  11.961 +            content.setBackgroundColor(backgroundColor);
  11.962 +        }
  11.963 +
  11.964 +        // add content to dialog and return
  11.965 +
  11.966 +        dialog.setContentView(content);
  11.967 +        dialog.setOnKeyListener(new Dialog.OnKeyListener() {
  11.968 +            @Override
  11.969 +            public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) {
  11.970 +                Button button = mapping.get(keyCode);
  11.971 +                if (button != null) {
  11.972 +                    if (event.getAction() == KeyEvent.ACTION_UP) {
  11.973 +                        button.performClick();
  11.974 +                    }
  11.975 +                    return true; // also for ignored actions
  11.976 +                }
  11.977 +                return false;
  11.978 +            }
  11.979 +        });
  11.980 +
  11.981 +        return dialog;
  11.982 +    }
  11.983 +
  11.984 +    /**
  11.985 +     * This method is called by SDL using JNI.
  11.986 +     */
  11.987 +    public static boolean clipboardHasText() {
  11.988 +        return mClipboardHandler.clipboardHasText();
  11.989 +    }
  11.990 +    
  11.991 +    /**
  11.992 +     * This method is called by SDL using JNI.
  11.993 +     */
  11.994 +    public static String clipboardGetText() {
  11.995 +        return mClipboardHandler.clipboardGetText();
  11.996 +    }
  11.997 +
  11.998 +    /**
  11.999 +     * This method is called by SDL using JNI.
 11.1000 +     */
 11.1001 +    public static void clipboardSetText(String string) {
 11.1002 +        mClipboardHandler.clipboardSetText(string);
 11.1003 +    }
 11.1004 +
 11.1005 +}
 11.1006 +
 11.1007 +/**
 11.1008 +    Simple runnable to start the SDL application
 11.1009 +*/
 11.1010 +class SDLMain implements Runnable {
 11.1011 +    @Override
 11.1012 +    public void run() {
 11.1013 +        // Runs SDL_main()
 11.1014 +        String library = SDLActivity.mSingleton.getMainSharedObject();
 11.1015 +        String function = SDLActivity.mSingleton.getMainFunction();
 11.1016 +        String[] arguments = SDLActivity.mSingleton.getArguments();
 11.1017 +
 11.1018 +        Log.v("SDL", "Running main function " + function + " from library " + library);
 11.1019 +        SDLActivity.nativeRunMain(library, function, arguments);
 11.1020 +
 11.1021 +        Log.v("SDL", "Finished main function");
 11.1022 +    }
 11.1023 +}
 11.1024 +
 11.1025 +
 11.1026 +/**
 11.1027 +    SDLSurface. This is what we draw on, so we need to know when it's created
 11.1028 +    in order to do anything useful.
 11.1029 +
 11.1030 +    Because of this, that's where we set up the SDL thread
 11.1031 +*/
 11.1032 +class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
 11.1033 +    View.OnKeyListener, View.OnTouchListener, SensorEventListener  {
 11.1034 +
 11.1035 +    // Sensors
 11.1036 +    protected static SensorManager mSensorManager;
 11.1037 +    protected static Display mDisplay;
 11.1038 +
 11.1039 +    // Keep track of the surface size to normalize touch events
 11.1040 +    protected static float mWidth, mHeight;
 11.1041 +
 11.1042 +    // Startup
 11.1043 +    public SDLSurface(Context context) {
 11.1044 +        super(context);
 11.1045 +        getHolder().addCallback(this);
 11.1046 +
 11.1047 +        setFocusable(true);
 11.1048 +        setFocusableInTouchMode(true);
 11.1049 +        requestFocus();
 11.1050 +        setOnKeyListener(this);
 11.1051 +        setOnTouchListener(this);
 11.1052 +
 11.1053 +        mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
 11.1054 +        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
 11.1055 +
 11.1056 +        if (Build.VERSION.SDK_INT >= 12) {
 11.1057 +            setOnGenericMotionListener(new SDLGenericMotionListener_API12());
 11.1058 +        }
 11.1059 +
 11.1060 +        // Some arbitrary defaults to avoid a potential division by zero
 11.1061 +        mWidth = 1.0f;
 11.1062 +        mHeight = 1.0f;
 11.1063 +    }
 11.1064 +
 11.1065 +    public void handlePause() {
 11.1066 +        enableSensor(Sensor.TYPE_ACCELEROMETER, false);
 11.1067 +    }
 11.1068 +
 11.1069 +    public void handleResume() {
 11.1070 +        setFocusable(true);
 11.1071 +        setFocusableInTouchMode(true);
 11.1072 +        requestFocus();
 11.1073 +        setOnKeyListener(this);
 11.1074 +        setOnTouchListener(this);
 11.1075 +        enableSensor(Sensor.TYPE_ACCELEROMETER, true);
 11.1076 +    }
 11.1077 +
 11.1078 +    public Surface getNativeSurface() {
 11.1079 +        return getHolder().getSurface();
 11.1080 +    }
 11.1081 +
 11.1082 +    // Called when we have a valid drawing surface
 11.1083 +    @Override
 11.1084 +    public void surfaceCreated(SurfaceHolder holder) {
 11.1085 +        Log.v("SDL", "surfaceCreated()");
 11.1086 +        holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
 11.1087 +    }
 11.1088 +
 11.1089 +    // Called when we lose the surface
 11.1090 +    @Override
 11.1091 +    public void surfaceDestroyed(SurfaceHolder holder) {
 11.1092 +        Log.v("SDL", "surfaceDestroyed()");
 11.1093 +
 11.1094 +        // Transition to pause, if needed
 11.1095 +        SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
 11.1096 +        SDLActivity.handleNativeState();
 11.1097 +
 11.1098 +        SDLActivity.mIsSurfaceReady = false;
 11.1099 +        SDLActivity.onNativeSurfaceDestroyed();
 11.1100 +    }
 11.1101 +
 11.1102 +    // Called when the surface is resized
 11.1103 +    @Override
 11.1104 +    public void surfaceChanged(SurfaceHolder holder,
 11.1105 +                               int format, int width, int height) {
 11.1106 +        Log.v("SDL", "surfaceChanged()");
 11.1107 +
 11.1108 +        int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
 11.1109 +        switch (format) {
 11.1110 +        case PixelFormat.A_8:
 11.1111 +            Log.v("SDL", "pixel format A_8");
 11.1112 +            break;
 11.1113 +        case PixelFormat.LA_88:
 11.1114 +            Log.v("SDL", "pixel format LA_88");
 11.1115 +            break;
 11.1116 +        case PixelFormat.L_8:
 11.1117 +            Log.v("SDL", "pixel format L_8");
 11.1118 +            break;
 11.1119 +        case PixelFormat.RGBA_4444:
 11.1120 +            Log.v("SDL", "pixel format RGBA_4444");
 11.1121 +            sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
 11.1122 +            break;
 11.1123 +        case PixelFormat.RGBA_5551:
 11.1124 +            Log.v("SDL", "pixel format RGBA_5551");
 11.1125 +            sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
 11.1126 +            break;
 11.1127 +        case PixelFormat.RGBA_8888:
 11.1128 +            Log.v("SDL", "pixel format RGBA_8888");
 11.1129 +            sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
 11.1130 +            break;
 11.1131 +        case PixelFormat.RGBX_8888:
 11.1132 +            Log.v("SDL", "pixel format RGBX_8888");
 11.1133 +            sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
 11.1134 +            break;
 11.1135 +        case PixelFormat.RGB_332:
 11.1136 +            Log.v("SDL", "pixel format RGB_332");
 11.1137 +            sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
 11.1138 +            break;
 11.1139 +        case PixelFormat.RGB_565:
 11.1140 +            Log.v("SDL", "pixel format RGB_565");
 11.1141 +            sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
 11.1142 +            break;
 11.1143 +        case PixelFormat.RGB_888:
 11.1144 +            Log.v("SDL", "pixel format RGB_888");
 11.1145 +            // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
 11.1146 +            sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
 11.1147 +            break;
 11.1148 +        default:
 11.1149 +            Log.v("SDL", "pixel format unknown " + format);
 11.1150 +            break;
 11.1151 +        }
 11.1152 +
 11.1153 +        mWidth = width;
 11.1154 +        mHeight = height;
 11.1155 +        SDLActivity.onNativeResize(width, height, sdlFormat, mDisplay.getRefreshRate());
 11.1156 +        Log.v("SDL", "Window size: " + width + "x" + height);
 11.1157 +
 11.1158 +
 11.1159 +        boolean skip = false;
 11.1160 +        int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
 11.1161 +
 11.1162 +        if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
 11.1163 +        {
 11.1164 +            // Accept any
 11.1165 +        }
 11.1166 +        else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT)
 11.1167 +        {
 11.1168 +            if (mWidth > mHeight) {
 11.1169 +               skip = true;
 11.1170 +            }
 11.1171 +        } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
 11.1172 +            if (mWidth < mHeight) {
 11.1173 +               skip = true;
 11.1174 +            }
 11.1175 +        }
 11.1176 +
 11.1177 +        // Special Patch for Square Resolution: Black Berry Passport
 11.1178 +        if (skip) {
 11.1179 +           double min = Math.min(mWidth, mHeight);
 11.1180 +           double max = Math.max(mWidth, mHeight);
 11.1181 +
 11.1182 +           if (max / min < 1.20) {
 11.1183 +              Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
 11.1184 +              skip = false;
 11.1185 +           }
 11.1186 +        }
 11.1187 +
 11.1188 +        if (skip) {
 11.1189 +           Log.v("SDL", "Skip .. Surface is not ready.");
 11.1190 +           SDLActivity.mIsSurfaceReady = false;
 11.1191 +           return;
 11.1192 +        }
 11.1193 +
 11.1194 +        /* Surface is ready */
 11.1195 +        SDLActivity.mIsSurfaceReady = true;
 11.1196 +
 11.1197 +        /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
 11.1198 +        SDLActivity.onNativeSurfaceChanged();
 11.1199 +
 11.1200 +        SDLActivity.handleNativeState();
 11.1201 +    }
 11.1202 +
 11.1203 +    // Key events
 11.1204 +    @Override
 11.1205 +    public boolean onKey(View  v, int keyCode, KeyEvent event) {
 11.1206 +        // Dispatch the different events depending on where they come from
 11.1207 +        // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
 11.1208 +        // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
 11.1209 +        //
 11.1210 +        // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
 11.1211 +        // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
 11.1212 +        // So, retrieve the device itself and check all of its sources
 11.1213 +        if (SDLControllerManager.isDeviceSDLJoystick(event.getDeviceId())) {
 11.1214 +            // Note that we process events with specific key codes here
 11.1215 +            if (event.getAction() == KeyEvent.ACTION_DOWN) {
 11.1216 +                if (SDLControllerManager.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
 11.1217 +                    return true;
 11.1218 +                }
 11.1219 +            } else if (event.getAction() == KeyEvent.ACTION_UP) {
 11.1220 +                if (SDLControllerManager.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
 11.1221 +                    return true;
 11.1222 +                }
 11.1223 +            }
 11.1224 +        }
 11.1225 +
 11.1226 +        if ((event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
 11.1227 +            if (event.getAction() == KeyEvent.ACTION_DOWN) {
 11.1228 +                //Log.v("SDL", "key down: " + keyCode);
 11.1229 +                SDLActivity.onNativeKeyDown(keyCode);
 11.1230 +                return true;
 11.1231 +            }
 11.1232 +            else if (event.getAction() == KeyEvent.ACTION_UP) {
 11.1233 +                //Log.v("SDL", "key up: " + keyCode);
 11.1234 +                SDLActivity.onNativeKeyUp(keyCode);
 11.1235 +                return true;
 11.1236 +            }
 11.1237 +        }
 11.1238 +
 11.1239 +        if ((event.getSource() & InputDevice.SOURCE_MOUSE) != 0) {
 11.1240 +            // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
 11.1241 +            // they are ignored here because sending them as mouse input to SDL is messy
 11.1242 +            if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
 11.1243 +                switch (event.getAction()) {
 11.1244 +                case KeyEvent.ACTION_DOWN:
 11.1245 +                case KeyEvent.ACTION_UP:
 11.1246 +                    // mark the event as handled or it will be handled by system
 11.1247 +                    // handling KEYCODE_BACK by system will call onBackPressed()
 11.1248 +                    return true;
 11.1249 +                }
 11.1250 +            }
 11.1251 +        }
 11.1252 +
 11.1253 +        return false;
 11.1254 +    }
 11.1255 +
 11.1256 +    // Touch events
 11.1257 +    @Override
 11.1258 +    public boolean onTouch(View v, MotionEvent event) {
 11.1259 +        /* Ref: http://developer.android.com/training/gestures/multi.html */
 11.1260 +        final int touchDevId = event.getDeviceId();
 11.1261 +        final int pointerCount = event.getPointerCount();
 11.1262 +        int action = event.getActionMasked();
 11.1263 +        int pointerFingerId;
 11.1264 +        int mouseButton;
 11.1265 +        int i = -1;
 11.1266 +        float x,y,p;
 11.1267 +
 11.1268 +        // !!! FIXME: dump this SDK check after 2.0.4 ships and require API14.
 11.1269 +        if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) {
 11.1270 +            if (Build.VERSION.SDK_INT < 14) {
 11.1271 +                mouseButton = 1; // all mouse buttons are the left button
 11.1272 +            } else {
 11.1273 +                try {
 11.1274 +                    mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
 11.1275 +                } catch(Exception e) {
 11.1276 +                    mouseButton = 1;    // oh well.
 11.1277 +                }
 11.1278 +            }
 11.1279 +            SDLActivity.onNativeMouse(mouseButton, action, event.getX(0), event.getY(0));
 11.1280 +        } else {
 11.1281 +            switch(action) {
 11.1282 +                case MotionEvent.ACTION_MOVE:
 11.1283 +                    for (i = 0; i < pointerCount; i++) {
 11.1284 +                        pointerFingerId = event.getPointerId(i);
 11.1285 +                        x = event.getX(i) / mWidth;
 11.1286 +                        y = event.getY(i) / mHeight;
 11.1287 +                        p = event.getPressure(i);
 11.1288 +                        if (p > 1.0f) {
 11.1289 +                            // may be larger than 1.0f on some devices
 11.1290 +                            // see the documentation of getPressure(i)
 11.1291 +                            p = 1.0f;
 11.1292 +                        }
 11.1293 +                        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
 11.1294 +                    }
 11.1295 +                    break;
 11.1296 +
 11.1297 +                case MotionEvent.ACTION_UP:
 11.1298 +                case MotionEvent.ACTION_DOWN:
 11.1299 +                    // Primary pointer up/down, the index is always zero
 11.1300 +                    i = 0;
 11.1301 +                case MotionEvent.ACTION_POINTER_UP:
 11.1302 +                case MotionEvent.ACTION_POINTER_DOWN:
 11.1303 +                    // Non primary pointer up/down
 11.1304 +                    if (i == -1) {
 11.1305 +                        i = event.getActionIndex();
 11.1306 +                    }
 11.1307 +
 11.1308 +                    pointerFingerId = event.getPointerId(i);
 11.1309 +                    x = event.getX(i) / mWidth;
 11.1310 +                    y = event.getY(i) / mHeight;
 11.1311 +                    p = event.getPressure(i);
 11.1312 +                    if (p > 1.0f) {
 11.1313 +                        // may be larger than 1.0f on some devices
 11.1314 +                        // see the documentation of getPressure(i)
 11.1315 +                        p = 1.0f;
 11.1316 +                    }
 11.1317 +                    SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
 11.1318 +                    break;
 11.1319 +
 11.1320 +                case MotionEvent.ACTION_CANCEL:
 11.1321 +                    for (i = 0; i < pointerCount; i++) {
 11.1322 +                        pointerFingerId = event.getPointerId(i);
 11.1323 +                        x = event.getX(i) / mWidth;
 11.1324 +                        y = event.getY(i) / mHeight;
 11.1325 +                        p = event.getPressure(i);
 11.1326 +                        if (p > 1.0f) {
 11.1327 +                            // may be larger than 1.0f on some devices
 11.1328 +                            // see the documentation of getPressure(i)
 11.1329 +                            p = 1.0f;
 11.1330 +                        }
 11.1331 +                        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
 11.1332 +                    }
 11.1333 +                    break;
 11.1334 +
 11.1335 +                default:
 11.1336 +                    break;
 11.1337 +            }
 11.1338 +        }
 11.1339 +
 11.1340 +        return true;
 11.1341 +   }
 11.1342 +
 11.1343 +    // Sensor events
 11.1344 +    public void enableSensor(int sensortype, boolean enabled) {
 11.1345 +        // TODO: This uses getDefaultSensor - what if we have >1 accels?
 11.1346 +        if (enabled) {
 11.1347 +            mSensorManager.registerListener(this,
 11.1348 +                            mSensorManager.getDefaultSensor(sensortype),
 11.1349 +                            SensorManager.SENSOR_DELAY_GAME, null);
 11.1350 +        } else {
 11.1351 +            mSensorManager.unregisterListener(this,
 11.1352 +                            mSensorManager.getDefaultSensor(sensortype));
 11.1353 +        }
 11.1354 +    }
 11.1355 +
 11.1356 +    @Override
 11.1357 +    public void onAccuracyChanged(Sensor sensor, int accuracy) {
 11.1358 +        // TODO
 11.1359 +    }
 11.1360 +
 11.1361 +    @Override
 11.1362 +    public void onSensorChanged(SensorEvent event) {
 11.1363 +        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
 11.1364 +            float x, y;
 11.1365 +            switch (mDisplay.getRotation()) {
 11.1366 +                case Surface.ROTATION_90:
 11.1367 +                    x = -event.values[1];
 11.1368 +                    y = event.values[0];
 11.1369 +                    break;
 11.1370 +                case Surface.ROTATION_270:
 11.1371 +                    x = event.values[1];
 11.1372 +                    y = -event.values[0];
 11.1373 +                    break;
 11.1374 +                case Surface.ROTATION_180:
 11.1375 +                    x = -event.values[1];
 11.1376 +                    y = -event.values[0];
 11.1377 +                    break;
 11.1378 +                default:
 11.1379 +                    x = event.values[0];
 11.1380 +                    y = event.values[1];
 11.1381 +                    break;
 11.1382 +            }
 11.1383 +            SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
 11.1384 +                                      y / SensorManager.GRAVITY_EARTH,
 11.1385 +                                      event.values[2] / SensorManager.GRAVITY_EARTH);
 11.1386 +        }
 11.1387 +    }
 11.1388 +}
 11.1389 +
 11.1390 +/* This is a fake invisible editor view that receives the input and defines the
 11.1391 + * pan&scan region
 11.1392 + */
 11.1393 +class DummyEdit extends View implements View.OnKeyListener {
 11.1394 +    InputConnection ic;
 11.1395 +
 11.1396 +    public DummyEdit(Context context) {
 11.1397 +        super(context);
 11.1398 +        setFocusableInTouchMode(true);
 11.1399 +        setFocusable(true);
 11.1400 +        setOnKeyListener(this);
 11.1401 +    }
 11.1402 +
 11.1403 +    @Override
 11.1404 +    public boolean onCheckIsTextEditor() {
 11.1405 +        return true;
 11.1406 +    }
 11.1407 +
 11.1408 +    @Override
 11.1409 +    public boolean onKey(View v, int keyCode, KeyEvent event) {
 11.1410 +        /* 
 11.1411 +         * This handles the hardware keyboard input
 11.1412 +         */
 11.1413 +        if (event.getAction() == KeyEvent.ACTION_DOWN) {
 11.1414 +            if (SDLActivity.isTextInputEvent(event)) {
 11.1415 +                ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
 11.1416 +            }
 11.1417 +            SDLActivity.onNativeKeyDown(keyCode);
 11.1418 +            return true;
 11.1419 +        } else if (event.getAction() == KeyEvent.ACTION_UP) {
 11.1420 +            SDLActivity.onNativeKeyUp(keyCode);
 11.1421 +            return true;
 11.1422 +        }
 11.1423 +        return false;
 11.1424 +    }
 11.1425 +
 11.1426 +    //
 11.1427 +    @Override
 11.1428 +    public boolean onKeyPreIme (int keyCode, KeyEvent event) {
 11.1429 +        // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
 11.1430 +        // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
 11.1431 +        // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
 11.1432 +        // FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
 11.1433 +        // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
 11.1434 +        // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
 11.1435 +        if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
 11.1436 +            if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
 11.1437 +                SDLActivity.onNativeKeyboardFocusLost();
 11.1438 +            }
 11.1439 +        }
 11.1440 +        return super.onKeyPreIme(keyCode, event);
 11.1441 +    }
 11.1442 +
 11.1443 +    @Override
 11.1444 +    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
 11.1445 +        ic = new SDLInputConnection(this, true);
 11.1446 +
 11.1447 +        outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
 11.1448 +        outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
 11.1449 +                | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
 11.1450 +
 11.1451 +        return ic;
 11.1452 +    }
 11.1453 +}
 11.1454 +
 11.1455 +class SDLInputConnection extends BaseInputConnection {
 11.1456 +
 11.1457 +    public SDLInputConnection(View targetView, boolean fullEditor) {
 11.1458 +        super(targetView, fullEditor);
 11.1459 +
 11.1460 +    }
 11.1461 +
 11.1462 +    @Override
 11.1463 +    public boolean sendKeyEvent(KeyEvent event) {
 11.1464 +        /*
 11.1465 +         * This handles the keycodes from soft keyboard (and IME-translated input from hardkeyboard)
 11.1466 +         */
 11.1467 +        int keyCode = event.getKeyCode();
 11.1468 +        if (event.getAction() == KeyEvent.ACTION_DOWN) {
 11.1469 +            if (SDLActivity.isTextInputEvent(event)) {
 11.1470 +                commitText(String.valueOf((char) event.getUnicodeChar()), 1);
 11.1471 +            }
 11.1472 +            SDLActivity.onNativeKeyDown(keyCode);
 11.1473 +            return true;
 11.1474 +        } else if (event.getAction() == KeyEvent.ACTION_UP) {
 11.1475 +            SDLActivity.onNativeKeyUp(keyCode);
 11.1476 +            return true;
 11.1477 +        }
 11.1478 +        return super.sendKeyEvent(event);
 11.1479 +    }
 11.1480 +
 11.1481 +    @Override
 11.1482 +    public boolean commitText(CharSequence text, int newCursorPosition) {
 11.1483 +
 11.1484 +        nativeCommitText(text.toString(), newCursorPosition);
 11.1485 +
 11.1486 +        return super.commitText(text, newCursorPosition);
 11.1487 +    }
 11.1488 +
 11.1489 +    @Override
 11.1490 +    public boolean setComposingText(CharSequence text, int newCursorPosition) {
 11.1491 +
 11.1492 +        nativeSetComposingText(text.toString(), newCursorPosition);
 11.1493 +
 11.1494 +        return super.setComposingText(text, newCursorPosition);
 11.1495 +    }
 11.1496 +
 11.1497 +    public native void nativeCommitText(String text, int newCursorPosition);
 11.1498 +
 11.1499 +    public native void nativeSetComposingText(String text, int newCursorPosition);
 11.1500 +
 11.1501 +    @Override
 11.1502 +    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
 11.1503 +        // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
 11.1504 +        // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
 11.1505 +        if (beforeLength > 0 && afterLength == 0) {
 11.1506 +            boolean ret = true;
 11.1507 +            // backspace(s)
 11.1508 +            while (beforeLength-- > 0) {
 11.1509 +               boolean ret_key = sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
 11.1510 +                              && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
 11.1511 +               ret = ret && ret_key; 
 11.1512 +            }
 11.1513 +            return ret;
 11.1514 +        }
 11.1515 +
 11.1516 +        return super.deleteSurroundingText(beforeLength, afterLength);
 11.1517 +    }
 11.1518 +}
 11.1519 +
 11.1520 +interface SDLClipboardHandler {
 11.1521 +
 11.1522 +    public boolean clipboardHasText();
 11.1523 +    public String clipboardGetText();
 11.1524 +    public void clipboardSetText(String string);
 11.1525 +
 11.1526 +}
 11.1527 +
 11.1528 +
 11.1529 +class SDLClipboardHandler_API11 implements
 11.1530 +    SDLClipboardHandler, 
 11.1531 +    android.content.ClipboardManager.OnPrimaryClipChangedListener {
 11.1532 +
 11.1533 +    protected android.content.ClipboardManager mClipMgr;
 11.1534 +
 11.1535 +    SDLClipboardHandler_API11() {
 11.1536 +       mClipMgr = (android.content.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
 11.1537 +       mClipMgr.addPrimaryClipChangedListener(this);
 11.1538 +    }
 11.1539 +
 11.1540 +    @Override
 11.1541 +    public boolean clipboardHasText() {
 11.1542 +       return mClipMgr.hasText();
 11.1543 +    }
 11.1544 +
 11.1545 +    @Override
 11.1546 +    public String clipboardGetText() {
 11.1547 +        CharSequence text;
 11.1548 +        text = mClipMgr.getText();
 11.1549 +        if (text != null) {
 11.1550 +           return text.toString();
 11.1551 +        }
 11.1552 +        return null;
 11.1553 +    }
 11.1554 +
 11.1555 +    @Override
 11.1556 +    public void clipboardSetText(String string) {
 11.1557 +       mClipMgr.removePrimaryClipChangedListener(this);
 11.1558 +       mClipMgr.setText(string);
 11.1559 +       mClipMgr.addPrimaryClipChangedListener(this);
 11.1560 +    }
 11.1561 +    
 11.1562 +    @Override
 11.1563 +    public void onPrimaryClipChanged() {
 11.1564 +        SDLActivity.onNativeClipboardChanged();
 11.1565 +    }
 11.1566 +
 11.1567 +}
 11.1568 +
 11.1569 +class SDLClipboardHandler_Old implements
 11.1570 +    SDLClipboardHandler {
 11.1571 +   
 11.1572 +    protected android.text.ClipboardManager mClipMgrOld;
 11.1573 +  
 11.1574 +    SDLClipboardHandler_Old() {
 11.1575 +       mClipMgrOld = (android.text.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
 11.1576 +    }
 11.1577 +
 11.1578 +    @Override
 11.1579 +    public boolean clipboardHasText() {
 11.1580 +       return mClipMgrOld.hasText();
 11.1581 +    }
 11.1582 +
 11.1583 +    @Override
 11.1584 +    public String clipboardGetText() {
 11.1585 +       CharSequence text;
 11.1586 +       text = mClipMgrOld.getText();
 11.1587 +       if (text != null) {
 11.1588 +          return text.toString();
 11.1589 +       }
 11.1590 +       return null;
 11.1591 +    }
 11.1592 +
 11.1593 +    @Override
 11.1594 +    public void clipboardSetText(String string) {
 11.1595 +       mClipMgrOld.setText(string);
 11.1596 +    }
 11.1597 +}
 11.1598 +
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java	Mon Oct 23 15:23:43 2017 -0700
    12.3 @@ -0,0 +1,178 @@
    12.4 +package org.libsdl.app;
    12.5 +
    12.6 +import android.media.*;
    12.7 +import android.util.Log;
    12.8 +
    12.9 +public class SDLAudioManager
   12.10 +{
   12.11 +    protected static final String TAG = "SDLAudio";
   12.12 +
   12.13 +    protected static AudioTrack mAudioTrack;
   12.14 +    protected static AudioRecord mAudioRecord;
   12.15 +
   12.16 +    public static void initialize() {
   12.17 +        mAudioTrack = null;
   12.18 +        mAudioRecord = null;
   12.19 +    }
   12.20 +
   12.21 +    // Audio
   12.22 +
   12.23 +    /**
   12.24 +     * This method is called by SDL using JNI.
   12.25 +     */
   12.26 +    public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
   12.27 +        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
   12.28 +        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
   12.29 +        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
   12.30 +
   12.31 +        Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
   12.32 +
   12.33 +        // Let the user pick a larger buffer if they really want -- but ye
   12.34 +        // gods they probably shouldn't, the minimums are horrifyingly high
   12.35 +        // latency already
   12.36 +        desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
   12.37 +
   12.38 +        if (mAudioTrack == null) {
   12.39 +            mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
   12.40 +                    channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
   12.41 +
   12.42 +            // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
   12.43 +            // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
   12.44 +            // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
   12.45 +
   12.46 +            if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
   12.47 +                Log.e(TAG, "Failed during initialization of Audio Track");
   12.48 +                mAudioTrack = null;
   12.49 +                return -1;
   12.50 +            }
   12.51 +
   12.52 +            mAudioTrack.play();
   12.53 +        }
   12.54 +
   12.55 +        Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
   12.56 +
   12.57 +        return 0;
   12.58 +    }
   12.59 +
   12.60 +    /**
   12.61 +     * This method is called by SDL using JNI.
   12.62 +     */
   12.63 +    public static void audioWriteShortBuffer(short[] buffer) {
   12.64 +        if (mAudioTrack == null) {
   12.65 +            Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
   12.66 +            return;
   12.67 +        }
   12.68 +
   12.69 +        for (int i = 0; i < buffer.length; ) {
   12.70 +            int result = mAudioTrack.write(buffer, i, buffer.length - i);
   12.71 +            if (result > 0) {
   12.72 +                i += result;
   12.73 +            } else if (result == 0) {
   12.74 +                try {
   12.75 +                    Thread.sleep(1);
   12.76 +                } catch(InterruptedException e) {
   12.77 +                    // Nom nom
   12.78 +                }
   12.79 +            } else {
   12.80 +                Log.w(TAG, "SDL audio: error return from write(short)");
   12.81 +                return;
   12.82 +            }
   12.83 +        }
   12.84 +    }
   12.85 +
   12.86 +    /**
   12.87 +     * This method is called by SDL using JNI.
   12.88 +     */
   12.89 +    public static void audioWriteByteBuffer(byte[] buffer) {
   12.90 +        if (mAudioTrack == null) {
   12.91 +            Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
   12.92 +            return;
   12.93 +        }
   12.94 +        
   12.95 +        for (int i = 0; i < buffer.length; ) {
   12.96 +            int result = mAudioTrack.write(buffer, i, buffer.length - i);
   12.97 +            if (result > 0) {
   12.98 +                i += result;
   12.99 +            } else if (result == 0) {
  12.100 +                try {
  12.101 +                    Thread.sleep(1);
  12.102 +                } catch(InterruptedException e) {
  12.103 +                    // Nom nom
  12.104 +                }
  12.105 +            } else {
  12.106 +                Log.w(TAG, "SDL audio: error return from write(byte)");
  12.107 +                return;
  12.108 +            }
  12.109 +        }
  12.110 +    }
  12.111 +
  12.112 +    /**
  12.113 +     * This method is called by SDL using JNI.
  12.114 +     */
  12.115 +    public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
  12.116 +        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
  12.117 +        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
  12.118 +        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
  12.119 +
  12.120 +        Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
  12.121 +
  12.122 +        // Let the user pick a larger buffer if they really want -- but ye
  12.123 +        // gods they probably shouldn't, the minimums are horrifyingly high
  12.124 +        // latency already
  12.125 +        desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
  12.126 +
  12.127 +        if (mAudioRecord == null) {
  12.128 +            mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
  12.129 +                    channelConfig, audioFormat, desiredFrames * frameSize);
  12.130 +
  12.131 +            // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
  12.132 +            if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
  12.133 +                Log.e(TAG, "Failed during initialization of AudioRecord");
  12.134 +                mAudioRecord.release();
  12.135 +                mAudioRecord = null;
  12.136 +                return -1;
  12.137 +            }
  12.138 +
  12.139 +            mAudioRecord.startRecording();
  12.140 +        }
  12.141 +
  12.142 +        Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
  12.143 +
  12.144 +        return 0;
  12.145 +    }
  12.146 +
  12.147 +    /** This method is called by SDL using JNI. */
  12.148 +    public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
  12.149 +        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
  12.150 +        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
  12.151 +        return mAudioRecord.read(buffer, 0, buffer.length);
  12.152 +    }
  12.153 +
  12.154 +    /** This method is called by SDL using JNI. */
  12.155 +    public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
  12.156 +        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
  12.157 +        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
  12.158 +        return mAudioRecord.read(buffer, 0, buffer.length);
  12.159 +    }
  12.160 +
  12.161 +
  12.162 +    /** This method is called by SDL using JNI. */
  12.163 +    public static void audioClose() {
  12.164 +        if (mAudioTrack != null) {
  12.165 +            mAudioTrack.stop();
  12.166 +            mAudioTrack.release();
  12.167 +            mAudioTrack = null;
  12.168 +        }
  12.169 +    }
  12.170 +
  12.171 +    /** This method is called by SDL using JNI. */
  12.172 +    public static void captureClose() {
  12.173 +        if (mAudioRecord != null) {
  12.174 +            mAudioRecord.stop();
  12.175 +            mAudioRecord.release();
  12.176 +            mAudioRecord = null;
  12.177 +        }
  12.178 +    }
  12.179 +
  12.180 +    public static native int nativeSetupJNI();
  12.181 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java	Mon Oct 23 15:23:43 2017 -0700
    13.3 @@ -0,0 +1,433 @@
    13.4 +package org.libsdl.app;
    13.5 +
    13.6 +import java.util.ArrayList;
    13.7 +import java.util.Collections;
    13.8 +import java.util.Comparator;
    13.9 +import java.util.List;
   13.10 +import java.util.Objects;
   13.11 +
   13.12 +import android.content.Context;
   13.13 +import android.os.*;
   13.14 +import android.view.*;
   13.15 +import android.util.Log;
   13.16 +
   13.17 +
   13.18 +public class SDLControllerManager 
   13.19 +{
   13.20 +
   13.21 +    public static native int nativeSetupJNI();
   13.22 +
   13.23 +    public static native int nativeAddJoystick(int device_id, String name, String desc,
   13.24 +                                               int is_accelerometer, int nbuttons,
   13.25 +                                               int naxes, int nhats, int nballs);
   13.26 +    public static native int nativeRemoveJoystick(int device_id);
   13.27 +    public static native int nativeAddHaptic(int device_id, String name);
   13.28 +    public static native int nativeRemoveHaptic(int device_id);
   13.29 +    public static native int onNativePadDown(int device_id, int keycode);
   13.30 +    public static native int onNativePadUp(int device_id, int keycode);
   13.31 +    public static native void onNativeJoy(int device_id, int axis,
   13.32 +                                          float value);
   13.33 +    public static native void onNativeHat(int device_id, int hat_id,
   13.34 +                                          int x, int y);
   13.35 +
   13.36 +    protected static SDLJoystickHandler mJoystickHandler;
   13.37 +    protected static SDLHapticHandler mHapticHandler;
   13.38 +
   13.39 +    private static final String TAG = "SDLControllerManager";
   13.40 +
   13.41 +    public static void initialize() {
   13.42 +        mJoystickHandler = null;
   13.43 +        mHapticHandler = null;
   13.44 +
   13.45 +        SDLControllerManager.setup();
   13.46 +    }
   13.47 +
   13.48 +    public static void setup() {
   13.49 +        if (Build.VERSION.SDK_INT >= 16) {
   13.50 +            mJoystickHandler = new SDLJoystickHandler_API16();
   13.51 +        } else if (Build.VERSION.SDK_INT >= 12) {
   13.52 +            mJoystickHandler = new SDLJoystickHandler_API12();
   13.53 +        } else {
   13.54 +            mJoystickHandler = new SDLJoystickHandler();
   13.55 +        }
   13.56 +        mHapticHandler = new SDLHapticHandler();
   13.57 +    }
   13.58 +
   13.59 +    // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
   13.60 +    public static boolean handleJoystickMotionEvent(MotionEvent event) {
   13.61 +        return mJoystickHandler.handleMotionEvent(event);
   13.62 +    }
   13.63 +
   13.64 +    /**
   13.65 +     * This method is called by SDL using JNI.
   13.66 +     */
   13.67 +    public static void pollInputDevices() {
   13.68 +        mJoystickHandler.pollInputDevices();
   13.69 +    }
   13.70 +
   13.71 +    /**
   13.72 +     * This method is called by SDL using JNI.
   13.73 +     */
   13.74 +    public static void pollHapticDevices() {
   13.75 +        mHapticHandler.pollHapticDevices();
   13.76 +    }
   13.77 +
   13.78 +    /**
   13.79 +     * This method is called by SDL using JNI.
   13.80 +     */
   13.81 +    public static void hapticRun(int device_id, int length) {
   13.82 +        mHapticHandler.run(device_id, length);
   13.83 +    }
   13.84 +
   13.85 +    // Check if a given device is considered a possible SDL joystick
   13.86 +    public static boolean isDeviceSDLJoystick(int deviceId) {
   13.87 +        InputDevice device = InputDevice.getDevice(deviceId);
   13.88 +        // We cannot use InputDevice.isVirtual before API 16, so let's accept
   13.89 +        // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
   13.90 +        if ((device == null) || (deviceId < 0)) {
   13.91 +            return false;
   13.92 +        }
   13.93 +        int sources = device.getSources();
   13.94 +
   13.95 +        if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) {
   13.96 +            Log.v(TAG, "Input device " + device.getName() + " is a joystick.");
   13.97 +        }
   13.98 +        if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
   13.99 +            Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
  13.100 +        }
  13.101 +        if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
  13.102 +            Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
  13.103 +        }
  13.104 +
  13.105 +        return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
  13.106 +                ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
  13.107 +                ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
  13.108 +        );
  13.109 +    }
  13.110 +
  13.111 +}
  13.112 +
  13.113 +/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
  13.114 +class SDLJoystickHandler {
  13.115 +
  13.116 +    /**
  13.117 +     * Handles given MotionEvent.
  13.118 +     * @param event the event to be handled.
  13.119 +     * @return if given event was processed.
  13.120 +     */
  13.121 +    public boolean handleMotionEvent(MotionEvent event) {
  13.122 +        return false;
  13.123 +    }
  13.124 +
  13.125 +    /**
  13.126 +     * Handles adding and removing of input devices.
  13.127 +     */
  13.128 +    public void pollInputDevices() {
  13.129 +    }
  13.130 +}
  13.131 +
  13.132 +/* Actual joystick functionality available for API >= 12 devices */
  13.133 +class SDLJoystickHandler_API12 extends SDLJoystickHandler {
  13.134 +
  13.135 +    static class SDLJoystick {
  13.136 +        public int device_id;
  13.137 +        public String name;
  13.138 +        public String desc;
  13.139 +        public ArrayList<InputDevice.MotionRange> axes;
  13.140 +        public ArrayList<InputDevice.MotionRange> hats;
  13.141 +    }
  13.142 +    static class RangeComparator implements Comparator<InputDevice.MotionRange> {
  13.143 +        @Override
  13.144 +        public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
  13.145 +            return arg0.getAxis() - arg1.getAxis();
  13.146 +        }
  13.147 +    }
  13.148 +
  13.149 +    private ArrayList<SDLJoystick> mJoysticks;
  13.150 +
  13.151 +    public SDLJoystickHandler_API12() {
  13.152 +
  13.153 +        mJoysticks = new ArrayList<SDLJoystick>();
  13.154 +    }
  13.155 +
  13.156 +    @Override
  13.157 +    public void pollInputDevices() {
  13.158 +        int[] deviceIds = InputDevice.getDeviceIds();
  13.159 +        // It helps processing the device ids in reverse order
  13.160 +        // For example, in the case of the XBox 360 wireless dongle,
  13.161 +        // so the first controller seen by SDL matches what the receiver
  13.162 +        // considers to be the first controller
  13.163 +
  13.164 +        for(int i=deviceIds.length-1; i>-1; i--) {
  13.165 +            SDLJoystick joystick = getJoystick(deviceIds[i]);
  13.166 +            if (joystick == null) {
  13.167 +                joystick = new SDLJoystick();
  13.168 +                InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
  13.169 +                if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
  13.170 +                    joystick.device_id = deviceIds[i];
  13.171 +                    joystick.name = joystickDevice.getName();
  13.172 +                    joystick.desc = getJoystickDescriptor(joystickDevice);
  13.173 +                    joystick.axes = new ArrayList<InputDevice.MotionRange>();
  13.174 +                    joystick.hats = new ArrayList<InputDevice.MotionRange>();
  13.175 +
  13.176 +                    List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
  13.177 +                    Collections.sort(ranges, new RangeComparator());
  13.178 +                    for (InputDevice.MotionRange range : ranges ) {
  13.179 +                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
  13.180 +                            if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
  13.181 +                                range.getAxis() == MotionEvent.AXIS_HAT_Y) {
  13.182 +                                joystick.hats.add(range);
  13.183 +                            }
  13.184 +                            else {
  13.185 +                                joystick.axes.add(range);
  13.186 +                            }
  13.187 +                        }
  13.188 +                    }
  13.189 +
  13.190 +                    mJoysticks.add(joystick);
  13.191 +                    SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
  13.192 +                                                           joystick.axes.size(), joystick.hats.size()/2, 0);
  13.193 +                }
  13.194 +            }
  13.195 +        }
  13.196 +
  13.197 +        /* Check removed devices */
  13.198 +        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
  13.199 +        for(int i=0; i < mJoysticks.size(); i++) {
  13.200 +            int device_id = mJoysticks.get(i).device_id;
  13.201 +            int j;
  13.202 +            for (j=0; j < deviceIds.length; j++) {
  13.203 +                if (device_id == deviceIds[j]) break;
  13.204 +            }
  13.205 +            if (j == deviceIds.length) {
  13.206 +                removedDevices.add(Integer.valueOf(device_id));
  13.207 +            }
  13.208 +        }
  13.209 +
  13.210 +        for(int i=0; i < removedDevices.size(); i++) {
  13.211 +            int device_id = removedDevices.get(i).intValue();
  13.212 +            SDLControllerManager.nativeRemoveJoystick(device_id);
  13.213 +            for (int j=0; j < mJoysticks.size(); j++) {
  13.214 +                if (mJoysticks.get(j).device_id == device_id) {
  13.215 +                    mJoysticks.remove(j);
  13.216 +                    break;
  13.217 +                }
  13.218 +            }
  13.219 +        }
  13.220 +    }
  13.221 +
  13.222 +    protected SDLJoystick getJoystick(int device_id) {
  13.223 +        for(int i=0; i < mJoysticks.size(); i++) {
  13.224 +            if (mJoysticks.get(i).device_id == device_id) {
  13.225 +                return mJoysticks.get(i);
  13.226 +            }
  13.227 +        }
  13.228 +        return null;
  13.229 +    }
  13.230 +
  13.231 +    @Override
  13.232 +    public boolean handleMotionEvent(MotionEvent event) {
  13.233 +        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
  13.234 +            int actionPointerIndex = event.getActionIndex();
  13.235 +            int action = event.getActionMasked();
  13.236 +            switch(action) {
  13.237 +                case MotionEvent.ACTION_MOVE:
  13.238 +                    SDLJoystick joystick = getJoystick(event.getDeviceId());
  13.239 +                    if ( joystick != null ) {
  13.240 +                        for (int i = 0; i < joystick.axes.size(); i++) {
  13.241 +                            InputDevice.MotionRange range = joystick.axes.get(i);
  13.242 +                            /* Normalize the value to -1...1 */
  13.243 +                            float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
  13.244 +                            SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
  13.245 +                        }
  13.246 +                        for (int i = 0; i < joystick.hats.size(); i+=2) {
  13.247 +                            int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
  13.248 +                            int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
  13.249 +                            SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
  13.250 +                        }
  13.251 +                    }
  13.252 +                    break;
  13.253 +                default:
  13.254 +                    break;
  13.255 +            }
  13.256 +        }
  13.257 +        return true;
  13.258 +    }
  13.259 +
  13.260 +    public String getJoystickDescriptor(InputDevice joystickDevice) {
  13.261 +        return joystickDevice.getName();
  13.262 +    }
  13.263 +}
  13.264 +
  13.265 +
  13.266 +class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
  13.267 +
  13.268 +    @Override
  13.269 +    public String getJoystickDescriptor(InputDevice joystickDevice) {
  13.270 +        String desc = joystickDevice.getDescriptor();
  13.271 +
  13.272 +        if (desc != null && desc != "") {
  13.273 +            return desc;
  13.274 +        }
  13.275 +
  13.276 +        return super.getJoystickDescriptor(joystickDevice);
  13.277 +    }
  13.278 +}
  13.279 +
  13.280 +class SDLHapticHandler {
  13.281 +
  13.282 +    class SDLHaptic {
  13.283 +        public int device_id;
  13.284 +        public String name;
  13.285 +        public Vibrator vib;
  13.286 +    }
  13.287 +
  13.288 +    private ArrayList<SDLHaptic> mHaptics;
  13.289 +    
  13.290 +    public SDLHapticHandler() {
  13.291 +        mHaptics = new ArrayList<SDLHaptic>();
  13.292 +    }
  13.293 +
  13.294 +    public void run(int device_id, int length) {
  13.295 +        SDLHaptic haptic = getHaptic(device_id);
  13.296 +        if (haptic != null) {
  13.297 +            haptic.vib.vibrate (length);
  13.298 +        }
  13.299 +    }
  13.300 +
  13.301 +    public void pollHapticDevices() {
  13.302 +        
  13.303 +        final int deviceId_VIBRATOR_SERVICE = 999999;
  13.304 +        boolean hasVibratorService = false;
  13.305 +
  13.306 +        int[] deviceIds = InputDevice.getDeviceIds();
  13.307 +        // It helps processing the device ids in reverse order
  13.308 +        // For example, in the case of the XBox 360 wireless dongle,
  13.309 +        // so the first controller seen by SDL matches what the receiver
  13.310 +        // considers to be the first controller
  13.311 +
  13.312 +        if (Build.VERSION.SDK_INT >= 16)
  13.313 +        {
  13.314 +            for (int i = deviceIds.length - 1; i > -1; i--) {
  13.315 +                SDLHaptic haptic = getHaptic(deviceIds[i]);
  13.316 +                if (haptic == null) {
  13.317 +                    InputDevice device = InputDevice.getDevice(deviceIds[i]);
  13.318 +                    Vibrator vib = device.getVibrator();
  13.319 +                    if (vib.hasVibrator()) {
  13.320 +                        haptic = new SDLHaptic();
  13.321 +                        haptic.device_id = deviceIds[i];
  13.322 +                        haptic.name = device.getName();
  13.323 +                        haptic.vib = vib;
  13.324 +                        mHaptics.add(haptic);
  13.325 +                        SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
  13.326 +                    }
  13.327 +                }
  13.328 +            }
  13.329 +        }
  13.330 +
  13.331 +        /* Check VIBRATOR_SERVICE */
  13.332 +        Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
  13.333 +        if (vib != null) {
  13.334 +            if (Build.VERSION.SDK_INT >= 11) {
  13.335 +                hasVibratorService = vib.hasVibrator();
  13.336 +            } else {
  13.337 +                hasVibratorService = true;
  13.338 +            }
  13.339 +
  13.340 +            if (hasVibratorService) {
  13.341 +                SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
  13.342 +                if (haptic == null) {
  13.343 +                    haptic = new SDLHaptic();
  13.344 +                    haptic.device_id = deviceId_VIBRATOR_SERVICE;
  13.345 +                    haptic.name = "VIBRATOR_SERVICE";
  13.346 +                    haptic.vib = vib; 
  13.347 +                    mHaptics.add(haptic);
  13.348 +                    SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
  13.349 +                }
  13.350 +            }
  13.351 +        }
  13.352 +
  13.353 +        /* Check removed devices */
  13.354 +        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
  13.355 +        for(int i=0; i < mHaptics.size(); i++) {
  13.356 +            int device_id = mHaptics.get(i).device_id;
  13.357 +            int j;
  13.358 +            for (j=0; j < deviceIds.length; j++) {
  13.359 +                if (device_id == deviceIds[j]) break;
  13.360 +            }
  13.361 +
  13.362 +            if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
  13.363 +                // don't remove the vibrator if it is still present
  13.364 +            } else if (j == deviceIds.length) {
  13.365 +                removedDevices.add(device_id);
  13.366 +            }
  13.367 +        }
  13.368 +
  13.369 +        for(int i=0; i < removedDevices.size(); i++) {
  13.370 +            int device_id = removedDevices.get(i);
  13.371 +            SDLControllerManager.nativeRemoveHaptic(device_id);
  13.372 +            for (int j=0; j < mHaptics.size(); j++) {
  13.373 +                if (mHaptics.get(j).device_id == device_id) {
  13.374 +                    mHaptics.remove(j);
  13.375 +                    break;
  13.376 +                }
  13.377 +            }
  13.378 +        }
  13.379 +    }
  13.380 +
  13.381 +    protected SDLHaptic getHaptic(int device_id) {
  13.382 +        for(int i=0; i < mHaptics.size(); i++) {
  13.383 +            if (mHaptics.get(i).device_id == device_id) {
  13.384 +                return mHaptics.get(i);
  13.385 +            }
  13.386 +        }
  13.387 +        return null;
  13.388 +    }   
  13.389 +}
  13.390 +
  13.391 +class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
  13.392 +    // Generic Motion (mouse hover, joystick...) events go here
  13.393 +    @Override
  13.394 +    public boolean onGenericMotion(View v, MotionEvent event) {
  13.395 +        float x, y;
  13.396 +        int action;
  13.397 +
  13.398 +        switch ( event.getSource() ) {
  13.399 +            case InputDevice.SOURCE_JOYSTICK:
  13.400 +            case InputDevice.SOURCE_GAMEPAD:
  13.401 +            case InputDevice.SOURCE_DPAD:
  13.402 +                return SDLControllerManager.handleJoystickMotionEvent(event);
  13.403 +
  13.404 +            case InputDevice.SOURCE_MOUSE:
  13.405 +                if (!SDLActivity.mSeparateMouseAndTouch) {
  13.406 +                    break;
  13.407 +                }
  13.408 +                action = event.getActionMasked();
  13.409 +                switch (action) {
  13.410 +                    case MotionEvent.ACTION_SCROLL:
  13.411 +                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
  13.412 +                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
  13.413 +                        SDLActivity.onNativeMouse(0, action, x, y);
  13.414 +                        return true;
  13.415 +
  13.416 +                    case MotionEvent.ACTION_HOVER_MOVE:
  13.417 +                        x = event.getX(0);
  13.418 +                        y = event.getY(0);
  13.419 +
  13.420 +                        SDLActivity.onNativeMouse(0, action, x, y);
  13.421 +                        return true;
  13.422 +
  13.423 +                    default:
  13.424 +                        break;
  13.425 +                }
  13.426 +                break;
  13.427 +
  13.428 +            default:
  13.429 +                break;
  13.430 +        }
  13.431 +
  13.432 +        // Event was not managed
  13.433 +        return false;
  13.434 +    }
  13.435 +}
  13.436 +
    14.1 Binary file android-project/app/src/main/res/mipmap-hdpi/ic_launcher.png has changed
    15.1 Binary file android-project/app/src/main/res/mipmap-mdpi/ic_launcher.png has changed
    16.1 Binary file android-project/app/src/main/res/mipmap-xhdpi/ic_launcher.png has changed
    17.1 Binary file android-project/app/src/main/res/mipmap-xxhdpi/ic_launcher.png has changed
    18.1 Binary file android-project/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png has changed
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/android-project/app/src/main/res/values/colors.xml	Mon Oct 23 15:23:43 2017 -0700
    19.3 @@ -0,0 +1,6 @@
    19.4 +<?xml version="1.0" encoding="utf-8"?>
    19.5 +<resources>
    19.6 +    <color name="colorPrimary">#3F51B5</color>
    19.7 +    <color name="colorPrimaryDark">#303F9F</color>
    19.8 +    <color name="colorAccent">#FF4081</color>
    19.9 +</resources>
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/android-project/app/src/main/res/values/strings.xml	Mon Oct 23 15:23:43 2017 -0700
    20.3 @@ -0,0 +1,3 @@
    20.4 +<resources>
    20.5 +    <string name="app_name">Game</string>
    20.6 +</resources>
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/android-project/app/src/main/res/values/styles.xml	Mon Oct 23 15:23:43 2017 -0700
    21.3 @@ -0,0 +1,8 @@
    21.4 +<resources>
    21.5 +
    21.6 +    <!-- Base application theme. -->
    21.7 +    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    21.8 +        <!-- Customize your theme here. -->
    21.9 +    </style>
   21.10 +
   21.11 +</resources>
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/android-project/build.gradle	Mon Oct 23 15:23:43 2017 -0700
    22.3 @@ -0,0 +1,23 @@
    22.4 +// Top-level build file where you can add configuration options common to all sub-projects/modules.
    22.5 +
    22.6 +buildscript {
    22.7 +    repositories {
    22.8 +        jcenter()
    22.9 +    }
   22.10 +    dependencies {
   22.11 +        classpath 'com.android.tools.build:gradle:2.3.3'
   22.12 +
   22.13 +        // NOTE: Do not place your application dependencies here; they belong
   22.14 +        // in the individual module build.gradle files
   22.15 +    }
   22.16 +}
   22.17 +
   22.18 +allprojects {
   22.19 +    repositories {
   22.20 +        jcenter()
   22.21 +    }
   22.22 +}
   22.23 +
   22.24 +task clean(type: Delete) {
   22.25 +    delete rootProject.buildDir
   22.26 +}
    23.1 --- a/android-project/build.properties	Mon Oct 23 12:33:18 2017 -0700
    23.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.3 @@ -1,17 +0,0 @@
    23.4 -# This file is used to override default values used by the Ant build system.
    23.5 -# 
    23.6 -# This file must be checked in Version Control Systems, as it is
    23.7 -# integral to the build system of your project.
    23.8 -
    23.9 -# This file is only used by the Ant script.
   23.10 -
   23.11 -# You can use this to override default values such as
   23.12 -#  'source.dir' for the location of your java source folder and
   23.13 -#  'out.dir' for the location of your output folder.
   23.14 -
   23.15 -# You can also use it define how the release builds are signed by declaring
   23.16 -# the following properties:
   23.17 -#  'key.store' for the location of your keystore and
   23.18 -#  'key.alias' for the name of the key to use.
   23.19 -# The password will be asked during the build when you use the 'release' target.
   23.20 -
    24.1 --- a/android-project/build.xml	Mon Oct 23 12:33:18 2017 -0700
    24.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.3 @@ -1,93 +0,0 @@
    24.4 -<?xml version="1.0" encoding="UTF-8"?>
    24.5 -<!-- This should be changed to the name of your project -->
    24.6 -<project name="SDLActivity" default="help">
    24.7 -
    24.8 -    <!-- The local.properties file is created and updated by the 'android' tool.
    24.9 -         It contains the path to the SDK. It should *NOT* be checked into
   24.10 -         Version Control Systems. -->
   24.11 -    <property file="local.properties" />
   24.12 -
   24.13 -    <!-- The ant.properties file can be created by you. It is only edited by the
   24.14 -         'android' tool to add properties to it.
   24.15 -         This is the place to change some Ant specific build properties.
   24.16 -         Here are some properties you may want to change/update:
   24.17 -
   24.18 -         source.dir
   24.19 -             The name of the source directory. Default is 'src'.
   24.20 -         out.dir
   24.21 -             The name of the output directory. Default is 'bin'.
   24.22 -
   24.23 -         For other overridable properties, look at the beginning of the rules
   24.24 -         files in the SDK, at tools/ant/build.xml
   24.25 -
   24.26 -         Properties related to the SDK location or the project target should
   24.27 -         be updated using the 'android' tool with the 'update' action.
   24.28 -
   24.29 -         This file is an integral part of the build system for your
   24.30 -         application and should be checked into Version Control Systems.
   24.31 -
   24.32 -         -->
   24.33 -    <property file="ant.properties" />
   24.34 -
   24.35 -    <!-- if sdk.dir was not set from one of the property file, then
   24.36 -         get it from the ANDROID_HOME env var.
   24.37 -         This must be done before we load project.properties since
   24.38 -         the proguard config can use sdk.dir -->
   24.39 -    <property environment="env" />
   24.40 -    <condition property="sdk.dir" value="${env.ANDROID_HOME}">
   24.41 -        <isset property="env.ANDROID_HOME" />
   24.42 -    </condition>
   24.43 -
   24.44 -    <!-- The project.properties file is created and updated by the 'android'
   24.45 -         tool, as well as ADT.
   24.46 -
   24.47 -         This contains project specific properties such as project target, and library
   24.48 -         dependencies. Lower level build properties are stored in ant.properties
   24.49 -         (or in .classpath for Eclipse projects).
   24.50 -
   24.51 -         This file is an integral part of the build system for your
   24.52 -         application and should be checked into Version Control Systems. -->
   24.53 -    <loadproperties srcFile="project.properties" />
   24.54 -
   24.55 -    <!-- quick check on sdk.dir -->
   24.56 -    <fail
   24.57 -            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
   24.58 -            unless="sdk.dir"
   24.59 -    />
   24.60 -
   24.61 -    <!--
   24.62 -        Import per project custom build rules if present at the root of the project.
   24.63 -        This is the place to put custom intermediary targets such as:
   24.64 -            -pre-build
   24.65 -            -pre-compile
   24.66 -            -post-compile (This is typically used for code obfuscation.
   24.67 -                           Compiled code location: ${out.classes.absolute.dir}
   24.68 -                           If this is not done in place, override ${out.dex.input.absolute.dir})
   24.69 -            -post-package
   24.70 -            -post-build
   24.71 -            -pre-clean
   24.72 -    -->
   24.73 -    <import file="custom_rules.xml" optional="true" />
   24.74 -
   24.75 -    <!-- Import the actual build file.
   24.76 -
   24.77 -         To customize existing targets, there are two options:
   24.78 -         - Customize only one target:
   24.79 -             - copy/paste the target into this file, *before* the
   24.80 -               <import> task.
   24.81 -             - customize it to your needs.
   24.82 -         - Customize the whole content of build.xml
   24.83 -             - copy/paste the content of the rules files (minus the top node)
   24.84 -               into this file, replacing the <import> task.
   24.85 -             - customize to your needs.
   24.86 -
   24.87 -         ***********************
   24.88 -         ****** IMPORTANT ******
   24.89 -         ***********************
   24.90 -         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
   24.91 -         in order to avoid having your file be overridden by tools such as "android update project"
   24.92 -    -->
   24.93 -    <!-- version-tag: 1 -->
   24.94 -    <import file="${sdk.dir}/tools/ant/build.xml" />
   24.95 -
   24.96 -</project>
    25.1 --- a/android-project/default.properties	Mon Oct 23 12:33:18 2017 -0700
    25.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.3 @@ -1,11 +0,0 @@
    25.4 -# This file is automatically generated by Android Tools.
    25.5 -# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
    25.6 -# 
    25.7 -# This file must be checked in Version Control Systems.
    25.8 -# 
    25.9 -# To customize properties used by the Ant build system use,
   25.10 -# "build.properties", and override values to adapt the script to your
   25.11 -# project structure.
   25.12 -
   25.13 -# Project target.
   25.14 -target=android-16
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/android-project/gradle.properties	Mon Oct 23 15:23:43 2017 -0700
    26.3 @@ -0,0 +1,17 @@
    26.4 +# Project-wide Gradle settings.
    26.5 +
    26.6 +# IDE (e.g. Android Studio) users:
    26.7 +# Gradle settings configured through the IDE *will override*
    26.8 +# any settings specified in this file.
    26.9 +
   26.10 +# For more details on how to configure your build environment visit
   26.11 +# http://www.gradle.org/docs/current/userguide/build_environment.html
   26.12 +
   26.13 +# Specifies the JVM arguments used for the daemon process.
   26.14 +# The setting is particularly useful for tweaking memory settings.
   26.15 +org.gradle.jvmargs=-Xmx1536m
   26.16 +
   26.17 +# When configured, Gradle will run in incubating parallel mode.
   26.18 +# This option should only be used with decoupled projects. More details, visit
   26.19 +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
   26.20 +# org.gradle.parallel=true
    27.1 Binary file android-project/gradle/wrapper/gradle-wrapper.jar has changed
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/android-project/gradle/wrapper/gradle-wrapper.properties	Mon Oct 23 15:23:43 2017 -0700
    28.3 @@ -0,0 +1,6 @@
    28.4 +#Mon Oct 23 13:51:26 PDT 2017
    28.5 +distributionBase=GRADLE_USER_HOME
    28.6 +distributionPath=wrapper/dists
    28.7 +zipStoreBase=GRADLE_USER_HOME
    28.8 +zipStorePath=wrapper/dists
    28.9 +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/android-project/gradlew	Mon Oct 23 15:23:43 2017 -0700
    29.3 @@ -0,0 +1,160 @@
    29.4 +#!/usr/bin/env bash
    29.5 +
    29.6 +##############################################################################
    29.7 +##
    29.8 +##  Gradle start up script for UN*X
    29.9 +##
   29.10 +##############################################################################
   29.11 +
   29.12 +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
   29.13 +DEFAULT_JVM_OPTS=""
   29.14 +
   29.15 +APP_NAME="Gradle"
   29.16 +APP_BASE_NAME=`basename "$0"`
   29.17 +
   29.18 +# Use the maximum available, or set MAX_FD != -1 to use that value.
   29.19 +MAX_FD="maximum"
   29.20 +
   29.21 +warn ( ) {
   29.22 +    echo "$*"
   29.23 +}
   29.24 +
   29.25 +die ( ) {
   29.26 +    echo
   29.27 +    echo "$*"
   29.28 +    echo
   29.29 +    exit 1
   29.30 +}
   29.31 +
   29.32 +# OS specific support (must be 'true' or 'false').
   29.33 +cygwin=false
   29.34 +msys=false
   29.35 +darwin=false
   29.36 +case "`uname`" in
   29.37 +  CYGWIN* )
   29.38 +    cygwin=true
   29.39 +    ;;
   29.40 +  Darwin* )
   29.41 +    darwin=true
   29.42 +    ;;
   29.43 +  MINGW* )
   29.44 +    msys=true
   29.45 +    ;;
   29.46 +esac
   29.47 +
   29.48 +# Attempt to set APP_HOME
   29.49 +# Resolve links: $0 may be a link
   29.50 +PRG="$0"
   29.51 +# Need this for relative symlinks.
   29.52 +while [ -h "$PRG" ] ; do
   29.53 +    ls=`ls -ld "$PRG"`
   29.54 +    link=`expr "$ls" : '.*-> \(.*\)$'`
   29.55 +    if expr "$link" : '/.*' > /dev/null; then
   29.56 +        PRG="$link"
   29.57 +    else
   29.58 +        PRG=`dirname "$PRG"`"/$link"
   29.59 +    fi
   29.60 +done
   29.61 +SAVED="`pwd`"
   29.62 +cd "`dirname \"$PRG\"`/" >/dev/null
   29.63 +APP_HOME="`pwd -P`"
   29.64 +cd "$SAVED" >/dev/null
   29.65 +
   29.66 +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
   29.67 +
   29.68 +# Determine the Java command to use to start the JVM.
   29.69 +if [ -n "$JAVA_HOME" ] ; then
   29.70 +    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
   29.71 +        # IBM's JDK on AIX uses strange locations for the executables
   29.72 +        JAVACMD="$JAVA_HOME/jre/sh/java"
   29.73 +    else
   29.74 +        JAVACMD="$JAVA_HOME/bin/java"
   29.75 +    fi
   29.76 +    if [ ! -x "$JAVACMD" ] ; then
   29.77 +        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
   29.78 +
   29.79 +Please set the JAVA_HOME variable in your environment to match the
   29.80 +location of your Java installation."
   29.81 +    fi
   29.82 +else
   29.83 +    JAVACMD="java"
   29.84 +    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
   29.85 +
   29.86 +Please set the JAVA_HOME variable in your environment to match the
   29.87 +location of your Java installation."
   29.88 +fi
   29.89 +
   29.90 +# Increase the maximum file descriptors if we can.
   29.91 +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
   29.92 +    MAX_FD_LIMIT=`ulimit -H -n`
   29.93 +    if [ $? -eq 0 ] ; then
   29.94 +        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
   29.95 +            MAX_FD="$MAX_FD_LIMIT"
   29.96 +        fi
   29.97 +        ulimit -n $MAX_FD
   29.98 +        if [ $? -ne 0 ] ; then
   29.99 +            warn "Could not set maximum file descriptor limit: $MAX_FD"
  29.100 +        fi
  29.101 +    else
  29.102 +        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
  29.103 +    fi
  29.104 +fi
  29.105 +
  29.106 +# For Darwin, add options to specify how the application appears in the dock
  29.107 +if $darwin; then
  29.108 +    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
  29.109 +fi
  29.110 +
  29.111 +# For Cygwin, switch paths to Windows format before running java
  29.112 +if $cygwin ; then
  29.113 +    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
  29.114 +    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
  29.115 +    JAVACMD=`cygpath --unix "$JAVACMD"`
  29.116 +
  29.117 +    # We build the pattern for arguments to be converted via cygpath
  29.118 +    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
  29.119 +    SEP=""
  29.120 +    for dir in $ROOTDIRSRAW ; do
  29.121 +        ROOTDIRS="$ROOTDIRS$SEP$dir"
  29.122 +        SEP="|"
  29.123 +    done
  29.124 +    OURCYGPATTERN="(^($ROOTDIRS))"
  29.125 +    # Add a user-defined pattern to the cygpath arguments
  29.126 +    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
  29.127 +        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
  29.128 +    fi
  29.129 +    # Now convert the arguments - kludge to limit ourselves to /bin/sh
  29.130 +    i=0
  29.131 +    for arg in "$@" ; do
  29.132 +        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
  29.133 +        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
  29.134 +
  29.135 +        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
  29.136 +            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
  29.137 +        else
  29.138 +            eval `echo args$i`="\"$arg\""
  29.139 +        fi
  29.140 +        i=$((i+1))
  29.141 +    done
  29.142 +    case $i in
  29.143 +        (0) set -- ;;
  29.144 +        (1) set -- "$args0" ;;
  29.145 +        (2) set -- "$args0" "$args1" ;;
  29.146 +        (3) set -- "$args0" "$args1" "$args2" ;;
  29.147 +        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
  29.148 +        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
  29.149 +        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
  29.150 +        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
  29.151 +        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
  29.152 +        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
  29.153 +    esac
  29.154 +fi
  29.155 +
  29.156 +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
  29.157 +function splitJvmOpts() {
  29.158 +    JVM_OPTS=("$@")
  29.159 +}
  29.160 +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
  29.161 +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
  29.162 +
  29.163 +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/android-project/gradlew.bat	Mon Oct 23 15:23:43 2017 -0700
    30.3 @@ -0,0 +1,90 @@
    30.4 +@if "%DEBUG%" == "" @echo off
    30.5 +@rem ##########################################################################
    30.6 +@rem
    30.7 +@rem  Gradle startup script for Windows
    30.8 +@rem
    30.9 +@rem ##########################################################################
   30.10 +
   30.11 +@rem Set local scope for the variables with windows NT shell
   30.12 +if "%OS%"=="Windows_NT" setlocal
   30.13 +
   30.14 +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
   30.15 +set DEFAULT_JVM_OPTS=
   30.16 +
   30.17 +set DIRNAME=%~dp0
   30.18 +if "%DIRNAME%" == "" set DIRNAME=.
   30.19 +set APP_BASE_NAME=%~n0
   30.20 +set APP_HOME=%DIRNAME%
   30.21 +
   30.22 +@rem Find java.exe
   30.23 +if defined JAVA_HOME goto findJavaFromJavaHome
   30.24 +
   30.25 +set JAVA_EXE=java.exe
   30.26 +%JAVA_EXE% -version >NUL 2>&1
   30.27 +if "%ERRORLEVEL%" == "0" goto init
   30.28 +
   30.29 +echo.
   30.30 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
   30.31 +echo.
   30.32 +echo Please set the JAVA_HOME variable in your environment to match the
   30.33 +echo location of your Java installation.
   30.34 +
   30.35 +goto fail
   30.36 +
   30.37 +:findJavaFromJavaHome
   30.38 +set JAVA_HOME=%JAVA_HOME:"=%
   30.39 +set JAVA_EXE=%JAVA_HOME%/bin/java.exe
   30.40 +
   30.41 +if exist "%JAVA_EXE%" goto init
   30.42 +
   30.43 +echo.
   30.44 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
   30.45 +echo.
   30.46 +echo Please set the JAVA_HOME variable in your environment to match the
   30.47 +echo location of your Java installation.
   30.48 +
   30.49 +goto fail
   30.50 +
   30.51 +:init
   30.52 +@rem Get command-line arguments, handling Windowz variants
   30.53 +
   30.54 +if not "%OS%" == "Windows_NT" goto win9xME_args
   30.55 +if "%@eval[2+2]" == "4" goto 4NT_args
   30.56 +
   30.57 +:win9xME_args
   30.58 +@rem Slurp the command line arguments.
   30.59 +set CMD_LINE_ARGS=
   30.60 +set _SKIP=2
   30.61 +
   30.62 +:win9xME_args_slurp
   30.63 +if "x%~1" == "x" goto execute
   30.64 +
   30.65 +set CMD_LINE_ARGS=%*
   30.66 +goto execute
   30.67 +
   30.68 +:4NT_args
   30.69 +@rem Get arguments from the 4NT Shell from JP Software
   30.70 +set CMD_LINE_ARGS=%$
   30.71 +
   30.72 +:execute
   30.73 +@rem Setup the command line
   30.74 +
   30.75 +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
   30.76 +
   30.77 +@rem Execute Gradle
   30.78 +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
   30.79 +
   30.80 +:end
   30.81 +@rem End local scope for the variables with windows NT shell
   30.82 +if "%ERRORLEVEL%"=="0" goto mainEnd
   30.83 +
   30.84 +:fail
   30.85 +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
   30.86 +rem the _cmd.exe /c_ return code!
   30.87 +if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
   30.88 +exit /b 1
   30.89 +
   30.90 +:mainEnd
   30.91 +if "%OS%"=="Windows_NT" endlocal
   30.92 +
   30.93 +:omega
    31.1 --- a/android-project/jni/Android.mk	Mon Oct 23 12:33:18 2017 -0700
    31.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.3 @@ -1,1 +0,0 @@
    31.4 -include $(call all-subdir-makefiles)
    32.1 --- a/android-project/jni/Application.mk	Mon Oct 23 12:33:18 2017 -0700
    32.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.3 @@ -1,10 +0,0 @@
    32.4 -
    32.5 -# Uncomment this if you're using STL in your project
    32.6 -# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
    32.7 -# APP_STL := stlport_static 
    32.8 -
    32.9 -APP_ABI := armeabi armeabi-v7a x86
   32.10 -
   32.11 -# Min SDK level
   32.12 -APP_PLATFORM=android-10
   32.13 -
    33.1 --- a/android-project/jni/src/Android.mk	Mon Oct 23 12:33:18 2017 -0700
    33.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.3 @@ -1,18 +0,0 @@
    33.4 -LOCAL_PATH := $(call my-dir)
    33.5 -
    33.6 -include $(CLEAR_VARS)
    33.7 -
    33.8 -LOCAL_MODULE := main
    33.9 -
   33.10 -SDL_PATH := ../SDL
   33.11 -
   33.12 -LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include
   33.13 -
   33.14 -# Add your application source files here...
   33.15 -LOCAL_SRC_FILES := YourSourceHere.c
   33.16 -
   33.17 -LOCAL_SHARED_LIBRARIES := SDL2
   33.18 -
   33.19 -LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog
   33.20 -
   33.21 -include $(BUILD_SHARED_LIBRARY)
    34.1 --- a/android-project/jni/src/Android_static.mk	Mon Oct 23 12:33:18 2017 -0700
    34.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.3 @@ -1,12 +0,0 @@
    34.4 -LOCAL_PATH := $(call my-dir)
    34.5 -
    34.6 -include $(CLEAR_VARS)
    34.7 -
    34.8 -LOCAL_MODULE := main
    34.9 -
   34.10 -LOCAL_SRC_FILES := YourSourceHere.c
   34.11 -
   34.12 -LOCAL_STATIC_LIBRARIES := SDL2_static
   34.13 -
   34.14 -include $(BUILD_SHARED_LIBRARY)
   34.15 -$(call import-module,SDL)LOCAL_PATH := $(call my-dir)
    35.1 --- a/android-project/proguard-project.txt	Mon Oct 23 12:33:18 2017 -0700
    35.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.3 @@ -1,20 +0,0 @@
    35.4 -# To enable ProGuard in your project, edit project.properties
    35.5 -# to define the proguard.config property as described in that file.
    35.6 -#
    35.7 -# Add project specific ProGuard rules here.
    35.8 -# By default, the flags in this file are appended to flags specified
    35.9 -# in ${sdk.dir}/tools/proguard/proguard-android.txt
   35.10 -# You can edit the include path and order by changing the ProGuard
   35.11 -# include property in project.properties.
   35.12 -#
   35.13 -# For more details, see
   35.14 -#   http://developer.android.com/guide/developing/tools/proguard.html
   35.15 -
   35.16 -# Add any project specific keep options here:
   35.17 -
   35.18 -# If your project uses WebView with JS, uncomment the following
   35.19 -# and specify the fully qualified class name to the JavaScript interface
   35.20 -# class:
   35.21 -#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   35.22 -#   public *;
   35.23 -#}
    36.1 --- a/android-project/project.properties	Mon Oct 23 12:33:18 2017 -0700
    36.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.3 @@ -1,14 +0,0 @@
    36.4 -# This file is automatically generated by Android Tools.
    36.5 -# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
    36.6 -#
    36.7 -# This file must be checked in Version Control Systems.
    36.8 -#
    36.9 -# To customize properties used by the Ant build system edit
   36.10 -# "ant.properties", and override values to adapt the script to your
   36.11 -# project structure.
   36.12 -#
   36.13 -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
   36.14 -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
   36.15 -
   36.16 -# Project target.
   36.17 -target=android-16
    37.1 Binary file android-project/res/drawable-hdpi/ic_launcher.png has changed
    38.1 Binary file android-project/res/drawable-mdpi/ic_launcher.png has changed
    39.1 Binary file android-project/res/drawable-xhdpi/ic_launcher.png has changed
    40.1 Binary file android-project/res/drawable-xxhdpi/ic_launcher.png has changed
    41.1 --- a/android-project/res/layout/main.xml	Mon Oct 23 12:33:18 2017 -0700
    41.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.3 @@ -1,13 +0,0 @@
    41.4 -<?xml version="1.0" encoding="utf-8"?>
    41.5 -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    41.6 -    android:orientation="vertical"
    41.7 -    android:layout_width="fill_parent"
    41.8 -    android:layout_height="fill_parent"
    41.9 -    >
   41.10 -<TextView  
   41.11 -    android:layout_width="fill_parent" 
   41.12 -    android:layout_height="wrap_content" 
   41.13 -    android:text="Hello World, SDLActivity"
   41.14 -    />
   41.15 -</LinearLayout>
   41.16 -
    42.1 --- a/android-project/res/values/strings.xml	Mon Oct 23 12:33:18 2017 -0700
    42.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.3 @@ -1,4 +0,0 @@
    42.4 -<?xml version="1.0" encoding="utf-8"?>
    42.5 -<resources>
    42.6 -    <string name="app_name">SDL App</string>
    42.7 -</resources>
    43.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.2 +++ b/android-project/settings.gradle	Mon Oct 23 15:23:43 2017 -0700
    43.3 @@ -0,0 +1,1 @@
    43.4 +include ':app'
    44.1 --- a/android-project/src/org/libsdl/app/SDL.java	Mon Oct 23 12:33:18 2017 -0700
    44.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    44.3 @@ -1,37 +0,0 @@
    44.4 -package org.libsdl.app;
    44.5 -
    44.6 -import android.content.Context;
    44.7 -
    44.8 -/**
    44.9 -    SDL library initialization
   44.10 -*/
   44.11 -public class SDL {
   44.12 -
   44.13 -    // This function should be called first and sets up the native code
   44.14 -    // so it can call into the Java classes
   44.15 -    public static void setupJNI() {
   44.16 -        SDLActivity.nativeSetupJNI();
   44.17 -        SDLAudioManager.nativeSetupJNI();
   44.18 -        SDLControllerManager.nativeSetupJNI();
   44.19 -    }
   44.20 -
   44.21 -    // This function should be called each time the activity is started
   44.22 -    public static void initialize() {
   44.23 -        setContext(null);
   44.24 -
   44.25 -        SDLActivity.initialize();
   44.26 -        SDLAudioManager.initialize();
   44.27 -        SDLControllerManager.initialize();
   44.28 -    }
   44.29 -
   44.30 -    // This function stores the current activity (SDL or not)
   44.31 -    public static void setContext(Context context) {
   44.32 -        mContext = context;
   44.33 -    }
   44.34 -
   44.35 -    public static Context getContext() {
   44.36 -        return mContext;
   44.37 -    }
   44.38 -
   44.39 -    protected static Context mContext;
   44.40 -}
    45.1 --- a/android-project/src/org/libsdl/app/SDLActivity.java	Mon Oct 23 12:33:18 2017 -0700
    45.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    45.3 @@ -1,1595 +0,0 @@
    45.4 -package org.libsdl.app;
    45.5 -
    45.6 -import java.io.IOException;
    45.7 -import java.io.InputStream;
    45.8 -import java.util.Arrays;
    45.9 -import java.lang.reflect.Method;
   45.10 -import java.util.Objects;
   45.11 -
   45.12 -import android.app.*;
   45.13 -import android.content.*;
   45.14 -import android.text.InputType;
   45.15 -import android.view.*;
   45.16 -import android.view.inputmethod.BaseInputConnection;
   45.17 -import android.view.inputmethod.EditorInfo;
   45.18 -import android.view.inputmethod.InputConnection;
   45.19 -import android.view.inputmethod.InputMethodManager;
   45.20 -import android.widget.RelativeLayout;
   45.21 -import android.widget.Button;
   45.22 -import android.widget.LinearLayout;
   45.23 -import android.widget.TextView;
   45.24 -import android.os.*;
   45.25 -import android.util.Log;
   45.26 -import android.util.SparseArray;
   45.27 -import android.graphics.*;
   45.28 -import android.graphics.drawable.Drawable;
   45.29 -import android.hardware.*;
   45.30 -import android.content.pm.ActivityInfo;
   45.31 -
   45.32 -/**
   45.33 -    SDL Activity
   45.34 -*/
   45.35 -public class SDLActivity extends Activity {
   45.36 -    private static final String TAG = "SDL";
   45.37 -
   45.38 -    public static boolean mIsResumedCalled, mIsSurfaceReady, mHasFocus;
   45.39 -
   45.40 -    // Handle the state of the native layer
   45.41 -    public enum NativeState {
   45.42 -           INIT, RESUMED, PAUSED 
   45.43 -    }
   45.44 -
   45.45 -    public static NativeState mNextNativeState;
   45.46 -    public static NativeState mCurrentNativeState;
   45.47 -
   45.48 -    public static boolean mExitCalledFromJava;
   45.49 -
   45.50 -    /** If shared libraries (e.g. SDL or the native application) could not be loaded. */
   45.51 -    public static boolean mBrokenLibraries;
   45.52 -
   45.53 -    // If we want to separate mouse and touch events.
   45.54 -    //  This is only toggled in native code when a hint is set!
   45.55 -    public static boolean mSeparateMouseAndTouch;
   45.56 -
   45.57 -    // Main components
   45.58 -    protected static SDLActivity mSingleton;
   45.59 -    protected static SDLSurface mSurface;
   45.60 -    protected static View mTextEdit;
   45.61 -    protected static boolean mScreenKeyboardShown;
   45.62 -    protected static ViewGroup mLayout;
   45.63 -    protected static SDLClipboardHandler mClipboardHandler;
   45.64 -
   45.65 -
   45.66 -    // This is what SDL runs in. It invokes SDL_main(), eventually
   45.67 -    protected static Thread mSDLThread;
   45.68 -
   45.69 -    /**
   45.70 -     * This method returns the name of the shared object with the application entry point
   45.71 -     * It can be overridden by derived classes.
   45.72 -     */
   45.73 -    protected String getMainSharedObject() {
   45.74 -        String library;
   45.75 -        String[] libraries = SDLActivity.mSingleton.getLibraries();
   45.76 -        if (libraries.length > 0) {
   45.77 -            library = "lib" + libraries[libraries.length - 1] + ".so";
   45.78 -        } else {
   45.79 -            library = "libmain.so";
   45.80 -        }
   45.81 -        return library;
   45.82 -    }
   45.83 -
   45.84 -    /**
   45.85 -     * This method returns the name of the application entry point
   45.86 -     * It can be overridden by derived classes.
   45.87 -     */
   45.88 -    protected String getMainFunction() {
   45.89 -        return "SDL_main";
   45.90 -    }
   45.91 -
   45.92 -    /**
   45.93 -     * This method is called by SDL before loading the native shared libraries.
   45.94 -     * It can be overridden to provide names of shared libraries to be loaded.
   45.95 -     * The default implementation returns the defaults. It never returns null.
   45.96 -     * An array returned by a new implementation must at least contain "SDL2".
   45.97 -     * Also keep in mind that the order the libraries are loaded may matter.
   45.98 -     * @return names of shared libraries to be loaded (e.g. "SDL2", "main").
   45.99 -     */
  45.100 -    protected String[] getLibraries() {
  45.101 -        return new String[] {
  45.102 -            "SDL2",
  45.103 -            // "SDL2_image",
  45.104 -            // "SDL2_mixer",
  45.105 -            // "SDL2_net",
  45.106 -            // "SDL2_ttf",
  45.107 -            "main"
  45.108 -        };
  45.109 -    }
  45.110 -
  45.111 -    // Load the .so
  45.112 -    public void loadLibraries() {
  45.113 -       for (String lib : getLibraries()) {
  45.114 -          System.loadLibrary(lib);
  45.115 -       }
  45.116 -    }
  45.117 -
  45.118 -    /**
  45.119 -     * This method is called by SDL before starting the native application thread.
  45.120 -     * It can be overridden to provide the arguments after the application name.
  45.121 -     * The default implementation returns an empty array. It never returns null.
  45.122 -     * @return arguments for the native application.
  45.123 -     */
  45.124 -    protected String[] getArguments() {
  45.125 -        return new String[0];
  45.126 -    }
  45.127 -
  45.128 -    public static void initialize() {
  45.129 -        // The static nature of the singleton and Android quirkyness force us to initialize everything here
  45.130 -        // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
  45.131 -        mSingleton = null;
  45.132 -        mSurface = null;
  45.133 -        mTextEdit = null;
  45.134 -        mLayout = null;
  45.135 -        mClipboardHandler = null;
  45.136 -        mSDLThread = null;
  45.137 -        mExitCalledFromJava = false;
  45.138 -        mBrokenLibraries = false;
  45.139 -        mIsResumedCalled = false;
  45.140 -        mIsSurfaceReady = false;
  45.141 -        mHasFocus = true;
  45.142 -        mNextNativeState = NativeState.INIT;
  45.143 -        mCurrentNativeState = NativeState.INIT;
  45.144 -    }
  45.145 -
  45.146 -    // Setup
  45.147 -    @Override
  45.148 -    protected void onCreate(Bundle savedInstanceState) {
  45.149 -        Log.v(TAG, "Device: " + android.os.Build.DEVICE);
  45.150 -        Log.v(TAG, "Model: " + android.os.Build.MODEL);
  45.151 -        Log.v(TAG, "onCreate()");
  45.152 -        super.onCreate(savedInstanceState);
  45.153 -
  45.154 -        // Load shared libraries
  45.155 -        String errorMsgBrokenLib = "";
  45.156 -        try {
  45.157 -            loadLibraries();
  45.158 -        } catch(UnsatisfiedLinkError e) {
  45.159 -            System.err.println(e.getMessage());
  45.160 -            mBrokenLibraries = true;
  45.161 -            errorMsgBrokenLib = e.getMessage();
  45.162 -        } catch(Exception e) {
  45.163 -            System.err.println(e.getMessage());
  45.164 -            mBrokenLibraries = true;
  45.165 -            errorMsgBrokenLib = e.getMessage();
  45.166 -        }
  45.167 -
  45.168 -        if (mBrokenLibraries)
  45.169 -        {
  45.170 -            AlertDialog.Builder dlgAlert  = new AlertDialog.Builder(this);
  45.171 -            dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
  45.172 -                  + System.getProperty("line.separator")
  45.173 -                  + System.getProperty("line.separator")
  45.174 -                  + "Error: " + errorMsgBrokenLib);
  45.175 -            dlgAlert.setTitle("SDL Error");
  45.176 -            dlgAlert.setPositiveButton("Exit",
  45.177 -                new DialogInterface.OnClickListener() {
  45.178 -                    @Override
  45.179 -                    public void onClick(DialogInterface dialog,int id) {
  45.180 -                        // if this button is clicked, close current activity
  45.181 -                        SDLActivity.mSingleton.finish();
  45.182 -                    }
  45.183 -                });
  45.184 -           dlgAlert.setCancelable(false);
  45.185 -           dlgAlert.create().show();
  45.186 -
  45.187 -           return;
  45.188 -        }
  45.189 -
  45.190 -        // Set up JNI
  45.191 -        SDL.setupJNI();
  45.192 -
  45.193 -        // Initialize state
  45.194 -        SDL.initialize();
  45.195 -
  45.196 -        // So we can call stuff from static callbacks
  45.197 -        mSingleton = this;
  45.198 -        SDL.setContext(this);
  45.199 -
  45.200 -        if (Build.VERSION.SDK_INT >= 11) {
  45.201 -            mClipboardHandler = new SDLClipboardHandler_API11();
  45.202 -        } else {
  45.203 -            /* Before API 11, no clipboard notification (eg no SDL_CLIPBOARDUPDATE) */
  45.204 -            mClipboardHandler = new SDLClipboardHandler_Old();
  45.205 -        }
  45.206 -
  45.207 -        // Set up the surface
  45.208 -        mSurface = new SDLSurface(getApplication());
  45.209 -
  45.210 -        mLayout = new RelativeLayout(this);
  45.211 -        mLayout.addView(mSurface);
  45.212 -
  45.213 -        setContentView(mLayout);
  45.214 -        
  45.215 -        // Get filename from "Open with" of another application
  45.216 -        Intent intent = getIntent();
  45.217 -        if (intent != null && intent.getData() != null) {
  45.218 -            String filename = intent.getData().getPath();
  45.219 -            if (filename != null) {
  45.220 -                Log.v(TAG, "Got filename: " + filename);
  45.221 -                SDLActivity.onNativeDropFile(filename);
  45.222 -            }
  45.223 -        }
  45.224 -    }
  45.225 -
  45.226 -    // Events
  45.227 -    @Override
  45.228 -    protected void onPause() {
  45.229 -        Log.v(TAG, "onPause()");
  45.230 -        super.onPause();
  45.231 -        mNextNativeState = NativeState.PAUSED;
  45.232 -        mIsResumedCalled = false;
  45.233 -
  45.234 -        if (SDLActivity.mBrokenLibraries) {
  45.235 -           return;
  45.236 -        }
  45.237 -
  45.238 -        SDLActivity.handleNativeState();
  45.239 -    }
  45.240 -
  45.241 -    @Override
  45.242 -    protected void onResume() {
  45.243 -        Log.v(TAG, "onResume()");
  45.244 -        super.onResume();
  45.245 -        mNextNativeState = NativeState.RESUMED;
  45.246 -        mIsResumedCalled = true;
  45.247 -
  45.248 -        if (SDLActivity.mBrokenLibraries) {
  45.249 -           return;
  45.250 -        }
  45.251 -
  45.252 -        SDLActivity.handleNativeState();
  45.253 -    }
  45.254 -
  45.255 -
  45.256 -    @Override
  45.257 -    public void onWindowFocusChanged(boolean hasFocus) {
  45.258 -        super.onWindowFocusChanged(hasFocus);
  45.259 -        Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
  45.260 -
  45.261 -        if (SDLActivity.mBrokenLibraries) {
  45.262 -           return;
  45.263 -        }
  45.264 -
  45.265 -        SDLActivity.mHasFocus = hasFocus;
  45.266 -        if (hasFocus) {
  45.267 -           mNextNativeState = NativeState.RESUMED;
  45.268 -        } else {
  45.269 -           mNextNativeState = NativeState.PAUSED;
  45.270 -        }
  45.271 -        
  45.272 -        SDLActivity.handleNativeState();
  45.273 -    }
  45.274 -
  45.275 -    @Override
  45.276 -    public void onLowMemory() {
  45.277 -        Log.v(TAG, "onLowMemory()");
  45.278 -        super.onLowMemory();
  45.279 -
  45.280 -        if (SDLActivity.mBrokenLibraries) {
  45.281 -           return;
  45.282 -        }
  45.283 -
  45.284 -        SDLActivity.nativeLowMemory();
  45.285 -    }
  45.286 -
  45.287 -    @Override
  45.288 -    protected void onDestroy() {
  45.289 -        Log.v(TAG, "onDestroy()");
  45.290 -
  45.291 -        if (SDLActivity.mBrokenLibraries) {
  45.292 -           super.onDestroy();
  45.293 -           // Reset everything in case the user re opens the app
  45.294 -           SDLActivity.initialize();
  45.295 -           return;
  45.296 -        }
  45.297 -
  45.298 -        mNextNativeState = NativeState.PAUSED;
  45.299 -        SDLActivity.handleNativeState();
  45.300 -
  45.301 -        // Send a quit message to the application
  45.302 -        SDLActivity.mExitCalledFromJava = true;
  45.303 -        SDLActivity.nativeQuit();
  45.304 -
  45.305 -        // Now wait for the SDL thread to quit
  45.306 -        if (SDLActivity.mSDLThread != null) {
  45.307 -            try {
  45.308 -                SDLActivity.mSDLThread.join();
  45.309 -            } catch(Exception e) {
  45.310 -                Log.v(TAG, "Problem stopping thread: " + e);
  45.311 -            }
  45.312 -            SDLActivity.mSDLThread = null;
  45.313 -
  45.314 -            //Log.v(TAG, "Finished waiting for SDL thread");
  45.315 -        }
  45.316 -
  45.317 -        super.onDestroy();
  45.318 -
  45.319 -        // Reset everything in case the user re opens the app
  45.320 -        SDLActivity.initialize();
  45.321 -    }
  45.322 -
  45.323 -    @Override
  45.324 -    public boolean dispatchKeyEvent(KeyEvent event) {
  45.325 -
  45.326 -        if (SDLActivity.mBrokenLibraries) {
  45.327 -           return false;
  45.328 -        }
  45.329 -
  45.330 -        int keyCode = event.getKeyCode();
  45.331 -        // Ignore certain special keys so they're handled by Android
  45.332 -        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
  45.333 -            keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
  45.334 -            keyCode == KeyEvent.KEYCODE_CAMERA ||
  45.335 -            keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */
  45.336 -            keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */
  45.337 -            ) {
  45.338 -            return false;
  45.339 -        }
  45.340 -        return super.dispatchKeyEvent(event);
  45.341 -    }
  45.342 -
  45.343 -    /* Transition to next state */
  45.344 -    public static void handleNativeState() {
  45.345 -
  45.346 -        if (mNextNativeState == mCurrentNativeState) {
  45.347 -            // Already in same state, discard.
  45.348 -            return;
  45.349 -        }
  45.350 -
  45.351 -        // Try a transition to init state
  45.352 -        if (mNextNativeState == NativeState.INIT) {
  45.353 -
  45.354 -            mCurrentNativeState = mNextNativeState;
  45.355 -            return;
  45.356 -        }
  45.357 -
  45.358 -        // Try a transition to paused state
  45.359 -        if (mNextNativeState == NativeState.PAUSED) {
  45.360 -            nativePause();
  45.361 -            mSurface.handlePause();
  45.362 -            mCurrentNativeState = mNextNativeState;
  45.363 -            return;
  45.364 -        }
  45.365 -
  45.366 -        // Try a transition to resumed state
  45.367 -        if (mNextNativeState == NativeState.RESUMED) {
  45.368 -            if (mIsSurfaceReady && mHasFocus && mIsResumedCalled) {
  45.369 -                if (mSDLThread == null) {
  45.370 -                    // This is the entry point to the C app.
  45.371 -                    // Start up the C app thread and enable sensor input for the first time
  45.372 -                    // FIXME: Why aren't we enabling sensor input at start?
  45.373 -
  45.374 -                    final Thread sdlThread = new Thread(new SDLMain(), "SDLThread");
  45.375 -                    mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
  45.376 -                    sdlThread.start();
  45.377 -
  45.378 -                    // Set up a listener thread to catch when the native thread ends
  45.379 -                    mSDLThread = new Thread(new Runnable() {
  45.380 -                        @Override
  45.381 -                        public void run() {
  45.382 -                            try {
  45.383 -                                sdlThread.join();
  45.384 -                            } catch (Exception e) {
  45.385 -                                // Ignore any exception
  45.386 -                            } finally {
  45.387 -                                // Native thread has finished
  45.388 -                                if (!mExitCalledFromJava) {
  45.389 -                                    handleNativeExit();
  45.390 -                                }
  45.391 -                            }
  45.392 -                        }
  45.393 -                    }, "SDLThreadListener");
  45.394 -
  45.395 -                    mSDLThread.start();
  45.396 -                }
  45.397 -
  45.398 -                nativeResume();
  45.399 -                mSurface.handleResume();
  45.400 -                mCurrentNativeState = mNextNativeState;
  45.401 -            }
  45.402 -        }
  45.403 -    }
  45.404 -
  45.405 -    /* The native thread has finished */
  45.406 -    public static void handleNativeExit() {
  45.407 -        SDLActivity.mSDLThread = null;
  45.408 -        mSingleton.finish();
  45.409 -    }
  45.410 -
  45.411 -
  45.412 -    // Messages from the SDLMain thread
  45.413 -    static final int COMMAND_CHANGE_TITLE = 1;
  45.414 -    static final int COMMAND_UNUSED = 2;
  45.415 -    static final int COMMAND_TEXTEDIT_HIDE = 3;
  45.416 -    static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
  45.417 -
  45.418 -    protected static final int COMMAND_USER = 0x8000;
  45.419 -
  45.420 -    /**
  45.421 -     * This method is called by SDL if SDL did not handle a message itself.
  45.422 -     * This happens if a received message contains an unsupported command.
  45.423 -     * Method can be overwritten to handle Messages in a different class.
  45.424 -     * @param command the command of the message.
  45.425 -     * @param param the parameter of the message. May be null.
  45.426 -     * @return if the message was handled in overridden method.
  45.427 -     */
  45.428 -    protected boolean onUnhandledMessage(int command, Object param) {
  45.429 -        return false;
  45.430 -    }
  45.431 -
  45.432 -    /**
  45.433 -     * A Handler class for Messages from native SDL applications.
  45.434 -     * It uses current Activities as target (e.g. for the title).
  45.435 -     * static to prevent implicit references to enclosing object.
  45.436 -     */
  45.437 -    protected static class SDLCommandHandler extends Handler {
  45.438 -        @Override
  45.439 -        public void handleMessage(Message msg) {
  45.440 -            Context context = SDL.getContext();
  45.441 -            if (context == null) {
  45.442 -                Log.e(TAG, "error handling message, getContext() returned null");
  45.443 -                return;
  45.444 -            }
  45.445 -            switch (msg.arg1) {
  45.446 -            case COMMAND_CHANGE_TITLE:
  45.447 -                if (context instanceof Activity) {
  45.448 -                    ((Activity) context).setTitle((String)msg.obj);
  45.449 -                } else {
  45.450 -                    Log.e(TAG, "error handling message, getContext() returned no Activity");
  45.451 -                }
  45.452 -                break;
  45.453 -            case COMMAND_TEXTEDIT_HIDE:
  45.454 -                if (mTextEdit != null) {
  45.455 -                    // Note: On some devices setting view to GONE creates a flicker in landscape.
  45.456 -                    // Setting the View's sizes to 0 is similar to GONE but without the flicker.
  45.457 -                    // The sizes will be set to useful values when the keyboard is shown again.
  45.458 -                    mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
  45.459 -
  45.460 -                    InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
  45.461 -                    imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
  45.462 -                    
  45.463 -                    mScreenKeyboardShown = false;
  45.464 -                }
  45.465 -                break;
  45.466 -            case COMMAND_SET_KEEP_SCREEN_ON:
  45.467 -            {
  45.468 -                if (context instanceof Activity) {
  45.469 -                    Window window = ((Activity) context).getWindow();
  45.470 -                    if (window != null) {
  45.471 -                        if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
  45.472 -                            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  45.473 -                        } else {
  45.474 -                            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  45.475 -                        }
  45.476 -                    }
  45.477 -                }
  45.478 -                break;
  45.479 -            }
  45.480 -            default:
  45.481 -                if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) {
  45.482 -                    Log.e(TAG, "error handling message, command is " + msg.arg1);
  45.483 -                }
  45.484 -            }
  45.485 -        }
  45.486 -    }
  45.487 -
  45.488 -    // Handler for the messages
  45.489 -    Handler commandHandler = new SDLCommandHandler();
  45.490 -
  45.491 -    // Send a message from the SDLMain thread
  45.492 -    boolean sendCommand(int command, Object data) {
  45.493 -        Message msg = commandHandler.obtainMessage();
  45.494 -        msg.arg1 = command;
  45.495 -        msg.obj = data;
  45.496 -        return commandHandler.sendMessage(msg);
  45.497 -    }
  45.498 -
  45.499 -    // C functions we call
  45.500 -    public static native int nativeSetupJNI();
  45.501 -    public static native int nativeRunMain(String library, String function, Object arguments);
  45.502 -    public static native void nativeLowMemory();
  45.503 -    public static native void nativeQuit();
  45.504 -    public static native void nativePause();
  45.505 -    public static native void nativeResume();
  45.506 -    public static native void onNativeDropFile(String filename);
  45.507 -    public static native void onNativeResize(int x, int y, int format, float rate);
  45.508 -    public static native void onNativeKeyDown(int keycode);
  45.509 -    public static native void onNativeKeyUp(int keycode);
  45.510 -    public static native void onNativeKeyboardFocusLost();
  45.511 -    public static native void onNativeMouse(int button, int action, float x, float y);
  45.512 -    public static native void onNativeTouch(int touchDevId, int pointerFingerId,
  45.513 -                                            int action, float x,
  45.514 -                                            float y, float p);
  45.515 -    public static native void onNativeAccel(float x, float y, float z);
  45.516 -    public static native void onNativeClipboardChanged();
  45.517 -    public static native void onNativeSurfaceChanged();
  45.518 -    public static native void onNativeSurfaceDestroyed();
  45.519 -    public static native String nativeGetHint(String name);
  45.520 -
  45.521 -    /**
  45.522 -     * This method is called by SDL using JNI.
  45.523 -     */
  45.524 -    public static boolean setActivityTitle(String title) {
  45.525 -        // Called from SDLMain() thread and can't directly affect the view
  45.526 -        return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
  45.527 -    }
  45.528 -
  45.529 -    /**
  45.530 -     * This method is called by SDL using JNI.
  45.531 -     * This is a static method for JNI convenience, it calls a non-static method
  45.532 -     * so that is can be overridden  
  45.533 -     */
  45.534 -    public static void setOrientation(int w, int h, boolean resizable, String hint)
  45.535 -    {
  45.536 -        if (mSingleton != null) {
  45.537 -            mSingleton.setOrientationBis(w, h, resizable, hint);
  45.538 -        }
  45.539 -    }
  45.540 -   
  45.541 -    /**
  45.542 -     * This can be overridden
  45.543 -     */
  45.544 -    public void setOrientationBis(int w, int h, boolean resizable, String hint) 
  45.545 -    {
  45.546 -      int orientation = -1;
  45.547 -
  45.548 -      if (!Objects.equals(hint, "")) {
  45.549 -         if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) {
  45.550 -            orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
  45.551 -         } else if (hint.contains("LandscapeRight")) {
  45.552 -            orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
  45.553 -         } else if (hint.contains("LandscapeLeft")) {
  45.554 -            orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
  45.555 -         } else if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) {
  45.556 -            orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
  45.557 -         } else if (hint.contains("Portrait")) {
  45.558 -            orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
  45.559 -         } else if (hint.contains("PortraitUpsideDown")) {
  45.560 -            orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
  45.561 -         }
  45.562 -      }
  45.563 -
  45.564 -      /* no valid hint */
  45.565 -      if (orientation == -1) {
  45.566 -         if (resizable) {
  45.567 -            /* no fixed orientation */
  45.568 -         } else {
  45.569 -            if (w > h) {
  45.570 -               orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
  45.571 -            } else {
  45.572 -               orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
  45.573 -            }
  45.574 -         }
  45.575 -      }
  45.576 -
  45.577 -      Log.v("SDL", "setOrientation() orientation=" + orientation + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint);
  45.578 -      if (orientation != -1) {
  45.579 -         mSingleton.setRequestedOrientation(orientation);
  45.580 -      }
  45.581 -    }
  45.582 -
  45.583 -
  45.584 -    /**
  45.585 -     * This method is called by SDL using JNI.
  45.586 -     */
  45.587 -    public static boolean isScreenKeyboardShown() 
  45.588 -    {
  45.589 -        if (mTextEdit == null) {
  45.590 -            return false;
  45.591 -        }
  45.592 -
  45.593 -        if (!mScreenKeyboardShown) {
  45.594 -            return false;
  45.595 -        }
  45.596 -
  45.597 -        InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
  45.598 -        return imm.isAcceptingText();
  45.599 -
  45.600 -    }
  45.601 -
  45.602 -    /**
  45.603 -     * This method is called by SDL using JNI.
  45.604 -     */
  45.605 -    public static boolean sendMessage(int command, int param) {
  45.606 -        if (mSingleton == null) {
  45.607 -            return false;
  45.608 -        }
  45.609 -        return mSingleton.sendCommand(command, Integer.valueOf(param));
  45.610 -    }
  45.611 -
  45.612 -    /**
  45.613 -     * This method is called by SDL using JNI.
  45.614 -     */
  45.615 -    public static Context getContext() {
  45.616 -        return SDL.getContext();
  45.617 -    }
  45.618 -
  45.619 -    static class ShowTextInputTask implements Runnable {
  45.620 -        /*
  45.621 -         * This is used to regulate the pan&scan method to have some offset from
  45.622 -         * the bottom edge of the input region and the top edge of an input
  45.623 -         * method (soft keyboard)
  45.624 -         */
  45.625 -        static final int HEIGHT_PADDING = 15;
  45.626 -
  45.627 -        public int x, y, w, h;
  45.628 -
  45.629 -        public ShowTextInputTask(int x, int y, int w, int h) {
  45.630 -            this.x = x;
  45.631 -            this.y = y;
  45.632 -            this.w = w;
  45.633 -            this.h = h;
  45.634 -        }
  45.635 -
  45.636 -        @Override
  45.637 -        public void run() {
  45.638 -            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
  45.639 -            params.leftMargin = x;
  45.640 -            params.topMargin = y;
  45.641 -
  45.642 -            if (mTextEdit == null) {
  45.643 -                mTextEdit = new DummyEdit(SDL.getContext());
  45.644 -
  45.645 -                mLayout.addView(mTextEdit, params);
  45.646 -            } else {
  45.647 -                mTextEdit.setLayoutParams(params);
  45.648 -            }
  45.649 -
  45.650 -            mTextEdit.setVisibility(View.VISIBLE);
  45.651 -            mTextEdit.requestFocus();
  45.652 -
  45.653 -            InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
  45.654 -            imm.showSoftInput(mTextEdit, 0);
  45.655 -
  45.656 -            mScreenKeyboardShown = true;
  45.657 -        }
  45.658 -    }
  45.659 -
  45.660 -    /**
  45.661 -     * This method is called by SDL using JNI.
  45.662 -     */
  45.663 -    public static boolean showTextInput(int x, int y, int w, int h) {
  45.664 -        // Transfer the task to the main thread as a Runnable
  45.665 -        return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
  45.666 -    }
  45.667 -
  45.668 -    public static boolean isTextInputEvent(KeyEvent event) {
  45.669 -      
  45.670 -        // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT
  45.671 -        if (android.os.Build.VERSION.SDK_INT >= 11) {
  45.672 -            if (event.isCtrlPressed()) {
  45.673 -                return false;
  45.674 -            }  
  45.675 -        }
  45.676 -
  45.677 -        return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
  45.678 -    }
  45.679 -
  45.680 -    /**
  45.681 -     * This method is called by SDL using JNI.
  45.682 -     */
  45.683 -    public static Surface getNativeSurface() {
  45.684 -        if (SDLActivity.mSurface == null) {
  45.685 -            return null;
  45.686 -        }
  45.687 -        return SDLActivity.mSurface.getNativeSurface();
  45.688 -    }
  45.689 -
  45.690 -    // Input
  45.691 -
  45.692 -    /**
  45.693 -     * This method is called by SDL using JNI.
  45.694 -     * @return an array which may be empty but is never null.
  45.695 -     */
  45.696 -    public static int[] inputGetInputDeviceIds(int sources) {
  45.697 -        int[] ids = InputDevice.getDeviceIds();
  45.698 -        int[] filtered = new int[ids.length];
  45.699 -        int used = 0;
  45.700 -        for (int i = 0; i < ids.length; ++i) {
  45.701 -            InputDevice device = InputDevice.getDevice(ids[i]);
  45.702 -            if ((device != null) && ((device.getSources() & sources) != 0)) {
  45.703 -                filtered[used++] = device.getId();
  45.704 -            }
  45.705 -        }
  45.706 -        return Arrays.copyOf(filtered, used);
  45.707 -    }
  45.708 -
  45.709 -    // APK expansion files support
  45.710 -
  45.711 -    /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
  45.712 -    private static Object expansionFile;
  45.713 -
  45.714 -    /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
  45.715 -    private static Method expansionFileMethod;
  45.716 -
  45.717 -    /**
  45.718 -     * This method is called by SDL using JNI.
  45.719 -     * @return an InputStream on success or null if no expansion file was used.
  45.720 -     * @throws IOException on errors. Message is set for the SDL error message.
  45.721 -     */
  45.722 -    public static InputStream openAPKExpansionInputStream(String fileName) throws IOException {
  45.723 -        // Get a ZipResourceFile representing a merger of both the main and patch files
  45.724 -        if (expansionFile == null) {
  45.725 -            String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION");
  45.726 -            if (mainHint == null) {
  45.727 -                return null; // no expansion use if no main version was set
  45.728 -            }
  45.729 -            String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION");
  45.730 -            if (patchHint == null) {
  45.731 -                return null; // no expansion use if no patch version was set
  45.732 -            }
  45.733 -
  45.734 -            Integer mainVersion;
  45.735 -            Integer patchVersion;
  45.736 -            try {
  45.737 -                mainVersion = Integer.valueOf(mainHint);
  45.738 -                patchVersion = Integer.valueOf(patchHint);
  45.739 -            } catch (NumberFormatException ex) {
  45.740 -                ex.printStackTrace();
  45.741 -                throw new IOException("No valid file versions set for APK expansion files", ex);
  45.742 -            }
  45.743 -
  45.744 -            try {
  45.745 -                // To avoid direct dependency on Google APK expansion library that is
  45.746 -                // not a part of Android SDK we access it using reflection
  45.747 -                expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
  45.748 -                    .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
  45.749 -                    .invoke(null, SDL.getContext(), mainVersion, patchVersion);
  45.750 -
  45.751 -                expansionFileMethod = expansionFile.getClass()
  45.752 -                    .getMethod("getInputStream", String.class);
  45.753 -            } catch (Exception ex) {
  45.754 -                ex.printStackTrace();
  45.755 -                expansionFile = null;
  45.756 -                expansionFileMethod = null;
  45.757 -                throw new IOException("Could not access APK expansion support library", ex);
  45.758 -            }
  45.759 -        }
  45.760 -
  45.761 -        // Get an input stream for a known file inside the expansion file ZIPs
  45.762 -        InputStream fileStream;
  45.763 -        try {
  45.764 -            fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName);
  45.765 -        } catch (Exception ex) {
  45.766 -            // calling "getInputStream" failed
  45.767 -            ex.printStackTrace();
  45.768 -            throw new IOException("Could not open stream from APK expansion file", ex);
  45.769 -        }
  45.770 -
  45.771 -        if (fileStream == null) {
  45.772 -            // calling "getInputStream" was successful but null was returned
  45.773 -            throw new IOException("Could not find path in APK expansion file");
  45.774 -        }
  45.775 -
  45.776 -        return fileStream;
  45.777 -    }
  45.778 -
  45.779 -    // Messagebox
  45.780 -
  45.781 -    /** Result of current messagebox. Also used for blocking the calling thread. */
  45.782 -    protected final int[] messageboxSelection = new int[1];
  45.783 -
  45.784 -    /** Id of current dialog. */
  45.785 -    protected int dialogs = 0;
  45.786 -
  45.787 -    /**
  45.788 -     * This method is called by SDL using JNI.
  45.789 -     * Shows the messagebox from UI thread and block calling thread.
  45.790 -     * buttonFlags, buttonIds and buttonTexts must have same length.
  45.791 -     * @param buttonFlags array containing flags for every button.
  45.792 -     * @param buttonIds array containing id for every button.
  45.793 -     * @param buttonTexts array containing text for every button.
  45.794 -     * @param colors null for default or array of length 5 containing colors.
  45.795 -     * @return button id or -1.
  45.796 -     */
  45.797 -    public int messageboxShowMessageBox(
  45.798 -            final int flags,
  45.799 -            final String title,
  45.800 -            final String message,
  45.801 -            final int[] buttonFlags,
  45.802 -            final int[] buttonIds,
  45.803 -            final String[] buttonTexts,
  45.804 -            final int[] colors) {
  45.805 -
  45.806 -        messageboxSelection[0] = -1;
  45.807 -
  45.808 -        // sanity checks
  45.809 -
  45.810 -        if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) {
  45.811 -            return -1; // implementation broken
  45.812 -        }
  45.813 -
  45.814 -        // collect arguments for Dialog
  45.815 -
  45.816 -        final Bundle args = new Bundle();
  45.817 -        args.putInt("flags", flags);
  45.818 -        args.putString("title", title);
  45.819 -        args.putString("message", message);
  45.820 -        args.putIntArray("buttonFlags", buttonFlags);
  45.821 -        args.putIntArray("buttonIds", buttonIds);
  45.822 -        args.putStringArray("buttonTexts", buttonTexts);
  45.823 -        args.putIntArray("colors", colors);
  45.824 -
  45.825 -        // trigger Dialog creation on UI thread
  45.826 -
  45.827 -        runOnUiThread(new Runnable() {
  45.828 -            @Override
  45.829 -            public void run() {
  45.830 -                showDialog(dialogs++, args);
  45.831 -            }
  45.832 -        });
  45.833 -
  45.834 -        // block the calling thread
  45.835 -
  45.836 -        synchronized (messageboxSelection) {
  45.837 -            try {
  45.838 -                messageboxSelection.wait();
  45.839 -            } catch (InterruptedException ex) {
  45.840 -                ex.printStackTrace();
  45.841 -                return -1;
  45.842 -            }
  45.843 -        }
  45.844 -
  45.845 -        // return selected value
  45.846 -
  45.847 -        return messageboxSelection[0];
  45.848 -    }
  45.849 -
  45.850 -    @Override
  45.851 -    protected Dialog onCreateDialog(int ignore, Bundle args) {
  45.852 -
  45.853 -        // TODO set values from "flags" to messagebox dialog
  45.854 -
  45.855 -        // get colors
  45.856 -
  45.857 -        int[] colors = args.getIntArray("colors");
  45.858 -        int backgroundColor;
  45.859 -        int textColor;
  45.860 -        int buttonBorderColor;
  45.861 -        int buttonBackgroundColor;
  45.862 -        int buttonSelectedColor;
  45.863 -        if (colors != null) {
  45.864 -            int i = -1;
  45.865 -            backgroundColor = colors[++i];
  45.866 -            textColor = colors[++i];
  45.867 -            buttonBorderColor = colors[++i];
  45.868 -            buttonBackgroundColor = colors[++i];
  45.869 -            buttonSelectedColor = colors[++i];
  45.870 -        } else {
  45.871 -            backgroundColor = Color.TRANSPARENT;
  45.872 -            textColor = Color.TRANSPARENT;
  45.873 -            buttonBorderColor = Color.TRANSPARENT;
  45.874 -            buttonBackgroundColor = Color.TRANSPARENT;
  45.875 -            buttonSelectedColor = Color.TRANSPARENT;
  45.876 -        }
  45.877 -
  45.878 -        // create dialog with title and a listener to wake up calling thread
  45.879 -
  45.880 -        final Dialog dialog = new Dialog(this);
  45.881 -        dialog.setTitle(args.getString("title"));
  45.882 -        dialog.setCancelable(false);
  45.883 -        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
  45.884 -            @Override
  45.885 -            public void onDismiss(DialogInterface unused) {
  45.886 -                synchronized (messageboxSelection) {
  45.887 -                    messageboxSelection.notify();
  45.888 -                }
  45.889 -            }
  45.890 -        });
  45.891 -
  45.892 -        // create text
  45.893 -
  45.894 -        TextView message = new TextView(this);
  45.895 -        message.setGravity(Gravity.CENTER);
  45.896 -        message.setText(args.getString("message"));
  45.897 -        if (textColor != Color.TRANSPARENT) {
  45.898 -            message.setTextColor(textColor);
  45.899 -        }
  45.900 -
  45.901 -        // create buttons
  45.902 -
  45.903 -        int[] buttonFlags = args.getIntArray("buttonFlags");
  45.904 -        int[] buttonIds = args.getIntArray("buttonIds");
  45.905 -        String[] buttonTexts = args.getStringArray("buttonTexts");
  45.906 -
  45.907 -        final SparseArray<Button> mapping = new SparseArray<Button>();
  45.908 -
  45.909 -        LinearLayout buttons = new LinearLayout(this);
  45.910 -        buttons.setOrientation(LinearLayout.HORIZONTAL);
  45.911 -        buttons.setGravity(Gravity.CENTER);
  45.912 -        for (int i = 0; i < buttonTexts.length; ++i) {
  45.913 -            Button button = new Button(this);
  45.914 -            final int id = buttonIds[i];
  45.915 -            button.setOnClickListener(new View.OnClickListener() {
  45.916 -                @Override
  45.917 -                public void onClick(View v) {
  45.918 -                    messageboxSelection[0] = id;
  45.919 -                    dialog.dismiss();
  45.920 -                }
  45.921 -            });
  45.922 -            if (buttonFlags[i] != 0) {
  45.923 -                // see SDL_messagebox.h
  45.924 -                if ((buttonFlags[i] & 0x00000001) != 0) {
  45.925 -                    mapping.put(KeyEvent.KEYCODE_ENTER, button);
  45.926 -                }
  45.927 -                if ((buttonFlags[i] & 0x00000002) != 0) {
  45.928 -                    mapping.put(KeyEvent.KEYCODE_ESCAPE, button); /* API 11 */
  45.929 -                }
  45.930 -            }
  45.931 -            button.setText(buttonTexts[i]);
  45.932 -            if (textColor != Color.TRANSPARENT) {
  45.933 -                button.setTextColor(textColor);
  45.934 -            }
  45.935 -            if (buttonBorderColor != Color.TRANSPARENT) {
  45.936 -                // TODO set color for border of messagebox button
  45.937 -            }
  45.938 -            if (buttonBackgroundColor != Color.TRANSPARENT) {
  45.939 -                Drawable drawable = button.getBackground();
  45.940 -                if (drawable == null) {
  45.941 -                    // setting the color this way removes the style
  45.942 -                    button.setBackgroundColor(buttonBackgroundColor);
  45.943 -                } else {
  45.944 -                    // setting the color this way keeps the style (gradient, padding, etc.)
  45.945 -                    drawable.setColorFilter(buttonBackgroundColor, PorterDuff.Mode.MULTIPLY);
  45.946 -                }
  45.947 -            }
  45.948 -            if (buttonSelectedColor != Color.TRANSPARENT) {
  45.949 -                // TODO set color for selected messagebox button
  45.950 -            }
  45.951 -            buttons.addView(button);
  45.952 -        }
  45.953 -
  45.954 -        // create content
  45.955 -
  45.956 -        LinearLayout content = new LinearLayout(this);
  45.957 -        content.setOrientation(LinearLayout.VERTICAL);
  45.958 -        content.addView(message);
  45.959 -        content.addView(buttons);
  45.960 -        if (backgroundColor != Color.TRANSPARENT) {
  45.961 -            content.setBackgroundColor(backgroundColor);
  45.962 -        }
  45.963 -
  45.964 -        // add content to dialog and return
  45.965 -
  45.966 -        dialog.setContentView(content);
  45.967 -        dialog.setOnKeyListener(new Dialog.OnKeyListener() {
  45.968 -            @Override
  45.969 -            public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) {
  45.970 -                Button button = mapping.get(keyCode);
  45.971 -                if (button != null) {
  45.972 -                    if (event.getAction() == KeyEvent.ACTION_UP) {
  45.973 -                        button.performClick();
  45.974 -                    }
  45.975 -                    return true; // also for ignored actions
  45.976 -                }
  45.977 -                return false;
  45.978 -            }
  45.979 -        });
  45.980 -
  45.981 -        return dialog;
  45.982 -    }
  45.983 -
  45.984 -    /**
  45.985 -     * This method is called by SDL using JNI.
  45.986 -     */
  45.987 -    public static boolean clipboardHasText() {
  45.988 -        return mClipboardHandler.clipboardHasText();
  45.989 -    }
  45.990 -    
  45.991 -    /**
  45.992 -     * This method is called by SDL using JNI.
  45.993 -     */
  45.994 -    public static String clipboardGetText() {
  45.995 -        return mClipboardHandler.clipboardGetText();
  45.996 -    }
  45.997 -
  45.998 -    /**
  45.999 -     * This method is called by SDL using JNI.
 45.1000 -     */
 45.1001 -    public static void clipboardSetText(String string) {
 45.1002 -        mClipboardHandler.clipboardSetText(string);
 45.1003 -    }
 45.1004 -
 45.1005 -}
 45.1006 -
 45.1007 -/**
 45.1008 -    Simple runnable to start the SDL application
 45.1009 -*/
 45.1010 -class SDLMain implements Runnable {
 45.1011 -    @Override
 45.1012 -    public void run() {
 45.1013 -        // Runs SDL_main()
 45.1014 -        String library = SDLActivity.mSingleton.getMainSharedObject();
 45.1015 -        String function = SDLActivity.mSingleton.getMainFunction();
 45.1016 -        String[] arguments = SDLActivity.mSingleton.getArguments();
 45.1017 -
 45.1018 -        Log.v("SDL", "Running main function " + function + " from library " + library);
 45.1019 -        SDLActivity.nativeRunMain(library, function, arguments);
 45.1020 -
 45.1021 -        Log.v("SDL", "Finished main function");
 45.1022 -    }
 45.1023 -}
 45.1024 -
 45.1025 -
 45.1026 -/**
 45.1027 -    SDLSurface. This is what we draw on, so we need to know when it's created
 45.1028 -    in order to do anything useful.
 45.1029 -
 45.1030 -    Because of this, that's where we set up the SDL thread
 45.1031 -*/
 45.1032 -class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
 45.1033 -    View.OnKeyListener, View.OnTouchListener, SensorEventListener  {
 45.1034 -
 45.1035 -    // Sensors
 45.1036 -    protected static SensorManager mSensorManager;
 45.1037 -    protected static Display mDisplay;
 45.1038 -
 45.1039 -    // Keep track of the surface size to normalize touch events
 45.1040 -    protected static float mWidth, mHeight;
 45.1041 -
 45.1042 -    // Startup
 45.1043 -    public SDLSurface(Context context) {
 45.1044 -        super(context);
 45.1045 -        getHolder().addCallback(this);
 45.1046 -
 45.1047 -        setFocusable(true);
 45.1048 -        setFocusableInTouchMode(true);
 45.1049 -        requestFocus();
 45.1050 -        setOnKeyListener(this);
 45.1051 -        setOnTouchListener(this);
 45.1052 -
 45.1053 -        mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
 45.1054 -        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
 45.1055 -
 45.1056 -        if (Build.VERSION.SDK_INT >= 12) {
 45.1057 -            setOnGenericMotionListener(new SDLGenericMotionListener_API12());
 45.1058 -        }
 45.1059 -
 45.1060 -        // Some arbitrary defaults to avoid a potential division by zero
 45.1061 -        mWidth = 1.0f;
 45.1062 -        mHeight = 1.0f;
 45.1063 -    }
 45.1064 -
 45.1065 -    public void handlePause() {
 45.1066 -        enableSensor(Sensor.TYPE_ACCELEROMETER, false);
 45.1067 -    }
 45.1068 -
 45.1069 -    public void handleResume() {
 45.1070 -        setFocusable(true);
 45.1071 -        setFocusableInTouchMode(true);
 45.1072 -        requestFocus();
 45.1073 -        setOnKeyListener(this);
 45.1074 -        setOnTouchListener(this);
 45.1075 -        enableSensor(Sensor.TYPE_ACCELEROMETER, true);
 45.1076 -    }
 45.1077 -
 45.1078 -    public Surface getNativeSurface() {
 45.1079 -        return getHolder().getSurface();
 45.1080 -    }
 45.1081 -
 45.1082 -    // Called when we have a valid drawing surface
 45.1083 -    @Override
 45.1084 -    public void surfaceCreated(SurfaceHolder holder) {
 45.1085 -        Log.v("SDL", "surfaceCreated()");
 45.1086 -        holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
 45.1087 -    }
 45.1088 -
 45.1089 -    // Called when we lose the surface
 45.1090 -    @Override
 45.1091 -    public void surfaceDestroyed(SurfaceHolder holder) {
 45.1092 -        Log.v("SDL", "surfaceDestroyed()");
 45.1093 -
 45.1094 -        // Transition to pause, if needed
 45.1095 -        SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
 45.1096 -        SDLActivity.handleNativeState();
 45.1097 -
 45.1098 -        SDLActivity.mIsSurfaceReady = false;
 45.1099 -        SDLActivity.onNativeSurfaceDestroyed();
 45.1100 -    }
 45.1101 -
 45.1102 -    // Called when the surface is resized
 45.1103 -    @Override
 45.1104 -    public void surfaceChanged(SurfaceHolder holder,
 45.1105 -                               int format, int width, int height) {
 45.1106 -        Log.v("SDL", "surfaceChanged()");
 45.1107 -
 45.1108 -        int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
 45.1109 -        switch (format) {
 45.1110 -        case PixelFormat.A_8:
 45.1111 -            Log.v("SDL", "pixel format A_8");
 45.1112 -            break;
 45.1113 -        case PixelFormat.LA_88:
 45.1114 -            Log.v("SDL", "pixel format LA_88");
 45.1115 -            break;
 45.1116 -        case PixelFormat.L_8:
 45.1117 -            Log.v("SDL", "pixel format L_8");
 45.1118 -            break;
 45.1119 -        case PixelFormat.RGBA_4444:
 45.1120 -            Log.v("SDL", "pixel format RGBA_4444");
 45.1121 -            sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
 45.1122 -            break;
 45.1123 -        case PixelFormat.RGBA_5551:
 45.1124 -            Log.v("SDL", "pixel format RGBA_5551");
 45.1125 -            sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
 45.1126 -            break;
 45.1127 -        case PixelFormat.RGBA_8888:
 45.1128 -            Log.v("SDL", "pixel format RGBA_8888");
 45.1129 -            sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
 45.1130 -            break;
 45.1131 -        case PixelFormat.RGBX_8888:
 45.1132 -            Log.v("SDL", "pixel format RGBX_8888");
 45.1133 -            sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
 45.1134 -            break;
 45.1135 -        case PixelFormat.RGB_332:
 45.1136 -            Log.v("SDL", "pixel format RGB_332");
 45.1137 -            sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
 45.1138 -            break;
 45.1139 -        case PixelFormat.RGB_565:
 45.1140 -            Log.v("SDL", "pixel format RGB_565");
 45.1141 -            sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
 45.1142 -            break;
 45.1143 -        case PixelFormat.RGB_888:
 45.1144 -            Log.v("SDL", "pixel format RGB_888");
 45.1145 -            // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
 45.1146 -            sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
 45.1147 -            break;
 45.1148 -        default:
 45.1149 -            Log.v("SDL", "pixel format unknown " + format);
 45.1150 -            break;
 45.1151 -        }
 45.1152 -
 45.1153 -        mWidth = width;
 45.1154 -        mHeight = height;
 45.1155 -        SDLActivity.onNativeResize(width, height, sdlFormat, mDisplay.getRefreshRate());
 45.1156 -        Log.v("SDL", "Window size: " + width + "x" + height);
 45.1157 -
 45.1158 - 
 45.1159 -        boolean skip = false;
 45.1160 -        int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
 45.1161 -
 45.1162 -        if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
 45.1163 -        {
 45.1164 -            // Accept any
 45.1165 -        }
 45.1166 -        else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT)
 45.1167 -        {
 45.1168 -            if (mWidth > mHeight) {
 45.1169 -               skip = true;
 45.1170 -            }
 45.1171 -        } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
 45.1172 -            if (mWidth < mHeight) {
 45.1173 -               skip = true;
 45.1174 -            }
 45.1175 -        }
 45.1176 -
 45.1177 -        // Special Patch for Square Resolution: Black Berry Passport
 45.1178 -        if (skip) {
 45.1179 -           double min = Math.min(mWidth, mHeight);
 45.1180 -           double max = Math.max(mWidth, mHeight);
 45.1181 -           
 45.1182 -           if (max / min < 1.20) {
 45.1183 -              Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
 45.1184 -              skip = false;
 45.1185 -           }
 45.1186 -        }
 45.1187 -
 45.1188 -        if (skip) {
 45.1189 -           Log.v("SDL", "Skip .. Surface is not ready.");
 45.1190 -           SDLActivity.mIsSurfaceReady = false;
 45.1191 -           return;
 45.1192 -        }
 45.1193 -        
 45.1194 -        /* Surface is ready */
 45.1195 -        SDLActivity.mIsSurfaceReady = true;
 45.1196 -
 45.1197 -        /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
 45.1198 -        SDLActivity.onNativeSurfaceChanged();
 45.1199 -
 45.1200 -        SDLActivity.handleNativeState();
 45.1201 -    }
 45.1202 -
 45.1203 -    // Key events
 45.1204 -    @Override
 45.1205 -    public boolean onKey(View  v, int keyCode, KeyEvent event) {
 45.1206 -        // Dispatch the different events depending on where they come from
 45.1207 -        // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
 45.1208 -        // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
 45.1209 -        //
 45.1210 -        // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
 45.1211 -        // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
 45.1212 -        // So, retrieve the device itself and check all of its sources
 45.1213 -        if (SDLControllerManager.isDeviceSDLJoystick(event.getDeviceId())) {
 45.1214 -            // Note that we process events with specific key codes here
 45.1215 -            if (event.getAction() == KeyEvent.ACTION_DOWN) {
 45.1216 -                if (SDLControllerManager.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
 45.1217 -                    return true;
 45.1218 -                }
 45.1219 -            } else if (event.getAction() == KeyEvent.ACTION_UP) {
 45.1220 -                if (SDLControllerManager.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
 45.1221 -                    return true;
 45.1222 -                }
 45.1223 -            }
 45.1224 -        }
 45.1225 -
 45.1226 -        if ((event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
 45.1227 -            if (event.getAction() == KeyEvent.ACTION_DOWN) {
 45.1228 -                //Log.v("SDL", "key down: " + keyCode);
 45.1229 -                SDLActivity.onNativeKeyDown(keyCode);
 45.1230 -                return true;
 45.1231 -            }
 45.1232 -            else if (event.getAction() == KeyEvent.ACTION_UP) {
 45.1233 -                //Log.v("SDL", "key up: " + keyCode);
 45.1234 -                SDLActivity.onNativeKeyUp(keyCode);
 45.1235 -                return true;
 45.1236 -            }
 45.1237 -        }
 45.1238 -
 45.1239 -        if ((event.getSource() & InputDevice.SOURCE_MOUSE) != 0) {
 45.1240 -            // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
 45.1241 -            // they are ignored here because sending them as mouse input to SDL is messy
 45.1242 -            if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
 45.1243 -                switch (event.getAction()) {
 45.1244 -                case KeyEvent.ACTION_DOWN:
 45.1245 -                case KeyEvent.ACTION_UP:
 45.1246 -                    // mark the event as handled or it will be handled by system
 45.1247 -                    // handling KEYCODE_BACK by system will call onBackPressed()
 45.1248 -                    return true;
 45.1249 -                }
 45.1250 -            }
 45.1251 -        }
 45.1252 -
 45.1253 -        return false;
 45.1254 -    }
 45.1255 -
 45.1256 -    // Touch events
 45.1257 -    @Override
 45.1258 -    public boolean onTouch(View v, MotionEvent event) {
 45.1259 -        /* Ref: http://developer.android.com/training/gestures/multi.html */
 45.1260 -        final int touchDevId = event.getDeviceId();
 45.1261 -        final int pointerCount = event.getPointerCount();
 45.1262 -        int action = event.getActionMasked();
 45.1263 -        int pointerFingerId;
 45.1264 -        int mouseButton;
 45.1265 -        int i = -1;
 45.1266 -        float x,y,p;
 45.1267 -
 45.1268 -        // !!! FIXME: dump this SDK check after 2.0.4 ships and require API14.
 45.1269 -        if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) {
 45.1270 -            if (Build.VERSION.SDK_INT < 14) {
 45.1271 -                mouseButton = 1; // all mouse buttons are the left button
 45.1272 -            } else {
 45.1273 -                try {
 45.1274 -                    mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
 45.1275 -                } catch(Exception e) {
 45.1276 -                    mouseButton = 1;    // oh well.
 45.1277 -                }
 45.1278 -            }
 45.1279 -            SDLActivity.onNativeMouse(mouseButton, action, event.getX(0), event.getY(0));
 45.1280 -        } else {
 45.1281 -            switch(action) {
 45.1282 -                case MotionEvent.ACTION_MOVE:
 45.1283 -                    for (i = 0; i < pointerCount; i++) {
 45.1284 -                        pointerFingerId = event.getPointerId(i);
 45.1285 -                        x = event.getX(i) / mWidth;
 45.1286 -                        y = event.getY(i) / mHeight;
 45.1287 -                        p = event.getPressure(i);
 45.1288 -                        if (p > 1.0f) {
 45.1289 -                            // may be larger than 1.0f on some devices
 45.1290 -                            // see the documentation of getPressure(i)
 45.1291 -                            p = 1.0f;
 45.1292 -                        }
 45.1293 -                        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
 45.1294 -                    }
 45.1295 -                    break;
 45.1296 -
 45.1297 -                case MotionEvent.ACTION_UP:
 45.1298 -                case MotionEvent.ACTION_DOWN:
 45.1299 -                    // Primary pointer up/down, the index is always zero
 45.1300 -                    i = 0;
 45.1301 -                case MotionEvent.ACTION_POINTER_UP:
 45.1302 -                case MotionEvent.ACTION_POINTER_DOWN:
 45.1303 -                    // Non primary pointer up/down
 45.1304 -                    if (i == -1) {
 45.1305 -                        i = event.getActionIndex();
 45.1306 -                    }
 45.1307 -
 45.1308 -                    pointerFingerId = event.getPointerId(i);
 45.1309 -                    x = event.getX(i) / mWidth;
 45.1310 -                    y = event.getY(i) / mHeight;
 45.1311 -                    p = event.getPressure(i);
 45.1312 -                    if (p > 1.0f) {
 45.1313 -                        // may be larger than 1.0f on some devices
 45.1314 -                        // see the documentation of getPressure(i)
 45.1315 -                        p = 1.0f;
 45.1316 -                    }
 45.1317 -                    SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
 45.1318 -                    break;
 45.1319 -
 45.1320 -                case MotionEvent.ACTION_CANCEL:
 45.1321 -                    for (i = 0; i < pointerCount; i++) {
 45.1322 -                        pointerFingerId = event.getPointerId(i);
 45.1323 -                        x = event.getX(i) / mWidth;
 45.1324 -                        y = event.getY(i) / mHeight;
 45.1325 -                        p = event.getPressure(i);
 45.1326 -                        if (p > 1.0f) {
 45.1327 -                            // may be larger than 1.0f on some devices
 45.1328 -                            // see the documentation of getPressure(i)
 45.1329 -                            p = 1.0f;
 45.1330 -                        }
 45.1331 -                        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
 45.1332 -                    }
 45.1333 -                    break;
 45.1334 -
 45.1335 -                default:
 45.1336 -                    break;
 45.1337 -            }
 45.1338 -        }
 45.1339 -
 45.1340 -        return true;
 45.1341 -   }
 45.1342 -
 45.1343 -    // Sensor events
 45.1344 -    public void enableSensor(int sensortype, boolean enabled) {
 45.1345 -        // TODO: This uses getDefaultSensor - what if we have >1 accels?
 45.1346 -        if (enabled) {
 45.1347 -            mSensorManager.registerListener(this,
 45.1348 -                            mSensorManager.getDefaultSensor(sensortype),
 45.1349 -                            SensorManager.SENSOR_DELAY_GAME, null);
 45.1350 -        } else {
 45.1351 -            mSensorManager.unregisterListener(this,
 45.1352 -                            mSensorManager.getDefaultSensor(sensortype));
 45.1353 -        }
 45.1354 -    }
 45.1355 -
 45.1356 -    @Override
 45.1357 -    public void onAccuracyChanged(Sensor sensor, int accuracy) {
 45.1358 -        // TODO
 45.1359 -    }
 45.1360 -
 45.1361 -    @Override
 45.1362 -    public void onSensorChanged(SensorEvent event) {
 45.1363 -        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
 45.1364 -            float x, y;
 45.1365 -            switch (mDisplay.getRotation()) {
 45.1366 -                case Surface.ROTATION_90:
 45.1367 -                    x = -event.values[1];
 45.1368 -                    y = event.values[0];
 45.1369 -                    break;
 45.1370 -                case Surface.ROTATION_270:
 45.1371 -                    x = event.values[1];
 45.1372 -                    y = -event.values[0];
 45.1373 -                    break;
 45.1374 -                case Surface.ROTATION_180:
 45.1375 -                    x = -event.values[1];
 45.1376 -                    y = -event.values[0];
 45.1377 -                    break;
 45.1378 -                default:
 45.1379 -                    x = event.values[0];
 45.1380 -                    y = event.values[1];
 45.1381 -                    break;
 45.1382 -            }
 45.1383 -            SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
 45.1384 -                                      y / SensorManager.GRAVITY_EARTH,
 45.1385 -                                      event.values[2] / SensorManager.GRAVITY_EARTH);
 45.1386 -        }
 45.1387 -    }
 45.1388 -}
 45.1389 -
 45.1390 -/* This is a fake invisible editor view that receives the input and defines the
 45.1391 - * pan&scan region
 45.1392 - */
 45.1393 -class DummyEdit extends View implements View.OnKeyListener {
 45.1394 -    InputConnection ic;
 45.1395 -
 45.1396 -    public DummyEdit(Context context) {
 45.1397 -        super(context);
 45.1398 -        setFocusableInTouchMode(true);
 45.1399 -        setFocusable(true);
 45.1400 -        setOnKeyListener(this);
 45.1401 -    }
 45.1402 -
 45.1403 -    @Override
 45.1404 -    public boolean onCheckIsTextEditor() {
 45.1405 -        return true;
 45.1406 -    }
 45.1407 -
 45.1408 -    @Override
 45.1409 -    public boolean onKey(View v, int keyCode, KeyEvent event) {
 45.1410 -        /* 
 45.1411 -         * This handles the hardware keyboard input
 45.1412 -         */
 45.1413 -        if (event.getAction() == KeyEvent.ACTION_DOWN) {
 45.1414 -            if (SDLActivity.isTextInputEvent(event)) {
 45.1415 -                ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
 45.1416 -            }
 45.1417 -            SDLActivity.onNativeKeyDown(keyCode);
 45.1418 -            return true;
 45.1419 -        } else if (event.getAction() == KeyEvent.ACTION_UP) {
 45.1420 -            SDLActivity.onNativeKeyUp(keyCode);
 45.1421 -            return true;
 45.1422 -        }
 45.1423 -        return false;
 45.1424 -    }
 45.1425 -
 45.1426 -    //
 45.1427 -    @Override
 45.1428 -    public boolean onKeyPreIme (int keyCode, KeyEvent event) {
 45.1429 -        // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
 45.1430 -        // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
 45.1431 -        // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
 45.1432 -        // FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
 45.1433 -        // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
 45.1434 -        // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
 45.1435 -        if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
 45.1436 -            if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
 45.1437 -                SDLActivity.onNativeKeyboardFocusLost();
 45.1438 -            }
 45.1439 -        }
 45.1440 -        return super.onKeyPreIme(keyCode, event);
 45.1441 -    }
 45.1442 -
 45.1443 -    @Override
 45.1444 -    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
 45.1445 -        ic = new SDLInputConnection(this, true);
 45.1446 -
 45.1447 -        outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
 45.1448 -        outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
 45.1449 -                | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
 45.1450 -
 45.1451 -        return ic;
 45.1452 -    }
 45.1453 -}
 45.1454 -
 45.1455 -class SDLInputConnection extends BaseInputConnection {
 45.1456 -
 45.1457 -    public SDLInputConnection(View targetView, boolean fullEditor) {
 45.1458 -        super(targetView, fullEditor);
 45.1459 -
 45.1460 -    }
 45.1461 -
 45.1462 -    @Override
 45.1463 -    public boolean sendKeyEvent(KeyEvent event) {
 45.1464 -        /*
 45.1465 -         * This handles the keycodes from soft keyboard (and IME-translated input from hardkeyboard)
 45.1466 -         */
 45.1467 -        int keyCode = event.getKeyCode();
 45.1468 -        if (event.getAction() == KeyEvent.ACTION_DOWN) {
 45.1469 -            if (SDLActivity.isTextInputEvent(event)) {
 45.1470 -                commitText(String.valueOf((char) event.getUnicodeChar()), 1);
 45.1471 -            }
 45.1472 -            SDLActivity.onNativeKeyDown(keyCode);
 45.1473 -            return true;
 45.1474 -        } else if (event.getAction() == KeyEvent.ACTION_UP) {
 45.1475 -            SDLActivity.onNativeKeyUp(keyCode);
 45.1476 -            return true;
 45.1477 -        }
 45.1478 -        return super.sendKeyEvent(event);
 45.1479 -    }
 45.1480 -
 45.1481 -    @Override
 45.1482 -    public boolean commitText(CharSequence text, int newCursorPosition) {
 45.1483 -
 45.1484 -        nativeCommitText(text.toString(), newCursorPosition);
 45.1485 -
 45.1486 -        return super.commitText(text, newCursorPosition);
 45.1487 -    }
 45.1488 -
 45.1489 -    @Override
 45.1490 -    public boolean setComposingText(CharSequence text, int newCursorPosition) {
 45.1491 -
 45.1492 -        nativeSetComposingText(text.toString(), newCursorPosition);
 45.1493 -
 45.1494 -        return super.setComposingText(text, newCursorPosition);
 45.1495 -    }
 45.1496 -
 45.1497 -    public native void nativeCommitText(String text, int newCursorPosition);
 45.1498 -
 45.1499 -    public native void nativeSetComposingText(String text, int newCursorPosition);
 45.1500 -
 45.1501 -    @Override
 45.1502 -    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
 45.1503 -        // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
 45.1504 -        // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
 45.1505 -        if (beforeLength > 0 && afterLength == 0) {
 45.1506 -            boolean ret = true;
 45.1507 -            // backspace(s)
 45.1508 -            while (beforeLength-- > 0) {
 45.1509 -               boolean ret_key = sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
 45.1510 -                              && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
 45.1511 -               ret = ret && ret_key; 
 45.1512 -            }
 45.1513 -            return ret;
 45.1514 -        }
 45.1515 -
 45.1516 -        return super.deleteSurroundingText(beforeLength, afterLength);
 45.1517 -    }
 45.1518 -}
 45.1519 -
 45.1520 -interface SDLClipboardHandler {
 45.1521 -
 45.1522 -    public boolean clipboardHasText();
 45.1523 -    public String clipboardGetText();
 45.1524 -    public void clipboardSetText(String string);
 45.1525 -
 45.1526 -}
 45.1527 -
 45.1528 -
 45.1529 -class SDLClipboardHandler_API11 implements
 45.1530 -    SDLClipboardHandler, 
 45.1531 -    android.content.ClipboardManager.OnPrimaryClipChangedListener {
 45.1532 -
 45.1533 -    protected android.content.ClipboardManager mClipMgr;
 45.1534 -
 45.1535 -    SDLClipboardHandler_API11() {
 45.1536 -       mClipMgr = (android.content.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
 45.1537 -       mClipMgr.addPrimaryClipChangedListener(this);
 45.1538 -    }
 45.1539 -
 45.1540 -    @Override
 45.1541 -    public boolean clipboardHasText() {
 45.1542 -       return mClipMgr.hasText();
 45.1543 -    }
 45.1544 -
 45.1545 -    @Override
 45.1546 -    public String clipboardGetText() {
 45.1547 -        CharSequence text;
 45.1548 -        text = mClipMgr.getText();
 45.1549 -        if (text != null) {
 45.1550 -           return text.toString();
 45.1551 -        }
 45.1552 -        return null;
 45.1553 -    }
 45.1554 -
 45.1555 -    @Override
 45.1556 -    public void clipboardSetText(String string) {
 45.1557 -       mClipMgr.removePrimaryClipChangedListener(this);
 45.1558 -       mClipMgr.setText(string);
 45.1559 -       mClipMgr.addPrimaryClipChangedListener(this);
 45.1560 -    }
 45.1561 -    
 45.1562 -    @Override
 45.1563 -    public void onPrimaryClipChanged() {
 45.1564 -        SDLActivity.onNativeClipboardChanged();
 45.1565 -    }
 45.1566 -
 45.1567 -}
 45.1568 -
 45.1569 -class SDLClipboardHandler_Old implements
 45.1570 -    SDLClipboardHandler {
 45.1571 -   
 45.1572 -    protected android.text.ClipboardManager mClipMgrOld;
 45.1573 -  
 45.1574 -    SDLClipboardHandler_Old() {
 45.1575 -       mClipMgrOld = (android.text.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
 45.1576 -    }
 45.1577 -
 45.1578 -    @Override
 45.1579 -    public boolean clipboardHasText() {
 45.1580 -       return mClipMgrOld.hasText();
 45.1581 -    }
 45.1582 -
 45.1583 -    @Override
 45.1584 -    public String clipboardGetText() {
 45.1585 -       CharSequence text;
 45.1586 -       text = mClipMgrOld.getText();
 45.1587 -       if (text != null) {
 45.1588 -          return text.toString();
 45.1589 -       }
 45.1590 -       return null;
 45.1591 -    }
 45.1592 -
 45.1593 -    @Override
 45.1594 -    public void clipboardSetText(String string) {
 45.1595 -       mClipMgrOld.setText(string);
 45.1596 -    }
 45.1597 -}
 45.1598 -
    46.1 --- a/android-project/src/org/libsdl/app/SDLAudioManager.java	Mon Oct 23 12:33:18 2017 -0700
    46.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    46.3 @@ -1,178 +0,0 @@
    46.4 -package org.libsdl.app;
    46.5 -
    46.6 -import android.media.*;
    46.7 -import android.util.Log;
    46.8 -
    46.9 -public class SDLAudioManager
   46.10 -{
   46.11 -    protected static final String TAG = "SDLAudio";
   46.12 -
   46.13 -    protected static AudioTrack mAudioTrack;
   46.14 -    protected static AudioRecord mAudioRecord;
   46.15 -
   46.16 -    public static void initialize() {
   46.17 -        mAudioTrack = null;
   46.18 -        mAudioRecord = null;
   46.19 -    }
   46.20 -
   46.21 -    // Audio
   46.22 -
   46.23 -    /**
   46.24 -     * This method is called by SDL using JNI.
   46.25 -     */
   46.26 -    public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
   46.27 -        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
   46.28 -        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
   46.29 -        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
   46.30 -
   46.31 -        Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
   46.32 -
   46.33 -        // Let the user pick a larger buffer if they really want -- but ye
   46.34 -        // gods they probably shouldn't, the minimums are horrifyingly high
   46.35 -        // latency already
   46.36 -        desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
   46.37 -
   46.38 -        if (mAudioTrack == null) {
   46.39 -            mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
   46.40 -                    channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
   46.41 -
   46.42 -            // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
   46.43 -            // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
   46.44 -            // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
   46.45 -
   46.46 -            if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
   46.47 -                Log.e(TAG, "Failed during initialization of Audio Track");
   46.48 -                mAudioTrack = null;
   46.49 -                return -1;
   46.50 -            }
   46.51 -
   46.52 -            mAudioTrack.play();
   46.53 -        }
   46.54 -
   46.55 -        Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
   46.56 -
   46.57 -        return 0;
   46.58 -    }
   46.59 -
   46.60 -    /**
   46.61 -     * This method is called by SDL using JNI.
   46.62 -     */
   46.63 -    public static void audioWriteShortBuffer(short[] buffer) {
   46.64 -        if (mAudioTrack == null) {
   46.65 -            Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
   46.66 -            return;
   46.67 -        }
   46.68 -
   46.69 -        for (int i = 0; i < buffer.length; ) {
   46.70 -            int result = mAudioTrack.write(buffer, i, buffer.length - i);
   46.71 -            if (result > 0) {
   46.72 -                i += result;
   46.73 -            } else if (result == 0) {
   46.74 -                try {
   46.75 -                    Thread.sleep(1);
   46.76 -                } catch(InterruptedException e) {
   46.77 -                    // Nom nom
   46.78 -                }
   46.79 -            } else {
   46.80 -                Log.w(TAG, "SDL audio: error return from write(short)");
   46.81 -                return;
   46.82 -            }
   46.83 -        }
   46.84 -    }
   46.85 -
   46.86 -    /**
   46.87 -     * This method is called by SDL using JNI.
   46.88 -     */
   46.89 -    public static void audioWriteByteBuffer(byte[] buffer) {
   46.90 -        if (mAudioTrack == null) {
   46.91 -            Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
   46.92 -            return;
   46.93 -        }
   46.94 -        
   46.95 -        for (int i = 0; i < buffer.length; ) {
   46.96 -            int result = mAudioTrack.write(buffer, i, buffer.length - i);
   46.97 -            if (result > 0) {
   46.98 -                i += result;
   46.99 -            } else if (result == 0) {
  46.100 -                try {
  46.101 -                    Thread.sleep(1);
  46.102 -                } catch(InterruptedException e) {
  46.103 -                    // Nom nom
  46.104 -                }
  46.105 -            } else {
  46.106 -                Log.w(TAG, "SDL audio: error return from write(byte)");
  46.107 -                return;
  46.108 -            }
  46.109 -        }
  46.110 -    }
  46.111 -
  46.112 -    /**
  46.113 -     * This method is called by SDL using JNI.
  46.114 -     */
  46.115 -    public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
  46.116 -        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
  46.117 -        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
  46.118 -        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
  46.119 -
  46.120 -        Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
  46.121 -
  46.122 -        // Let the user pick a larger buffer if they really want -- but ye
  46.123 -        // gods they probably shouldn't, the minimums are horrifyingly high
  46.124 -        // latency already
  46.125 -        desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
  46.126 -
  46.127 -        if (mAudioRecord == null) {
  46.128 -            mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
  46.129 -                    channelConfig, audioFormat, desiredFrames * frameSize);
  46.130 -
  46.131 -            // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
  46.132 -            if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
  46.133 -                Log.e(TAG, "Failed during initialization of AudioRecord");
  46.134 -                mAudioRecord.release();
  46.135 -                mAudioRecord = null;
  46.136 -                return -1;
  46.137 -            }
  46.138 -
  46.139 -            mAudioRecord.startRecording();
  46.140 -        }
  46.141 -
  46.142 -        Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
  46.143 -
  46.144 -        return 0;
  46.145 -    }
  46.146 -
  46.147 -    /** This method is called by SDL using JNI. */
  46.148 -    public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
  46.149 -        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
  46.150 -        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
  46.151 -        return mAudioRecord.read(buffer, 0, buffer.length);
  46.152 -    }
  46.153 -
  46.154 -    /** This method is called by SDL using JNI. */
  46.155 -    public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
  46.156 -        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
  46.157 -        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
  46.158 -        return mAudioRecord.read(buffer, 0, buffer.length);
  46.159 -    }
  46.160 -
  46.161 -
  46.162 -    /** This method is called by SDL using JNI. */
  46.163 -    public static void audioClose() {
  46.164 -        if (mAudioTrack != null) {
  46.165 -            mAudioTrack.stop();
  46.166 -            mAudioTrack.release();
  46.167 -            mAudioTrack = null;
  46.168 -        }
  46.169 -    }
  46.170 -
  46.171 -    /** This method is called by SDL using JNI. */
  46.172 -    public static void captureClose() {
  46.173 -        if (mAudioRecord != null) {
  46.174 -            mAudioRecord.stop();
  46.175 -            mAudioRecord.release();
  46.176 -            mAudioRecord = null;
  46.177 -        }
  46.178 -    }
  46.179 -
  46.180 -    public static native int nativeSetupJNI();
  46.181 -}
    47.1 --- a/android-project/src/org/libsdl/app/SDLControllerManager.java	Mon Oct 23 12:33:18 2017 -0700
    47.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    47.3 @@ -1,433 +0,0 @@
    47.4 -package org.libsdl.app;
    47.5 -
    47.6 -import java.util.ArrayList;
    47.7 -import java.util.Collections;
    47.8 -import java.util.Comparator;
    47.9 -import java.util.List;
   47.10 -import java.util.Objects;
   47.11 -
   47.12 -import android.content.Context;
   47.13 -import android.os.*;
   47.14 -import android.view.*;
   47.15 -import android.util.Log;
   47.16 -
   47.17 -
   47.18 -public class SDLControllerManager 
   47.19 -{
   47.20 -
   47.21 -    public static native int nativeSetupJNI();
   47.22 -
   47.23 -    public static native int nativeAddJoystick(int device_id, String name, String desc,
   47.24 -                                               int is_accelerometer, int nbuttons,
   47.25 -                                               int naxes, int nhats, int nballs);
   47.26 -    public static native int nativeRemoveJoystick(int device_id);
   47.27 -    public static native int nativeAddHaptic(int device_id, String name);
   47.28 -    public static native int nativeRemoveHaptic(int device_id);
   47.29 -    public static native int onNativePadDown(int device_id, int keycode);
   47.30 -    public static native int onNativePadUp(int device_id, int keycode);
   47.31 -    public static native void onNativeJoy(int device_id, int axis,
   47.32 -                                          float value);
   47.33 -    public static native void onNativeHat(int device_id, int hat_id,
   47.34 -                                          int x, int y);
   47.35 -
   47.36 -    protected static SDLJoystickHandler mJoystickHandler;
   47.37 -    protected static SDLHapticHandler mHapticHandler;
   47.38 -
   47.39 -    private static final String TAG = "SDLControllerManager";
   47.40 -
   47.41 -    public static void initialize() {
   47.42 -        mJoystickHandler = null;
   47.43 -        mHapticHandler = null;
   47.44 -
   47.45 -        SDLControllerManager.setup();
   47.46 -    }
   47.47 -
   47.48 -    public static void setup() {
   47.49 -        if (Build.VERSION.SDK_INT >= 16) {
   47.50 -            mJoystickHandler = new SDLJoystickHandler_API16();
   47.51 -        } else if (Build.VERSION.SDK_INT >= 12) {
   47.52 -            mJoystickHandler = new SDLJoystickHandler_API12();
   47.53 -        } else {
   47.54 -            mJoystickHandler = new SDLJoystickHandler();
   47.55 -        }
   47.56 -        mHapticHandler = new SDLHapticHandler();
   47.57 -    }
   47.58 -
   47.59 -    // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
   47.60 -    public static boolean handleJoystickMotionEvent(MotionEvent event) {
   47.61 -        return mJoystickHandler.handleMotionEvent(event);
   47.62 -    }
   47.63 -
   47.64 -    /**
   47.65 -     * This method is called by SDL using JNI.
   47.66 -     */
   47.67 -    public static void pollInputDevices() {
   47.68 -        mJoystickHandler.pollInputDevices();
   47.69 -    }
   47.70 -
   47.71 -    /**
   47.72 -     * This method is called by SDL using JNI.
   47.73 -     */
   47.74 -    public static void pollHapticDevices() {
   47.75 -        mHapticHandler.pollHapticDevices();
   47.76 -    }
   47.77 -
   47.78 -    /**
   47.79 -     * This method is called by SDL using JNI.
   47.80 -     */
   47.81 -    public static void hapticRun(int device_id, int length) {
   47.82 -        mHapticHandler.run(device_id, length);
   47.83 -    }
   47.84 -
   47.85 -    // Check if a given device is considered a possible SDL joystick
   47.86 -    public static boolean isDeviceSDLJoystick(int deviceId) {
   47.87 -        InputDevice device = InputDevice.getDevice(deviceId);
   47.88 -        // We cannot use InputDevice.isVirtual before API 16, so let's accept
   47.89 -        // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
   47.90 -        if ((device == null) || (deviceId < 0)) {
   47.91 -            return false;
   47.92 -        }
   47.93 -        int sources = device.getSources();
   47.94 -
   47.95 -        if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) {
   47.96 -            Log.v(TAG, "Input device " + device.getName() + " is a joystick.");
   47.97 -        }
   47.98 -        if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
   47.99 -            Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
  47.100 -        }
  47.101 -        if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
  47.102 -            Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
  47.103 -        }
  47.104 -
  47.105 -        return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
  47.106 -                ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
  47.107 -                ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
  47.108 -        );
  47.109 -    }
  47.110 -
  47.111 -}
  47.112 -
  47.113 -/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
  47.114 -class SDLJoystickHandler {
  47.115 -
  47.116 -    /**
  47.117 -     * Handles given MotionEvent.
  47.118 -     * @param event the event to be handled.
  47.119 -     * @return if given event was processed.
  47.120 -     */
  47.121 -    public boolean handleMotionEvent(MotionEvent event) {
  47.122 -        return false;
  47.123 -    }
  47.124 -
  47.125 -    /**
  47.126 -     * Handles adding and removing of input devices.
  47.127 -     */
  47.128 -    public void pollInputDevices() {
  47.129 -    }
  47.130 -}
  47.131 -
  47.132 -/* Actual joystick functionality available for API >= 12 devices */
  47.133 -class SDLJoystickHandler_API12 extends SDLJoystickHandler {
  47.134 -
  47.135 -    static class SDLJoystick {
  47.136 -        public int device_id;
  47.137 -        public String name;
  47.138 -        public String desc;
  47.139 -        public ArrayList<InputDevice.MotionRange> axes;
  47.140 -        public ArrayList<InputDevice.MotionRange> hats;
  47.141 -    }
  47.142 -    static class RangeComparator implements Comparator<InputDevice.MotionRange> {
  47.143 -        @Override
  47.144 -        public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
  47.145 -            return arg0.getAxis() - arg1.getAxis();
  47.146 -        }
  47.147 -    }
  47.148 -
  47.149 -    private ArrayList<SDLJoystick> mJoysticks;
  47.150 -
  47.151 -    public SDLJoystickHandler_API12() {
  47.152 -
  47.153 -        mJoysticks = new ArrayList<SDLJoystick>();
  47.154 -    }
  47.155 -
  47.156 -    @Override
  47.157 -    public void pollInputDevices() {
  47.158 -        int[] deviceIds = InputDevice.getDeviceIds();
  47.159 -        // It helps processing the device ids in reverse order
  47.160 -        // For example, in the case of the XBox 360 wireless dongle,
  47.161 -        // so the first controller seen by SDL matches what the receiver
  47.162 -        // considers to be the first controller
  47.163 -
  47.164 -        for(int i=deviceIds.length-1; i>-1; i--) {
  47.165 -            SDLJoystick joystick = getJoystick(deviceIds[i]);
  47.166 -            if (joystick == null) {
  47.167 -                joystick = new SDLJoystick();
  47.168 -                InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
  47.169 -                if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
  47.170 -                    joystick.device_id = deviceIds[i];
  47.171 -                    joystick.name = joystickDevice.getName();
  47.172 -                    joystick.desc = getJoystickDescriptor(joystickDevice);
  47.173 -                    joystick.axes = new ArrayList<InputDevice.MotionRange>();
  47.174 -                    joystick.hats = new ArrayList<InputDevice.MotionRange>();
  47.175 -
  47.176 -                    List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
  47.177 -                    Collections.sort(ranges, new RangeComparator());
  47.178 -                    for (InputDevice.MotionRange range : ranges ) {
  47.179 -                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
  47.180 -                            if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
  47.181 -                                range.getAxis() == MotionEvent.AXIS_HAT_Y) {
  47.182 -                                joystick.hats.add(range);
  47.183 -                            }
  47.184 -                            else {
  47.185 -                                joystick.axes.add(range);
  47.186 -                            }
  47.187 -                        }
  47.188 -                    }
  47.189 -
  47.190 -                    mJoysticks.add(joystick);
  47.191 -                    SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
  47.192 -                                                           joystick.axes.size(), joystick.hats.size()/2, 0);
  47.193 -                }
  47.194 -            }
  47.195 -        }
  47.196 -
  47.197 -        /* Check removed devices */
  47.198 -        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
  47.199 -        for(int i=0; i < mJoysticks.size(); i++) {
  47.200 -            int device_id = mJoysticks.get(i).device_id;
  47.201 -            int j;
  47.202 -            for (j=0; j < deviceIds.length; j++) {
  47.203 -                if (device_id == deviceIds[j]) break;
  47.204 -            }
  47.205 -            if (j == deviceIds.length) {
  47.206 -                removedDevices.add(Integer.valueOf(device_id));
  47.207 -            }
  47.208 -        }
  47.209 -
  47.210 -        for(int i=0; i < removedDevices.size(); i++) {
  47.211 -            int device_id = removedDevices.get(i).intValue();
  47.212 -            SDLControllerManager.nativeRemoveJoystick(device_id);
  47.213 -            for (int j=0; j < mJoysticks.size(); j++) {
  47.214 -                if (mJoysticks.get(j).device_id == device_id) {
  47.215 -                    mJoysticks.remove(j);
  47.216 -                    break;
  47.217 -                }
  47.218 -            }
  47.219 -        }
  47.220 -    }
  47.221 -
  47.222 -    protected SDLJoystick getJoystick(int device_id) {
  47.223 -        for(int i=0; i < mJoysticks.size(); i++) {
  47.224 -            if (mJoysticks.get(i).device_id == device_id) {
  47.225 -                return mJoysticks.get(i);
  47.226 -            }
  47.227 -        }
  47.228 -        return null;
  47.229 -    }
  47.230 -
  47.231 -    @Override
  47.232 -    public boolean handleMotionEvent(MotionEvent event) {
  47.233 -        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
  47.234 -            int actionPointerIndex = event.getActionIndex();
  47.235 -            int action = event.getActionMasked();
  47.236 -            switch(action) {
  47.237 -                case MotionEvent.ACTION_MOVE:
  47.238 -                    SDLJoystick joystick = getJoystick(event.getDeviceId());
  47.239 -                    if ( joystick != null ) {
  47.240 -                        for (int i = 0; i < joystick.axes.size(); i++) {
  47.241 -                            InputDevice.MotionRange range = joystick.axes.get(i);
  47.242 -                            /* Normalize the value to -1...1 */
  47.243 -                            float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
  47.244 -                            SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
  47.245 -                        }
  47.246 -                        for (int i = 0; i < joystick.hats.size(); i+=2) {
  47.247 -                            int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
  47.248 -                            int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
  47.249 -                            SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
  47.250 -                        }
  47.251 -                    }
  47.252 -                    break;
  47.253 -                default:
  47.254 -                    break;
  47.255 -            }
  47.256 -        }
  47.257 -        return true;
  47.258 -    }
  47.259 -
  47.260 -    public String getJoystickDescriptor(InputDevice joystickDevice) {
  47.261 -        return joystickDevice.getName();
  47.262 -    }
  47.263 -}
  47.264 -
  47.265 -
  47.266 -class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
  47.267 -
  47.268 -    @Override
  47.269 -    public String getJoystickDescriptor(InputDevice joystickDevice) {
  47.270 -        String desc = joystickDevice.getDescriptor();
  47.271 -
  47.272 -        if (desc != null && !Objects.equals(desc, "")) {
  47.273 -            return desc;
  47.274 -        }
  47.275 -
  47.276 -        return super.getJoystickDescriptor(joystickDevice);
  47.277 -    }
  47.278 -}
  47.279 -
  47.280 -class SDLHapticHandler {
  47.281 -
  47.282 -    class SDLHaptic {
  47.283 -        public int device_id;
  47.284 -        public String name;
  47.285 -        public Vibrator vib;
  47.286 -    }
  47.287 -
  47.288 -    private ArrayList<SDLHaptic> mHaptics;
  47.289 -    
  47.290 -    public SDLHapticHandler() {
  47.291 -        mHaptics = new ArrayList<SDLHaptic>();
  47.292 -    }
  47.293 -
  47.294 -    public void run(int device_id, int length) {
  47.295 -        SDLHaptic haptic = getHaptic(device_id);
  47.296 -        if (haptic != null) {
  47.297 -            haptic.vib.vibrate (length);
  47.298 -        }
  47.299 -    }
  47.300 -
  47.301 -    public void pollHapticDevices() {
  47.302 -        
  47.303 -        final int deviceId_VIBRATOR_SERVICE = 999999;
  47.304 -        boolean hasVibratorService = false;
  47.305 -
  47.306 -        int[] deviceIds = InputDevice.getDeviceIds();
  47.307 -        // It helps processing the device ids in reverse order
  47.308 -        // For example, in the case of the XBox 360 wireless dongle,
  47.309 -        // so the first controller seen by SDL matches what the receiver
  47.310 -        // considers to be the first controller
  47.311 -
  47.312 -        if (Build.VERSION.SDK_INT >= 16)
  47.313 -        {
  47.314 -            for (int i = deviceIds.length - 1; i > -1; i--) {
  47.315 -                SDLHaptic haptic = getHaptic(deviceIds[i]);
  47.316 -                if (haptic == null) {
  47.317 -                    InputDevice device = InputDevice.getDevice(deviceIds[i]);
  47.318 -                    Vibrator vib = device.getVibrator();
  47.319 -                    if (vib.hasVibrator()) {
  47.320 -                        haptic = new SDLHaptic();
  47.321 -                        haptic.device_id = deviceIds[i];
  47.322 -                        haptic.name = device.getName();
  47.323 -                        haptic.vib = vib;
  47.324 -                        mHaptics.add(haptic);
  47.325 -                        SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
  47.326 -                    }
  47.327 -                }
  47.328 -            }
  47.329 -        }
  47.330 -
  47.331 -        /* Check VIBRATOR_SERVICE */
  47.332 -        Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
  47.333 -        if (vib != null) {
  47.334 -            if (Build.VERSION.SDK_INT >= 11) {
  47.335 -                hasVibratorService = vib.hasVibrator();
  47.336 -            } else {
  47.337 -                hasVibratorService = true;
  47.338 -            }
  47.339 -
  47.340 -            if (hasVibratorService) {
  47.341 -                SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
  47.342 -                if (haptic == null) {
  47.343 -                    haptic = new SDLHaptic();
  47.344 -                    haptic.device_id = deviceId_VIBRATOR_SERVICE;
  47.345 -                    haptic.name = "VIBRATOR_SERVICE";
  47.346 -                    haptic.vib = vib; 
  47.347 -                    mHaptics.add(haptic);
  47.348 -                    SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
  47.349 -                }
  47.350 -            }
  47.351 -        }
  47.352 -
  47.353 -        /* Check removed devices */
  47.354 -        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
  47.355 -        for(int i=0; i < mHaptics.size(); i++) {
  47.356 -            int device_id = mHaptics.get(i).device_id;
  47.357 -            int j;
  47.358 -            for (j=0; j < deviceIds.length; j++) {
  47.359 -                if (device_id == deviceIds[j]) break;
  47.360 -            }
  47.361 -
  47.362 -            if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
  47.363 -                // don't remove the vibrator if it is still present
  47.364 -            } else if (j == deviceIds.length) {
  47.365 -                removedDevices.add(device_id);
  47.366 -            }
  47.367 -        }
  47.368 -
  47.369 -        for(int i=0; i < removedDevices.size(); i++) {
  47.370 -            int device_id = removedDevices.get(i);
  47.371 -            SDLControllerManager.nativeRemoveHaptic(device_id);
  47.372 -            for (int j=0; j < mHaptics.size(); j++) {
  47.373 -                if (mHaptics.get(j).device_id == device_id) {
  47.374 -                    mHaptics.remove(j);
  47.375 -                    break;
  47.376 -                }
  47.377 -            }
  47.378 -        }
  47.379 -    }
  47.380 -
  47.381 -    protected SDLHaptic getHaptic(int device_id) {
  47.382 -        for(int i=0; i < mHaptics.size(); i++) {
  47.383 -            if (mHaptics.get(i).device_id == device_id) {
  47.384 -                return mHaptics.get(i);
  47.385 -            }
  47.386 -        }
  47.387 -        return null;
  47.388 -    }   
  47.389 -}
  47.390 -
  47.391 -class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
  47.392 -    // Generic Motion (mouse hover, joystick...) events go here
  47.393 -    @Override
  47.394 -    public boolean onGenericMotion(View v, MotionEvent event) {
  47.395 -        float x, y;
  47.396 -        int action;
  47.397 -
  47.398 -        switch ( event.getSource() ) {
  47.399 -            case InputDevice.SOURCE_JOYSTICK:
  47.400 -            case InputDevice.SOURCE_GAMEPAD:
  47.401 -            case InputDevice.SOURCE_DPAD:
  47.402 -                return SDLControllerManager.handleJoystickMotionEvent(event);
  47.403 -
  47.404 -            case InputDevice.SOURCE_MOUSE:
  47.405 -                if (!SDLActivity.mSeparateMouseAndTouch) {
  47.406 -                    break;
  47.407 -                }
  47.408 -                action = event.getActionMasked();
  47.409 -                switch (action) {
  47.410 -                    case MotionEvent.ACTION_SCROLL:
  47.411 -                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
  47.412 -                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
  47.413 -                        SDLActivity.onNativeMouse(0, action, x, y);
  47.414 -                        return true;
  47.415 -
  47.416 -                    case MotionEvent.ACTION_HOVER_MOVE:
  47.417 -                        x = event.getX(0);
  47.418 -                        y = event.getY(0);
  47.419 -
  47.420 -                        SDLActivity.onNativeMouse(0, action, x, y);
  47.421 -                        return true;
  47.422 -
  47.423 -                    default:
  47.424 -                        break;
  47.425 -                }
  47.426 -                break;
  47.427 -
  47.428 -            default:
  47.429 -                break;
  47.430 -        }
  47.431 -
  47.432 -        // Event was not managed
  47.433 -        return false;
  47.434 -    }
  47.435 -}
  47.436 -
    48.1 --- a/build-scripts/androidbuild.sh	Mon Oct 23 12:33:18 2017 -0700
    48.2 +++ b/build-scripts/androidbuild.sh	Mon Oct 23 15:23:43 2017 -0700
    48.3 @@ -33,25 +33,26 @@
    48.4  
    48.5  SDLPATH="$( cd "$(dirname "$0")/.." ; pwd -P )"
    48.6  
    48.7 +if [ -z "$ANDROID_HOME" ];then
    48.8 +    echo "Please set the ANDROID_HOME directory to the path of the Android SDK"
    48.9 +    exit 1
   48.10 +fi
   48.11 +
   48.12  NDKBUILD=`which ndk-build`
   48.13  if [ -z "$NDKBUILD" ];then
   48.14      echo "Could not find the ndk-build utility, install Android's NDK and add it to the path"
   48.15      exit 1
   48.16  fi
   48.17  
   48.18 -ANDROID=`which android`
   48.19 +ANDROID="$ANDROID_HOME/tools/android"
   48.20 +if [ ! -f "$ANDROID" ]; then
   48.21 +    ANDROID=`which android`
   48.22 +fi
   48.23  if [ -z "$ANDROID" ];then
   48.24      echo "Could not find the android utility, install Android's SDK and add it to the path"
   48.25      exit 1
   48.26  fi
   48.27  
   48.28 -ANT=`which ant`
   48.29 -
   48.30 -if [ -z "$ANT" ];then
   48.31 -    echo "Could not find the ant utility, install Android's SDK and add it to the path"
   48.32 -    exit 1
   48.33 -fi
   48.34 -
   48.35  NCPUS="1"
   48.36  case "$OSTYPE" in
   48.37      darwin*)
   48.38 @@ -77,27 +78,28 @@
   48.39  cp -r $SDLPATH/android-project/* $BUILDPATH
   48.40  
   48.41  # Copy SDL sources
   48.42 -mkdir -p $BUILDPATH/jni/SDL
   48.43 +mkdir -p $BUILDPATH/app/jni/SDL
   48.44  if [ -z "$COPYSOURCE" ]; then
   48.45 -    ln -s $SDLPATH/src $BUILDPATH/jni/SDL
   48.46 -    ln -s $SDLPATH/include $BUILDPATH/jni/SDL
   48.47 +    ln -s $SDLPATH/src $BUILDPATH/app/jni/SDL
   48.48 +    ln -s $SDLPATH/include $BUILDPATH/app/jni/SDL
   48.49  else
   48.50 -    cp -r $SDLPATH/src $BUILDPATH/jni/SDL
   48.51 -    cp -r $SDLPATH/include $BUILDPATH/jni/SDL
   48.52 +    cp -r $SDLPATH/src $BUILDPATH/app/jni/SDL
   48.53 +    cp -r $SDLPATH/include $BUILDPATH/app/jni/SDL
   48.54  fi
   48.55  
   48.56 -cp -r $SDLPATH/Android.mk $BUILDPATH/jni/SDL
   48.57 -sed -i -e "s|YourSourceHere.c|$MKSOURCES|g" $BUILDPATH/jni/src/Android.mk
   48.58 -sed -i -e "s|org\.libsdl\.app|$APP|g" $BUILDPATH/AndroidManifest.xml
   48.59 +cp -r $SDLPATH/Android.mk $BUILDPATH/app/jni/SDL
   48.60 +sed -i -e "s|YourSourceHere.c|$MKSOURCES|g" $BUILDPATH/app/jni/src/Android.mk
   48.61 +sed -i -e "s|org\.libsdl\.app|$APP|g" $BUILDPATH/app/build.gradle
   48.62 +sed -i -e "s|org\.libsdl\.app|$APP|g" $BUILDPATH/app/src/main/AndroidManifest.xml
   48.63  
   48.64  # Copy user sources
   48.65  for src in "${SOURCES[@]}"
   48.66  do
   48.67 -    cp $src $BUILDPATH/jni/src
   48.68 +    cp $src $BUILDPATH/app/jni/src
   48.69  done
   48.70  
   48.71  # Create an inherited Activity
   48.72 -cd $BUILDPATH/src
   48.73 +cd $BUILDPATH/app/src/main/java
   48.74  for folder in "${APPARR[@]}"
   48.75  do
   48.76      mkdir -p $folder
   48.77 @@ -105,31 +107,38 @@
   48.78  done
   48.79  
   48.80  ACTIVITY="${folder}Activity"
   48.81 -sed -i -e "s|SDLActivity|$ACTIVITY|g" $BUILDPATH/AndroidManifest.xml
   48.82 -sed -i -e "s|SDLActivity|$APP|g" $BUILDPATH/build.xml
   48.83 +sed -i -e "s|\"SDLActivity\"|\"$ACTIVITY\"|g" $BUILDPATH/app/src/main/AndroidManifest.xml
   48.84  
   48.85  # Fill in a default Activity
   48.86 -echo "package $APP;" >  "$ACTIVITY.java"
   48.87 -echo "import org.libsdl.app.SDLActivity;" >> "$ACTIVITY.java"
   48.88 -echo "public class $ACTIVITY extends SDLActivity {}" >> "$ACTIVITY.java"
   48.89 +cat >"$ACTIVITY.java" <<__EOF__
   48.90 +package $APP;
   48.91 +
   48.92 +import org.libsdl.app.SDLActivity;
   48.93 +
   48.94 +public class $ACTIVITY extends SDLActivity
   48.95 +{
   48.96 +}
   48.97 +__EOF__
   48.98  
   48.99  # Update project and build
  48.100  cd $BUILDPATH
  48.101 -$ANDROID update project --path $BUILDPATH
  48.102 +pushd $BUILDPATH/app/jni
  48.103  $NDKBUILD -j $NCPUS $NDKARGS
  48.104 -$ANT debug
  48.105 +popd
  48.106 +
  48.107 +# Start gradle build
  48.108 +$BUILDPATH/gradlew build
  48.109  
  48.110  cd $CURDIR
  48.111  
  48.112 -APK="$BUILDPATH/bin/$APP-debug.apk"
  48.113 +APK="$BUILDPATH/app/build/outputs/apk/app-debug.apk"
  48.114  
  48.115  if [ -f "$APK" ]; then
  48.116      echo "Your APK is ready at $APK"
  48.117      echo "To install to your device: "
  48.118 -    echo "cd  $BUILDPATH"
  48.119 -    echo "ant debug install"
  48.120 +    echo "$ANDROID_HOME/platform-tools/adb install -r $APK"
  48.121      exit 0
  48.122  fi
  48.123  
  48.124  echo "There was an error building the APK"
  48.125 -exit 1
  48.126 \ No newline at end of file
  48.127 +exit 1
    49.1 --- a/docs/README-android.md	Mon Oct 23 12:33:18 2017 -0700
    49.2 +++ b/docs/README-android.md	Mon Oct 23 15:23:43 2017 -0700
    49.3 @@ -4,7 +4,8 @@
    49.4  Matt Styles wrote a tutorial on building SDL for Android with Visual Studio:
    49.5  http://trederia.blogspot.de/2017/03/building-sdl2-for-android-with-visual.html
    49.6  
    49.7 -The rest of this README covers the old style build process.
    49.8 +The rest of this README covers the traditional style build process.
    49.9 +
   49.10  
   49.11  ================================================================================
   49.12   Requirements
   49.13 @@ -19,6 +20,7 @@
   49.14  Minimum API level supported by SDL: 10 (Android 2.3.3)
   49.15  Joystick support is available for API level >= 12 devices.
   49.16  
   49.17 +
   49.18  ================================================================================
   49.19   How the port works
   49.20  ================================================================================
   49.21 @@ -31,15 +33,12 @@
   49.22  - This eventually produces a standard Android .apk package
   49.23  
   49.24  The Android Java code implements an "Activity" and can be found in:
   49.25 -android-project/src/org/libsdl/app/SDLActivity.java
   49.26 +android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
   49.27  
   49.28  The Java code loads your game code, the SDL shared library, and
   49.29  dispatches to native functions implemented in the SDL library:
   49.30  src/core/android/SDL_android.c
   49.31  
   49.32 -Your project must include some glue code that starts your main() routine:
   49.33 -src/main/android/SDL_android_main.c
   49.34 -
   49.35  
   49.36  ================================================================================
   49.37   Building an app
   49.38 @@ -74,71 +73,32 @@
   49.39      
   49.40  1. Copy the android-project directory wherever you want to keep your projects
   49.41     and rename it to the name of your project.
   49.42 -2. Move or symlink this SDL directory into the "<project>/jni" directory
   49.43 -3. Edit "<project>/jni/src/Android.mk" to include your source files
   49.44 +2. Move or symlink this SDL directory into the "<project>/app/jni" directory
   49.45 +3. Edit "<project>/app/jni/src/Android.mk" to include your source files
   49.46  4. Run 'ndk-build' (a script provided by the NDK). This compiles the C source
   49.47  
   49.48 -If you want to use the Eclipse IDE, skip to the Eclipse section below.
   49.49 +If you want to use Android Studio (recommended), skip to the Android Studio section below.
   49.50  
   49.51 -5. Create "<project>/local.properties" and use that to point to the Android SDK directory, by writing a line with the following form:
   49.52 -
   49.53 -       sdk.dir=PATH_TO_ANDROID_SDK
   49.54 -
   49.55 -6. Run 'ant debug' in android/project. This compiles the .java and eventually 
   49.56 -   creates a .apk with the native code embedded
   49.57 -7. 'ant debug install' will push the apk to the device or emulator (if connected)
   49.58 +5. Run './gradlew installDebug' in the project directory. This compiles the .java, creates an .apk with the native code embedded, and installs it on any connected Android device
   49.59  
   49.60  Here's an explanation of the files in the Android project, so you can customize them:
   49.61  
   49.62 -    android-project/
   49.63 -        AndroidManifest.xml	- package manifest. Among others, it contains the class name
   49.64 +    android-project/app
   49.65 +        build.gradle            - build info including the application version and SDK
   49.66 +        src/main/AndroidManifest.xml	- package manifest. Among others, it contains the class name
   49.67          			  of the main Activity and the package name of the application.
   49.68 -        build.properties	- empty
   49.69 -        build.xml		- build description file, used by ant. The actual application name
   49.70 -        			  is specified here.
   49.71 -        default.properties	- holds the target ABI for the application, android-10 and up
   49.72 -        project.properties	- holds the target ABI for the application, android-10 and up
   49.73 -        local.properties	- holds the SDK path, you should change this to the path to your SDK
   49.74          jni/			- directory holding native code
   49.75 -        jni/Android.mk		- Android makefile that can call recursively the Android.mk files
   49.76 -        			  in all subdirectories
   49.77 +        jni/Application.mk	- Application JNI settings, including target platform and STL library
   49.78 +        jni/Android.mk		- Android makefile that can call recursively the Android.mk files in all subdirectories
   49.79          jni/SDL/		- (symlink to) directory holding the SDL library files
   49.80          jni/SDL/Android.mk	- Android makefile for creating the SDL shared library
   49.81          jni/src/		- directory holding your C/C++ source
   49.82 -        jni/src/Android.mk	- Android makefile that you should customize to include your 
   49.83 -                                  source code and any library references
   49.84 -        res/			- directory holding resources for your application
   49.85 -        res/drawable-*		- directories holding icons for different phone hardware. Could be
   49.86 -        			  one dir called "drawable".
   49.87 -        res/layout/main.xml	- Usually contains a file main.xml, which declares the screen layout.
   49.88 -        			  We don't need it because we use the SDL video output.
   49.89 -        res/values/strings.xml	- strings used in your application, including the application name
   49.90 -        			  shown on the phone.
   49.91 -        src/org/libsdl/app/SDLActivity.java - the Java class handling the initialization and binding
   49.92 -        			  to SDL.  Be very careful changing this, as the SDL library relies
   49.93 -        			  on this implementation.
   49.94 -
   49.95 -
   49.96 -================================================================================
   49.97 - Build an app with static linking of libSDL
   49.98 -================================================================================
   49.99 -
  49.100 -This build uses the Android NDK module system.
  49.101 -
  49.102 -Instructions:
  49.103 -1. Copy the android-project directory wherever you want to keep your projects
  49.104 -   and rename it to the name of your project.
  49.105 -2. Rename "<project>/jni/src/Android_static.mk" to "<project>/jni/src/Android.mk"
  49.106 -   (overwrite the existing one)
  49.107 -3. Edit "<project>/jni/src/Android.mk" to include your source files
  49.108 -4. create and export an environment variable named NDK_MODULE_PATH that points
  49.109 -   to the parent directory of this SDL directory. e.g.:
  49.110 -
  49.111 -       export NDK_MODULE_PATH="$PWD"/..
  49.112 -
  49.113 -5. Edit "<project>/src/org/libsdl/app/SDLActivity.java" and remove the call to
  49.114 -   System.loadLibrary("SDL2").
  49.115 -6. Run 'ndk-build' (a script provided by the NDK). This compiles the C source
  49.116 +        jni/src/Android.mk	- Android makefile that you should customize to include your source code and any library references
  49.117 +        src/main/assets/	- directory holding asset files for your application
  49.118 +        src/main/res/		- directory holding resources for your application
  49.119 +        src/main/res/mipmap-*	- directories holding icons for different phone hardware
  49.120 +        src/main/res/values/strings.xml	- strings used in your application, including the application name
  49.121 +        src/main/java/org/libsdl/app/SDLActivity.java - the Java class handling the initialization and binding to SDL. Be very careful changing this, as the SDL library relies on this implementation. You should instead subclass this for your application.
  49.122  
  49.123  
  49.124  ================================================================================
  49.125 @@ -171,25 +131,23 @@
  49.126  Then replace "SDLActivity" in AndroidManifest.xml with the name of your
  49.127  class, .e.g. "MyGame"
  49.128  
  49.129 +
  49.130  ================================================================================
  49.131   Customizing your application icon
  49.132  ================================================================================
  49.133  
  49.134  Conceptually changing your icon is just replacing the "ic_launcher.png" files in
  49.135 -the drawable directories under the res directory. There are four directories for
  49.136 -different screen sizes. These can be replaced with one dir called "drawable",
  49.137 -containing an icon file "ic_launcher.png" with dimensions 48x48 or 72x72.
  49.138 +the drawable directories under the res directory. There are several directories
  49.139 +for different screen sizes.
  49.140  
  49.141 -You may need to change the name of your icon in AndroidManifest.xml to match
  49.142 -this icon filename.
  49.143  
  49.144  ================================================================================
  49.145   Loading assets
  49.146  ================================================================================
  49.147  
  49.148 -Any files you put in the "assets" directory of your android-project directory
  49.149 -will get bundled into the application package and you can load them using the
  49.150 -standard functions in SDL_rwops.h.
  49.151 +Any files you put in the "app/src/main/assets" directory of your project
  49.152 +directory will get bundled into the application package and you can load
  49.153 +them using the standard functions in SDL_rwops.h.
  49.154  
  49.155  There are also a few Android specific functions that allow you to get other
  49.156  useful paths for saving and loading data:
  49.157 @@ -211,6 +169,7 @@
  49.158      
  49.159  http://ponystyle.com/blog/2010/03/26/dealing-with-asset-compression-in-android-apps/
  49.160  
  49.161 +
  49.162  ================================================================================
  49.163   Pause / Resume behaviour
  49.164  ================================================================================
  49.165 @@ -231,6 +190,7 @@
  49.166  manually or quit the app (which is actually the kind of behaviour you'll see
  49.167  under iOS, if the OS can not restore your GL context it will just kill your app)
  49.168  
  49.169 +
  49.170  ================================================================================
  49.171   Threads and the Java VM
  49.172  ================================================================================
  49.173 @@ -246,6 +206,7 @@
  49.174  your thread automatically anyway (when you make an SDL call), but it'll never
  49.175  detach it.
  49.176  
  49.177 +
  49.178  ================================================================================
  49.179   Using STL
  49.180  ================================================================================
  49.181 @@ -257,6 +218,7 @@
  49.182  
  49.183  For more information check out CPLUSPLUS-SUPPORT.html in the NDK documentation.
  49.184  
  49.185 +
  49.186  ================================================================================
  49.187   Additional documentation
  49.188  ================================================================================
  49.189 @@ -268,19 +230,10 @@
  49.190  
  49.191  
  49.192  ================================================================================
  49.193 - Using Eclipse
  49.194 + Using Android Studio
  49.195  ================================================================================
  49.196  
  49.197 -First make sure that you've installed Eclipse and the Android extensions as described here:
  49.198 -	https://developer.android.com/tools/sdk/eclipse-adt.html
  49.199 -
  49.200 -Once you've copied the SDL android project and customized it, you can create an Eclipse project from it:
  49.201 - * File -> New -> Other
  49.202 - * Select the Android -> Android Project wizard and click Next
  49.203 - * Enter the name you'd like your project to have
  49.204 - * Select "Create project from existing source" and browse for your project directory
  49.205 - * Make sure the Build Target is set to Android 3.1 (API 12)
  49.206 - * Click Finish
  49.207 +You can open your project directory with Android Studio and run it normally.
  49.208  
  49.209  
  49.210  ================================================================================
  49.211 @@ -295,13 +248,11 @@
  49.212  Notice that this software emulator is incredibly slow and needs a lot of disk space.
  49.213  Using a real device works better.
  49.214  
  49.215 +
  49.216  ================================================================================
  49.217   Troubleshooting
  49.218  ================================================================================
  49.219  
  49.220 -You can create and run an emulator from the Eclipse IDE:
  49.221 - * Window -> Android SDK and AVD Manager
  49.222 -
  49.223  You can see if adb can see any devices with the following command:
  49.224  
  49.225      adb devices
  49.226 @@ -426,19 +377,21 @@
  49.227  
  49.228      adb shell setprop wrap.org.libsdl.app ""
  49.229  
  49.230 +
  49.231  ================================================================================
  49.232   Graphics debugging
  49.233  ================================================================================
  49.234  
  49.235  If you are developing on a compatible Tegra-based tablet, NVidia provides
  49.236 -Tegra Graphics Debugger at their website.  Because SDL2 dynamically loads EGL
  49.237 +Tegra Graphics Debugger at their website. Because SDL2 dynamically loads EGL
  49.238  and GLES libraries, you must follow their instructions for installing the
  49.239 -interposer library on a rooted device.  The non-rooted instructions are not
  49.240 +interposer library on a rooted device. The non-rooted instructions are not
  49.241  compatible with applications that use SDL2 for video.
  49.242  
  49.243  The Tegra Graphics Debugger is available from NVidia here:
  49.244  https://developer.nvidia.com/tegra-graphics-debugger
  49.245  
  49.246 +
  49.247  ================================================================================
  49.248   Why is API level 10 the minimum required?
  49.249  ================================================================================
  49.250 @@ -446,7 +399,7 @@
  49.251  API level 10 is the minimum required level at runtime (that is, on the device) 
  49.252  because SDL requires some functionality for running not
  49.253  available on older devices. Since the incorporation of joystick support into SDL,
  49.254 -the minimum SDK required to *build* SDL is version 12. Devices running API levels
  49.255 +the minimum SDK required to *build* SDL is version 16. Devices running API levels
  49.256  10-11 are still supported, only with the joystick functionality disabled.
  49.257  
  49.258  Support for native OpenGL ES and ES2 applications was introduced in the NDK for
  49.259 @@ -457,6 +410,7 @@
  49.260  about 90% of the Android devices accessing Google Play support API level 10 or
  49.261  higher (March 2013).
  49.262  
  49.263 +
  49.264  ================================================================================
  49.265   A note regarding the use of the "dirty rectangles" rendering technique
  49.266  ================================================================================
  49.267 @@ -475,6 +429,7 @@
  49.268  
  49.269  Reference: http://www.khronos.org/registry/egl/specs/EGLTechNote0001.html
  49.270  
  49.271 +
  49.272  ================================================================================
  49.273   Known issues
  49.274  ================================================================================